我正在尝试使用foreach
包将函数应用于r中的多个组/标识。通过%dopar%
使用并行处理需要花费很多时间,所以我想知道是否可以通过apply
或其他软件包来运行c++
或rcpp
中的for循环部分使它更快。我对c++
或其他可以做到这一点的软件包并不熟悉,所以我希望了解这是否可行。示例代码如下。我的实际功能更长,有20多个输入,并且运行时间比我发布的时间还要长
感谢您的帮助。
编辑:
我意识到最初的问题很模糊,所以我会尝试做得更好。我有一个表,其中包含按组的时间序列数据。每个组有> 10K行。我已经通过c++
在rcpp
中编写了一个函数,该函数按组过滤表并应用函数。我想遍历唯一的组,并像rbind
那样使用rcpp
合并结果,以使其运行更快。请参见下面的示例代码(我的实际功能更长)
library(data.table)
library(inline)
library(Rcpp)
library(stringi)
library(Runuran)
# Fake data
DT <- data.table(Group = rep(do.call(paste0, Map(stri_rand_strings, n=10, length=c(5, 4, 1),
pattern = c('[A-Z]', '[0-9]', '[A-Z]'))), 180))
df <- DT[order(Group)][
, .(Month = seq(1, 180, 1),
Col1 = urnorm(180, mean = 500, sd = 1, lb = 5, ub = 1000),
Col2 = urnorm(180, mean = 1000, sd = 1, lb = 5, ub = 1000),
Col3 = urnorm(180, mean = 300, sd = 1, lb = 5, ub = 1000)),
by = Group
]
# Rcpp function
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::export]]
DataFrame testFunc(DataFrame df, StringVector ids, double var1, double var2) {
// Filter by group
using namespace std;
StringVector sub = df["Group"];
std::string level = Rcpp::as<std::string>(ids[0]);
Rcpp::LogicalVector ind(sub.size());
for (int i = 0; i < sub.size(); i++){
ind[i] = (sub[i] == level);
}
// Access the columns
CharacterVector Group = df["Group"];
DoubleVector Month = df["Month"];
DoubleVector Col1 = df["Col1"];
DoubleVector Col2 = df["Col2"];
DoubleVector Col3 = df["Col3"];
// Create calculations
DoubleVector Cola = Col1 * (var1 * var2);
DoubleVector Colb = Col2 * (var1 * var2);
DoubleVector Colc = Col3 * (var1 * var2);
DoubleVector Cold = (Cola + Colb + Colc);
// Result summary
std::string Group_ID = level;
double SumCol1 = sum(Col1);
double SumCol2 = sum(Col2);
double SumCol3 = sum(Col3);
double SumColAll = sum(Cold);
// return a new data frame
return DataFrame::create(_["Group_ID"]= Group_ID, _["SumCol1"]= SumCol1,
_["SumCol2"]= SumCol2, _["SumCol3"]= SumCol3, _["SumColAll"]= SumColAll);
}
# Test function
Rcpp::sourceCpp('sample.cpp')
testFunc(df, ids = "BFTHU1315C", var1 = 24, var2 = 76) # ideally I would like to loop through all groups (unique(df$Group))
# Group_ID SumCol1 SumCol2 SumCol3 SumColAll
# 1 BFTHU1315C 899994.6 1798561 540001.6 5907129174
谢谢。
答案 0 :(得分:2)
我建议重新考虑我们的方法。我认为您的测试数据集与实际数据集相当,具有3e8行。我估计大约有10 GB的数据。您似乎会对这些数据执行以下操作:
group_b(ID)
,但是数据集中只剩下一个ID 对我来说,这似乎效率很低。内存使用情况。一般来说,对于此类问题,您需要“共享内存并行性”,但是foreach
仅给您“进程并行性”。进程并行性的缺点是它增加了内存成本。
此外,您将丢弃存在于基本R / dplyr / data.table / SQL引擎/中的所有分组和聚合代码。...或者您在此处阅读您的问题的任何人都不太可能能够改进这些现有代码库。
我的建议:
dplyr
/ mutate
/ group_by
的{{1}}管道。summarize
一起工作,data.table
已知更快,并且可以通过OpenMP提供“共享内存并行处理”。使其更加明确。这里是仅data.table
的解决方案:
library(data.table)
library(stringi)
# Fake data
set.seed(42)
var1 <- 24
var2 <- 76
DT <- data.table(Group = rep(do.call(paste0, Map(stri_rand_strings, n=10, length=c(5, 4, 1),
pattern = c('[A-Z]', '[0-9]', '[A-Z]'))), 180))
setkey(df, Group)
df <- DT[order(Group)][
, .(Month = seq(1, 180, 1),
Col1 = rnorm(180, mean = 500, sd = 1),
Col2 = rnorm(180, mean = 1000, sd = 1),
Col3 = rnorm(180, mean = 300, sd = 1)),
by = Group
][, c("Cola", "Colb", "Colc") := .(Col1 * (var1 * var2),
Col2 * (var1 * var2),
Col3 * (var1 * var2))
][, Cold := Cola + Colb + Colc]
# aggregagation
df[, .(SumCol1 = sum(Col1),
SumCol2 = sum(Col2),
SumCol3 = sum(Col3),
SumColAll = sum(Cold)), by = Group]
我正在通过引用添加计算列。聚合步骤使用data.table
提供的分组功能。如果您的汇总更为复杂,则还可以使用以下函数:
# aggregation function
mySum <- function(Col1, Col2, Col3, Cold) {
list(SumCol1 = sum(Col1),
SumCol2 = sum(Col2),
SumCol3 = sum(Col3),
SumColAll = sum(Cold))
}
df[, mySum(Col1, Col2, Col3, Cold), by = Group]
如果使用C ++时聚合可能更快(sum
之类的情况下不是!),您甚至可以使用:
# aggregation function in C++
Rcpp::cppFunction('
Rcpp::List mySum(Rcpp::NumericVector Col1,
Rcpp::NumericVector Col2,
Rcpp::NumericVector Col3,
Rcpp::NumericVector Cold) {
double SumCol1 = Rcpp::sum(Col1);
double SumCol2 = Rcpp::sum(Col2);
double SumCol3 = Rcpp::sum(Col3);
double SumColAll = Rcpp::sum(Cold);
return Rcpp::List::create(Rcpp::Named("SumCol1") = SumCol1,
Rcpp::Named("SumCol2") = SumCol2,
Rcpp::Named("SumCol3") = SumCol3,
Rcpp::Named("SumColAll") = SumColAll);
}
')
df[, mySum(Col1, Col2, Col3, Cold), by = Group]
在所有这些示例中,data.table
会导致摸索和循环,因为您自己这样做不会有任何收获。