处理数值型特征
多种原始数值型数据转换为机器学习算法所需特征的方法。
1. 特征的缩放
用sklearn的MinMaxScaler来缩放一个特征数组,将一个数值型特征的值缩放到两个特定的值之间。
import numpy as np
from sklearn import preprocessing
#创建特征
feature = np.array([[-500.5],
[-100.1],
[0],
[100.1],
[900.9]])
#创建缩放器
minmax_scale = preprocessing.MinMaxScaler(feature_range=(0,1))
#缩放特征的值
scaled_feature = minmax_scale.fit_transform(feature)
#查看特征
scaled_feature
—>
array([[0. ],
[0.28571429],
[0.35714286],
[0.42857143],
[1. ]])
2. 特征的标准化
对一个特征进行转换,使其平均值为0,标准差为1.
import numpy as np
from sklearn import preprocessing
#创建特征
x = np.array([[-1000.1],
[-200.2],
[500.5],
[600.6],
[9000.9]])
#创建缩放器
scaler = preprocessing.StandardScaler()
#转换特征
standardized = scaler.fit_transform(x)
#查看特征
standardized
—>
array([[-0.76058269],
[-0.54177196],
[-0.35009716],
[-0.32271504],
[ 1.97516685]])
3. 归一化观察值
对观察值的每一个特征进行缩放,使其拥有一致的范数。
使用normalizer并指定norm参数:
import numpy as np
from sklearn.preprocessing import Normalizer
#创建特征
features = np.array([[0.5,0.5],
[1.1,3.4],
[1.5,20.2],
[1.63,34.4],
[10.9,3.3]])
#创建归一化器
normalizer = Normalizer(norm= "l2")
#转换特征特征
normalizer.transform(features)
—>
array([[0.70710678, 0.70710678],
[0.30782029, 0.95144452],
[0.07405353, 0.99725427],
[0.04733062, 0.99887928],
[0.95709822, 0.28976368]])
Normalizer提供三个范数选项,默认值是欧式范数(Euclidean norm,L2范数):
∣ ∣ x ∣ ∣ 2 = x 1 2 + ⋅ ⋅ ⋅ + x n 2 ||x||_2 = \sqrt {x_1^2+···+x_n^2} ∣∣x∣∣2=x12+⋅⋅⋅+xn2
或者可以指定使用曼哈顿范数(L1范数):
∣ ∣ x ∣ ∣ 1 = ∑ i = 1 n ∣ x i ∣ ||x||_1 = \sum _{i=1}^n |x_i| ∣∣x∣∣1=i=1∑n∣xi∣
4. 生成多项式和交互特征
scikit-learn内置方法创建多项式特征和交互特征:
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
#创建特征矩阵
features = np.array([[2,3],
[2,3],
[2,3]])
#创建PolynomialFeatures对象
polynomial_interaction = PolynomialFeatures(degree=2,include_bias=False)
#创建多项式特征
polynomial_interaction.fit_transform(features)
—>
array([[2., 3., 4., 6., 9.],
[2., 3., 4., 6., 9.],
[2., 3., 4., 6., 9.]])
默认情况下PolynomialFeatures包含交互特征 x 1 x 2 x_1x_2 x1x2
通过设置interaction_only为True,可以强制创建出来的特征只包含交互特征。
用这种方法来产生更高阶的形式——以此表示对目标值造成的非线性影响。
5. 转换特征
在sklearn中,使用FunctionTransformer对一组特征应用在一个函数上,对一个或多个特征进行自定义转换。
import numpy as np
from sklearn.preprocessing import FunctionTransformer
#创建特征矩阵
features = np.array([[2,3],
[2,3],
[2,3]])
#定义一个简单的函数
def add_ten(x):
return x+10
#创建转换器
ten_transformer = FunctionTransformer(add_ten)
#转换特征矩阵
ten_transformer.transform(features)
—>
array([[12, 13],
[12, 13],
[12, 13]])
在pandas中可以使用apply进行同样的转换
6. 识别异常值
识别样本中的一些异常值
常用的方法是假设数据是正态分布的,基于这个假设,在数据周围“画”一个椭圆,将所有处于椭圆内的观察值视为正常值(标注为1),将所有处于椭圆外的观察值视为异常值(标注为-1)。
import numpy as np
from sklearn.covariance import EllipticEnvelope
from sklearn.datasets import make_blobs
#创建模拟数据
features,_ = make_blobs(n_samples = 10,
n_features = 2,
centers = 1,
random_state = 1)
#将第一个观察值的值替换为极端值
features[0,0] = 10000
features[0,1] = 10000
#创建识别器
outlier_detector = EllipticEnvelope(contamination=0.1)
#拟合识别器
outlier_detector.fit(features)
#预测异常值
outlier_detector.predict(features)
—>
array([-1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
这个方法的一个主要限制是需要制定一个contamination(污染指数)参数,表示异常值在观察值中的比例。
除了查看所有观察值,还可以只查看某些特征,并使用四分位差(interqutile range,IQR)来识别这些特征的异常值。
IQR是数据集的第1个四分位数和第3个四分位数之差。可以将IQR视为数据集中大部分数据的延展距离,而异常值会远远地偏离数据较为集中的区域。异常值常常被定义为比第一个四分位数小1.5IQR的值,或比第3个四分位数大1.5IQR的值。
7. 处理异常值
解决方案:
- 舍弃异常值
- 标记异常值,作为数据特征
- 对有异常值的特征进行转换,降低异常值的影响
import pandas as pd
#创建数据
houses = pd.DataFrame()
houses['Price'] = [555555,455445,433434,233223]
houses['Bathrooms'] = [2,3,4,999]
houses['Square_Feet'] = [1500,2500,1500,48000]
#筛选出观察值
houses[houses['Bathrooms'] < 20]
—>
Price Bathrooms Square_Feet
0 555555 2 1500
1 455445 3 2500
2 433434 4 1500
第二种方法,标记为异常值作为特征:
import numpy as np
#基于布尔条件语句来创建特征
houses["outlier"] = np.where(houses["Bathrooms"]<20,0,1)
houses
—>
Price Bathrooms Square_Feet outlier
0 555555 2 1500 0
1 455445 3 2500 0
2 433434 4 1500 0
3 233223 999 48000 1
第三种方法,对有异常值的特征进行转换,降低异常值的影响:
#将特征取对数值
houses["Log of Square Feet"] = [np.log(x) for x in houses["Square_Feet"]]
houses
—>
Price Bathrooms Square_Feet outlier Log of Square Feet
0 555555 2 1500 0 7.313220
1 455445 3 2500 0 7.824046
2 433434 4 1500 0 7.313220
3 233223 999 48000 1 10.778956
和识别异常值一样,处理异常值时也不存在一个绝对准则。应该基于两个方面来考虑对异常值的处理。
- 是什么让他们成为异常值
- 基于机器学习的目标来处理异常值。
所以,对于异常值到底如何处理,首先思考一下为什么是异常值,然后对于数据要有一个最终的目标。最重要的是,要记住“决定不处理异常值”本身就是一个有潜在影响的决定。
另外,如果数据中有异常值,那么采用标准化方法做缩放就不太合适了,因为平均值和方差受异常值的影响很大。在这种情况下,需要针对异常值使用一个鲁棒性更高的缩放方法,比如RobustScaler。
8. 将特征离散化
将一个数值型特征离散化,分到多个离散的小区间中。
根据数据离散化的方式,有两种方法可以使用。
- 1.根据阈值将特征二值化
import numpy as np
from sklearn.preprocessing import Binarizer
#创建特征
age = np.array([[6],
[12],
[20],
[36],
[65]])
#创建二值化器
binarizer = Binarizer(18)
#转换特征
binarizer.fit_transform(age)
—>
array([[0],
[0],
[1],
[1],
[1]])
- 2.根据多个阈值将数值型特征离散化
np.digitize(age,bins=[20,30,64])
—>
array([[0],
[0],
[1],
[2],
[3]])
bins参数中的每个数字表示的是每个区间的左边界(左闭右开)。可以设置right参数为True来改变这个行为。
如果有足够多的理由认为某个数值型特征应该被视为一个分类特征(categorical feature),那么离散化会是一个卓有成效的策略。
9. 使用聚类的方式将观察值分组
问题描述:对观察值进行聚类操作,使相似的观察值被分为一组。
解决方案:如果有k个分组,可以使用K-means(K均值)聚类法将相似的观察值分到一个组,并输出一个新的特征,以标识观察值属于哪一组:
import pandas as pd
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
#创建模拟的特征矩阵
features,_=make_blobs(n_samples = 50,
n_features = 2,
centers = 3,
random_state = 1)
#创建数据帧
dataframe = pd.DataFrame(features,columns = ["feature_1","feature_2"])
#创建K-means聚类器
clusterer = KMeans(3,random_state = 0)
#将聚类器应用在特征上
clusterer.fit(features)
#预测聚类的值
dataframe["group"] = clusterer.predict(features)
dataframe.head(5)
—>
feature_1 feature_2 group
0 -9.877554 -3.336145 2
1 -7.287210 -8.353986 0
2 -6.943061 -7.023744 0
3 -7.440167 -8.791959 0
4 -6.641388 -8.075888 0
10. 删除带有缺失值的观察值
import numpy as np
features = np.array([[1.1,11.1],
[2.2,22.2],
[3.3,33.3],
[4.4,44.4],
[np.nan,55]])
#只保留没有缺失值的观察值
features[~np.isnan(features).any(axis=1)]
—>
array([[ 1.1, 11.1],
[ 2.2, 22.2],
[ 3.3, 33.3],
[ 4.4, 44.4]])
或者,使用pandas丢弃有缺失值的观察值:
import pandas as pd
dataframe = pd.DataFrame(features,columns=["feature_1","feature_2"])
#删除带有缺失值的观察值
dataframe.dropna()
—>
feature_1 feature_2
0 1.1 11.1
1 2.2 22.2
2 3.3 33.3
3 4.4 44.4
值得注意,删除缺失值可能会在数据中引入偏差,这主要由缺失值的成因决定。缺失值一共有三种类型:
- 完全随机确实(MCAR)
- 随机确实(MAR)
- 完全非随机确实(MNAR)
如果观察值是MCAR或者MAR,有时候删除是可以接受的。但是如果是MNAR,那么数据确实本身其实就是一个信息。删除MNAR观察值会导致数据产生偏差,因为这些观察值是由未观察到的系统效应产生的。
11. 填充缺失值
如果数据量不大,可以使用KNN算法来预测缺失值:
import numpy as np
from fancyimpute import KNN
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
#创建模拟的特征矩阵
features,_=make_blobs(n_samples = 1000,
n_features = 2,
random_state = 1)
#标准化特征
scaler = StandardScaler()
standardized_features = scaler.fit_transform(features)
#将第一个特征向量的第一个值替换为缺失值
true_value = standardized_features[0,0]
standardized_features[0,0] = np.nan
#预测特征矩阵中的缺失值
features_knn_imputed = KNN(k=5,verbose=0).fit_transform(standardized_features)
#对比真实值和填充值
print("True Value:",true_value)
print("Imputed Value:",features_knn_imputed)
—>
True Value: 0.8730186113995938
Imputed Value: [[ 1.09553327 1.31426523]
还可以使用sklearn的Imputer模块,用特征的平均值、中位数或者众数来填充缺失值,不过效果通常会比使用KNN的差。
比较容易扩展到大数据集的替代方案是用平均值来填充缺失值。