imd 统计教程 · 数据清理与基础统计
← 教程首页 返回本模块

史上最强∶ 缺失值插补,吐血总结R代码

聂志强实战医学统计2025-12-08 16:58:21广东

数据缺失(missing value)的问题十分重要,且是所有统计分析的最上游。处理不好或者不妥当可能对从数据中得出的结论产生重大影响。缺失值分析是个巨大的体系,细说起来是个深坑,要不要插补如何插补插补质量评价怎么看,都是一门学问。看似最简单的入门分析,但是我始终觉得是比较难的。所以说统计的水,蛮深,当我以为掌握的差不多的时候发现,只是掌握了我以为掌握的,世界其实很大。缺失值内容诸多,我自己很多R包也没有实战,文章仅仅结合自身体会把来龙去脉说地有意思些。下面分为理论和大类,我细细说来。

一、缺失类型

Donald Bruce Rubin根据缺失原因,描述了缺失数据的3种类型。

 1 完全随机丢失(missing completely at random,MCAR),即数据的缺失是完全随机的,不依赖于任何变量,不影响样本的无偏性。在这种情况下删除缺失值,统计效能也许会下降,但是不影响参数估计。

 2 部分随机丢失(missing at random,MAR),即数据缺失的概率与缺失的数据本身无关,而仅与部分完整数据有关。也就是说,数据的缺失不是完全随机的,该类数据的缺失依赖于其他完全变量(在数据集中,我们将不含缺失值的变量称为完全变量,数据集中含有缺失值的变量称为不完全变量)。比如女性通常不想透露她们的年龄/体重,则年龄/体重变量缺失值受性别变量的影响。在这种情况下,可以通过已知变量对缺失值进行估计(数据插补)。

 3 非随机丢失(missing not at random,MNAR),即数据的缺失与不完全变量自身的取值有关。例如,高收入人群通常不希望在调查中透露他们的收入,也就是收入在这个变量的缺失情况受到收入本身的影响。 正确判断缺失值的类型,能给我们处理变量指明方向,但目前还没有一套公认的缺失值类型判定标准。大多是依据经验或文献回顾进行判断。一般而言,所有数据缺失的处理方法仅适合于MCAR与MAR的处理,而对于MNAR最好是能重新设计实验或获取数据。


二、填补常规9法

Missing value填补方法有几十种,但是经典的方法有以下9种。其他方法和体系见后续部分。

1. 删除含有缺失值的个案(case deletion)

处理缺失数据最常见的方法是简单地删掉那些有缺失数据的患者,并分析剩余的数据,这种方法称为完整案例分析(complete case)或列表删除(listwisedeletion)。如果满足MCAR的假设,且缺失值<5%,完整案例分析可以产生无偏估计。缺点:大多数时候数据都是不满足MCAR假设的,尤其样本量较小时,是下下策。

2. 成对删除(pairwise deletion)

假设存在A、B、C、D、E五个变量,C有缺失值,其他的都是完整数据。那么,当对A、B、D、E进行分析时,就可以用完整数据进行分析。而当要纳入C时,则不得不删除相应的缺失值。优点:相比完整案例分析,尽量利用了可用信息。缺点:不同模型对应的样本量不一样,结果之间难以直接比较;相关系数矩阵可能不再“自洽”,在做多变量建模时容易出问题。

3. 均值插补(mean imputation)

不删病例,而是直接填值。最简单的做法是:用某变量的平均值、中位数或众数替代其缺失值。连续近似正态分布 → 用均值插补;明显偏态 → 优先考虑中位数;分类变量 → 可以考虑众数替代。 缺点:同一变量所有缺失都用同一个数字补齐,会人为压缩该变量的变异度,改变原有分布;并没有引入任何新信息,只是“复制”现有均值,导致对该变量相关参数的标准差、标准误明显被低估;后续回归分析中,相关系数和回归系数都可能产生系统性偏倚。

4. 回归插补(regression imputation)

利用其他变量预测缺失值:先用完整病例建立一个回归模型(线性回归、logistic 回归等),再用模型的预测值填补缺失。优点:与均值插补相比,回归插补利用了其他协变量的信息,预测更“个体化”,不会简单拉平所有人;一般不会严重改变变量的整体分布形状。 缺点:预测值没有反映估计的不确定性,和均值插补一样,低估了标准误;结果往往会“过拟合”,关联被夸大,有时会呈现“过于乐观”的回归系数。

5. 末次观测值转结法(last observation carried forward,LOCF)

常见于纵向随访或时间序列研究。一旦中途缺失,就用“患者上一时点的观测值”填补后续所有缺失值。例:某病人血钾 3.7 → 3.8 → 之后失访,我们直接把后面所有时间点都填为 3.8,假定血钾水平稳定不变。优点:思路直观、实现简单;在部分临床试验中,易于向监管机构解释。缺点:实际上会低估纵向变化的变异度;一旦真实趋势是“逐渐恶化”或“逐渐好转”,LOCF 会系统性偏倚结论。 相关变体:WCCF(worst case carry forward):用“最差的可能值”去填补缺失值。在临床试验中,有时会将缺失结局全部按“失败/未缓解”处理,以获得保守的疗效估计——如果在这种苛刻假设下仍然有效,结果更有说服力。

6. 把“缺失”当作一类(dummy variable)

主要用于自变量是分类变量时。做法是:将“缺失”编码成一个新的类别,例如:糖尿病变量:0 = 有糖尿病,1 = 无糖尿病,缺失时记为 2,相当于三类:有 / 无 / 信息缺失。回归分析时,可以仅关注“糖尿病 vs 无糖尿病”的比较,同时保留缺失的一类,使这些病例不至于被整体删除。缺点:若缺失并非真正“独立的一类”,而是不随机缺失,这种做法仍可能导致偏倚,还有就是小样本单元格问题。

7. 热卡填充(hot deck imputation,或就近补齐)

对于有缺失的个体,从完整样本中找一个最相似的“捐赠者”病例,将捐赠者的真实观测值复制过来进行填补。“热卡”一词来自早期纸质卡片时代:被机器当前读取的卡片是“热的”。 优点:使用的是数据中真实出现过的值,保持了原始数据的分布;直观易懂,在一些官方统计和调查研究中常用。缺点:如何定义“相似”需要人为设定(例如根据年龄、性别、病种等分层),主观性较强;若捐赠规则过于简单,仍会引入偏倚。

8. 最近邻插补(k-nearest neighbor,KNN)

可以理解为 hot deck 的“升级版”。先定义样本之间的“距离”(如欧式距离、马氏距离),同时对不同变量进行标准化;对每个缺失的样本,寻找 K 个最近的邻居;用这 K 个邻居在该变量上的值(如加权平均或加权中位数)作为插补值。优点:比简单 hot deck 更系统,利用了多维协变量的信息;对非线性关系有一定适应性,不依赖特定分布假设。缺点:在大样本、高维数据下,距离计算非常耗时;高维情形中“近邻”和“远邻”的差距变小,KNN 的区分度下降(“维数灾难”),插补精度会受影响。

9. 多重插补(multiple imputation,MI)

Rubin 在 1970s 提出的方法,目前是主流且被 SCI 杂志高度认可的缺失处理框架。基本思路:建模缺失机制和数据的联合分布;为每个缺失值随机生成多次预测值,得到 MMM 份完整的数据集(例如 M = 20);在每一份数据集上按常规方法做分析,得到 MMM 组估计量与标准误;用 Rubin 规则将这 MMM 组结果合并,得到整体的点估计 + 置信区间。 优势:通过多个“版本”的插补,显式考虑了插补的不确定性,标准误和置信区间更接近真值;对连续、分类、自定义模型都很灵活(比如线性回归、logistic、Cox、生存联合模型等);在满足 MAR(缺失随机依赖于已观测变量) 的前提下,理论基础扎实。在医学统计实践中,多重插补一般被视为缺失数据处理的金标准之一。


三、缺失比例与检验

下面总结的是缺失值%,以及完全随机缺失Little's MCAR检验。P>0.05就是MCAR,适配后续任意缺失值填补方法。


R代码分享如下:

#--------(二) 缺失值处理 ------------------
## 实战医学统计整理
#导入SAV数据
library(haven)
# recine <- read_sav("reicin100.SAV")
#View(recine)
# pacman::p_load(haven, here, tidyverse, stringr,frailtypack, dsr, PHEindicatormethods)
# missdata<-select(recine,distinctD,randomid2,weight_2num, bmi,lvef_2num, cndose_2num, hb_2num,hct_2num,bun_2num,hdlc_2num,alb_2num,ck_2num, ckmb_2num) 
#导出sav
# write_sav(missdata, "missdata.sav")
#读取sav
recine <- read_sav("missdata.sav")
recine$distinctD <- ifelse(recine$distinctD == 1, 0, ifelse(recine$distinctD== 2, 1, recine$distinctD))
#  新增一列 blood_type,随机填入 0, 1, 2
# nrow(recine) 确保生成的数据行数和原数据一致
set.seed(123) # 设置种子数,保证每次生成的随机结果一致(可选)
recine$blood_type <- sample(c(0, 1, 2), size = nrow(recine), replace = TRUE)
# 将其转换为 Factor 并赋予 Label
recine$blood_type <- factor(recine$blood_type
                            levels = c(0, 1, 2),        # 原始数值
                            labels = c("A型""B型""O型")) # 对应的标签
str(recine$blood_type)
table(recine$blood_type)
# 计算需要设置为缺失值的行数 (总行数 * 15%)
n_miss <- round(nrow(recine) * 0.15)
# 随机抽取这些行的索引 (行号)
miss_indices <- sample(1:nrow(recine), n_miss)
# 3. 将抽中行的 blood_type 设为 NA
recine$blood_type[miss_indices] <- NA



#-----  1 变量缺失比例----
pacman::p_load(mice,VIM, dplyr,Cairo)
pMiss <- function(x){round(sum(is.na(x))/length(x),3)}
#查看全var缺失情况
apply(recine,2, pMiss)
#??方法2 缺失比例
library(dlookr)
diagnose(recine)

#---?? 方法3
library(dlookr)
recine %>% 
  dlookr::plot_na_pareto()
#方法4 naniar
library(naniar)
library(ggplot2)
recine %>%
  naniar::gg_miss_var(show_pct = TRUE) +
  theme_bw(base_size = 16)
#方法5 
library(DataExplorer)
DataExplorer::plot_missing(recine) #绘制缺失值的比例

##---  1.1 Little's MCAR判断随机缺失----
#判断P>0.05=MCAR 总体是MCAR,P<0.05= MAR NMAR 
#方法1 P>0.05好!
pacman::p_load(misty,naniar)
misty::na.test(recine)
#方法2
library(naniar)
# 进行 Little's MCAR 检验
naniar::mcar_test(recine)

##---  1.2 随机缺失可视化----
pacman::p_load(VIM)
str(recine)
dput(names(recine)) 
#---方法1 marginplot
# marginplot 会绘制散点图,并在边缘展示箱线图
marginplot(recine[, c("lvef_2num""blood_type")])
#'x' must be 2-dimensional
# 观察图两侧红色的箱线图(代表缺失组)和蓝色的箱线图(代表完整组)。
# 1. 如果红箱和蓝箱的长度、中位数位置非常接近 -> 支持 MCAR。
# 2. 如果红箱和蓝箱明显错开 -> 支持 MAR(缺失可能受 score 影响)。

#-- ?? 方法2 绘制矩阵图
matrixplot(recine, sortby = "lvef_2num"
# 看着 lvef_2num 从小到大排列后,
# blood_type 的红色缺失块是否明显集中在某一段?
# 如果还是很乱(随机分布),那就是 MCAR。

四、多重插补路线

我总结了缺失值插补相关单一与多重插补(基于回归、随机森林、矩阵分解及链式方程)、似然推断(SEM 与 EM 算法)以及加权方法四大核心方法论。如果不确定用什么,首选 mice(最稳健、最通用)。


五、其他插补R包 各领域的“核心” R 包,主要包括:探索与可视化:naniar、VIM、mice(也涵盖探索功能);通用与特定算法插补:mice(链式方程多重插补)、Amelia(基于 EM 的多重插补)、missForest(基于随机森林)、missMDA(基于 SVD/PCA 矩阵分解)、Hmisc(简单插补);模型与特定数据:lavaan(结构方程 FIML)、imputeTS(时间序列)、jomo(多层数据);应用领域:涉及遗传学(snpStats)、单细胞组学(DrImpute)及因果推断(grf)等专用工具 。具体R包和相关网页链接,我打包了一个.docx文档,需要的朋友领取文末福利即可。


六、MI多重插补与mice包

6.1 MI原理

多重插补(MI)的核心逻辑可以概括为先发散模拟,后汇聚整合。具体而言,MI 不仅仅填补一次空缺,而是利用算法(如 MCMC)对含有缺失值的原始数据(100行)进行多次填补,生成多份(如 5 份)略有差异的完整数据集,以此来模拟缺失值本身的随机不确定性。随后根据分析目的分为两条路径: 标准推断路径(方法1):对这 5 份数据分别进行独立的统计分析,最后汇总(Pool)统计参数(如 OR 值和 95% CI),这是为了获得统计学上最严谨、包含插补误差的推断结果。 数据生成路径(方法2):将 5 次插补的具体数值通过取均值、中位数或众数进行合并,将数据重新“压缩”回一份单一的完整数据集(100行),通常用于需要单一输入源的机器学习或特定分析场景!强烈推荐,方法2


6.2 mice包R代码

Mice包有17种插补方法,其中首选rf随机森林。Mice参数与细节诸多,小伙伴们可以参考我的总结。


#----- 3 核心变量 mice填补 -----
#PMM、cart、rf任意格式都行
##数值型数据适用 pmm;二分类数据适用 logreg, 无序多类别数据适用 ployreg,有序多分类变量适用 polr
#其它包括,比如贝叶斯线性回归(norm)、基于bootstrap的线性回归(norm.boot)、线性回归预测值(norm.predict)、
#分类回归树(cart)、随机森林(rf)等等;
#PMM、cart、rf任意格式都行!!
#mice 17种方法,用3种;10000推荐迭代
tempmice.rf<- mice(recine_cleaned,m=5,maxit=10,meth='rf',seed=34, nnet.MaxNWts = 1000)
#pmm推荐连续,虽然万能
tempmice.pmm<- mice(recine_cleaned,m=5,maxit=10,meth='pmm',seed=34, nnet.MaxNWts = 1000)
tempmice.cart<- mice(recine_cleaned,m=5,maxit=10,meth='cart',seed=34, nnet.MaxNWts = 1000)
# 打开PDF设备,设置页面尺寸和字体
# library(Cairo)
# Cairo::CairoPDF(file="tempmice.pdf", family="Times New Roman", width=8, height=6)
# # 将绘图区域划分为2行2列
# par(mfrow=c(2,2))
# 生成填补数据,默认迭代10000
# 关闭设备
##dev.off()
# 绘制密度图、
densityplot(tempmice.rf)
densityplot(tempmice.pmm)
densityplot(tempmice.cart)
#----图形导出PPT完美export包 ggplot对象
#export包 R4.2可用
pacman::p_load(ggplotify, export)
#rf 图形导出PPT
tmp.rf <- densityplot(tempmice.rf)
tmp.pmm <-densityplot(tempmice.pmm)
tmp.cart <-densityplot(tempmice.cart)
#---?? 导出pptx编辑
#, aspectr=1.7,aspectr=sqrt(2)       
graph2ppt(x=tmp.rf,file="MI.pptx",width=8, height=6)
# 第2页
graph2ppt(x=tmp.pmm,file="MI.pptx",width=8, height=6,  append=TRUE)
# 第3页
graph2ppt(x=tmp.cart,file="MI.pptx",width=8, height=6,  append=TRUE)

#----- 3 核心变量 mice填补 -----
#PMM、cart、rf任意格式都行
##数值型数据适用 pmm;二分类数据适用 logreg, 无序多类别数据适用 ployreg,有序多分类变量适用 polr
#其它包括,比如贝叶斯线性回归(norm)、基于bootstrap的线性回归(norm.boot)、线性回归预测值(norm.predict)、
#分类回归树(cart)、随机森林(rf)等等;
#PMM、cart、rf任意格式都行!!
#mice 17种方法,用3种;10000推荐迭代
tempmice.rf<- mice(recine_cleaned,m=5,maxit=10,meth='rf',seed=34, nnet.MaxNWts = 1000)
#pmm推荐连续,虽然万能
tempmice.pmm<- mice(recine_cleaned,m=5,maxit=10,meth='pmm',seed=34, nnet.MaxNWts = 1000)
tempmice.cart<- mice(recine_cleaned,m=5,maxit=10,meth='cart',seed=34, nnet.MaxNWts = 1000)
# 打开PDF设备,设置页面尺寸和字体
# library(Cairo)
# Cairo::CairoPDF(file="tempmice.pdf", family="Times New Roman", width=8, height=6)
# # 将绘图区域划分为2行2列
# par(mfrow=c(2,2))
# 生成填补数据,默认迭代10000
# 关闭设备
##dev.off()
# 绘制密度图、
densityplot(tempmice.rf)
densityplot(tempmice.pmm)
densityplot(tempmice.cart)
#----图形导出PPT完美export包 ggplot对象
#export包 R4.2可用
pacman::p_load(ggplotify, export)
#rf 图形导出PPT
tmp.rf <- densityplot(tempmice.rf)
tmp.pmm <-densityplot(tempmice.pmm)
tmp.cart <-densityplot(tempmice.cart)
#---?? 导出pptx编辑
#, aspectr=1.7,aspectr=sqrt(2)       
graph2ppt(x=tmp.rf,file="MI.pptx",width=8, height=6)
# 第2页
graph2ppt(x=tmp.pmm,file="MI.pptx",width=8, height=6,  append=TRUE)
# 第3页
graph2ppt(x=tmp.cart,file="MI.pptx",width=8, height=6,  append=TRUE)

#------3.1 智荟一键缺失值 -------
#方法1 5次填补后取均数、众数
missdata <- recine_cleaned
# MI后 步骤2 SCI版 m=5 25 50 次后合并众数、均值
#定义求众数函数getmode
#定义补充缺失值函数fix_na、n5次填补function(factorCols,data,n),本样例采用最优rf法 
compiler::loadcmp("智荟缺失值合并-修复factor202512.Rc", envir = rlang::current_env()) 
#调用函数补充缺失值
# 定义因子变量, 分类无因子缺失, 数据集为 missdata
#factorCols <- c("sex",..."disease")
#----默认是 numeric变量,指定factor变量;本例都是连续所以c()---
factorCols <- c()
# 调用函数补充缺失值,其中参数data是待插补数据,5表示5重插补后取均值
MI_data <- fix_na(factorCols,missdata,5)
#MI_data <- fix_na(factorCols,missdata,25)
##观察值到蓝色点,插补值是红色点
stripplot(tempmice.rf,pch=19,cex=1.2,alpha=.3)

##-------3.2 pooled统计量------
library(mice)
set.seed(2025)
##str(MI_data)
fit.pooled<-with(tempmice.rf, glm(distinctD~weight_2num + hb_2num+ckmb_2num, family = binomial)) #对插补后的每个数据集分别进行分析
est<-mice::pool(fit.pooled) #汇总分析
summary(est)

七、发散思维

1.mice在常规分析中属于金标准。但是机器学习预测模型领域,BMJ首选的是hmisc包+rcs限制性立方样条非线性插补。rcs 无处不在。


2.复杂抽样公共数据库,如nhanes这种,自行填补需要考虑wt权重。


3.mice新增了多水平填补,越来越丰富。

八、小结

缺失值插补主流统计推断用mice,预测模型用hmisc



PS1: 本公众号建立了学术交流群7,还剩50人位置,需要实名制。入群请加笔者微信 popnie请备注说明:姓名-学校(单位)-专业。 

PS2:R代码+ 缺失值word总结文档与文献,请公众号聊天后台回复:1208



对以上内容感兴趣同学,可以看看我的ABC 3款课程,内容比推文更牛。

B款课程是R数据清理心得+ 临床小样本RCT+AI科研自动化工具+交叉学科(护理 中医 环境 社会医学)方法学+真实世界系列RWE + TTE系列 + 因果ML+纵向数据方法体系 150小时+

上课每周6或日直播,课后播放器回看永久本地或网络观看,课件代码附送。 B款课程品质超过公众号的文章品质,“聂老师出品必属精品”A款其实已经做到了。

下面是部分课程细节截图,与前沿方法大纲。具体课程,远超这里字数,文末有表格版。

1.1 B款交叉学科-因果机器系列导图(私聊)

1.2 B款精美课件

海量前沿方法,高水准方法学解析,公共数据库整洁数据与衍生变量和SCI文章R代码复现。

每一页都是艺术加专业,每一节课都可以反复把玩,温故知新。




1.3 20倍超高性价比

性价比拉满,全网唯一! 我本就是爱好数据分析,推广统计知识的同时顺便站着有尊严的赚点奶粉钱。如果你认为价格贵,请看单独因果推断课程一项,国外教程就高达5万,课程价格童叟无欺,价值远超价格。且不说B款的交叉前沿方法含金量与思维丰富度,学习后让你面对数据的时候思维野马越疆,横行八方。

二、A款口碑

B款内容比A款更新颖更全面更丰富,但是价格依旧不变,保持初心。我想的是,大家能爱上学习统计,就是对我最大的肯定。A款口碑极高,部分截图如下,就不放太多了,问问你身边的朋友也许就在学习ing。其实AB款都是自来水多,我很少推广广告,顶多文章里面插一句。这里一并感谢小伙伴们的给力口口相传,感谢A款粉丝的信任。


三、聂老师授课

我的初衷是为做个有口皆碑的公众号。慢慢地,许多身边同事和粉丝们想系统的学习统计,但是网上许多的老师存在的问题,一个是不用心,一个资质不够,一个商业标题党太多,容易劣币驱除良币,让统计这个本该严谨的学科变得过于功利化。我尽量保持自己的初心,力求安安静静地做个有温度有品质的精品统计课程。

大家可以看我的资质,虽说不是大牛但是应该对得起操守。新来的小伙伴,也可以把我当做亦师亦友未曾谋面的朋友。

四、直播细节

1.直播在腾讯会议每周6或周日白天,回看平台需要实名制认证(保证你我互信,我一直都是实名制),回看预计3年起-永久(app不倒闭才行),尽量满足大家长期需求。成为正式学员后会进入B款正式群学习。

2.感谢小伙伴们支持厚爱,愿B课能帮大家多发paper。

3.课程优惠及报名联系严老师。

4.本科、硕士、博士均适合本课程(越早越学越容易保研直博。除了临床医学,交叉学科领域专业同样适合。面对AB款课程,相信你只会感到相见恨晚。

五、B课程内容细节

细节超多,这只是大目录!课程波澜壮阔,统计思路渗透每根毛细血管。


六、支付方式

官方原价每人4700元9折优惠联系严老师)。

可对公转账、公务卡扫码支付,转账请务必联系客服确认转账信息,包括

 ①备注信息与发票抬头 ② 是否享有9折优惠政策。

如需银行app扫码请联系严老师获得二维码。 

合作公众号:实战医学统计、easydsm联盟

客服联系人: 严老师 

微信号:

注意:需求更改学习班通知时间、定制会议通知、发票请务必联系严老师

七、AC 款链接

A款统计九阳神功,《实战医学统计精品课程》A款-1折!精美课件一键R包深度统计预测模型公共数据库解析复现

 C款写作易筋经,从0到顶刊sci精品写作课程-全网首发。 

B款统计独孤九剑, ??。

感谢您的信任,期待相约ABC。