2015-05-27 16 views
23

Ho una soluzione funzionante ma sono alla ricerca di una soluzione più chiara e più leggibile che forse sfrutta alcune delle più recenti funzioni della finestra di dplyr.Uso delle funzioni della finestra di dplyr per calcolare i percentili

Utilizzando il set di dati mtcars, se voglio guardare ai 25 °, 50 °, 75 ° percentile e la media e il numero di miglia per gallone ("mpg") per il numero di cilindri ("CYL"), io uso il codice seguente:

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 

c'è un modo che io possa fare questo in modo più pulito con dplyr utilizzando alcune delle funzioni di riepilogo (n_tiles, percent_rank, ecc)? In parole povere, intendo senza la dichiarazione "fai".

Grazie

+0

Dovrei aggiungere che questo codice utilizza anche il pacchetto "tidyr", che è dove la funzione "spread" viene da – dreww2

risposta

32

Ecco un approccio dplyr che evita do ma richiede una chiamata separata a quantile per ogni valore 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 

Sarebbe meglio se summarise potrebbe restituire più valori con una singola chiamata a quantile, ma questo sembra essere an open issue in dplyr sviluppo.

UPDATE: Ecco una variazione sul @ risposta di JuliaSilge che utilizza nidificazione per ottenere i quantili, ma senza l'uso di map. Tuttavia, richiede una riga aggiuntiva di codice per aggiungere una colonna che elenca i livelli quantili, poiché non sono sicuro di come (o se è possibile) catturare i nomi dei quantili in una colonna separata direttamente dalla chiamata a 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

Grazie - questa è la risposta che stavo cercando, che è che puoi farlo, ma non in modo trasparente con una singola chiamata a quantile (e che si tratta di un problema aperto nello sviluppo di dplyr). – dreww2

9

Non sei sicuro di come evitare do() in dplyr, ma si può fare questo con c() e as.list() con data.table in maniera piuttosto semplice:

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 

Sostituire by con keyby se si desidera ordinate per colonna cyl.

+0

Buono. Ero a conoscenza del metodo 'as.list' in' [.data.table' e l'ho provato in 'dplyr', ma non ci sono riuscito. –

+0

Questa è una bella soluzione - Vorrei poterla usare per il mio particolare progetto ma non per ragioni estranee alla risposta stessa – dreww2

11

Questo è un approccio dplyr che utilizza la funzione tidy() del pacchetto broom, purtroppo, richiede ancora do(), ma è molto più semplice.

library(dplyr) 
library(broom) 

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

che dà:

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 

Nota l'uso della t() poiché il pacchetto broom non dispone di un metodo per numerici denominati.

Questo è basato sul mio earlier answer for summary() here.

11

Se si utilizza purrr::map, è possibile farlo in questo modo!

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 

Una cosa bella di questo approccio è l'uscita è ordinato, un'osservazione per riga.

+0

Grazie, penso che questo sia l'approccio più pulito. – Fato39

0

Ecco una soluzione abbastanza leggibile che utilizza dplyr e purrr di tornare quantili in un formato ordinato:

Codice

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))) 
     }) 

Risultato

# 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

Questa soluzione utilizza dplyr eSolo, consente di specificare i quantili nella catena dplyr e di sfruttare tidyr::crossing() per "impilare" più copie del set di dati prima di raggruppare e riepilogare.

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 

Risultato:

# 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 

Il unique() è necessario lasciar dplyr::summarise() sapere che si desidera un solo valore per ogni gruppo.

Problemi correlati