如何重新编码数据帧值以仅保留满足特定集合的值,将其他值替换为 "other"

How to recode dataframe values to keep only those that satisfy a certain set, replace others with "other"

我正在寻找一个简洁的解决方案,最好是使用 dplyr 来清理数据框列中的值,这样我就可以保留它们匹配特定集合的值,但其他不匹配的值t 匹配将被重新编码为“其他”。

例子

我有一个包含动物名称的数据框。有 4 个合法的动物名称,但其他行包含乱码而不是名称。我想清理该列,只保留合法的动物名称:zebralioncowcat.

数据

library(tidyverse)
library(stringi)

real_animals_names <- sample(c("zebra", "cow", "lion", "cat"), size = 50, replace = TRUE)
gibberish <- do.call(paste0, Map(stri_rand_strings, n = 50, length=c(5, 4, 1),
                                 pattern = c('[a-z]', '[0-9]', '[A-Z]')))

df <- tibble(animals = sample(c(animals, gibberish)))

> df

## # A tibble: 100 x 1
##    animals   
##    <chr>     
##  1 zebra     
##  2 zebra     
##  3 rbzal0677O
##  4 lion      
##  5 cat       
##  6 cfsgt0504G
##  7 cat       
##  8 jhixe2566V
##  9 lion      
## 10 zebra     
## # ... with 90 more rows

解决问题的一种方法 -- 我觉得很烦人而且不简洁

使用 dplyr 1.0.2

df %>%
  mutate(across(animals, recode,
                "lion" = "lion",
                "zebra" = "zebra",
                "cow" = "cow",
                "cat" = "cat",
                .default = "other"))

这就完成了,但是这段代码重复了每个动物的名字两次,我觉得它很笨拙。有没有更清洁的解决方案,最好使用 dplyr?


编辑下面给出的建议答案

因为我喜欢 dplyr::recode 的可读性,但不喜欢每个动物的名字重复两次;由于下面的答案使用 %in% – 我可以将 %in% 合并到我自己的 recode 解决方案中以使其 simpler/more 简洁吗?

你可以保留你需要的动物,把剩下的变成"Others" :

library(dplyr)

keep_names <- c('lion', 'zebra', 'cow', 'cat')

df %>% mutate(animals = ifelse(animals %in% keep_names, animals, 'Others'))

一个base解决方案:

keep_names <- c('lion', 'zebra', 'cow', 'cat')

within(df, animals[!animals %in% keep_names] <- "other")

dplyr 选项 replace():

library(tidyverse)

df %>%
  mutate(animals = replace(animals, !animals %in% keep_names, "other"))

使用recode(),可以使用命名字符向量与!!!进行反引号拼接。

df %>%
  mutate(animals = recode(animals, !!!set_names(keep_names), .default = "other"))

注: set_names(keep_names)等同于setNames(keep_names, keep_names).

我知道你更喜欢 dplyr 解决方案,但这里是 data.table 解决方案(请注意,我将 tibble() 调用更改为 data.table()):

library(stringi)
library(data.table)

real_animals_names <- sample(c("zebra", "cow", "lion", "cat"), size = 50, replace = TRUE)
gibberish <- do.call(paste0, Map(stri_rand_strings, n = 50, length=c(5, 4, 1),
                                 pattern = c('[a-z]', '[0-9]', '[A-Z]')))

df <- data.table(animals = sample(c(real_animals_names, gibberish)))

keep_names <- c("lion", "zebra", "cow", "cat")
df[!animals %in% keep_names, animals := "other"]