用Python进行数据挖掘(数据预处理)
本博客进行数据预处理的方法总结自kaggle的几道题目:
1.HousePrices
2.Titanic
以及比较不错的几个kernels:
1.https://www.kaggle.com/pmarcelino/house-prices-advanced-regression-techniques/comprehensive-data-exploration-with-python/run/1432348
2.https://www.kaggle.com/sinakhorami/titanic-best-working-classifier
3.https://www.kaggle.com/apapiu/regularized-linear-models
4.https://www.kaggle.com/serigne/stacked-regressions-top-4-on-leaderboard
本博客只能稍微了解下数据预处理,入门都算不上,真正想要深入学习特征工程建议应该先刷完sklearn的数据预处理API文档:
http://scikit-learn.org/stable/modules/preprocessing.html#preprocessing
- 数据加载与粗略查看
- 处理丢失的数据
- 处理偏离值
- 数据统计
- 特征值的合并、连接
- 数据转换、标准化、归一化
数据加载与粗略查看
数据加载
一般训练与测试的数据都提供csv格式,使用pandas库读取:
df_train = pd.read_csv('../train.csv')
此时读取的df_train为DataFrame格式。
同时pandas还可以读取各种不同格式的数据,如存储比较快的hdf格式、excel等
但有时数据不是简单的csv,它按照文本保存,如“ID||texttexttexttext”这样的一条数据需要将中间的“||”当作分隔符,读取方式如下:
train = pd.read_csv('../input/training_text', sep="\|\|", engine='python', header=None, skiprows=1, names=["ID","Text"])
更多参数应该查阅pandas文档。
数据粗略查看
在pandas读进来数据一个train后,train的格式为DataFrame,调用下面的几个方法就可以大致了解我们得到的数据是什么,有什么特征值,特征值的数据类型是什么,如果是数值那么最大最小值是什么等:
train.head(5) #显示前5行数据
train.tail(5) #显示后5行
train.columns #查看列名
train.info() #查看各字段的信息
train.shape #查看数据集行列分布,几行几列
train.describe() #查看数据的大体情况
如train.describe():
处理丢失的数据
处理这些数据以前不建议把train与test 连接起来,因为这样容易造成test里数据的丢失,个人认为较好的方式为:
full_data = [train, test]
将两个数据集合成为list,然后清洗时对其for循环即可,如:
for dataset in full_data:
dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
下面只介绍对测试集train 的操作。
找到丢失的位置
输出每个列丢失值也即值为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’最少。
缺失值得处理
对缺失数据的处理我们有很多方法,如《机器学习实战》里提到:
- 使用可用特征的均值来填补缺失值;
- 使用特殊值来填补缺失值,如-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())
忽略
根据情况可以选择忽略这一特征的列和忽略出现缺失的那几行。
通常后者出现在缺失的行数比较少的情况下。
#去掉一列
train = train.drop(['Cabin'], axis = 1)
#去掉这个特征为空的行
#当然后面可以加上inplace=True表示直接就在内存中替换了不用再赋值个train_new,但是本人多次删除掉几个行,发现有问题时又需要重新建立已经分析好的train,很浪费时间,个人认为还是重新开辟一个比较好
train_new = train.drop(train[train['Embarked'].isnull()].index)
#返回已经去掉重复行的数据集
train.drop_duplicates()
处理偏离值
查找偏离值
将数据可视化(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));
可以发现,有四个点在非常偏离的位置,先讨论上面两个偏移位。
在下方无数点形成非常好的线性关系,那这偏移很大的两点是异常值吗?不是,虽然偏离位置很远,但是与下面无数点形成的线性关系还是拟合的。
再看看右下两个值,显然它们偏离得没有道理,删!
删除偏离值
train.sort_values(by = 'GrLivArea', ascending = False)[:2]
train= train.drop(train[train['Id'] == 1299].index)
train= train.drop(train[train['Id'] == 524].index)
保留偏离值
当然并不是所有的偏离值都需要删除,具体需要在分析之后选择处理方式。这里将偏离值保留下来并不是原封不动保留,而需要做标准化或归一化处理,具体的处理方式可查看最后一节数据转换、标准化、归一化。
数据统计
现在我们已经拿到了一份比较完整的数据,让我们开始对它做一个简单的统计。
统计包括数据的偏斜度、峰度、不同特征值的相关系数、协方差、均值等。
#统计某一列中各个元素值出现的次数
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()
热点图展示:
特征值的合并、连接
在合并连接之前,我们需要了解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)
#另一种合并方式,按列名字进行合并。
all_data = pd.concat((train.loc[:,'MSSubClass':'SaleCondition'], test.loc[:,'MSSubClass':'SaleCondition']))
数据转换、标准化、归一化
数值转换
#浮点型数值转换为整型
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转换,使之尽量符合正态分布。
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])
数据标准化和归一化(Standardization、Normalization)
标准化归一化概念不再赘述,实际使用时最主要的还是要了解什么时候需要标准化,什么时候用归一化,还需要清楚当前数据适合什么标准化方式等等。
在sklearn.preprocessing 介绍的标准化方式有:
1. preprocessing.scale()、preprocessing.StandardScaler(),使数据集呈现标准正态分布,即mean = 0,且标准差std = 1。
2. MinMaxScaler 、MaxAbsScaler,前者使数据集分布在[0,1],后者分布在[-1,1]。这种方式通常在(1) 特征的标准差较小 (2) 可以使稀疏数据集中的0值继续为0,这两种情况下使用。
3. preprocessing.QuantileTransformer(),将数据映射到[0,1]之间均匀分布,会破坏原数据之间的相关特性。
4. 归一化方式:preprocessing.normalize(),将样本缩放成单位向量,(1)需要使用二次方程,比如点积或者其他核方法计算样本对之间的相似性(2)常用于文本分类和内容聚类的向量空间模型的基础。