寻找R语言中ntile()的等区间划分等价实现方案
分组内等区间分箱的简洁实现方案
刚好碰到过类似的需求!你要的是分组数据里的等区间宽度分箱,和ntile()那种按样本数量均分的逻辑完全不是一回事。虽然base R的cut()能实现,但确实写法可以更清爽,下面给你几个实用的方案:
方案1:把你的逻辑封装成易用的自定义函数
你最初处理向量的思路完全没问题,把它封装成函数后,就能直接在dplyr的分组管道里用,非常顺手:
library(dplyr) library(purrr) set.seed(1234) # 定义等宽分箱函数,直接返回分箱序号 equi_width_bin <- function(x, bins = 4) { bin_size <- (max(x) - min(x)) / bins bin_limits <- min(x) + bin_size * 1:bins map_dbl(x, ~ min(which(.x <= bin_limits))) } # 测试分组场景 df <- data.frame( grouping = sample(1:2, 20, replace = TRUE), val = sample(1:100, 20) ) # 分组后调用自定义函数 df_equi_bin <- df %>% group_by(grouping) %>% mutate(equi_bin = equi_width_bin(val, bins = 4)) %>% ungroup() df_equi_bin
这个函数完全复用了你最初的逻辑,封装后在管道里调用就跟用ntile()一样方便。
方案2:简化版cut()写法,不用自定义函数
其实你觉得cut()写法麻烦,是因为没用到它的简化参数!配合as.integer()就能直接得到分箱序号,非常简洁:
df_equi_bin_cut <- df %>% group_by(grouping) %>% mutate(equi_bin = as.integer(cut(val, breaks = 4, include.lowest = TRUE))) %>% ungroup()
这里breaks = 4会自动帮你计算等宽的区间,include.lowest = TRUE确保最小值不会被排除在第一个区间外,as.integer()把cut返回的区间因子直接转成1到4的序号,是不是比你想的简单多了?
方案3:用ggplot2的cut_width(如果已经加载了ggplot2)
如果你刚好在用ggplot2,它的cut_width()是专门做等宽分箱的工具,同样可以转成分箱序号:
library(ggplot2) df_equi_bin_ggplot <- df %>% group_by(grouping) %>% mutate(equi_bin = as.integer(cut_width(val, width = (max(val)-min(val))/4))) %>% ungroup()
不过这个需要手动计算宽度,相比简化版的cut(),其实没那么省事,所以更推荐前两个方案。
关于内置函数的小遗憾
说实话,R base或者dplyr核心包里目前还没有像ntile()那样直接的等宽分箱内置函数,所以最简洁高效的方式要么是封装自己的小函数,要么就是用简化版的cut()。后者写法已经非常简洁,而且性能也不错,日常用完全足够。
内容的提问来源于stack exchange,提问作者Robert Hickman
相关产品推荐
相关产品推荐

