我有一个数据框:
id <- c(rep(1, 4), rep(2, 3), rep(3, 2), 4)
rate <- c(rep(1, 3), NA, 0.5, 0.6, NA, 0.7, NA, NA)
df <- data.frame(id, rate)
我需要根据以下条件替换NA
:
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)
if (is.nan(mrate)) {
df$rate[i] <- 1
} else {
df$rate[i] <- mrate
}
}
}
显然for
循环对于行数> 200K的大数据帧来说太慢了。如何在不使用for
循环的情况下使用更快的方法?
谢谢!
答案 0 :(得分:5)
这是使用data.table
s:
library(data.table)
dt <- data.table( df, key = "id" )
dt[ , rate := ifelse( is.na(rate), round( mean(rate, na.rm=TRUE), 1), rate ), by = id ]
dt[ is.na(rate), rate := 1 ]
dt
id rate
1: 1 1.0
2: 1 1.0
3: 1 1.0
4: 1 1.0
5: 2 0.5
6: 2 0.6
7: 2 0.6
8: 3 0.7
9: 3 0.7
10: 4 1.0
我不确定,ifelse
是否可以/应该避免。
答案 1 :(得分:4)
正如我的评论中所提到的,R中的for
循环并不是特别慢。但是,通常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]]
...
但是,我仍然在大型data.frame上的df$id[i]
处进行了一个子集。相反,使用实现拆分 - 应用 - 组合策略的工具之一是个好主意。另外,让我们编写一个函数,它接受一个值和一个预先计算的组平均值并做正确的事情:
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,往往以牺牲清晰度为代价!
答案 2 :(得分:3)
我不确定这确切地回答了OP的问题,但是对于后来读到这个问题的其他人来说,除了实际对数据进行子集化之外,还有一种不同且更快的方法对数据子集执行计算:向量数学。 人群中的工程师会知道我在说什么。
不是子集,而是分配一个非常快速的函数来创建一个身份向量,并将数据乘以身份。
现在,对于所有情况来说,这并不快。有些情况下,矢量化函数实际上比项目显式函数慢,这完全取决于您的特定应用程序。 [在此处插入您选择的O-notation咆哮。]
以下是我们如何为这种情况进行矢量数学实现:
# Create the NA identity vector.
na_identity <- is.na(df$rate)
# Initialize the final data frame.
# This is for non-destructive purposes.
df_revised <- df
# Replace all NA occurrences in final
# data frame with zero values.
df_revised$rate[na_identity] <- 0
# Loop through each unique [id]
# value in the data.
# Create an identity vector for the
# current ID, calculate the mean
# rate for that ID (replacing NaN with 1),
# and insert the mean for any NA values
# associated with that ID.
for (i in unique(df$id)){
id_identity <- df$id==i
id_mean <- sum(df_revised$rate * id_identity * !na_identity) / sum(id_identity * !na_identity)
if(is.nan(id_mean)){id_mean <- 1}
df_revised$rate <- df_revised$rate + id_mean * id_identity * na_identity
}
# id rate
# 1 1 1.00
# 2 1 1.00
# 3 1 1.00
# 4 1 1.00
# 5 2 0.50
# 6 2 0.60
# 7 2 0.55
# 8 3 0.70
# 9 3 0.70
# 10 4 1.00
从矢量数学角度来看,此代码易于阅读。在这个小例子中,代码非常快,但循环时间直接随着唯一ID值的数量而增加。我不确定这是否是OP更大应用程序的正确方法,但该解决方案可行且理论上合理,无需复杂且难以读取的逻辑块。