基于向量中指定的列表对 R 中的数据帧进行子集化(使用 'starts with' 表达式或等效表达式)

Subset dataframe in R based on a list specified in a vector (using a 'starts with' expression or equivalent)

我正在尝试根据此在超过 100 万行和子集的数据集中识别任何服用他汀类药物的参与者。我有一个向量,其中包含这些药物的所有代码(我只是出于演示目的编造了一些代码),接下来我想创建一个函数来搜索数据框并识别任何具有药物代码的病例"starts with" df 中列出的任何字符。 df 看起来像这样:

     ID readcode_1 readcode_2 generic_name
1  1001       bxd1 1146785342  Simvastatin
2  1002       <NA>       <NA>         <NA>
3  1003       <NA>       <NA>  Pravastatin
4  1004       <NA>       <NA>         <NA>
5  1005       bxd4   45432344         <NA>
6  1006       <NA>       <NA>         <NA>
7  1007       <NA>       <NA>         <NA>
8  1008       <NA>       <NA>         <NA>
9  1009       <NA>       <NA>         <NA>
10 1010       bxde       <NA>         <NA>
11 1011       <NA>       <NA>         <NA>

理想情况下,我希望最终产品看起来像这样:

     ID readcode_1 readcode_2 generic_name
1  1001       bxd1 1146785342  Simvastatin
3  1003       <NA>       <NA>  Pravastatin
5  1005       bxd4   45432344         <NA>
10 1010       bxde       <NA>         <NA>

这是我目前的代码(目前无法使用)

#create vector with list of medication codes of interest
medications <- c("bxd", "Simvastatin", "1146785342", "45432344", "Pravastatin")

# look through all columns (apart from IDs in first column) and if any of them start with the codes listed in the medications vector, return a 1
df$statin_prescribed <- apply(df[, -1], 1, function(x) {
  if(any(x %in% startsWith(x, medications))) {
    return(1)
  } else {
    return(0)
  }
})

# subset to include only individuals prescribed statins
df <- subset(df, statin_prescribed == 1)

似乎不​​起作用的部分是 startsWith(x, statin)

如果您有任何建议和补充,请告诉我是否有可能更省时的替代代码!

这是使用 dplyr

的解决方案
library(dplyr)
df %>% 
  filter_at(vars(-ID), any_vars(grepl(paste(medications, collapse = "|"), .)))

小解释:我们要求 filter 所有那些至少有一个变量(不包括 ID)以 medications

中的值之一开头的行

输出

#     ID readcode_1 readcode_2 generic_name
# 1 1001       bxd1 1146785342  Simvastatin
# 2 1003       <NA>       <NA>  Pravastatin
# 3 1005       bxd4   45432344         <NA>
# 4 1010       bxde       <NA>         <NA>

基于 R 的另一个具有类似原理的解决方案如下

df[apply(df[,-1], 1, function(x) {any(grepl(paste(medications, collapse = "|"), x))}),]

Output is the same (except row index which I believe is not relevant)
#      ID readcode_1 readcode_2 generic_name
# 1  1001       bxd1 1146785342  Simvastatin
# 3  1003       <NA>       <NA>  Pravastatin
# 5  1005       bxd4   45432344         <NA>
# 10 1010       bxde       <NA>         <NA>

经过一些基准测试,基本的 R 解决方案似乎比 dplyr 解决方案快 5 倍左右。因此,如果您主要关心时间效率,我建议您使用基础 R 解决方案。

microbenchmark::microbenchmark(
  df %>% filter_at(vars(-ID), any_vars(grepl(paste(medications, collapse = "|"), .))),
  df[apply(df[,-1], 1, function(x) {any(grepl(paste(medications, collapse = "|"), x))}),],
  times = 100
)

# Unit: microseconds
#                                                                                             # expr    min
#         df %>% filter_at(vars(-ID), any_vars(grepl(paste(medications,      collapse = "|"), .))) 1958.4
#  df[apply(df[, -1], 1, function(x) {     any(grepl(paste(medications, collapse = "|"), x)) }), ]  341.7
#       lq     mean  median      uq    max neval
#  1989.55 2146.993 2041.30 2149.05 7851.1   100
#   352.50  405.972  380.25  401.55 2154.0   100