摘要:欢迎来到利用 Pandas 进行数据预处理的第五部分——数据分箱。数据分箱(Data Binning or Bucketing)是一种数据预处理技术,用于减少次要噪声和找出数据中的有效模式。它涉及将原始数据分组到一组“桶” 或 “箱”中,这些桶通常基于值范围、频率或预定义的标准。在 Pandas 中,数据分箱常用于分类数据、减少小的观测误差或处理不连续的数据源。有时候,分箱还可以提升预测模型的准确性。
数据分箱是数据预处理的一种类型,数据预处理也是一种处理缺失值(Python Pandas数据预处理—Part 1:缺失值处理)、格式化(Python Pandas数据预处理—Part 2:数据格式化)、规范化(Python Pandas数据预处理—Part 3:数据规范化)和标准化(Python Pandas数据预处理—Part 4:数据标准化)的机制。
分箱可用于将数值数据转换为分类数据或对数值数据进行采样(量化)。
分箱是一种数据平滑技术。数据平滑可用于去除数据中的噪声。有三种数据平滑技术:
在本教程中,我们利用了 cupcake.csv 数据集,该数据集包含了在 Google Trends 上对 cupcake 这个词的趋势搜索。数据集获取方式见文末。首先,我们利用 pandas 库导入数据集,并通过 read_csv() 函数将其转换为数据框。
import pandas as pd
df = pd.read_csv('datasets/cupcake.csv')
df.head(5)
在这种情况下,我们定义每个箱子的边。在Python中,pandas 按距离分箱可以通过 cut() 函数实现。
我们将与 Cupcake 列相关的值分为三组:小、中、大。为了实现这一点,我们需要计算每组所属的区间。我们将最大值和最小值之间的差值作为区间范围,然后将该区间分成三部分,每一部分代表一组。我们利用数据框的 min() 和 max() 函数来计算 Cupcake 列的最小值和最大值。
min_value = df['Cupcake'].min()
max_value = df['Cupcake'].max()
print(f'min_value: {min_value}')
print(f'max_value: {max_value}')
输出:
min_value: 4
max_value: 100
现在我们可以开始计算每个区间的范围,即每个区间的最小值和最大值。因为我们有3个组,所以需要4个区间边(箱):
我们可以使用 numpy 包的 linspace() 函数来计算4个等宽分布的箱子:
import numpy as np
bins = np.linspace(min_value, max_value, 4)
bins
输出:
array([ 4., 36., 68., 100.])
现在,我们定义分组标签:
labels = ['small', 'medium', 'big']
我们可以使用 cut() 函数将列 Cupcake 的数值转换为分类值。通过参数指定箱子和标签。此外,我们将参数 include_lowest 设置为 True,使得区间包含最小值。
df['bins'] = pd.cut(df['Cupcake'], bins=bins, labels=labels,
include_lowest=True)
我们可以用直方图表示分组的分布,利用 matplotlib 包的 hist() 函数实现:
import matplotlib.pyplot as plt
plt.hist(df['bins'], bins=3)
或者,我们可以手动设置每个箱子的边缘:
bins = [0, 10, 50, 100]
df['bin_cut_manual'] = pd.cut(df['Cupcake'], bins=bins,
labels=labels, include_lowest=True)
plt.hist(df['bin_cut_manual'], bins=3)
plt.show()
按频率分箱需要计算每个箱的大小,以便每个箱包含(几乎)相同数量的观察值,但箱的范围会有所不同。可以使用 pandas的 qcut() 函数实现,可以用 precision 参数来控制小数点的位数。
df['bin-qcut'] = pd.qcut(df['Cupcake'], q=3, precision=1, labels=labels)
采样是数据分箱的另一种技术,它允许你通过对相似值或连续值进行分组的方式减少样本数量。有三种采样方法:
要执行采样,可以使用 scipy.stats 包的 binned_statistic() 函数。该函数接收两个数组(x_data 和 y_data)作为输入,以及要使用的统计数据(例如中位数或均值)和待创建的箱子数量。同时函数会返回箱子的值以及每个箱子的边。
from scipy.stats import binned_statistic
x_data = np.arange(0, len(df))
y_data = df['Cupcake']
x_bins, bin_edges, misc = binned_statistic(y_data, x_data,
statistic='median', bins=2)
现在,我们应该可以将 df['Cupcake'] 列的每个值近似估计为相应箱子的中位数。因此,我们将 bin 边转换为 IntervalIndex,它接收每个区间的左边和右边作为索引。在我们的例子中,左边从 bin 边的第一个值处开始,不包含 bin 边的最后一个值。相反,右边从 bin 边的第二个值开始,一直持续到最后一个值。
bin_intervals = pd.IntervalIndex.from_arrays(bin_edges[:-1], bin_edges[1:])
我们可以定义一个 set_to_median() 函数来量化 Cupcake 列,该函数循环遍历区间,当它找到正确的间隔时,则返回中间值。
def set_to_median(x, bin_intervals):
for interval in bin_intervals:
if x in interval:
return interval.mid
然后,我们用 apply() 函数将 set_to_median() 应用到 Cupcake 列:
df['sampled_cupcake'] = df['Cupcake'].apply(lambda x:
set_to_median(x, bin_intervals))
现在我们可以绘制结果。从图中我们注意到信息有所损失:
plt.plot(df['Cupcake'], label='original')
plt.plot(df['sampled_cupcake'], color='red', label='sampled')
plt.legend()
plt.show()
最后,我们可以绘制中位数。我们可以计算bin 范围中心处的值,它等于分箱值(x_bins)所对应的 y 值(y_bins) :
y_bins = (bin_edges[:-1] + bin_edges[1:]) / 2
y_bins
输出:
array([28., 76.])
然后,我们绘制图形:
plt.plot(x_data, y_data)
plt.xlabel('X')
plt.ylabel('Y')
plt.scatter(x_bins, y_bins, color='red', linewidth=5)
plt.show()
我们可以使用 jenkspy 包,它提供一个叫 jenks_breaks() 的函数,该函数利用 Fisher-Jenks 算法计算数组的自然断点。如果你的环境中没有这个包的话,可以通过在终端中运行 pip3 install jenkspy 来安装。
import jenkspy
breaks = jenkspy.jenks_breaks(df['Cupcake'], n_classes=3)
现在我们可以使用 cut() 函数来转换标签中的数据:
df['bin_cut_break'] = pd.cut(df['Cupcake'], bins=breaks,
labels=labels, include_lowest=True)
然后,我们绘制该分箱的直方图:
plt.hist(df['bin_cut_break'], bins=3)
plt.show()
在该教程中,我们学习了如何执行数据分箱,这是一种数据预处理技术。可以采用两种方法:第一种方法将数值数据转换为分类数据,第二种方法通过减少样本数量来执行数据采样。当需要对数据进行离散化处理时,数据分箱将会非常有用。
数据分箱可以应用在多种场景中,比如:
总之,数据分箱是一种简单且强大的数据预处理技术,有助于数据分析和模型构建过程。
示例数据集获取:关注我,在公众号聊天窗口中回复关键字“数据分箱”即可免费获取。赶快动手实践起来吧!Happy learning and good luck!
如果你觉得这篇文章对你有用,请点赞➕分享支持我,让更多的人加入我们的学习阵营。如果还想学习更多数据分析的相关技术和知识,请持续关注我......


往期推荐⬇️⬇️⬇️
Python Pandas数据预处理—Part 4:数据标准化
Python Pandas数据预处理—Part 3:数据规范化
Python Pandas数据预处理—Part 2:数据格式化
原文作者:数据派探险家;如有侵权,请联系管理员删除。