我经常需要根据查找表重新编码数据框列中的一些(不是全部!)值。我对我所知道的解决问题的方法不满意。我希望能够在清晰、稳定、高效方式。在编写自己的函数之前,我想确保我没有复制已经存在的标准。
## Toy example
data = data.frame(
id = 1:7,
x = c("A", "A", "B", "C", "D", "AA", ".")
)
lookup = data.frame(
old = c("A", "D", "."),
new = c("a", "d", "!")
)
## desired result
# id x
# 1 1 a
# 2 2 a
# 3 3 B
# 4 4 C
# 5 5 d
# 6 6 AA
# 7 7 !
我可以通过连接、合并、取消选择来做到这一点,如下所示,但是这个不太清楚如我所愿 - 步骤太多。
## This works, but is more steps than I want
library(dplyr)
data %>%
left_join(lookup, by = c("x" = "old")) %>%
mutate(x = coalesce(new, x)) %>%
select(-new)
也可以用以下方法完成dplyr::recode
,如下所示,将查找表转换为命名查找向量。我更喜欢lookup
作为数据框,但我对命名向量解决方案很满意。我担心的是recode
is the 质疑生命周期阶段,所以我担心这个方法不稳定.
lookup_v = pull(lookup, new) %>% setNames(lookup$old)
data %>%
mutate(x = recode(x, !!!lookup_v))
也可以这样完成:stringr::str_replace
,但使用正则表达式进行整个字符串匹配效率不高。我想有forcats::fct_recode
是一个稳定版本recode
,但我不想要factor
输出(虽然mutate(x = as.character(fct_recode(x, !!!lookup_v)))
也许是迄今为止我最喜欢的选择...)。
我本来希望新的rows_update()
的家人dplyr
函数可以工作,但它对列名很严格,而且我认为它不能更新它所加入的列。 (这是实验性的,所以还没有满足我的稳定性要求。)
我的要求摘要:
- 单个数据列根据查找数据框(最好)或命名向量(允许)进行更新
- 并非数据中的所有值都包含在查找中 - 不存在的值不会被修改
- 必须努力
character
类输入。更普遍的工作是一件好事。
- 基本 R 之外没有依赖项
tidyverse
包(尽管我也有兴趣看到data.table
解决方案)
- 没有使用处于生命周期阶段的功能,例如被取代或质疑。请注意任何实验性生命周期函数,因为它们具有未来的潜力。
- 简洁、清晰的代码
- 我不需要极端的优化,但也不需要太低效(比如不需要时的正则表达式)