问题
I am trying to identify any participant taking statins in a dataset of over 1 million rows and subset based on this. I have a vector that includes all the codes for these medications (I've just made a few up for demonstration purposes), and I would next like to create a function that searches through the dataframe and identifies any case that has a medication code that "starts with" any of the characters listed in the df. The df looks like this:
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>
Ideally, I'd like the end product to look like this:
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>
Here is my code so far (doesn't currently work)
#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)
The part that doesn't seem to work is startsWith(x, statin)
.
Please let me know if you have any suggestions and additional, whether there is alternative code that may be more time efficient!
回答1:
This is a solution using the dplyr
package
library(dplyr)
df %>%
filter_at(vars(-ID), any_vars(grepl(paste(medications, collapse = "|"), .)))
Small explanation: we are asking to filter
all those rows where at least one variable (excluding ID
) starts with one of the values inside medications
Output
# 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>
Another solution in base R with a similar rationale is the following
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>
After some benchmarking tests, the base R solution seems to be around 5x faster than the dplyr
one. So I suggest you to use the base R solution if time efficiency is your main concern.
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
来源:https://stackoverflow.com/questions/62195183/subset-dataframe-in-r-based-on-a-list-specified-in-a-vector-using-a-starts-wit