如何在R中使用with()函数而不是apply()函数
我正在尝试优化我使用apply()和类似函数(例如lappy()编写的代码)。不幸的是,我没有看到太多的改进,所以我看到了这篇文章,其中一个建议是使用函数with()而不是apply(),这肯定要快得多 我想做的是对矩阵的每一行应用一个用户定义的函数。此函数将行中的数据作为输入,进行一些计算并返回一个带有结果的向量。 我使用apply()函数、with()和矢量化版本的玩具示例:如何在R中使用with()函数而不是apply()函数,r,vectorization,apply,lapply,R,Vectorization,Apply,Lapply,我正在尝试优化我使用apply()和类似函数(例如lappy()编写的代码)。不幸的是,我没有看到太多的改进,所以我看到了这篇文章,其中一个建议是使用函数with()而不是apply(),这肯定要快得多 我想做的是对矩阵的每一行应用一个用户定义的函数。此函数将行中的数据作为输入,进行一些计算并返回一个带有结果的向量。 我使用apply()函数、with()和矢量化版本的玩具示例: #Generate a matrix 10x3 prbl1=matrix(runif(30),nrow=10) pr
#Generate a matrix 10x3
prbl1=matrix(runif(30),nrow=10)
prbl2=data.frame(prbl1)
prbl3=prbl2
#function for the apply()
fn1=function(row){
x=row[1]
y=row[2]
z=row[3]
k1=2*x+3*y+4*z
k2=2*x*3*y*4*z
k3=2*x*y+3*x*z
return(c(k1,k2,k3))
}
#function for the with()
fn2=function(x,y,z){
k1=2*x+3*y+4*z
k2=2*x*3*y*4*z
k3=2*x*y+3*x*z
return(c(k1,k2,k3))
}
#Vectorise fn2
fn3=Vectorize(fn2)
#apply the functions:
rslt1=t(apply(prbl1,1,fn1))
rslt2=t(with(prbl2,fn2(X1,X2,X3)))
rslt2=cbind(rslt2[1:10],rslt2[11:20],rslt2[21:30])
rslt3=t(with(prbl3,fn3(X1,X2,X3)))
所有三个都产生相同的输出,一个矩阵10x3,这就是我想要的。不过,请注意,在rslt2处,我需要绑定结果,因为使用with()的输出是长度为300的向量。我怀疑这是由于函数没有矢量化(如果我理解正确的话)。在rslt3中,我使用的是fn2的矢量化版本,它以预期的方式生成输出
当我比较三者的性能时,我得到:
library(rbenchmark)
benchmark(rslt1=t(apply(prbl1,1,fn1)),
rslt2=with(prbl2,fn2(X1,X2,X3)),
rslt3=with(prbl3,fn3(X1,X2,X3)),
replications=1000000)
test replications elapsed relative user.self sys.self user.child sys.child
1 rslt1 1000000 103.51 7.129 102.63 0.02 NA NA
2 rslt2 1000000 14.52 1.000 14.41 0.01 NA NA
3 rslt3 1000000 123.44 8.501 122.41 0.05 NA NA
其中,使用()而不使用矢量化肯定更快
我的问题:既然rslt2是最有效的方法,有没有一种方法可以让我正确地使用它,而无需在事后绑定结果?它确实起作用,但我觉得编码效率不高 您给出的第一个和第三个函数一次应用一行,因此在您的示例中调用了10次。第二个函数利用了R中的乘法和加法已经向量化的事实,因此不需要使用任何形式的循环或ply函数。该函数只调用一次。如果要使用当前代码,只需将fn2中的
c
更改为cbind
fn2=function(x,y,z){
k1=2*x+3*y+4*z
k2=2*x*3*y*4*z
k3=2*x*y+3*x*z
return(cbind(k1,k2,k3))
}
使用
所做的只是计算列表、data.frame或给定环境中给定的表达式。因此,与(prbl2,fn2(X1,X2,X3))
完全等同于fn2(prbl2$X1,prbl2$X2,prbl2$X3)
这是你真正的功能吗?如果是,那么问题就解决了。如果不是,则取决于实际函数是否完全由已矢量化的操作和函数组成,或者是否可以用矢量化等价物替换
对于根据注释修改的功能:
单行:
fn1with
不用于将函数应用于每一行。它所做的是将数据框的列附加到您的工作环境中,以便您可以将它们作为变量引用。使用
的在这里速度更快的原因是,它使用向量化版本的乘法处理所有变量,同时逐行调用apply。例如,您可以这样调用函数:fn2(prbl1[,1],prbl1[,2],prbl1[,3])
。不需要任何带有
的。非常感谢您的回答。正如您所怀疑的,这不是真正的功能。在计算k1、k2和k3之后,我使用的实际函数将继续运行,并检查它们是否都为正(在本例中,它们始终为正),如果不是,则进行更多的计算,以此类推。我使用的是if()语句,但当我在没有矢量化的情况下执行此操作时,我会收到预期的错误:条件的长度>1,并且只使用第一个元素。有没有办法保留这种形式的函数,使用if()语句并跳过向量化?在这种情况下,通常可以将if
替换为ifelse
。因此,如果你可以发布你的完整函数,我可以更具体地使用if(x==1)y else z
if(x==1,y,z)
而不是if(x==1)y else z
你可以使用if(x==1)y else z
{return(c(k1,k2,k3))}else{k1=5*x+3*y+4*zk2=5*x*3*y*4*zk3=5*x*y+3*x*z如果(k1
fn1 <- function(row){
x <- row[1]
y <- row[2]
z <- row[3]
k1 <- 2*x+3*y+4*z
k2 <- 2*x*3*y*4*z
k3 <- 2*x*y+3*x*z
if (k1>0 & k2>0 &k3>0){
return(cbind(k1,k2,k3))
} else {
k1 <- 5*x+3*y+4*z
k2 <- 5*x*3*y*4*z
k3 <- 5*x*y+3*x*z
if (k1<0 || k2<0 || k3<0) {
return(cbind(0,0,0))
} else {
return(cbind(k1,k2,k3))
}
}
}
fn2 <- function(mat) {
x <- mat[, 1]
y <- mat[, 2]
z <- mat[, 3]
k1 <- 2*x+3*y+4*z
k2 <- 2*x*3*y*4*z
k3 <- 2*x*y+3*x*z
l1 <- 5*x+3*y+4*z
l2 <- 5*x*3*y*4*z
l3 <- 5*x*y+3*x*z
out <- array(0, dim = dim(mat))
useK <- k1 > 0 & k2 > 0 & k3 > 0
useL <- !useK & l1 >= 0 & l2 >= 0 & l3 >= 0
out[useK, ] <- cbind(k1, k2, k3)[useK, ]
out[useL, ] <- cbind(l1, l2, l3)[useL, ]
out
}