R:apply vs do.call

时间:2018-06-06 09:37:51

标签: r apply do.call

我刚刚阅读了@David Arenburg的个人资料,并找到了一些关于如何培养良好的R编程技巧/习惯的有用技巧,尤其令我印象深刻。我一直认为R中的apply函数是使用数据帧的基石,但他写道:

  

如果您正在使用data.frames,请忘记有一个名为的函数   适用 - 无论你做什么 - 都不要使用它。特别是边际为1   (此功能的唯一好用途是在矩阵上运行   列 - 边缘2)。

     

一些好的选择:?do.call,?pmax / pmin,?max.col,   ?rowSums / rowMeans / etc,令人敬畏的matrixStats包(for   matrices),?rowsum等等

有人可以向我解释一下吗?为什么申请职能不受欢迎?

3 个答案:

答案 0 :(得分:4)

  • apply(DF, 1, f)DF的每一行转换为向量,然后将该向量传递给f。如果DF是字符串和数字的混合,那么该行将被转换为字符向量,然后将其传递给f,以便例如apply(iris, 1, function(x) sum(x[-5]))即使行也不会有效iris[i, -5]包含所有数字元素。该行将转换为字符串,您无法对字符串求和。另一方面,apply(iris[-5], 1, sum)的工作方式与rowSums(iris[-5])相同。

  • 如果f生成一个向量,则结果是矩阵,而不是另一个数据帧;而且,结果是你可能期望的转置。此

    apply(BOD, 1, identity)
    

    给出以下内容而不是给予BOD

           [,1] [,2] [,3] [,4] [,5] [,6]
    Time    1.0  2.0    3    4  5.0  7.0
    demand  8.3 10.3   19   16 15.6 19.8
    

    很多年前,Hadley Wickham做了post iapply,在iapply(mat, 1, identity)返回mat而不是t(mat)的意义上是幂等的,mat是一个矩阵。最近,他的plyr包可以写:

    library(plyr)
    ddplyr(BOD, 1, identity)
    

    并将BOD作为数据框返回。

另一方面,apply(BOD, 1, sum)将提供与rowSums(BOD)相同的结果,而apply(BOD, 1, f)可能对f生成标量的函数f有用, sum / rowSums案例中没有对应词。此外,如果f生成一个向量而你不介意矩阵结果,你可以自己调换apply的输出,虽然很丑,但它会起作用。

答案 1 :(得分:2)

我认为作者的意思是,你应该使用预构建/矢量化函数(因为它更容易),如果可以并且避免应用(因为原则上它是for循环并且需要更长时间):

library(microbenchmark)

d <- data.frame(a = rnorm(10, 10, 1),
                b = rnorm(10, 200, 1))

# bad - loop
microbenchmark(apply(d, 1, function(x) if (x[1] < x[2]) x[1] else x[2]))

# good - vectorized but same result
microbenchmark(pmin(d[[1]], d[[2]])) # use double brackets!

# edited:
# -------
# bad: lapply
microbenchmark(data.frame(lapply(d, round, 1)))

# good: do.call faster than lapply
microbenchmark(do.call("round", list(d, digits = 1)))

# --------------
# Unit: microseconds
#                                  expr     min    lq     mean  median      uq     max neval
# do.call("round", list(d, digits = 1)) 104.422 107.1 148.3419 134.767 184.524 332.009   100
#                            expr     min       lq     mean  median      uq      max neval
# data.frame(lapply(d, round, 1)) 235.619 243.2055 298.5042 252.353 276.004 1550.265   100
#
#                                  expr    min      lq    mean median       uq     max neval
# do.call("round", list(d, digits = 1)) 96.389 97.5055 113.075 98.175 105.5375 730.954   100
#                            expr     min       lq     mean  median      uq      max neval
# data.frame(lapply(d, round, 1)) 235.619 243.2055 298.5042 252.353 276.004 1550.265   100

答案 2 :(得分:1)

它与R如何存储矩阵和数据帧*有关。您可能知道,data.framelist个向量,也就是说,data.frame中的每一列都是向量。作为一个矢量化语言,最好对向量进行操作,这就是apply边缘为2的原因:这样做你不会在向量上工作,相反,你将跨越不同的向量每次迭代。

据我所知,使用边距为1的apply与使用do.call没有太大区别。虽然后者可能允许更多的使用灵活性。

*此信息应位于manuals