数据分析2.5 —— 特征工程入门!
垃圾进,垃圾出。数据的质量永远是第一准则
特征工程的介绍
特征工程是机器学习中非常关键的一步,它通过提取、构建、选择和处理模型输入数据的特征,使得模型能够更有效地从数据中学习并提高预测性能。对于泰坦尼克(Titanic)数据集,特征工程的主要任务是将原始数据中的各种信息(如乘客年龄、船票等级、登船地点等)处理成适合模型训练的格式,并构建新的有用特征。
下面我们将通过Titanic数据集演示常见的特征工程操作,并解释其重要性。
1. 数据加载和初步处理
首先,我们加载Titanic数据集并查看其结构。
import pandas as pd
# 加载数据
df = pd.read_csv('titanic.csv')
# 查看数据结构
print(df.info())
print(df.head())
我们可以看到数据集中存在诸如Age(年龄)、Sex(性别)、Embarked(登船地点)、Cabin(舱位)等特征。这些特征在模型训练前需要进行预处理。
2. 处理缺失值
在任何实际数据集中,缺失值都是常见的现象。特征工程中,处理缺失值是非常重要的一步,因为机器学习模型通常无法处理包含缺失值的数据。
# 查看数据中的缺失值
print(df.isnull().sum())
结果显示Age、Embarked和Cabin特征中存在缺失值:
- Age(年龄):通常通过填充缺失值来处理,例如使用中位数或平均值。
- Embarked(登船地点):使用众数(最常见值)进行填充。
- Cabin(舱位):缺失值过多,直接删除该列或将其转换为是否有舱位的信息。
处理代码:
# 填充年龄的缺失值
df['Age'].fillna(df['Age'].median(), inplace=True)
# 填充登船地点的缺失值
df['Embarked'].fillna(df['Embarked'].mode()[0], inplace=True)
# 将舱位信息是否缺失转换为二值特征
df['Cabin_null'] = df['Cabin'].isnull().astype(int)
# 删除原始的Cabin列
df.drop(columns=['Cabin'], inplace=True)
# 再次检查缺失值
print(df.isnull().sum())
重要性:
- 处理缺失值可以防止模型训练时出错,并最大限度地保留有用信息,确保数据的完整性。
- 对于像
Cabin这样缺失率过高的列,转换为二进制特征是一个有效的做法,因为是否有舱位可能与生存率相关。
注意!,中值插补仅用出现频次最高的或者平均数代替缺失数据,看似不影响结果的插补方式,实际会改变数据的结构分布。高级的方式会在以后介绍,也可使用一些对于缺失数据不敏感的算法如XGBoost
3. 类别型特征编码
机器学习模型无法直接处理类别型特征(如Sex和Embarked),因此需要将它们转换为数值形式。这通常通过独热编码(One-Hot Encoding)或标签编码(Label Encoding)来实现。
# 独热编码(将类别变量转换为0/1的二进制变量)
df = pd.get_dummies(df, columns=['Sex', 'Embarked'], drop_first=True)
# 查看编码后的数据
print(df.head())
- Sex 被转换为
Sex_male(1表示男性,0表示女性)。 - Embarked 被转换为
Embarked_Q和Embarked_S两列,分别表示是否从不同地点登船。
重要性:
- 类别特征编码让模型能够理解类别信息,并避免类别间的隐含顺序假设(如简单的数值编码可能会让模型误解类别间有顺序性)。
4. 构建新特征
构建新特征是提升模型性能的关键技巧之一。基于领域知识和已有特征,我们可以创建更具信息量的特征。例如:
- 家庭规模(FamilySize):通过
SibSp(兄弟姐妹/配偶)和Parch(父母/子女)的信息构造家庭规模特征。 - 是否为孤身一人(IsAlone):根据
FamilySize判断乘客是否是独自乘船。
# 构造家庭规模特征
df['FamilySize'] = df['SibSp'] + df['Parch'] + 1 # 自身也算作家庭成员
# 构造是否独自一人的特征
df['IsAlone'] = (df['FamilySize'] == 1).astype(int)
# 查看新特征
print(df[['FamilySize', 'IsAlone']].head())
重要性:
- 家庭规模和是否独自一人可能与乘客的生存率密切相关,因为有些研究表明,大家庭可能在混乱中不易生存,而独自一人可能更有灵活性。
- 通过构建与任务相关的特征,模型可以获得更多有助于分类的信息,从而提升预测效果。
5. 特征缩放
对于某些机器学习模型(如线性回归、支持向量机、K近邻等),特征缩放非常重要,避免因特征取值范围不同而导致模型性能下降。我们可以使用标准化(Standardization)或归一化(Normalization)来进行特征缩放。
from sklearn.preprocessing import StandardScaler
# 定义需要缩放的特征
features_to_scale = ['Age', 'Fare', 'FamilySize']
# 进行标准化
scaler = StandardScaler()
df[features_to_scale] = scaler.fit_transform(df[features_to_scale])
# 查看缩放后的特征
print(df[features_to_scale].head())
重要性:
- 特征缩放确保所有特征在同一数量级上,使得模型的训练过程更加稳定,尤其对于梯度下降法、KNN等依赖距离度量的模型,缩放至关重要。
6. 特征选择
在处理高维数据时,特征选择能够帮助我们减少数据的复杂度,并且有助于消除噪声特征。常用的特征选择方法有:
- 基于统计方法:例如
chi2检验、互信息、方差阈值等。 - 基于模型的方法:使用
L1正则化(Lasso)、随机森林等模型自动选择重要特征。
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectFromModel
# 使用随机森林进行特征选择
rf = RandomForestClassifier(random_state=42)
rf.fit(df.drop(columns=['Survived']), df['Survived'])
# 选择重要性高于某个阈值的特征
selector = SelectFromModel(rf, threshold=0.01, prefit=True)
selected_features = df.drop(columns=['Survived']).columns[selector.get_support()]
# 查看选择的特征
print("选择的重要特征:", selected_features)
重要性:
- 特征选择能够去除冗余或噪声特征,减少模型的复杂度,从而提升模型的泛化能力,并减少过拟合风险。
7. 树模型特征选择可视化
首先,确保已经加载并预处理数据集,例如处理缺失值和进行特征编码。
import pandas as pd
from sklearn.model_selection import train_test_split
# 加载数据集
df = pd.read_csv('titanic.csv')
# 处理缺失值
df['Age'].fillna(df['Age'].median(), inplace=True)
df['Embarked'].fillna(df['Embarked'].mode()[0], inplace=True)
df['Cabin_null'] = df['Cabin'].isnull().astype(int)
df.drop(columns=['Cabin'], inplace=True)
# 类别型特征编码
df = pd.get_dummies(df, columns=['Sex', 'Embarked'], drop_first=True)
# 划分特征和标签
X = df.drop(columns=['Survived'])
y = df['Survived']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
训练随机森林模型
使用RandomForestClassifier来训练模型。
from sklearn.ensemble import RandomForestClassifier
# 创建随机森林模型
rf_model = RandomForestClassifier(random_state=42)
rf_model.fit(X_train, y_train)
查看特征重要性
随机森林模型可以通过feature_importances_属性来访问特征重要性。
# 获取特征重要性
importances = rf_model.feature_importances_
# 将特征重要性与特征名称组合成DataFrame
feature_importance_df = pd.DataFrame({'Feature': X.columns, 'Importance': importances})
# 按重要性排序
feature_importance_df = feature_importance_df.sort_values(by='Importance', ascending=False)
print(feature_importance_df)
可视化特征重要性
为了更好地理解特征重要性,可以使用可视化工具(如matplotlib)来绘制条形图。
import matplotlib.pyplot as plt
# 绘制特征重要性图
plt.figure(figsize=(10, 6))
plt.barh(feature_importance_df['Feature'], feature_importance_df['Importance'], color='skyblue')
plt.xlabel('Importance')
plt.title('Feature Importance using Random Forest')
plt.gca().invert_yaxis() # 反转y轴,使得最重要的特征在上方
plt.show()8. 完整代码总结
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectFromModel
# 加载数据
df = pd.read_csv('titanic.csv')
# 处理缺失值
df['Age'].fillna(df['Age'].median(), inplace=True)
df['Embarked'].fillna(df['Embarked'].mode()[0], inplace=True)
df['Cabin_null'] = df['Cabin'].isnull().astype(int)
df.drop(columns=['Cabin'], inplace=True)
# 类别型特征编码
df = pd.get_dummies(df, columns=['Sex', 'Embarked'], drop_first=True)
# 构造新特征
df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
df['IsAlone'] = (df['FamilySize'] == 1).astype(int)
# 特征缩放
features_to_scale = ['Age', 'Fare', 'FamilySize']
scaler = StandardScaler()
df[features_to_scale] = scaler.fit_transform(df[features_to_scale])
# 特征选择
X = df.drop(columns=['Survived'])
y = df['Survived']
rf = RandomForestClassifier(random_state=42)
rf.fit(X, y)
selector = SelectFromModel(rf, threshold=0.01, prefit=True)
selected_features = X.columns[selector.get_support()]
print("最终选择的重要特征:", selected_features)
总结
特征工程是机器学习流程中不可忽视的一步,它能够帮助模型更好地理解数据,提高预测性能。通过处理缺失值、类别特征编码、构建新特征、特征缩放和特