如何在Pandas中按组分配并复用分位数分桶?
解决方案
问题核心
原代码报错是因为不同分组的pd.qcut生成的Categorical标签(Interval类型)属于不同的分类集合,直接赋值到同一列时,Pandas要求列的Categorical类型必须一致,导致类型冲突。
方法一:统一分类类型后赋值
先收集所有分组的Interval类别,创建统一的Categorical类型,再转换每个分组的标签后赋值,同时保留分桶边界:
import numpy as np import pandas as pd np.random.seed(42) # 固定随机种子便于复现 df1 = pd.DataFrame({"group": "A", "val": np.random.normal(loc=10, scale=5, size=100)}) df2 = pd.DataFrame({"group": "B", "val": np.random.normal(loc=5, scale=3, size=100)}) df = pd.concat([df1, df2], ignore_index=True) # 计算每个分组的分箱标签与边界 grouped_qcut = df.groupby("group")["val"].apply( lambda x: pd.qcut(x, q=5, duplicates="drop", retbins=True) ) # 收集所有分组的Interval类别并去重排序 all_categories = [] for group in grouped_qcut.index: all_categories.extend(grouped_qcut[group][0].cat.categories) all_categories = sorted(list(set(all_categories)), key=lambda x: x.left) # 创建统一的Categorical类型 unified_cat = pd.CategoricalDtype(categories=all_categories, ordered=True) # 保存分桶边界到字典,供后续新数据使用 bin_borders = {group: grouped_qcut[group][1] for group in grouped_qcut.index} # 转换标签类型并赋值 for group in df["group"].unique(): converted_labels = grouped_qcut[group][0].astype(unified_cat) df.loc[df["group"] == group, "value_label"] = converted_labels
方法二:使用groupby.apply统一处理(更简洁)
通过groupby.apply对每个分组添加标签,合并时Pandas会自动合并不同分组的分类集合:
import numpy as np import pandas as pd np.random.seed(42) df1 = pd.DataFrame({"group": "A", "val": np.random.normal(loc=10, scale=5, size=100)}) df2 = pd.DataFrame({"group": "B", "val": np.random.normal(loc=5, scale=3, size=100)}) df = pd.concat([df1, df2], ignore_index=True) # 用于保存各分组分桶边界的字典 bin_borders = {} def add_qcut_label(group): labels, bins = pd.qcut(group["val"], q=5, duplicates="drop", retbins=True) bin_borders[group.name] = bins group["value_label"] = labels return group # 应用分箱并合并结果 df = df.groupby("group", group_keys=False).apply(add_qcut_label)
后续使用分桶边界处理新数据
用保存的bin_borders字典,对新数据的对应分组执行分箱:
# 示例:处理分组A的新数据 new_data = pd.DataFrame({"group": "A", "val": np.random.normal(loc=10, scale=5, size=20)}) new_data["value_label"] = new_data.groupby("group")["val"].apply( lambda x: pd.cut(x, bins=bin_borders[x.name], include_lowest=True) )
内容的提问来源于stack exchange,提问作者arabinelli
相关产品推荐
相关产品推荐

