根据使用列的累积和创建的分组过滤 R data.table
Filter R data.table based on groupings created using cumulative sum of a column
我需要一个高效的 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 函数获得相同的结果?谢谢!
我想我已经正确解释了您的要求:
- 您想计算向量(列)的累加和。
- 如果累计总和达到 300,您想将其重置为 0。
- 每次重置为 0 时,您都想说向量的这些值在一个新组中。
- 您想select每组的第一行和最后一行
如果是这种情况,你可以在Rcpp
中编写自己的快速'vectorised'函数
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]
仅使用 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
确保第一组的大小正确
我需要一个高效的 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 函数获得相同的结果?谢谢!
我想我已经正确解释了您的要求:
- 您想计算向量(列)的累加和。
- 如果累计总和达到 300,您想将其重置为 0。
- 每次重置为 0 时,您都想说向量的这些值在一个新组中。
- 您想select每组的第一行和最后一行
如果是这种情况,你可以在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]
仅使用 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
是 组 .N
是当前的行数 子集(等同于nrow(.SD
))-1
确保第一组的大小正确
data.table
的当前子集