【sklearn】数据预处理之独热编码(含两种实现方式+代码理解)

最近学习机器学习,接触到独热编码相关内容,参考了一些资料,加上自己的思考,做出了如下总结:

一、什么是独热编码

独热编码,即One-Hot编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都有它独立的寄存器位,并且在任意时候,其中只有一位有效。

说起来这么复杂,举个例子就很容易理解了:

比如爱吃的水果有3种:苹果、葡萄和橙子,转换成独热编码分别表示为(此时上述描述中的N=3):001, 010, 100。(当然转换成100, 010, 001也可以,只要有确定的一一对应关系即可)。

那么提出一个问题,苹果、葡萄和橙子分别转换成1, 2, 3行不行?

一般不这样处理,这样处理也不叫独热编码了,只能说是文本转换成数字,具体原因可以往下看。

二、为什么要进行独热编码

在回归,分类,聚类等机器学习算法中,特征之间距离的计算或相似度的计算是非常重要的。而常用的距离或相似度的计算都是在欧式空间的相似度计算,计算余弦相似性,基于的就是欧式空间。

使用独热编码(One-Hot Encoding),将离散特征的取值扩展到了欧式空间,离散特征的某个取值就对应欧式空间的某个点。将离散型特征使用独热编码(One-Hot Encoding),会让特征之间的距离计算更加合理。

接下来举例说明:

我们选取离散型特征,共有五个取值,不使用独热编码(One-Hot Encoding),其表示分别是:

演员 =0;厨师 =1;公务员 =2;工程师 =3;律师 =4

两个工作之间的距离是:

d(演员,厨师) = 1
d(厨师,公务员) = 1
d(公务员,工程师) = 1
d(工程师,律师) = 1
d(演员,公务员) = 2
d(演员,工程师) = 3

显然这样的表示,计算出来的特征的距离是不合理。那如果使用独热编码(One-Hot Encoding),则得到d(演员,厨师) = 1与d(演员,公务员)都是1。那么,两个工作之间的距离就都是sqrt(2)。即每两个工作之间的距离是一样的,显得更合理。

此处参考文章为:

https://www.jianshu.com/p/42e93acacc52

三、如何用Python实现独热编码

方法1:get_dummies

将类别变量转换成虚拟变量/指示变量,也叫哑变量。

我们看一下它的定义:

pandas.get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None, 
                   sparse=False, drop_first=False, dtype=None)

各参数的含义:

data: array-like, Series, or DataFrame

prefix: string, list of strings, or dict of strings, default None

prefix_sep: str, default ‘_’ (转换后列名的前缀)

dummy_na: bool, default False(增加一列表示空缺值,如果False就忽略空缺值)

columns: list-like, default None (指定需要实现类别转换的列名)

sparse: bool, default False

drop_first: bool, default False (获得k中的k-1个类别值,去除第一个)

dtype: dtype, default np.uint8

举例说明一下:

import numpy as np
import pandas as pd
s=pd.Series(list('abcd'))
s_=pd.get_dummies(s)
print(s_)

我们看一下结果:

   a  b  c  d
0  1  0  0  0
1  0  1  0  0
2  0  0  1  0
3  0  0  0  1

我们换一种表示方法:

import numpy as np
import pandas as pd
s=pd.Series(list('abcd'))
s_=pd.get_dummies(s,drop_first=True)
print(s_)
   b  c  d
0  0  0  0
1  1  0  0
2  0  1  0
3  0  0  1

再来看一个例子:

import numpy as np
import pandas as pd
df=pd.DataFrame({'city':['hang zhou','shang hai','bei jing'],'population':[20,32,51],'class':['A','A+','A+']})
print(df)
df_=pd.get_dummies(df)
print(df_)

首先看一下df的输出结果:

        city  population class
0  hang zhou          20     A
1  shang hai          32    A+
2   bei jing          51    A+

看一下df_的输出结果:

   population  city_bei jing  city_hang zhou  city_shang hai  class_A  class_A+
0          20              0               1               0        1         0
1          32              0               0               1        0         1
2          51              1               0               0        0         1

方法2:sklearn

Sklearn提供了一个编码器OneHotEncoder,用于将整数分类值转换成独热向量。

我们举一个例子:

from scipy.io import loadmat
from sklearn.preprocessing import OneHotEncoder

dataset=loadmat('neural_network_dataset.mat')
print('数据集展示为:\n',dataset)

展示一下数据集的样子:

数据集展示为:
 {'__header__': b'MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Sun Oct 16 13:09:09 2011', '__version__': '1.0', '__globals__': [], 'X': array([[0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       ...,
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.]]), 'y': array([[10],
       [10],
       [10],
       ...,
       [ 9],
       [ 9],
       [ 9]], dtype=uint8)}

接下来我们把标签向量转换为独热向量:

X=dataset['X']
y=dataset['y']
encoder=OneHotEncoder(sparse=False)
y_onehot=encoder.fit_transform(y)
print(y[0])
print(y_onehot[0,:])

我们看一下输出结果:

[10]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
<think>好的,用户现在问的是当模型特征包独热编码数据和连续型数据时,应该采用什么样的数据预处理方法。这个问题看起来是关于混合类型特征的处理,这在机器学习项目中挺常见的。我需要先理解用户的需求,可能他们正在处理一个数据集,其中既有分类变量(经过独热编码处理)又有连续变量,想了解如何有效整合这些不同类型的特征来进行模型训练。 首先,用户可能已经知道独热编码的基本概念,但可能在实际操作中遇到了问题,比如数据尺度不一致,或者模型对不同类型的特征处理不当。他们需要的是预处理步骤,以确保模型能够有效利用这些不同类型的特征,避免出现性能下降或训练不稳定的情况。 接下来,我需要回顾处理混合类型数据的常见方法。独热编码后的特征通常是二进制的(0或1),而连续型特征的取值范围可能差异很大。这时候,标准化或归一化可能是必要的,尤其是对于使用梯度下降的模型(如神经网络、SVM、线性回归等)。但需要特别注意,独热编码后的特征是否需要进行缩放,这可能取决于具体情况。 另外,数据不平衡也是一个问题,尤其是当某些类别在独热编码后出现极端稀疏的情况。可能需要考虑是否进行特征选择,比如使用方差阈值过滤掉低方差的特征,或者通过嵌入法(如L1正则化)来选择重要特征。 还有,特征组合的可能性。有时候,将独热编码的特征与连续特征进行交互,可能会提升模型的表现,但这也需要谨慎处理,避免维度爆炸。 用户的另一个潜在问题是,如何处理这些不同预处理步骤的顺序。例如,是否应该先进行独热编码,再进行标准化?或者是否需要分别处理不同类型的数据?这需要明确步骤,确保流程正确。 此外,用户可能对某些预处理方法的效果不太清楚,比如标准化和归一化的区别,或者什么时候应该使用哪种方法。需要解释清楚这些方法的适用场景,以及它们对模型的影响。 还要考虑到不同模型对数据预处理的需求不同。例如,树模型(如随机森林、梯度提升树)通常不需要对连续特征进行标准化,因为它们对特征的尺度不敏感。而线性模型或神经网络则可能受益于标准化。用户可能没有明确说明他们使用的模型类型,因此需要给出通用的建议,同时指出不同模型的注意事项。 另一个需要注意的点是,独热编码可能导致的高维度问题。如果原始分类变量有很多类别,独热编码后会产生大量稀疏的特征,这可能会增加计算负担,甚至导致模型过拟合。这时候可能需要降维技术,如PCA,但PCA通常处理的是连续变量,对于稀疏的二进制变量可能效果不佳,因此需要特别处理,或者使用其他方法如特征选择。 总结用户可能的疑问点: 1. 如何处理独热编码和连续特征的尺度差异? 2. 是否需要分别处理这两种类型的数据? 3. 如何避免高维度和稀疏性问题? 4. 不同模型(如线性模型、树模型)需要不同的预处理步骤吗? 5. 特征组合是否有必要? 接下来,我需要整理出一个清晰的步骤,涵盖这些问题的解决方案。可能需要分步骤说明,先处理独热编码后的稀疏特征,再处理连续特征,然后整合,并给出具体的方法如标准化、归一化、特征选择等。同时,需要举例说明,比如使用Pipeline来分别处理不同特征,或者使用ColumnTransformer来应用不同的预处理方法到不同的特征列。 还需要注意一些常见的陷阱,比如对独热编码后的特征进行标准化,这可能会破坏其二进制特性,导致信息丢失。因此,可能需要仅对连续特征进行标准化,而保持独热编码的特征不变,或者使用不同的缩放方法。 最后,应该给出代码示例,帮助用户更好地理解如何实现这些预处理步骤,例如使用sklearn的ColumnTransformer和Pipeline来构建处理流程,以及如何在实践中应用这些方法。</think>--- ### **一、核心结论** 针对包**独热编码(One-Hot)特征**和**连续型特征**的混合数据预处理的核心原则是: **① 差异化处理不同特征类型**(避免盲目统一操作)、**② 解决尺度差异**(如连续特征标准化)、**③ 抑制维度灾难**(优化独热编码后的稀疏性)。 推荐使用**分类型处理流水线**(如 `ColumnTransformer`)确保各特征处理逻辑独立。 --- ### **二、特征类型特性与挑战** #### 1. **独热编码特征** - **数据结构**:稀疏二值矩阵(0/1),维度高($k$ 个类别 → $k$ 维); - **典型问题**:维度爆炸(尤其当类别数多时)、特征间强正交性。 #### 2. **连续型特征** - **数据结构**:实数域分布,可能存在量纲差异(如年龄 $[0,100]$ vs 收入 $[0,10^6]$); - **典型问题**:尺度敏感模型(如 SVM、神经网络)需标准化。 #### 3. **混合数据的矛盾点** - **尺度冲突**:独热编码为 0/1,连续型可能跨越多个数量级; - **模型误判**:统一标准化会破坏独热编码的语义(如将 0/1 变为负值); - **计算效率**:高维稀疏 + 密集特征混合时内存占用激增。 --- ### **三、分阶段预处理方法** #### **1. 独热编码优化(前置步骤)** - **(1)类别合并** 对低频类别(出现次数 < 阈值)合并为 “其他” 类别,减少冗余维度。 **公式**: $$\text{if } \text{count}(c_i) < \theta, \text{ then } c_i \rightarrow \text{"other"}$$ **示例代码**: ```python from sklearn.preprocessing import OneHotEncoder encoder = OneHotEncoder(min_frequency=50, handle_unknown='infrequent_if_exist') ``` - **(2)稀疏存储** 使用 `scipy.sparse` 矩阵存储独热编码结果,避免内存浪费。 **对比实验**: - 10万样本 × 1000类别 → 独热编码后,密集矩阵占用 **800MB**,稀疏矩阵仅 **1.2MB**。 #### **2. 连续型特征标准化** - **(1)标准化方法选择** | 方法 | 公式 | 适用场景 | |--------------------|-------------------------------|-------------------------| | **Z-Score标准化** | $$x' = \frac{x - \mu}{\sigma}$$ | 分布近似高斯时 | | **Robust Scaling** | $$x' = \frac{x - \text{median}}{IQR}$$ | 存在离群点时 | | **Min-Max缩放** | $$x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}}$$ | 需要严格限定范围时(如 [0,1])| - **(2)分位数变换** 将连续特征映射到均匀或正态分布: ```python from sklearn.preprocessing import QuantileTransformer qt = QuantileTransformer(output_distribution='normal') X_cont_trans = qt.fit_transform(X_continuous) ``` #### **3. 差异化流水线构建** **使用 `ColumnTransformer` 分离处理逻辑**: ```python from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler, OneHotEncoder # 定义特征列(示例) categorical_cols = [0, 1] # 假设第0、1列为分类特征 numeric_cols = [2, 3, 4] # 第2、3、4列为连续特征 # 构建处理流水线 preprocessor = ColumnTransformer( transformers=[ ('cat', OneHotEncoder(sparse=True), categorical_cols), ('num', StandardScaler(), numeric_cols) ]) # 在完整流水线中集成预处理与模型 from sklearn.linear_model import LogisticRegression pipeline = Pipeline(steps=[ ('preprocessor', preprocessor), ('classifier', LogisticRegression()) ]) ``` --- ### **四、高阶优化策略** #### 1. **稀疏-密集特征混合存储** - **问题**:独热编码后的稀疏矩阵与标准化后的密集矩阵直接拼接会导致类型转换(稀疏→密集),内存暴增; - **解决方案**: 使用 `scipy.sparse.hstack` 显式拼接: ```python from scipy import sparse X_sparse_cat = preprocessor.named_transformers_['cat'].transform(X[:, categorical_cols]) X_dense_num = preprocessor.named_transformers_['num'].transform(X[:, numeric_cols]) X_combined = sparse.hstack([X_sparse_cat, X_dense_num], format='csr') ``` #### 2. **特征选择与降维** - **(1)针对独热编码特征** - **方差阈值过滤**:删除低方差特征(如全为0或1的特征): ```python from sklearn.feature_selection import VarianceThreshold selector = VarianceThreshold(threshold=0.01) # 保留方差>0.01的特征 X_cat_selected = selector.fit_transform(X_sparse_cat) ``` - **卡方检验 / 互信息法**:筛选与目标相关性高的类别特征。 - **(2)针对连续特征** - **递归特征消除(RFE)**:基于模型权重迭代剔除不重要特征; - **L1正则化选择**:使用 Lasso 回归自动筛选特征。 - **(3)联合降维** - **KernelPCA**:对混合特征进行非线性降维(需转为密集矩阵); - **Feature Agglomeration**:对独热编码特征进行聚类合并。 #### 3. **树模型的特例处理** - **独热编码的可省略性**: 树模型(如 XGBoost、CatBoost)可直接处理类别型特征(无需独热编码),通过 `enable_categorical=True` 参数优化: ```python # XGBoost 示例(需将类别列标记为 'category' 类型) df[categorical_cols] = df[categorical_cols].astype('category') model = xgboost.XGBClassifier(enable_categorical=True, tree_method='hist') model.fit(df, y) ``` - **优势**:减少维度爆炸风险,保留类别间序关系(若存在)。 --- ### **五、不同模型的预处理要点** | 模型类型 | 独热编码处理建议 | 连续特征处理建议 | 注意事项 | |--------------------|---------------------------|-------------------------------|----------------------------| | **线性模型** | 必须独热编码 | 必须标准化 | 避免多重共线性(可加入L2正则)| | **树模型** | 可选(推荐直接输入类别特征)| 无需标准化 | 优先使用类别原生处理 | | **神经网络** | 必须独热编码 | 标准化或Min-Max缩放 | 批量归一化(BatchNorm)可替代部分预处理 | | **SVM** | 必须独热编码 | 强烈建议标准化 | 对尺度敏感,需统一量纲 | --- ### **六、完整预处理流程示例** #### 1. **结构化数据场景(如客户信用评分)** ``` 原始数据 → 分类特征独热编码(合并低频类) → 连续特征Robust Scaling → 特征选择(方差过滤+L1正则) → 混合拼接 → 输入XGBoost模型 ``` #### 2. **代码实现** ```python from sklearn.compose import ColumnTransformer from sklearn.feature_selection import VarianceThreshold, SelectFromModel from sklearn.ensemble import RandomForestClassifier # 步骤1:差异化预处理 preprocessor = ColumnTransformer( transformers=[ ('cat', Pipeline([ ('onehot', OneHotEncoder(min_frequency=100)), ('var_thresh', VarianceThreshold(threshold=0.05)) ]), ['gender', 'city']), # 分类列名称 ('num', RobustScaler(), ['age', 'income']) # 连续列名称 ]) # 步骤2:特征选择与模型集成 pipeline = Pipeline([ ('preprocess', preprocessor), ('feature_selector', SelectFromModel(RandomForestClassifier(n_estimators=100))), ('classifier', xgboost.XGBClassifier(enable_categorical=False)) ]) # 执行训练 pipeline.fit(X_train, y_train) ``` --- ### **七、常见错误与修正** | 错误现象 | 原因分析 | 修正方案 | |-----------------------------|-----------------------------|-----------------------------| | 模型训练时内存溢出 | 独热编码后直接转为密集矩阵 | 使用 `scipy.sparse` 保持稀疏性 | | 连续特征重要性被低估 | 未标准化导致尺度差异影响权重 | 对连续特征强制标准化 | | 预测时出现未知类别报错 | 未处理未见类别 | 设置 `OneHotEncoder(handle_unknown='ignore')` | | 树模型过拟合独热编码特征 | 高维度噪声干扰 | 启用特征重要性筛选或改用类别原生处理 | --- ### **八、评估与验证** 1. **预处理效果验证** - **可视化检查**:对连续特征绘制标准化前后分布对比图; - **稀疏性检查**:通过 `X.nbytes` 监控内存占用变化; - **模型对比**:比较预处理前后的模型AUC/准确率差异。 2. **消融实验设计** ```python # 实验组:完整预处理流程 pipeline_full = Pipeline([preprocessor, model]) # 对照组:仅处理连续特征 pipeline_partial = Pipeline([ ColumnTransformer([('num', RobustScaler(), numeric_cols)]), model ]) # 比较两者的交叉验证得分 from sklearn.model_selection import cross_val_score score_full = cross_val_score(pipeline_full, X, y, cv=5).mean() score_partial = cross_val_score(pipeline_partial, X, y, cv=5).mean() ``` --- ### **九、场景化推荐** | 应用场景 | 推荐预处理方案 | |---------------------------|-------------------------------------------------------------------------------| | 高维分类特征 + 小样本数据 | 独热编码(合并低频类) + 连续特征标准化 + L1正则化特征选择 | | 类别特征内在序关系 | 放弃独热编码,使用序数编码(Ordinal Encoding)或目标编码(Target Encoding) | | 实时推理场景 | 预计算类别编码表,避免在线调用 `OneHotEncoder.transform()` | --- **总结**:混合类型数据预处理需遵循“分而治之”原则。对独热编码特征,重点在于**维度控制**与**稀疏优化**;对连续特征,核心是**尺度归一化**。通过 `ColumnTransformer` 实现类型分离处理,并结合特征选择/降维抑制维度灾难,最终达到模型友好、计算高效的数据状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旅途中的宽~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值