Supposons que j'ai des données de comptage qui ressemblent à ceci:
library(tidyr)
library(dplyr)
X.raw <- data.frame(
x = as.factor(c("A", "A", "A", "B", "B", "B")),
y = as.factor(c("i", "ii", "ii", "i", "i", "i")),
z = 1:6)
X.raw
# x y z
# 1 A i 1
# 2 A ii 2
# 3 A ii 3
# 4 B i 4
# 5 B i 5
# 6 B i 6
Je voudrais ranger et résumer comme ceci:
X.tidy <- X.raw %>% group_by(x,y) %>% summarise(count=sum(z))
X.tidy
# Source: local data frame [3 x 3]
# Groups: x
#
# x y count
# 1 A i 1
# 2 A ii 5
# 3 B i 15
Je sais que pour x=="B"
et y=="ii"
nous avons observé un décompte de zéro, plutôt qu'une valeur manquante. c'est-à-dire que le travailleur sur le terrain était réellement là, mais comme il n'y avait pas de compte positif, aucune ligne n'a été entrée dans les données brutes. Je peux ajouter explicitement le nombre zéro en procédant comme suit:
X.fill <- X.tidy %>% spread(y, count, fill=0) %>% gather(y, count, -x)
X.fill
# Source: local data frame [4 x 3]
#
# x y count
# 1 A i 1
# 2 B i 15
# 3 A ii 5
# 4 B ii 0
Mais cela semble un peu une manière détournée de faire les choses. Leur idiome est-il plus propre pour cela?
Juste pour clarifier: mon code fait déjà ce dont j'ai besoin, en utilisant spread
puis gather
, donc ce qui m'intéresse, c'est de trouver un itinéraire plus direct within = tidyr
et dplyr
.
Puisque dplyr 0.8
vous pouvez le faire en définissant le paramètre .drop = FALSE
dans group_by
:
X.tidy <- X.raw %>% group_by(x, y, .drop = FALSE) %>% summarise(count=sum(z))
X.tidy
# # A tibble: 4 x 3
# # Groups: x [2]
# x y count
# <fct> <fct> <int>
# 1 A i 1
# 2 A ii 5
# 3 B i 15
# 4 B ii 0
La fonction complete
de tidyr est conçue uniquement pour cette situation.
De la documentation:
Il s'agit d'un wrapper autour de expand (), left_join () et replace_na qui est utile pour compléter les combinaisons de données manquantes.
Vous pouvez l'utiliser de deux manières. Tout d'abord, vous pouvez l'utiliser sur l'ensemble de données d'origine avant de résumer, en "complétant" l'ensemble de données avec toutes les combinaisons de x
et y
, et en remplissant z
avec 0 (vous pouvez utiliser la valeur par défaut NA
fill
et utilisez na.rm = TRUE
dans sum
).
X.raw %>%
complete(x, y, fill = list(z = 0)) %>%
group_by(x,y) %>%
summarise(count = sum(z))
Source: local data frame [4 x 3]
Groups: x [?]
x y count
<fctr> <fctr> <dbl>
1 A i 1
2 A ii 5
3 B i 15
4 B ii 0
Vous pouvez également utiliser complete
sur votre jeu de données pré-résumé. Notez que complete
respecte le regroupement. X.tidy
est groupé, vous pouvez donc soit ungroup
et compléter l'ensemble de données par x
et y
ou simplement lister la variable que vous souhaitez compléter dans chaque groupe - dans ce cas, y
.
# Complete after ungrouping
X.tidy %>%
ungroup %>%
complete(x, y, fill = list(count = 0))
# Complete within grouping
X.tidy %>%
complete(y, fill = list(count = 0))
Le résultat est le même pour chaque option:
Source: local data frame [4 x 3]
x y count
<fctr> <fctr> <dbl>
1 A i 1
2 A ii 5
3 B i 15
4 B ii 0
Vous pouvez utiliser expand
de tidyr pour effectuer toutes les combinaisons de niveaux de facteurs, puis left_join
:
X.tidy %>% expand(x, y) %>% left_join(X.tidy)
# Joining by: c("x", "y")
# Source: local data frame [4 x 3]
#
# x y count
# 1 A i 1
# 2 A ii 5
# 3 B i 15
# 4 B ii NA
Vous pouvez ensuite conserver les valeurs sous forme de NA ou les remplacer par 0 ou toute autre valeur. De cette façon, ce n'est pas une solution complète du problème aussi, mais c'est plus rapide et plus convivial en RAM que spread
& gather
.
plyr
possède les fonctionnalités que vous recherchez, mais dplyr
ne le fait pas (encore), vous avez donc besoin de code supplémentaire pour inclure les groupes de comptage zéro, comme indiqué par @momeara. Voir aussi cette question . Dans plyr::ddply
vous venez d'ajouter .drop=FALSE
pour conserver les groupes comptant zéro dans le résultat final. Par exemple:
library(plyr)
X.tidy = ddply(X.raw, .(x,y), summarise, count=sum(z), .drop=FALSE)
X.tidy
x y count
1 A i 1
2 A ii 5
3 B i 15
4 B ii 0
Vous pouvez explicitement faire toutes les combinaisons possibles, puis le joindre avec le résumé bien rangé:
x.fill <- expand.grid(x=unique(x.tidy$x), x=unique(x.tidy$y)) %>%
left_join(x.tidy, by=("x", "y")) %>%
mutate(count = ifelse(is.na(count), 0, count)) # replace null values with 0's