正如我在评论中提到的,for
R 中的循环并不是特别慢。但是,循环通常for
表明代码中的其他低效率。在这种情况下,为每一行重复确定的子集操作mean
很可能是最慢的代码位。
for (i in 1:dim(df)[1]) {
if (is.na(df$rate[i])) {
mrate <- round(mean(df$rate[df$id == df$id[i]], na.rm = T), 1) ## This line!
if (is.nan(mrate)) {
df$rate[i] <- 1
} else {
df$rate[i] <- mrate
}
}
}
相反,如果这些组平均值是事先确定的,则循环可以进行快速查找。
foo <- aggregate(df$rate, list(df$id), mean, na.rm=TRUE)
for (i in 1:dim(df)[1]) {
if (is.na(df$rate[i])) {
mrate <- foo$x[foo$Group.1 == df$id[i]]
...
但是,我仍然在df$id[i]
大型 data.frame 上做一个子集。相反,使用实现拆分-应用-组合策略的工具之一是一个好主意。另外,让我们编写一个函数,它采用单个值和预先计算的组平均值并做正确的事情:
myfun <- function(DF) {
avg <- avgs$rate[avgs$id == unique(DF$id)]
if (is.nan(avg)) {
avg <- 1
}
DF$rate[is.na(DF$rate)] <- avg
return (DF)
}
plyr
版本:
library(plyr)
avgs <- ddply(df, .(id), summarise, rate=mean(rate, na.rm=TRUE))
result <- ddply(df, .(id), myfun)
并且可能更快的data.table
版本:
library(data.table)
DT <- data.table(df)
setkey(DT, id)
DT[, avg := mean(rate, na.rm=TRUE), by=id]
DT[is.nan(avg), avg := 1]
DT[, rate := ifelse(is.na(rate), avg, rate)]
这样,我们避免了在 leiu 中添加预先计算的列的所有查找子集,现在可以进行快速高效的逐行查找。可以使用以下方法廉价地删除额外的列:
DT[, avg := NULL]
整个shebang可以写成函数或data.table
表达式。但是,IMO,这通常是以牺牲清晰度为代价的!