其他分享
首页 > 其他分享> > 【skLearn 数据预处理和特征工程】数据预处理

【skLearn 数据预处理和特征工程】数据预处理

作者:互联网


文章目录


skLearn中的数据预处理和特征工程

数据挖掘的五大流程
1. 获取数据
2. 数据预处理
    数据预处理是从数据中检测、纠正或删除损坏,不准确或不适用于模型的记录的过程。可能面对的问题有:数据类型不同,比如有的是文字,有的是数字,有的含时间序列,有的连续,有的间断。也可能,数据的质量不行,有噪声,有异常,有缺失,数据出错,量纲不一,有重复,数据是偏态,数据量太大或太小。
    数据预处理的目的:让数据适应模型,匹配模型的需求
3. 特征工程
    特征工程是将原始数据转换为更能代表预测模型的潜在问题的特征的过程。可以通过挑选最相关的特征,提取特征以及创造特征来实现。其中创造特征又经常以降维算法的方式实现。可能面对的问题有:特征之间有相关性特征和标签无关特征太多或太小,或者干脆就无法表现出应有的数据现象无法展示数据的真实面貌
    特征工程的目的:
           1) 降低计算成本
           2) 提升模型上限
4. 建模 ---- 测试模型并预测出结果
5. 上线 ---- 验证模型效果

sklearn中包含众多数据预处理和特征工程相关的模块,虽然刚接触sklearn时,大家都会为其中包含的各种算法的广度深度所震惊,但其实sklearn六大板块中有两块都是关于数据预处理和特征工程的,两个板块互相交互,建模之前的全部工程打下基础。

在这里插入图片描述

返回顶部


♑ 数据预处理 Preprocessing & Impute

① 数据无量纲化

在机器学习算法实践中,我们往往有着将不同规格的数据转换到同一规格,或不同分布的数据转换到某个特定分布的需求,这种需求统称为将数据“无量纲化”。譬如梯度和矩阵为核心的算法中,譬如逻辑回归,支持向量机,神经网络,无量纲化可以加快求解速度;而在距离类模型,譬如K近邻,K-Means聚类中,无量纲化可以帮我们提升模型精度,避免某一个取值范围特别大的特征对距离计算造成影响。(一个特例是决策树和树的集成算法们,对决策树我们不需要无量纲化,决策树可以把任意数据都处理得很好。)

数据的无量纲化可以是线性的,也可以是非线性的线性的无量纲化包括中心化(Zero-centered或者Meansubtraction)处理和缩放处理(Scale)


• preprocessing.MinMaxScaler

当数据(x)按照最小值中心化后,再按极差(最大值 - 最小值)缩放,数据移动了最小值个单位,并且会被收敛到[0,1]之间,而这个过程,就叫做数据归一化(Normalization,又称Min-Max Scaling)。注意,Normalization是归一化,不是正则化,真正的正则化是regularization,不是数据预处理的一种手段。归一化之后的数据服从正态分布,公式如下:

在这里插入图片描述
在sklearn当中,我们使用preprocessing.MinMaxScaler来实现这个功能。MinMaxScaler有一个重要参数,feature_range:控制我们希望把数据压缩到的范围,默认是[0,1]。

# 导包
from sklearn.preprocessing import MinMaxScaler

# 构建数据集
data = [[-1,2],[-0.5,6],[0,10],[1,18]]

# 实现归一化
scaler = MinMaxScaler() # 实例化
scaler = scaler.fit(data) # fit,在这里本质是生成min(x)和max(x)
result = scaler.transform(data) # 通过接口导出结果
print("归一化的结果是:\n",result) # 通过结果可以看出两列数据的分布大致一致

归一化的结果是:
 [[0.   0.  ]
 [0.25 0.25]
 [0.5  0.5 ]
 [1.   1.  ]]
# fit()+transform() => fit_transform()
result_ = scaler.fit_transform(data)
print("归一化的结果是:\n",result_)

归一化的结果是:
 [[0.   0.  ]
 [0.25 0.25]
 [0.5  0.5 ]
 [1.   1.  ]]
# 将归一化的结果进行逆转 --- inverse_transform(归一化结果)
scaler.inverse_transform(result)

array([[-1. ,  2. ],
       [-0.5,  6. ],
       [ 0. , 10. ],
       [ 1. , 18. ]])
# 实现数据归一化至其他范围的值
data = [[-1,2],[-0.5,6],[0,10],[1,18]]
scaler = MinMaxScaler(feature_range=[5,10]) # 实例化并将数据归结到5-10
result = scaler.fit_transform(data)
print("重新设置范围后的结果:\n",result)

重新设置范围后的结果:
 [[ 5.    5.  ]
 [ 6.25  6.25]
 [ 7.5   7.5 ]
 [10.   10.  ]]
import pandas as pd
# 归一化处理
data = pd.DataFrame(data)
data_nor = (data - data.min())/(data.max()-data.min())
data_nor

归一化结果:
       0	   1
0	0.00	0.00
1	0.25	0.25
2	0.50	0.50
3	1.00	1.00

# 归一化逆转
x_returned = data_nor*(data.max()-data.min()) + data.min()
x_returned

归一化逆转结果:
       0	  1
0	-1.0	2.0
1	-0.5	6.0
2	 0.0   10.0
3	 1.0   18.0

注意:
当data中的特征数量非常多的时候,fit会报错并表示 — 数据量太大了计算机无法计算
此时使用partial_fit()作为训练接口 scaler = scaler.partial_fit(data)

返回顶部


• preprocessing.StandardScaler

当数据(x)按均值(μ)中心化后,再按标准差(σ)缩放,数据就会服从为均值为0,方差为1的正态分布(即标准正态分布),而这个过程,就叫做数据标准化(Standardization,又称Z-score normalization),公式如下:
在这里插入图片描述

# 1.导包
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler

# 2.构建数据集
data = [[-1,2],[-0.5,6],[0,10],[1,18]]

scaler = StandardScaler() # 实例化
scaler = scaler.fit(data) # 本质是生成均值和方差
print("均值是:\n",scaler.mean_)
print("方差是:\n",scaler.var_)

均值是:
 [-0.125  9.   ]
方差是:
 [ 0.546875 35.      ]

# 3.获取标准化结果
result = scaler.transform(data)
print("查看标准化后的数据集:\n",result)
print("查看标准化后的数据的方差:\n",result.var())
print("查看标准化后的数据的均值:\n",result.mean())

查看标准化后的数据集:
 [[-1.18321596 -1.18321596]
 [-0.50709255 -0.50709255]
 [ 0.16903085  0.16903085]
 [ 1.52127766  1.52127766]]
查看标准化后的数据的方差:
 1.0
查看标准化后的数据的均值:
 0.0

# 4.实现数据集逆转
data_returned = scaler.inverse_transform(result)
data_returned

逆转的结果:
array([[-1. ,  2. ],
       [-0.5,  6. ],
       [ 0. , 10. ],
       [ 1. , 18. ]])

对于StandardScalerMinMaxScaler来说,空值NaN会被当做是缺失值,在fit()的时候忽略,在transform()的时候保持缺失值NaN的状态显示。并且,尽管去量纲化过程不是具体的算法,但在fit()接口中,依然只允许导入至少二维数组,一维数组导入会报错。通常来说,我们输入的X会是我们的特征矩阵,现实案例中特征矩阵不太可能是一维所以不会存在这个问题。

返回顶部


② 缺失值处理

对于实际收集数据的人却不是如此,因此数据挖掘之中,常常会有重要的字段缺失值很多,但又不能舍弃字段的情况。因此,数据预处理中非常重要的一项就是处理缺失值。在这里,我们使用从泰坦尼克号提取出来的数据,这个数据有三个特征,一个数值型,两个字符型,标签也是字符型。

• impute.SimpleImputer

class sklearn.impute.SimpleImputer (missing_values=nan, strategy=’mean’, fill_value=None,
                                    verbose=0,copy=True)

在随机森林的案例时,我们用这个类和随机森林回归填补了缺失值,对比了不同的缺失值填补方式对数据的影响。这个类是专门用来填补缺失值的。它包括四个重要参数:

参数含义&输入
missing_values告诉SimpleImputer,数据中的缺失值长什么样,默认空值np.nan
strategy我们填补缺失值的策略,默认均值。
输入“mean”使用均值填补(仅对数值型特征可用)
输入“median"用中值填补(仅对数值型特征可用)
输入"most_frequent”用众数填补(对数值型和字符型特征都可用)
输入“constant"表示请参考参数“fill_value"中的值(对数值型和字符型特征都可用)
fill_value当参数startegy”constant"的时候可用,可输入字符串或数字表示要填充的值,常用0
copy默认为True,将创建特征矩阵的副本,反之则会将缺失值填补到原本的特征矩阵中去。
# 导包
import numpy as np
import pandas as pd
from sklearn.impute import SimpleImputer

# 读取数据集
data = pd.read_csv(r"G:\Projects\Jupyter notebook\机器学习skLearn\data\Narrativedata.csv"
                  ,index_col=0)
data.head(20)
data.info() # 查看整体数据集信息 --- 检测缺失值

<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 0 to 890
Data columns (total 4 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Age       714 non-null    float64
 1   Sex       891 non-null    object 
 2   Embarked  889 non-null    object 
 3   Survived  891 non-null    object 
dtypes: float64(1), object(3)
memory usage: 34.8+ KB
# 提取数据集,并进行升维处理
age = data.loc[:,'Age'].values.reshape(-1,1)
age

array([[22.  ],
       [38.  ],
       [26.  ],
       [35.  ],
       [35.  ],
       [  nan],
       [54.  ],
       .........
       [28.  ],
       [25.  ],
       [39.  ],
       [27.  ],
       [19.  ],
       [  nan],
       [26.  ],
       [32.  ]])

使用三种策略进行填补:均值中位数0值

# 实例化
imp_mean = SimpleImputer() # 默认采用均值填补 
imp_median = SimpleImputer(strategy='median') # 用中位数填补
imp_0 = SimpleImputer(strategy='constant',fill_value=0) # 用0值填补

# 训练、获取结果
imp_mean = imp_mean.fit_transform(age)
imp_median = imp_median.fit_transform(age)
imp_0 = imp_0.fit_transform(age)

print("均值填补的结果:\n",imp_mean[:10])
print("中位数填补的结果:\n",imp_median[:10])
print("o值填补的结果:\n",imp_0[:10])

均值填补的结果:
 [[22.        ]
 [38.        ]
 [26.        ]
 [35.        ]
 [35.        ]
 [29.69911765] # 均值填补
 [54.        ]
 [ 2.        ]
 [27.        ]
 [14.        ]]
中位数填补的结果:
 [[22.]
 [38.]
 [26.]
 [35.]
 [35.]
 [28.] # 中位数填补
 [54.]
 [ 2.]
 [27.]
 [14.]]
o值填补的结果:
 [[22.]
 [38.]
 [26.]
 [35.]
 [35.]
 [ 0.] # 0值填补
 [54.]
 [ 2.]
 [27.]
 [14.]]
# 使用中位数填补Age
Age = data.loc[:,'Age'].values.reshape(-1,1)
imp_mode = SimpleImputer(strategy="median")
data.loc[:,'Age'] = imp_mode.fit_transform(Age)
data.info()

中位数填补结果:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 0 to 890
Data columns (total 4 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Age       891 non-null    float64 # 记录不为空值数补全
 1   Sex       891 non-null    object 
 2   Embarked  889 non-null    object 
 3   Survived  891 non-null    object 
dtypes: float64(1), object(3)
memory usage: 34.8+ KB
# 使用众数填补Embarked
Embarked = data.loc[:,'Embarked'].values.reshape(-1,1)
imp_mode = SimpleImputer(strategy="most_frequent")
data.loc[:,'Embarked'] = imp_mode.fit_transform(Embarked)
data.info()

众数填补结果:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 0 to 890
Data columns (total 4 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Age       891 non-null    float64
 1   Sex       891 non-null    object 
 2   Embarked  891 non-null    object 
 3   Survived  891 non-null    object 
dtypes: float64(1), object(3)
memory usage: 34.8+ KB

返回顶部


③ 处理分类型特征

在机器学习中,大多数算法,譬如逻辑回归,支持向量机SVM,k近邻算法等都只能够处理数值型数据,不能处理文字;在sklearn当中,除了专用来处理文字的算法,其他算法在fit()的时候全部要求输入数组或矩阵,也不能够导入文字型数据(其实手写决策树和普斯贝叶斯可以处理文字,但是sklearn中规定必须导入数值型)。然而在现实中,许多标签和特征在数据收集完毕的时候,都不是以数字来表现的。比如说,学历的取值可以是[“小学”,“初中”,“高中”,“大学”],付费方式可能包含[“支付宝”,“现金”,“微信”]等等。在这种情况下,为了让数据适应算法和库,我们必须将数据进行编码,即是说,将文字型数据转换为数值型

在这里插入图片描述
数据类型以及常用统计量:

数据类型数据名称数学含义描述举例可用操作
离散,
定性
名义=,!=名义变量就是不同的名字,是用来告诉我们:
这两个数据是否相同的
邮编,性别,眼睛的
颜色,职工号
众数,
信息熵
情形分析表或
列联表,
相关性分析,
卡方检验
离散,
定性
有序< ,>有序变量为数据的相对大小提供信息,告诉
我们数据的顺序,但数据之间大小的间隔不
是具有固定意义的,因此有序变量不能加减
材料的硬度,学历中位数,
分位数,
非参数相关分析(等级相关),
测量系统分析,
符号检验
连续,
定量
有距+,-有距变量之间的间隔是有固定意义的,可以
加减,比如,一单位量纲
日期,以摄氏度或
华氏度为量纲的温度
均值,标准差,
皮尔逊相关系数,
t和F检验
连续,
定量
比率*,/比变量之间的间隔和比例本身都是有意义的,
既可以加减又可以乘除
以开尔文为量纲
的温度,货币数量,计数,
年龄,质量,长度,电流
百分数,
变化量,
几何平均,
调和平均

编码

• preprocessing.LabelEncoder
# 导包
import numpy as np
import pandas as pd
from sklearn.impute import SimpleImputer

# 读取数据集
data = pd.read_csv(r"G:\Projects\Jupyter notebook\机器学习skLearn\data\Narrativedata.csv"
                  ,index_col=0)

from sklearn.preprocessing import LabelEncoder
y = data.iloc[:,-1] # 提取标签列
y:
0           No
1          Yes
2          Yes
3          Yes
4           No
        ...   
886         No
887        Yes
888         No
889    Unknown
890         No
Name: Survived, Length: 891, dtype: object

le = LabelEncoder() # 实例化
le = le.fit(y)      # 导入数据
label = le.transform(y) # 调取结果
label:
array([0, 2, 2, 2, 0, 0, 0, 0, 2, 2, 1, 2, 0, 0, 0, 1, 0, 2, 0, 2, 1, 2,
       2, 2, 0, 1, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1,
       ................................................................
       2, 0, 0, 0, 2, 0, 1, 1, 1, 0, 0, 2, 0, 1, 0, 0, 2, 2, 0, 0, 0, 2,
       2, 0, 0, 1, 0, 0, 0, 2, 0, 1, 0])


# 查看标签中的类别
le.classes_
array(['No', 'Unknown', 'Yes'], dtype=object)

# 逆转标签
le.inverse_transform(label) # 还原标签为字符型

实际应用:

from sklearn.preprocessing import LabelEncoder
data.iloc[:,-1] = LabelEncoder().fit_transform(data.iloc[:,-1])

返回顶部


• preprocessing.OrdinalEncoder
from sklearn.preprocessing import OrdinalEncoder

data_ = data.copy()
categories = OrdinalEncoder().fit(data_.iloc[:,1:-1]).categories
categories

直接运行的时候报错了:ValueError: Input contains NaN,因为我重新读取了数据,没有先进行缺失值处理,同时也间接说明了对于特征的转化之前必须进行数据预处理。

from sklearn.preprocessing import OrdinalEncoder

# 拷贝数据
data_ = data.copy()

# 数据预处理
from sklearn.impute import SimpleImputer
Age = data_.loc[:,'Age'].values.reshape(-1,1)
Embarked = data_.loc[:,'Embarked'].values.reshape(-1,1)
data_.loc[:,'Age'] = SimpleImputer(strategy='median').fit_transform(Age) # 中位数填补年龄
data_.loc[:,'Embarked'] = SimpleImputer(strategy='most_frequent').fit_transform(Embarked) #众数填补舱门

categories = OrdinalEncoder().fit(data_.iloc[:,1:-1]).categories_
print("特征转化的类别有:\n",categories)

# 一步转化
data_.iloc[:,1:-1] = OrdinalEncoder().fit_transform(data_.iloc[:,1:-1])
print("转化后的数据前5行是:\n",data_.head())

特征转化的类别有:
 [array(['female', 'male'], dtype=object), array(['C', 'Q', 'S'], dtype=object)]
转化后的数据前5行是:
     Age  Sex  Embarked  Survived
0  22.0  1.0       2.0         0
1  38.0  0.0       0.0         2
2  26.0  0.0       2.0         2
3  35.0  0.0       2.0         2
4  35.0  1.0       2.0         0

可以看到,经过preprocessing.OrdinalEncoder的处理后,分类特征也转化成了数值类型数据。

返回顶部


哑变量

preprocessing.OneHotEncoder

preprocessing.OneHotEncoder是指独热编码,创建哑变量

我们刚才已经用OrdinalEncoder把分类变量Sex和Embarked都转换成数字对应的类别了。在舱门Embarked这一列中,我们使用[0,1,2]代表了三个不同的舱门,然而这种转换是正确的吗?
我们来思考三种不同性质的分类数据:

然而在对特征进行编码的时候,这三种分类数据都会被我们转换为[0,1,2],这三个数字在算法看来,是连续且可以计算的,这三个数字相互不等,有大小,并且有着可以相加相乘的联系。所以算法会把舱门,学历这样的分类特征,都误会成是体重这样的分类特征。这是说,我们把分类转换成数字的时候,忽略了数字中自带的数学性质,所以给算法传达了一些不准确的信息,而这会影响我们的建模

类别OrdinalEncoder可以用来处理有序变量但对于名义变量,我们只有使用哑变量的方式来处理,才能够尽量向算法传达最准确的信息
在这里插入图片描述
这样的变化,让算法能够彻底领悟,原来三个取值是没有可计算性质的,是“有你就没有我”的不等概念。在我们的数据中,性别舱门都是这样的名义变量。因此我们需要使用独热编码,将两个特征都转换为哑变量。

# 数据预处理
from sklearn.impute import SimpleImputer
Age = data.loc[:,'Age'].values.reshape(-1,1)
Embarked = data.loc[:,'Embarked'].values.reshape(-1,1)
data.loc[:,'Age'] = SimpleImputer(strategy='median').fit_transform(Age)
data.loc[:,'Embarked'] = SimpleImputer(strategy='most_frequent').fit_transform(Embarked)

# 使用独热编码转化数据
from sklearn.preprocessing import OneHotEncoder
x = data.iloc[:,1:-1]

ohe = OneHotEncoder(categories='auto').fit(x)
result = ohe.transform(x).toarray()
result
array([[0., 1., 0., 0., 1.],
       [1., 0., 1., 0., 0.],
       [1., 0., 0., 0., 1.],
       ...,
       [1., 0., 0., 0., 1.],
       [0., 1., 1., 0., 0.],
       [0., 1., 0., 1., 0.]])
# 获取稀疏矩阵特征类别名称
ohe.get_feature_names()

array(['x0_female', 'x0_male', 'x1_C', 'x1_Q', 'x1_S'], dtype=object)
# 合并数据
newdata = pd.concat([data,pd.DataFrame(result)],axis=1)
newdata.drop(['Sex','Embarked'],axis=1,inplace=True)
newdata.columns = ["Age","Survived","Female","Male","Embarked_C","Embarked_Q","Embarked_S"]
newdata.head()

在这里插入图片描述

返回顶部


④ 处理连续型特征

二值化

• sklearn.preprocessing.Binarizer

根据阈值将数据二值化将特征值设置为0或1),用于处理连续型变量。大于阈值的值映射为1,而小于或等于阈值的值映射为0。默认阈值为0时,特征中所有的正值都映射到1。二值化是对文本计数数据的常见操作,分析人员可以决定仅考虑某种现象的存在与否。它还可以用作考虑布尔随机变量的估计器的预处理步骤(例如,使用贝叶斯设置中的伯努利分布建模)

# 将年龄二值化处理
from sklearn.preprocessing import Binarizer

data_age = data.copy()
x = data_age.iloc[:,0].values.reshape(-1,1)
transformer = Binarizer(threshold=30).fit_transform(x) # threshold 用来设定二值化的分界点
transformer 

array([[0.],
       [1.],
       [0.],
       [1.],
       [1.],
       [0.],
       [1.],
       [0.],
       [0.],
       ....
       [0.],
       [0.],
       [0.],
       [1.]])

返回顶部


分段(分箱)

• preprocessing.KBinsDiscretizer

这是将连续型变量划分为分类变量的类,能够将连续型变量排序按顺序分箱编码。总共包含三个重要参数:

参数含义&输入
n_bins每个特征中分箱的个数,默认5,一次会被运用到所有导入的特征
encode编码的方式,默认“onehot
"onehot":做哑变量,之后返回一个稀疏矩阵,每一列是一个特征中的一个类别,含有该类别的样本表示为1,不含的表示为0
ordinal”:每个特征的每个箱都被编码为一个整数,返回每一列是一个特征,每个特征下含有不同整数编码的箱的矩阵
"onehot-dense":做哑变量,之后返回一个密集数组。
strategy用来定义箱宽的方式,默认"quantile"
"uniform":表示等宽分箱,即每个特征中的每个箱的最大值之间的差为(特征.max() - 特征.min())/(n_bins)
"quantile":表示等位分箱,即每个特征中的每个箱内的样本数量都相同
"kmeans":表示按聚类分箱,每个箱中的值到最近的一维k均值聚类的簇心得距离都相同
# 分箱 --- 将连续型数据划分为类别
from sklearn.preprocessing import KBinsDiscretizer

x = data.iloc[:,0].values.reshape(-1,1)
# 分三箱,采用ordinal编码方式,每箱为一个编码,并且使用等宽分箱
kbd = KBinsDiscretizer(n_bins=3,encode='ordinal',strategy='uniform').fit_transform(x)

# 检测分箱结果
set(kbd.ravel())
{0.0, 1.0, 2.0}
# 分三箱,采用onehot独热编码方式,并且使用等宽分箱
est = KBinsDiscretizer(n_bins=3, encode='onehot', strategy='uniform')
#查看转换后分的箱:变成了哑变量
est.fit_transform(x).toarray()

array([[1., 0., 0.],
       [0., 1., 0.],
       [1., 0., 0.],
       ...,
       [0., 1., 0.],
       [1., 0., 0.],
       [0., 1., 0.]])

返回顶部


标签:fit,特征,数据,transform,preprocessing,data,预处理,skLearn
来源: https://blog.csdn.net/qq_45797116/article/details/113809635