web-dev-qa-db-fra.com

Idiome approprié pour ajouter des lignes de comptage nul dans tidyr / dplyr

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.

34
pete

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
19
Moody_Mudskipper

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 NAfill 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
25
aosmith

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.

4
inscaven

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
3
eipi10

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
2
momeara