dplyr summarize: Equivalent of".drop=FALSE " pour garder les groupes de longueur zéro en sortie

Lorsqu'on utilise la fonction summarise avec la fonction plyr 's ddply , les catégories vides sont supprimées par défaut. Vous pouvez modifier ce comportement en ajoutant .drop = FALSE . Cependant, cela ne fonctionne pas lorsque vous utilisez summarise avec dplyr . Est-il une autre façon de garder vide catégories dans le résultat?

voici un exemple avec de fausses données.

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

Pas exactement ce que j'espérais. Existe-t-il une méthode dplyr pour atteindre le même résultat que .drop=FALSE dans plyr ?

77
demandé sur eipi10 2014-03-20 07:52:09

3 réponses

la question est toujours ouverte, mais en attendant, surtout depuis que vos données sont déjà prises en compte, vous pouvez utiliser complete de "tidyr" pour obtenir ce que vous pourriez être à la recherche de:

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 vous voulez que la valeur de remplacement soit zéro, vous devez spécifier que avec 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
46
répondu A5C1D2H2I1M1N2O1R2T1 2016-03-18 19:07:51

dplyr solution:

première marque groupée DF

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

nous résumons ensuite les niveaux qui se produisent en comptant avec n()

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

ensuite nous fusionnons nos résultats dans une base de données qui contient tous les niveaux de facteurs:

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

enfin, dans ce cas, puisque nous regardons les comptes, les valeurs NA sont changées à 0.

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

cela peut aussi être mis en œuvre fonctionnellement, voir les réponses: Ajouter des lignes de données regroupées avec dplyr?

Une bidouille:

j'ai pensé que je posterais un terrible hack qui fonctionne dans ce cas pour intérêt. Je doute sérieusement que vous devriez réellement faire cela mais il montre comment group_by() génère les atrributes comme si df$b était un vecteur de caractère pas un facteur avec des niveaux. Aussi, Je ne prétendez pas comprendre cela correctement -- mais j'espère que cela m'aidera à apprendre -- c'est la seule raison pour laquelle je l'affiche!

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

définit une valeur" hors limites " qui ne peut pas exister dans l'ensemble de données.

oob_val <- nrow(by_b)+1

modifier les attributs pour" trick " summarise() :

attr(by_b, "indices")[[3]] <- rep(NA,oob_val)
attr(by_b, "group_sizes")[3] <- 0
attr(by_b, "labels")[3,] <- 3

faire le résumé:

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

index et remplacer toutes les occurences de oob_val

res[res == oob_val] <- 0

qui donne la destination:

> res
Source: local data frame [3 x 2]

b count_a
1 1       6
2 2       6
3 3       0
20
répondu npjc 2017-05-23 12:18:11

ce n'est pas exactement ce qui a été demandé dans la question, mais au moins pour cet exemple simple, vous pourriez obtenir le même résultat en utilisant xtabs, par exemple:

utilisant dplyr:

df %.%
  xtabs(formula = ~ b) %.%
  as.data.frame()

ou moins:

as.data.frame(xtabs( ~ b, df))

résultat (égal dans les deux cas):

  b Freq
1 1    6
2 2    6
3 3    0
9
répondu docendo discimus 2014-05-05 19:12:45