使用tidyr的crossing函数合并数据框时自动修复重复列名
使用
tidyr::crossing合并带重复列名的数据框时自动添加后缀 问题说明
想要用crossing函数合并两个存在重复列名的数据框,期望为重复列自动添加“_数据框名称”的后缀,避免列名冲突。
原始数据
> df1 person V1 V2 V3 1 A 1 3 3 2 B 4 4 5 3 C 2 1 1 > df2 V2 V3 1 2 5 2 1 6 3 1 2
当前代码的问题
运行以下代码会生成重复列名的结果:
library(tidyr) crossing(df1, df2, .name_repair = "minimal") #> # A tibble: 9 × 6 #> person V1 V2 V3 V2 V3 #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 A 1 3 3 1 2 #> 2 A 1 3 3 1 6 #> 3 A 1 3 3 2 5 #> 4 B 4 4 5 1 2 #> 5 B 4 4 5 1 6 #> 6 B 4 4 5 2 5 #> 7 C 2 1 1 1 2 #> 8 C 2 1 1 1 6 #> 9 C 2 1 1 2 5
期望输出
需要得到列名唯一的结果,重复列自动添加对应数据框的后缀:
person V1 V2_df1 V3_df1 V2_df2 V3_df2 1 A 1 3 3 1 2 2 A 1 3 3 1 6 3 A 1 3 3 2 5 4 B 4 4 5 1 2 5 B 4 4 5 1 6 6 B 4 4 5 2 5 7 C 2 1 1 1 2 8 C 2 1 1 1 6 9 C 2 1 1 2 5
可复现代码
df1 <- structure(list(person = c("A", "B", "C"), V1 = c(1, 4, 2), V2 = c(3, 4, 1), V3 = c(3, 5, 1)), class = "data.frame", row.names = c(NA, -3L)) df2 <- structure(list(V2 = c(2, 1, 1), V3 = c(5, 6, 2)), class = "data.frame", row.names = c(NA, -3L))
解决方案
方法1:提前重命名重复列后合并
先识别重复列名,给每个数据框的重复列添加对应后缀,再执行crossing:
library(tidyverse) # 获取两个数据框的共同列名 common_cols <- intersect(names(df1), names(df2)) # 给df1的重复列添加_df1后缀 df1_renamed <- df1 %>% rename_with(~ paste0(., "_df1"), all_of(common_cols)) # 给df2的重复列添加_df2后缀 df2_renamed <- df2 %>% rename_with(~ paste0(., "_df2"), all_of(common_cols)) # 执行笛卡尔积合并 crossing(df1_renamed, df2_renamed)
方法2:自定义.name_repair函数
利用crossing的.name_repair参数,自定义函数自动为重复列添加后缀:
library(tidyr) custom_name_repair <- function(col_names) { cols_df1 <- names(df1) cols_df2 <- names(df2) new_names <- map_chr(col_names, function(col) { # 判断当前列是否是重复列 if (col %in% cols_df1 && col %in% cols_df2) { # 由于crossing先按输入顺序排列列,前半部分来自df1,后半来自df2 col_idx <- which(col_names == col) if (col_idx <= length(cols_df1)) { paste0(col, "_df1") } else { paste0(col, "_df2") } } else { col } }) # 确保名称唯一(防止极端情况) make.unique(new_names, sep = "") } # 使用自定义名称修复函数 crossing(df1, df2, .name_repair = custom_name_repair)
方法3:利用列表批量处理重命名
将数据框放入列表,结合imap批量添加后缀,再通过!!!展开执行crossing:
library(tidyverse) # 构造带名称的列表,批量重命名重复列 df_list <- list(df1 = df1, df2 = df2) %>% imap(~ rename_with(.x, ~ ifelse(.x %in% intersect(names(df1), names(df2)), paste0(.x, "_", .y), .x))) # 展开列表并执行crossing crossing(!!!df_list)
内容的提问来源于stack exchange,提问作者Quinten
相关产品推荐
相关产品推荐

