如何在R中对共享类别的多列实现统一独热编码?
问题描述
原始数据框:
| Label 1 | Label 2 |
|---|---|
| A | B |
| A | C |
| B | C |
| C | A |
需求为生成包含标记列的新数据框:对每个类别(A、B、C及更多)添加对应列,判断该类别是否出现在当前行的任意一列,是则标记1,否则标记0,目标格式如下:
| Label 1 | Label 2 | is_A | is_B | is_C |
|---|---|---|---|---|
| A | B | 1 | 1 | 0 |
| A | C | 1 | 0 | 1 |
| B | C | 0 | 1 | 1 |
| C | A | 1 | 0 | 1 |
用户尝试过两种方法但均有局限:
- 用
if_else逐个编写标记列,但类别多达50+,重复代码过多; - 用
model.matrix生成的是label1A、label2A这类分列的编码,不符合“任意一列出现即标记1”的需求。
解决方案
方法1:tidyverse系列函数(适配数据框操作习惯)
通过长格式转换统计每行的类别出现情况,再转宽格式合并回原数据,自动适配所有类别:
library(tidyverse) # 原始数据 df <- tibble( Label1 = c("A", "A", "B", "C"), Label2 = c("B", "C", "C", "A") ) # 获取所有唯一类别 all_categories <- unique(c(df$Label1, df$Label2)) # 生成标记列并合并 encoded_df <- df %>% mutate(row_id = row_number()) %>% pivot_longer(cols = starts_with("Label"), names_to = "col", values_to = "category") %>% group_by(row_id, category) %>% summarise(exists = 1, .groups = "drop") %>% pivot_wider(names_from = category, values_from = exists, names_prefix = "is_", values_fill = 0) %>% left_join(df, by = "row_id") %>% select(-row_id, Label1, Label2, everything()) print(encoded_df)
方法2:base R的sapply(简洁高效)
直接遍历所有类别,对每行检查是否包含该类别,生成标记矩阵后合并:
# 原始数据 df <- data.frame( Label1 = c("A", "A", "B", "C"), Label2 = c("B", "C", "C", "A") ) # 获取所有唯一类别 all_categories <- unique(c(df$Label1, df$Label2)) # 生成标记矩阵 indicator_matrix <- sapply(all_categories, function(cat) { as.integer(rowSums(df == cat) > 0) }) # 重命名列并合并 colnames(indicator_matrix) <- paste0("is_", all_categories) encoded_df <- cbind(df, indicator_matrix) print(encoded_df)
方法3:dplyr的across函数(tidyverse简化写法)
利用across遍历类别,结合if_any快速判断每行是否包含目标类别:
library(dplyr) df <- tibble( Label1 = c("A", "A", "B", "C"), Label2 = c("B", "C", "C", "A") ) all_categories <- unique(c(df$Label1, df$Label2)) encoded_df <- df %>% mutate( across(all_categories, ~if_any(starts_with("Label"), ~.x == .y), .names = "is_{.col}") ) print(encoded_df)
以上三种方法均无需手动编写每个类别的判断代码,自动适配所有唯一类别,适合50+类别的场景。
内容的提问来源于stack exchange,提问作者user276238
相关产品推荐
相关产品推荐

