Вот решение, которое использует вложение, чтобы избежать многократного вызова quantile
. В любое время, когда вам нужно сохранить вектор результатов внутри summarize
, просто оберните его внутри list
. После этого вы можете удалить эти результаты, сопоставить их с именами и использовать spread
, чтобы поместить их в отдельные столбцы:
summarise_posterior2 <- function(data, var, ...){
qvar <- ensym(var)
vq <- c(0.025, 0.25, 0.5, 0.75, 0.975)
summarise( data, .qq = list(quantile(!!qvar, vq, names=FALSE)),
.nms = list(str_c("p", vq*100)), mean = mean(!!qvar), ... ) %>%
unnest %>% spread( .nms, .qq )
}
Это не дает вам такой же скорости, как у решения @ jay.sf
d <- data.frame("a"=sample(1:10, 5e5, TRUE), "b"=rnorm(5e5))
microbenchmark::microbenchmark( f1 = summarise_posterior(d, b, mean.b = mean(b), min=min(b)),
f2 = summarise_posterior2(d, b, mean.b = mean(b), min=min(b)) )
# Unit: milliseconds
# expr min lq mean median uq max neval
# f1 49.06697 50.81422 60.75100 52.43030 54.17242 200.2961 100
# f2 29.05209 29.66022 32.32508 30.84492 32.56364 138.9579 100
, но он будет корректно работать с group_by
и внутри вложенных функций (, тогда как решения на основе substitute
сломаются при вложении) .
r1 <- d %>% dplyr::group_by(a) %>% summarise_posterior(b, mean.b = mean(b), min=min(b))
r2 <- d %>% dplyr::group_by(a) %>% summarise_posterior2(b, mean.b = mean(b), min=min(b))
all_equal( r1, r2 ) # TRUE
Если вы профилируете код, вы увидите, где основные зависания
Rprof()
for( i in 1:100 )
d %>% dplyr::group_by(a) %>% summarise_posterior2(b, mean.b = mean(b), min=min(b))
Rprof(NULL)
summaryRprof()$by.self %>% head
# self.time self.pct total.time total.pct
# ".Call" 1.84 49.73 3.18 85.95
# "sort.int" 0.94 25.41 1.12 30.27
# "eval" 0.08 2.16 3.64 98.38
# "tryCatch" 0.08 2.16 1.44 38.92
# "anyNA" 0.08 2.16 0.08 2.16
# "structure" 0.04 1.08 0.08 2.16
.Call
соответствует в основном бэкэнду C ++ dplyr
, тогда как sort.int
является рабочим позади quantile()
. Решение @ jay.sf значительно ускоряется за счет отделения от dplyr
, но также теряет соответствующую гибкость (например, интеграция с group_by
). В конечном счете, вам решать, что важнее.