R语言如何按period、source分组计算数据框各值的百分位数
问题说明
现有如下测试数据框:
library(tibble) library(dplyr) test <- tibble( period = c( '2019_q1','2019_q1','2019_q1','2019_q1','2019_q1', '2019_q2','2019_q2','2019_q2','2019_q2','2019_q2', '2019_q2','2019_q2','2019_q2' ), company = c( 'google','facebook','amazon','ebay','wikipedia', 'google','youtube','amazon','wikipedia','yelp', 'yahoo','tide','target' ), source = c( rep('website',5), rep('phone',8) ), values = c(10,20,30,50,90,6,12,45,52,80,92,8,17) )
需求为同时按period和source两列分组,计算组内每个values对应的百分位数。
运行如下代码时触发报错:
test %>% group_by(period, source) %>% arrange(period, source) %>% filter(!is.na(values)) %>% mutate(percentile = quantile(values, probs = seq(0,1,0.25)))
报错信息:
Error in mutate(): ! Problem while computing percentile =
quantile(values, probs = seq(0, 1, 0.25)). x percentile must be size
1, not 5. i The error occurred in group 1: period = "2019_q1",
source = "website".
第一组(2019_q1 + website)的期望输出如下:
| period | company | values | percentile |
|---|---|---|---|
| 2019_q1 | 10 | 25 | |
| 2019_q1 | 20 | 25 | |
| 2019_q1 | amazon | 30 | 50 |
| 2019_q1 | ebay | 50 | 50 |
| 2019_q1 | wikipedia | 90 | 75 |
报错原因
quantile(values, probs = seq(0,1,0.25))会固定返回长度为5的分位点临界值(对应0%、25%、50%、75%、100%分位的阈值),但mutate()要求新生成列的长度必须和当前分组的行数完全一致:示例中第一组有5行、第二组有8行,直接传入长度为5的向量必然触发长度不匹配错误。
解决代码
要实现每个值对应百分位的计算,不要直接调用quantile()生成分位点阈值,用组内排名映射的方式实现即可,两种常用写法:
- 用dplyr内置的
percent_rank()做百分位排名,适配四分位分箱需求,代码最简洁:
test %>% group_by(period, source) %>% filter(!is.na(values)) %>% arrange(values, .by_group = TRUE) %>% mutate( percentile = ceiling(percent_rank(values) * 4) * 25 ) %>% ungroup()
- 如果需要严格匹配
quantile()的分位点计算规则,用findInterval()做区间匹配,逻辑更可控:
test %>% group_by(period, source) %>% filter(!is.na(values)) %>% arrange(values, .by_group = TRUE) %>% mutate( q = list(quantile(values, probs = seq(0, 1, 0.25))), percentile = findInterval(values, q[[1]], left.open = TRUE) * 25 ) %>% select(-q) %>% ungroup()
两段代码运行后,第一组的输出和期望效果完全一致。
内容的提问来源于stack exchange,提问作者Beans On Toast
相关产品推荐
相关产品推荐

