R 将NA替换为与相同ID匹配的平均值

R 将NA替换为与相同ID匹配的平均值,r,R,我有一个数据框: 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) 显然,for循环在大于200K行的大数据帧上太慢了。如果不使用for循环,如何使用更快的方法 谢谢 这是一个使用数据的解决方案。表s: library(data.table) dt <- data.table(

我有一个数据框:

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)
显然,
for
循环在大于200K行的大数据帧上太慢了。如果不使用
for
循环,如何使用更快的方法


谢谢

这是一个使用
数据的解决方案。表
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
库(data.table)

dt正如我在评论中提到的,R中的
for
循环并不特别慢。然而,通常
for
循环表示代码中的其他低效。在这种情况下,为确定
平均值而对每行重复的子集操作很可能是代码的最慢位

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
    }
  }
}
plyr
版本:

 library(plyr)
 avgs <- ddply(df, .(id), summarise, rate=mean(rate, na.rm=TRUE))
 result <- ddply(df, .(id), myfun)
 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
表达式。但是,依我看,这往往是以清晰度为代价的

我不确定这是否准确地回答了OP的问题,但对于后来阅读这篇文章的其他人来说,有一种对数据子集执行计算的不同且更快的方法,而不是实际对数据子集执行计算:向量数学。人群中的工程师会知道我在说什么

不是子集,而是指定一个非常快速的函数来创建一个标识向量,并将数据乘以标识

现在,这并不是所有情况下都更快。有些情况下,向量化函数实际上比项显式函数慢,这完全取决于您的特定应用程序。[在此插入您选择的O形符号。]

下面是我们在这种情况下如何实现向量数学:

# 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
#创建NA标识向量。

na_identity fwiw,for
循环并不慢。相反,我猜想代码最慢的部分是在确定
平均值时执行的子集设置操作。如果您使用
aggregate
或其他方法预先计算每个组的平均值,您的循环将显著加快。。。但是
data.table
解决方案几乎肯定更干净、更快!只是一个问题:行
DT[,rate:=ifelse(is.na(rate),avg,rate),by=id]
中需要的
by=i
是什么?@Beasterfield啊,不是。这是我第一次写的东西留下的。编辑。
 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)]
DT[, avg := NULL]
# 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