【问题标题】:Per group, select first row and another which matches a condition每组,选择第一行和另一个匹配条件的行
【发布时间】:2026-01-11 14:30:01
【问题描述】:

假设我有以下data.table:

x <- data.table(a = c(1, 3, 2, 2, 4, 3, 7, 10, 9, 8),
                b = c(1, 1, 1, 2, 2, 2, 2, 3, 3, 3))

并且,在按b 分组后,我想选择以下行:

  • 是组的第一行
  • 在群组中拥有最高的a

如果单行同时满足这两个条件,则只应选择一次(该组将仅包含一行)。

这些选择中的每一个都是微不足道的:

x[, .SD[1], by = b]  # selects first row per group
#    b  a
# 1: 1  1
# 2: 2  2
# 3: 3 10

x[, .SD[which.max(a)], by = b]  # selects row with the highest 'a' in the group
#    b  a
# 1: 1  3
# 2: 2  7
# 3: 3 10

但我不知道如何同时做到这两个(显然.SD[1 | which.max(a)] 不起作用)。我可以分别执行它们,然后rbindlist 最终结果,但我想知道是否有更简单的方法。

为了清楚起见,在上述情况下,预期的输出将是(也可以接受不同的顺序):

   b  a
1: 1  1
2: 1  3
3: 2  2
4: 2  7
5: 3 10

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    一个选项是将索引 1(第一行)与 which.max 连接起来 - 也返回一个数字索引,然后取其中的 unique(如果 @987654323 返回相同的值 1 @ 并使用它来子集 data.table (.SD)

    x[, .SD[unique(c(1, which.max(a)))], by = b]
    #   b  a
    #1: 1  1
    #2: 1  3
    #3: 2  2
    #4: 2  7
    #5: 3 10
    

    或使用.I

    x[x[, .I[unique(c(1, which.max(a)))], by = b]$V1]
    

    【讨论】:

      【解决方案2】:

      这是我在dplyr 中的做法:

      library(dplyr)
      x <- data.frame(a = c(1, 3, 2, 2, 4, 3, 7, 10, 9, 8),
                      b = c(1, 1, 1, 2, 2, 2, 2, 3, 3, 3))
      
      x %>% group_by(b) %>% filter(row_number() == 1 | a == max(a))
      

      输出

      #   a  b
      #1: 1  1
      #2: 3  1
      #3: 2  2
      #4: 7  2
      #5: 10 3
      

      【讨论】:

        【解决方案3】:

        如果你只有这两列,就取两个表的并集:

        funion(
          x[, lapply(.SD, max), by=b],
          x[, lapply(.SD, first), by=b]
        )
        

        我猜 max 比你的 which.max 更有效,因为它已经过优化(参见 ?GForce)。

        【讨论】:

          最近更新 更多