Подтвердить что ты не робот

Суммирование не возвращает предупреждение от max, если значения не-NA

Когда max(x, na.rm = TRUE) вызывается без значений NA, он возвращает -Inf с предупреждением. Однако в некоторых случаях функция summarise в dplyr не возвращает предупреждение:

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, с теми же результатами.

4b9b3361

Ответ 1

Для max() доступна гибридная версия, которая работает намного быстрее для сгруппированного фрейма данных, потому что вся оценка может быть выполнена на С++ без обратного вызова R для каждой группы. В dplyr 0.5.0 гибридная версия запускается, когда выполняются все следующие условия:

  • Первый аргумент относится к переменной, которая существует в кадре данных
  • Второй аргумент - константа logical

Подробнее см. гибридная виньетка.

Гибридная версия max() в некоторых аспектах отличается от реализации R:

В вашем примере 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

Ответ 2

Ниже представлен частичный диагноз; доказывает, что каким-то образом dplyr испортил ссылку на имя функции max(). Кроме того, dplyr обычно использует SE (стандартная оценка) в своих аргументах: lazyeval::lazydots(..., .follow_symbols=F)), поэтому, возможно, это влияет на обещание, хотя я не вижу, как:

A) group_by() не является виновником. df2 %>% group_by(a) %>% summarise(length(na.omit(b))) доказывает, что группа b передает вектор с одним элементом NA на max()

B) Когда мы ссылаемся на max по его квалифицированному имени base::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 обычно использует SE (стандартная оценка) для своих аргументов: lazyeval::lazydots(..., .follow_symbols=F)), но я не вижу, как это может вызвать это.

C2) Я попытался воссоздать внутренний результат group_by с помощью:

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

и воссоздать обещание с чем-то вроде:

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 - последний Хэдли