原创

机器学习手册03---处理数值型数据


在学习本篇之前,我们要引入一个机器学习的“新角色”————scikit-learn(最常见的机器学习包之一),主要包含以下几个常用子模块:

(1)分类:SVM、最近邻、随机森林、逻辑回归等

(2)回归:Lasso、岭回归等

(3)聚类:k-means、谱聚类等

(4)降维:PCA、特征选择、矩阵分解等

(5)模型选择:网格搜索、交叉验证、指标矩阵

(6)预处理:特征提取、正态化

1.特征的缩放

将一个数值型特征的值缩放(rescale)到两个特定的值之间,利用MinMaxScaler():

import numpy as np
from sklearn import preprocessing

feature = np.array([[-100],
                    [-20.5],
                    [20],
                    [140]])

# 创建缩放器: 范围是0~1
minmax_scale = preprocessing.MinMaxScaler(feature_range=(0,1))
# 缩放特征的值
scaled_feature = minmax_scale.fit_transform(feature)

# 输出
print(scaled_feature)
[[0.     ]
 [0.33125]
 [0.5    ]
 [1.     ]]

【小提示】:min-max缩放是最简单的一种,常见缩放范围是[0,1]或[-1,1]。

2.特征的标准化

将一个特征进行准换,使其平均值为0、标准差为1,利用StandardScaler()

import numpy as np
from sklearn import preprocessing

# 创建特征
x = np.array([[-1000.1],
              [-200.3],
              [500.3],
              [9200]])

# 创建缩放器: 平均值为0,标准差为1
scaler = preprocessing.StandardScaler()
# 转换特征
standardized = scaler.fit_transform(x)

# 输出
print(standardized)
[[-0.75867569]
 [-0.56450793]
 [-0.39442299]
 [ 1.71760662]]

【小提示】:在PCA(主成分分析)中标准化更有用,而在神经网络中更推荐使用min-max缩放。

# 查看特征标准化后: 平均值、标准差
print(round(standardized.mean()))
print(standardized.std())
0.0
0.9999999999999999

如果数据中存在很大的异常值,可能会影响特征的平均值和方差,也会对标准化造成不良影响。在这种情况下,使用中位数和四分数间距进行缩放会更有效,利用RobustScaler()。

3.归一化观察值

min-max与标准化都是对特征进行操作的,但是其实对观察值也可以进行缩放。Normalizer()可以对单个观察值进行缩放,使其具有一致的范围(总长度为1)。当一个观察值有多个相等的特征时(例如:文本分类,每个词或每几个词就是一个特征),经常使用这种缩放。

import numpy as np
from sklearn import preprocessing

# 创建特征
features = np.array([[0.5, 0.5],
              [1.1, 3.4],
              [1.5, 20.2],
              [10.9, 3.3]])

# 创建归一化器: 欧氏范数(l2范数)
normalizer = preprocessing.Normalizer(norm='l2')
# 创建特征矩阵
normalizer_l2 = normalizer.transform(features)

print(normalizer_l2)
[[0.70710678 0.70710678]
 [0.30782029 0.95144452]
 [0.07405353 0.99725427]
 [0.95709822 0.28976368]]

Normalizer提供了三种范数类型,其中前两种比较常用:

(1)欧式范数(L2范数):||X||=[(x1)^2+(x2)^2+...+(xn)^2]^(1/2),其中x是独立观察值,xn是这个观察值的第n个特征。

(2)曼哈顿范数(L1范数):如果L2范数可以被当成两点间的距离,那么曼哈顿范数可以被视为一个人沿着街道行走的距离(先向北走,再向东走,等等),这就是为什么别称叫做“出租车范数”。

# Normalizer() :曼哈顿范数(l1范数)
normalizer = preprocessing.Normalizer(norm='l1')
normalizer_l1 = normalizer.transform(features)
print(normalizer_l1)

# 验证总和是否为1
print(normalizer_l1[0,0]+normalizer_l1[0,1])
[[0.5        0.5       ]
 [0.24444444 0.75555556]
 [0.06912442 0.93087558]
 [0.76760563 0.23239437]]
 1.0

4.生成多项式和交互特征

PolynomialFeatures(): 参数degree、include_bias、interaction_only

import numpy as np
from sklearn import preprocessing

# 创建特征
features = np.array([[2, 3],
                     [2, 3],
                     [1, 3]])

# 创建PolynomialFeatures对象: degree=2阶数最高为2
polynomial_interaction = preprocessing.PolynomialFeatures(degree=2, include_bias=False)

# 创建多项式特征
result1 = polynomial_interaction.fit_transform(features)

print(result1)
# x1, x2, x1^2, x1*x2, x2^2
[[2. 3. 4. 6. 9.]
 [2. 3. 4. 6. 9.]
 [1. 3. 1. 3. 9.]]

如果在参数中添加:interaction_only=True,则输出只包含交互特征:

# x1, x2, x1*x2
[[2. 3. 6.]
 [2. 3. 6.]
 [1. 3. 3.]]

【小提示】:当特征与目标值(预测值)之间存在非线性关系时,就需要创建多项式特征。另外,有时我们会遇到一个特征需要依赖另一个特征才能对目标值造成影响的情况。例如:要预测一杯咖啡是否为甜的,要考虑两个特征:咖啡是否被搅拌过、是否加了糖。两者对目标值的作用是互相依赖的,生成一个交互特征。

5.自定义转换特征

import numpy as np
from sklearn import preprocessing
import pandas as pd

# 创建特征
features = np.array([[2, 3],
                     [2, 3],
                     [1, 3]])

def add_ten(x):
    return x + 10

# 方法一:利用sklearn中的FunctionTransformer():
# 创建转换器
ten_transformer = preprocessing.FunctionTransformer(add_ten)
# 转换特征矩阵
print(ten_transformer.transform(features))

# 方法二:利用pandas中的apply
# 创建数据帧
df = pd.DataFrame(features, columns=['feature_1', 'feature_2'])

print(df.apply(add_ten))
[[12 13]
 [12 13]
 [11 13]]

   feature_1  feature_2
0         12         13
1         12         13
2         11         13

6.识别异常值

常用的方法是假设数据是正态分布的,基于这个假设,在数据周围“画”一个椭圆,将所有处于椭圆内的观察值视为1(正常值),将所有处于椭圆外的观察值视为-1(异常值),利用EllipticEnvelope():

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)

print(features)

# 将第一个观察值的值替换为极端值
features[0,0] = 10000
features[0,1] = 10000

# 创建识别器
outlier_detector = EllipticEnvelope(ccontamination=.1)

# 拟合识别器
outlier_detector.fit(features)

# 预测异常值
print(outlier_detector.predict(features))
[[-1.83198811  3.52863145]
 [-2.76017908  5.55121358]
 [-1.61734616  4.98930508]
 [-0.52579046  3.3065986 ]
 [ 0.08525186  3.64528297]
 [-0.79415228  2.10495117]
 [-1.34052081  4.15711949]
 [-1.98197711  4.02243551]
 [-2.18773166  3.33352125]
 [-0.19745197  2.34634916]]

[-1  1  1  1  1  1  1  1  1  1]

【小提示】:这个方法的主要限制是contamination(污染指数)参数的设定,它表示异常值在观察值中的比例,我们无法确定具体是多少,只能通过尝试来判断,如果你认为数据中的异常值很少,就设置小一点。

【拓展】:还有另外一种方法,基于IQR的识别:数据集第1个四分位数和第3个四分位数之差。

7.处理异常值

通常有三种方式:丢弃标记转换

import pandas as pd

# 创建数据帧
houses = pd.DataFrame()
houses['Price'] = [534433, 392132, 293332, 4322032]
houses['Bathrooms'] = [2, 3.5, 3, 116]
houses['Square_Feet'] = [1500, 2500, 1500, 48000]

# 方法一:直接丢弃异常值
print(houses[houses['Bathrooms'] < 20])
    Price  Bathrooms  Square_Feet
0  534433        2.0         1500
1  392132        3.5         2500
2  293332        3.0         1500
# 方法二:利用布尔条件,创建特征 ---> 标记为异常值
houses['Outlier'] = np.where(houses['Bathrooms'] < 20, 0, 1)

print(houses)
     Price  Bathrooms  Square_Feet  Outlier
0   534433        2.0         1500        0
1   392132        3.5         2500        0
2   293332        3.0         1500        0
3  4322032      116.0        48000        1
# 方法三:对特征值进行转换,以降低异常值的影响
houses['Log_Of_Square_Feet'] = [np.log(x) for x in houses['Square_Feet']]

print(houses)
     Price  Bathrooms  Square_Feet    Log_Of_Square_Feet
0   534433        2.0         1500              7.313220
1   392132        3.5         2500              7.824046
2   293332        3.0         1500              7.313220
3  4322032      116.0        48000             10.778956

【说明】:和识别异常值一样,处理异常值也不存在一个绝对的准则,但是应该基于个方面来考虑:(1)要弄清楚是什么让它们成为异常值?如果是因为它们是错误的观察值(如:传感器损坏导致记录错误数据),那么就要丢弃它们或者用NaN代替;但是如果你认为这些异常值就是极端值(如:一个大富豪家的宅子真的有116间卧室),那么把它们标记为异常值或对它们的值进行转换,更合理一些。(2)基于机器学习的目标来处理异常值(例如:我们想预测普通家庭的住宅房价,那么含有116间卧室的房子就不在我们的考虑范围内)。 另外,如果数据中存在异常值,那么就不建议采用标准化缩放,影响会比较大,这种情况使用RobustScaler()更合理。

8.将特征离散化

将一个数值型特征离散化,分到多个离散的小区间中。有两种方法:根据阈值将特征二值化Binarizer根据多个阈值将数值型特征离散化digitize

import numpy as np
from sklearn.preprocessing import Binarizer

# 创建特征
age = np.array([[6],
                [12],
                [20],
                [36],
                [65]])

# 方法一:创建二值化器: 阈值为18
binarizer = Binarizer(18)

# 转换特征
print(binarizer.fit_transform(age))
## print(np.digitize(age, bins=[18]))
[[0]
 [0]
 [1]
 [1]
 [1]]
# 方法二:小于20、大于20小于30、大于30小于64、大于64
print(np.digitize(age, bins=[20,30,64]))
[[0]
 [0]
 [1]
 [2]
 [3]]

【小提示】:bins参数是左闭右开,或者 利用right=True。

9.使用“聚类”将观察值分组

使相似的观察值被分为一组,下面是第一个聚类算法: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)

print(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

【提示】:这里不详细解释具体的步骤,后面会单独拿出一篇文章来讲解**“聚类”。这里使用类似K-Means的无监督学习**算法是将观察值聚类分成几组,最后得到一个分类特征,相似的观察值会被分到同一组。目前,只要心中有印象,在数据预处理中能使用聚类就行了。

10.删除带有缺失值的观察值

两种方法:NumPy利用isnan()、pandas利用dropna()

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]])

# 方法一:NumPy利用isnan()
print(features[~np.isnan(features).any(axis=1)])
[[ 1.1 11.1]
 [ 2.2 22.2]
 [ 3.3 33.3]
 [ 4.4 44.4]]
# 方法二:pandas利用dropna()
dataframe = pd.DataFrame(features, columns=['feature_1', 'feature_2'])

print(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。

11.填补缺失值

数据中的存在的缺失值,我们希望填充或者预测这些值,常见的方法有两种:KNN算法(预测缺失值)、sklearn中的Imputer模块(利用特征的平均值、中位数或者众数来填补缺失值)。

【说明】:KNN也是机器学习重要的算法,后面会单独讲。

Python
机器学习
  • 作者:李延松(联系作者)
  • 发表时间:2020-07-20 00:08
  • 版本声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 公众号转载:请在文末添加作者公众号二维码

评论

留言