我有一个非常大的数据框,大约有1000万行。它有x
和y
列,我想要的是计算
hypot <- function(x) {sqrt(x[1]^2 + x[2]^2)}
每行。使用apply
需要花费大量时间(大约5分钟,从较小尺寸插值)和内存。
但对我来说似乎太过分了,所以我尝试了不同的东西:
hypot
函数可将时间缩短约10%plyr
中的函数会大大增加运行时间。做这件事的最快方法是什么?
答案 0 :(得分:21)
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()
解决方案和列索引解决方案与Tyler Rinker基本相同; hypot2
的速度是原始hypot
的两倍(但仍然比矢量化解决方案慢约150倍)。正如OP已经指出的那样,编译并没有多大帮助。
答案 1 :(得分:10)
虽然Ben Bolkers的回答非常全面,但我会解释其他原因,以避免在data.frames上使用apply
。
apply
会将您的data.frame
转换为矩阵。这将创建一个副本(浪费时间和内存),并可能导致意外的类型转换。
鉴于您有1000万行数据,我建议您查看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万行,可能会在某些时候咬你。
如果我们使用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]
答案 2 :(得分:4)
R是矢量化的,所以你可以使用以下内容,当然插入你自己的矩阵
X = t(matrix(1:4, 2, 2))^2
> [,1] [,2]
[1,] 1 4
[2,] 9 16
rowSums(X)^0.5
美好而高效:)