如何用dplyr处理非互斥指示变量:复制重叠行并合并列?
如何在R中处理指示变量重叠时的行复制与变量合并?
问题背景
我有如下R数据框:
x = data.frame(a=c(1,2,3,4), b = c("g1","g2","g3","g4"), dummy_1 = c(1,1,1,0), dummy_2 = c(0,0,1,1))
它的结构是:
a b dummy_1 dummy_2 1 g1 1 0 2 g2 1 0 3 g3 1 1 4 g4 0 1
我希望修改该数据框:当指示变量(dummy_1、dummy_2)重叠(同一行多个指示变量为1)时添加重复行,并将所有指示变量合并到一列中,最终得到如下格式:
a b dummy 1 g1 dummy_1 2 g2 dummy_1 3 g3 dummy_1 3 g3 dummy_2 4 g4 dummy_2
我曾尝试使用unite()函数,但难以设置行复制的条件,请问实现行复制与变量合并的最佳方法是什么?
最佳解决方案:用tidyverse的pivot_longer一步到位
这绝对是最简洁高效的方法,pivot_longer就是专门为这种宽转长的需求设计的,它能自动帮你处理行复制的问题,完全不需要复杂的条件判断。
步骤说明:
- 先确保你安装了
tidyverse包(没安装的话先跑install.packages("tidyverse")) - 用
pivot_longer把所有以dummy_开头的列转成长格式,指定新列名为dummy,同时保留原来的指示值 - 过滤掉指示值为0的行——我们只需要那些标记为1的情况
- 最后删掉多余的
value列就行
完整代码:
library(tidyverse) x = data.frame(a=c(1,2,3,4), b = c("g1","g2","g3","g4"), dummy_1 = c(1,1,1,0), dummy_2 = c(0,0,1,1)) result <- x %>% pivot_longer(cols = starts_with("dummy_"), # 选中所有dummy开头的列 names_to = "dummy", # 新列的名字叫dummy values_to = "value") %>% # 原来的1/0存在value列里 filter(value == 1) %>% # 只保留标记为1的行 select(-value) # 删掉没用的value列 print(result)
运行后直接得到你想要的结果:
# A tibble: 5 × 3 a b dummy <dbl> <chr> <chr> 1 1 g1 dummy_1 2 2 g2 dummy_1 3 3 g3 dummy_1 4 3 g3 dummy_2 5 4 g4 dummy_2
备选方案:不用tidyverse的Base R写法
如果你不想加载额外的包,用Base R也能实现,就是步骤稍微繁琐一点:
x = data.frame(a=c(1,2,3,4), b = c("g1","g2","g3","g4"), dummy_1 = c(1,1,1,0), dummy_2 = c(0,0,1,1)) # 先找出所有dummy开头的列名 dummy_cols <- grep("dummy_", colnames(x), value = TRUE) # 循环每一行,生成对应的重复行 rows_list <- lapply(1:nrow(x), function(i) { # 找出当前行里值为1的dummy列 active_dummies <- dummy_cols[x[i, dummy_cols] == 1] # 复制当前行,次数等于active_dummies的数量 data.frame( a = rep(x$a[i], length(active_dummies)), b = rep(x$b[i], length(active_dummies)), dummy = active_dummies ) }) # 把所有行合并成一个数据框 result_base <- do.call(rbind, rows_list) print(result_base)
为什么unite()不适合你的需求?
unite()的作用是把同一行的多个列拼接成一个字符串,比如第三行用unite()会得到dummy_1,dummy_2这样的结果,而不是拆成两行。它本质是列合并,不是行复制转长,所以完全不符合你要的效果。而pivot_longer是专门处理宽格式转长格式的工具,正好匹配你的需求。
内容的提问来源于stack exchange,提问作者Ajjit Narayanan
相关产品推荐
相关产品推荐

