复杂场景下合并DataFrame:冲突值随机选择的实现求助
合并互补数据集并处理冲突值
问题说明
我有两个理论上应包含互补缺失信息但存在错误的数据集,需合并到主DataFrame「df」中。理论上,若ID x在df1中存在年份y、变量z的数据,则该ID在df2中不应有对应值,但实际存在两者均有值的冲突情况。需要生成前缀为「apcs」、格式与df1和df2一致的新变量:仅其中一个数据集有值时取该值,两者均有值时随机选择其一。
模拟数据
library(dplyr) set.seed(123) # 设定随机种子保证结果可复现 df <- data.frame(id = c(1:20), og_var1 = sample(c(1:50), 20, replace=TRUE), state = sample(c(1:52), 20, replace=TRUE), race = sample(c(1:5), 20, replace=TRUE) ) df1 <- left_join(data.frame(id = (1:20)), data.frame( id = c(3,6,9,12), acs09_married = c(1,NA,2,1), acs09_happiness = c(1,NA,3,2), acs10_married = c(NA,1,2,2), acs10_happiness = c(NA,5,2,5)), by=c("id")) df2 <- left_join(data.frame(id = (1:20)), data.frame( id = c(3,6,11,15), pcs09_married = c(NA,1,1,1), pcs09_happiness = c(NA,3,3,2), pcs10_married = c(1,2,2,1), pcs10_happiness = c(2,5,4,4)), by=c("id"))
原尝试代码(存在问题)
df <- left_join(df, df1, by=c("id")) df <- left_join(df, df2, by=c("id")) df$random <- sample(0:1, size=nrow(df), replace =TRUE) years=c("09", "10") list <- colnames(df %>% select(contains(c("var", "prcs")))) list <- sub(".*_", "", x) list <- unique(x) list for(x in years){ for(j in list){ df <- df %>% mutate(paste0("apcs",x,"_",j) = ifelse( !is.na(get(paste0("acs",x,"_",j))) & !is.na(get(paste0("pcs",x,"_",j))), ifelse(random = 1, get(paste0("acs",x,"_",j)), get(paste0("pcs",x,"_",j))), NA ) ) } }
简便解决方案
方法一:长格式转换处理(简洁直观)
通过宽转长分组处理冲突,再转回宽格式,避免复杂循环:
library(tidyr) # 合并所有数据到主df df_combined <- df %>% left_join(df1, by = "id") %>% left_join(df2, by = "id") # 生成apcs变量并处理冲突 df_final <- df_combined %>% # 拆分acs/pcs变量为来源、年份、变量名三部分 pivot_longer( cols = starts_with(c("acs", "pcs")), names_to = c("source", "year", "var"), names_pattern = "(acs|pcs)(\\d{2})_(.*)", values_to = "value" ) %>% # 按ID、年份、变量分组 group_by(id, year, var) %>% # 过滤缺失值 filter(!is.na(value)) %>% # 冲突时随机选1个值,非冲突时保留唯一值 slice_sample(n = 1) %>% ungroup() %>% # 将来源标记改为apcs mutate(source = "apcs") %>% # 转回宽格式得到apcs前缀变量 pivot_wider( names_from = c(source, year, var), names_sep = "", values_from = value ) %>% # 合并回原始数据,保留所有变量 right_join(df_combined, by = "id") %>% # 调整列顺序(可选) select(id, og_var1, state, race, starts_with("apcs"), everything())
方法二:动态变量赋值循环(保留宽格式)
如果偏好直接在宽格式下处理,可使用动态变量名实现:
library(stringr) # 合并数据 df_combined <- df %>% left_join(df1, by = "id") %>% left_join(df2, by = "id") # 提取目标变量名(married、happiness) target_vars <- df_combined %>% select(starts_with("acs")) %>% colnames() %>% str_remove("acs\\d{2}_") %>% unique() years <- c("09", "10") # 生成每行随机选择标记(1选acs,2选pcs) df_final <- df_combined %>% mutate(random_flag = sample(c(1, 2), n(), replace = TRUE)) # 循环生成每个apcs变量 for (year in years) { for (var in target_vars) { acs_col <- paste0("acs", year, "_", var) pcs_col <- paste0("pcs", year, "_", var) apcs_col <- paste0("apcs", year, "_", var) df_final <- df_final %>% mutate( !!apcs_col := case_when( !is.na(.data[[acs_col]]) & is.na(.data[[pcs_col]]) ~ .data[[acs_col]], is.na(.data[[acs_col]]) & !is.na(.data[[pcs_col]]) ~ .data[[pcs_col]], !is.na(.data[[acs_col]]) & !is.na(.data[[pcs_col]]) ~ ifelse(random_flag == 1, .data[[acs_col]], .data[[pcs_col]]), TRUE ~ NA_real_ ) ) } } # 移除临时随机标记列 df_final <- df_final %>% select(-random_flag)
原代码问题说明
- 变量提取错误:
select(contains(c("var", "prcs")))应改为contains(c("acs", "pcs")),且sub(".*_", "", x)中的x未定义,需替换为list。 - 动态赋值错误:
mutate中不能直接用paste0(...) = ...,需使用!!apcs_col := ...的动态赋值语法。 - 逻辑判断错误:
ifelse(random = 1是赋值操作,应为ifelse(random == 1。
内容的提问来源于stack exchange,提问作者tchoup
相关产品推荐
相关产品推荐

