组填充最大值很慢,缺少值

Group-filling maximum is slow with missing values

我正在尝试在 R 中对 ~50k 行进行 groupby 最大操作。我的数据如下所示:

> head(df, 10)
   group       val
1      2 0.9891907
2      2 0.8965835
3      2        NA
4      2        NA
5      3        NA
6      4 0.8681051
7      4 0.7861292
8      5 0.9110303
9      7        NA
10     7        NA

我想根据 group 中的组创建一个新列 maxval,它的组最大值为 val。当一个组有任何非缺失值时,我想忽略缺失值,当该组有所有缺失值时,我想 return NA 。因此,前几行的预期结果为:

   group    val maxval
 1     2  0.989  0.989 # 0.989 is the max value for all of group == 2
 2     2  0.897  0.989
 3     2 NA      0.989
 4     2 NA      0.989
 5     3 NA     NA     # for group == 3, val is always missing, so return NA
 6     4  0.868  0.868
 7     4  0.786  0.868
 8     5  0.911  0.911
 9     7 NA     NA     # for group == 7, val is always missing, so return NA
10     7 NA     NA  

我尝试使用 dplyr 工具来做到这一点:

df %>% group_by(group) %>% mutate(maxval=max(val, na.rm=T)) %>% ungroup()

这有效*,但速度非常慢(将近 30 秒):

> system.time(df %>% group_by(group) %>% mutate(maxval=max(val, na.rm=T)) %>% ungroup())
   user  system elapsed 
 27.021   0.093  27.171 

* 请注意,因为它 return 是 -Inf 而不是 NA,但这可以很快解决。

如果我在对 max 的调用中省略 na.rm = T,则操作是即时的(0.06 秒)。但是输出不正确,因为 maxval 列中只有部分缺失值 return NA 的组。

我认为速度缓慢可能是由于在空序列上使用 max 产生的警告,但使用 suppressWarnings 并没有改善时间:

# following here: 
suppressWarnings(df %>% group_by(group) %>% mutate(maxval=max(val, na.rm=T)) %>% ungroup())

我找到了一个解决方案,我会 post,但我不太明白它为什么有效,我也想知道是否有更好的解决方案。我对 R 不是很熟悉,所以让我知道你会怎么做(或者如果我遗漏了一些明显的东西)。我愿意使用其他非基础包。谢谢!


数据构造代码:

set.seed(13)

# create data
n <- 50000
df <- data.frame(group = sample(1:n, size=n, replace=T),
                 val = runif(n))

# sort
df <- df[order(df$group), ]
rownames(df) <- NULL

# sparsify
df$val <- ifelse(df$val < .75, NA, df$val)

我从 this post 中获取了这个自定义最大值函数。根据需要,当组的所有值都是 NA:

时,它将 return NA
> my.max <- function(x) ifelse( !all(is.na(x)), max(x, na.rm=T), NA)
> df %>% group_by(group) %>% mutate(maxval=my.max(val)) %>% ungroup()
# A tibble: 50,000 x 3
   group    val maxval
   <int>  <dbl>  <dbl>
 1     2  0.989  0.989
 2     2  0.897  0.989
 3     2 NA      0.989
 4     2 NA      0.989
 5     3 NA     NA    
 6     4  0.868  0.868
 7     4  0.786  0.868
 8     5  0.911  0.911
 9     7 NA     NA    
10     7 NA     NA    
# ... with 49,990 more rows

> system.time(df %>% group_by(group) %>% mutate(maxval=my.max(val)) %>% ungroup())
   user  system elapsed 
   0.14    0.00    0.14 

这比 na.rm = F 的常规 max 慢 2-3 倍,但仍然比 na.rm = T 快很多倍(并给出正确的输出)。

如果整个向量是NA,我们可以使用if(){}绕过max计算。这是一个巨大的加速:

fmax = function(x, na.rm = TRUE) {
  if(all(is.na(x))) return(x[1])
  return(max(x, na.rm = na.rm))
}

system.time(df %>%
  group_by(group) %>%
  mutate(maxval = fmax(val)))
# user  system elapsed 
# 0.20    0.01    0.22