R:应用与 do.call
R: apply vs do.call
我刚刚阅读了@David Arenburg 的个人资料,发现了一堆关于如何开发良好的 R 编程的有用技巧 skills/habits,其中一个特别打动了我。我一直认为 R 中的应用函数是使用数据帧的基石,但他写道:
If you are working with data.frames, forget there is a function called
apply- whatever you do - don't use it. Especially with a margin of 1
(the only good usecase for this function is to operate over matrix
columns- margin of 2).
Some good alternatives: ?do.call, ?pmax/pmin, ?max.col,
?rowSums/rowMeans/etc, the awesome matrixStats packages (for
matrices), ?rowsum and many more
有人能给我解释一下吗?为什么应用函数不受欢迎?
我认为作者的意思是你应该使用 pre-built/vectorized 函数(因为它更容易),如果可以并且避免应用(因为原则上它是一个 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
apply(DF, 1, f)
将 DF
的每一行转换为向量,然后将该向量传递给 f。如果 DF
是字符串和数字的混合,那么在将行传递给 f
之前,该行将被转换为字符向量,因此,例如,即使行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)
returns 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
的输出,虽然它很丑陋但它会起作用。
这与R如何存储矩阵和数据帧有关*。你可能知道,一个data.frame
是一个list
的向量,也就是说,data.frame
中的每一列都是一个向量。作为一种矢量化语言,最好在矢量上进行操作,这就是不赞成 margin 为 2 的 apply
的原因:这样做你不会在矢量上工作,相反,你将跨越不同的矢量在每次迭代中。
据我所知,使用带边距 1 的 apply
与使用 do.call
没有太大区别。尽管后者可能允许更多的使用灵活性。
*此信息应位于 manuals.
中的某处
我刚刚阅读了@David Arenburg 的个人资料,发现了一堆关于如何开发良好的 R 编程的有用技巧 skills/habits,其中一个特别打动了我。我一直认为 R 中的应用函数是使用数据帧的基石,但他写道:
If you are working with data.frames, forget there is a function called apply- whatever you do - don't use it. Especially with a margin of 1 (the only good usecase for this function is to operate over matrix columns- margin of 2).
Some good alternatives: ?do.call, ?pmax/pmin, ?max.col, ?rowSums/rowMeans/etc, the awesome matrixStats packages (for matrices), ?rowsum and many more
有人能给我解释一下吗?为什么应用函数不受欢迎?
我认为作者的意思是你应该使用 pre-built/vectorized 函数(因为它更容易),如果可以并且避免应用(因为原则上它是一个 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
apply(DF, 1, f)
将DF
的每一行转换为向量,然后将该向量传递给 f。如果DF
是字符串和数字的混合,那么在将行传递给f
之前,该行将被转换为字符向量,因此,例如,即使行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)
returnsmat
的意义上是幂等的,而不是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
的输出,虽然它很丑陋但它会起作用。
这与R如何存储矩阵和数据帧有关*。你可能知道,一个data.frame
是一个list
的向量,也就是说,data.frame
中的每一列都是一个向量。作为一种矢量化语言,最好在矢量上进行操作,这就是不赞成 margin 为 2 的 apply
的原因:这样做你不会在矢量上工作,相反,你将跨越不同的矢量在每次迭代中。
据我所知,使用带边距 1 的 apply
与使用 do.call
没有太大区别。尽管后者可能允许更多的使用灵活性。
*此信息应位于 manuals.
中的某处