Суммирование не возвращает предупреждение от 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, с теми же результатами.
Ответы
Ответ 1
Для max()
доступна гибридная версия, которая работает намного быстрее для сгруппированного фрейма данных, потому что вся оценка может быть выполнена на С++ без обратного вызова R для каждой группы. В dplyr 0.5.0 гибридная версия запускается, когда выполняются все следующие условия:
- Первый аргумент относится к переменной, которая существует в кадре данных
- Второй аргумент - константа
logical
Подробнее см. гибридная виньетка.
Гибридная версия max()
в некоторых аспектах отличается от реализации R:
- Предупреждения не создаются для пустого вектора, тихо возвращаясь
-Inf
- Вектор all-
NA
вернет NA
даже с na.rm = TRUE
В вашем примере 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 - последний Хэдли