用于行方向分位数的{dplyr

时间:2017-04-18 19:08:56

标签: r dplyr

我有一个df的分层,每个分层有1000个样本来自该分层估计的后验分布。

mydf <- as.data.frame(lapply(seq(1, 1000), rnorm, n=100))
colnames(mydf) <- paste('s', seq(1, ncol(mydf)), sep='')

我想为每行的分布的几个分位数添加列。在经典R中,我会写这个。

quants <- t(apply(mydf, 1, quantile, probs=c(.025, .5, .975)))
colnames(quants) <- c('s_lo', 's_med', 's_hi')
mydf <- cbind(mydf, quants)

我怀疑在dplyr(也许是rowwise?)中有直接的方法可以做到这一点,但我的尝试失败了。想法?

3 个答案:

答案 0 :(得分:5)

dplyr未针对基于行的计算进行优化。虽然你可以使用rowwise()执行此操作,但我建议不要这样做:性能会非常糟糕。您的最佳速度可能是期望matrix,并且可以对行进行操作。我建议apply

为简洁而不是处理100x1000 data.frame而不是处理5列:

set.seed(2)
mydf <- as.data.frame(lapply(seq(1, 5), rnorm, n=10))
colnames(mydf) <- paste('s', seq(1, ncol(mydf)), sep='')

如果所有列都具有相同的matrix,则转换为class是合理的。在这种情况下,它们都是numeric所以我们是安全的。 (如果数据框中有非数字列,请仅提取此处所需的列,然后再将其绑定。)

mymtx <- as.matrix(mydf)
apply(mymtx, 1, quantile, c(0.1, 0.9))
#         [,1]     [,2]     [,3]     [,4]     [,5]       [,6]     [,7]     [,8]     [,9]    [,10]
# 10% 1.028912 1.430939 1.999521 0.305907 1.753824 0.03267599 1.934381 1.270504 2.995816 1.489634
# 90% 4.950067 3.807735 4.881554 6.123989 4.886388 5.55628806 4.207605 4.184460 4.406384 3.782134

值得注意的是,使用apply这样的结果是基于行的形式,可能是从人们期望的转换。只需将其打包在t(...)中,您就会看到您可能期望的列。

可以使用cbind或类似功能将其与原始数据帧重新组合。

这可以在如下的管道中完成:

mydf %>%
  bind_cols(as.data.frame(t(apply(., 1, quantile, c(0.1, 0.9)))))
#            s1         s2        s3       s4       s5        10%      90%
# 1   0.1030855  2.4176508 5.0908192 4.738939 4.616414 1.02891157 4.950067
# 2   1.1848492  2.9817528 1.8000742 4.318960 3.040897 1.43093918 3.807735
# 3   2.5878453  1.6073046 4.5896382 5.076164 4.158295 1.99952092 4.881554
# 4  -0.1303757  0.9603310 4.9546516 3.715842 6.903547 0.30590700 6.123989
# 5   0.9197482  3.7822290 3.0049378 3.223325 5.622494 1.75382406 4.886388
# 6   1.1324203 -0.3110691 0.5482936 3.404340 6.990920 0.03267599 5.556288
# 7   1.7079547  2.8786046 3.4772373 2.274020 4.694516 1.93438093 4.207605
# 8   0.7603020  2.0358067 2.4034418 3.097416 4.909156 1.27050387 4.184460
# 9   2.9844739  3.0128287 3.7922033 3.440938 4.815839 2.99581584 4.406384
# 10  0.8612130  2.4322652 3.2896367 3.753487 3.801232 1.48963385 3.782134

我会将列命名为你。

答案 1 :(得分:5)

对于类似data.frame的结构,由于数据结构的性质,很难有效地进行行向运算。更有效的解决方案可能是重新整形数据,在列中按块进行计算,然后将结果重新加入。使用dplyr + tidyr,就像这样:

library(dplyr)
library(tidyr)
mydf <- as_data_frame(mydf) %>% 
    mutate(id = row_number())

quants <- mydf %>% 
    gather(sample, value, -id) %>% 
    group_by(id) %>% 
    summarize(q025 = quantile(value, 0.025),
              q500 = quantile(value, 0.5),
              q975 = quantile(value, 0.975)) %>% 
    ungroup()

result <- left_join(quants, mydf)

或者,如果速度特别重要,请使用data.table ...

library(data.table)
setDT(mydf)
mydf[, id := .I]
mydf_melt <- melt(mydf, id.vars = 'id')
quants <- mydf_melt[, as.list(quantile(value, c(0.025, 0.5, 0.975))), by = id]
setkey(quants, 'id')
setkey(mydf, 'id')
result <- quants[mydf]

答案 2 :(得分:3)

purrr::pmap对于此类情况非常有用,可以通过列表中的项目并行迭代,其中data.frame以行方式运行。如果每个项目包含一个参数或函数接受点,它会更有用;否则你必须使用c收集矢量。

library(tidyverse)
set.seed(47)

mydf <- as.data.frame(lapply(seq(1000), rnorm, n = 100))
names(mydf) <- paste0('s', seq_along(mydf))

# make vector of each row; pass to quantile; convert to list; simplify to data.frame
mydf %>% pmap_df(~as.list(quantile(c(...), c(.025, .5, .975)))) %>% 
    bind_cols(mydf)    # self join to original columns

#> # A tibble: 100 × 1,003
#>      `2.5%`    `50%`  `97.5%`          s1       s2        s3       s4
#>       <dbl>    <dbl>    <dbl>       <dbl>    <dbl>     <dbl>    <dbl>
#> 1  24.52876 501.2313 974.1547  2.99469634 1.857485 4.8062449 5.412425
#> 2  25.96306 501.5381 975.4427  1.71114251 1.534527 5.0045983 4.029735
#> 3  25.36792 499.8048 974.9472  1.18540528 1.575371 2.1515656 4.537178
#> 4  27.15081 500.9932 975.3688  0.71823499 2.747321 0.9841692 3.774623
#> 5  25.77212 498.7223 974.5576  1.10877555 2.659429 4.6865536 5.448446
#> 6  25.43256 501.2437 973.7319 -0.08573747 2.198829 3.7851258 5.769600
#> 7  24.29993 500.8599 975.5050  0.01451784 1.938954 4.1822894 5.205473
#> 8  25.16637 501.8597 974.8636  1.01513086 3.492032 3.2551467 2.570020
#> 9  25.36332 500.3975 973.3588  0.74795410 3.660735 3.3051286 4.270915
#> 10 27.02456 499.8759 974.3890 -0.46575030 2.771156 3.4292355 3.372155
#> # ... with 90 more rows, and 996 more variables: s5 <dbl>, s6 <dbl>,
#> #   s7 <dbl>, s8 <dbl>, s9 <dbl>, s10 <dbl>, s11 <dbl>, s12 <dbl>,
#> #   s13 <dbl>, s14 <dbl>, ...

quantile生成的名称不是语法,但可以通过在set_names(c('s_lo', 's_med', 's_hi'))之前插入bind_cols轻松替换。如果您愿意,还有许多其他方法可以重新组合结果。

相关问题