R语言分组分位数计算:Method 2正确性验证及修正问询
分组分位数分组实现验证与优化
数据集构建
set.seed(123) library(dplyr) var1 = rnorm(10000, 100,100) var2 = rnorm(10000, 100,100) var3 = rnorm(10000, 100,100) var4 = rnorm(10000, 100,100) var5 <- factor(sample(c("Yes", "No"), 1000, replace=TRUE, prob=c(0.4, 0.6))) var6 <- factor(sample(c("Yes", "No"), 1000, replace=TRUE, prob=c(0.4, 0.6))) my_data = data.frame( var1, var2, var3, var4, var5, var6)
需求目标
- 按分类变量
var5和var6分组 - 对
var3划分10个分位组,生成变量class3 - 对
var4按20%间隔划分5个分位组,生成变量class4
当前两种手动实现方法
Method 1:生成较多NA值
library(dplyr) final = my_data %>% group_by(var5, var6) %>% mutate(class3 = case_when(ntile(var3, 10) == 1 ~ paste0(round(min(var3), 2), " to ", round(quantile(var3, 0.1), 2), " decile 1"), ntile(var3, 10) == 2 ~ paste0(round(quantile(var3, 0.1), 2), " to ", round(quantile(var3, 0.2), 2), " decile 2"), ntile(var3, 10) == 3 ~ paste0(round(quantile(var3, 0.2), 2), " to ", round(quantile(var3, 0.3), 2), " decile 3"), ntile(var3, 10) == 4 ~ paste0(round(quantile(var3, 0.3), 2), " to ", round(quantile(var3, 0.4), 2), " decile 4"), ntile(var3, 10) == 5 ~ paste0(round(quantile(var3, 0.4), 2), " to ", round(quantile(var3, 0.5), 2), " decile 5"), ntile(var3, 10) == 6 ~ paste0(round(quantile(var3, 0.5), 2), " to ", round(quantile(var3, 0.6), 2), " decile 6"), ntile(var3, 10) == 7 ~ paste0(round(quantile(var3, 0.6), 2), " to ", round(quantile(var3, 0.7), 2), " decile 7"), ntile(var3, 10) == 8 ~ paste0(round(quantile(var3, 0.7), 2), " to ", round(quantile(var3, 0.8), 2), " decile 8"), ntile(var3, 10) == 9 ~ paste0(round(quantile(var3, 0.8), 2), " to ", round(quantile(var3, 0.9), 2), " decile 9"), ntile(var3, 10) == 10 ~ paste0(round(quantile(var3, 0.9), 2), " to ", round(max(var3), 2), " decile 10"))) %>% mutate(class4 = case_when(ntile(var4, 20) == 1 ~ paste0(round(min(var4), 2), " to ", round(quantile(var4, 0.1), 2), " pcile 1"), ntile(var4, 20) == 2 ~ paste0(round(quantile(var4, 0.1), 2), " to ", round(quantile(var4, 0.2), 2), " pcile 2"), ntile(var4, 20) == 3 ~ paste0(round(quantile(var4, 0.2), 2), " to ", round(quantile(var4, 0.3), 2), " pcile 3"), ntile(var4, 20) == 4 ~ paste0(round(quantile(var4, 0.3), 2), " to ", round(quantile(var4, 0.4), 2), " pcile 4"), ntile(var4, 20) == 5 ~ paste0(round(quantile(var4, 0.4), 2), " to ", round(quantile(var4, 0.5), 2), " pcile 5")))
Method 2:生成较少NA值
final = my_data %>% group_by(var5, var6) %>% mutate(class3 = paste0(cut(var3, breaks = c(-Inf, quantile(var3, c(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)), Inf), labels = c("ptile 1", "ptile 2", "ptile 3", "ptile 4", "ptile 5", "ptile 6", "ptile 7", "ptile 8", "ptile 9", "ptile 10")), " (", round(min(var3), 2), " to ", round(max(var3), 2), ")")) %>% mutate(class4 = paste0(cut(var4, breaks = c(-Inf, quantile(var4, c(0.2, 0.4, 0.6, 0.8)), Inf), labels = c("ptile 1", "ptile 2", "ptile 3", "ptile 4", "ptile 5")), " (", round(min(var4), 2), " to ", round(max(var4), 2), ")"))
Method 2的问题分析与修正方案
存在的问题
Method 2的分组逻辑方向正确,但有两个明显缺陷:
- 区间标签信息错误:当前
class3和class4后拼接的是分组整体的最小/最大值,而非对应分位组的实际区间范围,无法体现分位的真实边界。 - 小样本分组下的分位数稳定性问题:
quantile()默认的type=7计算方式,在分组样本量较小时可能生成不稳定的分位数,甚至出现NA,导致cut()输出NA值。
修正后的实现方案
以下方案解决上述问题,同时简化逻辑、保证标签准确性:
library(dplyr) library(scales) # 用于统一数值格式化 final <- my_data %>% group_by(var5, var6) %>% # 处理var3的10分位分组 mutate( # 用type=1计算分位数,避免小样本下的NA与不稳定问题 q3 = quantile(var3, probs = seq(0, 1, 0.1), type = 1), # 执行分组,include.lowest确保最小值被归入第一组 class3_raw = cut(var3, breaks = q3, include.lowest = TRUE, labels = paste0("decile ", 1:10)), # 自动提取对应分位的区间并格式化标签 class3 = paste0(class3_raw, " (", number(q3[as.integer(class3_raw)], accuracy = 0.01), " to ", number(q3[as.integer(class3_raw)+1], accuracy = 0.01), ")") ) %>% # 处理var4的5分位分组(20%间隔) mutate( q4 = quantile(var4, probs = seq(0, 1, 0.2), type = 1), class4_raw = cut(var4, breaks = q4, include.lowest = TRUE, labels = paste0("pcile ", 1:5)), class4 = paste0(class4_raw, " (", number(q4[as.integer(class4_raw)], accuracy = 0.01), " to ", number(q4[as.integer(class4_raw)+1], accuracy = 0.01), ")") ) %>% # 移除临时变量 select(-q3, -class3_raw, -q4, -class4_raw) %>% ungroup()
关键优化点
- 采用
type=1的分位数计算逻辑,确保小样本分组下的断点稳定无NA。 include.lowest=TRUE避免最小值被遗漏在分组之外。- 自动匹配分位组对应的区间断点,生成准确的标签内容。
- 使用
scales::number()统一数值格式,保证输出美观一致。
另外,Method 1的核心问题在于ntile()强制均分的逻辑与quantile()的分位数逻辑不匹配,加上冗余的case_when写法容易出错,不推荐使用。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

