我在r中有一个很大的性能问题。我写了一个迭代data.frame对象的函数。它只是简单地向data.frame添加一个新列并累积一些东西。(操作简单)。data.frame大约有850K行。我的电脑还在工作(大约10小时了),我不知道运行时间。
dayloop2 <- function(temp){
for (i in 1:nrow(temp)){
temp[i,10] <- i
if (i > 1) {
if ((temp[i,6] == temp[i-1,6]) & (temp[i,3] == temp[i-1,3])) {
temp[i,10] <- temp[i,9] + temp[i-1,10]
} else {
temp[i,10] <- temp[i,9]
}
} else {
temp[i,10] <- temp[i,9]
}
}
names(temp)[names(temp) == "V10"] <- "Kumm."
return(temp)
}
有什么办法可以加快这次行动吗?
如果你在使用for循环,你很可能会像编写C或Java或其他语言一样编写R。正确向量化的R代码非常快。
以这两段简单的代码为例,按顺序生成一个10,000个整数的列表:
第一个代码示例是如何使用传统的编码范式编写循环代码。它需要28秒才能完成
system.time({
a <- NULL
for(i in 1:1e5)a[i] <- i
})
user system elapsed
28.36 0.07 28.61
你可以通过简单的预分配内存来获得几乎100倍的性能提升:
system.time({
a <- rep(1, 1e5)
for(i in 1:1e5)a[i] <- i
})
user system elapsed
0.30 0.00 0.29
但是使用冒号操作符的基本R向量操作:这个操作几乎是瞬时的:
system.time(a <- 1:1e5)
user system elapsed
0 0 0
处理数据。表是一个可行的选择:
n <- 1000000
df <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
colnames(df) <- paste("col", 1:9, sep = "")
library(data.table)
dayloop2.dt <- function(df) {
dt <- data.table(df)
dt[, Kumm. := {
res <- .I;
ifelse (res > 1,
ifelse ((col6 == shift(col6, fill = 0)) & (col3 == shift(col3, fill = 0)) ,
res <- col9 + shift(res)
, # else
res <- col9
)
, # else
res <- col9
)
}
,]
res <- data.frame(dt)
return (res)
}
res <- dayloop2.dt(df)
m <- microbenchmark(dayloop2.dt(df), times = 100)
#Unit: milliseconds
# expr min lq mean median uq max neval
#dayloop2.dt(df) 436.4467 441.02076 578.7126 503.9874 575.9534 966.1042 10
如果忽略条件过滤可能带来的收益,它会非常快。显然,如果您可以在数据子集上进行计算,则会有所帮助。
如果你在使用for循环,你很可能会像编写C或Java或其他语言一样编写R。正确向量化的R代码非常快。
以这两段简单的代码为例,按顺序生成一个10,000个整数的列表:
第一个代码示例是如何使用传统的编码范式编写循环代码。它需要28秒才能完成
system.time({
a <- NULL
for(i in 1:1e5)a[i] <- i
})
user system elapsed
28.36 0.07 28.61
你可以通过简单的预分配内存来获得几乎100倍的性能提升:
system.time({
a <- rep(1, 1e5)
for(i in 1:1e5)a[i] <- i
})
user system elapsed
0.30 0.00 0.29
但是使用冒号操作符的基本R向量操作:这个操作几乎是瞬时的:
system.time(a <- 1:1e5)
user system elapsed
0 0 0
在R中,您通常可以通过使用apply族函数来加速循环处理(在您的示例中,可能是复制)。看一下提供进度条的plyr包。
另一种选择是完全避免循环,用向量化算法代替它们。我不确定你到底在做什么,但你可能可以将你的函数一次性应用到所有行:
temp[1:nrow(temp), 10] <- temp[1:nrow(temp), 9] + temp[0:(nrow(temp)-1), 10]
这将会快得多,然后你可以用你的条件过滤行:
cond.i <- (temp[i, 6] == temp[i-1, 6]) & (temp[i, 3] == temp[i-1, 3])
temp[cond.i, 10] <- temp[cond.i, 9]
向量化算术需要更多的时间和思考问题,但有时可以节省几个数量级的执行时间。
这里的答案很好。有一个小方面没有被提及,那就是这个问题说的是“我的电脑还在工作(现在大约10小时了),我不知道运行时间”。在开发时,我总是将以下代码放入循环中,以了解更改如何影响速度,并监视完成所需的时间。
dayloop2 <- function(temp){
for (i in 1:nrow(temp)){
cat(round(i/nrow(temp)*100,2),"% \r") # prints the percentage complete in realtime.
# do stuff
}
return(blah)
}
也可以使用lapply。
dayloop2 <- function(temp){
temp <- lapply(1:nrow(temp), function(i) {
cat(round(i/nrow(temp)*100,2),"% \r")
#do stuff
})
return(temp)
}
如果循环中的函数非常快,但循环的数量很大,那么可以考虑偶尔打印一次,因为打印到控制台本身会有开销。如。
dayloop2 <- function(temp){
for (i in 1:nrow(temp)){
if(i %% 100 == 0) cat(round(i/nrow(temp)*100,2),"% \r") # prints every 100 times through the loop
# do stuff
}
return(temp)
}