Cuando se utiliza summarise con plyr‘s ddply función de las categorías vacías son descartados por defecto. Puede cambiar este comportamiento por la adición de .drop = FALSE. Sin embargo, esto no funciona cuando se utiliza summarise con dplyr. Hay otra forma de mantener las categorías vacías en el resultado?

He aquí 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 yo estaba esperando. Hay un dplyr método para lograr el mismo resultado que .drop=FALSE en plyr?

InformationsquelleAutor eipi10 | 2014-03-20

4 Comentarios

  1. 15

    Desde dplyr 0.8 group_by ganado la .drop argumento de que hace exactamente lo que usted solicitó:

    df = data.frame(a=rep(1:3,4), b=rep(1:2,6))
    df$b = factor(df$b, levels=1:3)
    
    df %>%
      group_by(b, .drop=FALSE) %>%
      summarise(count_a=length(a))
    
    #> # A tibble: 3 x 2
    #>   b     count_a
    #>   <fct>   <int>
    #> 1 1           6
    #> 2 2           6
    #> 3 3           0

    Una nota adicional a ir con @Moody_Mudskipper la respuesta: el Uso de .drop=FALSE puede potencialmente dar lugar a resultados inesperados cuando una o más de las variables de agrupación no están codificados de los factores. Vea los ejemplos a continuación:

    library(dplyr)
    data(iris)
    
    # Add an additional level to Species
    iris$Species = factor(iris$Species, levels=c(levels(iris$Species), "empty_level"))
    
    # Species is a factor and empty groups are included in the output
    iris %>% group_by(Species, .drop=FALSE) %>% tally
    
    #>   Species         n
    #> 1 setosa         50
    #> 2 versicolor     50
    #> 3 virginica      50
    #> 4 empty_level     0
    
    # Add character column
    iris$group2 = c(rep(c("A","B"), 50), rep(c("B","C"), each=25))
    
    # Empty groups involving combinations of Species and group2 are not included in output
    iris %>% group_by(Species, group2, .drop=FALSE) %>% tally
    
    #>   Species     group2     n
    #> 1 setosa      A         25
    #> 2 setosa      B         25
    #> 3 versicolor  A         25
    #> 4 versicolor  B         25
    #> 5 virginica   B         25
    #> 6 virginica   C         25
    #> 7 empty_level <NA>       0
    
    # Turn group2 into a factor
    iris$group2 = factor(iris$group2)
    
    # Now all possible combinations of Species and group2 are included in the output, 
    #  whether present in the data or not
    iris %>% group_by(Species, group2, .drop=FALSE) %>% tally
    
    #>    Species     group2     n
    #>  1 setosa      A         25
    #>  2 setosa      B         25
    #>  3 setosa      C          0
    #>  4 versicolor  A         25
    #>  5 versicolor  B         25
    #>  6 versicolor  C          0
    #>  7 virginica   A          0
    #>  8 virginica   B         25
    #>  9 virginica   C         25
    #> 10 empty_level A          0
    #> 11 empty_level B          0
    #> 12 empty_level C          0
    
    Created on 2019-03-13 by the reprex package (v0.2.1)
    • He añadido una nota adicional a su respuesta. Por favor, siéntase libre de eliminar si no te gusta la edición.
    • He presentado un problema acerca de este en github para averiguar si esto es un bug o la intención de comportamiento.
    • ligeramente más corto es el uso de count: iris %>% count(Species, group2, .drop=FALSE)
  2. 59

    El tema sigue abierto, pero en el ínterin, especialmente desde que sus datos están ya incluido, usted puede utilizar complete de «tidyr» para conseguir lo que usted 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 quería que el valor de reposición a cero, es necesario precisar que 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
    • Me tomó un montón de golpearse la cabeza contra la pared para averiguar esto, así que voy a mencionar aquí… Si grupo 2 variables, y son caracteres en lugar de los factores, usted tendrá que utilizar ungroup() antes de completar. Si alguna vez te aviso complete en realidad no completar, ungroup probablemente es necesario.
    • ¿Qué pasa Si usted tiene aún más las variables de agrupación? Tengo un enorme número de filas (mucho más que el original de mi dataframe) si puedo utilizar todas las de la agrupación de vars de mi group_by
    • Me lo imaginé: Usted tiene que utilizar de anidación 🙂 Así que poner todas las Variables que no debe también ser combinados entre sí en complete(variablewithdroppedlevels, nesting(var1,var2,var3)) (es, en realidad, en la ayuda para complete todavía me tomó un tiempo para averiguar
  3. 20

    dplyr solución:

    Primero hacer agrupados df

    by_b <- tbl_df(df) %>% group_by(b)

    a continuación hacemos un resumen de los niveles que se producen por contar con n()

    res <- by_b %>% summarise( count_a = n() )

    entonces podemos combinar nuestros resultados dentro de un marco de datos que contiene todos los niveles de los factores:

    expanded_res <- left_join(expand.grid(b = levels(df$b)),res)

    por último, en este caso, dado que estamos mirando en cuenta el NA se cambian los valores a 0.

    final_counts <- expanded_res[is.na(expanded_res)] <- 0

    Esto también puede ser implementado funcionalmente, vea las respuestas:
    Agregar filas para datos agrupados con dplyr?

    Un hack:

    Pensé que iba a publicar un terrible hack que funciona en este caso, por causa de interés. Tengo serias dudas de que alguna vez debería hacer esto pero muestra cómo group_by() genera el atributo como si df$b era un personaje de vectores no es un factor con los niveles. Además, yo no pretendo entender esto correctamente, pero yo estoy esperando que esto me ayuda a aprender-esta es la única razón por la que estoy publicando!

    by_b <- tbl_df(df) %>% group_by(b)

    definir un «fuera de límites» valor que no puede existir en el conjunto de datos.

    oob_val <- nrow(by_b)+1

    modificar los atributos de «engañar» a 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())

    índice y reemplazar todas las ocurrencias de oob_val

    res[res == oob_val] <- 0

    que da a la intención de:

    > res
    Source: local data frame [3 x 2]
    
    b count_a
    1 1       6
    2 2       6
    3 3       0
  4. 11

    esto no es exactamente lo que se les pide en la pregunta, pero al menos para este sencillo ejemplo, usted podría conseguir el mismo resultado con xtabs, por ejemplo:

    utilizando 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

Dejar respuesta

Please enter your comment!
Please enter your name here