R语言根据多列指定类别存在性生成对应二进制列的方法
原有代码失效原因
- 第一种dplyr写法错误:
starts_with()是tidyverse专用的列选择辅助函数,仅能在select()、across()这类支持列选择的函数上下文里使用,直接拿它和"A"做等值判断完全不符合语法逻辑,无法得到正确结果。 - 第二种for循环写法有两个核心问题:一是循环变量大小写不一致(定义的是大写
I,内部调用的是小写i)、括号未闭合存在语法错误;二是每次循环都直接对colA整列重新赋值,没有累积之前列的判断结果,循环结束后自然只会保留最后一列的判断值。
可用实现方案
以下方案均自动适配2-4列不等的col类字段,且自动处理NA值(NA默认判定为不存在对应类别),无需手动调整列数逻辑。
方案1:dplyr简洁版(推荐)
不用写循环,代码可读性高、出错概率低,自动匹配所有col开头的时间点字段:
library(dplyr) df <- df %>% mutate( colA = as.integer(rowSums(across(starts_with("col"), ~.x == "A"), na.rm = TRUE) > 0), colB = as.integer(rowSums(across(starts_with("col"), ~.x == "B"), na.rm = TRUE) > 0), colC = as.integer(rowSums(across(starts_with("col"), ~.x == "C"), na.rm = TRUE) > 0), colD = as.integer(rowSums(across(starts_with("col"), ~.x == "D"), na.rm = TRUE) > 0) )
逻辑说明:across()选中所有符合前缀规则的列,逐列判断是否等于目标类别;rowSums()按行统计符合条件的列数,只要数量大于0就说明该行存在对应类别,转成整数后即为要求的1/0标识。
如果明确知道列的序号范围,也可以把starts_with("col")替换为列范围,比如只有col1、col2两列时写col1:col2即可。
方案2:修正后的for循环版
如果习惯用循环逻辑,核心是不要每次覆盖整列结果,而是累积判断结果:
# 先初始化4个新列,默认全为0 df[, c("colA", "colB", "colC", "colD")] <- 0 # 自动匹配所有col+数字格式的字段 target_cols <- grep("^col\\d+$", names(df), value = TRUE) for (cur_col in target_cols) { # 当前列存在对应类别时,把对应标识列改为1,否则保留原有值 df$colA <- ifelse(!is.na(df[[cur_col]]) & df[[cur_col]] == "A", 1, df$colA) df$colB <- ifelse(!is.na(df[[cur_col]]) & df[[cur_col]] == "B", 1, df$colB) df$colC <- ifelse(!is.na(df[[cur_col]]) & df[[cur_col]] == "C", 1, df$colC) df$colD <- ifelse(!is.na(df[[cur_col]]) & df[[cur_col]] == "D", 1, df$colD) }
测试数据集运行结果
用提供的测试结构数据集运行上述代码,得到的结果完全符合预期:
| id | colA | colB | colC | colD |
|---|---|---|---|---|
| 1 | 1 | 1 | 0 | 0 |
| 2 | 1 | 1 | 0 | 0 |
| 3 | 0 | 1 | 1 | 0 |
| 4 | 1 | 0 | 0 | 0 |
| 5 | 0 | 0 | 1 | 1 |
| 6 | 0 | 1 | 1 | 0 |
内容的提问来源于stack exchange,提问作者craby111
相关产品推荐
相关产品推荐

