【问题标题】:Why does dplyr::case_when() give different output for equivalent logic, unless using rowwise()?为什么 dplyr::case_when() 为等效逻辑提供不同的输出,除非使用 rowwise()?
【发布时间】:2021-07-19 11:32:55
【问题描述】:

我在dplyr::case_when() 中构建逻辑时遇到了一个奇怪的行为。
为什么逻辑 (col_a != col_b) | (col_a != col_c) 有效,而等效的 !col_a %in% c(col_b, col_c) 却无效,除非我们先 rowwise()

示例

数据

df_colors <-
  structure(
    list(
      col_a = c(
        NA,
        NA,
        "blue",
        "green",
        "red",
        "green",
        "blue",
        "red",
        "green",
        "red"
      ),
      col_b = c(
        NA,
        NA,
        "green",
        "blue",
        "blue",
        "red",
        NA,
        "orange",
        "orange",
        "blue"
      ),
      col_c = c(NA,
                NA, NA, NA, NA, NA, "blue", NA, NA, NA)
    ),
    row.names = c(NA,-10L),
    class = c("tbl_df", "tbl", "data.frame")
  )

df_colors
#> # A tibble: 10 x 3
#>    col_a col_b  col_c
#>    <chr> <chr>  <chr>
#>  1 <NA>  <NA>   <NA> 
#>  2 <NA>  <NA>   <NA> 
#>  3 blue  green  <NA> 
#>  4 green blue   <NA> 
#>  5 red   blue   <NA> 
#>  6 green red    <NA> 
#>  7 blue  <NA>   blue 
#>  8 red   orange <NA> 
#>  9 green orange <NA> 
#> 10 red   blue   <NA>

reprex package (v2.0.0) 于 2021-07-19 创建

我的代码
两段等价的代码导致不同的输出。

library(dplyr)

desired_output <-
  df_colors %>%
  mutate(new_col = case_when(if_all(c(col_a, col_b, col_c), is.na) ~ NA_character_,
                             col_a == col_c ~ "col_a equals col_c",
                             col_a == col_b ~ "col_a equals col_b",
                             (col_a != col_b) | (col_a != col_c) ~ "col_a is neither col_b nor col_c" ))



strange_output <-
  df_colors %>%
  mutate(new_col = case_when(if_all(c(col_a, col_b, col_c), is.na) ~ NA_character_,
                             col_a == col_c ~ "col_a equals col_c",
                             col_a == col_b ~ "col_a equals col_b",
                             !col_a %in% c(col_b, col_c) ~ "col_a is neither col_b nor col_c" ))


desired_output
#> # A tibble: 10 x 4
#>    col_a col_b  col_c new_col                         
#>    <chr> <chr>  <chr> <chr>                           
#>  1 <NA>  <NA>   <NA>  <NA>                            
#>  2 <NA>  <NA>   <NA>  <NA>                            
#>  3 blue  green  <NA>  col_a is neither col_b nor col_c
#>  4 green blue   <NA>  col_a is neither col_b nor col_c
#>  5 red   blue   <NA>  col_a is neither col_b nor col_c
#>  6 green red    <NA>  col_a is neither col_b nor col_c
#>  7 blue  <NA>   blue  col_a equals col_c              
#>  8 red   orange <NA>  col_a is neither col_b nor col_c
#>  9 green orange <NA>  col_a is neither col_b nor col_c
#> 10 red   blue   <NA>  col_a is neither col_b nor col_c

strange_output
#> # A tibble: 10 x 4
#>    col_a col_b  col_c new_col           
#>    <chr> <chr>  <chr> <chr>             
#>  1 <NA>  <NA>   <NA>  <NA>              
#>  2 <NA>  <NA>   <NA>  <NA>              
#>  3 blue  green  <NA>  <NA>              
#>  4 green blue   <NA>  <NA>              
#>  5 red   blue   <NA>  <NA>              
#>  6 green red    <NA>  <NA>              
#>  7 blue  <NA>   blue  col_a equals col_c
#>  8 red   orange <NA>  <NA>              
#>  9 green orange <NA>  <NA>              
#> 10 red   blue   <NA>  <NA

只有当我使用rowwise() 时,我才能通过第二个代码获得所需的输出。

desired_output_with_rowwise <-
  df_colors %>%
  rowwise() %>%
  mutate(new_col = case_when(if_all(c(col_a, col_b, col_c), is.na) ~ NA_character_,
                             col_a == col_c ~ "col_a equals col_c",
                             col_a == col_b ~ "col_a equals col_b",
                             !col_a %in% c(col_b, col_c) ~ "col_a is neither col_b nor col_c" ))

除非我使用rowwise(),否则!col_a %in% c(col_b, col_c) 的结果为何与(col_a != col_b) | (col_a != col_c) 不同?


编辑


除了想了解为什么会有这种差异之外,这也是一个性能问题,因为使用rowwise() 会使操作在处理大数据时显着变慢。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    让我们举一个更小的例子来理解这个问题。

    a <- c(1, 1, 4, 5, 7, 8)
    b <- c(2, 1, 4, 5, 6, 8)
    c <- c(1, 1, 2, 6, 6, 8)
    

    我们正在寻找abc 不同的值。所以它应该返回TRUE 除了第 2 和最后一个是第 6 的所有位置。

    (a != b) | (a != c)
    [1]  TRUE FALSE  TRUE  TRUE  TRUE FALSE
    

    这是正确的,因为位置 2 和 6 是 FALSE

    !a %in% c(b, c) 
    [1] FALSE FALSE FALSE FALSE  TRUE FALSE
    

    这看起来不正确。

    为什么?

    在第一种情况下,我们按元素比较值,即 a[1]b[1]c[1] 进行比较。类似地,a[2]b[2]c[2] 等进行比较。在第二种情况下,我们使用 bc 中的所有值检查 a[1]。同样,我们正在检查a[2]bc 中的所有值。只有 7 是 a 中的值,它在 bc 的任何位置都不存在。

    【讨论】:

    • 谢谢!换句话说,你会说(a != b) | (a != c) 是矢量化操作而(a != b) | (a != c) 不是矢量化的吗?
    • 你的意思是!a %in% c(b, c) 在第二种情况下?我不认为我会说 !a %in% c(b, c) 不是矢量化的。它显然适用于向量(a 是一个向量),但我们只想在这里检查 bc 值的子集,而不是全部。
    • 是的,我的意思是 !a %in% c(b, c) 在第二种情况下。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 2021-12-19
    • 2021-08-21
    • 1970-01-01
    • 2020-10-04
    相关资源
    最近更新 更多