如何从 dplyr 中的 case_when 捕获逻辑

Posted

技术标签:

【中文标题】如何从 dplyr 中的 case_when 捕获逻辑【英文标题】:how to capture logic from case_when in dplyr 【发布时间】:2022-01-22 09:25:36 【问题描述】:

我正在使用dplyr 中的case_when() 创建以下列result

z <- tibble(a = c(40, 30, NA), 
       b = c(NA, 20, 10))


z %>%
          mutate(result = case_when(
                    !is.na(a) ~ a,
                    is.na(a) & !is.na(b) ~ b
          )
          )  

上面给出了以下内容:

      a     b result
  <dbl> <dbl>  <dbl>
1    40    NA     40
2    30    20     30
3    NA    10     10   

但是,我想同时创建另一列result_logic,它显示result 中的值从哪里提取(a 或b)。输出将如下所示。

      a     b result result_logic
  <dbl> <dbl>  <dbl>        <chr>
1    40    NA     40          a
2    30    20     30          a
3    NA    10     10          b

有什么方法可以捕获在case_when() 中评估的这个逻辑?

谢谢

【问题讨论】:

我认为您需要进行两次逻辑检查,因为mutate 每次都创建一个变量。在一个 mutate 函数中进行两项检查很容易(在下面的答案中添加) - 您是否有特殊原因要从一个 case_when 测试中获得两列输出? 【参考方案1】:

类似以下内容?

library(tidyverse)

z <- tibble(a = c(40, 30, NA), 
            b = c(NA, 20, 10))

z %>%
  mutate(result = case_when(
    !is.na(a) ~ str_c(a, "a", sep = " "),
    is.na(a) & !is.na(b) ~ str_c(b, "b", sep = " "))) %>% 
  separate(result, into=c("result", "result_logic"), convert = T)

#> # A tibble: 3 × 4
#>       a     b result result_logic
#>   <dbl> <dbl>  <int> <chr>       
#> 1    40    NA     40 a           
#> 2    30    20     30 a           
#> 3    NA    10     10 b

【讨论】:

谢谢。我试图避免两个不同的“case_when()”调用。在我的真实数据集中,我的 case_when 有更多“案例”——如果有办法处理单个“case_when()”会简单得多……如果可能的话 @mdb_ftl:我已经更新了我的解决方案,现在它只使用了一个case_when。希望对你有帮助!【参考方案2】:

这是一种替代方法,仅限dplyr

library(dplyr)

z %>% 
  mutate(result = case_when(
    !is.na(a) ~ a, 
    is.na(a) & !is.na(b) ~ b),
    across(-result, ~case_when(
    !is.na(.) ~ cur_column()), .names = 'new_col'),
    result_logic = coalesce(new_a, new_b), .keep="unused")
  a     b result result_logic
  <dbl> <dbl>  <dbl> <chr>       
1    40    NA     40 a           
2    30    20     30 a           
3    NA    10     10 b  

【讨论】:

【参考方案3】:

您可以颠倒上述两个步骤,让第二个步骤“简单地”选择所选值。这将只涉及一个case_when 调用:

library(tidyverse)

z <- tibble(a = c(40, 30, NA), 
            b = c(NA, 20, 10))

z %>% 
  mutate(result_logic = case_when(
    !is.na(a) ~ "a",
    is.na(a) & !is.na(b) ~ "b"
  ),
  result = map2_dbl(row_number(), result_logic, ~ z[[.x, .y]]))

#> # A tibble: 3 x 4
#>       a     b result_logic result
#>   <dbl> <dbl> <chr>         <dbl>
#> 1    40    NA a                40
#> 2    30    20 a                30
#> 3    NA    10 b                10

由reprex package (v2.0.1) 于 2021-12-20 创建

【讨论】:

【参考方案4】:
library(dplyr, warn.conflicts = FALSE)
z <- tibble(a = c(40, 30, NA), 
       b = c(NA, 20, 10))

z %>% 
  mutate(
    result = do.call(coalesce, across(a:b)),
    result_logic = 
      do.call(coalesce,
        across(a:b, ~ ifelse(is.na(.), NA, cur_column())))
  )
#> # A tibble: 3 × 4
#>       a     b result result_logic
#>   <dbl> <dbl>  <dbl> <chr>       
#> 1    40    NA     40 a           
#> 2    30    20     30 a           
#> 3    NA    10     10 b

由reprex package (v2.0.1) 于 2021 年 12 月 20 日创建

【讨论】:

以上是关于如何从 dplyr 中的 case_when 捕获逻辑的主要内容,如果未能解决你的问题,请参考以下文章

在 dplyr 中使用 case_when 改变新列时遇到问题

R语言dplyr包使用case_when函数和mutate函数生成新的数据列实战:基于单列生成新的数据列基于多列生成新的数据列

R语言case_when函数和cases函数实战

R:dplyr 有条件地汇总并重新编码列中的值

Mutate和case_when正在给NA

使用 dplyr 在自定义函数中无法识别默认参数