我有一个相当大的数据帧,约10百万行。 它列x
和y
,和我要的是计算
hypot <- function(x) {sqrt(x[1]^2 + x[2]^2)}
每一行。 使用apply
它需要占用大量的时间(约5分钟,从较低的大小插值)和内存。
但是,这似乎是我太重要了,所以我尝试了不同的东西:
- 编译
hypot
约10%的功能降低的时间 - 使用函数从
plyr
大大增加了运行时间。
什么是做这个事情的最快方法?
我有一个相当大的数据帧,约10百万行。 它列x
和y
,和我要的是计算
hypot <- function(x) {sqrt(x[1]^2 + x[2]^2)}
每一行。 使用apply
它需要占用大量的时间(约5分钟,从较低的大小插值)和内存。
但是,这似乎是我太重要了,所以我尝试了不同的东西:
hypot
约10%的功能降低的时间 plyr
大大增加了运行时间。 什么是做这个事情的最快方法?
怎么样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,编译并没有太大帮助。
虽然本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^2
, y^2
, x^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]
R的矢量化,所以你可以使用下面,在你自己的,当然基质堵塞
X = t(matrix(1:4, 2, 2))^2
> [,1] [,2]
[1,] 1 4
[2,] 9 16
rowSums(X)^0.5
尼斯和高效的:)