如何使用fill()函数按条件填充NA值(无需循环)
分组填充NA值的解决方案
嘿,我明白你遇到的问题了——你有按caseid分组的时间序列数据,想把每组里caseval的NA用上一行的有效值填充,但之前试的方法都没生效对吧?大概率是没做分组操作导致的,直接整列填充会跨组乱填,咱们来解决这个问题。
首先先把你的示例数据整理成可运行的代码,方便测试:
# 构造你提到的示例数据 casedate <- seq(as.Date('2018/1/1'), as.Date('2018/3/5'), by='week') caseid <- c(rep(1,10), rep(2,10), rep(3,10)) caseval <- c(80, rep(NA,4), rep(80,5), 40, rep(NA,2), rep(40,4), rep(NA,3), 60, rep(NA,3), rep(60,6)) df <- data.frame(casedate, caseid, caseval)
下面是三个靠谱的解决方案,选你顺手的用就行:
方案1:用tidyverse的fill()(最直观,新手友好)
tidyverse里的fill()就是专门干这个的,配合group_by()锁定每个caseid分组,就能实现组内填充:
library(tidyverse) # 分组后向下填充NA(用上一行的有效值填后面的空) df_filled <- df %>% group_by(caseid) %>% fill(caseval, .direction = "down") %>% # direction控制填充方向 ungroup()
要是你需要双向填充(比如中间NA,前后都有有效值),把.direction改成"downup"就行。
方案2:用data.table的nafill()(大数据首选,速度快)
如果你的数据量很大,data.table的方法性能会好很多,而且代码也简洁:
library(data.table) setDT(df) # 把数据转成data.table格式 df_filled <- df[, caseval := nafill(caseval, type = "locf"), by = caseid]
这里type = "locf"是“last observation carried forward”的缩写,意思就是用最近的上一个有效值填充,和上面fill的down方向效果一样。
方案3:Base R原生方法(不用装额外包)
要是不想加载第三方库,用base R的ave()也能实现:
df_filled <- df df_filled$caseval <- ave(df_filled$caseval, df_filled$caseid, FUN = function(x) { # 先找到组内所有非NA的位置 non_na_pos <- which(!is.na(x)) # 给每个位置匹配对应的非NA值 x[non_na_pos[cumsum(!is.na(x))]] })
你可以运行完代码后打印df_filled看看,每个caseid分组里的NA都会被前面的有效值正确填充,不会跨组乱套。
内容的提问来源于stack exchange,提问作者Dan Blanchard
相关产品推荐
相关产品推荐

