好的,各位观众老爷们,欢迎来到“数据预处理与特征工程:Python 实战技巧”专场!我是你们的老朋友,江湖人称“数据魔法师”的程序员老张。今天咱们不聊高深莫测的理论,就讲讲实战中那些能让你起飞的小技巧,保证听完就能用,用了就灵!😎
开场白:数据,才是真正的石油!
在人工智能时代,数据就如同工业时代的石油,谁掌握了高质量的数据,谁就能在算法的战场上所向披靡。但是,现实往往是残酷的。我们辛辛苦苦收集来的数据,可能长得歪瓜裂枣,参差不齐,甚至还带着各种“噪音”和“污垢”。这个时候,就需要我们出马,化腐朽为神奇,把这些原始数据打磨成闪闪发光的“钻石”💎,才能喂饱那些嗷嗷待哺的机器学习模型。
第一幕:数据预处理,给数据洗个“SPA” 🛀
数据预处理,顾名思义,就是对原始数据进行清洗、转换、集成和规约等操作,让数据变得更干净、更规范、更适合后续的分析和建模。这就像给数据洗个“SPA”,去除杂质,舒缓疲劳,焕发新生。
1. 缺失值处理:填坑大法好!
缺失值,就像数据中的“黑洞”,让人头疼不已。处理缺失值的方法有很多,我们来一一盘点:
-
删除法:简单粗暴,但要慎用!
- 完全删除: 如果某个特征的缺失值太多(比如超过80%),或者某个样本的关键特征缺失,那就直接删除吧,留着也是个累赘。
- 成对删除: 只在计算相关系数或者统计量时,忽略掉含有缺失值的样本。
注意: 删除法虽然简单,但可能会丢失大量有用的信息,不到万不得已,还是尽量不要用。
-
填充法:各显神通,八仙过海!
- 均值/中位数/众数填充: 这是最常用的方法,简单有效。对于数值型特征,可以用均值或中位数填充;对于类别型特征,可以用众数填充。
- 固定值填充: 用一个特定的值来填充缺失值,比如0,-1,或者其他有意义的值。
- 插值法: 利用已有的数据,推算出缺失值。常用的插值方法有线性插值、多项式插值、样条插值等。
- 模型预测法: 用机器学习模型(比如回归、分类)来预测缺失值。这种方法比较复杂,但效果往往更好。
举个栗子:
假设我们有一个客户信息表,其中有一列是“年龄”,但是有些客户没有填写。我们可以用平均年龄来填充这些缺失值。
import pandas as pd import numpy as np # 创建一个包含缺失值的DataFrame data = {'姓名': ['张三', '李四', '王五', '赵六'], '年龄': [25, np.nan, 30, 28], '性别': ['男', '女', '男', '女']} df = pd.DataFrame(data) # 用平均年龄填充缺失值 df['年龄'].fillna(df['年龄'].mean(), inplace=True) print(df)
输出:
姓名 年龄 性别 0 张三 25.0 男 1 李四 27.666667 女 2 王五 30.0 男 3 赵六 28.0 女
表格总结:
填充方法 适用类型 优点 缺点 均值/中位数/众数 数值型/类别型 简单易用 可能会引入偏差 固定值 所有类型 可以自定义填充值,赋予特殊含义 可能不符合数据的真实分布 插值法 数值型 可以利用数据之间的关系,更精确地填充缺失值 计算复杂度较高 模型预测 所有类型 效果更好,可以考虑多个特征之间的关系 需要训练模型,计算量大,容易过拟合
2. 异常值处理:揪出“捣蛋鬼”!
异常值,就像数据中的“捣蛋鬼”,会干扰模型的训练,影响预测的准确性。我们需要把这些“捣蛋鬼”揪出来,然后“绳之以法”。
-
定义异常值: 什么是异常值?这是一个主观的问题,需要根据具体的业务场景来判断。一般来说,我们可以用以下方法来定义异常值:
- 统计方法: 比如3σ原则、箱线图等。
- 领域知识: 比如某个产品的价格不可能低于0,某个人的年龄不可能超过150岁。
-
处理异常值:
- 删除: 直接删除含有异常值的样本。
- 替换: 用一个合理的值来替换异常值,比如均值、中位数、或者一个自定义的值。
- 转换: 对数据进行转换,比如对数转换、Box-Cox转换等,可以降低异常值的影响。
- 保留: 有时候,异常值本身就包含有用的信息,可以保留下来,作为一种特殊的特征。
举个栗子:
假设我们有一个电商网站的订单数据,其中有一列是“订单金额”。我们可以用箱线图来找出异常的订单金额。
import pandas as pd import matplotlib.pyplot as plt # 创建一个包含异常值的DataFrame data = {'订单金额': [100, 200, 300, 400, 500, 10000]} df = pd.DataFrame(data) # 画箱线图 plt.boxplot(df['订单金额']) plt.show() # 用箱线图的上下界来定义异常值 Q1 = df['订单金额'].quantile(0.25) Q3 = df['订单金额'].quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR # 找出异常值 outliers = df[(df['订单金额'] < lower_bound) | (df['订单金额'] > upper_bound)] print(outliers)
输出:
订单金额 5 10000
注意: 处理异常值要谨慎,一定要结合实际情况,不能盲目地删除或者替换。
3. 数据归一化/标准化:让数据站在同一起跑线!
不同的特征,可能具有不同的量纲和取值范围。这会导致一些问题:
- 影响模型的收敛速度: 取值范围大的特征,可能会 dominate 取值范围小的特征,导致模型收敛速度变慢。
- 影响模型的准确性: 某些模型(比如KNN、SVM)对特征的量纲比较敏感,需要进行归一化/标准化。
数据归一化/标准化,就是把不同的特征转换到同一个尺度上,让它们站在同一起跑线。
-
归一化: 把数据缩放到[0, 1]之间。常用的方法有Min-Max归一化。
from sklearn.preprocessing import MinMaxScaler # 创建一个MinMaxScaler对象 scaler = MinMaxScaler() # 对数据进行归一化 df['订单金额'] = scaler.fit_transform(df[['订单金额']]) print(df)
-
标准化: 把数据转换成均值为0,方差为1的标准正态分布。常用的方法有Z-score标准化。
from sklearn.preprocessing import StandardScaler # 创建一个StandardScaler对象 scaler = StandardScaler() # 对数据进行标准化 df['订单金额'] = scaler.fit_transform(df[['订单金额']]) print(df)
表格总结:
方法 公式 优点 缺点 Min-Max归一化 (x – min) / (max – min) 简单易懂,适用于数据分布比较集中的情况 对异常值比较敏感 Z-score标准化 (x – mean) / std 适用于数据分布比较分散的情况 需要计算均值和方差,计算量稍大
第二幕:特征工程,打造专属的“超能力” 💪
特征工程,就是对原始特征进行转换、组合和选择,创造出更有用的新特征,从而提高模型的性能。这就像给模型打造专属的“超能力”,让它在战场上更加骁勇善战。
1. 特征编码:把文字变成数字!
机器学习模型只能处理数值型数据,所以我们需要把类别型特征转换成数值型特征。这个过程就叫做特征编码。
-
独热编码(One-Hot Encoding): 把每个类别都表示成一个二进制向量。
from sklearn.preprocessing import OneHotEncoder # 创建一个OneHotEncoder对象 encoder = OneHotEncoder() # 对数据进行独热编码 encoded_features = encoder.fit_transform(df[['性别']]) # 将编码后的特征转换成DataFrame encoded_df = pd.DataFrame(encoded_features.toarray(), columns=encoder.get_feature_names_out(['性别'])) # 将编码后的特征和原始数据合并 df = pd.concat([df, encoded_df], axis=1) # 删除原始的类别型特征 df.drop(['性别'], axis=1, inplace=True) print(df)
-
标签编码(Label Encoding): 把每个类别都表示成一个整数。
from sklearn.preprocessing import LabelEncoder # 创建一个LabelEncoder对象 encoder = LabelEncoder() # 对数据进行标签编码 df['性别'] = encoder.fit_transform(df['性别']) print(df)
-
顺序编码(Ordinal Encoding): 把每个类别都表示成一个整数,并且保持类别之间的顺序关系。
注意: 选择哪种编码方式,要根据具体的业务场景来判断。一般来说,独热编码适用于类别数量较少的情况,标签编码适用于类别数量较多的情况,顺序编码适用于类别之间有顺序关系的情况。
2. 特征缩放:让数值型特征更加平易近人!
我们之前已经讲过数据归一化/标准化,它们也属于特征缩放的一种。特征缩放可以把数值型特征转换到同一个尺度上,让它们更加平易近人,更容易被模型理解。
-
对数转换: 对数据取对数,可以降低数据的偏度,让数据更接近正态分布。
import numpy as np # 对数据进行对数转换 df['订单金额'] = np.log1p(df['订单金额'])
-
Box-Cox转换: 一种更通用的转换方法,可以自动选择最佳的转换参数。
from scipy import stats # 对数据进行Box-Cox转换 df['订单金额'], _ = stats.boxcox(df['订单金额'] + 1) # Box-Cox不能处理负数和0
3. 特征组合:创造新的可能性!
特征组合,就是把两个或多个原始特征组合起来,创造出新的特征。这就像化学反应,可以产生意想不到的效果。
-
加减乘除: 对两个或多个数值型特征进行加减乘除运算。
# 创建一个新的特征,表示订单金额和订单数量的乘积 df['总金额'] = df['订单金额'] * df['订单数量']
-
多项式特征: 对原始特征进行多项式展开。
from sklearn.preprocessing import PolynomialFeatures # 创建一个PolynomialFeatures对象 poly = PolynomialFeatures(degree=2, include_bias=False) # 对数据进行多项式展开 poly_features = poly.fit_transform(df[['订单金额', '订单数量']]) # 将展开后的特征转换成DataFrame poly_df = pd.DataFrame(poly_features, columns=poly.get_feature_names_out(['订单金额', '订单数量'])) # 将展开后的特征和原始数据合并 df = pd.concat([df, poly_df], axis=1)
-
领域知识: 结合领域知识,创造出更有意义的特征。
举个栗子:
假设我们有一个电商网站的用户行为数据,包括用户的浏览时间、点击次数、购买次数等。我们可以结合领域知识,创造出以下特征:
- 用户活跃度: 根据用户的浏览时间、点击次数等指标,计算用户的活跃度。
- 用户购买力: 根据用户的购买次数、订单金额等指标,计算用户的购买力。
- 用户偏好: 根据用户的浏览历史、购买历史等信息,分析用户的偏好。
4. 特征选择:去伪存真,精益求精!
并不是所有的特征都对模型有用,有些特征甚至会干扰模型的训练。特征选择,就是从所有的特征中,选择出最有用的特征,去除冗余和无关的特征,从而提高模型的性能。
-
过滤法(Filter): 根据特征的统计指标(比如方差、相关系数)来选择特征。
- 方差选择法: 选择方差大于某个阈值的特征。
- 相关系数法: 选择与目标变量相关系数较高的特征。
-
包裹法(Wrapper): 把特征选择看作一个搜索问题,通过不断地尝试不同的特征组合,来找到最佳的特征子集。
- 递归特征消除法(RFE): 从所有的特征开始,每次删除一个最不重要的特征,直到剩下指定数量的特征。
-
嵌入法(Embedded): 把特征选择嵌入到模型的训练过程中,利用模型自身的特性来选择特征。
- L1正则化: L1正则化可以使一些特征的系数变为0,从而实现特征选择。
- 树模型: 树模型可以输出特征的重要性排序,可以根据重要性来选择特征。
表格总结:
方法 优点 缺点 过滤法 计算简单,速度快 没有考虑特征之间的相互作用,效果可能不好 包裹法 考虑了特征之间的相互作用,效果较好 计算量大,容易过拟合 嵌入法 结合了模型训练,效果更好 需要选择合适的模型和参数
第三幕:实战演练,手把手教你撸代码! 💻
光说不练假把式,接下来我们通过一个实际的案例,来演示如何进行数据预处理和特征工程。
案例:泰坦尼克号生存预测
这是一个经典的机器学习案例,目标是根据乘客的个人信息(比如年龄、性别、船票等级等),预测乘客是否在泰坦尼克号沉船事故中幸存。
1. 数据加载和探索
import pandas as pd
# 加载数据
train_df = pd.read_csv('train.csv')
test_df = pd.read_csv('test.csv')
# 查看数据的前几行
print(train_df.head())
# 查看数据的统计信息
print(train_df.describe())
# 查看数据的缺失值情况
print(train_df.isnull().sum())
2. 数据预处理
# 填充缺失值
train_df['Age'].fillna(train_df['Age'].median(), inplace=True)
test_df['Age'].fillna(test_df['Age'].median(), inplace=True)
test_df['Fare'].fillna(test_df['Fare'].median(), inplace=True)
train_df['Embarked'].fillna(train_df['Embarked'].mode()[0], inplace=True)
# 删除 Cabin 列,缺失值太多
train_df.drop('Cabin', axis=1, inplace=True)
test_df.drop('Cabin', axis=1, inplace=True)
# 删除 PassengerId、Name、Ticket 列,对预测没有帮助
train_df.drop(['PassengerId', 'Name', 'Ticket'], axis=1, inplace=True)
test_df.drop(['PassengerId', 'Name', 'Ticket'], axis=1, inplace=True)
3. 特征工程
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
# 标签编码 Sex 列
le = LabelEncoder()
train_df['Sex'] = le.fit_transform(train_df['Sex'])
test_df['Sex'] = le.transform(test_df['Sex'])
# 独热编码 Embarked 列
ohe = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
train_embarked = ohe.fit_transform(train_df[['Embarked']])
test_embarked = ohe.transform(test_df[['Embarked']])
train_embarked_df = pd.DataFrame(train_embarked, columns=ohe.get_feature_names_out(['Embarked']))
test_embarked_df = pd.DataFrame(test_embarked, columns=ohe.get_feature_names_out(['Embarked']))
train_df = pd.concat([train_df.reset_index(drop=True), train_embarked_df], axis=1)
test_df = pd.concat([test_df.reset_index(drop=True), test_embarked_df], axis=1)
train_df.drop('Embarked', axis=1, inplace=True)
test_df.drop('Embarked', axis=1, inplace=True)
4. 模型训练和预测
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# 准备数据
X = train_df.drop('Survived', axis=1)
y = train_df['Survived']
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建模型
model = LogisticRegression(max_iter=1000)
# 训练模型
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_val)
# 评估模型
accuracy = accuracy_score(y_val, y_pred)
print(f'Accuracy: {accuracy}')
# 对测试集进行预测
test_X = test_df.copy() # 确保测试集不影响训练集
predictions = model.predict(test_X)
print(predictions)
总结:数据预处理和特征工程,是成功的基石!
各位观众老爷们,今天我们一起学习了数据预处理和特征工程的各种技巧,并通过一个实际的案例进行了演练。希望这些技巧能帮助大家在数据挖掘的道路上越走越远,早日成为数据领域的“武林高手”! 🚀
记住,数据预处理和特征工程,是机器学习模型的基石。只有把数据打磨好,才能让模型发挥出最大的潜力。
最后,送给大家一句至理名言:
“巧妇难为无米之炊,英雄难过数据关!”
感谢大家的观看,我们下期再见! 👋