如何用ifelse封装字符串提取函数实现灵活单位映射与非覆盖赋值?
实现匹配映射并保留原有值的data.table列新增方案
示例数据
现有如下示例数据:
library(data.table) example_dat <- fread("var_nam description some_var this_is_some_var_kg other_var this_is_meters_for_another_var extra_var the_price_of_apples another_var cost_of_goods_sold") example_dat$description <- gsub("_", " ", example_dat$description) # 处理后的数据: # var_nam description # 1: some_var this is some var kg # 2: other_var this is meters for another var # 3: extra_var the price of apples # 4: another_var cost of goods sold vector_of_units <- c("kg", "meters", "var")
已有解决方案
此前已有两种匹配单位并新增列的方案:
- 提取所有匹配项(maydin方案):
library(tidyverse) setDT(example_dat)[, unit := unlist(lapply(example_dat$description,function(x) paste0(vector_of_units[str_detect(x,vector_of_units)], collapse = ",")))] # 输出结果: # var_nam description unit # 1: some_var this is some var kg kg,var # 2: other_var this is meters for another var meters,var # 3: extra_var the price of apples # 4: another_var cost of goods sold
- 提取第一个匹配项(langtang方案,更贴合需求):
example_dat[, unit:=stringr::str_extract(description, paste0(vector_of_units,collapse = "|"))] # 输出结果: # var_nam description unit # 1: some_var this is some var kg var # 2: other_var this is meters for another var meters # 3: extra_var the price of apples <NA> # 4: another_var cost of goods sold <NA>
需求与问题
需要实现更灵活的匹配逻辑:
- 分别定义匹配向量和输出向量,实现匹配值到目标值的映射:
vector_of_units_in <- c("kg", "meters", "var") vector_of_units_out <- c("kilogram", "meters", "variable") vector_of_units_euro <- c("cost", "price") vector_of_units_euro_out <- "euro"
- 无匹配时不覆盖原有值(避免用
<NA>替换已存在的有效值)
尝试基于langtang方案改写后得到错误结果:
setDT(example_dat)[, unit := ifelse(!is.na(stringr::str_extract(description, vector_of_units_in)), paste0(vector_of_units_out, collapse = "|"), NA)] # 尝试补充欧元匹配逻辑 setDT(example_dat)[, unit := ifelse(!is.na(stringr::str_extract(description, vector_of_units_euro)), paste0(vector_of_units_euro_out, collapse = "|"), unit)]
错误输出:
var_nam description unit 1: some_var this is some var kg kilogram|meters|variable 2: other_var this is meters for another var kilogram|meters|variable 3: extra_var the price of apples <NA> 4: another_var cost of goods sold <NA>
期望得到如下输出:
var_nam description unit 1: some_var this is some var kg kilogram 2: other_var this is meters for another var meters 3: extra_var the price of apples euro 4: another_var cost of goods sold euro
正确实现方案
可以通过构建匹配映射字典,结合字符串提取和条件赋值实现,以下是两种可行方案:
方案1:使用dplyr+stringr实现
library(dplyr) library(stringr) # 构建完整的匹配映射表 match_map <- tibble( pattern = c(vector_of_units_in, vector_of_units_euro), output = c(vector_of_units_out, rep(vector_of_units_euro_out, length(vector_of_units_euro))) ) # 生成匹配正则表达式(按字典顺序匹配,可调整顺序修改优先级) match_pattern <- str_c(match_map$pattern, collapse = "|") example_dat <- example_dat %>% mutate( # 提取匹配的原始值 matched_val = str_extract(description, match_pattern), # 根据映射替换目标值,无匹配则保留原有unit值 unit = case_when( !is.na(matched_val) ~ match_map$output[match(matched_val, match_map$pattern)], TRUE ~ unit ) ) %>% select(-matched_val)
方案2:纯data.table实现
library(data.table) library(stringr) # 构建匹配映射字典 match_dict <- data.table( pattern = c(vector_of_units_in, vector_of_units_euro), output = c(vector_of_units_out, rep(vector_of_units_euro_out, length(vector_of_units_euro))) ) # 生成正则表达式 match_regex <- paste0(match_dict$pattern, collapse = "|") # 步骤1:提取匹配的原始值 example_dat[, matched_val := str_extract(description, match_regex)] # 步骤2:根据映射替换,无匹配则保留原有unit值 example_dat[, unit := fifelse(!is.na(matched_val), match_dict$output[match(matched_val, match_dict$pattern)], unit)] # 可选:删除中间辅助列 example_dat[, matched_val := NULL]
关键说明
- 映射字典可清晰管理匹配规则,便于后续扩展新的匹配项
- 使用
match()函数实现原始匹配值到目标输出的精准映射,避免手动编写大量条件 fifelse()(data.table)或case_when()(dplyr)确保无匹配时保留原有值,不会被<NA>覆盖- 如需调整匹配优先级,只需修改映射字典中
pattern的顺序(正则匹配会优先匹配先出现的模式)
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

