Lorsque vous utilisez summarise
avec la fonction plyr
de 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
. Existe-t-il un autre moyen de conserver les catégories vides 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 obtenir le même résultat que .drop=FALSE
dans plyr
?
Depuis dplyr 0.8group_by
a gagné le .drop
argument qui fait exactement ce que vous avez demandé:
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
Une note supplémentaire pour accompagner la réponse de @ Moody_Mudskipper: Utiliser .drop=FALSE
peut donner des résultats potentiellement inattendus lorsqu'une ou plusieurs variables de regroupement ne sont pas codées en tant que facteurs. Voir les exemples ci-dessous:
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)
Le problème est toujours ouvert, mais en attendant, d'autant plus que vos données sont déjà factorisées, vous pouvez utiliser complete
de "tidyr" pour obtenir ce que vous cherchez peut-être:
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 vouliez que la valeur de remplacement soit nulle, vous devez spécifier cela 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
Faire d'abord un groupe df
by_b <- tbl_df(df) %>% group_by(b)
puis nous résumons les niveaux qui se produisent en comptant avec n()
res <- by_b %>% summarise( count_a = n() )
puis nous fusionnons nos résultats dans un bloc 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 nombres, les valeurs NA
sont changées à 0.
final_counts <- expanded_res[is.na(expanded_res)] <- 0
Cela peut également être implémenté fonctionnellement, voir les réponses: Ajouter des lignes aux données groupées avec dplyr?
Je pensais que je posterais un terrible piratage qui fonctionne dans ce cas par intérêt. Je doute sérieusement que vous devriez réellement le faire, mais cela montre comment group_by()
génère les attributs comme si df$b
Était un vecteur de caractères et non un facteur avec des niveaux. De plus, je ne prétends pas comprendre cela correctement - mais j'espère que cela m'aidera à apprendre - c'est la seule raison pour laquelle je le poste!
by_b <- tbl_df(df) %>% group_by(b)
définir une valeur "hors limites" qui ne peut pas exister dans l'ensemble de données.
oob_val <- nrow(by_b)+1
modifier les attributs en "astuce" 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())
indexer et remplacer toutes les occurrences de oob_val
res[res == oob_val] <- 0
ce qui donne l'intention:
> res
Source: local data frame [3 x 2]
b count_a
1 1 6
2 2 6
3 3 0
ce n'est pas exactement ce qui a été demandé dans la question, mais au moins pour cet exemple simple, vous pouvez obtenir le même résultat en utilisant xtabs, par exemple:
en utilisant dplyr:
df %>%
xtabs(formula = ~ b) %>%
as.data.frame()
ou plus court:
as.data.frame(xtabs( ~ b, df))
résultat (égal dans les deux cas):
b Freq
1 1 6
2 2 6
3 3 0