从已有特征中生成新特征是为建模准备数据的一个主要部分。这个过程常被称为特征工程,通过显现原始数据中不明显的潜在模式或关系,可以大幅提升机器学习模型的表现。特征工程通常受数据分析(EDA)指导,并与理解现有变量相辅相成。可以把它看作是为分析或建模任务制作更优工具。通过查看直方图、散点图和频数统计获得的认识,常表明了更有效地组合、转换或提取信息的方法。我们来看一些常见方法。交互特征有时,两个特征的组合效应比它们各自的效应能提供更多信息。例如,在住房数据集中,房间的总面积(长度 * 宽度)可能是比单独长度或宽度更好的价格预测因子。同样,在营销场景中,用户年龄和网站停留时间之间的相互影响可能显示特定年龄组的参与模式。在Pandas中,使用基本算术运算生成这些交互项通常很简单。import pandas as pd # 示例数据框 data = {'length': [10, 12, 8, 15], 'width': [5, 6, 4, 7], 'price': [500, 700, 300, 1000]} df = pd.DataFrame(data) # 生成“面积”交互特征 df['area'] = df['length'] * df['width'] print(df) # length width price area # 0 10 5 500 50 # 1 12 6 700 72 # 2 8 4 300 32 # 3 15 7 1000 105当双变量分析(例如数值变量之间的散点图或数值变量与类别变量的分组分析)表明一个变量与目标变量的关系取决于另一个变量的水平时,你可以考虑生成交互项。多项式特征散点图可能显示变量之间存在非线性关系。简单的线性模型可能无法有效捕捉这类曲线。生成多项式特征包括添加现有数值特征的幂次(平方、立方等)。这使得线性模型能够拟合非线性模式。例如,如果年龄与收入以曲线方式相关(可能最初增加,然后趋于平稳或下降),添加age^2作为特征可以有助于捕捉这一点。# 示例数据框(添加年龄) data = {'age': [25, 35, 45, 55, 65], 'income': [50, 80, 95, 100, 90]} df_poly = pd.DataFrame(data) # 生成一个年龄平方特征 df_poly['age_squared'] = df_poly['age'] ** 2 print(df_poly) # age income age_squared # 0 25 50 625 # 1 35 80 1225 # 2 45 95 2025 # 3 55 100 3025 # 4 65 90 4225添加多项式特征应受可视化结果指导。如果 $y$ 对 $x$ 的散点图显示明显的抛物线形状,添加 $x^2$ 可能有益。但要小心,添加过多的高阶多项式特征可能导致过拟合。Scikit-learn之类的库提供了专用工具(PolynomialFeatures)来系统地生成这些特征。从日期/时间特征中提取信息日期和时间列通常包含丰富、潜在的信息。像2023-10-27 15:30:00这样的单个时间戳可以分解成不同组成部分,这些组成部分可能各自与你的目标变量相关联:年份(例如,2023)月份(例如,10月或十月)月中的日(例如,27)星期几(例如,星期五)一天中的小时(例如,15)是否是周末或工作日自特定事件以来经过的时间Pandas通过带日期时间对象的Series的.dt属性,提供了便捷的访问器方法。# 带有日期时间列的示例数据框 dates = pd.to_datetime(['2023-01-15 10:00:00', '2023-01-16 18:30:00', '2023-02-20 09:15:00']) df_dates = pd.DataFrame({'timestamp': dates, 'value': [10, 15, 12]}) # 确保列为日期时间类型 df_dates['timestamp'] = pd.to_datetime(df_dates['timestamp']) # 提取组成部分 df_dates['year'] = df_dates['timestamp'].dt.year df_dates['month'] = df_dates['timestamp'].dt.month df_dates['dayofweek'] = df_dates['timestamp'].dt.dayofweek # 星期一=0, 星期日=6 df_dates['hour'] = df_dates['timestamp'].dt.hour df_dates['is_weekend'] = df_dates['dayofweek'].isin([5, 6]).astype(int) # 如果是周末为1,否则为0 print(df_dates) # timestamp value year month dayofweek hour is_weekend # 0 2023-01-15 10:00:00 10 2023 1 6 10 1 # 1 2023-01-16 18:30:00 15 2023 1 0 18 0 # 2 2023-02-20 09:15:00 12 2023 2 0 9 0在EDA期间分析时间序列图或比较不同时间组成部分的分布(例如,每周销售额的箱线图)可以指导哪些组成部分值得提取。分箱或离散化有时将连续数值变量转换为离散类别或箱很有用。这可以帮助捕捉非线性效应或简化模型。例如,你可能不使用精确的年龄,而是将个体分组为年龄组类别,如'18-25'、'26-40'、'41-60'、'60+'。在单变量分析期间生成的直方图通常是分箱的主要原因。如果直方图显示出不同的组,或者你认为与目标变量的关系在某些阈值上明显改变,那么分箱可以有效。Pandas的cut函数非常适合这一点。# 使用之前的df_poly示例 age_bins = [18, 25, 40, 60, 100] # 定义箱的边界 age_labels = ['18-25', '26-40', '41-60', '61+'] # 定义箱的标签 df_poly['age_group'] = pd.cut(df_poly['age'], bins=age_bins, labels=age_labels, right=True) print(df_poly) # age income age_squared age_group # 0 25 50 625 18-25 # 1 35 80 1225 26-40 # 2 45 95 2025 41-60 # 3 55 100 3025 41-60 # 4 65 90 4225 61+你可以选择等宽箱、等频箱(使用qcut),或基于专业知识或EDA观察结果的自定义箱。合并类别类别特征有时包含很多类别,有些出现频率非常低。这些罕见类别可能会引入噪音而非信号。在EDA期间,查看频数统计(使用.value_counts())显示这些罕见类别。你可能决定将它们合并到一个'其他'类别中。# 带有类别特征的示例数据框 data_cat = {'category': ['A', 'B', 'A', 'C', 'B', 'A', 'D', 'E', 'B', 'D'], 'value': [10, 15, 12, 20, 18, 11, 25, 30, 16, 22]} df_cat = pd.DataFrame(data_cat) # 识别不常见类别(例如,出现少于3次) counts = df_cat['category'].value_counts() rare_categories = counts[counts < 3].index.tolist() # ['C', 'E'] # 将不常见类别替换为“Other” df_cat['category_grouped'] = df_cat['category'].replace(rare_categories, 'Other') print(df_cat) # category value category_grouped # 0 A 10 A # 1 B 15 B # 2 A 12 A # 3 C 20 Other # 4 B 18 B # 5 A 11 A # 6 D 25 D # 7 E 30 Other # 8 B 16 B # 9 D 22 D print("\nNew Counts:") print(df_cat['category_grouped'].value_counts()) # A 3 # B 3 # D 2 # Other 2 # Name: category_grouped, dtype: int64这简化了特征,有时可以提升模型稳定性。定义“罕见”的阈值取决于数据集大小和具体问题。生成新特征是一个迭代过程。你可以生成一个特征,可视化它与其它变量或目标变量的关系,然后改进它或尝试不同方法。此处讨论的方法为将您的EDA认识转化为后续分析或建模阶段更强的输入奠定了坚实的根本。请记住,专业知识在表明有意义的特征转换方面通常扮演重要角色。