Агрегирование, если каждое наблюдение может принадлежать нескольким группам - PullRequest
0 голосов
/ 23 мая 2018

Я хочу агрегировать дату по группам.Однако каждое наблюдение может принадлежать нескольким группам (например, наблюдение 1 относится к группам A и B).Я не мог найти хороший способ достичь этого с data.table.В настоящее время я создал для каждой из возможных групп логическую переменную, которая принимает значение TRUE, если наблюдение принадлежит этой группе.Я ищу лучший способ сделать это, чем представлено ниже.Я также хотел бы знать, как я могу добиться этого с 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 ]

0 голосов
/ 23 мая 2018

Вот решение с 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

или (thx to @ chinsoon12 за комментарий) более программно:

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

Если вы хотите получить результат вдлинный формат вы можете сделать:

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"))] 
0 голосов
/ 23 мая 2018

В качестве опции можно использовать пакеты tidyr и dplyr в сочетании с data.table.Попробуйте поработать с данными в длинном формате, а затем измените их на широкоформатный.

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
0 голосов
/ 23 мая 2018

Я думаю, что здесь легче работать в длинном формате.Сначала я собираю наблюдения в длинный формат, затем сохраняю только те значения, в которых наблюдения принадлежат соответствующей группе.Затем я удаляю логический столбец и переименовываю группы в отдельные буквы.Затем я агрегирую по группам и по времени (суммируем в dplyr).Наконец я снова распространился на широкий формат.

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)

Вывод

# 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
...