summarise在没有非NA值时不会从max返回警告

13

max(x, na.rm = TRUE) 被调用时,如果没有非NA值存在,它会返回-Inf,并出现警告。但是,在某些情况下,dplyr中的summarise函数不会返回警告:

library(magrittr)
library(dplyr)

df1 <- data.frame(a = c("a","b"), b = c(NA,NA))
df1 %>% group_by(a) %>% summarise(x = max(b, na.rm = TRUE))
# Three warnings, as expected.

df2 <- data.frame(a = c("a","b"), b = c(1,NA))
df2 %>% group_by(a) %>% summarise(x = max(b, na.rm = TRUE))
# No warning. Unexpected.

有趣的是,如果我重新命名该函数,就会按预期收到警告:

# Pointer to same function.
stat <- max

df1 <- data.frame(a = c("a","b"), b = c(NA,NA))
df1 %>% group_by(a) %>% summarise(x = stat(b, na.rm = TRUE))
# Three warnings, as expected.

df2 <- data.frame(a = c("a","b"), b = c(1,NA))
df2 %>% group_by(a) %>% summarise(x = stat(b, na.rm = TRUE))
# Single warning, as expected.

实际上,我认为应该是两个警告而不是三个,因为只有两组需要进行summarise。但我不确定内部警告系统的工作方式,所以也许三个警告就像预期的那样。

我的问题是:为什么summarise在特定情况下没有输出警告,如果这是预期的,那么一个简单的函数重命名为什么会改变这种行为?

我的sessionInfo()

R version 3.3.2 (2016-10-31)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 14.04.5 LTS

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C
 [9] LC_ADDRESS=C               LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base

other attached packages:
[1] dplyr_0.5.0.9000 magrittr_1.5

loaded via a namespace (and not attached):
[1] lazyeval_0.2.0.9000 R6_2.2.0            assertthat_0.1
[4] tools_3.3.2         DBI_0.5-1           tibble_1.2
[7] Rcpp_0.12.8
尽管我正在使用 "dev" 版本的 dplyr,但我也已经在可用的 CRAN 版本上进行了测试,并获得了相同的结果。

1
在我看来,group_by 中似乎有一个错误,因为没有使用它,summarize 可以给出预期的输出。此外,在您的第二种情况中,mutate 也不会返回警告。 - David Arenburg
1
此外,在您的第二种情况中,您得到的是NA而不是-Inf,这意味着问题出现在警告之前的某个地方。 - David Arenburg
很好的发现。df2 %>% group_by(a) %>% summarise(length(na.omit(b))) 确实证实了组 b 正在传递一个长度为零的向量给 max() - smci
2个回答

6
以下是部分诊断结果;表明dplyr某些地方搞砸了对函数名max()的引用。此外,dplyr通常在其参数上使用SE(标准评估):lazyeval::lazydots(..., .follow_symbols=F)),因此可能影响承诺,尽管我看不出来具体怎么影响:

A) group_by() 不是罪魁祸首。 df2 %>% group_by(a) %>% summarise(length(na.omit(b))) 证明 group b 正确传递了一个带有一个NA元素的向量给 max()

B) 当我们使用其限定名称base::max引用max时,我们确实会看到警告:

> df2 %>% group_by(a) %>% summarise(x = base::max(b, na.rm = TRUE))
       a     x
1      a     1
2      b  -Inf
Warning message:
In base::max(NA_real_, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf

我检查了一下,发现没有 dplyr:::max(),所以这不是命名空间遮蔽。

B2) 同样地,do.call(max, ...) 会如预期一样弹出警告。

> df2 %>% group_by(a) %>% summarise(x = do.call(max, list(b, na.rm = TRUE)))
       a     x
1      a     1
2      b  -Inf
Warning message:
In .Primitive("max")(NA_real_, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf

C) 此外,注意dplyr通常在其args上使用SE(标准评估):lazyeval::lazydots(..., .follow_symbols=F)),但我不知道这会导致什么问题。

C2) 我尝试使用以下代码重新创建group_by的内部结果:

grouped_df(as.numeric(NA), list()), na.rm=T)

并通过类似以下方法重新创建该Promise:
p <- lazyeval::lazy_dots( max, list( grouped_df(as.numeric(NA), list()), na.rm=T )  , .follow_symbols=F)

我无法使用.follow_symbols=T来表达这个意思。

我对标准评估知之甚少,所以请参考http://adv-r.had.co.nz/Expressions.html#metaprogramming

使用的版本:dplyr 0.5.0;lazyeval 0.1.10;尽管最新的lazyeval版本是0.2.0(Hadley的)。


2
不错的侦探工作。我怀疑,在内部,max被C++函数覆盖了。 - nograpes
个人怀疑是 lazyeval::lazydots(..., .follow_symbols=F),但我不知道它在这种情况下会有什么区别。 - smci

2
对于max(),有一个混合版本可用,对于分组的数据帧工作速度更快,因为整个评估可以在C++中完成,而不需要为每个组调用R回调函数。 在dplyr 0.5.0中,当满足以下所有条件时,将触发混合版本:
  • 第一个参数引用了数据框中存在的变量
  • 第二个参数是一个logical常量
有关详细信息,请参见混合vignettemax()的混合版本在某些方面与R实现不同:
  • 对于空向量不会引发警告,而是默默地返回-Inf
  • 一个全部是NA的向量即使使用na.rm = TRUE也会返回NA
在您的示例中,c(NA, NA)是一个logical向量,因此dplyr会回退到每个组的一个R回调函数的“常规”评估。 如果需要原始行为,只需使用包装器或别名;混合评估器将回退到常规评估:
max_ <- max
data_frame(a = NA_real_) %>% summarise(a = max_(a, na.rm = TRUE))
## # A tibble: 1 × 1
##       a
##   <dbl>
## 1  -Inf
## Warning message:
## In max_(a, na.rm = TRUE) : no non-missing arguments to max; returning -Inf

有用的知识。如何在 R 命令行中判断 dplyr 调用哪个 max() 函数,而无需查看 C++ 源代码? - smci
1
@smci:最近,出现了内部函数verify_hybrid()verify_not_hybrid(),允许进行这种测试,但它们尚未记录并且可能会更改。请参见https://github.com/hadley/dplyr/pull/2306。 - krlmlr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接