【问题标题】:Filtering by different ranges by group in data.table在data.table中按组按不同范围过滤
【发布时间】:2026-01-16 08:45:01
【问题描述】:

我在表中有一些数据,我想做一个非 equi 连接(我认为这是正确的术语),并针对不同的组按不同的范围对其进行过滤。在下面的示例中,我想过滤组“a”,使其仅返回 1 和 20(含)之间的值和组“b”,因此它仅返回 80 和 100(含)之间的值。我的阅读表明 inrange 应该是要使用的项目。我知道如何在一般情况下使用它,但我不确定如何让它在不同的范围内按组运行。 (示例代码改编自?inrange

创建样本数据

set.seed(1234)
Y = data.table(a=sample(1:100,100), val=runif(100), group=c(rep("a",50),rep("b",50)))
range = data.table(group=c("a","b"),start = c(1,80), end = c(20,100))

尝试过滤

Y[inrange(a, range$start, range$end),,by=group]

这显然不起作用,而是将这些范围应用于整个数据集并抛出错误消息Ignoring by= because j= is not supplied。我想我很清楚这不起作用,因为我没有在范围表和 Y 之间创建“连接”,但我没有看到如何通过inrange 使两个表进行分组通信。

注意:实际上 a 中的值将是 posixct 日期时间,但为了简单起见,我在这里没有使用它。

【问题讨论】:

  • 您是否意识到runif 是从[0,1] 提取的,而您的range 似乎在[0,100] 上?
  • val 只是一个额外的列来近似真实数据(有额外的列可供使用)。过滤应在a 上。我认为没有问题。
  • 哦,我错过了加入a,这似乎很清楚(也很明显),我的错 :-)

标签: r data.table non-equi-join


【解决方案1】:

也许:

Y[range, K := TRUE, on = .(group, a >= start, a <= end)][!is.na(K),]
#         a        val  group      K
#     <int>      <num> <char> <lgcl>
#  1:     9 0.60189755      a   TRUE
#  2:     5 0.99874081      a   TRUE
#  3:    16 0.55512663      a   TRUE
#  4:     4 0.42944396      a   TRUE
#  5:    14 0.43101637      a   TRUE
#  6:     3 0.47880269      a   TRUE
#  7:     2 0.02220682      a   TRUE
#  8:     6 0.63891131      a   TRUE
#  9:     8 0.83470266      a   TRUE
# 10:    17 0.98304402      a   TRUE
# 11:    98 0.76785547      b   TRUE
# 12:    94 0.30766574      b   TRUE
# 13:    88 0.25814665      b   TRUE
# 14:    89 0.49954639      b   TRUE
# 15:    83 0.50892062      b   TRUE
# 16:    95 0.49443856      b   TRUE
# 17:    97 0.56695890      b   TRUE
# 18:    87 0.98970989      b   TRUE
# 19:    82 0.53190509      b   TRUE
# 20:   100 0.59662376      b   TRUE
#         a        val  group      K

还有其他方法可以做到这一点,但它们涉及重命名或丢失信息。例如,

  • 左加入rangeY,我们输了a

    Y[range, on = .(group, a >= start, a <= end)]
    #         a        val  group   a.1
    #     <int>      <num> <char> <int>
    #  1:     1 0.60189755      a    20
    #  2:     1 0.99874081      a    20
    #  3:     1 0.55512663      a    20
    # ...
    # 18:    80 0.98970989      b   100
    # 19:    80 0.53190509      b   100
    # 20:    80 0.59662376      b   100
    #         a        val  group   a.1
    

    解决方法是将Y$a 复制到一个新变量中并加入它:

    Y[,a1 := a][range, on = .(group, a1 >= start, a1 <= end)]
    #         a        val  group    a1  a1.1
    #     <int>      <num> <char> <int> <int>
    #  1:     9 0.60189755      a     1    20
    #  2:     5 0.99874081      a     1    20
    #  3:    16 0.55512663      a     1    20
    # ...
    # 18:    87 0.98970989      b    80   100
    # 19:    82 0.53190509      b    80   100
    # 20:   100 0.59662376      b    80   100
    #         a        val  group    a1  a1.1
    
  • 左加入Yrange,我们得到a 复制到startend,但没有明确的过滤指标:

    range[Y, on = .(group, start <= a, end >= a)]
    #       group start   end        val
    #      <char> <int> <int>      <num>
    #   1:      a    28    28 0.85026492
    #   2:      a    80    80 0.23466126
    #   3:      a    22    22 0.98816745
    # ...
    #  98:      b    82    82 0.53190509
    #  99:      b   100   100 0.59662376
    # 100:      b    30    30 0.26388647
    #       group start   end        val
    

    一种补救方法是复制另一个字段,这将为我们提供我们需要能够过滤的合并指示符。但即便如此,我们也必须重命名才能重新获得a 的数据:

    range[, K := TRUE][Y, on = .(group, start <= a, end >= a)][ !is.na(K), ]
    #      group start   end      K        val
    #     <char> <int> <int> <lgcl>      <num>
    #  1:      a     9     9   TRUE 0.60189755
    #  2:      a     5     5   TRUE 0.99874081
    #  3:      a    16    16   TRUE 0.55512663
    # ...
    # 18:      b    87    87   TRUE 0.98970989
    # 19:      b    82    82   TRUE 0.53190509
    # 20:      b   100   100   TRUE 0.59662376
    #      group start   end      K        val
    

【讨论】:

  • 我喜欢这个。必须添加 K 似乎有点不雅,但它有效,我只需要稍后删除它。我将把它留到明天,看看是否有其他的 data.table 解决方案。谢谢!
  • 我同意它看起来不优雅,但对保留哪些列和重命名哪些列所做的内部假设对我来说并不总是显而易见的。 (显然,只需将[, K := NULL] 添加到它的末尾即可摆脱K,我也希望这不是必需的。)需要类似于Y[,a1 := a][range, on = .(group, a1 &gt;= start, a1 &lt;= end)],其中清理需要[, c("a1","a1.1"):=NULL]。无论哪种方式......清理。