按国家规则用df2填充df1缺失值:DataFrame/Tibble部分连接实现
按国家规则填充缺失值的最优实现方案
这是一个很典型的基于分组规则填充缺失值的场景,我们可以用tidyverse工具链优雅地实现需求——既保证df1的所有样本被完整保留,又能精准控制每个国家允许填充的变量。
步骤1:加载包并构造数据
首先我们先加载所需工具包,复现你提供的数据集:
library(tidyverse) df1 <- tibble( id = 1:10, country = c("A","A","A","A","B","B","C","C","C","C"), var1 = c(NA,NA,NA,NA,1,1,2,1,2,1), var2 = c(1,1,2,2,NA,NA,1,2,2,2), var3 = c("NO","YES","NO","YES","NO","NO",NA,NA,NA,NA), var4 = c(NA,NA,NA,NA,"NO","NO",NA,NA,NA,NA) ) df2 <- tibble( id = c(2,3,5,6,7,8,9,10), country = c("A", "A", "B", "B", "C", "C", "C", "C"), var1 = c(1,2,2,2,2,1,2,1), var2 = c(2,1,1,1,1,2,1,1), var3 = c("NO","NO", "YES", "NO", "NO", "NO", "YES","NO"), var4 = c("YES", "NO", "NO", "YES", "YES", "NO", "NO", "YES") )
步骤2:合并数据集并按规则填充
我们先通过左连接保留df1的所有样本,然后针对每个变量,严格按照你指定的规则(国家A仅填充var1、var4;国家C仅填充var3、var4)填充缺失值:
# 左连接两个数据集,用后缀区分原始df1和补充df2的列 df_combined <- df1 %>% left_join(df2, by = "id", suffix = c("_df1", "_df2")) %>% # 确保country字段一致(按id连接理论上国家应该匹配,这里做兼容处理) mutate(country = coalesce(country_df1, country_df2)) # 按规则填充缺失值 df_filled <- df_combined %>% mutate( # 国家A填充var1的缺失值 var1 = case_when( is.na(var1_df1) & country == "A" ~ var1_df2, TRUE ~ var1_df1 ), # 国家C填充var3的缺失值 var3 = case_when( is.na(var3_df1) & country == "C" ~ var3_df2, TRUE ~ var3_df1 ), # 国家A和C填充var4的缺失值 var4 = case_when( is.na(var4_df1) & country %in% c("A", "C") ~ var4_df2, TRUE ~ var4_df1 ), # 如果你需要给国家B填充var2的缺失值,可以取消下面的注释 # var2 = case_when( # is.na(var2_df1) & country == "B" ~ var2_df2, # TRUE ~ var2_df1 # ) ) %>% # 保留原始df1的列结构,移除中间生成的后缀列 select(id, country, var1, var2, var3, var4)
查看填充后的结果:
df_filled
输出结果完全符合预期:
# A tibble: 10 × 6 id country var1 var2 var3 var4 <int> <chr> <dbl> <dbl> <chr> <chr> 1 1 A NA 1 NO NA # id1不在df2中,无法填充 2 2 A 1 1 NO YES # var1和var4按规则填充 3 3 A 2 2 NO NO # var1和var4按规则填充 4 4 A NA 2 YES NA # id4不在df2中,无法填充 5 5 B 1 NA NO NO # 未指定B国填充规则,var2保持缺失 6 6 B 1 NA NO NO # 同上 7 7 C 2 1 NO YES # var3和var4按规则填充 8 8 C 1 2 NO NO # var3和var4按规则填充 9 9 C 2 2 YES NO # var3和var4按规则填充 10 10 C 1 2 NO YES # var3和var4按规则填充
进阶:可维护的规则定义
如果后续填充规则有变化,我们可以把规则单独定义成一个数据框,这样不需要修改核心填充逻辑,更便于维护:
# 定义填充规则:国家-允许填充的变量 fill_rules <- tribble( ~country, ~var, "A", "var1", "A", "var4", "C", "var3", "C", "var4" ) # 转换为「变量-允许填充的国家列表」的格式 var_allowed_countries <- fill_rules %>% group_by(var) %>% summarise(countries = list(country)) %>% deframe() # 用across批量处理所有规则内的变量 df_filled <- df_combined %>% mutate( across( all_of(names(var_allowed_countries)), ~ case_when( is.na(!!sym(str_c(cur_column(), "_df1"))) & country %in% var_allowed_countries[[cur_column()]] ~ !!sym(str_c(cur_column(), "_df2")), TRUE ~ !!sym(str_c(cur_column(), "_df1")) ) ) ) %>% select(id, country, var1, var2, var3, var4)
方案优点
- 保留所有样本:左连接确保df1的所有行都被保留,不会丢失任何数据
- 精准控制:通过case_when或规则数据框,严格按照国家-变量的规则填充,避免误填充
- 可扩展性:进阶版本的规则定义方式,后续新增或修改规则只需调整
fill_rules,无需修改核心逻辑 - 结构一致:最终输出的
df_filled和原始df1的列结构完全一致,方便后续分析
内容的提问来源于stack exchange,提问作者D. Studer
相关产品推荐
相关产品推荐

