对数据集的分析常会显现分类特征,这些变量表示诸如“城市”、“产品类型”或“客户细分”等组别或标签。尽管这些类别对我们有意义,但大多数机器学习算法主要处理数值数据。因此,将这些分类特征转换为数值格式是特征工程中一个标准且必需的步骤,这直接依据我们在单变量和双变量分析中获得的关于这些类别性质和分布的见解。这个过程称为编码。为何对分类特征进行编码?机器学习模型是数学函数。它们通过对输入数字进行计算来学习模式。像“纽约”、“伦敦”或“东京”这样的文本类别不能直接代入这些计算中。编码将这些标签转换为数字,使算法能够理解它们。选择合适的编码方法非常重要,因为不同方法会影响模型性能。常见的编码方法对分类数据进行编码没有唯一最佳方式。最优选择取决于特征的具体属性(例如唯一类别的数量或是否存在固有顺序)以及您稍后打算使用的机器学习模型的要求。接下来,我们查看一些常见技术。1. 标签编码标签编码为每个类别分配一个唯一的整数。例如,如果特征“温度”有“低”、“中”和“高”这些类别,标签编码可能会将0分配给“低”,1分配给“中”,2分配给“高”。工作原理: 将每个唯一类别标签映射到一个整数。优点: 实现简单,所需内存最少。缺点: 它引入任意的序数关系。模型可能将“高”(2)解释为“中”(1)的“两倍”,这对于名义类别(没有自然顺序的类别)通常没有意义。这可能对线性模型或基于距离的算法产生负面影响。何时考虑使用: 主要用于序数特征,其中整数映射反映固有顺序(例如,“低” < “中” < “高”)。有些基于树的模型(如决策树、随机森林)有时可以正确处理名义特征的这类编码,但使用其他方法通常更安全。实现范例(Pandas):import pandas as pd # 示例DataFrame df = pd.DataFrame({'Temperature': ['High', 'Low', 'Medium', 'Low', 'High']}) # 使用类别代码应用标签编码 df['Temperature_Encoded'] = df['Temperature'].astype('category').cat.codes print(df) # Temperature Temperature_Encoded # 0 High 0 <- 注意:Pandas默认按字母顺序分配代码 # 1 Low 1 # 2 Medium 2 # 3 Low 1 # 4 High 0 # 对于序数数据,显式控制顺序: temp_order = ['Low', 'Medium', 'High'] df['Temperature'] = pd.Categorical(df['Temperature'], categories=temp_order, ordered=True) df['Temperature_Ordinal_Encoded'] = df['Temperature'].cat.codes print("\n序数编码:") print(df) # 序数编码: # Temperature Temperature_Encoded Temperature_Ordinal_Encoded # 0 High 0 2 # 1 Low 1 0 # 2 Medium 2 1 # 3 Low 1 0 # 4 High 0 22. 独热编码独热编码可能是最常见且普遍推荐的方法,特别是对于名义分类特征。它将每个类别值转换为一个新的二元列(只包含0或1)。工作原理: 为具有 $N$ 个唯一类别的特征创建 $N$ 个新列。对于每一行,对应其原始类别的列获得1,所有其他新列获得0。优点: 不施加任何人为顺序。通常非常适合线性模型、基于距离的算法和神经网络。缺点: 如果原始变量有许多唯一类别(高基数),可能会显著增加特征数量(维度)。这可能导致更高的内存使用,并可能影响模型性能(有时称为“维度灾难”)。通常可以删除其中一个生成的列以避免完全多重共线性,尽管许多现代库会处理这种情况。何时考虑使用: 通常是具有合理数量唯一类别的名义特征的默认选择。digraph G { rankdir=LR; node [shape=box, style=rounded, fontname="helvetica", fontsize=10]; edge [arrowhead=vee, fontname="helvetica", fontsize=10]; splines=ortho; bgcolor="transparent"; "Original" [label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"><TR><TD BGCOLOR="#e9ecef"><b>ID</b></TD><TD BGCOLOR="#e9ecef"><b>颜色</b></TD></TR><TR><TD>1</TD><TD>Red</TD></TR><TR><TD>2</TD><TD>Blue</TD></TR><TR><TD>3</TD><TD>Green</TD></TR><TR><TD>4</TD><TD>Red</TD></TR> </TABLE> >]; "Encoded" [label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"><TR><TD BGCOLOR="#e9ecef"><b>ID</b></TD><TD BGCOLOR="#ffc9c9"><b>颜色_Red</b></TD><TD BGCOLOR="#a5d8ff"><b>颜色_Blue</b></TD><TD BGCOLOR="#b2f2bb"><b>颜色_Green</b></TD></TR><TR><TD>1</TD><TD>1</TD><TD>0</TD><TD>0</TD></TR><TR><TD>2</TD><TD>0</TD><TD>1</TD><TD>0</TD></TR><TR><TD>3</TD><TD>0</TD><TD>0</TD><TD>1</TD></TR><TR><TD>4</TD><TD>1</TD><TD>0</TD><TD>0</TD></TR> </TABLE> >]; "Original" -> "Encoded" [label=" 独热\n编码 "]; }独热编码将“颜色”特征转换为三个新的二元特征。实现范例(Pandas):import pandas as pd # 示例DataFrame df = pd.DataFrame({'Color': ['Red', 'Blue', 'Green', 'Red']}) # 应用独热编码 df_encoded = pd.get_dummies(df, columns=['Color'], prefix='Color') print(df_encoded) # Color_Blue Color_Green Color_Red # 0 0 0 1 # 1 1 0 0 # 2 0 1 0 # 3 0 0 1pd.get_dummies函数对此非常方便。您也可以使用sklearn.preprocessing.OneHotEncoder,它能更好地集成到Scikit-learn管道中,特别是在确保训练和测试数据分割之间编码一致性方面。3. 频率(或计数)编码此方法用每个类别在数据集中出现的频率(计数)替换该类别。工作原理: 计算每个类别出现的次数,并将该计数用作数值表示。优点: 简单,不增加维度,捕获类别普遍性的信息。缺点: 频率相同的类别将获得相同的编码值(冲突)。它丢失原始标签信息。编码值的解释取决于数据集。何时考虑使用: 当类别的频率可能具有预测能力时。对高基数特征有用,在这些情况下独热编码会创建太多列。常与基于树的模型一起使用。实现范例(Pandas):import pandas as pd # 示例DataFrame df = pd.DataFrame({'City': ['London', 'Paris', 'London', 'Tokyo', 'Paris', 'London']}) # 计算频率 city_freq = df['City'].value_counts() # 将频率映射到列 df['City_Freq_Encoded'] = df['City'].map(city_freq) print(df) # City City_Freq_Encoded # 0 London 3 # 1 Paris 2 # 2 London 3 # 3 Tokyo 1 # 4 Paris 2 # 5 London 34. 目标编码(均值编码)目标编码用与每个类别相关的目标变量的平均值替换该类别。例如,如果对“城市”进行编码且目标是“购买金额”,则“伦敦”将被替换为所有来自伦敦客户的平均购买金额。工作原理: 计算每个类别的目标变量均值,并将该均值用作编码值。优点: 可以捕获与目标变量直接相关的信息,通常表现良好,不显著增加维度。缺点: 极易导致数据泄露和过拟合,特别是在实现不仔细时。在整个数据集上计算均值并将其用于编码,会将目标变量的信息泄露到特征中,这对于新的、未见过的数据将不可用。何时考虑使用: 常用于机器学习竞赛(如Kaggle),以期获得潜在的性能提升,尤其是在处理高基数特征时。需要仔细实现,通常涉及在交叉验证设置中仅在训练折叠上计算均值以防止数据泄露。这是一种更高级的技术。由于存在数据泄露的风险,简单的实现通常不足以进行可靠的模型构建。正确应用通常涉及诸如K折目标编码之类的技术。选择合适的方法选择合适的编码方法是特征工程过程的一部分,依据您的EDA(探索性数据分析)来确定:特征类型: 特征是名义型(无顺序,例如“颜色”)还是序数型(有顺序,例如“尺寸”为“S”、“M”、“L”)?序数型:标签编码(确保整数映射与顺序匹配)可能合适。名义型:独热编码通常是最安全的起点。基数: 该特征有多少个唯一类别?低基数:独热编码通常没问题。高基数:独热编码可能导致过多特征。考虑频率编码、目标编码(谨慎使用),或在编码前可能对不常出现的类别进行分组。模型选择: 某些模型对编码特征的处理方式不同。线性模型/基于距离的模型(如SVM,KNN):通常更偏好独热编码,因为标签编码可能误导它们。基于树的模型(决策树、随机森林、梯度提升):有时能更好地处理标签编码,并可能从频率或目标编码中受益。性能: 最终,您可能需要尝试不同的编码方法,并使用交叉验证评估它们对模型性能的影响。实现考量尽管Pandas(pd.get_dummies、.map、.astype('category').cat.codes)提供了快速执行编码的方法,但对于构建机器学习模型而言,在管道(sklearn.pipeline.Pipeline)中使用Scikit-learn的转换器(OneHotEncoder、LabelEncoder、OrdinalEncoder)通常更受青睐。这可确保从训练数据中学到的编码被一致地应用于验证和测试数据,从而防止错误和数据泄露。对分类特征进行编码是连接数据分析和模型准备的基本步骤。通过慎重地使用标签编码、独热编码或其他方法将类别转换为数字,您可以使数据适用于机器学习算法,为获取预测性信息铺平道路。方法的选择应由您通过详尽的EDA(探索性数据分析)获得的对数据的理解以及后续建模任务的要求来引导。