在数据分析与预处理过程中,经常会遇到需要对多个数据框的同一数值列采用统一的分组标准进行编码的场景,比如训练集和测试集需要使用相同的分位数分箱规则,避免数据分布差异导致模型效果异常。此时就需要先从基准数据框df2中提取分位数分箱的规则,再将该规则应用到目标数据框df1上完成分组编码。
分位数分箱的基本原理
分位数分箱是将连续数值按照分位数划分为若干个区间,每个区间对应一个分组编码。比如4分位数分箱会将数值分为4个区间,对应编码0、1、2、3。Pandas中常用pd.qcut函数实现分位数分箱,该函数可以自动计算指定分位数对应的边界值。
提取df2的分位数分箱规则
首先我们需要从df2中提取目标列的分位数分箱边界,后续将这些边界作为df1分箱的依据。假设df2中有一个数值列score,我们需要按照4分位数进行分箱,提取边界的代码如下:
import pandas as pd
import numpy as np
# 构造示例df2数据
np.random.seed(42)
df2 = pd.DataFrame({
'score': np.random.randint(0, 100, 200)
})
# 提取df2的4分位数分箱边界,这里获取分位点的边界值,不包含最小值和最大值
# q=4表示4分位数,retbins=True会返回分箱后的结果和边界数组
_, bins = pd.qcut(df2['score'], q=4, retbins=True)
print("df2分位数分箱边界:", bins)
上述代码中,bins变量存储的就是df2的4分位数分箱边界,是一个包含最小边界、各分位点边界、最大边界的数组,后续会直接用这个数组对df1进行分箱。
使用df2的分箱规则对df1编码
得到分箱边界后,我们可以使用pd.cut函数,将bins作为分箱的边界参数,对df1的对应列进行分组编码。pd.cut函数支持自定义分箱边界,正好满足跨数据框应用规则的需求。
构造示例df1数据并完成编码的代码如下:
# 构造示例df1数据
df1 = pd.DataFrame({
'score': np.random.randint(0, 100, 100)
})
# 使用df2的分箱边界对df1的score列进行分箱编码
# pd.cut的bins参数传入之前提取的bins数组,labels=False表示返回分组的索引编码
df1['score_bin'] = pd.cut(df1['score'], bins=bins, labels=False)
print("df1编码后结果前5行:")
print(df1.head())
这里需要注意,pd.cut的labels=False参数会返回每个数值所在的区间索引,从0开始计数,正好作为分组编码。如果需要自定义编码标签,可以传入labels参数指定对应的标签列表。
边界情况的注意事项
在实际使用中需要注意两个边界问题:
- 如果df1中的数值超出了df2分箱边界的最小值或最大值,
pd.cut会将该数值划分为NaN,此时可以通过include_lowest=True参数将最小值边界的数值包含在第一个区间内。 - 分位数分箱的边界是浮点型数值,如果df1的列是整数类型,分箱过程不会出现问题,编码结果依然正确。
处理边界情况的代码示例如下:
# 处理df1中存在超出df2分箱边界的情况
# include_lowest=True确保最小值边界的数值被包含在第一个区间
df1['score_bin_safe'] = pd.cut(df1['score'], bins=bins, labels=False, include_lowest=True)
# 查看是否存在NaN值
print("编码后NaN值数量:", df1['score_bin_safe'].isna().sum())
完整流程总结
整个跨数据框分组编码的流程可以总结为三步:第一步准备基准数据框df2和目标数据框df1;第二步用pd.qcut从df2提取分位数分箱边界;第三步用pd.cut将边界应用到df1完成编码。这种方法的优势是保证了两个数据框的分组标准完全一致,避免了单独分箱导致的分组区间差异,非常适合训练集和测试集的预处理场景。