是否有跨不同列的条件值的 R 函数?

Is there a R function for conditional values across different columns?

假设您有一个如下所示的数据框:

df <- tibble(PatientID = c(1,2,3,4,5),
         Treat1 = c("R", "O", "C", "O", "C"),
         Treat2 = c("O", "R", "R", NA, "O"),
         Treat3 = c("C", NA, "O", NA, "R"),
         Treat4 = c("H", NA, "H", NA, "H"),
         Treat5 = c("H", NA, NA, NA, "H"))

治疗 1:Treat5 是患者接受过的不同治疗。我希望创建一个新变量“Chemo”,其中 1 表示是,0 表示否,具体取决于患者是否接受过治疗“C”。

我一直在使用 if_else(),但由于我的实际数据集中有 10 个不同的治疗变量,我想为每个治疗创建这样一个列,我想知道我是否可以这样做不用写这么长的 if 语句。有更简单的方法吗?

使用if_any遍历starts_with'Treat'的列,创建一个逻辑向量%in%-if_anyreturnsTRUE/FALSE 如果选定的任何列对特定行具有 'C',则逻辑将转换为二进制 +(或 as.integer

library(dplyr)
df <- df %>% 
   mutate(Chemo = +(if_any(starts_with("Treat"), ~ .x %in% "C")))

-输出

df
# A tibble: 5 × 7
  PatientID Treat1 Treat2 Treat3 Treat4 Treat5 Chemo
      <dbl> <chr>  <chr>  <chr>  <chr>  <chr>  <int>
1         1 R      O      C      H      H          1
2         2 O      R      <NA>   <NA>   <NA>       0
3         3 C      R      O      H      <NA>       1
4         4 O      <NA>   <NA>   <NA>   <NA>       0
5         5 C      O      R      H      H          1

或使用 base RrowSums

df$Chemo <- +(rowSums(df[startsWith(names(df), "Treat")] == "C", 
      na.rm = TRUE) > 0)

另一个选项使用 str_detectany 来确定 C 是否出现在每一行的任何 Treat 列中。 + 将逻辑值转换为整数。

library(tidyverse)

df %>%
  rowwise() %>%
  mutate(Chemo = +any(str_detect(c_across(starts_with("Treat")), "C"), na.rm = TRUE)) %>%
  ungroup

输出

  PatientID Treat1 Treat2 Treat3 Treat4 Treat5 Chemo
      <dbl> <chr>  <chr>  <chr>  <chr>  <chr>  <int>
1         1 R      O      C      H      H          1
2         2 O      R      NA     NA     NA         0
3         3 C      R      O      H      NA         1
4         4 O      NA     NA     NA     NA         0
5         5 C      O      R      H      H          1

另一种dplyr方式:

library(dplyr)

df %>% 
  mutate(across(starts_with("Treat"), ~case_when(.=="C" ~1,
                                                 TRUE ~0), .names = 'new_{col}')) %>%
  mutate(Chemo = rowSums(select(., starts_with("new")))) %>% 
  select(-starts_with("new"))
  PatientID Treat1 Treat2 Treat3 Treat4 Treat5 Chemo
      <dbl> <chr>  <chr>  <chr>  <chr>  <chr>  <dbl>
1         1 R      O      C      H      H          1
2         2 O      R      NA     NA     NA         0
3         3 C      R      O      H      NA         1
4         4 O      NA     NA     NA     NA         0
5         5 C      O      R      H      H          1