Je souhaite agréger la date par groupe. Cependant, chaque observation peut appartenir à plusieurs groupes (par exemple, l'observation 1 appartient aux groupes A et B). Je ne pouvais pas trouver une bonne façon d'y parvenir avec data.table. Actuellement, j'ai créé pour chacun des groupes possibles une variable logique qui prend la valeur TRUE si l'observation appartient à ce groupe. Je recherche une meilleure façon de le faire que celle présentée ci-dessous. J'aimerais aussi savoir comment je pourrais y parvenir avec le tidyverse.

library(data.table)
# Data
set.seed(1)
TF <- c(TRUE, FALSE)
time <- rep(1:4, each = 5)
df <- data.table(time = time, x = rnorm(20), groupA = sample(TF, size = 20, replace = TRUE),
                                             groupB = sample(TF, size = 20, replace = TRUE),
                                             groupC = sample(TF, size = 20, replace = TRUE))

# This should be nicer and less repetitive
df[groupA == TRUE, .(A = sum(x)), by = time][
  df[groupB == TRUE, .(B = sum(x)), by = time], on = "time"][
    df[groupC == TRUE, .(C = sum(x)), by = time], on = "time"]

# desired output
time          A          B         C
1:    1         NA  0.9432955 0.1331984
2:    2  1.2257538  0.2427420 0.1882493
3:    3 -0.1992284 -0.1992284 1.9016244
4:    4  0.5327774  0.9438362 0.9276459
3
Alex 23 mai 2018 à 11:35

3 réponses

Meilleure réponse

Voici une solution avec data.table:

df[, lapply(.SD[, .(groupA, groupB, groupC)]*x, sum), time]
# > df[, lapply(.SD[, .(groupA, groupB, groupC)]*x, sum), time]
#    time     groupA     groupB    groupC
# 1:    1  0.0000000  0.9432955 0.1331984
# 2:    2  1.2257538  0.2427420 0.1882493
# 3:    3 -0.1992284 -0.1992284 1.9016244
# 4:    4  0.5327774  0.9438362 0.9276459

Ou (merci à @ chinsoon12 pour le commentaire) de manière plus programmatique:

df[, lapply(.SD*x, sum), by=.(time), .SDcols=paste0("group", c("A","B","C"))]

Si vous voulez le résultat au format long, vous pouvez faire:

df[, colSums(.SD*x), by=.(time), .SDcols=paste0("group", c("A","B","C"))]
### with indicator for the group:
df[, .(colSums(.SD*x), c("A","B","C")), by=.(time), .SDcols=paste0("group", c("A","B","C"))] 
3
jogo 23 mai 2018 à 09:22

Je pense que c'est plus facile ici de travailler en format long. Je rassemble d'abord les observations au format long, puis je ne garde que les valeurs où l'observation appartient au groupe correspondant. Ensuite, je supprime la colonne logique et renomme les groupes en lettres simples. Ensuite, j'agrège à travers les groupes et le temps (résumer dans dplyr). Enfin je suis revenu au grand format.

library(dplyr)
library(tidyr)

set.seed(1)
TF <- c(TRUE, FALSE)
time <- rep(1:4, each = 5)


df <- data.frame(time = time, x = rnorm(20), groupA = sample(TF, size = 20, replace = TRUE),
                 groupB = sample(TF, size = 20, replace = TRUE),
                 groupC = sample(TF, size = 20, replace = TRUE))


df %>% 
  gather(group, belongs, groupA:groupC) %>% 
  filter(belongs) %>% 
  select(-belongs) %>% 
  mutate(group = gsub("group", "", group)) %>% 
  group_by(time, group) %>% 
  summarise(x = sum(x)) %>% 
  spread(group, x)

Production

# A tibble: 4 x 4
# Groups:   time [4]
   time       A      B     C
  <int>   <dbl>  <dbl> <dbl>
1     1  NA      0.943 0.133
2     2   1.23   0.243 0.188
3     3  -0.199 -0.199 1.90 
4     4   0.533  0.944 0.928
2
Jack Brookes 23 mai 2018 à 08:50

Une option peut utiliser les packages tidyr et dplyr en combinaison avec data.table. Essayez de travailler sur des données au format long, puis changez-les en format large.

library(dplyr)
library(tidyr)

melt(df, id.vars = c("time", "x")) %>%
  filter(value) %>%
  group_by(time, variable) %>%
  summarise(sum = sum(x)) %>%
  spread(variable, sum)

# # A tibble: 4 x 4
# # Groups: time [4]
# time  groupA groupB groupC
# * <int>   <dbl>  <dbl>  <dbl>
# 1     1  NA      0.943  0.133
# 2     2   1.23   0.243  0.188
# 3     3 - 0.199 -0.199  1.90 
# 4     4   0.533  0.944  0.928
1
MKR 23 mai 2018 à 08:56