大多数机器学习模型都要求数据为数值型——所有对象或类别数据必须先转换为数值格式。然而,实际上,有时类别数据会非常有用(对于人类来说,大多数时候它比我们的机器更有用)。离散化(或分箱)正是做到了这一点——将数值数据转换为类别数据!
根据你的目标,有很多方法可以对数据进行分类。在这里,我们将使用一个简单的数据集来展示六种不同的分箱方法。从等宽到基于聚类的方法,我们将把这些数值扫入一些类别箱中!
什么是离散化?
离散化,也称为分箱,是将连续数值变量转换为离散类别特征的过程。它涉及将连续变量的范围划分为多个区间(箱子),并根据数据点的值将其分配到这些箱子中。
为什么我们需要分箱?
哪些数据需要分箱?
通常从分箱中受益的数据:
通常不需要分箱的数据:
数据集
为了演示这些分箱技术,我们将使用这个人工数据集。假设这是某个高尔夫球场在15天内收集的天气状况数据。
import pandas as pd
import numpy as np
# Create the dataset as a dictionary
data = {
'UVIndex': [2, 10, 1, 7, 3, 9, 5, 11, 1, 8, 3, 9, 11, 5, 7],
'Humidity': [15, 95, 10, 98, 18, 90, 25, 80, 95, 40, 20, 30, 85, 92, 12],
'WindSpeed': [2, 90, 1, 30, 3, 10, 40, 5, 60, 15, 20, 45, 25, 35, 50],
'RainfallAmount': [5,2,7,3,18,3,0,1,25,0,9,0,18,7,0],
'Temperature': [68, 60, 63, 55, 50, 56, 57, 65, 66, 68, 71, 72, 79, 83, 81],
'Crowdedness': [0.15, 0.98, 0.1, 0.85, 0.2, 0.9, 0.92, 0.25, 0.12, 0.99, 0.2, 0.8, 0.05, 0.3, 0.95]
}
# Create a DataFrame from the dictionary
df = pd.DataFrame(data)
利用这个数据集,我们来看看如何将各种分箱技术应用到我们的列上!
方法一:等宽分箱
等宽分箱将变量的范围划分为指定数量的区间,所有这些区间的宽度都相同。
常见数据类型:这种方法适用于分布大致均匀的数据,并且当最小值和最大值具有实际意义时效果良好。
在我们的案例中:我们将对UV指数变量应用等宽分箱。我们将创建四个箱子:低、中、高和非常高。我们选择这种方法对UV指数进行分箱,因为它为我们提供了清晰、直观的指数范围划分,这有助于理解不同的指数范围如何影响打高尔夫的决策。
# 1. Equal-Width Binning for UVIndex
df['UVIndexBinned'] = pd.cut(df['UVIndex'], bins=4,
labels=['Low', 'Moderate', 'High', 'Very High'])
方法二:等频分箱(分位数分箱)
等频分箱创建的箱子包含大致相同数量的观测值。
常见数据类型:这种方法对于偏态数据或当你希望确保各类别之间均衡表示时特别有用。
在我们的案例中:我们将对等湿度变量应用等频分箱,创建三个箱子:低、中和高。我们选择这种方法对湿度进行分箱,是因为它能确保每个类别中有相同数量的观测值,如果湿度值在其范围内分布不均匀,这将非常有帮助。
# 2. Equal-Frequency Binning for Humidity
df['HumidityBinned'] = pd.qcut(df['Humidity'], q=3,
labels=['Low', 'Medium', 'High'])
方法三:自定义分箱
自定义分箱允许你根据领域知识或特定需求来定义自己的箱子边界。
常见数据类型:当你在领域中具有特定且有意义的阈值,或者当你想关注特定值范围时,这种方法非常理想。
在我们的案例中:我们将对降雨量应用自定义分箱。我们选择对这一列使用这种方法,是因为降雨有标准化的类别,这些类别比任意划分更有意义。
# 3. Custom Binning for RainfallAmount
df['RainfallAmountBinned'] = pd.cut(df['RainfallAmount'], bins=[-np.inf, 2, 4, 12, np.inf],
labels=['No Rain', 'Drizzle', 'Rain', 'Heavy Rain'])
方法四:对数分箱
对数分箱创建的箱子大小呈指数增长。这种方法基本上是先进行对数转换,然后进行等宽分箱。
常见数据类型:这种方法对于跨越多个数量级或遵循幂律分布的数据特别有用。
在我们的案例中:我们将对风速变量应用对数分箱。我们选择这种方法对风速进行分箱,是因为风对高尔夫球轨迹的影响可能不是线性的。从0到5英里/小时的变化可能比从20到25英里/小时的变化更为显著。
# 4. Logarithmic Binning for WindSpeed
df['WindSpeedBinned'] = pd.cut(np.log1p(df['WindSpeed']), bins=3,
labels=['Light', 'Moderate', 'Strong'])
方法五:基于标准差的分箱
基于标准差的分箱是根据数据距离均值的标准差数量来创建箱子的。这种方法在处理正态分布的数据或者当你想根据数据值偏离中心趋势的程度来进行分箱时非常有用。
变化形式:用于分箱的标准差具体数量可以根据分析的具体需求进行调整。箱子的数量通常是奇数(以便有一个中心箱子)。一些实现可能会使用不等宽的箱子,在均值附近使用较窄的箱子,在尾部使用较宽的箱子。
常见数据类型:这种方法非常适合遵循正态分布的数据,或者当你想要识别异常值并了解数据的分布范围时。对于高度偏态的分布可能不太适用。
在我们的案例中:我们将把这种分箱方法应用到温度变量上。我们选择这种方法对温度进行分箱,是因为它允许我们根据温度偏离平均值的程度来进行分类,这对于理解天气模式或气候趋势特别有用。
# 5. Standard Deviation-Based Binning for Temperature
mean_temp, std_dev = df['Temperature'].mean(), df['Temperature'].std()
bin_edges = [
float('-inf'), # Ensure all values are captured
mean_temp - 2.5 * std_dev,
mean_temp - 1.5 * std_dev,
mean_temp - 0.5 * std_dev,
mean_temp + 0.5 * std_dev,
mean_temp + 1.5 * std_dev,
mean_temp + 2.5 * std_dev,
float('inf') # Ensure all values are captured
]
df['TemperatureBinned'] = pd.cut(df['Temperature'], bins=bin_edges,
labels=['Very Low', 'Low', 'Below Avg', 'Average','Above Avg', 'High', 'Very High'])
方法六:K均值分箱
K均值分箱使用K均值聚类算法来创建箱子。它根据数据点之间的相似程度将数据点分组,每个聚类成为一个箱子。
常见数据类型:这种方法非常适合于在数据中寻找可能初看并不明显的群体。它适用于具有一个或多个峰值的数据,并且能够根据数据的组织方式进行调整。
在我们的案例中:我们将对拥挤度变量应用K均值分箱。我们选择这种方法对拥挤度进行分箱,是因为它可能会揭示出高尔夫球场繁忙程度的自然分组,这些分组可能受到简单阈值分箱无法捕捉到的各种因素的影响。
# 6. K-Means Binning for Crowdedness
kmeans = KMeans(n_clusters=3, random_state=42).fit(df[['Crowdedness']])
df['CrowdednessBinned'] = pd.Categorical.from_codes(kmeans.labels_, categories=['Low', 'Medium', 'High'])
结论
我们尝试了六种不同的方法来对高尔夫数据中的数值进行“离散化”。因此,最终的数据集现在看起来是这样的:
# Print only the binned columns
binned_columns = [col for col in df.columns if col.endswith('Binned')]
print(df[binned_columns])
让我们回顾一下每种分箱技术是如何转换我们的天气数据的:
重要的是要避免盲目地应用分箱技术。每个变量的性质和你的分析目标都是多变的,在选择分箱方法时,牢记这一点是很有帮助的。在许多情况下,尝试多种技术并比较它们的结果,可以为你提供对数据的最深入见解!
离散化总结
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
# Create the dataset
data = {
'UVIndex': [2, 10, 1, 7, 3, 9, 5, 11, 1, 8, 3, 9, 11, 5, 7],
'Humidity': [15, 95, 10, 98, 18, 90, 25, 80, 95, 40, 20, 30, 85, 92, 12],
'WindSpeed': [2, 90, 1, 30, 3, 10, 40, 5, 60, 15, 20, 45, 25, 35, 50],
'RainfallAmount': [5,2,7,3,18,3,0,1,25,0,9,0,18,7,0],
'Temperature': [68, 60, 63, 55, 50, 56, 57, 65, 66, 68, 71, 72, 79, 83, 81],
'Crowdedness': [0.15, 0.98, 0.1, 0.85, 0.2, 0.9, 0.92, 0.25, 0.12, 0.99, 0.2, 0.8, 0.05, 0.3, 0.95]
}
# Create a DataFrame from the dictionary
df = pd.DataFrame(data)
# 1. Equal-Width Binning for UVIndex
df['UVIndexBinned'] = pd.cut(df['UVIndex'], bins=4,
labels=['Low', 'Moderate', 'High', 'Very High'])
# 2. Equal-Frequency Binning for Humidity
df['HumidityBinned'] = pd.qcut(df['Humidity'], q=3,
labels=['Low', 'Medium', 'High'])
# 3. Custom Binning for RainfallAmount
df['RainfallAmountBinned'] = pd.cut(df['RainfallAmount'], bins=[-np.inf, 2, 4, 12, np.inf],
labels=['No Rain', 'Drizzle', 'Rain', 'Heavy Rain'])
# 4. Logarithmic Binning for WindSpeed
df['WindSpeedBinned'] = pd.cut(np.log1p(df['WindSpeed']), bins=3,
labels=['Light', 'Moderate', 'Strong'])
# 5. Standard Deviation-Based Binning for Temperature
mean_temp, std_dev = df['Temperature'].mean(), df['Temperature'].std()
bin_edges = [
float('-inf'), # Ensure all values are captured
mean_temp - 2.5 * std_dev,
mean_temp - 1.5 * std_dev,
mean_temp - 0.5 * std_dev,
mean_temp + 0.5 * std_dev,
mean_temp + 1.5 * std_dev,
mean_temp + 2.5 * std_dev,
float('inf') # Ensure all values are captured
]
df['TemperatureBinned'] = pd.cut(df['Temperature'], bins=bin_edges,
labels=['Very Low', 'Low', 'Below Avg', 'Average','Above Avg', 'High', 'Very High'])
# 6. KMeans Binning for Crowdedness
kmeans = KMeans(n_clusters=3, random_state=42).fit(df[['Crowdedness']])
df['CrowdednessBinned'] = pd.Categorical.from_codes(kmeans.labels_, categories=['Low', 'Medium', 'High'])
# Print only the binned columns
binned_columns = [col for col in df.columns if col.endswith('Binned')]
print(df[binned_columns])