将大型data.frame的行作为参数传递给R中的函数的最快和最有效的方法
我想在 data.frame 中建立一个新列,myDF这是一个函数为每一行返回的值,该函数getval将该行中的元素作为参数。getval还使用外部向量v1作为参数。例如:
myn = 1000
a = seq(0, 1, length.out = myn)
b = seq(-1, 1, length.out = myn)
myDF = expand.grid(a=a, b=b)
set.seed(13)
v1 = rnorm(100)
getval = function(a, b, v) {
return(sum(a*v + b/2*v))
}
myDF$val = apply(myDF, 1, function(x) {getval(a=x[1], b=x[2], v=v1)})
head(myDF)
# a b val
# 1 0.000000000 -1 3.091267
# 2 0.001001001 -1 3.085078
# 3 0.002002002 -1 3.078889
# 4 0.003003003 -1 3.072700
# 5 0.004004004 -1 3.066512
# 6 0.005005005 -1 3.060323
但这太慢了(这里约 4 秒,但对于更高的会增加很多myn)。
我正在寻找最快的方法来实现这个 - 竞赛!;-)
欢迎所有解决方案(包括并行化?)和包(dplyr,data.table?) - 例如,我真的需要尽可能快的东西myn= 5000。
编辑
实际上,getval不是那么(容易?)可矢量化的......
getval = function(a, b, v) {
return(sum(a/(a/v +1) + b/(b+2) * v))
}
myDF$val = apply(myDF, 1, function(x) {getval(a=x[1], b=x[2], v=v1)})
head(myDF)
# a b val
# 1 0.000000000 -1 6.182533
# 2 0.001001001 -1 6.282782
# 3 0.002002002 -1 6.383424
# 4 0.003003003 -1 6.484682
# 5 0.004004004 -1 6.586980
# 6 0.005005005 -1 6.691260
回答
您应该尽可能努力避免在行上循环。对于您的示例:
getval = function(a, b, v) {
return((a + b / 2) *sum(v))
}
myDF$val1 = getval(myDF$a, myDF$b, v1)
head(myDF)
# a b val val1
#1 0.000000000 -1 3.091267 3.091267
#2 0.001001001 -1 3.085078 3.085078
#3 0.002002002 -1 3.078889 3.078889
#4 0.003003003 -1 3.072700 3.072700
#5 0.004004004 -1 3.066512 3.066512
#6 0.005005005 -1 3.060323 3.060323
您将无法击败这种矢量化解决方案的性能。如果这在 R 中无法实现,请使用 Rcpp 实现所有内容(包括循环)。使用这么简单的功能并不难。
编辑:
这是第二个示例的 Rcpp 函数。由于 Rcpp 糖函数,例如sum.
library(Rcpp)
cppFunction(
"
NumericVector rcpp_geval(const NumericVector a, const NumericVector b, const NumericVector v) {
const double n = a.length();
NumericVector res(n);
for (double i = 0; i < n; ++i) {
res[i] = sum(a[i]/(a[i]/v +1) + b[i]/(b[i]+2) * v);
}
return res;
}
"
)
myDF$val1 <- rcpp_geval(myDF$a, myDF$b, v1)
head(myDF)
# a b val val1
#1 0.000000000 -1 6.182533 6.182533
#2 0.001001001 -1 6.282782 6.282782
#3 0.002002002 -1 6.383424 6.383424
#4 0.003003003 -1 6.484682 6.484682
#5 0.004004004 -1 6.586980 6.586980
#6 0.005005005 -1 6.691260 6.691260
THE END
二维码