2015-05-27 2 views
23

У меня есть рабочее решение, но я ищу более чистое и понятное решение, которое, возможно, использует некоторые из новых функций окна dplyr.Использование функций окна dplyr для вычисления процентилей

Используя набор данных mtcars, если я хочу посмотреть на 25-й, 50-й, 75-й процентили и среднее число миль на галлон («миль на галлон») на количество цилиндров («цил»), я использую следующий код:

library(dplyr) 
library(tidyr) 

# load data 
data("mtcars") 

# Percentiles used in calculation 
p <- c(.25,.5,.75) 

# old dplyr solution 
mtcars %>% group_by(cyl) %>% 
    do(data.frame(p=p, stats=quantile(.$mpg, probs=p), 
       n = length(.$mpg), avg = mean(.$mpg))) %>% 
    spread(p, stats) %>% 
    select(1, 4:6, 3, 2) 

# note: the select and spread statements are just to get the data into 
#  the format in which I'd like to see it, but are not critical 

есть ли способ, что я могу сделать это более чисто с dplyr, используя некоторые из суммарных функций (n_tiles, percent_rank и т.д.)? Чисто, я имею в виду без заявления «сделать».

Спасибо

+0

Я должен добавить, что этот код также использует пакет «tidyr», в котором функция «спред» поступает от – dreww2

ответ

32

Подход dplyr, который позволяет избежать do, но для каждого значения квантилирования требуется отдельный звонок quantile.

mtcars %>% group_by(cyl) %>% 
    summarise(`25%`=quantile(mpg, probs=0.25), 
      `50%`=quantile(mpg, probs=0.5), 
      `75%`=quantile(mpg, probs=0.75), 
      avg=mean(mpg), 
      n=n()) 

    cyl 25% 50% 75%  avg n 
1 4 22.80 26.0 30.40 26.66364 11 
2 6 18.65 19.7 21.00 19.74286 7 
3 8 14.40 15.2 16.25 15.10000 14 

Было бы лучше, если бы summarise может возвращать несколько значений с одним вызовом quantile, но это, кажется, an open issue в dplyr развития.

UPDATE: Вот вариация на тему @ ответ JuliaSilge, что использует вложенности, чтобы получить квантили, но без использования map. Тем не менее, для добавления столбца, в котором перечислены уровни квантилей, требуется дополнительная строка кода, так как я не знаю, как (или, если возможно) захватить имена квантилей в отдельный столбец непосредственно от вызова до quantile ,

p = c(0.25,0.5,0.75) 

mtcars %>% 
    group_by(cyl) %>% 
    summarise(quantiles = list(sprintf("%1.0f%%", p*100)), 
      mpg = list(quantile(mpg, p))) %>% 
    unnest 
cyl quantiles mpg 
1  4  25% 22.80 
2  4  50% 26.00 
3  4  75% 30.40 
4  6  25% 18.65 
5  6  50% 19.70 
6  6  75% 21.00 
7  8  25% 14.40 
8  8  50% 15.20 
9  8  75% 16.25 
+0

Спасибо - это тот ответ, который я искал, а это то, что вы можете это сделать, но не в бесшовной форме с одним вызовом квантиля (и что это открытая проблема в разработке dplyr). – dreww2

9

Не знаете, как избежать do() в dplyr, но вы можете сделать это с c() и as.list() с data.table в довольно простым способом:

require(data.table) 
as.data.table(mtcars)[, c(as.list(quantile(mpg, probs=p)), 
         avg=mean(mpg), n=.N), by=cyl] 
# cyl 25% 50% 75%  avg n 
# 1: 6 18.65 19.7 21.00 19.74286 7 
# 2: 4 22.80 26.0 30.40 26.66364 11 
# 3: 8 14.40 15.2 16.25 15.10000 14 

Replace by с keyby, если вы хотите их заказал cyl столбец.

+0

Хорошо. Я знал о методе 'as.list' в' .data.table', и я попробовал его в 'dplyr', но не смог. –

+0

Это приятное решение - я бы хотел использовать его для своего конкретного проекта, но не могу по причинам, не связанным с самим ответом – dreww2

11

Это dplyr подход, который использует tidy() функцию broom пакета, к сожалению, до сих пор требует do(), но это намного проще.

library(dplyr) 
library(broom) 

mtcars %>% 
    group_by(cyl) %>% 
    do(tidy(t(quantile(.$mpg)))) 

, который дает:

cyl X0. X25. X50. X75. X100. 
    (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) 
1  4 21.4 22.80 26.0 30.40 33.9 
2  6 17.8 18.65 19.7 21.00 21.4 
3  8 10.4 14.40 15.2 16.25 19.2 

Обратите внимание на использование t() с broom пакета не имеет метод для именованных числовых значений.

Это основано на моем earlier answer for summary() here.

11

Если вы хотите использовать purrr::map, вы можете сделать это вот так!

library(dplyr) 
library(tidyr) 
library(broom) 
library(purrr) 

mtcars %>% 
    nest(-cyl) %>% 
    mutate(Quantiles = map(data, ~ quantile(.$mpg))) %>% 
    unnest(map(Quantiles, tidy)) 

#> # A tibble: 15 × 3 
#>  cyl names  x 
#> <dbl> <chr> <dbl> 
#> 1  6 0% 17.80 
#> 2  6 25% 18.65 
#> 3  6 50% 19.70 
#> 4  6 75% 21.00 
#> 5  6 100% 21.40 
#> 6  4 0% 21.40 
#> 7  4 25% 22.80 
#> 8  4 50% 26.00 
#> 9  4 75% 30.40 
#> 10  4 100% 33.90 
#> 11  8 0% 10.40 
#> 12  8 25% 14.40 
#> 13  8 50% 15.20 
#> 14  8 75% 16.25 
#> 15  8 100% 19.20 

Одна хорошая вещь об этом подходе является выход опрятно, одно наблюдение за ряд.

+0

Спасибо, я думаю, что это самый чистый подход. – Fato39

0

Вот довольно читаемым решение, которое использует dplyr и purrr вернуть квантили в аккуратном формате:

Код

library(dplyr) 
library(purrr) 

mtcars %>% 
    group_by(cyl) %>% 
    do({x <- .$mpg 
     map_dfr(.x = c(.25, .5, .75), 
       .f = ~ data_frame(Quantile = .x, 
            Value = quantile(x, probs = .x))) 
     }) 

Результат

# A tibble: 9 x 3 
# Groups: cyl [3] 
    cyl Quantile Value 
    <dbl> <dbl> <dbl> 
1  4  0.25 22.80 
2  4  0.50 26.00 
3  4  0.75 30.40 
4  6  0.25 18.65 
5  6  0.50 19.70 
6  6  0.75 21.00 
7  8  0.25 14.40 
8  8  0.50 15.20 
9  8  0.75 16.25 
0

Это решение использует dplyr и tidyr, вы можете указать свои квантили в цепочке dplyr и использует tidyr::crossing() для «стека» нескольких копий набора данных перед группировкой и суммированием.

diamonds %>% # Initial data 
    tidyr::crossing(pctile = 0:4/4) %>% # Specify quantiles; crossing() is like expand.grid() 
    dplyr::group_by(cut, pctile) %>% # Indicate your grouping var, plus your quantile var 
    dplyr::summarise(quantile_value = quantile(price, unique(pctile))) %>% # unique() is needed 
    dplyr::mutate(pctile = sprintf("%1.0f%%", pctile*100)) # Optional prettification 

Результат:

# A tibble: 25 x 3 
# Groups: cut [5] 
     cut pctile quantile_value 
     <ord> <chr>   <dbl> 
1  Fair  0%   337.00 
2  Fair 25%  2050.25 
3  Fair 50%  3282.00 
4  Fair 75%  5205.50 
5  Fair 100%  18574.00 
6  Good  0%   327.00 
7  Good 25%  1145.00 
8  Good 50%  3050.50 
9  Good 75%  5028.00 
10  Good 100%  18788.00 
11 Very Good  0%   336.00 
12 Very Good 25%   912.00 
13 Very Good 50%  2648.00 
14 Very Good 75%  5372.75 
15 Very Good 100%  18818.00 
16 Premium  0%   326.00 
17 Premium 25%  1046.00 
18 Premium 50%  3185.00 
19 Premium 75%  6296.00 
20 Premium 100%  18823.00 
21  Ideal  0%   326.00 
22  Ideal 25%   878.00 
23  Ideal 50%  1810.00 
24  Ideal 75%  4678.50 
25  Ideal 100%  18806.00 

unique() необходимо, чтобы dplyr::summarise() знать, что вы хотите только одно значение для каждой группы.

Смежные вопросы