2012-08-15 107 views
9

示例數據:如何用中位數填充NA?

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 $值來代替NA來的人月平均? 「價值」必須包含同一月份之前所有價值的中位數。也就是說,如果當前月份是五月份,則「價值」必須包含五月份以前所有值的中間值。

+3

+1因爲您在10分鐘內完成了5個不同的答案。 – Andrie 2012-08-15 15:22:38

+0

我編輯的問題,包括'set.seed(1)' – Andrie 2012-08-15 15:29:11

回答

8

或用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認爲不重要。嘗試設置replications1並改爲增加數據大小。或者計時最快的3次運行也是一種常見的經驗法則。在這些環節更詳細的討論:

+0

謝謝。我認爲它工作正常 – Sheridan 2012-08-15 15:44:01

+3

+1非常明確地完成。數據變大和/或分組變量具有許多級別時,'data.table'確實會發光。使用不同的數據集,所有的時間將會有很大的不同。 – Justin 2012-08-15 15:48:22

+0

ave與'tapply'有什麼不同?它是'tapply'與'mean'作爲默認和稍微不同的語法? – 2012-08-15 16:47:33

1

這是使用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)] 
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] 

還有很多其他的方法,但有兩個!

+0

+1的解釋。我不會使用'plyr',所以我只是好奇,'轉換'(你使用的)和'Sacha'使用的'彙總'之間的主要區別是什麼? – A5C1D2H2I1M1N2O1R2T1 2012-08-15 15:41:02

+1

'transform'是爲現有的'data.frame'改變或添加一列。因爲它將返回給定的整個數據框加上您添加的任何新行。 'summarise'會返回一個像每月平均值等的「摘要」,並且只返回指定的行。 – Justin 2012-08-15 15:42:39

+0

不錯,不知道'轉換'。我認爲應該有一種方法可以用'plyr'在一行中完成。 – 2012-08-15 16:24:46

3

與基礎R堅持,你也可以嘗試以下方法:

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

這隻適用於只有第61-72行包含「NA」的情況,這在OP的完整數據集中可能不是這種情況。 – 2012-08-15 16:23:35

+0

@SachaEpskamp,因此downvote?對不起,但我不明白你還有什麼期望。您的解決方案是否提供了超過一年的缺失數據的滾動中位數?如果是這樣,再次,我不是一個普通的'plyr'用戶,所以請用一個工作示例更新您的答案。 – A5C1D2H2I1M1N2O1R2T1 2012-08-15 16:36:02

+1

對不起,確實沒有必要,但無法修復它。我花了太多時間在Reddit上投票的事情變成自動的:)至於'plyr',Justins的回答要好得多。 – 2012-08-15 16:44:44

4

這裏是最強大的解決方案我可以想到。它確保年份的正確排列,並且在您有多年缺失值的情況下,將正確計算以前所有月份的中位數。

# 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)) 
+0

確保在嘗試執行其他任何操作之前組織數據的好工作。 – A5C1D2H2I1M1N2O1R2T1 2012-08-15 16:41:49

+0

+1可靠的解決方案。 – Andrie 2012-08-15 16:57:13

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), .))