这篇是展示平时工作中如何做数据清理和预处理。
一般数据清理和预处理的流程是:
下面会拿热门的铁达尼号等数据做示范:
1.数据加载鱼粗略查看
在pandas读进来数据一个train后,train的格式为DataFrame,调用下面的几个方法就可以大致了解我们得到的数据是什么,有什么特征值,特征值的数据类型是什么,如果是数值那么最大最小值是什么等。
train.head(5) #显示前5行数据
train.tail(5) #显示后5行
train.columns #查看列名
train.info() #查看各字段的信息
train.shape #查看数据集行列分布,几行几列
train.describe() #查看数据的大体情况
2.处理丢失数据(缺失值)
2.1 找到丢失的位置
输出每个列丢失值也即值为NaN的数据和,并从多到少排序。
total = train.isnull().sum().sort_values(ascending=False)
print(total)
Cabin 687
Age 177
Embarked 2
Fare 0
Ticket 0
Parch 0
SibSp 0
Sex 0
Name 0
Pclass 0
Survived 0
PassengerId 0
也可以输出百分比:
percent =(train.isnull().sum()/train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data.head(20)
由此可以看到‘Cabin’的缺失数量最多,‘Embarked’最少。
2.2 缺失值处理
对缺失数据的处理我们有很多方法:
2.2.1 填补:
#使用出现次数最多的值填补
train['Embarked'] = train['Embarked'].fillna('S')
train.product_type[train.product_type.isnull()]=train.product_type.dropna().mode().values
#使用中位数填补
train['Fare'] = train['Fare'].fillna(train['Fare'].median())
使用平均数填补
train['Age'] = train['Age'].fillna(train['Age'].mean())
train['LotFrontage'].fillna(train['LotFrontage'].mean())
2.2.2 去除缺失值由于缺失数量太多
根据情况可以选择忽略这一特征的列和忽略出现缺失的那几行。
通常后者出现在缺失的行数比较少的情况下。
#去掉一列
train = train.drop(['Cabin'], axis = 1)
#去掉这个特征为空的行
#当然后面可以加上inplace=True表示直接就在内存中替换了
train_new = train.drop(train[train['Embarked'].isnull()].index)
2.3 处理离群点
2.3.1查找离群点
将数据可视化(python中的pyplot),观察异常值,引用HousePrice中的一个例子:
#bivariate analysis saleprice/grlivarea
var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
可以发现,有四个点在非常偏离的位置,先讨论上面两个偏移位。
在下方无数点形成非常好的线性关系,那这偏移很大的两点是异常值吗?不是,虽然偏离位置很远,但是与下面无数点形成的线性关系还是拟合的。
再看看右下两个值,显然它们偏离得没有道理,删!
2.3.2 离群点处理
删除离群点
train.sort_values(by = 'GrLivArea', ascending = False)[:2]
train= train.drop(train[train['Id'] == 1299].index)
train= train.drop(train[train['Id'] == 524].index)
保留偏离值
当然并不是所有的偏离值都需要删除,具体需要在分析之后选择处理方式。这里将偏离值保留下来并不是原封不动保留,而需要做标准化或归一化处理,具体的处理方式会在下篇特征处理说明。
除此之外,还有一种方法去除离群点。
我们还可以采用了另外一种简单有效的方法:
在原始数据上训练 xgboost,用得到的 xgb 模型输出特征的重要性,取最重要的前 20 个特征,统计每个样本在这 20 个特征上的缺失值个数,将缺失值个数大于 10 的样本作为离群点。
2.4 数据统计
#统计某一列中各个元素值出现的次数
train['MSSubClass'].value_counts()
#列出数据的偏斜度
train['MSSubClass'].skew()
#列出数据的峰度
train['MSSubClass'].kurt()
#计算两个列的相关度
train['LotFrontage'].corr(train['LotArea'])
#观察两个列的值的二维图
x = 'GrLivArea';y = 'SalePrice'
data = pd.concat([train[y], train[x]], axis=1)
data.plot.scatter(x=x, y=y, ylim=(0,800000));
#这里800000为y的最大值
#计算所有特征值每两个之间的相关系数,并作图表示。
corrmat = train.corr()#得到相关系数
f,ax = plt.subplots(figsize = (12,9))
sns.heatmap(corrmat, vmax = .8, square = True)#热点图
#取出相关性最大的前十个,做出热点图表示
k = 10
#number of variables for heatmap
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(train[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={'size': 10}, yticklabels=cols.values,xticklabels=cols.values)
plt.show()
#就可以查看到相关性有多大
2.5 特征值合并和连接
在合并连接之前,我们需要了解pandas.groupby这个分组方法,因为很多时候我们是在从几个特征值里挖掘一些值来当作新的特征值,这样子我们这个分组的方法就显得尤为重要了,如按照同一个用户进行分组来计算这个用户的行为次数当作新的特征值等等
#按照用户分组---------------------一个特征值
train.groupby('userid',as_index=False)
#按照用户与目的地分组---------------两个特征值
train.groupby(['userid','end_loc'],as_index=False)
#用户、起点、目的地-----------------三个特征值
train.groupby(['userid','start_loc','end_loc'],as_index=False)
#跟MSSubClass进行分组,并求分组后的平均值
train[['MSSubClass', 'LotFrontage']].groupby(['MSSubClass'], as_index=False).mean()
#选取特定的属性的某个值然后进行分类
train[train['date']=='2017-1-2'].groupby(['userid'],as_index=False)
#获得分组后,统计分组中'end_loc'的数量返回为一列由‘userid’和‘user_count’组成的新的DataFrame
user_count = train.groupby('userid',as_index=False)['end_loc'].agg({'user_count':'count'})
#将获得的新的DataFrame合并到train,更多的merge参数请查阅文档
train= pd.merge(train,user_count,on=['userid'],how='left')
user_eloc_count = train.groupby(['userid','end_loc'],as_index=False)['userid'].agg({'user_eloc_count':'count'})
train= pd.merge(train,user_eloc_count,on=['userid','end_loc'],how='left')
还有许多方法构造新特征,后面会有篇写这个特征处理。
#讲训练数据与测试数据连接起来,以便一起进行数据清洗。
#这里需要注意的是,如果没有后面的ignore_index=True,
那么index的值在连接后的这个新数据中是不连续的,
如果要按照index删除一行数据,可能会发现多删一条。
merge_data=pd.concat([train,test],ignore_index=True)
2.6 数据转换,标准化和归一化
2.6.1 数据转换
#浮点型数值转换为整型
train['Age']=train['Age'].astype(int)
#字符串的替换--映射
#将字符串数值化
train['MSZoning']=train['MSZoning'].map({'RL':1,'RM':2,'RR':3,}).astype(int)
train['Embarked'] = train['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)
#一般建议将map拿出来
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5}
train['Title'] = train['Title'].map(title_mapping)
train['Title'] = train['Title'].fillna(0)
#将字符串特征列中的内容分别提出来作为新的特征出现,表现为0、1。
train= pd.get_dummies(houseprice)
#将连续型特征值分块,每一块用数字标识
train.loc[ train['Fare'] <= 7.91, 'Fare'] = 0
train.loc[(train['Fare'] > 7.91) & (train['Fare'] <= 14.454), 'Fare'] = 1
train.loc[(train['Fare'] > 14.454) & (train['Fare'] <= 31), 'Fare'] = 2
train.loc[ train['Fare'] > 31, 'Fare'] = 3
train['Fare'] = train['Fare'].astype(int)
下面这个数值转换是将数值进行log计算,使分布的数值显常态
train['SalePrice'] = np.log(train['SalePrice'])
而有时这样的log不可行,就需要使用log(x+1)来 处理.
train["SalePrice"] = np.log1p(train["SalePrice"])
#将偏斜度大于0.75的数值列log转换,使之尽量符合正态分布。
numeric_feats = train[train.dtypes != 'object'].columns
skewed_feats =
train[numeric_feats].apply(lambda x: skew(x.dropna())) #compute skewness
skewed_feats = skewed_feats[skewed_feats > 0.75]
skewed_feats = skewed_feats.index
all_data[skewed_feats] = np.log1p(all_data[skewed_feats])
2.6.2 数据标准化和归一化(Standardization、Normalization)
标准化归一化会在后面说,实际使用时最主要的还是要了解什么时候需要标准化,什么时候用归一化,还需要清楚当前数据适合什么标准化方式等等。
在sklearn.preprocessing 介绍的标准化方式有:
2.7 去除常变量
常变量对于因变量来说都一样,没什么几乎大的变化,所以相关性不是很强。可以通过计算每个数值型特征的标准差,剔除部分变化很 小的特征。