按组和列查找最小值

Finding minimum by groups and among columns

我试图在不同的列和组中找到最小值。 我的一小部分数据样本如下所示:

     group cut group_score_1 group_score_2
1        a   1             3           5.0
2        b   2             2           4.0
3        a   0             2           2.5
4        b   3             5           4.0
5        a   2             3           6.0
6        b   1             5           1.0

我想按组分组,对于每个组,在两个组分数中找到包含最小组分数的行,然后还得到包含最小值的列的名称(group_score_1 或group_score_2), 所以基本上我的结果应该是这样的:

      group cut group_score_1 group_score_2
1        a   0             2           2.5
2        b   1             5           1.0

我尝试了一些想法,最终想出将其分成几个新的数据框,按组过滤并选择相关列,然后使用 which.min(),但我确信还有更多有效的方法来做到这一点。不确定我错过了什么。

对于每个 group,您可以计算 min 值和 select 列之一中存在该值的行。

library(dplyr)

df %>%
  group_by(group) %>%
  filter({tmp = min(group_score_1, group_score_2);
          group_score_1 == tmp | group_score_2 == tmp})

#  group   cut group_score_1 group_score_2
#  <chr> <int>         <int>         <dbl>
#1 a         0             2           2.5
#2 b         1             5           1  

当您只有两个 group_score 列时,上面的方法很有效。如果您有很多这样的列,则不可能用 group_score_1 == tmp | group_score_2 == tmp 等列出每个列。在这种情况下,以长格式获取数据并获取对应的 cut 最小值的值和加入数据。假设 cut 在每个组中都是唯一的。

df %>%
  tidyr::pivot_longer(cols = starts_with('group_score')) %>%
  group_by(group) %>%
  summarise(cut = cut[which.min(value)]) %>%
  left_join(df, by = c("group", "cut"))

这是使用 pmin + ave + subset

的基础 R 选项
subset(
  df,
  as.logical(ave(
    do.call(pmin, df[grep("group_score_\d+", names(df))]),
    group,
    FUN = function(x) x == min(x)
  ))
)

这给出了

  group cut group_score_1 group_score_2
3     a   0             2           2.5
6     b   1             5           1.0

数据

> dput(df)
structure(list(group = c("a", "b", "a", "b", "a", "b"), cut = c(1L, 
2L, 0L, 3L, 2L, 1L), group_score_1 = c(3L, 2L, 2L, 5L, 3L, 5L
), group_score_2 = c(5, 4, 2.5, 4, 6, 1)), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6"))

我们可以使用data.table方法

library(data.table)
setDT(df)[df[, .I[which.min(do.call(pmin, .SD))], 
     group, .SDcols = patterns('^group_score')]$V1]
#   group cut group_score_1 group_score_2
#1:     a   0             2           2.5
#2:     b   1             5           1.0