7

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

如果您正在使用 data.frames,请忘记有一个名为 apply 的函数——无论你做什么——都不要使用它。尤其是边距为 1(此函数唯一好的用例是对矩阵列进行操作 - 边距为 2)。

一些不错的选择:?do.call、?pmax/pmin、?max.col、?rowSums/rowMeans/etc、很棒的 matrixStats 包(用于矩阵)、?rowsum 等等

有人可以向我解释一下吗?为什么应用功能不受欢迎?

4

3 回答 3

5
  • 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发表 了在返回矩阵iapply的意义上说是幂等的,而不是矩阵。最近用他的 plyr 包可以写:iapply(mat, 1, identity)matt(mat)mat

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

    BOD作为数据框返回。

另一方面,apply(BOD, 1, sum)将给出相同的结果,rowSums(BOD)并且apply(BOD, 1, f)可能ff产生标量且没有对应物的函数有用,例如sum/ rowSums情况。此外,如果f产生一个向量并且您不介意矩阵结果,您可以转置apply自己的输出,尽管它很难看。

于 2018-06-06T11:29:00.340 回答
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
于 2018-06-06T09:58:56.520 回答
1

它与 R 如何存储矩阵和数据帧*有关。你可能知道,adata.frame是一个list向量,也就是说,其中的每一列data.frame都是一个向量。作为一种矢量化语言,最好对矢量进行操作,这就是不赞成apply使用边距 2 的原因:这样做您将不会处理矢量,而是在每次迭代时跨越不同的矢量。

据我所知,使用applywith margin 1 与使用do.call. 尽管后者可能允许更多的使用灵活性。

*此信息应在手册中的某处。

于 2018-06-06T11:51:48.327 回答