separate - dplyr summarize: Equivalente a ".drop=FALSE" para mantener grupos con longitud cero en salida
tidyr cheat sheet (3)
solución dplyr:
Primero haz un grupo de df
by_b <- tbl_df(df) %>% group_by(b)
luego resumimos esos niveles que ocurren contando con n()
res <- by_b %>% summarise( count_a = n() )
luego fusionamos nuestros resultados en un marco de datos que contiene todos los niveles de factores:
expanded_res <- left_join(expand.grid(b = levels(df$b)),res)
finalmente, en este caso, dado que estamos mirando los recuentos, los valores de NA
se cambian a 0.
final_counts <- expanded_res[is.na(expanded_res)] <- 0
Esto también se puede implementar funcionalmente, ver respuestas: ¿ Agregar filas a los datos agrupados con dplyr?
Un truco:
Pensé que publicaría un truco terrible que funciona en este caso por interés. Dudo seriamente que alguna vez lo hagas, pero muestra cómo group_by()
genera los atrributes como si df$b
fuera un vector de caracteres, no un factor con niveles. Además, no pretendo entender esto correctamente, pero espero que esto me ayude a aprender: ¡esta es la única razón por la que lo estoy publicando!
by_b <- tbl_df(df) %>% group_by(b)
define un valor "fuera de límites" que no puede existir en el conjunto de datos.
oob_val <- nrow(by_b)+1
modificar atributos para "truco" summarise()
:
attr(by_b, "indices")[[3]] <- rep(NA,oob_val)
attr(by_b, "group_sizes")[3] <- 0
attr(by_b, "labels")[3,] <- 3
hacer el resumen:
res <- by_b %>% summarise(count_a = n())
indexe y reemplace todas las ocurrencias de oob_val
res[res == oob_val] <- 0
que da la intención:
> res
Source: local data frame [3 x 2]
b count_a
1 1 6
2 2 6
3 3 0
Al usar summarise
con la función ddply
, las categorías vacías se descartan por defecto. Puede cambiar este comportamiento agregando .drop = FALSE
. Sin embargo, esto no funciona cuando se usa summarise
con dplyr
. ¿Hay alguna otra forma de mantener categorías vacías en el resultado?
Aquí hay un ejemplo con datos falsos.
library(dplyr)
df = data.frame(a=rep(1:3,4), b=rep(1:2,6))
# Now add an extra level to df$b that has no corresponding value in df$a
df$b = factor(df$b, levels=1:3)
# Summarise with plyr, keeping categories with a count of zero
plyr::ddply(df, "b", summarise, count_a=length(a), .drop=FALSE)
b count_a
1 1 6
2 2 6
3 3 0
# Now try it with dplyr
df %.%
group_by(b) %.%
summarise(count_a=length(a), .drop=FALSE)
b count_a .drop
1 1 6 FALSE
2 2 6 FALSE
No es exactamente lo que esperaba. ¿Hay un método dplyr
para lograr el mismo resultado que .drop=FALSE
en plyr
?
El problema sigue abierto, pero mientras tanto, especialmente debido a que sus datos ya están incluidos, puede usar los datos complete
de "tidyr" para obtener lo que podría estar buscando:
library(tidyr)
df %>%
group_by(b) %>%
summarise(count_a=length(a)) %>%
complete(b)
# Source: local data frame [3 x 2]
#
# b count_a
# (fctr) (int)
# 1 1 6
# 2 2 6
# 3 3 NA
Si desea que el valor de reemplazo sea cero, debe especificarlo con fill
:
df %>%
group_by(b) %>%
summarise(count_a=length(a)) %>%
complete(b, fill = list(count_a = 0))
# Source: local data frame [3 x 2]
#
# b count_a
# (fctr) (dbl)
# 1 1 6
# 2 2 6
# 3 3 0
esto no es exactamente lo que se preguntó en la pregunta, pero al menos para este simple ejemplo, puede obtener el mismo resultado usando xtabs, por ejemplo:
usando dplyr:
df %.%
xtabs(formula = ~ b) %.%
as.data.frame()
o más corto:
as.data.frame(xtabs( ~ b, df))
resultado (igual en ambos casos):
b Freq
1 1 6
2 2 6
3 3 0