数据准备指南:10种基础特征工程方法的实战教程

数据派THU

共 14375字,需浏览 29分钟

 ·

2024-10-26 17:00

   

源:Deephub Imba

本文约5000字,建议阅读9分钟
本文深入探讨Transformer模型中三种关键的注意力机制:自注意力、交叉注意力和因果自注意力。

在数据分析和机器学习领域,从原始数据中提取有价值的信息是一个关键步骤。这个过程不仅有助于辅助决策,还能预测未来趋势。为了实现这一目标,特征工程技术显得尤为重要。


特征工程是将原始数据转化为更具信息量的特征的过程。本文将详细介绍十种基础特征工程技术,包括其基本原理和实现示例。

首先,我们需要导入必要的库以确保代码的正常运行。以下是本文中使用的主要库:

   
 import pandas as pd  # 用于数据处理和操作import numpy as np  # 用于数值计算import matplotlib.pyplot as plt  # 用于数据可视化import gensim.downloader as api  # 用于下载gensim提供的语料库from gensim.models import Word2Vec  # 用于词嵌入from sklearn.pipeline import Pipeline  # 用于构建数据处理管道from sklearn.decomposition import PCA  # 用于主成分分析from sklearn.datasets import load_iris  # 用于加载iris数据集from sklearn.impute import SimpleImputer  # 用于数据插补from sklearn.compose import ColumnTransformer  # 用于对数据集应用转换
from sklearn.feature_extraction.text import TfidfVectorizer # 用于TF-IDF实现 from sklearn.preprocessing import MinMaxScaler, StandardScaler # 用于数据缩放


1、数据插补


数据插补是处理缺失数据的重要技术,它通过用其他值替换缺失数据来完善数据集。在实际应用中,许多算法(如线性回归和逻辑回归)无法直接处理包含缺失值的数据集。因此我们通常有两种选择:

  1. 删除包含缺失值的行或列
  2. 对缺失值进行插补

数据插补的方法多样,包括:

  1. 使用常数值填充(如0、1、2等)
  2. 使用统计量填充(如均值或中位数)
  3. 使用相邻数据值填充(如前值或后值)
  4. 构建预测模型估计缺失值

以下是一个数据插补的实现示例:

   
 
data = pd.DataFrame({     'doors': [2, np.nan, 2, np.nan, 4],     'topspeed': [100, np.nan, 150, 200, np.nan],     'model': ['Daihatsu', 'Toyota', 'Suzuki', 'BYD','Wuling'] })
doors_imputer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='constant', fill_value=4)) ])
topspeed_imputer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='median')) ])
pipeline = ColumnTransformer( transformers=[ ('doors_imputer', doors_imputer, ['doors']), ('topspeed_imputer', topspeed_imputer, ['topspeed']) ], remainder='passthrough' )
transformed = pipeline.fit_transform(data)
transformed_df = pd.DataFrame(transformed, columns=['doors', 'topspeed', 'model'])

在这个例子中创建了一个包含汽车数据的DataFrame,其中doors和topspeed列存在缺失值。对于doors列,使用常数4进行填充(假设大多数汽车有4个门)。对于topspeed列,使用中位数进行填充。

下图展示了插补前后的数据对比:


可以观察到doors列的缺失值被填充为4,而topspeed列的缺失值被填充为数据的中位数。

2、数据分箱


数据分箱是将连续变量转换为离散分类变量的技术。这种技术在日常生活中常被无意识地使用,例如将人按年龄段分类。

数据分箱的主要目的包括:

  1. 简化数据,将连续值转换为离散类别
  2. 处理非线性关系
  3. 减少数据中的噪声和异常值

以下是一个数据分箱的实现示例:

   
np.random.seed(42) data = pd.DataFrame({'age' : np.random.randint(0, 100, 100)}) data['category'] = pd.cut(data['age'], [0, 2, 11, 18, 65, 101], labels=['infants', 'children', 'teenagers', 'adults', 'elders']) print(data) print(data['category'].value_counts()) data['category'].value_counts().plot(kind='bar')

在这个例子中,我们生成了100个0到100之间的随机整数作为年龄数据,然后将其分为五个类别:婴儿、儿童、青少年、成年人和老年人。

以下是分箱结果的可视化:


通过数据分箱,可以更直观地理解数据的分布情况。在某些算法中,经过分箱处理的离散数据可能比原始的连续数据更有优势。

3、对数变换


对数变换是将特征值从x转换为log(x)的技术。这种方法常用于处理高度偏斜的数据分布或存在大量异常值的情况。

对数变换在线性回归和逻辑回归等模型中特别有用,因为它可以将乘法关系转换为加法关系,从而简化模型。

以下是对数变换的实现示例:

   
 rskew_data = np.random.exponential(scale=2, size=100)
log_data = np.log(rskew_data)
plt.title('Right Skewed Data') plt.hist(rskew_data, bins=10) plt.show() plt.title('Log Transformed Data') plt.hist(log_data, bins=20) plt.show()

在这个例子中,生成了100个右偏的数据点,然后对其进行对数变换。下图展示了变换前后的数据分布对比:


需要注意的是,对数变换并不会自动将数据分布变为正态分布,它主要用于减少数据的偏度。

4、数据缩放


数据缩放是将数据调整到特定范围或满足特定条件的预处理技术。常见的缩放方法包括:

  • 最小-最大缩放:将数据调整到[0, 1]区间
  • 标准化:将数据调整为均值为0,标准差为1的分布

最小-最大缩放主要用于将数据归一化到特定范围,而标准化则考虑了数据的分布特征。

以下是数据缩放的实现示例:

   
 data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).reshape(-1, 1)
scaler = MinMaxScaler() minmax = scaler.fit_transform(data)
scaler = StandardScaler() standard = scaler.fit_transform(data)
df = pd.DataFrame({'original':data.flatten(),'Min-Max Scaling':minmax.flatten(),'Standard Scaling':standard.flatten()}) df

下图展示了原始数据、最小-最大缩放后的数据和标准化后的数据的对比:


可以观察到,最小-最大缩放将数据调整到[0, 1]区间,而标准化后的数据均值接近0,标准差接近1。

5、One-Hot编码


One-Hot编码是处理分类数据的常用方法,特别适用于那些没有固有顺序的名义变量。这种技术将每个分类变量转换为一系列二进制特征。

One-Hot编码的工作原理如下:

  1. 对于分类特征中的每个唯一值,创建一个新的二进制列。
  2. 在新创建的列中,如果原始数据中出现了相应的分类值,则标记为1,否则为0。

这种方法也被称为虚拟编码(dummy encoding)。

以下是One-Hot编码的实现示例:

   
 data = pd.DataFrame({'models':['toyota','ferrari','byd','lamborghini','honda','tesla'],                     'speed':['slow','fast','medium','fast','slow','medium']}) data = pd.concat([data, pd.get_dummies(data['speed'], prefix='speed')],axis=1) data

下图展示了编码后的结果:


'speed'列被转换为三个新的二进制列:
'speed_fast'、'speed_medium'和'speed_slow'。每行在这些新列中只有一个1,其余为0,对应原始的速度类别。

当分类变量的唯一值数量很大时,One-Hot编码可能会导致特征空间的急剧膨胀。在这种情况下,可能需要考虑其他编码方法或降维技术。

6、目标编码


目标编码是一种利用目标变量来编码分类特征的方法。这种技术特别适用于高基数的分类变量(即具有大量唯一值的变量)。

目标编码的基本步骤如下:

  1. 对于分类特征中的每个类别,计算对应的目标变量统计量(如均值)。
  2. 用计算得到的统计量替换原始的类别值。

以下是目标编码的一个简单实现:

   
fruits = ['banana','apple','durian','durian','apple','banana'] price = [120,100,110,150,140,160] data = pd.DataFrame({   'fruit': fruits,   'price': price }) data['encoded_fruits'] = data.groupby('fruit')['price'].transform('mean') data

结果如下图所示:


我们用每种水果的平均价格替换了原始的水果名称。这种方法不仅可以处理高基数的分类变量,还能捕捉类别与目标变量之间的关系。

使用目标编码时需要注意以下几点:

  1. 可能导致数据泄露,特别是在不做适当的交叉验证的情况下。
  2. 对异常值敏感,可能需要进行额外的异常值处理。
  3. 在测试集中遇到训练集中未出现的类别时,需要有合适的处理策略。


7、主成分分析(PCA)


主成分分析(Principal Component Analysis,PCA)是一种常用的无监督学习方法,主要用于降维和特征提取。PCA通过线性变换将原始特征投影到一个新的特征空间,使得新的特征(主成分)按方差大小排序。

PCA的主要步骤包括:

  1. 数据标准化
  2. 计算协方差矩阵
  3. 计算协方差矩阵的特征值和特征向量
  4. 选择主成分
  5. 投影数据到新的特征空间

以下是使用PCA的一个示例,我们使用著名的Iris数据集:

   
 iris_data = load_iris() features = iris_data.data targets = iris_data.target
features.shape # 输出: (150, 4)
pca = PCA(n_components=2) pca_features = pca.fit_transform(features)
pca_features.shape # 输出: (150, 2)
for point in set(targets): plt.scatter(pca_features[targets == point, 0], pca_features[targets == point,1], label=iris_data.target_names[point]) plt.xlabel('PCA Component 1') plt.ylabel('PCA Component 2') plt.title('PCA on Iris Dataset') plt.legend() plt.show()

结果如下图所示:


在这个例子中将原始的4维特征空间降至2维。从图中可以看出,即使在降维后,不同类别的数据点仍然保持了良好的可分性。

PCA的优点包括:

  1. 减少数据的维度,降低计算复杂度。
  2. 去除噪声和冗余信息。
  3. 有助于数据可视化。

PCA也有一些局限性:

  1. 可能导致一定程度的信息损失。
  2. 转换后的特征难以解释,因为每个主成分都是原始特征的线性组合。
  3. 仅捕捉线性关系,对于非线性关系效果可能不佳。


8、 特征聚合


特征聚合是一种通过组合现有特征来创建新特征的方法。这种技术常用于时间序列数据、分组数据或者需要综合多个特征信息的场景。

常见的特征聚合方法包括:

  1. 统计聚合:如平均值、中位数、最大值、最小值等。
  2. 时间聚合:如按天、周、月等时间单位聚合数据。
  3. 分组聚合:根据某些类别特征对数据进行分组,然后在每个组内进行聚合。

以下是一个特征聚合的示例:

   
 quarter = ['Q1','Q2','Q3','Q4'] car_sales = [10000,9850,13000,20000] motorbike_sales = [14000,18000,9000,11000] sparepart_sales = [5000, 7000,3000, 10000]
data = pd.DataFrame({'car':car_sales, 'motorbike':motorbike_sales, 'sparepart':sparepart_sales}, index=quarter)
data['avg_sales'] = data[['car','motorbike','sparepart']].mean(axis=1).astype(int) data['total_sales'] = data[['car','motorbike','sparepart']].sum(axis=1).astype(int)
data

结果如下图所示:


在这个例子中创建了两个新的特征:

  1. 'avg_sales':每个季度不同产品的平均销售额。
  2. 'total_sales':每个季度所有产品的总销售额。

这种聚合可以帮助我们从不同角度理解数据,发现可能被单个特征忽略的模式。

特征聚合的优点包括:

  1. 可以捕捉多个特征之间的关系。
  2. 减少特征的数量,有助于模型的解释和计算效率。
  3. 可能创造出更有预测力的特征。

在使用特征聚合时也需要注意:

  1. 聚合可能会导致一些细节信息的丢失。
  2. 需要领域知识来决定哪些聚合是有意义的。
  3. 过度聚合可能会导致过拟合。


9、TF-IDF(词频-逆文档频率)


TF-IDF(Term Frequency-Inverse Document Frequency)是一种广泛用于文本分析和信息检索的特征提取技术。它结合了词频(TF)和逆文档频率(IDF)两个指标,用于评估一个词对于一个文档集或一个语料库中的某一个文档的重要程度。
TF-IDF的计算基于以下两个概念:

  1. 词频(TF):衡量一个词在文档中出现的频率。计算公式为:TF(t,d) = (词t在文档d中出现的次数) / (文档d中的总词数)
  2. 逆文档频率(IDF):衡量一个词在整个文档集中的普遍重要性。计算公式为:IDF(t) = log(总文档数 / 包含词t的文档数)

TF-IDF的最终得分是TF和IDF的乘积:TF-IDF(t,d) = TF(t,d) * IDF(t)

以下是使用TF-IDF的一个示例:

   
 texts = ["I eat rice with eggs.",         "I also love to eat fried rice. Rice is the most delicious food in the world"]  vectorizer = TfidfVectorizer() tfidfmatrix = vectorizer.fit_transform(texts) features = vectorizer.get_feature_names_out() data = pd.DataFrame(tfidfmatrix.toarray(), columns=features)  print("TF-IDF matrix") data

结果如下图所示:


在这个例子中:

  • 第一行代表句子 "I eat rice with eggs."
  • 第二行代表句子 "I also love to eat fried rice. Rice is the most delicious food in the world"

可以观察到,"rice" 这个词在第一个句子中的TF-IDF值(0.409)比在第二个句子中的值(0.349)更高。这是因为虽然 "rice" 在第二个句子中出现得更频繁,但第一个句子更短,使得 "rice" 在其中的相对重要性更高。

TF-IDF的主要优点包括:

  1. 能够反映词语在文档中的重要程度。
  2. 可以过滤掉常见词语,突出关键词。
  3. 计算简单,易于理解和实现。

TF-IDF也有一些局限性:

  1. 没有考虑词序和语法结构。
  2. 对于极短文本可能效果不佳。
  3. 不能捕捉词语之间的语义关系。


10、文本嵌入


文本嵌入是将文本数据(如单词、短语或文档)映射到连续向量空间的技术。这种技术能够捕捉词语之间的语义关系,是现代自然语言处理中的基础技术之一。

常见的文本嵌入方法包括:

  1. Word2Vec
  2. GloVe (Global Vectors for Word Representation)
  3. FastText
  4. BERT (Bidirectional Encoder Representations from Transformers)

以下是使用Word2Vec进行文本嵌入的示例:
   

 corpus = api.load('text8')   model = Word2Vec(corpus)   dog = model.wv['dog'] print("Embedding vector for 'dog':\n", dog)

  
输出结果示例:


我们使用了gensim库提供的text8语料库(包含维基百科文本的前100,000,000个字节)来训练Word2Vec模型。每个词被映射到一个100维的向量空间中。

文本嵌入的一个重要特性是能够捕捉词语之间的语义关系。我们可以通过计算词向量之间的相似度来展示这一点:

 cat = model.wv['cat'] car = model.wv['car']
dogvscat = model.wv.similarity('dog','cat') dogvscar = model.wv.similarity('dog','car')
print("Similarity:") print("Dog vs Cat: ", dogvscat) print("Dog vs Car: ", dogvscar)

  

输出结果:


从结果可以看出,"dog"和"cat"的相似度明显高于"dog"和"car"的相似度,这符合我们的语义直觉。

文本嵌入的主要优点包括:

  1. 能够捕捉词语之间的语义关系。
  2. 可以处理高维稀疏的文本数据,将其转换为低维稠密的向量表示。
  3. 通过迁移学习,可以在小规模数据集上也能获得良好的表现。

文本嵌入也存在一些挑战:

  1. 训练高质量的嵌入模型通常需要大量的文本数据和计算资源。
  2. 词语的多义性可能无法被单一的静态向量完全捕捉。
  3. 对于特定领域的任务,可能需要在领域特定的语料上重新训练或微调嵌入模型。


总结


本文介绍了十种基本的特征工程技术,涵盖了数值型、分类型和文本型数据的处理方法。

每种技术都有其特定的应用场景和优缺点。在实际应用中,选择合适的特征工程技术需要考虑数据的特性、问题的性质以及模型的要求。often需要结合多种技术来获得最佳的特征表示。

还有许多其他高级的特征工程技术未在本文中涉及,如时间序列特征工程、图像特征提取等。随着机器学习和深度学习技术的发展,特征工程的重要性可能会有所变化,但理解和掌握这些基本技术仍然是数据科学实践中的重要基础。

特征工程不仅是一门技术,更是一门艺术。它需要领域知识、直觉和经验的结合。通过不断的实践和实验,我们可以逐步提高特征工程的技能,从而为后续的机器学习任务奠定坚实的基础。


编辑:王菁



关于我们

数据派THU作为数据科学类公众号,背靠清华大学大数据研究中心,分享前沿数据科学与大数据技术创新研究动态、持续传播数据科学知识,努力建设数据人才聚集平台、打造中国大数据最强集团军。



新浪微博:@数据派THU

微信视频号:数据派THU

今日头条:数据派THU

浏览 76
1点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
1点赞
评论
收藏
分享

手机扫一扫分享

分享
举报