数据中心化与标准化
举个🌰来说明
通过一个例子来说明什么是中心化与标准化,并且如何进行标准化与中心化。
1、Python包的加载与画图设置(不懂可以不看):
# 包的加载
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import palettable #python颜色库
# 用于显示中文
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
2、加载数据:
不懂代码的话可以理解为这里是读取一个表格数据。
# 这里做一个对矩阵进行标准化与中心化的对比,阐述其意义
state_data_0 = pd.read_csv('./state.csv')
state_data_0.set_index('Unnamed: 0', inplace=True)
state_data_0.index._name = 'state'
state_data = state_data_0.copy()
state_data
这个数据表是美国几个州的统计数据,每一行代表一个州,每一列分别是人口(Population)、收入(Income)、受教育程度(Illiteracy)、平均寿命(Life Exp)等等。
3、使用表格数据画图:
plt.figure(dpi=120) # 在进行标准化与中心化处理之前绘制热图,由于不同列的数据差距过大,在绘制热图的时候就会导致反应出来的效果不好(例如:某一列的数据都在100~900,而另一列的数据都在10~99)
# sns.heatmap(state_data)
sns.heatmap(data=state_data,
cmap=sns.cubehelix_palette(as_cmap=True),
cbar=True,
cbar_kws={'label': 'ColorBar', #color bar的名称
'orientation': 'vertical', #color bar的方向设置,默认为'vertical',可水平显示'horizontal'
# "ticks": np.arange(4.5, 8, 0.5), #color bar中刻度值范围和间隔
# "format": "%.2f", #格式化输出color bar中刻度值
"pad": 0.05, #color bar与热图之间距离,距离变大热图会被压缩
},
# annot=True, #默认为False,当为True时,在每个格子写入data中数据
# fmt=".2f",#设置每个格子中数据的格式,参考之前的文章,此处保留两位小数
# annot_kws={'size':4,'weight':'normal', 'color':'blue'},#设置格子中数据的大小、粗细、颜色
linewidths=1, #格子与格子,默认为0
# linecolor='red', #每个格子边框颜色,默认为白色
)
plt.title('未经过中心化与标准化的数据')
4、遇到了问题:
如上图所示,使用表格数据绘制了一张热图,但是发现由于表格中的数据相差过大(Population列都在千、万级别,Forst都在10~1000级别)导致绘制出来的图并不能很好地表达出来一定的区分度。
这个时候就在想有没有一种方法能够解决这个问题呢?——数据的标准化与中心化,也被称为均值方差归一化(Standardization),把所有数据归一到均值为0方差为1的分布中,适用于数据分布没有明显边界的情况中。
标准化与中心化其实就是对一组数据求平均值和方差,然后计算:
$$ 处理后的结果=\frac{(源数据-源数据平均值)}{源数据的方差} $$
Python代码实现:
def norm_(pd_raw):
"""
定义一个可以对DataFrame进行中心化与标准化的函数
:param x: DataFrame
:return: 经过标准化的DataFrame
"""
pd_mean = np.mean(pd_raw, 0) # 求DataFrame每一列的平均值
pd_std = np.std(pd_raw, 0) # 求DataFrame每一列的标准差
return (pd_raw - pd_mean) / pd_std
上面这个函数就是定义一个可以用于将数据(Python中的DataFrame对象)进行标准化与中心化的函数,不懂代码的话可以理解为这一步就是如何将数据进行标准化与中心化。
5、对源数据进行标准化与中心化,然后重新出图:
state_data_norm = norm_(state_data) # 这里对数据进行标准化与中心化处理,处理后各个列的数据都向中间靠拢
sns.heatmap(data=state_data_norm,
cmap=sns.cubehelix_palette(as_cmap=True),
cbar=True,
cbar_kws={'label': 'ColorBar', #color bar的名称
'orientation': 'vertical', #color bar的方向设置,默认为'vertical',可水平显示'horizontal'
# "ticks": np.arange(4.5, 8, 0.5), #color bar中刻度值范围和间隔
# "format": "%.2f", #格式化输出color bar中刻度值
"pad": 0.05, #color bar与热图之间距离,距离变大热图会被压缩
},
# annot=True, #默认为False,当为True时,在每个格子写入data中数据
# fmt=".2f",#设置每个格子中数据的格式,参考之前的文章,此处保留两位小数
# annot_kws={'size':4,'weight':'normal', 'color':'blue'},#设置格子中数据的大小、粗细、颜色
linewidths=1, #格子与格子,默认为0
# linecolor='red', #每个格子边框颜色,默认为白色
)
plt.title('经过中心化与标准化的数据')
可以看到,经过中心化与标准化后,可以在热图上很好地对级别相差较大的数据进行区分。
这就是数据中心化与标准化的流程。
最值归一化
在有些文章中还会提到一个概念叫做最值归一化(Normalization),所谓最值归一化就是把一组数据映射到0~1
之间:
$$ x_{scale} = \frac{x-x{min}}{x_{max} - x_{min}} $$
这个方法适用于分布有明显边界的情况,且受outlier的影响较大。
scikit-learn中的封装方法
MinMaxScaler
在sklearn.preprocessing.MinMaxScaler中封装着最值归一化的方法,具体的使用方法可以参考官网链接。
StandardScaler
均值方差归一化的调用对象为sklearn.preprocessing.StandardScaler
,下面是一个举例:
# Import package
from sklearn.preprocessing import StandardScaler
# Create object
scale_scaler = StandardScaler()
# Input train data set
scale_scaler.fit(x_train)
# Get mean and std of train data set
scale_scaler.mean_, scale_scaler.scale_
# Get standard train data and test data
x_train_std = scale_scaler.transform(x_train)
x_test_std = scale_scaler.transform(x_test)
注意 / WARNING
这里有一个值得注意的问题:在进行机器学习的过程中,将特征矩阵
x
分为训练集x_train
和测试集x_test
后,需要分别进行归一化操作。但是切记,这里并不是分别求出x_train
和x_test
的均值mean
和方差std
,然后再分别进行归一化;而是对x_test
进行归一化也要采用x_train
的均值和方差。还有一个非常重要的点是,如果仅对
x_train
进行了归一化,而x_test
却没有归一化,那么机器学习预测结果将会非常非常差😡😡😡!!!别问我怎么知道的!都是泪!
DIY一个
import numpy as np
class StandardScalerDiy:
"""
Do it yourself: sklearn.preprocessing.StandardScaler
"""
def __init__(self):
self.mean_ = None
self.scale_ = None
def fit(self, x):
assert x.ndim == 2, 'The dimension of x must be two.'
self.mean_ = np.mean(x, axis=0)
self.scale_ = np.std(x, axis=0)
return self
def transform(self, x):
assert x.ndim == 2, 'The dimension of x must be two.'
assert x.shape[1] == len(self.mean_), 'x must have same number of column with self.mean_.'
return (x - self.mean_) / self.scale_