-->

适用于()是缓慢的 - 如何使它更快,我有什么办法?(apply() is slow - how t

2019-07-17 11:00发布

我有一个相当大的数据帧,约10百万行。 它列xy ,和我要的是计算

hypot <- function(x) {sqrt(x[1]^2 + x[2]^2)}

每一行。 使用apply它需要占用大量的时间(约5分钟,从较低的大小插值)和内存。

但是,这似乎是我太重要了,所以我尝试了不同的东西:

  • 编译hypot约10%的功能降低的时间
  • 使用函数从plyr大大增加了运行时间。

什么是做这个事情的最快方法?

Answer 1:

怎么样with(my_data,sqrt(x^2+y^2))

set.seed(101)
d <- data.frame(x=runif(1e5),y=runif(1e5))

library(rbenchmark)

两种不同的每行功能,矢量化的一个趁势:

hypot <- function(x) sqrt(x[1]^2+x[2]^2)
hypot2 <- function(x) sqrt(sum(x^2))

尝试过这些编译:

library(compiler)
chypot <- cmpfun(hypot)
chypot2 <- cmpfun(hypot2)

benchmark(sqrt(d[,1]^2+d[,2]^2),
          with(d,sqrt(x^2+y^2)),
          apply(d,1,hypot),
          apply(d,1,hypot2),
          apply(d,1,chypot),
          apply(d,1,chypot2),
          replications=50)

结果:

                       test replications elapsed relative user.self sys.self
5       apply(d, 1, chypot)           50  61.147  244.588    60.480    0.172
6      apply(d, 1, chypot2)           50  33.971  135.884    33.658    0.172
3        apply(d, 1, hypot)           50  63.920  255.680    63.308    0.364
4       apply(d, 1, hypot2)           50  36.657  146.628    36.218    0.260
1 sqrt(d[, 1]^2 + d[, 2]^2)           50   0.265    1.060     0.124    0.144
2  with(d, sqrt(x^2 + y^2))           50   0.250    1.000     0.100    0.144

如预期的那样with()溶液和点菜泰勒瑞科尔列索引溶液基本上是相同的; hypot2是快两倍的原始hypot (但仍慢大约150倍,比所述向量化的解决方案)。 前面已经指出的OP,编译并没有太大帮助。



Answer 2:

虽然本Bolkers答案是全面的,我会解释其他原因,以避免apply上data.frames。

apply将您的转换data.frame到矩阵。 这将创建(时间和浪费内存)的副本,以及可能造成意想不到的类型转换。

假设你有10万行的数据,我建议你看一下data.table包,可以让你在内存和时间上有效地做事情。


例如,使用tracemem

x <- apply(d,1, hypot2)
tracemem[0x2f2f4410 -> 0x2f31b8b8]: as.matrix.data.frame as.matrix apply 

这是更糟糕的,如果你然后分配到一列d

d$x <- apply(d,1, hypot2)
tracemem[0x2f2f4410 -> 0x2ee71cb8]: as.matrix.data.frame as.matrix apply 
tracemem[0x2f2f4410 -> 0x2fa9c878]: 
tracemem[0x2fa9c878 -> 0x2fa9c3d8]: $<-.data.frame $<- 
tracemem[0x2fa9c3d8 -> 0x2fa9c1b8]: $<-.data.frame $<- 

4份! - 拥有1000万行,这可能会来,咬你somepoint。

如果我们用with ,没有copying参与,如果我们分配到一个向量

y <- with(d, sqrt(x^2 + y^2))

但会有,如果我们在data.frame指派到一列d

d$y <- with(d, sqrt(x^2 + y^2))
tracemem[0x2fa9c1b8 -> 0x2faa00d8]: 
tracemem[0x2faa00d8 -> 0x2faa0f48]: $<-.data.frame $<- 
tracemem[0x2faa0f48 -> 0x2faa0d08]: $<-.data.frame $<- 

现在,如果你使用data.table:=参考分配(无复制)

 library(data.table)
 DT <- data.table(d)



tracemem(DT)
[1] "<0x2d67a9a0>"
DT[,y := sqrt(x^2 + y^2)]

无份!


也许我会在这里纠正,但另一个存储需要考虑的问题是, sqrt(x^2+y^2))将创建4个临时变量(内部) x^2y^2x^2 + y^2和然后sqrt(x^2 + y^2))

下面会慢一些,但只涉及创建两个变量。

 DT[, rowid := .I] # previous option: DT[, rowid := seq_len(nrow(DT))]
 DT[, y2 := sqrt(x^2 + y^2), by = rowid]


Answer 3:

R的矢量化,所以你可以使用下面,在你自己的,当然基质堵塞

X = t(matrix(1:4, 2, 2))^2
>      [,1] [,2]
 [1,]    1    4
 [2,]    9   16

rowSums(X)^0.5

尼斯和高效的:)



文章来源: apply() is slow - how to make it faster or what are my alternatives?
标签: r apply r-faq