R语言中ntile分位数分组后出现多范围的问题排查
问题原因
你遇到的问题核心在于分组区间的计算逻辑错误:
ntile(height, 3)是基于整个数据集的身高值划分出3个等数量的组,但你后续在group_by(country, gender, height_group, age_group)后,计算的是每个细分小组内的身高极值——这相当于把同一个全局height_group拆成了更小的子集,每个子集的身高范围自然不同,因此会出现同一个height_group对应多个height_range的情况。
修正方案
下面提供两种符合需求的修正代码,你可以根据偏好选择:
方案一:直接生成全局分位数区间(推荐)
用cut()结合quantile()直接为每个观测分配全局统一的分位数区间,避免后续区间混乱:
library(dplyr) set.seed(123) n <- 100 country <- sample(c("USA", "Canada", "UK"), n, replace = TRUE) gender <- sample(c("M", "F"), n, replace = TRUE) age <- sample(18:100, n, replace = TRUE) height <- runif(n, min = 150, max = 180) owns_bicycle <- sample(c("Yes", "No"), n, replace = TRUE) df <- data.frame(country, gender, age, height, owns_bicycle) # 修正后的核心代码 df %>% # 生成全局统一的身高3分位数区间 mutate( height_group = cut(height, breaks = quantile(height, probs = c(0, 1/3, 2/3, 1)), include.lowest = TRUE, dig.lab = 3), # 生成全局统一的年龄5分位数区间 age_group = cut(age, breaks = quantile(age, probs = seq(0, 1, 1/5)), include.lowest = TRUE, dig.lab = 3) ) %>% group_by(country, gender, height_group, age_group) %>% summarise( count = n(), percent_own_bicycle = mean(owns_bicycle == "Yes") * 100 ) %>% ungroup()
方案二:保留数字分组+关联全局区间
如果需要保留ntile()生成的数字分组编号,可以先提前计算全局分组对应的区间,再合并到结果中:
library(dplyr) set.seed(123) n <- 100 country <- sample(c("USA", "Canada", "UK"), n, replace = TRUE) gender <- sample(c("M", "F"), n, replace = TRUE) age <- sample(18:100, n, replace = TRUE) height <- runif(n, min = 150, max = 180) owns_bicycle <- sample(c("Yes", "No"), n, replace = TRUE) df <- data.frame(country, gender, age, height, owns_bicycle) # 提前计算全局身高分组对应的区间 height_intervals <- df %>% mutate(height_group = ntile(height, 3)) %>% group_by(height_group) %>% summarise(height_range = paste0(round(min(height), 1), "-", round(max(height), 1))) %>% ungroup() # 提前计算全局年龄分组对应的区间 age_intervals <- df %>% mutate(age_group = ntile(age, 5)) %>% group_by(age_group) %>% summarise(age_range = paste0(min(age), "-", max(age))) %>% ungroup() # 主计算流程 df %>% mutate(height_group = ntile(height, 3), age_group = ntile(age, 5)) %>% group_by(country, gender, height_group, age_group) %>% summarise( count = n(), percent_own_bicycle = mean(owns_bicycle == "Yes") * 100 ) %>% ungroup() %>% # 合并全局区间 left_join(height_intervals, by = "height_group") %>% left_join(age_intervals, by = "age_group") %>% # 调整列顺序 select(country, gender, height_group, height_range, age_group, age_range, count, percent_own_bicycle)
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

