
10 个 NumPy 单行代码简化特征工程
作者 | Ideogram 提供图片
在构建机器学习模型时,大多数开发人员专注于模型架构和超参数调优。然而,真正的竞争优势来自于精心设计能够帮助模型理解数据潜在模式的代表性特征。
虽然 Pandas 和 Scikit-learn 等库提供了出色的工具来完成这项任务,但 NumPy 的向量化操作可以使特征工程既快速又优雅。
在本文中,我们将探讨 10 个强大的 NumPy 单行代码,它们可以简化您的特征工程工作流程。这些技术利用 NumPy 的广播、高级索引和数学函数来高效地创建新特征。
1. 使用中位数绝对偏差进行鲁棒缩放
标准缩放对于服从正态分布的数据效果很好,但在存在异常值时会失效。一个极端的数值会完全扭曲您的归一化,使您的特征对机器学习模型的作用降低。
这在金融或 Web 分析等领域尤其成问题,因为异常值通常包含重要信息。中位数绝对偏差 (MAD) 缩放提供了一种鲁棒的替代方法,可以处理大量异常值。
1 2 3 4 5 6 7 8 |
import numpy as np # 带有异常值的示例数据 data = np.array([1, 200, 3, 10, 4, 50, 6, 9, 3, 100]) # 单行代码:使用 MAD 进行鲁棒缩放 scaled = (data - np.median(data)) / np.median(np.abs(data - np.median(data))) print(scaled) |
输出
1 2 |
[-1.44444444 42.77777778 -1. 0.55555556 -0.77777778 9.44444444 -0.33333333 0.33333333 -1. 20.55555556] |
此单行代码首先通过 (data - np.median(data))
将数据围绕中位数居中,然后除以 MAD。MAD 是中位数绝对偏差的中位数。这为您提供了一个衡量尺度而不易受异常值影响的鲁棒指标,同时仍保留了极端值的相对重要性。
2. 使用分位数对连续变量进行分箱
将连续变量转换为分类分箱对于许多算法至关重要,并且有助于捕获非线性关系。等宽分箱通常会创建不平衡的组,尤其是在数据倾斜的情况下。然而,使用基于分位数的分箱,您可以获得每个分箱中大致相同数量的样本。
当您需要对树模型进行变量离散化,或为业务利益相关者创建可解释的特征时,此技术特别有用。
1 2 3 4 5 6 |
# 示例连续数据(例如,客户年龄) ages = np.array([18, 25, 35, 22, 45, 67, 23, 29, 34, 56, 41, 38, 52, 28, 33]) # 单行代码:创建 4 个等频率分箱 binned = np.digitize(ages, np.percentile(ages, [25, 50, 75])) - 1 print(binned) |
输出
1 |
[-1 -1 1 -1 2 2 -1 0 1 2 1 1 2 0 0] |
np.percentile()
函数计算分位数边界(25%、50%、75% 百分位数),然后 np.digitize()
将每个值分配到其合适的分箱。这种方法会自动处理倾斜分布,并创建有意义的分组,无论底层数据分布如何。
3. 无循环的多项式特征
多项式特征有助于捕获变量之间的非线性关系。传统方法通常涉及嵌套循环或复杂的库调用。
当您怀疑变量之间存在交互效应时,创建多项式特征很重要,例如温度和湿度对作物产量的影响,或者价格和质量如何共同影响客户满意度。
1 2 3 4 5 6 |
# 原始特征(例如,温度、湿度) X = np.array([[20, 65], [25, 70], [30, 45], [22, 80]]) # 单行代码:生成二阶多项式特征 poly_features = np.column_stack([X[:, [i, j]].prod(axis=1) for i in range(X.shape[1]) for j in range(i, X.shape[1])]) print(poly_features) |
输出
1 2 3 4 |
[[ 400 1300 4225] [ 625 1750 4900] [ 900 1350 2025] [ 484 1760 6400]] |
此列表推导式通过迭代列对并计算它们的乘积来创建所有可能的多项式组合。我们使用 np.column_stack()
函数将它们放入特征矩阵。结果包括平方项(x₁²,x₂²)和交互项(x₁x₂),使您的模型能够访问非线性关系。
3. 时间序列的滞后特征
时间序列分析通常需要捕获时间依赖性的特征。滞后特征允许您的模型访问历史值,这对于预测和异常检测至关重要。
创建滞后特征通常需要仔细管理索引的循环。这种向量化方法可以同时生成所有所需的滞后,并自动处理边缘情况。
1 2 3 4 5 |
# 时间序列数据(例如,每日销售额) sales = np.array([100, 98, 120,130, 74, 145, 110, 140, 65, 105, 135]) lags = np.column_stack([np.roll(sales, shift) for shift in [1, 2, 3]])[3:] print(lags) |
输出
1 2 3 4 5 6 7 8 |
[[120 98 100] [130 120 98] [ 74 130 120] [145 74 130] [110 145 74] [140 110 145] [ 65 140 110] [105 65 140]] |
np.roll()
函数将数组元素按指定的位数进行移位。列表推导式创建多个移位版本,np.column_stack()
将它们组合成一个特征矩阵。使用 [3:]
进行切片会删除滞后值无效的初始行,从而确保干净的训练数据。
4. 无 Pandas 的独热编码
独热编码对于处理机器学习中的类别变量至关重要。虽然 Pandas 提供了便捷的方法,但纯 NumPy 实现对于大型数据集来说更快、内存效率更高。
这种方法在处理高基数类别特征时尤其有价值。
1 2 3 4 5 6 |
# 类别数据(例如,产品类别) categories = np.array([0, 1, 2, 1, 0, 2, 3, 1]) # 单行代码:独热编码 one_hot = (categories[:, None] == np.arange(categories.max() + 1)).astype(int) print(one_hot) |
输出
1 2 3 4 5 6 7 8 |
[[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 1 0 0] [1 0 0 0] [0 0 1 0] [0 0 0 1] [0 1 0 0]] |
此技术利用广播将每个类别值与所有可能的类别进行比较。[:, None]
会重塑数组以实现广播,而 np.arange(categories.max() + 1)
创建比较范围。布尔结果被转换为整数,创建一个二进制矩阵,其中每一行代表一个样本,每一列代表一个类别。
5. 从坐标计算距离特征
地理空间特征通常需要从参考点计算距离。这在基于位置的模型中很常见,用于交付优化、房地产定价或人口统计分析。
当处理大量坐标数据集时,高效地计算距离至关重要,而这种向量化方法可以很好地扩展到数百万个数据点。
1 2 3 4 5 6 7 8 9 10 |
# 坐标数据 locations = np.array([[40.7128, -74.0060], [34.0522, -118.2437], [41.8781, -87.6298], [29.7604, -95.3698]]) reference = np.array([39.7392, -104.9903]) # 单行代码:计算到参考点的欧几里得距离 distances = np.sqrt(((locations - reference) ** 2).sum(axis=1)) print(distances) |
输出
1 |
[30.99959263 14.42201722 17.4917653 13.86111358] |
这利用 NumPy 的广播将参考点同时从所有位置减去。求平方差并在轴 1 上求和,然后取平方根得到欧几里得距离。为了获得更精确的地理距离,您可以将其扩展为使用半正矢公式。
6. 变量对之间的交互特征
特征交互通常有助于理解单个特征所忽略的隐藏模式。这在诸如营销(价格 × 质量)、医学(药物相互作用)或金融(波动性 × 交易量)等领域中很常见。
手动创建所有成对交互既繁琐又容易出错。这种向量化方法系统有效地生成它们。
1 2 3 4 5 6 7 8 |
# 示例特征(例如,价格、质量、品牌评分) features = np.array([[10, 8, 7], [15, 9, 6], [12, 7, 8], [20, 10, 9]]) # 单行代码:创建所有成对交互 interactions = np.array([features[:, i] * features[:, j] for i in range(features.shape[1]) for j in range(i+1, features.shape[1])]).T print(interactions) |
输出
1 2 3 4 |
[[ 80 70 56] [135 90 54] [ 84 96 56] [200 180 90]] |
嵌套推导式生成所有唯一的特征对(避免重复,如特征 1 × 特征 2 和特征 2 × 特征 1)。.T 转置结果,使每一行代表一个样本,每一列代表一个交互项。这种系统的方法确保您不会错过重要的特征组合。
7. 滑动窗口统计
滑动统计可以平滑噪声数据并捕获局部趋势。这对于时间序列分析、信号处理以及创建代表近期行为而不是历史平均值的特征至关重要。
传统方法通常涉及循环或复杂的 Pandas 操作。这种基于卷积的方法既优雅又高效。
1 2 3 4 5 6 7 |
# 噪声信号数据(例如,股票价格、传感器读数) signal = np.array([10, 27, 12, 18, 11, 19, 20, 26, 12, 19, 25, 31, 28]) window_size = 4 # 单行代码:创建滑动平均值特征 rolling_mean = np.convolve(signal, np.ones(window_size)/window_size, mode='valid') print(rolling_mean) |
输出
1 |
[16.75 17. 15. 17. 19. 19.25 19.25 20.5 21.75 25.75] |
卷积自然地实现了滑动窗口操作。np.ones(window_size)/window_size
创建一个均匀平均核,而 mode='valid'
确保输出仅包含窗口与数据完全重叠的位置。这种方法很容易扩展到其他窗口函数,例如高斯或指数加权。
8. 异常值指示符特征
与其删除异常值,不如创建标记其存在的特征可以为模型提供有价值的信息。这在欺诈检测、质量控制或任何异常有意义的领域中特别有用。
这种方法保留了异常值的信息内容,同时防止它们主导模型的训练过程。
1 2 3 4 5 6 7 |
# 带有潜在异常值的数据(例如,交易金额) amounts = np.array([25, 30, 28, 32, 500, 29, 31, 27, 33, 26]) # 单行代码:创建异常值指示符特征 outlier_flags = ((amounts < np.percentile(amounts, 5)) | (amounts > np.percentile(amounts, 95))).astype(int) print(outlier_flags) |
输出
1 |
[1 0 0 0 1 0 0 0 0 0] |
此技术使用第 5 个和第 95 个百分位数作为异常值阈值,标记超出此范围的任何值。布尔结果被转换为整数,创建指示异常观测值的二进制特征。您可以根据您的领域知识和可接受的误报率调整百分位数阈值。
9. 类别变量的频率编码
频率编码用出现次数替换类别值,这可能比任意标签编码更具信息量。当类别频率与您的目标变量相关时,这特别有用。
1 2 3 4 5 6 7 8 |
# 类别数据(例如,产品类别) categories = np.array(['Electronics', 'Books', 'Electronics', 'Clothing', 'Books', 'Electronics', 'Home', 'Books']) # 单行代码:频率编码 unique_cats, counts = np.unique(categories, return_counts=True) freq_encoded = np.array([counts[np.where(unique_cats == cat)[0][0]] for cat in categories]) print(freq_encoded) |
输出
1 |
[3 3 3 1 3 3 1 3] |
这种方法首先使用 np.unique()
查找所有唯一的类别及其计数。然后,对于每个原始类别值,它会查找相应的频率计数。结果是一个数值特征,其中每个值表示该类别在数据集中出现的次数,从而为模型提供有关类别受欢迎程度或稀有度信息。
特征工程最佳实践
当您创建新的、更具代表性的特征时,请牢记以下几点:
内存效率:处理大型数据集时,请考虑特征工程的内存影响。某些操作可能会显著增加数据集的大小。
特征选择:更多的特征并不总是更好的。使用相关性分析或特征重要性等技术来选择最相关的工程特征。
验证:始终在保留集上验证您的工程特征,以确保它们能提高模型性能,而不会导致过拟合。
领域知识:最好的工程特征通常来自对您所在领域的理解。这些 NumPy 技术是高效实现您的领域见解的工具。
结论
这些 NumPy 单行代码是针对常见特征工程挑战的实用解决方案。
在处理时间序列、地理空间数据或传统表格数据集时,这些技术将帮助您构建更高效、更易于维护的特征工程管道。关键在于了解何时使用每种方法以及如何组合它们来从数据中提取最大信号。
请记住,最好的特征工程技术是能够帮助您的模型学习特定于问题领域的模式的技术。将这些简短的技巧作为构建块,但务必通过适当的交叉验证和领域专业知识来验证它们的有效性。所以,祝您特征工程愉快!
感谢您提供这些信息丰富且有用的用例集
你好 Chris……非常欢迎!请随时向我们更新您的进展。