使用 dplyr window 函数计算百分位数
Using dplyr window functions to calculate percentiles
我有一个可行的解决方案,但我正在寻找一个更清晰、更具可读性的解决方案,它可能利用了一些较新的 dplyr window 函数。
使用 mtcars 数据集,如果我想查看第 25、50、75 个百分位数以及每加仑英里数的平均值和计数 ("mpg") 按气缸数 ("cyl" ), 我使用以下代码:
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 等)更干净地完成此操作?干净地,我的意思是没有 "do" 语句。
谢谢
不确定如何在 dplyr
中避免 do()
,但是您可以使用 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
如果您希望它们按 cyl
列排序,请将 by
替换为 keyby
。
在dplyr 1.0
中,summarise
可以return多个值,允许如下:
library(tidyverse)
mtcars %>%
group_by(cyl) %>%
summarise(quantile = scales::percent(c(0.25, 0.5, 0.75)),
mpg = quantile(mpg, c(0.25, 0.5, 0.75)))
或者,您可以使用 enframe
:
来避免使用单独的行来命名分位数
mtcars %>%
group_by(cyl) %>%
summarise(enframe(quantile(mpg, c(0.25, 0.5, 0.75)), "quantile", "mpg"))
cyl quantile mpg
<dbl> <chr> <dbl>
1 4 25% 22.8
2 4 50% 26
3 4 75% 30.4
4 6 25% 18.6
5 6 50% 19.7
6 6 75% 21
7 8 25% 14.4
8 8 50% 15.2
9 8 75% 16.2
以前版本的回答dplyr
library(tidyverse)
mtcars %>%
group_by(cyl) %>%
summarise(x=list(enframe(quantile(mpg, probs=c(0.25,0.5,0.75)), "quantiles", "mpg"))) %>%
unnest(x)
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
可以使用 tidyeval 将其转换为更通用的函数:
q_by_group = function(data, value.col, ..., probs=seq(0,1,0.25)) {
groups=enquos(...)
data %>%
group_by(!!!groups) %>%
summarise(x = list(enframe(quantile({{value.col}}, probs=probs), "quantiles", "mpg"))) %>%
unnest(x)
}
q_by_group(mtcars, mpg)
q_by_group(mtcars, mpg, cyl)
q_by_group(mtcars, mpg, cyl, vs, probs=c(0.5,0.75))
q_by_group(iris, Petal.Width, Species)
这是一个dplyr
方法,使用了broom
包的tidy()
功能,不幸的是它仍然需要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
包没有命名数字的方法。
这是基于我的。
如果您准备好使用 purrr::map
,您可以这样做!
library(tidyverse)
mtcars %>%
tbl_df() %>%
nest(-cyl) %>%
mutate(Quantiles = map(data, ~ quantile(.$mpg)),
Quantiles = map(Quantiles, ~ bind_rows(.) %>% gather())) %>%
unnest(Quantiles)
#> # A tibble: 15 x 3
#> cyl key value
#> <dbl> <chr> <dbl>
#> 1 6 0% 17.8
#> 2 6 25% 18.6
#> 3 6 50% 19.7
#> 4 6 75% 21
#> 5 6 100% 21.4
#> 6 4 0% 21.4
#> 7 4 25% 22.8
#> 8 4 50% 26
#> 9 4 75% 30.4
#> 10 4 100% 33.9
#> 11 8 0% 10.4
#> 12 8 25% 14.4
#> 13 8 50% 15.2
#> 14 8 75% 16.2
#> 15 8 100% 19.2
由 reprex package (v0.2.1)
创建于 2018-11-10
这种方法的一个好处是输出很整洁,每行一个观察值。
这是一个相当易读的解决方案,它以整洁的格式使用 dplyr
和 purrr
到 return 分位数:
代码
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
此解决方案仅使用 dplyr
和 tidyr
,允许您在 dplyr
链中指定分位数,并利用 tidyr::crossing()
到 "stack"分组和汇总之前的数据集的多个副本。
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()
知道您每组只需要一个值的必要条件。
这是使用 dplyr
、purrr
和 rlang
组合的解决方案:
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(tidyr)
library(purrr)
# load data
data("mtcars")
# Percentiles used in calculation
p <- c(.25,.5,.75)
p_names <- paste0(p*100, "%")
p_funs <- map(p, ~partial(quantile, probs = .x, na.rm = TRUE)) %>%
set_names(nm = p_names)
# dplyr/purrr/rlang solution
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg), funs(!!!p_funs))
#> # A tibble: 3 x 4
#> cyl `25%` `50%` `75%`
#> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 26 30.4
#> 2 6 18.6 19.7 21
#> 3 8 14.4 15.2 16.2
#Especially useful if you want to summarize more variables
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg, drat), funs(!!!p_funs))
#> # A tibble: 3 x 7
#> cyl `mpg_25%` `drat_25%` `mpg_50%` `drat_50%` `mpg_75%` `drat_75%`
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 3.81 26 4.08 30.4 4.16
#> 2 6 18.6 3.35 19.7 3.9 21 3.91
#> 3 8 14.4 3.07 15.2 3.12 16.2 3.22
由 reprex package (v0.2.0) 创建于 2018-10-01。
编辑(2019-04-17):
自 dplyr 0.8.0
起,funs
函数已被弃用,取而代之的是使用 list
将所需函数传递到作用域 dplyr
函数中。因此,上面的实现变得更加直接。我们不再需要担心使用 !!!
取消引用函数。请看下面reprex
:
library(dplyr)
#> Warning: package 'dplyr' was built under R version 3.5.2
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(tidyr)
library(purrr)
# load data
data("mtcars")
# Percentiles used in calculation
p <- c(.25,.5,.75)
p_names <- paste0(p*100, "%")
p_funs <- map(p, ~partial(quantile, probs = .x, na.rm = TRUE)) %>%
set_names(nm = p_names)
# dplyr/purrr/rlang solution
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg), p_funs)
#> # A tibble: 3 x 4
#> cyl `25%` `50%` `75%`
#> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 26 30.4
#> 2 6 18.6 19.7 21
#> 3 8 14.4 15.2 16.2
#Especially useful if you want to summarize more variables
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg, drat), p_funs)
#> # A tibble: 3 x 7
#> cyl `mpg_25%` `drat_25%` `mpg_50%` `drat_50%` `mpg_75%` `drat_75%`
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 3.81 26 4.08 30.4 4.16
#> 2 6 18.6 3.35 19.7 3.9 21 3.91
#> 3 8 14.4 3.07 15.2 3.12 16.2 3.22
由 reprex package (v0.2.0) 创建于 2019-04-17。
do()
实际上是正确的用法,因为它是为分组转换而设计的。将其视为映射到数据框组的 lapply()
。 (对于这样一个专门的功能,像“do”这样的通用名称并不理想。但可能为时已晚。)
从道德上讲,在每个 cyl
组中,您想将 quantile()
应用于 mpg
列:
library(dplyr)
p <- c(.2, .5, .75)
mtcars %>%
group_by(cyl) %>%
do(quantile(.$mpg, p))
#> Error: Results 1, 2, 3 must be data frames, not numeric
除非那不起作用,因为 quantile()
不是 return 数据框;您必须明确地转换其输出。由于此更改相当于用数据框包装 quantile()
,因此您可以使用 gestalt 函数组合运算符 %>>>%
:
library(gestalt)
library(tibble)
quantile_tbl <- quantile %>>>% enframe("quantile")
mtcars %>%
group_by(cyl) %>%
do(quantile_tbl(.$mpg, p))
#> # A tibble: 9 x 3
#> # Groups: cyl [3]
#> cyl quantile value
#> <dbl> <chr> <dbl>
#> 1 4 20% 22.8
#> 2 4 50% 26
#> 3 4 75% 30.4
#> 4 6 20% 18.3
#> 5 6 50% 19.7
#> 6 6 75% 21
#> 7 8 20% 13.9
#> 8 8 50% 15.2
#> 9 8 75% 16.2
回答了很多不同的方式。 dplyr distinct 改变了我想做的事情..
mtcars %>%
select(cyl, mpg) %>%
group_by(cyl) %>%
mutate( qnt_0 = quantile(mpg, probs= 0),
qnt_25 = quantile(mpg, probs= 0.25),
qnt_50 = quantile(mpg, probs= 0.5),
qnt_75 = quantile(mpg, probs= 0.75),
qnt_100 = quantile(mpg, probs= 1),
mean = mean(mpg),
sd = sd(mpg)
) %>%
distinct(qnt_0 ,qnt_25 ,qnt_50 ,qnt_75 ,qnt_100 ,mean ,sd)
渲染
# A tibble: 3 x 8
# Groups: cyl [3]
qnt_0 qnt_25 qnt_50 qnt_75 qnt_100 mean sd cyl
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 17.8 18.6 19.7 21 21.4 19.7 1.45 6
2 21.4 22.8 26 30.4 33.9 26.7 4.51 4
3 10.4 14.4 15.2 16.2 19.2 15.1 2.56 8
另一种实现方式,unnest_wider/longer
mtcars %>%
group_by(cyl) %>%
summarise(quants = list(quantile(mpg, probs = c(.01, .1, .25, .5, .75, .90,.99)))) %>%
unnest_wider(quants)
如果你想为多个变量做这件事,你可以在分组之前收集:
mtcars %>%
gather(key = 'metric', value = 'value', -cyl) %>%
group_by(cyl, metric) %>%
summarise(quants = list(quantile(value, probs = c(.01, .1, .25, .5, .75, .90,.99)))) %>%
unnest_wider(quants)
我有一个可行的解决方案,但我正在寻找一个更清晰、更具可读性的解决方案,它可能利用了一些较新的 dplyr window 函数。
使用 mtcars 数据集,如果我想查看第 25、50、75 个百分位数以及每加仑英里数的平均值和计数 ("mpg") 按气缸数 ("cyl" ), 我使用以下代码:
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 等)更干净地完成此操作?干净地,我的意思是没有 "do" 语句。
谢谢
不确定如何在 dplyr
中避免 do()
,但是您可以使用 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
如果您希望它们按 cyl
列排序,请将 by
替换为 keyby
。
在dplyr 1.0
中,summarise
可以return多个值,允许如下:
library(tidyverse)
mtcars %>%
group_by(cyl) %>%
summarise(quantile = scales::percent(c(0.25, 0.5, 0.75)),
mpg = quantile(mpg, c(0.25, 0.5, 0.75)))
或者,您可以使用 enframe
:
mtcars %>%
group_by(cyl) %>%
summarise(enframe(quantile(mpg, c(0.25, 0.5, 0.75)), "quantile", "mpg"))
cyl quantile mpg <dbl> <chr> <dbl> 1 4 25% 22.8 2 4 50% 26 3 4 75% 30.4 4 6 25% 18.6 5 6 50% 19.7 6 6 75% 21 7 8 25% 14.4 8 8 50% 15.2 9 8 75% 16.2
以前版本的回答dplyr
library(tidyverse)
mtcars %>%
group_by(cyl) %>%
summarise(x=list(enframe(quantile(mpg, probs=c(0.25,0.5,0.75)), "quantiles", "mpg"))) %>%
unnest(x)
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
可以使用 tidyeval 将其转换为更通用的函数:
q_by_group = function(data, value.col, ..., probs=seq(0,1,0.25)) {
groups=enquos(...)
data %>%
group_by(!!!groups) %>%
summarise(x = list(enframe(quantile({{value.col}}, probs=probs), "quantiles", "mpg"))) %>%
unnest(x)
}
q_by_group(mtcars, mpg)
q_by_group(mtcars, mpg, cyl)
q_by_group(mtcars, mpg, cyl, vs, probs=c(0.5,0.75))
q_by_group(iris, Petal.Width, Species)
这是一个dplyr
方法,使用了broom
包的tidy()
功能,不幸的是它仍然需要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
包没有命名数字的方法。
这是基于我的
如果您准备好使用 purrr::map
,您可以这样做!
library(tidyverse)
mtcars %>%
tbl_df() %>%
nest(-cyl) %>%
mutate(Quantiles = map(data, ~ quantile(.$mpg)),
Quantiles = map(Quantiles, ~ bind_rows(.) %>% gather())) %>%
unnest(Quantiles)
#> # A tibble: 15 x 3
#> cyl key value
#> <dbl> <chr> <dbl>
#> 1 6 0% 17.8
#> 2 6 25% 18.6
#> 3 6 50% 19.7
#> 4 6 75% 21
#> 5 6 100% 21.4
#> 6 4 0% 21.4
#> 7 4 25% 22.8
#> 8 4 50% 26
#> 9 4 75% 30.4
#> 10 4 100% 33.9
#> 11 8 0% 10.4
#> 12 8 25% 14.4
#> 13 8 50% 15.2
#> 14 8 75% 16.2
#> 15 8 100% 19.2
由 reprex package (v0.2.1)
创建于 2018-11-10这种方法的一个好处是输出很整洁,每行一个观察值。
这是一个相当易读的解决方案,它以整洁的格式使用 dplyr
和 purrr
到 return 分位数:
代码
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
此解决方案仅使用 dplyr
和 tidyr
,允许您在 dplyr
链中指定分位数,并利用 tidyr::crossing()
到 "stack"分组和汇总之前的数据集的多个副本。
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()
知道您每组只需要一个值的必要条件。
这是使用 dplyr
、purrr
和 rlang
组合的解决方案:
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(tidyr)
library(purrr)
# load data
data("mtcars")
# Percentiles used in calculation
p <- c(.25,.5,.75)
p_names <- paste0(p*100, "%")
p_funs <- map(p, ~partial(quantile, probs = .x, na.rm = TRUE)) %>%
set_names(nm = p_names)
# dplyr/purrr/rlang solution
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg), funs(!!!p_funs))
#> # A tibble: 3 x 4
#> cyl `25%` `50%` `75%`
#> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 26 30.4
#> 2 6 18.6 19.7 21
#> 3 8 14.4 15.2 16.2
#Especially useful if you want to summarize more variables
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg, drat), funs(!!!p_funs))
#> # A tibble: 3 x 7
#> cyl `mpg_25%` `drat_25%` `mpg_50%` `drat_50%` `mpg_75%` `drat_75%`
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 3.81 26 4.08 30.4 4.16
#> 2 6 18.6 3.35 19.7 3.9 21 3.91
#> 3 8 14.4 3.07 15.2 3.12 16.2 3.22
由 reprex package (v0.2.0) 创建于 2018-10-01。
编辑(2019-04-17):
自 dplyr 0.8.0
起,funs
函数已被弃用,取而代之的是使用 list
将所需函数传递到作用域 dplyr
函数中。因此,上面的实现变得更加直接。我们不再需要担心使用 !!!
取消引用函数。请看下面reprex
:
library(dplyr)
#> Warning: package 'dplyr' was built under R version 3.5.2
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(tidyr)
library(purrr)
# load data
data("mtcars")
# Percentiles used in calculation
p <- c(.25,.5,.75)
p_names <- paste0(p*100, "%")
p_funs <- map(p, ~partial(quantile, probs = .x, na.rm = TRUE)) %>%
set_names(nm = p_names)
# dplyr/purrr/rlang solution
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg), p_funs)
#> # A tibble: 3 x 4
#> cyl `25%` `50%` `75%`
#> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 26 30.4
#> 2 6 18.6 19.7 21
#> 3 8 14.4 15.2 16.2
#Especially useful if you want to summarize more variables
mtcars %>%
group_by(cyl) %>%
summarize_at(vars(mpg, drat), p_funs)
#> # A tibble: 3 x 7
#> cyl `mpg_25%` `drat_25%` `mpg_50%` `drat_50%` `mpg_75%` `drat_75%`
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 4 22.8 3.81 26 4.08 30.4 4.16
#> 2 6 18.6 3.35 19.7 3.9 21 3.91
#> 3 8 14.4 3.07 15.2 3.12 16.2 3.22
由 reprex package (v0.2.0) 创建于 2019-04-17。
do()
实际上是正确的用法,因为它是为分组转换而设计的。将其视为映射到数据框组的 lapply()
。 (对于这样一个专门的功能,像“do”这样的通用名称并不理想。但可能为时已晚。)
从道德上讲,在每个 cyl
组中,您想将 quantile()
应用于 mpg
列:
library(dplyr)
p <- c(.2, .5, .75)
mtcars %>%
group_by(cyl) %>%
do(quantile(.$mpg, p))
#> Error: Results 1, 2, 3 must be data frames, not numeric
除非那不起作用,因为 quantile()
不是 return 数据框;您必须明确地转换其输出。由于此更改相当于用数据框包装 quantile()
,因此您可以使用 gestalt 函数组合运算符 %>>>%
:
library(gestalt)
library(tibble)
quantile_tbl <- quantile %>>>% enframe("quantile")
mtcars %>%
group_by(cyl) %>%
do(quantile_tbl(.$mpg, p))
#> # A tibble: 9 x 3
#> # Groups: cyl [3]
#> cyl quantile value
#> <dbl> <chr> <dbl>
#> 1 4 20% 22.8
#> 2 4 50% 26
#> 3 4 75% 30.4
#> 4 6 20% 18.3
#> 5 6 50% 19.7
#> 6 6 75% 21
#> 7 8 20% 13.9
#> 8 8 50% 15.2
#> 9 8 75% 16.2
回答了很多不同的方式。 dplyr distinct 改变了我想做的事情..
mtcars %>%
select(cyl, mpg) %>%
group_by(cyl) %>%
mutate( qnt_0 = quantile(mpg, probs= 0),
qnt_25 = quantile(mpg, probs= 0.25),
qnt_50 = quantile(mpg, probs= 0.5),
qnt_75 = quantile(mpg, probs= 0.75),
qnt_100 = quantile(mpg, probs= 1),
mean = mean(mpg),
sd = sd(mpg)
) %>%
distinct(qnt_0 ,qnt_25 ,qnt_50 ,qnt_75 ,qnt_100 ,mean ,sd)
渲染
# A tibble: 3 x 8
# Groups: cyl [3]
qnt_0 qnt_25 qnt_50 qnt_75 qnt_100 mean sd cyl
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 17.8 18.6 19.7 21 21.4 19.7 1.45 6
2 21.4 22.8 26 30.4 33.9 26.7 4.51 4
3 10.4 14.4 15.2 16.2 19.2 15.1 2.56 8
另一种实现方式,unnest_wider/longer
mtcars %>%
group_by(cyl) %>%
summarise(quants = list(quantile(mpg, probs = c(.01, .1, .25, .5, .75, .90,.99)))) %>%
unnest_wider(quants)
如果你想为多个变量做这件事,你可以在分组之前收集:
mtcars %>%
gather(key = 'metric', value = 'value', -cyl) %>%
group_by(cyl, metric) %>%
summarise(quants = list(quantile(value, probs = c(.01, .1, .25, .5, .75, .90,.99)))) %>%
unnest_wider(quants)