R语言 case_when不适用于多个变量的多个条件

r6l8ljro  于 2022-12-25  发布在  其他
关注(0)|答案(2)|浏览(184)

我刚刚发现,如果一个变量是基于多个变量重新编码的,case_when可能不起作用。
可重现数据:

data <- data.frame(f103 = c(2, NA, NA, 1, 2, 2),
                       f76 = c(2, NA, NA, NA, 3, 3),
                       f4 = c(1,3,3,1,1,2))

下面的代码对var1和var2产生了相同的结果(这不是我想要的):

reprdata <- reprdata %>%
  mutate(var1 = f4) %>% 
  mutate(var1 = case_when(f103 == 2 ~ 3, TRUE ~ as.numeric(var1))) %>%
  mutate(var2 = f4) %>% 
  mutate(var2 = case_when(f103 == 2 ~ 3, f76 == 1 ~ 1, f76 == 2 ~ 2, f76 == 3 ~ 3, TRUE ~ as.numeric(var2)))

下面的代码会产生正确的结果(即我的问题的解决方案):

reprdata <- reprdata %>%
  mutate(var1 = f4) %>% 
  mutate(var1 = case_when(f103 == 2 ~ 3, TRUE ~ as.numeric(var1))) %>%
  mutate(var2 = f4) %>% 
  mutate(var2 = case_when(f103 == 2 ~ 3, TRUE ~ as.numeric(var2))) %>%
  mutate(var2 = case_when(f76 == 1 ~ 1, f76 == 2 ~ 2, f76 == 3 ~ 3, TRUE ~ as.numeric(var2)))

(我知道在我的这段数据中,var1的f103条件是多余的,但我不认为它会导致这个问题。)
我很想知道是否有人能向我的解释为什么会出现这个问题以及将来如何预防它。

knpiaxh1

knpiaxh11#

它与case_when如何计算有关:它是自下而上的评估,这与大多数人的直觉(我的经验)相反。
f76胜出(您所期望的!)

library(dplyr)

data |>
    mutate(var1 = case_when(f103 == 2 ~ 3,
                            TRUE ~ f4)) |>
    mutate(var2 = case_when(f76 %in% 1:3 ~ f76,
                            f103 == 2 ~ 3, # NB!
                            TRUE ~ f4))
f103 f76 f4 var1 var2
1    2   2  1    3    2
2   NA  NA  3    3    3
3   NA  NA  3    3    3
4    1  NA  1    1    1
5    2   3  1    3    3
6    2   3  2    3    3

f103胜出(出乎意料)
一个二个一个一个

rhfm7lfc

rhfm7lfc2#

case_when表达式本质上是if … else if …的矢量化形式,它的参数一直计算到找到第一个匹配为止,* 然后停止 *。
换句话说,这个子表达式

case_when(f103 == 2 ~ 3, f76 == 1 ~ 1, f76 == 2 ~ 2, f76 == 3 ~ 3, TRUE ~ as.numeric(var2))

等效于(同样是的矢量化形式)

if (f103 == 2) 3
  else if (f76 == 1) 1
  else if (f76 == 2) 2
  else if (f76 == 3) 3
  else as.numeric(var2)

要将其矢量化,可以使用ifelse(或'dplyr'中的if_else)重写此表达式,但这会过于冗长,而且我不知道如何以紧凑但可读的方式格式化它(以下代码看起来 * 不错 *,但自动格式化程序会向右扩展平面结构,导致代码极度缩进):

ifelse(f103 == 2, 3,
  ifelse(f76 == 1, 1,
  ifelse(f76 == 2, 2,
  ifelse(f76 == 3, 3,
  as.numeric(var2)))))

因此,dplyr给了我们case_when来更好地表达这一点,但逻辑是完全相同的。
所以,如果你想让f76的值优先于f103的值,你需要反转你的条件:

case_when(
  f76 == 1 ~ 1,
  f76 == 2 ~ 2,
  f76 == 3 ~ 3,
  f103 == 2 ~ 3,
  TRUE ~ as.numeric(var2)
)

......当然,通过合并测试f76值的所有三个案例,可以缩短时间,如另一个答案所示。

相关问题