【问题标题】:Add one variable to specify which one of several variables that equals a value添加一个变量以指定几个变量中的哪一个等于一个值
【发布时间】:2020-12-31 14:36:18
【问题描述】:

我正在学习 R 并在这里寻找最佳实践...

主要问题

鉴于标题my_tibble

# A tibble: 5 x 3
  chkA  chkB  chkC 
  <chr> <chr> <chr>
1 NA    NA    NA   
2 x     NA    NA   
3 NA    x     NA   
4 x     NA    NA   
5 NA    NA    x    

我想创建一个变量/列checked,指定变量chkAchkBchkC 中的哪一个等于"x"。对于每个观察/行,这三个变量中只有零个或一个可以等于"x",而其余的都是NA

我可以用这段代码解决它:

my_tibble <- my_tibble %>% mutate(checked = case_when(
  chkA == "x" ~ "A",
  chkB == "x" ~ "B",
  chkC == "x" ~ "C",
  TRUE ~ "(none)"
))

,它会产生这个小标题:

# A tibble: 5 x 4
  chkA  chkB  chkC  checked
  <chr> <chr> <chr> <chr>  
1 NA    NA    NA    (none) 
2 x     NA    NA    A      
3 NA    x     NA    B      
4 x     NA    NA    A      
5 NA    NA    x     C      

但是,我认为/希望有一个更方便/优雅的解决方案,可能是单线或其他什么,因为我认为这是一个常见的问题。

奖励/后续问题

对于“更清洁”的小标题,我可能想通过转换为 lgl 变量来摆脱 chr 变量及其NAs。我用这段代码解决了这个问题:

my_tibble <- my_tibble %>% mutate(chkA = ifelse(is.na(chkA), FALSE, TRUE))
my_tibble <- my_tibble %>% mutate(chkB = ifelse(is.na(chkB), FALSE, TRUE))
my_tibble <- my_tibble %>% mutate(chkC = ifelse(is.na(chkC), FALSE, TRUE))

,创建这个小标题:

# A tibble: 5 x 3
  chkA  chkB  chkC 
  <lgl> <lgl> <lgl>
1 FALSE FALSE FALSE
2 TRUE  FALSE FALSE
3 FALSE TRUE  FALSE
4 TRUE  FALSE FALSE
5 FALSE FALSE TRUE 

有没有更好的办法?

【问题讨论】:

    标签: r tidyverse


    【解决方案1】:

    这里可以使用acrossmutate 用于多列。此外,is.na 的输出是合乎逻辑的,它可以被否定 (!) 以返回相反的结果,而不是使用 ifelsecase_when

    library(dplyr)
    my_tibble %>%
         select(starts_with('chk')) %>%
         mutate(across(everything(),  ~!is.na(.)))
    # A tibble: 5 x 3
    #  chkA  chkB  chkC 
    #  <lgl> <lgl> <lgl>
    #1 FALSE FALSE FALSE
    #2 TRUE  FALSE FALSE
    #3 FALSE TRUE  FALSE
    #4 TRUE  FALSE FALSE
    #5 FALSE FALSE TRUE 
    

    或者没有匿名函数调用

    library(purrr)
    my_tibble %>%
       select(starts_with('chk')) %>%
       mutate(across(everything(), negate(is.na)))
    # A tibble: 5 x 3
    #  chkA  chkB  chkC 
    #  <lgl> <lgl> <lgl>
    #1 FALSE FALSE FALSE
    #2 TRUE  FALSE FALSE
    #3 FALSE TRUE  FALSE
    #4 TRUE  FALSE FALSE
    #5 FALSE FALSE TRUE 
    

    对于根据NA 的出现创建列名,矢量化选项是max.col

    nm1 <- sub('chk', '', names(my_tibble))[max.col(!is.na(my_tibble), 'first')]
    nm1[!rowSums(!is.na(my_tibble))] <- NA_character_
    my_tibble$checks <- nm1
    my_tibble
    # A tibble: 5 x 4
    #  chkA  chkB  chkC  checks
    #  <chr> <chr> <chr> <chr> 
    #1 <NA>  <NA>  <NA>  <NA>  
    #2 x     <NA>  <NA>  A     
    #3 <NA>  x     <NA>  B     
    #4 x     <NA>  <NA>  A     
    #5 <NA>  <NA>  x     C   
    

    tidyverse 中创建列的一个选项是首先通过删除“chk”前缀来rename 列,然后使用imap 循环列,用列名替换非 NA 元素并使用coalesce 返回第一个非 NA 值,在 'my_tibble' 中创建 'checked' 列

    library(stringr)
    my_tibble <- my_tibble %>%
       rename_all(~ str_remove(., 'chk')) %>%
       imap_dfc(~  case_when(!is.na(.x) ~  .y)) %>%
       invoke(coalesce, .) %>% 
       mutate(my_tibble, checked = .)
    

    -输出

    my_tibble
    # A tibble: 5 x 4
    #  chkA  chkB  chkC  checked
    #  <chr> <chr> <chr> <chr>  
    #1 <NA>  <NA>  <NA>  <NA>   
    #2 x     <NA>  <NA>  A      
    #3 <NA>  x     <NA>  B      
    #4 x     <NA>  <NA>  A      
    #5 <NA>  <NA>  x     C  
    

    数据

    my_tibble <- structure(list(chkA = c(NA, "x", NA, "x", NA), chkB = c(NA, NA, 
    "x", NA, NA), chkC = c(NA, NA, NA, NA, "x")), row.names = c(NA, 
    -5L), class = c("tbl_df", "tbl", "data.frame"))
    

    【讨论】:

      【解决方案2】:

      一个dplyr 用于查找带有 x 的列的选项可能是:

      df %>%
       rowwise() %>%
       mutate(checked = names(.)[replace(which(c_across(everything()) == "x"), length(.) == 0, NA)])
      
        chkA  chkB  chkC  checked
        <chr> <chr> <chr> <chr>  
      1 <NA>  <NA>  <NA>  <NA>   
      2 x     <NA>  <NA>  chkA   
      3 <NA>  x     <NA>  chkB   
      4 x     <NA>  <NA>  chkA   
      5 <NA>  <NA>  x     chkC  
      

      【讨论】:

        猜你喜欢
        • 2017-06-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多