如何用中位数填充NA?

She*_*dan 11 statistics r plyr data.table

示例数据:

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
Run Code Online (Sandbox Code Playgroud)

请告诉我,我怎样才能将df $值中的NA替换为其他月份的中位数?"value"必须包含同月所有先前值的中值.也就是说,如果当前月份是5月,则"值"必须包含5月份所有先前值的中值.

Jus*_*tin 10

你想使用测试is.na功能:

df$value[is.na(df$value)] <- median(df$value, na.rm=TRUE)
Run Code Online (Sandbox Code Playgroud)

它表示所有值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))
Run Code Online (Sandbox Code Playgroud)

你也可以用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]
Run Code Online (Sandbox Code Playgroud)

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


Luc*_*zer 10

或者与大道

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)]
Run Code Online (Sandbox Code Playgroud)

由于答案太多,让我们看看哪个答案最快.

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
Run Code Online (Sandbox Code Playgroud)

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

[Matthew Dowle]这里定时的任务最多需要0.02秒(2.075/100).data.table认为那是微不足道的.尝试设置replications1和增加数据的大小,来代替.或者3次运行中最快的时间也是一个常见的经验法则.在这些链接中更详细的讨论:

  • +1非常清楚地完成了.一旦数据变大和/或分组变量有很多级别,`data.table`就会闪耀.使用不同的数据集,您的所有时间都会有很大差异. (3认同)
  • @SachaEpskamp主要区别在于返回值.在这种情况下,`ave`将返回与`df`相同长度的向量,而`tapply`将返回长度为'unique(months)`的长度向量.这只是一个输出更方便你的问题. (2认同)

小智 8

还有另一种方法可以使用dplyr.

如果要将所有列替换为其中位数,请执行以下操作:

library(dplyr)
df %>% 
   mutate_all(~ifelse(is.na(.), median(., na.rm = TRUE), .))
Run Code Online (Sandbox Code Playgroud)

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

df %>% 
  mutate_at(vars(value), ~ifelse(is.na(.), median(., na.rm = TRUE), .))
Run Code Online (Sandbox Code Playgroud)