我有一个与此类似的 data.table (df):

df <- read.table(header=TRUE, text='
ID AltID   Crit1   Crit2   Crit3
1  1       1       5       10
1  2       3       7       15
1  3       2       6       11')

对于每个 Crit -column 我有一个这样的上限和下限:
minCutoff = c(0, 5, 10)
maxCutoff = c(4, 7, 12)

从 data.table (df) 计算得出。

我想要一个函数,它排除一个值超出范围的任何行。此外,我希望此函数能够使用可变数量的 Crit 列(例如 3 个 Crit 列、4 个 Crit 列等),因为我的输入数据可能会发生变化。

因此,对于此示例,尽管 Crit3 (15) > maxCutoff (12)Crit1 在可接受的范围内,但第 1 行和第 3 行将被保留,但第 2 行将被丢弃,因为其 Crit2。因此,输出将是:
ID AltID   Crit1   Crit2   Crit3
1  1       1       5       10
1  3       2       6       11

我已经尝试使用 for 循环来计算我拥有的列数,然后使用嵌套的 for 循环来使用类似...
for (c in 1:(ncol(df)-2)+2)
{
    for (r in 1:nrow(df))
    {
     between(df[r,c], minCutoff[c], maxCutoff[c])
    }
}

* ncol(df)-2)+2 是由于围绕 ID 列工作

但是,现在我有 TON 的 T/F 值,我无法聚合这些值来确定是否应该保留或丢弃一行。

我确信有一种神奇的 R 方法可以使这个过程更简单,但我不够熟练,无法看到它。

如果有人有任何提示、技巧或其他线索来为我指明正确的方向,我将不胜感激。

最佳答案

您的数据:

df <- read.table(header=TRUE, text='
ID AltID   Crit1   Crit2   Crit3
1  1       1       5       10
1  2       3       7       15
1  3       2       6       11')
minCutoff = c(0, 5, 10)
maxCutoff = c(4, 7, 12)

特尔;博士:
df[rowSums(mapply(between, df[ grep("Crit", colnames(df)) ], minCutoff, maxCutoff)) >= 3,]
#   ID AltID Crit1 Crit2 Crit3
# 1  1     1     1     5    10
# 3  1     3     2     6    11

使用一个函数可以轻松处理可变数量的 Crit 列,依次应用于每个列,然后聚合结果。如果您已经在使用 dplyr 包,那么您已经有了 dplyr::between ,但如果没有,那么这里是一个可接受的替代品:
between <- function(x, low, hi) low <= x & x <= hi

我将带您完成工作:
isbetween <- mapply(between, df[ grep("Crit", colnames(df)) ], minCutoff, maxCutoff)
isbetween
#      Crit1 Crit2 Crit3
# [1,]  TRUE  TRUE  TRUE
# [2,]  TRUE  TRUE FALSE
# [3,]  TRUE  TRUE  TRUE
  • df[grepl("Crit", colnames(df)) ] 是一种(多种)方式,用于查看您感兴趣的列;
  • mapply 应用一个函数(在本例中为 between ),其中包含每个其他列表/向量的第一个值。它实际上等同于:
    between(df[3], minCutoff[1], maxCutoff[1])
    between(df[4], minCutoff[2], maxCutoff[2])
    ...
    

  • 现在我们在各自的截止值内有一个单独值的逻辑矩阵,我们查看每一行以检查它们是否满足您的过滤器要求 3 或更多。不幸的是,您列出的预期输出与您的规则不兼容,因此我将提供一些替代方案:
  • “其中任何 3 列不在范围内”,这意味着如果 FALSE 有 3 列或更多列,则应删除该行
    rowSums(!isbetween) >= 3
    # [1] FALSE FALSE FALSE
    
  • “其中至少有 3 列落在范围内”,这是您预期的输出建议:
    rowSums(isbetween) >= 3
    # [1]  TRUE FALSE  TRUE
    

  • 无论您选择哪个,都使用此逻辑向量并对行进行子集化,例如
    df[rowSums(isbetween) >= 3,]
    #   ID AltID Crit1 Crit2 Crit3
    # 1  1     1     1     5    10
    # 3  1     3     2     6    11
    

    (Rui's answer 和 this 之间的最大区别在于,该答案在 apply 上使用 data.frame 进行行操作,将相关列隐式转换为矩阵。我的答案按列进行(使用框架进行自然操作),因此未进行任何转换. 除了这种转换,如果框架不是很大,那么 row-wise 和 column-wise 的性能应该大致相同。如果它在很大程度上是不对称的(例如,行比列多得多),那么它可能是一个以列方式工作的速度稍快。R 中的矢量化工作几乎总是比迭代快得多。)

    关于r - 过滤位于 2 个数字向量之间的数据表行向量,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51770443/

    10-12 17:09