ddply multiple quantiles by group
Asked Answered
T

4

16

how can I do this calculation:

library(ddply)
quantile(baseball$ab)
  0%  25%  50%  75% 100% 
  0   25  131  435  705 

by groups, say by "team"? I want a data.frame with rownames "team" and column names "0% 25% 50% 75% 100%", i.e. one quantile call per group.

doing

ddply(baseball,"team",quantile(ab))

is not the correct solution. my problem is that the OUTPUT of each grouped operation is a vector of length 5 here.

in other words, what's a neat solution to this (nevermind the header):

m=data.frame()
for (i in unique(baseball$team)){m=rbind(m,quantile(baseball[baseball$team==i, ]$ab))}
head(m,3)
  X120 X120.1 X120.2 X120.3 X120.4
1  120  120.0  120.0 120.00    120
2  162  162.0  162.0 162.00    162
3   89   89.0   89.0  89.00     89
Tynes answered 14/3, 2014 at 11:14 Comment(0)
V
25

With base R you could use tapply and do.call

library(plyr)
do.call("rbind", tapply(baseball$ab, baseball$team, quantile))

do.call("rbind", tapply(baseball$ab, baseball$team, quantile, c(0.05, 0.1, 0.2)))

Or, with ddply

ddply(baseball, .(team), function(x) quantile(x$ab))
Valise answered 14/3, 2014 at 13:34 Comment(2)
That's the right answer! (i just messed up the definition of the anonymous function). thanks!Tynes
Yep. I knew that there was an easier way, but could not figure it out. Very nice solution.Algetic
P
9

A slightly different approach using dplyr:

library(tidyverse)

baseball %>% 
  group_by(team) %>% 
  nest() %>% 
  mutate(
    ret = map(data, ~quantile(.$ab, probs = c(0.25, 0.75))),
    ret = invoke_map(tibble, ret)
  ) %>%
  unnest(ret)

Here you can specify the needed quantiles in the probs argument.

The invoke_map call seems to be necessary, as quantile does not return a data frame; see this answer.

You can also put that all into a function:

get_quantiles <- function(.data, .var, .probs = c(0.25, 0.75), .group_vars = vars()) {
  .var = deparse(substitute(.var))
  return(
    .data %>% 
    group_by_at(.group_vars) %>% 
    nest() %>% 
    mutate(
      ret = map(data, ~quantile(.[[.var]], probs = .probs)),
      ret = invoke_map(tibble, ret)
    ) %>%
    unnest(ret, .drop = TRUE)
  )
}

mtcars %>% get_quantiles(wt, .group_vars = vars(cyl))

A new approach would be to use group_modify() from dplyr. Then you'd call:

baseball %>%
  group_by(team) %>% 
  group_modify(~{
    quantile(.x$ab, probs = c(0.25, 0.75)) %>% 
    tibble::enframe()
  }) %>%
  spread(name, value)
Polybius answered 10/10, 2018 at 7:58 Comment(0)
A
3

You should define the calculation for each quantile separately and use summarise. Also use .(team).

library(plyr)
data(baseball)
ddply(baseball,.(team),summarise, X0 = quantile(ab, probs = 0), X25 = quantile(ab, probs = 0.25), X50 = quantile(ab, probs = 0.50), X75 = quantile(ab, probs = 0.75), X100 = quantile(ab, probs = 1))
Algetic answered 14/3, 2014 at 11:19 Comment(1)
This answer is great as well. I wanted custom quantiles, not standard quartiles, so this is what I needed.Elgon
A
2

You can do this with non-standard quantiles, in dplyr:

library(plyr)
data(baseball)
library(dplyr)
prob=c(0.2, 0.8)
summarise(group_by(baseball,team), 
    p1 = quantile(ab, probs = prob[1]), 
    p2 = quantile(ab, probs = prob[2]))

NB this is dplyr::summarise, not plyr::summarise

Antisepsis answered 18/1, 2017 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.