3
votes

Le group_by de R dplyr considère également les groupes vides

Considérons le bloc de données suivant:

data %>%
  group_by(col1, col2) %>%
  summarize(stat = sum(val2) - sum(val1))

# A tibble: 5 x 3
# Groups:   col1 [?]
  col1  col2   stat
  <fct> <fct> <dbl>
1 A     A      58.1
2 B     A     -16.4
3 B     B      17.0
4 C     A     -12.9
5 C     C     -41.9

Le tableau de contingence est le suivant:

cont_tab <- table(data$col1, data$col2, dnn = c("col1", "col2"))

cont_tab

    col2
col1 A B C
   A 4 0 0
   B 1 3 0
   C 1 0 3

Comme vous pouvez le voir, certaines paires n'ont pas ne se produisent pas: (A, B), (A, C), (B, C), (C, B). Le but final de mon analyse est de lister toutes les paires (dans ce cas 9) et d'afficher une statistique pour chacune d'elles. En utilisant la fonction dplyr :: group_by () , j'ai atteint une limitation. À savoir, le dplyr :: group_by () ne considère que les paires existantes (paires qui se sont produites au moins une fois):

set.seed(123)
data <- data.frame(col1 = factor(rep(c("A", "B", "C"), 4)),
                   col2 = factor(c(rep(c("A", "B", "C"), 3), c("A", "A", "A"))),
                   val1 = 1:12,
                   val2 = rnorm(12, 10, 15))

Le résultat que j'ai en tête a 9 lignes (dont 4 ont stat égal à 0). Est-ce faisable dans dplyr?

EDIT: Désolé d'être trop vague au début. Le vrai problème est plus complexe que de compter le nombre de fois qu'une paire particulière se produit. J'ai ajouté les nouvelles données afin de rendre le vrai problème plus visible.


1 commentaires

Vous pouvez ajouter spread à partir de tidyr %>% spread (col2, stat, fill = 0) Le groupe par étape peut se faire avec une seule ligne count (data, col1, col2)%>% spread (col2, n, fill = 0)


5 Réponses :


4
votes

Ceci est faisable même sans dplyr

as.data.frame(table(data$col1, data$col2, dnn = c("col1", "col2")))
#  col1 col2 Freq
#1    A    A    4
#2    B    A    1
#3    C    A    1
#4    A    B    0
#5    B    B    3
#6    C    B    0
#7    A    C    0
#8    B    C    0
#9    C    C    3


0 commentaires

5
votes

Il est beaucoup plus facile d'ajouter spread depuis tidyr pour obtenir le même résultat qu'avec table

data %>%
   group_by(col1, col2) %>%
   summarize(stat = sum(val2) - sum(val1)) %>% 
   spread(col2, stat, fill = 0)  %>% 
   gather(col2, stat, -1)
# A tibble: 9 x 3
# Groups:   col1 [3]
#  col1  col2    stat
#  <fct> <chr>  <dbl>
#1 A     A       7.76
#2 B     A     -20.8 
#3 C     A       6.97
#4 A     B       0   
#5 B     B      28.8 
#6 C     B       0   
#7 A     C       0   
#8 B     C       0   
#9 C     C       9.56

REMARQUE: l'étape group_by / summary est remplacée par count ici

Comme @divibisan l'a suggéré, si l'OP voulait un format long, ajoutez rassembler à la fin

data %>%
   group_by(col1, col2) %>%
   summarize(stat = n()) %>%
   spread(col2, stat, fill = 0) %>%
   gather(col2, stat, A:C)
# A tibble: 9 x 3
# Groups:   col1 [3]
#  col1  col2   stat
#  <fct> <chr> <dbl>
#1 A     A         4
#2 B     A         1
#3 C     A         1
#4 A     B         0
#5 B     B         3
#6 C     B         0
#7 A     C         0
#8 B     C         0
#9 C     C         3

Mettre à jour

Avec les données mises à jour dans le post de OP

library(dplyr)
library(tidyr)
count(data, col1, col2) %>% 
      spread(col2, n, fill = 0)
# A tibble: 3 x 4
# Groups:   col1 [3]
#  col1      A     B     C
#  <fct> <dbl> <dbl> <dbl>
#1 A         4     0     0
#2 B         1     3     0
#3 C         1     0     3


3 commentaires

Il s'agit simplement de répliquer la table avec laquelle le demandeur a commencé. Ils veulent les décomptes au format long sous forme de trame de données 9x3


Les spread () et rassembler () sont vraiment utiles. Je me demande s'il est possible de ne pas coder en dur A: C dans la dernière ligne?


@ balkon16 Vous pouvez le changer en -1 c'est-à-dire sauf la première colonne. Mise à jour du code



2
votes

Vous pouvez utiliser tidyr::complete

data %>%
  count(col1, col2) %>%
  complete(col1, col2, fill = list(n = 0)) 

Vous pouvez également utiliser count pour la première partie. Le code ci-dessous donne le même résultat que le code ci-dessus

library(tidyverse)

data %>%
  group_by(col1, col2) %>%
  summarize(stat = n()) %>% 
  # additions below
  ungroup %>% 
  complete(col1, col2, fill = list(stat = 0))

# # A tibble: 9 x 3
#   col1  col2   stat
#   <chr> <chr> <dbl>
# 1 A     A         4
# 2 A     B         0
# 3 A     C         0
# 4 B     A         1
# 5 B     B         3
# 6 B     C         0
# 7 C     A         1
# 8 C     B         0
# 9 C     C         3


1 commentaires

C'est une belle option



1
votes

Également une possibilité tidyverse en utilisant tidyr::complete():

data %>%
 count(col1, col2) %>%
 right_join(crossing(col1 = unique(data$col1), 
                     col2 = unique(data$col2)), by = c("col1" = "col1",
                                                       "col2" = "col2")) %>%
 replace_na(list(n = 0))

Ou en utilisant tidyr :: expand ( ) :

data %>% 
 count(col1, col2) %>%
 right_join(data %>%
            expand(col1, col2), by = c("col1" = "col1",
                                       "col2" = "col2")) %>%
 replace_na(list(n = 0))

Ou en utilisant tidyr::crossing():

data %>% 
 group_by_all() %>%
 add_count() %>%
 complete(col1, col2, fill = list(n = 0)) %>%
 distinct()

  col1  col2      n
  <fct> <fct> <dbl>
1 A     A         4
2 A     B         0
3 A     C         0
4 B     A         1
5 B     B         3
6 B     C         0
7 C     A         1
8 C     B         0
9 C     C         3


0 commentaires

0
votes

Voici une petite solution de contournement, j'espère que cela fonctionne pour vous. Fusionnez votre tableau avec le tableau de toutes les combinaisons et remplacez les NA par 0.

data %>%
group_by(col1, col2) %>%
summarize(stat = n()) %>% 
merge(unique(expand.grid(data)), by=c("col1","col2"), all=T) %>% 
replace_na(list(stat=0))


0 commentaires