【问题标题】:How to fill NA with median?如何用中位数填充 NA?
【发布时间】:2012-08-11 21:36:15
【问题描述】:

示例数据:

set.seed(1)
df <- data.frame(years=sort(rep(2005:2010, 12)), 
                 months=1:12, 
                 value=c(rnorm(60),NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA))

head(df)
  years months      value
1  2005      1 -0.6264538
2  2005      2  0.1836433
3  2005      3 -0.8356286
4  2005      4  1.5952808
5  2005      5  0.3295078
6  2005      6 -0.8204684

请告诉我,我如何将 df$value 中的 NA 替换为其他月份的中位数? “value”必须包含同一月份所有先前值的中值。也就是说,如果当前月份是 5 月,“值”必须包含 5 月份所有先前值的中值。

【问题讨论】:

  • +1,因为您设法在 10 分钟内获得了 5 个不同的答案。
  • 我编辑了问题以包含set.seed(1)

标签: r plyr data.table statistics


【解决方案1】:

dplyr 还有另一种方法可以做到这一点。

如果你想用它们的中位数替换所有列,请执行以下操作:

library(dplyr)
df %>% 
   mutate_all(~ifelse(is.na(.), median(., na.rm = TRUE), .))

如果要替换列的子集(例如 OP 示例中的“值”),请执行以下操作:

df %>% 
  mutate_at(vars(value), ~ifelse(is.na(.), median(., na.rm = TRUE), .))

【讨论】:

  • 这是最好的解决方案。
【解决方案2】:

或者用 ave

df <- data.frame(years=sort(rep(2005:2010, 12)),
months=1:12,
value=c(rnorm(60),NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA))
df$value[is.na(df$value)] <- with(df, ave(value, months, 
   FUN = function(x) median(x, na.rm = TRUE)))[is.na(df$value)]

既然有这么多答案,让我们看看哪个最快。

plyr2 <- function(df){
  medDF <- ddply(df,.(months),summarize,median=median(value,na.rm=TRUE))
df$value[is.na(df$value)] <- medDF$median[match(df$months,medDF$months)][is.na(df$value)]
  df
}
library(plyr)
library(data.table)
DT <- data.table(df)
setkey(DT, months)


benchmark(ave = df$value[is.na(df$value)] <- 
  with(df, ave(value, months, 
               FUN = function(x) median(x, na.rm = TRUE)))[is.na(df$value)],
          tapply = df$value[61:72] <- 
            with(df, tapply(value, months, median, na.rm=TRUE)),
          sapply = df[61:72, 3] <- sapply(split(df[1:60, 3], df[1:60, 2]), median),
          plyr = ddply(df, .(months), transform, 
                       value=ifelse(is.na(value), median(value, na.rm=TRUE), value)),
          plyr2 = plyr2(df),
          data.table = DT[,value := ifelse(is.na(value), median(value, na.rm=TRUE), value), by=months],
          order = "elapsed")
        test replications elapsed relative user.self sys.self user.child sys.child
3     sapply          100   0.209 1.000000     0.196    0.000          0         0
1        ave          100   0.260 1.244019     0.244    0.000          0         0
6 data.table          100   0.271 1.296651     0.264    0.000          0         0
2     tapply          100   0.271 1.296651     0.256    0.000          0         0
5      plyr2          100   1.675 8.014354     1.612    0.004          0         0
4       plyr          100   2.075 9.928230     2.004    0.000          0         0

我敢打赌 data.table 是最快的。

[ Matthew Dowle ] 这里定时的任务最多需要 0.02 秒 (2.075/100)。 data.table 认为这无关紧要。尝试将 replications 设置为 1 并增加数据大小。或者计时 3 次运行中最快的时间也是一个常见的经验法则。这些链接中更详细的讨论:

【讨论】:

  • +1 非常清楚地完成了。一旦数据变大和/或分组变量有很多级别,data.table 真的会发光。使用不同的数据集,您的所有时间都会有很大不同。
  • avetapply 有什么不同?是否只是 tapplymean 作为默认和略有不同的语法?
  • @SachaEpskamp 主要区别在于返回值。在这种情况下,ave 将返回与df 相同长度的向量,而tapply 将返回长度为unique(months) 的向量。这只是什么输出对你来说更方便的问题。
【解决方案3】:

坚持使用base R,你还可以尝试以下方法:

medians = sapply(split(df[1:60, 3], df[1:60, 2]), median)
df[61:72, 3] = medians

【讨论】:

  • 这仅在只有第 61 - 72 行包含 NA 时才有效,而在 OP 的完整数据集中可能并非如此。
  • @SachaEpskamp,因此投反对票?对不起,但我不明白你还有什么期望。您的解决方案是否提供超过一年缺失数据的滚动中位数?如果是这样,我又不是plyr 的普通用户,所以请用一个工作示例更新你的答案。
  • 抱歉,确实没有必要,但无法修复。我花了太多时间在 Reddit 上,它会自动投票 :) 至于 plyr,贾斯汀的回答要好得多。
  • @SachaEpskamp -- 在这里,我会赞成为你解决这个问题。干杯。
  • @woliveirajr,哈哈。在看到您的评论之前,我查看了编辑,并想,“多么毫无意义的编辑”:)
【解决方案4】:

这是使用plyr的一种方式,它不是很漂亮,但我认为它可以满足您的需求:

library("plyr")

# Make a separate dataframe with month as first column and median as second:
medDF <- ddply(df,.(months),summarize,median=median(value,na.rm=TRUE))

# Replace `NA` values in `df$value` with medians from the second data frame
# match() here ensures that the medians are entered in the correct elements.
df$value[is.na(df$value)] <- medDF$median[match(df$months,medDF$months)][is.na(df$value)]

【讨论】:

    【解决方案5】:

    这是我能想到的最强大的解决方案。它可确保正确排序年份,并在您有多个年份缺少值的情况下正确计算所有前几个月的中位数。

    # first, reshape your data so it is years by months:
    library(reshape2)
    tmp <- dcast(years ~ months, data=df)  # convert data to years x months
    tmp <- tmp[order(tmp$years),]          # order years
    # now calculate the running median on each month
    library(caTools)
    # function to replace NA with rolling median
    tmpfun <- function(x) {
      ifelse(is.na(x), runquantile(x, k=length(x), probs=0.5, align="right"), x)
    }
    # apply tmpfun to each column and convert back to data.frame
    tmpmed <- as.data.frame(lapply(tmp, tmpfun))
    # reshape back to long and convert 'months' back to integer
    res <- melt(tmpmed, "years", variable.name="months")
    res$months <- as.integer(gsub("^X","",res$months))
    

    【讨论】:

    • 在尝试做任何其他事情之前确保数据组织得很好。
    【解决方案6】:

    你想使用测试is.na函数:

    df$value[is.na(df$value)] <- median(df$value, na.rm=TRUE)
    

    对于df$valueNA 的所有值,将其替换为右侧。您需要 na.rm=TRUE 块,否则 median 函数将返回 NA

    按月做这个,有很多选择,但我认为plyr的语法最简单:

    library(plyr)
    ddply(df, 
          .(months), 
          transform, 
          value=ifelse(is.na(value), median(value, na.rm=TRUE), value))
    

    您也可以使用data.table。如果您的数据很大,这是一个特别好的选择:

    library(data.table)
    DT <- data.table(df)
    setkey(DT, months)
    
    DT[,value := ifelse(is.na(value), median(value, na.rm=TRUE), value), by=months]
    

    还有很多其他的方法,但有两种!

    【讨论】:

    • +1 进行解释。我很少使用plyr,所以我只是好奇,transform(您使用的)和 Sacha 使用的summarize 之间的主要区别是什么?
    • transform 是在现有data.frame 中更改或添加列。因为它将返回给定的整个数据框以及您添加的任何新行。 summarise 返回“摘要”,例如每月平均值或其他内容,并且仅返回指定的行。
    • 很好,不知道transform。我认为应该有一种方法可以与plyr 在一行中做到这一点。
    • 这里提出了类似的问题:stackoverflow.com/questions/9322773/… 但意思是
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-09-30
    • 2022-07-23
    • 1970-01-01
    相关资源
    最近更新 更多