如果类别以 R 中的特定字母开头,如何在 R 中的多列中进行过滤?

How to filter in multiple columns in R if the category starts with specific letter in R?

我有一个庞大的数据集,其中包含超过 200 万个 obs 和 100 列。有 9 列包含 ICD-10 疾病代码,每列都以不同的字母开头。

例如:

icd1 <- c("O230", "B540", "D990", "Y555", "E980", "J777", "P090", "Q090", "R433")

icd2 <- c("O230", "B540", "D990", "Y555", "E980", "J777", "P090", "Q090", "Z433")

icd3 <- c("X230", "B540", "D990", "Y555", "E980", "J777", "P090", "Q090", "Z433")

data <- as.data.frame(rbind(icd1, icd2, icd3))

我需要做的是创建一个名为“ICD_Z”的新列来检查这 9 个 ICD-10 列代码中的每一个,看看它们是否以字母 Z 开头,如果是在这种情况下,我创建的列将接收 1,否则接收 0。但如果所有这 9 列都没有以字母 O 开头的 ICD-10 代码,它只能接收 1。

所以我的输出看起来像这样:

我该怎么做?

结合使用 rowwise()c_across() 非常简单:

library(dplyr)

icd1 <- c("O230", "B540", "D990", "Y555", "E980", "J777", "P090", "Q090", "R433")

icd2 <- c("O230", "B540", "D990", "Y555", "E980", "J777", "P090", "Q090", "Z433")

icd3 <- c("X230", "B540", "D990", "Y555", "E980", "J777", "P090", "Q090", "Z433")

data <- as.data.frame(rbind(icd1, icd2, icd3))

data %>% 
  rowwise() %>% 
  mutate(has_z = any(startsWith(c_across(V1:V9), "Z")),
         has_o = any(startsWith(c_across(V1:V9), "O")),
         icdz = as.numeric(has_z & !has_o)
  ) %>% 
  select(-has_z, -has_o)

#> # A tibble: 3 × 10
#> # Rowwise: 
#>   V1    V2    V3    V4    V5    V6    V7    V8    V9     icdz
#>   <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <dbl>
#> 1 O230  B540  D990  Y555  E980  J777  P090  Q090  R433      0
#> 2 O230  B540  D990  Y555  E980  J777  P090  Q090  Z433      0
#> 3 X230  B540  D990  Y555  E980  J777  P090  Q090  Z433      1

reprex package (v2.0.1)

于 2022-05-25 创建

更新:更快的方法:

icd_cols = paste0("V",1:9)
f <- function(v) !any(v=="O") & any(v=="Z")
setDT(data)
set(data, j="icdz", value= apply(data[,..icd_cols],1,\(i) f(substr(i,1,1))))

原解

对于您的大型数据集,您可能会使用这种使用 data.table 的方法来加快速度。在 3 行的小示例中,我非正式地发现这大约快 5 倍。

  1. 确定9个ICD列的列表;我在这里使用了一个简单的结构来得到 V1:V9,但你可以使用 icd_cols = colnames(data)[c(20,21,37:45)]
  2. 创建一个计算首字母向量的小函数
  3. 将原始帧设置为data.table
  4. 通过id熔化并应用函数,并将结果赋值给原始帧
icd_cols = paste0("V",1:9)
f <- function(v) !any(v=="O") & any(v=="Z")
setDT(data)
data[, icdz:=(melt(data[,id:=.I],"id",icd_cols)[,f(substr(value,1,1)), by=id]$V1)][id:=NULL]

输出:

     V1   V2   V3   V4   V5   V6   V7   V8   V9  icdz
1: O230 B540 D990 Y555 E980 J777 P090 Q090 R433 FALSE
2: O230 B540 D990 Y555 E980 J777 P090 Q090 Z433 FALSE
3: X230 B540 D990 Y555 E980 J777 P090 Q090 Z433  TRUE

您可以使用 if_anyif_all:

data %>%
  mutate(ICD_Z = if_any(V1:V9, ~grepl('^Z', .)) * 
           if_all(V1:V9, ~!grepl('^O', .)))

       V1   V2   V3   V4   V5   V6   V7   V8   V9 ICD_Z
icd1 O230 B540 D990 Y555 E980 J777 P090 Q090 R433     0
icd2 O230 B540 D990 Y555 E980 J777 P090 Q090 Z433     0
icd3 X230 B540 D990 Y555 E980 J777 P090 Q090 Z433     1

编辑:

您可以使用变量来存储相关列的位置:

nms <- c(1,2,3,5,7,8)
data %>%
  mutate(ICD_Z = if_any(all_of(nms), ~grepl('^Z', .)) * 
           if_all(all_of(nms), ~!grepl('^O', .)))