interpretacion - Usar las funciones de ventana dplyr para calcular percentiles
percentiles estadistica interpretacion (6)
Aquí hay un enfoque de dplyr
que evita do
pero que requiere una llamada separada a quantile
para cada valor de cuantil.
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
Sería mejor si summarise
podría devolver múltiples valores con una sola llamada a quantile
, pero esto parece ser un problema abierto en el desarrollo dplyr
.
ACTUALIZACIÓN: Aquí hay una variación de la respuesta de @ JuliaSilge que usa la anidación para obtener los cuantiles, pero sin el uso del map
. Sin embargo, requiere una línea de código adicional para agregar una columna que enumere los niveles de cuantil, ya que no estoy seguro de cómo (o si es posible) capturar los nombres de los cuantiles en una columna separada directamente de la llamada 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
Tengo una solución de trabajo, pero estoy buscando una solución más limpia y legible que tal vez aproveche algunas de las funciones más recientes de la ventana dplyr.
Usando el conjunto de datos mtcars, si quiero ver los percentiles 25, 50, 75 y la media y el recuento de millas por galón ("mpg") por el número de cilindros ("cyl"), uso el siguiente código:
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
¿Hay alguna forma de que pueda hacer esto más limpiamente con dplyr usando algunas de las funciones de resumen (n_tiles, percent_rank, etc.)? Por limpio, me refiero sin la declaración "do".
Gracias
Aquí hay una solución bastante legible que usa dplyr
y purrr
para devolver cuantiles en un formato ordenado:
Código
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)))
})
Resultado
# 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
Esta solución usa solo dplyr
y tidyr
, le permite especificar sus cuantiles en la cadena dplyr
y aprovecha tidyr::crossing()
para "apilar" múltiples copias del conjunto de datos antes de agrupar y resumir.
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
Resultado:
# 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
El unique()
es necesario para que dplyr::summarise()
sepa que solo desea un valor por grupo.
Este es un enfoque dplyr
que usa la función tidy()
del paquete broom
, lamentablemente aún requiere do()
, pero es mucho más simple.
library(dplyr)
library(broom)
mtcars %>%
group_by(cyl) %>%
do( tidy(t(quantile(.$mpg))) )
lo que da:
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
Tenga en cuenta el uso de t()
ya que el paquete de broom
no tiene un método para los valores numéricos con nombre.
Esto se basa en mi respuesta anterior para el resumen () aquí .
No estoy seguro de cómo evitar do()
en dplyr
, pero puede hacerlo con c()
y as.list()
con data.table
de una manera bastante sencilla:
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
Sustitúyalo by
keyby
si lo desea ordenado por columna de cyl
.
Si te gusta el uso de purrr::map
, ¡puedes hacerlo así!
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 buena de este enfoque es que la salida es ordenada, una observación por fila.