如何在R中使用with()函数而不是apply()函数

如何在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

我正在尝试优化我使用apply()和类似函数(例如lappy()编写的代码)。不幸的是,我没有看到太多的改进,所以我看到了这篇文章,其中一个建议是使用函数with()而不是apply(),这肯定要快得多

我想做的是对矩阵的每一行应用一个用户定义的函数。此函数将行中的数据作为输入,进行一些计算并返回一个带有结果的向量。 我使用apply()函数、with()和矢量化版本的玩具示例:

#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)

这是你真正的功能吗?如果是,那么问题就解决了。如果不是,则取决于实际函数是否完全由已矢量化的操作和函数组成,或者是否可以用矢量化等价物替换

对于根据注释修改的功能:

单行:
fn1
with
不用于将函数应用于每一行。它所做的是将数据框的列附加到您的工作环境中,以便您可以将它们作为变量引用。使用
在这里速度更快的原因是,它使用向量化版本的乘法处理所有变量,同时逐行调用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
}