
作者提供图片
特征工程是机器学习流程中的重要一步。它是一个将数据从其原生格式转换为有意义的特征的过程,以帮助机器学习模型更好地从数据中学习。
如果特征工程做得好,可以显著提高机器学习算法的性能。除了理解数据和预处理的基础知识外,有效的特征工程还包括创建交互项、生成指示变量以及将特征分箱。
这些技术有助于从数据中提取相关信息,并有助于构建健壮的机器学习解决方案。在本指南中,我们将通过构建一个示例住房数据集来探讨这些特征工程技术。
注意:您可以在您喜欢的 Jupyter Notebook 环境中跟随本教程进行编码。您也可以跟随本教程的 Google Colab Notebook。
1. 了解您的数据
在深入进行特征工程之前,您应该首先彻底了解您的数据。这包括:
- 探索和可视化数据集,以了解变量之间的分布和关系
- 了解您拥有的特征类型(分类、数值、日期时间对象等)并理解它们在分析中的重要性
- 尝试使用领域知识来理解每个特征的含义以及它可能如何与其他特征交互。这种洞察力可以指导您创建有意义的新特征
让我们创建一个示例住房数据集来处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import pandas as pd import numpy as np # 设置随机种子以保证可复现性 np.random.seed(42) # 创建样本数据 n_samples = 1000 data = { 'price': np.random.normal(200000, 50000, n_samples).astype(int), 'size': np.random.normal(1500, 500, n_samples).astype(int), 'num_rooms': np.random.randint(2, 8, n_samples), 'num_bathrooms': np.random.randint(1, 4, n_samples), 'age': np.random.randint(0, 40, n_samples), 'neighborhood': np.random.choice(['A', 'B', 'C', 'D', 'E'], n_samples), 'income': np.random.normal(60000, 15000, n_samples).astype(int) } df = pd.DataFrame(data) print(df.head()) |
除了获取数据集的基本信息外,您还可以分别生成数值变量的分布图和分类变量的计数图。以下代码片段展示了数据集上的基本探索性数据分析。
首先,我们获取数据帧的一些基本信息
1 2 3 4 |
# 对整个数据集进行基本数据探索 print(df.head()) print(df.info()) print(df.describe()) |
您可以尝试可视化数据集中数值特征“size”和“income”的分布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import matplotlib.pyplot as plt import seaborn as sns # 使用 distplot 可视化“size”和“income”的分布 plt.figure(figsize=(8, 6)) sns.histplot(df['size'], kde=True) plt.title('房屋大小分布') plt.xlabel('大小') plt.ylabel('频率') plt.show() plt.figure(figsize=(8, 6)) sns.histplot(df['income'], kde=True) plt.title('家庭收入分布') plt.xlabel('收入') plt.ylabel('频率') plt.show() |
对于分类变量,计数图有助于了解不同值的分布情况
1 2 3 4 5 6 7 8 |
# “neighborhood”的计数图 plt.figure(figsize=(8, 6)) sns.countplot(x='neighborhood', data=df, order=df['neighborhood'].value_counts().index) plt.title('各社区房屋数量') plt.xlabel('社区') plt.ylabel('数量') plt.xticks(rotation=45) plt.show() |
通过了解您的数据,您可以识别关键特征以及特征之间的关系,这些将为后续的特征工程步骤提供信息。这一步确保您的预处理和特征创建工作基于对数据集的深入理解。
2. 有效地预处理数据
有效的预处理包括处理缺失值和异常值、缩放数值特征以及编码分类变量。预处理技术的选择也取决于数据和机器学习算法的要求。
在示例数据帧中,我们没有缺失值。对于大多数真实世界的数据集,您可以使用合适的插补策略来处理缺失值。
在进行预处理之前,先将数据集分割为训练集和测试集
1 2 3 4 5 6 7 8 |
from sklearn.model_selection import train_test_split # 将数据分割为特征 X 和目标标签 y X = df.drop('price', axis=1) y = df['price'] # 将数据分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) |
为了将数值特征缩放到相同的尺度,您可以使用 minmax 或 standard 缩放。以下是插补缺失值和缩放数值特征的通用代码片段
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from sklearn.model_selection import train_test_split from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler # 处理缺失值 imputer = SimpleImputer(strategy='mean') X_train['feature_to_impute'] = imputer.fit_transform(X_train[['feature_to_impute']]) X_test['feature_to_impute'] = imputer.transform(X_test[['features_to_impute']]) # 特征缩放 scaler = StandardScaler() X_train[['features_to_scale']] = scaler.fit_transform(X_train[['features_to_scale']]) X_test[['features_to_scale']] = scaler.transform(X_test[['features_to_scale']]) |
将 'features_to_impute' 和 'features_to_scale' 替换为您要插补或缩放的具体特征。我们还将在后续部分中介绍如何从现有特征创建更具代表性的特征。
总而言之,有效的预处理通过确保一致性并解决原始数据中的任何问题来为所有下游任务准备数据。此步骤对于从机器学习模型中获得准确可靠的结果至关重要。
3. 创建交互项
创建交互项涉及生成捕获现有特征之间交互的新特征。
对于我们的示例数据集,我们将使用 scikit-learn 的 PolynomialFeatures 为“size”和“num_rooms”生成交互项
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from sklearn.preprocessing import PolynomialFeatures # 在训练集上创建多项式和交互特征 poly = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False) interaction_terms_train = poly.fit_transform(X_train[['size', 'num_rooms']]) interaction_terms_test = poly.transform(X_test[['size', 'num_rooms']]) interaction_df_train = pd.DataFrame(interaction_terms_train, columns=poly.get_feature_names_out(['size', 'num_rooms'])) interaction_df_test = pd.DataFrame(interaction_terms_test, columns=poly.get_feature_names_out(['size', 'num_rooms'])) # 添加交互项 X_train = pd.concat([X_train, interaction_df_train], axis=1) X_test = pd.concat([X_test, interaction_df_test], axis=1) |
创建交互项可以通过捕获特征之间假定的复杂关系来改进模型。
4. 创建指示变量
您可以创建指示变量来标记某些条件或标记数据中的阈值。这些变量的值为 0 或 1,表示某个值是否存在。
例如,假设您有一个预测贷款违约的数据集,其中学生贷款违约数量很多。从“professions”分类列创建“is_student”特征会很有帮助。
在住房数据集中,我们可以创建一个指示变量来表示房屋是否超过 30 年,并对“age_indicator”指示变量创建计数图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import seaborn as sns import matplotlib.pyplot as plt # 为超过 30 年的房屋创建指示变量 X_train['age_indicator'] = (X_train['age'] > 30).astype(int) X_test['age_indicator'] = (X_test['age'] > 30).astype(int) # 可视化指示变量 plt.figure(figsize=(10, 6)) sns.countplot(x='age_indicator', data=X_train) plt.title('基于年龄指示变量(>30岁)的房屋数量') plt.xlabel('年龄指示变量') plt.ylabel('数量') plt.show() |
您也可以从房间数量“num_rooms”列创建指示变量。如您所见,创建指示变量有助于为机器学习模型编码额外的信息。
5. 使用分箱创建更具代表性的特征
将特征分箱到桶中涉及将连续变量分组到离散的间隔中。有时将年龄和收入等特征分组到桶中可以帮助找到在连续数据中难以识别的模式。
对于示例住房数据集,让我们将房屋的年龄和家庭的收入分到不同的桶中,并加上描述性标签。您可以使用 pandas 中的 cut() 函数将特征分到等宽区间,如下所示:
1 2 3 4 5 6 7 |
# 创建收入分箱 X_train['age_bin'] = pd.cut(X_train['age'], bins=3, labels=['new', 'moderate', 'old']) X_test['age_bin'] = pd.cut(X_test['age'], bins=3, labels=['new', 'moderate', 'old']) # 创建收入分箱 X_train['income_bin'] = pd.cut(X_train['income'], q=4, labels=['low', 'medium', 'high', 'very_high']) X_test['income_bin'] = pd.cut(X_test['income'], q=4, labels=['low', 'medium', 'high', 'very_high']) |
将连续特征分箱到离散区间中,可以将连续变量的表示形式简化为具有更强预测能力的特征。
总结
在本指南中,我们介绍了以下有效特征工程技巧:
- 执行 EDA 并使用可视化来理解您的数据。
- 通过处理缺失值、编码分类变量、去除异常值和确保正确的训练-测试分割来进行有效预处理。
- 创建交互项,将特征组合起来以捕获有意义的交互。
- 根据阈值和特定值创建指示变量。
- 以捕获关键的分类信息。
- 将特征分箱或分成离散区间,以创建更具代表性的特征。
请务必在您的下一个机器学习项目中测试这些特征工程技巧。祝您特征工程愉快!
你好 Bala!
很棒的文章!非常感谢分享……
在你的第 5 点“使用分箱创建更具代表性的特征”中
写着:
# 创建收入分箱
X_train[‘age_bin’] = pd.cut(X_train[‘age’], bins=3, labels=[‘new’, ‘moderate’, ‘old’])
X_test[‘age_bin’] = pd.cut(X_test[‘age’], bins=3, labels=[‘new’, ‘moderate’, ‘old’])
我相信这里有一个拼写错误,因为你指的是:“# 创建年龄分箱”。
另外,你的指令:“X_train[‘age_bin’] = pd.cut(X_train[‘age’], bins=3, labels=[‘new’, ‘moderate’, ‘old’])”在 Pandas 中引发了一个错误:“putmask: 第一个参数必须是数组”。
再次感谢您。
何塞
你好 Jose…你说得对,评论应该关于创建“年龄分箱”而不是“收入分箱”。感谢你指出这一点。你遇到的错误可能是由于使用了非 ASCII 字符来指定代码中的引号。Pandas 在指定列名时需要标准的 ASCII 引号。
这是修正后的代码:
python
# 创建年龄分箱
X_train['age_bin'] = pd.cut(X_train['age'], bins=3, labels=['new', 'moderate', 'old'])
X_test['age_bin'] = pd.cut(X_test['age'], bins=3, labels=['new', 'moderate', 'old'])
这应该可以正常工作,不会引发任何错误。请确保您使用的是直单引号 (
'
),而不是任何风格化或弯曲的引号 (‘’
),因为这可能会在 Python 中引起问题。如果您仍然遇到问题,请仔细检查
age
列是否存在于您的X_train
和X_test
数据框中,并且age
列的数据类型是数字。