R语言:如何匹配数据框食物项并返回对应分类列表名称?
问题描述
我有一个列表对象a,示例如下:
a <- list("FRUITS" = c("apples","bananas","pears"), "VEGGIES" = c("lettuce","pepper","carrot"), "BREADS" = c("bagel","muffin","sandwhich"))
实际数据包含300个长度不等的子列表,每个子列表约有90个元素。另有一个数据框b(实际含数千行):
b <- data.frame("Sample" = c("BB","TT","LT"), "Food" = c("SandWich","Tomato","BANANA"), "Volume" = c(124,1356,1343))
目标是为数据框b新增一列Match,填入每个Food项所属的列表名称,要求大小写不敏感匹配,示例结果如下:
data.frame("Sample" = c("BB","TT","LT"), "Food" = c("SandWich","Tomato","BANANA"), "Volume" = c(124,1356,1343), "Match" = c("BREADS","VEGGIES","FRUITS"))
尝试过类似Excel VLOOKUP的方法,但未找到能返回列表名称的方案,请问该如何实现?
解决方案
方法1:基础R实现
核心思路是先将列表a转换为键值对格式的匹配表,统一大小写后再与b进行匹配:
# 把列表a转换为长格式匹配表,统一转小写 lookup_df <- data.frame( food = tolower(unlist(a)), category = rep(names(a), sapply(a, length)), stringsAsFactors = FALSE ) # 对b的Food列转小写,匹配分类并新增列 b$Match <- lookup_df$category[match(tolower(b$Food), lookup_df$food)] # 查看结果 print(b)
方法2:tidyverse工具链实现
用purrr和dplyr的语法可以更简洁地完成转换与匹配,适合习惯tidy风格的用户:
library(tidyverse) # 将列表转为长格式匹配表,统一食物名称大小写 lookup_df <- enframe(a, name = "Match", value = "Food") %>% unnest(Food) %>% mutate(Food = tolower(Food)) # 匹配并合并到原数据框 b <- b %>% mutate(Food_lower = tolower(Food)) %>% left_join(lookup_df, by = c("Food_lower" = "Food")) %>% select(-Food_lower) # 查看结果 print(b)
方法3:data.table高效实现(适合大数据量)
如果数据框b行数较多(数千行及以上),data.table的匹配效率会远高于基础R和tidyverse:
library(data.table) # 转换列表为data.table格式的匹配表 lookup_dt <- rbindlist( lapply(names(a), function(x) data.table(Food = tolower(a[[x]]), Match = x)) ) # 转换b为data.table,完成匹配并清理临时列 setDT(b)[, Food_lower := tolower(Food)] b <- lookup_dt[b, on = .(Food = Food_lower)] b[, Food_lower := NULL] # 查看结果 print(b)
注意事项
- 如果某个
Food项同时属于多个分类,以上方法会返回第一个匹配到的分类;若需返回所有匹配分类,可修改代码将结果用逗号分隔。 - 匹配时必须统一使用
tolower()或toupper(),避免因大小写差异导致匹配失败。
内容的提问来源于stack exchange,提问作者user7264
相关产品推荐
相关产品推荐

