在机器学习领域,特征工程是构建智能系统过程中不可或缺的一环。它既是一门艺术,也是一门科学。数据科学家通常会花费70%至80%的时间在数据的清洗和准备上,这直接影响着模型的质量。Google研究总监Peter Norvig曾说过:“并没有比别人更好的算法,只是拥有更多的数据。”
特征工程是一个利用领域知识从给定数据集中创建或提取新特征的过程,它通过数据挖掘技术帮助机器学习算法理解数据并识别可以提高算法性能的模式。特征工程的步骤包括:头脑风暴特征、创建特征、检查特征与模型的协作效果,然后不断重复这个过程直到特征完美协作。
例如,预测时尚店销售量的算法,如果在冬季特别是圣诞节期间销售高峰,添加一个特征来表示距离圣诞节还有多少天,将比单纯的日期信息给算法提供更多的直觉。
在处理数值数据时,经常会遇到一些特征的分布是偏斜的,即某些值集出现的频率很高,而有些则很少。直接使用这类特征可能会导致问题或不准确的结果。分箱是将数值连续变量转换为离散变量的一种方式,通过基于它们落在的列值范围进行分类。这种转换可以防止过拟合并增强模型的鲁棒性。
例如,有10名学生的成绩分别为35, 46, 89, 20, 58, 99, 74, 60, 18, 81。任务是将他们分成3个队伍。队伍1将包含成绩在1-40之间的学生,队伍2包含成绩在41-80之间的学生,队伍3包含成绩在81-100之间的学生。
分箱可以以不同的方式进行,包括:
正如名字所示,在固定宽度分箱中,为每个箱子设定了特定的固定宽度,这些宽度通常由分析数据的用户预先定义。每个箱子有一个预设的值范围,应该根据一些领域知识、规则或约束将值分配到相应的箱子中。
import pandas as pd
# 读取文件
df_bin = pd.read_csv('stroke_prediction.csv')
# 创建箱子和标签
bins = [1,10,20,30,40]
labels = ['bin-1','bin-2','bin-3','bin-4']
df_bin['age_range'] = pd.cut(df_bin['age'], bins=bins, labels=labels)
如果数值特征的范围中存在大量间隙,固定宽度分箱将不会那么有效,会出现许多没有数据的空箱子。在这种情况下,分箱是基于分位数分布进行的。分位数将数据分成相等的部分。中位数将数据分成两部分,一半数据小于中位数,一半数据大于中位数。四分位数将数据分成四部分,十分位数分成十部分等。
这实际上涉及到一个手动过程,根据个人对数据的洞察手动进行分箱,并设置想要的箱子范围。例如,可以将人的年龄分组,1-18岁属于未成年人,19-29岁属于青年,30-49岁属于中年,50-100岁属于老年人。
import pandas as pd
# 读取文件
df_bin = pd.read_csv('stroke_prediction.csv')
# 创建箱子和标签
bins = [1,19,30,50,100]
labels = ['minor','young','old','very_old']
df_bin['age_range'] = pd.cut(df_bin['age'], bins=bins, labels=labels)
大多数时候,数据集包含复杂的特征,即两个或更多特征的组合。通过提取和创建更有意义的特征,可以改善模型性能,揭示潜在信息。这可以应用于连续和分类特征。
import pandas as pd
# 读取文件
df = pd.read_csv('housing_price.csv')
# 分离整数部分和小数部分以创建两个新特征
df['floor_num'] = df['floors'].apply(lambda x: x//1)
df['Is_penrhouse'] = df['floors'].apply(lambda x: x%1)
日期列为模型目标提供了宝贵的信息,它们通常被忽视作为机器学习算法的输入。如果日期列未经处理,构建机器学习算法之间的关系将非常具有挑战性。可以将日期的部分提取到不同的列中,如年、月、日、周等。可以计算两个日期之间的天数。可以创建新特征,比如是否是周末或工作日。可以创建特征,比如是否是假日。
import pandas as pd
# 读取文件
df = pd.read_csv('housing_price.csv')
df['date'] = pd.to_datetime(df['date'])
df['month'] = pd.DatetimeIndex(df['date']).month
df['week'] = pd.DatetimeIndex(df['date']).week
在这种特征工程技术中,通过组合两个或更多特征来创建新特征,这可以为提供更多的洞察。例如,有一个中风预测数据集,需要预测一个人是否会中风。可以看到,并且也可以通过探索性数据分析(EDA)确认,年龄越大的人中风的几率越高,同样,BMI和平均血糖水平越高的人也越有可能中风。
import pandas as pd
# 读取文件
df = pd.read_csv('stroke_prediction.csv')
# 创建新特征,如果人的年龄>50且平均血糖水平>180,则填充1
a = (df["hypertension"]=="1")
b = (df["heart_disease"]=="1")
df['hypertension_heart_patient'] = a & b
df = df.replace(True, 1)
df = df.replace(False, 0)
一些机器学习模型,如线性和逻辑回归,假设变量遵循正态分布。更有可能的是,数据集中的变量具有偏斜分布。为了去除变量的偏斜性并使其接近正态分布,应用不同的转换以提高模型的性能。
最常用的变量转换方法包括:
这是所有转换中最受欢迎的转换,也是最简单的一个。它通常用于右偏斜分布,使其接近正态分布。
import pandas as pd
import numpy as np
from sklearn.preprocessing import FunctionTransformer
# 加载数据
df = pd.read_csv('anydata.csv')
# 创建列变量以保存需要转换的列
columns = ['col_1','col_2','col_3']
# 创建函数转换器对象,使用对数转换
logarithm_transfer = FunctionTransformer(np.log, validate=True)
# 应用转换
data_new = logarithm_transfer.transform(data[columns])
倒数转换将大值转换为小值,同时保持相同符号的值之间的顺序。
import pandas as pd
import numpy as np
from sklearn.preprocessing import FunctionTransformer
# 加载数据
df = pd.read_csv('anydata.csv')
# 创建列变量以保存需要转换的列
columns = ['col_1','col_2','col_3']
# 创建函数转换器对象,使用倒数转换
reciprocal_transfer = FunctionTransformer(np.reciprocal, validate=True)
# 应用转换
data_new = reciprocal_transfer.transform(data[columns])
import pandas as pd
import numpy as np
from sklearn.preprocessing import FunctionTransformer
# 加载数据
df = pd.read_csv('anydata.csv')
# 创建列变量以保存需要转换的列
columns = ['col_1','col_2','col_3']
# 创建函数转换器对象,使用指数转换
# 使用x^3是任意的,可以选择任何指数
exponential_transfer = FunctionTransformer(lambda x: x**3, validate=True)
# 应用转换
data_new = exponential_transfer.transform(data[columns])