【问题标题】:How to recode dataframe values to keep only those that satisfy a certain set, replace others with "other"如何重新编码数据帧值以仅保留满足特定集合的值,将其他值替换为“其他”
【发布时间】:2020-09-16 08:41:42
【问题描述】:

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

示例

我有一个带有动物名称的数据框。有 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 解决方案中以使其更简单/更简洁吗?

【问题讨论】:

    标签: r dplyr recode


    【解决方案1】:

    base 解决方案:

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

    带有replace()dplyr 选项:

    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)

    【讨论】:

    • 首先,必须指定rlang::set_names(),因为purrr 也有set_names()。其次,我最终扭曲了您的解决方案以使用mutate(across(...)),但原理保持不变:df %&gt;% mutate(across(animals, recode,!!!rlang::set_names(keep_names), .default = "other"))
    • @Emman 实际上rlang::set_namespurrr::set_names 是完全一样的。 set_names 最初位于 rlang 中,purrr 将其导入自身。在您的问题中,您已附加tidyverse,因此还附加了purrr。您不需要指定rlang::set_names(),因为将调用来自purrrset_names
    • 我明白了。但是,当我省略 rlang:: 时,我得到一个错误:Error in set_names(keep_names) : 1 argument passed to 'names&lt;-' which requires 2。但是当显式调用rlang::set_names 时,它运行时不会出错。
    • @Emman 我想你已经附上了magrittr,对吧? magrittr 也有一个名为 set_names 的函数,它从 rlangpurrr 中屏蔽了 set_names。在这种情况下,您需要指定rlang::purrr::
    • 是的,没错 :)
    【解决方案2】:

    你可以保留你需要的动物,剩下的交给"Others"

    library(dplyr)
    
    keep_names <- c('lion', 'zebra', 'cow', 'cat')
    
    df %>% mutate(animals = ifelse(animals %in% keep_names, animals, 'Others'))
    

    【讨论】:

      【解决方案3】:

      我知道您最好要求 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"]
      

      【讨论】:

        猜你喜欢
        • 2022-11-14
        • 2023-01-19
        • 2021-07-25
        • 2012-12-23
        • 2021-04-12
        • 1970-01-01
        • 2020-11-16
        • 1970-01-01
        • 2021-04-13
        相关资源
        最近更新 更多