R语言如何根据legend中的匹配值更新数据集对应列取值
问题描述
我有一个包含键(key)和多列数据的“legend(映射参照表)”:
library(dplyr) (legend <- tibble(key = 1:3, value_1 = c("x", "y", "z"), value_2 = c("fg", "d", "f"), value_3 = c("dsf", "sdf", "sdf"))) #> # A tibble: 3 × 4 #> key value_1 value_2 value_3 #> <int> <chr> <chr> <chr> #> 1 1 x fg dsf #> 2 2 y d sdf #> 3 3 z f sdf
在我的真实业务数据中,存在与legend同名的列,但这些列存储的是legend中key对应的编码值:
(data = tibble(value_1 = c(1, 3, 3), value_2 = c(2, 2, 2), value_3 = c(1, 2, 3))) #> # A tibble: 3 × 3 #> value_1 value_2 value_3 #> <dbl> <dbl> <dbl> #> 1 1 2 1 #> 2 3 2 2 #> 3 3 2 3
能否通过匹配两个数据集的对应关系,使用legend中的映射值更新替换数据集内的编码值,得到如下预期结果?
# 预期输出 tibble(value_1 = c("x", "z", "z"), value_2 = c("d", "d", "d"), value_3 = c("dsf", "sdf", "sdf")) #> # A tibble: 3 × 3 #> value_1 value_2 value_3 #> <chr> <chr> <chr> #> 1 x d dsf #> 2 z d sdf #> 3 z d sdf
本示例于2022-06-02由reprex包(v2.0.1)创建
解决方案
完全可以实现,下面两种tidyverse写法都支持自动适配多列场景,不需要逐列硬编码映射规则:
- 方法1:构造命名向量批量匹配
核心逻辑是将legend中每一列的映射关系转换为编码值=实际值格式的命名向量,再对业务数据的对应列做批量索引匹配,代码简洁运行效率高:library(dplyr) library(purrr) map_dfc(names(data), ~{ match_rule <- setNames(legend[[.x]], legend$key) unname(match_rule[as.character(data[[.x]])]) }) %>% set_names(names(data)) - 方法2:长宽表转换关联匹配
先将业务数据和映射表都转换为长表结构,通过列名+编码值做关联合并,最后转回原宽表结构,逻辑直观易读,适合需要校验匹配结果的场景:library(dplyr) library(tidyr) data %>% mutate(row_id = row_number()) %>% pivot_longer(-row_id, names_to = "col", values_to = "key") %>% left_join( legend %>% pivot_longer(-key, names_to = "col", values_to = "match_val"), by = c("col", "key") ) %>% select(-key) %>% pivot_wider(names_from = col, values_from = match_val) %>% select(-row_id)
两种方法运行后都可以直接得到预期的替换结果,当映射列数较多时,比逐列调用recode/case_when的维护成本低很多。
内容的提问来源于stack exchange,提问作者John-Henry
相关产品推荐
相关产品推荐

