【问题标题】:Filter R data.table based on groupings created using cumulative sum of a column根据使用列的累积总和创建的分组过滤 R data.table
【发布时间】:2019-01-18 16:06:28
【问题描述】:

我需要一个有效的 data.table 解决方案来过滤到每 300 个列的累积总和的第一个和最后一个实例。我的真实数据集是数百万行,所以我不是在寻找循环解决方案。

#Example data:
  dt <- data.table(idcolref=c(1:1000),y=rep(10,1000))

下面是一个执行我想要的示例循环,但它对于大型 data.table 来说太慢了。

###example of a loop that produces the result I want but is too slow
  library(foreach)
  dt[,grp:=1,]
  dt[,cumsum:=0,]
  grp <- 1
  foreach(a=2:nrow(dt))%do%{
    dt[a,"cumsum"]<-dt[a,"y"]+dt[a-1,"cumsum"]
    if(dt[a,"cumsum"]>300){
      dt[a,"grp"] <- grp
      grp <- grp+1
      dt[a,"cumsum"]<-0
    }else{
      dt[a,"grp"]<-dt[a-1,"grp"]
    }
  }
  dt.desired <- foreach(a=2:nrow(dt),.combine=rbind)%do%{
    if(dt[a,"grp"]!=dt[a-1,"grp"]){
      dt[c(a-1,a),]
    }
  }
  dt.desired <- rbind(dt[1,],dt.desired)
  dt.desired <- rbind(dt.desired,dt[nrow(dt),])

如何使用快速矢量化 data.table 函数获得相同的结果?谢谢!

【问题讨论】:

  • x 是你的组吗?
  • 不行,我需要根据x(或y)生成组
  • 你能不能再清楚一点,你的id栏是哪个?
  • 您确定您的dt.desired 正确吗?如果您在dt[, id := .I] 上放置一个索引列,则表明第一组是第 1 行和第 122 行。?
  • 为什么你的输出的第一个值是 0?不应该是 10 吗?

标签: r filter data.table cumsum


【解决方案1】:

我想我已经正确解释了您的要求:

  1. 您想计算一个向量(列)的累积和。
  2. 如果累积总和达到 300,您希望将其重置回 0。
  3. 每次重置为 0 时,您都想说向量的这些值在一个新组中。
  4. 您要选择每个组的第一行和最后一行

如果是这种情况,您可以在Rcpp 中编写自己的快速“矢量化”函数

library(data.table)

dt <- data.table(x=rep(5,1e7),y=rep(10,1e7))
## adding a row index to keep track of which rows are returned
dt[, id := .I]

library(Rcpp)

cppFunction('Rcpp::NumericVector findGroupRows(Rcpp::NumericVector x) {

  int cumsum = 0;
  int grpCounter = 0;
  size_t n = x.length();
  Rcpp::NumericVector groupedCumSum(n);

  for ( size_t i = 0; i < n; i++) {
    cumsum += x[i];
    if (cumsum > 300) {
      cumsum = 0;
      grpCounter++;
    }
    groupedCumSum[i] = grpCounter;
  }
  return groupedCumSum;
}')

dt[, grp := findGroupRows(y)]

dt[ dt[, .I[c(1, .N)], by = grp]$V1]

【讨论】:

  • dt[ dt[, .I[c(1, .N)], by = grp2]$V1] 非常简洁。不过,您的群组少了一个元素(31 个成员而不是 30 个)
  • cpp 的速度非常快。不错。
  • @StefanF - 你确定我差一点吗?
  • @NealBarsch - 是的。值得记住的是,“向量化”几乎是一个用编译语言编写的循环,通常是 CC++fortran
  • 试试table(dt[, .N[[1]], by = "grp"]$V1),至少在我的机器上它给了我错误的大小(第一组是30个元素,除了最后一个是31个)。在我编辑之前,我的原始解决方案遇到了相反的问题(例如,我的第一组太小了)。
【解决方案2】:

仅使用 data.table 和基本 R 函数的简单解决方案:

dt[, grp2 := (cumsum(y) - 1) %/% 300]  

# straight forward solution:
dt[, .SD[c(1, .N)], by = "grp"]

# more efficient for large datasets, as suggested by SymbolixAU
dt[ dt[, .I[c(1, .N)], by = "grp"]$V1]

# check if your groups are of the correct size
table(dt[, .N[[1]], by = "grp"]$V1)
  • %/% 是整数除法运算符
  • .SD 是当前 data.table 按组的子集
  • .N 是当前行数 子集(等同于nrow(.SD))
  • -1 确保第一组的大小正确

【讨论】:

    猜你喜欢
    • 2020-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-08
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    • 2021-05-19
    相关资源
    最近更新 更多