数据预处理对于机器学习任务的成功至关重要,以下是机器学习技术进行数据预处理的步骤和方法
一.数据收集与了解
1.导入获取的原始数据
import pandas as pd
data=pd.read_csv('iris.csv')
2.对数据进行初步了解,数据特征,类型,结构,缺失值情况,异常值情况
数据查看:
查看行列: data.shape
查看数据的描述统计分析: data.describe(),可以查看到异常数据
获取前/后10行数据: data.head(10)、data.tail(10)
查看列标签: data.columns.tolist()
查看行索引: data.index
查看数据类型: data.dtypes
查看数据维度: data.ndim
查看数据详细信息: data.info(),可以查看是否有缺失值
输出:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 sepal_len 150 non-null float64
1 sepal_width 149 non-null float64
2 petal_len 149 non-null float64
3 petal_width 150 non-null float64
4 target 150 non-null int64
dtypes: float64(4), int64(1)
memory usage: 6.0 KB
可以得出:数据是DataFrame (表格型的)数据结构,且数据又150行sepal_width和petal_len有缺失值,target字段是整数型
二.缺失值处理
1.查看缺失值情况
# 找出有null值的字段
print(data.isnull().any())输出:
sepal_len False
sepal_width True
petal_len True
petal_width False
target False
dtype: bool
# 统计各个列有多少个空值
print(data.isnull().sum())
输出:
sepal_len 0
sepal_width 1
petal_len 1
petal_width 0
target 0
dtype: int64
可以得出:显示为true的字段有缺失值,统计结果表示sepal_width 和petal_len字段各有1个缺失值
2.对缺失值进行处理
(1)直接删除
最简单的方法是删除行或列(特性)。这通常是在缺失值的百分比非常大或缺失值对分析或结果没有显著影响时进行的。
删除缺少值的行
1.#直接删除缺失值存在的行
df_droprows = data.dropna()
print(df_droprows.isnull().sum())输出:
sepal_len 0
sepal_width 0
petal_len 0
petal_width 0
target 0
dtype: int64可以看出:数据已经不存在缺失值,此时看数据的整体情况
<class 'pandas.core.frame.DataFrame'>
Int64Index: 148 entries, 0 to 149
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 sepal_len 148 non-null float64
1 sepal_width 148 non-null float64
2 petal_len 148 non-null float64
3 petal_width 148 non-null float64
4 target 148 non-null int64
dtypes: float64(4), int64(1)
memory usage: 6.9 KB
数据变为148行
2.#删除列或特性:
df_dropcols = data.drop(columns=['sepal_width','petal_len'])
df_dropcols.isnull().sum()输出:
sepal_len 0
petal_width 0
target 0
dtype: int64
可以看出:通过删除行,我们最终得到一个更短的数据集。当删除特征时,我们最终会得到一个完整的数据集,但会丢失某些特征.所以一般情况下不建议使用。
(2)使用均值,中位数(数值型数据),众数,常量填充(Unknown等)
#1.用均值来填充缺失值:
df = data.fillna(data.mean())
#2.也可以是median:中位数,most_frequent:众数
#3.用常数填充空缺值
#用1填充"sepal_width"的空缺值,用0填充"petal_len"的空缺值
values={"sepal_width":1,"petal_len":0} df=data.fillna(value=values)
#4.可以用该列中的前一个或下一个值替换该列中的缺失值。
#data=data.fillna(method='ffill')#向前填充缺失值
#data=data.fillna(method='bfill')#向后填充缺失值
三.异常值处理
#查看数据的描述统计分析:
data.describe()
输出:
sepal_len sepal_width petal_len petal_width target
count 150.000000 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.060000 3.760000 1.199333 1.000000
std 0.828066 0.439799 1.762434 0.762238 0.819232
min 4.300000 2.000000 1.000000 0.100000 0.000000
25% 5.100000 2.800000 1.600000 0.300000 0.000000
50% 5.800000 3.000000 4.350000 1.300000 1.000000
75% 6.400000 3.300000 5.100000 1.800000 2.000000
max 7.900000 4.400000 6.900000 2.500000 2.000000
通过绘制箱线图观察异常值的分布情况,然后利用“天花板_地板法”进行处理
# 绘制箱型图
sns.boxplot(data=data, orient="h", palette="Set2")
plt.show()
#将离群值置为临界值:
def std_data(data):
#计算标准差
std = data['sepal_width'].std()
sw = []
for item in data['sepal_width']:
if item < 3*std or item > - 3*std:
sw.append(item)
if item > 3*std:
item = 3*std
sw.append(item)
else:
item = -3*std
new_sepal_width = pd.DataFrame(sw)
data['new_sepal_width'] = new_sepal_width
return data
std_data(data)
# 绘制箱型图
sns.boxplot(data=data, orient="h", palette="Set2")
plt.show()
#既然我们已经处理了异常值,那原先的列就可以删除了
new_data = data.drop('sepal_width', axis=1)
#空值处理
new_data.fillna(method='ffill', inplace=True)
for i in new_data.columns:
print(set(new_data[i]))
四.数据变换(归一化,标准化)
1.标准化Z-Score
公式:x-mean/std
特点:数据变成均值为0,标准差为1的分布;不改变原始数据的分布;缩放后的数据并非一定是正态分布
from sklearn.preprocessing import StandardScaler
transfer = StandardScaler()
data_standard=transfer.fit_transform(data)
2.归一化Max-Min
公式:x-min(x)/max(x)-min(x)
特点:缩放到0和1之间;目的是使各个特征维度对目标函数的影响权重是一致的;改变了原始数据的一个分布
from sklearn.preprocessing import MinMaxScaler
transfer=MinMaxScaler(feature_range=[0,1]) #范围可改变,若不写,默认为0-1
data_standard=transfer.fit_transform(data)
五.特征选择
对于一个真实的数据集来讲我们可能获取非常多的特征,但是特征并不是越多越好,有的特征可能压根就与实际的结果没有关系,特征数量过多对计算机的开销也会增多。通常来看,会从以下两个大的方面进行选择:
特征是否发散:如果一个特征不发散,比如方差接近0,那这个特征几乎是不变的,因此也就无法得知该特征对结果的影响。
特征与目标的相关性:与目标相关性高的特征,显然是要优先选择的,而与目标几乎不相关的特征则是需要放弃,相关性也是用得非常多的一种方式。
1.过滤法
过滤法的主要思想是使用某种方式对每个特征进行打分,即相当于给每个特征赋予一个权重,按照权重排序,对不达标的特征进行过滤。打分的方式有以下几种:
(1)方差选择法
方差选择法即根据每个特征的方差,设定好阈值,选择方差大于阈值的特征。
from sklearn.feature_selection import VarianceThreshold
VarianceThreshold(threshold=3).fit_transform(iris.data)
(2)相关系数选择法
相关系数选择法是根据相关系数的大小来判定两个变量之间的相关性的强弱,进而选择相关的特征。一般常用的是皮尔逊相关系数。皮尔逊相关系数计算比较方便,通常拿到特征时即可计算,但是其确定是只对线性关系敏感,对非线性关系比较哪怕关系非常强计算出来的结果也几乎为0。pearsonr(x,y)将返回x与y的相关系数和p-value值,p-value值是一种在原假设为真的前提下出现观察样本以及更极端情况的概率,是拒绝原假设的最小显著性水平
SelectKBest(lambda X, Y: array(list(map(lambda x:pearsonr(x, Y), X.T))).T[0], k=2).fit_transform(data, target)
(3)卡方检验
卡方检验就是统计样本的实际观测值与理论推断值之间的偏离程度,实际观测值与理论推断值之间的偏离程度就决定卡方值的大小,卡方值越大,越不符合;卡方值越小,偏差越小,越趋于符合,若两个值完全相等时,卡方值就为0,表明理论值完全符合。
可以使用sklearn.feature_selection中的SelectKBest来根据卡方检验选取特征。
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
SelectKBest(chi2,k = 2).fit_transform(data,target)
2.Weapper(包装法)
其主要思想是:将子集的选择看作是一个搜索寻优问题,生成不同的组合,对组合进行评价,再与其他的组合进行比较。这样就将子集的选择看作是一个是一个优化问题,利用优化算法进行求解。该方法根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征。即Weapper方法是一种搜索方式,将当前的特征组合作为待搜素的集合,从集合中找出最优的特征组合然后返回结果。
常用的有递归特征消除法,递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。
使用feature_selection库的RFE类来选择特征。
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
RFE(LogisticRegression(),n_features_to_select=2).fit_transform(data,target)
3.Embedded(嵌入法)
先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。因此,从根本上来说,Embedded方法是在选定模型的基础上,选择对模型最有利的特征。
(1)基于惩罚项
所谓惩罚,就是对损失函数中的某些参数作一些限制。机器学习中对损失函数的正则化过程也叫惩罚项,正则化产生的原因是为了防止过拟合。常见有L1正则化和L2正则化,因此此处的惩罚项也是以上两种。
L1是权重的绝对值之和,L2是权重的平方和。对有线性模型来说,使用L1正则化的模型叫做Lasso回归,可以产生一个稀疏权值矩阵,即产生稀疏模型可以用于特征选择;使用L2正则化的模型叫做Rideg回归(岭回归),偏向于权值偏向于正态分布且值较小,实现对模型空间的限制,可以防止模型过拟合,但是由于加入平方项,对异常值敏感,且对计算量并没有降低。
选择L1惩罚项来选择特征
利用feature_selection库中的SelectFromModel库
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
SelectFromModel(LogisticRegression(penalty='l1',C=0.1)).fit_transform(data,target)
选择L2惩罚项来选择特征
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
SelectFromModel(LogisticRegression(penalty='l2',C=0.1)).fit_transform(data,target)
(2)基于树模型
树模型也可以作为基模型进行特征选择,如随机森林和GBDT。
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier,RandomForestClassifier
SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data,iris.target)
SelectFromModel(RandomForestClassifier()).fit_transform(iris.data,iris.target)
六.特征构建
1.类别特征:表示某特征的固定类别
- 定类:只做单纯分类,不存在相对大小的特征。例如,当我们为性别编号为 男:1,女:2时,并不意味着其间存在大小的关系,而只是作为类别的区别。
- 定序:有相对大小的特征。例如,当我们对成绩编码时 90-100:A,90-60:B时,有些时候表示其存在一定的大小顺序
(1)类别编码能够将object的变量准换位数值类型,例如若在数据集中性别的编码为:男:MAN,女:WOMAN,则我们首先需要将其转换为数值类型。对一个属性为shape进行特征编码
from sklearn import preprocessing
LabelEncoder = preprocessing.LabelEncoder()
LabelEncoder.fit(data['shape'])
data['shape'] = LabelEncoder.transform(data['shape'])
但要注意,进行类别特征编码的后的结果通常不能够直接使用,因为这样会间接的引入顺序。当类别特征存在一定的大小顺序时,类别特征可以进行灵活运用。例如,某些群体的年纪分为幼年,青年,老年等。
(2)独立热编码:对每个类分别使用二进制编码,使用二进制创建新列,一表示特定行属于该类别,使用该方法会让我们的数据升维
hot = pd.get_dummies(data['shape'].values)
pd.concat([data,hot], axis=1)
(3)Frequncy编码
通过计算特征变量中每个值出现次数来表示该特征信息,通常情况下的问题使用此种方法提取特征都有助于帮助我们更好的构建模型。
data['shape_cnt'] = data['shape'].map(data['shape'].value_counts())
(4)Target编码
综合预测目标和存在的特征对特征进行构造
from category_encoders import TargetEncoder
encoder= TargetEncoder(cols=['power'])
training_set = encoder.fit_transform(train_data['power'],train_data['price'])
2.数值特征/定量特征:表示某特征的连续数值
定类:存在某些特定类别。
定序:存在一定顺序。
定距:存在固定距离。
定比:存在某些固定比值。
(1)二值化:在某些情况下,我们只关注特征的有无,并不关心特征的具体大小
train_data['power_on'] = (train_data['power'] > 0) * 1
(2)交互特征:对多个特征做组合,做差,积等,能够捕获特征间的交互作用。
例如,当我们需要评估一个人胖与瘦是不能够只关注其身高和体重,此时我们就可以根据现有的数据构造BMI指数的特征。
3.时间特征:表示某些特征的时间特征
根据我们的先验知识,我们可以对年月日进行拆解,并观察其相对于我们的预测目标的关联情况。
下面我们以某二手车数据为例,统计其创建时间与最终出售价格的周期性
# 转换时间格式
train_data['regDate'] = pd.to_datetime(train_data['regDate'], format='%Y%m%d', errors='coerce')
train_data['creatDate'] = pd.to_datetime(train_data['creatDate'], format='%Y%m%d', errors='coerce')
plt.bar(train_data['regDate'].dt.year.value_counts().index, train_data['regDate'].dt.year.value_counts().values)
七.数据集切分
from sklearn.model_selection import train_test_split x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.25)
八.数据集平衡(过采样,下采样,SMOTE)
过采样 通过增加分类中样本较少的类别的采样数量来实现平衡,最直接的方法是简单复制小样本数据,缺点是如果特征少,会导致过拟合的问题。经过改进的过抽样方法通过在少数类中加入随机噪声、干扰数据或通过一定规则产生新的合成样本。
from imblearn.over_sampling import SMOTE OverSample = SMOTE(random_state=0) train, target = OverSample.fit_resample(x_train, y_train)
欠采样 通过减少分类中多数类样本的数量来实现样本均衡,最直接的方法是随机去掉一些多数类样本来减小多数类的规模,缺点是会丢失多数类中的一些重要信息。
#对训练集欠采样
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler()
train, target = rus.fit_resample(x_train, y_train)
九.数据归档
为了方便后续的分析和模型训练,将处理后的数据保存为CSV等格式来实现
import pandas as pd
data.to_csv('data.cvx',index=False)
十.数据验证与评估(准确度,精确度,召回率)
准确率是指分类正确的样本占总样本个数的比例
精确率是指分类正确的正样本个数占分类器判定为正样本的样本个数的比例
召回率是指分类正确的正样本个数占真正的正样本个数的比例
ROC曲线的横坐标为假阳性率,纵坐标为真阳性率
#绘制roc曲线
from sklearn.metrics import roc_curve, auc, confusion_matrix
# 计算
fpr, tpr, thread = roc_curve(y_test, y_pred)
roc_auc = auc(fpr, tpr)
# 绘制ROC曲线
plt.figure()
lw = 2
plt.plot(fpr, tpr, color='darkorange',
lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()
ROC曲线示例:ROC曲线下的面积对应的就是AUC(Area under curve),该值能够量化地反映基于ROC曲线衡量出的模型性能。计算AUC值只需要沿着ROC横轴做积分求和就得到了。AUC值一般在0.5~1之间,值越大说明分类器越可能把真正的正样本排在前面,分类性能越好
F-1指标是分类问题的一个衡量指标,它是精确率和召回率的调和平均数。F1值的取值范围(0~1),越接近1说明模型预测效果越好
#F1指标
from sklearn.metrics import f1_score
f1 = f1_score(y_test, y_pred, average='micro')
print(f1)
#混淆矩阵
matrix = confusion_matrix(y_test, y_pred)
#画出混淆矩阵
dataframe = pd.DataFrame(matrix)
sns.heatmap(dataframe, annot=True, fmt='.20g',cbar=None, cmap="Blues")
plt.title("Confusion Matrix"), plt.tight_layout()
plt.ylabel("True Class"), plt.xlabel("Predicted Class")
plt.show()
混淆矩阵示例: