如何按分组断点对数据程序化分箱?(group_by与cut应用)
按分组自定义断点对数据分箱的程序化实现
我有两个分组一致的数据框,一个是基础数据,另一个是每组对应的独立断点集合。想通过这些断点把数据划分到不同分箱里,能不能用group_by() %>% cut()这类流程实现程序化操作,不用手动指定每组的断点?
以下是用iris数据集的可复现示例:
data("iris") boundaries <- iris %>% group_by(Species) %>% summarize(mean = mean(Sepal.Length), upper=mean(Sepal.Length)+0.2, lower=mean(Sepal.Length)-0.2) %>% data.frame() boundaries # Species mean upper lower # 1 setosa 5.006 5.206 4.806 # 2 versicolor 5.936 6.136 5.736 # 3 virginica 6.588 6.788 6.388
给整个数据集用单一断点集很简单:
boundsSample <- c(-Inf, as.numeric(boundaries[1,2:4]), Inf) binned <- iris %>% mutate(bin = cut(Sepal.Length, breaks=boundsSample, labels = c('1', '2', '3', '4')))
但我没法实现按分组分箱且不用手动提取每组断点。我试过下面的写法,但不知道怎么让group_by()作用于断点:
binned <- iris %>% group_by(Species) %>% mutate(bin = cut(Sepal.Length, breaks=boundaries, labels = c('1', '2', '3', '4')))
还试过把断点数据框和主数据合并,让每个数据点关联对应断点,但出现“断点不唯一”的错误:
joined <- left_join(iris, boundaries, by="Species") %>% mutate(bin = cut(Sepal.Length, breaks=c(-Inf, lower, mean, upper, Inf), labels = c('1', '2', '3', '4')))
解决方案
方法1:分组后匹配当前组的断点
利用dplyr的cur_group()获取当前分组信息,匹配boundaries中对应组的断点,再传入cut():
library(dplyr) binned <- iris %>% group_by(Species) %>% mutate( # 构造当前组的断点向量 group_breaks = list(c(-Inf, pull(boundaries[boundaries$Species == cur_group()$Species, c("lower", "mean", "upper")]), Inf)), bin = cut(Sepal.Length, breaks = unlist(group_breaks), labels = c('1', '2', '3', '4')) ) %>% select(-group_breaks) %>% # 移除临时生成的断点列 ungroup()
方法2:合并数据后按行处理
先合并基础数据和断点,再用rowwise()让每行单独生成断点并分箱,解决“断点不唯一”的问题:
library(dplyr) joined <- left_join(iris, boundaries, by = "Species") %>% rowwise() %>% mutate( bin = cut(Sepal.Length, breaks = c(-Inf, lower, mean, upper, Inf), labels = c('1', '2', '3', '4')) ) %>% ungroup()
两种方法都能实现按分组自动匹配对应断点完成分箱,无需手动指定每组的断点参数。
内容的提问来源于stack exchange,提问作者Jaken
相关产品推荐
相关产品推荐

