使用不同的过滤器参数和向量化的 fifelse() 在函数内部过滤 data.table

Filter data.table inside function with varying filter arguments and vectorised fifelse()

任务: 我在用户定义函数中有一个大数字 data.table(列 1:3 是子集化所需的字符),用于对数据。
在函数内进行下游分析之前,我想使用函数的参数传递值以过滤 data.table,或者如果没有为给定的列过滤器提供值,则使用所有行。

然而,困难出现了,因为并不是所有的列过滤器都会一直被使用。

目前,我的方法是使用单独的 if else 语句块进行过滤以获得每列的 ID 列表,然后我 intersect 在使用之前这是 data.table 的子集。该示例包括状态 3 列,但实际数据中有更多列,这使得这种方法笨拙且效率低下(尽管它有效)。

查询: 如果不知道将使用哪些过滤列,是否有直接过滤 data.table 的方法?或者,有没有办法用 ifelse()fifelse() 来矢量化过程?
我什至会寻求帮助将其嵌入 for 循环中,但要使其正常工作,我需要动态创建变量名来存储每个 ID 列表。

我想保留使用 data.tablebase 函数的解决方案。此外,函数的参数名称可以更改为与 data.table 的列名称相同,如果这样可以使编码和可读性更容易。

感谢您提供的任何帮助。

数据:

# Install data.table package if not installed and load
if (!require("data.table")) {
  install.packages("data.table")
  library(data.table)
}

# Data (example)
head(DT, n=2)

#>     ID     info1  info2  name1  name2  name3  name4
#>     <char> <char> <char> <dbl>  <dbl>  <dbl>  <dbl>
#> 1:  A100   StuffA StuffX 0.1460 NA     -0.019 0.2102
#> 2:  A101   StuffA StuffY 0.0987 -1.307 -0.174 NA

当前方法:

# Function (example)
myfunc <- function(ID_filter = "", info1_filter = "", info2_filter = "") {

  # Get IDs to use as filter
  if (ID_filter != "") {
    ID_list <- DT[ID %in% ID_filter][["ID"]]
  } else {
    ID_list <- DT[["ID"]]
  }
  
  if (info1_filter != "") {
    info1_list <- DT[info1 %in% info1_filter][["info1"]]
  } else {
    info1_list <- DT[["info1"]]
  }

  # Get overall filter list
  filters <- Reduce(intersect, list(ID, info1, info2))

  # Subset data.table
  DT <- DT[ID %in% filters]  

}

函数可以简化为

myfunc <- function(DT, ID_filter = "", info1_filter = "", info2_filter = "") {

 lst1 <- Filter(function(x) all(x != ""), 
     dplyr::lst(ID_filter, info1_filter, info2_filter))
 if(length(lst1) > 0 ) {
      pat <- paste(sub("_filter", "", names(lst1)), collapse="|")     
      i1 <- DT[, Reduce(`&`, Map(`%in%`, .SD, lst1)), .SDcols = patterns(pat)]
      DT[i1]
 } else DT
 
 }

-测试

setDT(DT)
myfunc(DT, ID_filter = "A100")
#     ID  info1  info2 name1 name2  name3  name4
#1: A100 StuffA StuffX 0.146    NA -0.019 0.2102
myfunc(DT, ID_filter = "A100", info1 = "StuffA")
#     ID  info1  info2 name1 name2  name3  name4
#1: A100 StuffA StuffX 0.146    NA -0.019 0.2102
myfunc(DT, ID_filter = "A100", info1 = "StuffA", info2 = "StuffX")
#     ID  info1  info2 name1 name2  name3  name4
#1: A100 StuffA StuffX 0.146    NA -0.019 0.2102

myfunc(DT, ID_filter = "A100", info1 = "StuffA", info2 = "StuffY")
#Empty data.table (0 rows and 7 cols): ID,info1,info2,name1,name2,name3...

myfunc(DT) # if all the parameters are "", return the full data
#     ID  info1  info2  name1  name2  name3  name4
#1: A100 StuffA StuffX 0.1460     NA -0.019 0.2102
#2: A101 StuffA StuffY 0.0987 -1.307 -0.174     NA

数据

DT <- structure(list(ID = c("A100", "A101"), info1 = c("StuffA", "StuffA"
), info2 = c("StuffX", "StuffY"), name1 = c(0.146, 0.0987), name2 = c(NA, 
-1.307), name3 = c(-0.019, -0.174), name4 = c(0.2102, NA)), 
 class = "data.frame", row.names = c(NA, 
-2L))