数据归一化-Normalization和标准化-Standardization
数据的归一化和标准化都是对数据做变换,指通过某种处理方法将待处理的数据限制在一定的范围内或者符合某种分布。
- 它们都是属于特征工程中的特征缩放过程。
- 特征缩放的目的是使得所有特征都在相似的范围内,因此在建模的时候每个特征都会变得相同重要。
- 一般在建模的过程中,大多数模型对数据都要求特征缩放,比如KNN、SVM、Kmeans等涉及到距离的模型,但是对决策树、随机森利等树模型是不需要进行特征缩放。
本文基于一份模拟的数据,介绍为什么及如何进行归一化和标准化:
- 线性归一化:通用的Normalization模式
- 均值归一化:Mean Normalization
- 标准化:Standardization(z-score)
模拟数据
In [1]:
1 | import pandas as pd |
In [2]:
1 | # 1、身高 |
Out[2]:
1 | array([188., 174., 180., 192., 189., 160., 180., 168., 169., 174., 171., |
In [3]:
1 | # 2、收入 |
Out[3]:
1 | array([195868, 186179, 111834, 182865, 162570, 173929, 156620, 183725, |
In [4]:
1 | df = pd.DataFrame({"height":height, |
将上面的数据字段转成整型int:
1 | df = df.astype(int) # 转成整型int |
模拟的两个字段数据,height的数值明显比salary小,二者不在一个数量级,即:量纲是不同的。查看数据的描述统计信息:
1 | # 描述统计信息 |
为什么需要归一化?
比如,当我们使用和距离相关的算法模型(KNN、k-means、SVM等)进行两个样本之间的欧式距离计算,此时salary的数值明显大于height的数值,求解的结果几乎取决于salary。
$$\sqrt{(188-624)^2 + (195868-186179)^2}$$
这样就会导致整个结果的值会过度依赖于salary。但是实际上,建模的过程中height和salary的重要性是一致的,因此在这种情况下,我们需要将两组数据的值缩放到相同的范围内,再进行计算和建模。
数据分布
身高height
In [7]:
1 | fig = px.violin(df,x="height") |
1 | plt.hist(df["height"], 30) |
1 | # 基于sns + y轴是频数 |
1 | # 基于sns + y轴是概率 |
收入salary
In [11]:
1 | fig = px.violin(df,x="salary") |
1 | plt.hist(df["salary"], 20) |
二者散点分布
基于两个字段的散点分布:
In [15]:
1 | fig = px.scatter(df,x="height",y="salary") |
二者密度分布
In [16]:
身高取值的密度分布:
1 | sns.distplot(df["height"], color="red",label="Height") |
1 | sns.distplot(df["salary"], color="blue", label="Salary") |
如果将两个字段的密度分布图放在一个画布中:
1 | sns.distplot(df["height"], color="red",label="Height") |
可以看到salary的密度值明显是小于height的密度。
标准化(中心标准化z-score)
中心标准化(Z-score normalization)的做法是将所有特征的数值被转化成为均值u为0、标准差std为1的正态分布。
要求原数据满足正态分布,实施变换后的数据也是满足正态分布的
$$X_{new} = \frac{X-mean(X)}{std(X)}$$
用sklearn的StandardScaler模块也能实现。
1、首先对身高height进行标准化操作:
In [19]:
下面的操作是针对副本:
1 | df1 = df.copy() # 副本 |
In [20]:
1 | mean_h = df1["height"].mean() |
Out[20]:
1 | 170.71 |
In [21]:
1 | std_h = np.std(df1["height"]) |
Out[21]:
1 | 10.218899157932816 |
我们在创建数据的时候,均值是170,方差是10。与求出来的相比较:基本是一致的。
In [22]:
1 | df1["height"] = df1["height"].apply(lambda x: (x - mean_h) / std_h) |
2、对收入salary进行同样的操作:
In [23]:
1 | mean_s = df1["salary"].mean() |
In [24]:
1 | df1["salary"] = df1["salary"].apply(lambda x: (x - mean_s) / std_s) |
再次查看两个字段的描述统计信息:经过标准化之后,均值为0,标准差为1.
绘制标准化后的密度分布图:
1 | sns.distplot(df1["height"], color="red") |
归一化
Max-Min:0-1之间
通过下面的公式进行转化:
$$X_{new} = \frac{X-X_{min}}{X_{max} - X_{min}}$$
对于每个特征,最小值被转化为0,最大值被转化为1。
In [27]:
1 | df2 = df.copy() # 副本 |
In [28]:
1 | df2["height"] = df2["height"].apply(lambda x: (x - min_h) / (max_h - min_h)) # 实施缩放 |
对salary实施相同的操作:
查看数据统计信息:
1 | sns.distplot(df2["height"], color="red",label="Height") |
ax-Abs:-1-1之间
通过下面的公式进行转换
In [32]:
1 | df3 = df.copy() |
In [33]:
1 | minmax_h = max(abs(df3["height"])) # 选择绝对值大者 |
Out[33]:
1 | 194 |
In [34]:
1 | df3["height"] = df3["height"].apply(lambda x: x / minmax_h) |
Out[34]:
height | salary | |
---|---|---|
0 | 0.969072 | 195868 |
1 | 0.896907 | 186179 |
2 | 0.927835 | 111834 |
3 | 0.989691 | 182865 |
4 | 0.974227 | 162570 |
In [35]:
1 | minmax_s = max(abs(df3["salary"])) # 选择绝对值大者 |
Out[35]:
height | salary | |
---|---|---|
0 | 0.969072 | 0.979462 |
1 | 0.896907 | 0.931011 |
2 | 0.927835 | 0.559240 |
3 | 0.989691 | 0.914439 |
4 | 0.974227 | 0.812952 |
In [36]:
1 | sns.distplot(df3["height"], color="red",label="Height") |
sklearn实现归一化和标准化
使用sklearn库也能够快速实现数据的归一化和标准化:
In [37]:
1 | from sklearn import preprocessing |
方法1:StandardScaler
In [38]:
1 | ss = preprocessing.StandardScaler() |
1 | ss_s = ss.fit_transform(df["salary"].values.reshape(-1,1)) |
方法2:MinMaxScaler
In [40]:
1 | mm = preprocessing.MinMaxScaler() |
方法3:MaxAbsScaler
In [42]:
1 | ma = preprocessing.MaxAbsScaler() |