【问题标题】:Subset and aggregate an original data.table based on a different column根据不同的列对原始 data.table 进行子集和聚合
【发布时间】:2019-08-07 01:10:21
【问题描述】:

这非常困难,但我正在尝试按照标题所说的进行操作,例如假设我有一个数据表dat,并且我正在尝试计算新列中的累积总和(从第 1 列到第 3 列,当它出现在第二列中的任何组的第 2) 时。

dat = data.table(A=c(1,2,3,1,4,5,1,2,3),B=c(1,1,1,NA,1,NA,2,NA,2),C=c(1,12,24.2,251,2,1,2,3,-1))
dat[,cumsum:=0]

所以数据看起来像

   > dat
   A  B     C
1: 1  1   1.0
2: 2  1  12.0
3: 3  1  24.2
4: 1 NA 251.0
5: 4  1   2.0
6: 5 NA   1.0
7: 1  2   2.0
8: 2 NA   3.0
9: 3  2  -1.0

我希望输出是这样的:

> dat
   A  B     C cumsum
1: 1  1   1.0      1
2: 2  1  12.0      1
3: 3  1  24.2      1
4: 1 NA 251.0      0
5: 4  1   2.0      252
6: 5 NA   1.0      0
7: 1  2   2.0      12
8: 2 NA   3.0      0
9: 3  2  -1.0      15

有没有一种有效的数据表方法来做到这一点?我可以用循环来做到这一点,但这会很慢,我觉得这必须以一种更具可扩展性的方式可行,但我被卡住了。

【问题讨论】:

  • @chinsoon12 已修复,你是对的

标签: r data.table


【解决方案1】:

一种使用非等自连接的可能方法:

dat[, rn := .I]
dat[!is.na(B), cumsum := dat[.SD, on=.(A=B, rn<=rn), sum(x.C), by=.EACHI]$V1]

输出:

   A  B     C cumsum rn
1: 1  1   1.0      1  1
2: 2  1  12.0      1  2
3: 3  1  24.2      1  3
4: 1 NA 251.0      0  4
5: 4  1   2.0    252  5
6: 5 NA   1.0      0  6
7: 1  2   2.0     12  7
8: 2 NA   3.0      0  8
9: 3  2  -1.0     15  9

数据:

dat = data.table(A=c(1,2,3,1,4,5,1,2,3),B=c(1,1,1,NA,1,NA,2,NA,2),C=c(1,12,24.2,251,2,1,2,3,-1))
dat[,cumsum:=0]

编辑:添加受弗兰克回答启发的另一种方法

dat = data.table(A=c(1,2,3,1,4,5,1,2,3),B=c(1,1,1,NA,1,NA,2,NA,2),C=c(1,12,24.2,251,2,1,2,3,-1))
dat[, rn := .I][, cs := cumsum(C), A]
dat[, cumsum := 0][
    !is.na(B), cumsum :=  dat[.SD, on=.(A=B, rn), allow.cartesian=TRUE, roll=TRUE, x.cs]]

【讨论】:

  • 我在我的实际数据上尝试了这个,似乎有问题,所以为了复制它,我将数据更改为:dat = data.table(A=c(1,2,3,1,4,5,1,2,3),B=c(1,1,1,NA,1,NA,2,NA,2),C=c(1,12,24.2,251,2,1,2,3,-1)) 输出结果不正确,我试图弄清楚为什么。我猜您的解决方案要求 B 不能有多个唯一的 A?
  • 在 B 列中的 1 比 A 列中的多的情况下,所需的输出是什么?
  • 我认为这是基准测试后最有效的答案!
  • 有人可以向我解释为什么这会在分组中失效吗?例如。假设我有一个 D 列,我想为每个 D 组执行上述操作...当我尝试以下操作时,我得到了不正确的结果:dat = data.table(A=c(1,2,3,1,4,5,1,2,3),B=c(1,1,1,NA,1,NA,2,NA,2),C=c(1,12,24.2,251,2,1,2,3,-1),D=c(1,1,1,1,2,2,2,2,2)); dat[, rn := 1:.N,by=D][, cs := cumsum(C), .(A,D)]; dat[, cumsum := 0][ !is.na(B), cumsum := dat[.SD, on=.(A=B, rn,D), allow.cartesian=TRUE, roll=TRUE, x.cs]]
【解决方案2】:

与@chinsoon 的答案相同,但使用滚动连接:

dat[, rn := .I]
mDT = dat[.(setdiff(B, NA)), on=.(A), .(rn, v = cumsum(C)), by=.EACHI]

dat[, cumsum := 0]
dat[!is.na(B), cumsum := mDT[.SD, on=.(A=B, rn), roll=TRUE, x.v]]

   A  B     C cumsum rn
1: 1  1   1.0      1  1
2: 2  1  12.0      1  2
3: 3  1  24.2      1  3
4: 1 NA 251.0      0  4
5: 4  1   2.0    252  5
6: 5 NA   1.0      0  6
7: 1  2   2.0     12  7
8: 2 NA   3.0      0  8
9: 3  2  -1.0     15  9

对于B 的每个值,mDT 都有对应的A 行,行号和cumsum。我们通过滚动到最近的行号来查找 cumsum 的最新值。


这是@chinsoon 建议的另一种滚动连接方法:

dat[, rn := .I]
dat[, cs := cumsum(C), by=A]

dat[, cumsum := 0]
dat[ !is.na(B), cumsum := dat[.SD, on=.(A=B, rn), allow.cartesian=TRUE, roll=TRUE, x.cs]] 

【讨论】:

  • 这会更快。第一次 cumsum 可能是dat[, rn := .I][, cs := cumsum(C), A]; dat[, cumsum := 0][ !is.na(B), cumsum := dat[.SD, on=.(A=B, rn), allow.cartesian=TRUE, roll=TRUE, x.cs]]
【解决方案3】:

不是data.table 解决方案,而是使用dplyr 的一种方法

library(dplyr)

dat %>%
   mutate(row = row_number(), 
   cumsum = purrr::map2_dbl(B, row, ~sum(C[A == .x & row <= .y], na.rm = TRUE))) %>%
   select(-row)

#  A  B     C cumsum
#1 1  1   1.0      1
#2 2  1  12.0      1
#3 3  1  24.2      1
#4 1 NA 251.0      0
#5 4  1   2.0    252
#6 5 NA   1.0      0
#7 1  2   2.0     12
#8 2 NA   3.0      0
#9 3  2  -1.0     15

不确定仅针对 B 的非 NA 值计算 sum 是否有效

dat %>%
  mutate(row = row_number(), 
         cumsum = ifelse(is.na(B), 0, 
          purrr::map2_dbl(B, row, ~sum(C[A == .x & row <= .y], na.rm = TRUE)))) %>%
  select(-row)

【讨论】:

  • 测试一下,如果我有第四列 D,我希望为 D 中的每个组完成此操作,我只需添加 dat %>% group_by(D) %>% ... 对吗? (对不起,我对 dplyr 很陌生)
猜你喜欢
  • 1970-01-01
  • 2017-12-23
  • 2021-10-04
  • 2022-01-23
  • 1970-01-01
  • 2023-04-06
  • 2019-11-16
  • 2016-05-01
相关资源
最近更新 更多