Разделить результаты из cumum на две разные группы в две разные колонки? - PullRequest
2 голосов
/ 26 июня 2019

Я пытаюсь найти кумулятивную сумму для двух отдельных групп и перечислить каждую из этих сумм в отдельных столбцах.

Вот кадр данных, отсортированный по времени:

time  group  value
0     A      0
0     B      0
0     A      0
1     A      0
1     B      1
1     B      0
2     B      1
2     A      1
2     A      1
2     A     -1
3     A      0
3     B      1

Это то, что я должен найти cumsum по группам и создать столбец cumsum:

df$cumsum <- ave(df$value, df$group, FUN=cumsum)

time  group  value  cumsum
0     A      0      0
0     B      0      0
0     A      0      0
1     A      0      0
1     B      1      1
1     B      0      1
2     B      1      2
2     A      1      1
2     A      1      2
2     A     -1      1
3     A      0      1
3     B      1      3

Как можно разделить результаты на два столбца, один для A и один для B? Как вариант, можно ли найти условную сумму? В любом случае, я хочу, чтобы результаты выглядели так:

time  group  value  cumsum_A  cumsum_B
0      A      0     0         0
0      B      0     0         0
0      A      0     0         0
1      A      0     0         0
1      B      1     0         1
1      B      0     0         1
2      B      1     0         2
2      A      1     1         2
2      A      1     2         2
2      A     -1     1         2
3      A      0     1         2
3      B      1     1         3

Спасибо!

Ответы [ 3 ]

1 голос
/ 26 июня 2019

Вы также можете использовать if_else для замены value на 0, когда он не входит в желаемую группу, как показано ниже. dplyr здесь не обязательно (используйте base::ifelse и избегайте mutate)

library(tidyverse)
df1 <- structure(list(time = c(0L, 0L, 0L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L), group = c("A", "B", "A", "A", "B", "B", "B", "A", "A", "A", "A", "B"), value = c(0L, 0L, 0L, 0L, 1L, 0L, 1L, 1L, 1L, -1L, 0L, 1L)), class = "data.frame", row.names = c(NA, -12L))
df1 %>%
  mutate(
    cumsum_A = cumsum(if_else(group == "A", value, 0L)),
    cumsum_B = cumsum(if_else(group == "B", value, 0L))
  )
#>    time group value cumsum_A cumsum_B
#> 1     0     A     0        0        0
#> 2     0     B     0        0        0
#> 3     0     A     0        0        0
#> 4     1     A     0        0        0
#> 5     1     B     1        0        1
#> 6     1     B     0        0        1
#> 7     2     B     1        0        2
#> 8     2     A     1        1        2
#> 9     2     A     1        2        2
#> 10    2     A    -1        1        2
#> 11    3     A     0        1        2
#> 12    3     B     1        1        3

Создано в 2019-06-25 пакетом Представление (v0.3.0)

1 голос
/ 26 июня 2019

Сначала вы можете узнать значения unique и использовать цикл sapply / lapply над ними, чтобы условно вычислить cumsum для каждого из них.

unique_val <- unique(df$group)
df[paste0("cumsum_", unique_val)] <- lapply(unique_val, 
                     function(x) cumsum((df$group == x) * df$value))

df
#   time group value cumsum_A cumsum_B
#1     0     A     0        0        0
#2     0     B     0        0        0
#3     0     A     0        0        0
#4     1     A     0        0        0
#5     1     B     1        0        1
#6     1     B     0        0        1
#7     2     B     1        0        2
#8     2     A     1        1        2
#9     2     A     1        2        2
#10    2     A    -1        1        2
#11    3     A     0        1        2
#12    3     B     1        1        3
0 голосов
/ 26 июня 2019

Вот вариант с table и colCumsums

library(matrixStats)
nm1 <- paste0("cumsum_", unique(df1$group))
df1[nm1] <- colCumsums(table(seq_len(nrow(df1)),df1$group) * df1$value)
df1
#   time group value cumsum_A cumsum_B
#1     0     A     0        0        0
#2     0     B     0        0        0
#3     0     A     0        0        0
#4     1     A     0        0        0
#5     1     B     1        0        1
#6     1     B     0        0        1
#7     2     B     1        0        2
#8     2     A     1        1        2
#9     2     A     1        2        2
#10    2     A    -1        1        2
#11    3     A     0        1        2
#12    3     B     1        1        3

Или другой вариант model.matrix

colCumsums((model.matrix(~  group -1, df1)) * df1$value)

Или model.matrix с tidyverse

library(tidyverse)
df1 %>%
    model.matrix( ~group - 1, .) %>%
    as_tibble %>% 
    mutate_all(~ cumsum(. * df1$value)) %>% 
    rename_all(~ str_replace(., "group", "cumsum")) %>%
    bind_cols(df1, .)
#    time group value cumsumA cumsumB
#1     0     A     0       0       0
#2     0     B     0       0       0
#3     0     A     0       0       0
#4     1     A     0       0       0
#5     1     B     1       0       1
#6     1     B     0       0       1
#7     2     B     1       0       2
#8     2     A     1       1       2
#9     2     A     1       2       2
#10    2     A    -1       1       2
#11    3     A     0       1       2
#12    3     B     1       1       3

или с использованием count вместе с spread

df1 %>%
      mutate(rn = row_number()) %>%
      dplyr::count(group, rn) %>% 
      mutate(group = str_c("cumsum", group)) %>%
      spread(group, n, fill = 0) %>% 
      mutate_at(-1, ~ cumsum(. * df1$value)) %>% 
      select(-rn) %>%
      bind_cols(df1, .)

data

df1 <- structure(list(time = c(0L, 0L, 0L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
3L, 3L), group = c("A", "B", "A", "A", "B", "B", "B", "A", "A", 
"A", "A", "B"), value = c(0L, 0L, 0L, 0L, 1L, 0L, 1L, 1L, 1L, 
-1L, 0L, 1L)), class = "data.frame", row.names = c(NA, -12L))
...