数据清洗及特征处理
导入numpy、pandas包和数据
#加载所需的库
import numpy as np
import pandas as pd
#加载数据train.csv
df = pd.read_csv('train.csv')
df.head(3)
数据清洗概述
我们拿到的数据通常是不干净的,所谓的不干净,就是数据中有缺失值和异常点等,需要经过一定的处理才能继续做后面的分析或建模,所以拿到数据的第一步是进行数据清洗,本次我们将学习缺失值、重复值、字符串和数据转换等操作,清洗数据为后续的数据分析和建模做准备。
1. 缺失值观察与处理
1.1 缺失值观察
有很多方法可以查看缺失值:
方法1
df.info()
通过观察,可以发现 Age, Cabin, Embarked 都存在缺失值。
方法2
df.isnull().sum()
1.2 对缺失值进行处理
将缺失值赋值为0是一种常见操作,有以下几种尝试:
尝试1
df[df['Age']==None]=0
df.isnull().sum()
发现 Age 缺失值无变化,说明这种方法无效。
尝试2
df[df['Age'].isnull()] = 0
df.isnull().sum()
Age 缺失值个数为0,说明该方法有效。
尝试3
df[df['Age'] == np.nan] = 0
df.isnull().sum()
发现 Age 缺失值无变化,说明这种方法无效。
总结:数值列读取数据后,缺失值的数据类型为float64所以用None和np.nan可能索引不到,最好用.isnull()进行索引。
使用 dropna 丢弃缺失值
DataFrame.dropna(axis=0, how=‘any’, thresh=None, subset=None, inplace=False)
Parameters:
axis:{0 or ‘index’, 1 or ‘columns’}, default 0
确定丢弃包含缺失值的行还是列
- 0是丢弃包含缺失值的行
- 1是丢弃包含缺失值的列
how:{‘any’, ‘all’}, default ‘any’
- ‘any’ : 如果有一个缺失值,就丢弃所在行或列
- ‘all’ : 只有某一行或某一列都为缺失值,才丢弃
inplace:bool, default False
如果为True,丢弃缺失值操作将原地进行,并返回 None。
Returns:DataFrame or None
如果 inplace 为 False,返回丢弃了缺失值的 DataFrame,否则返回None。
丢弃数据集中的缺失值,只要某行存在缺失值,就丢弃该行
df.dropna().info()
观察发现,最后只有183条可用数据,证明这种做法很浪费数据。
使用 fillna 填充缺失值
DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None)
Parameters:
value:scalar, dict, Series, or DataFrame
用来填充缺失值的值,可以是标量、字典、Series 或 DataFrame。
method:{‘backfill’, ‘bfill’, ‘pad’, ‘ffill’, None}, default None
- ffill:使用缺失值前面一个有效值去填充;
- bfill:使用缺失值后面一个有效值去填充;
axis:{0 or ‘index’, 1 or ‘columns’}
按行或列方向填充缺失值
inplacebool, default False
是否原地执行操作
limitint, default None
对于连续的缺失值被最多填充个数,缺省值是无个数限制
Returns:DataFrame or None
如果 inplace 为 False,返回填充了缺失值的 DataFrame,否则返回None
将数据集中的缺失值都用0填充
df.fillna(0).info()
填充之后,数据集中没有缺失值,数据条数也没有减少。
2. 重复值观察与处理
2.1 查看数据中的重复值
df[df.duplicated()]
原始数据里没有重复值。
2.2 丢弃重复值
df = df.drop_duplicates()
2.3 保存清洗过的数据
df.to_csv('test_clear.csv')
3. 特征观察与处理
我们对特征进行一下观察,可以把特征大概分为两大类:
数值型特征:Survived ,Pclass, Age ,SibSp, Parch, Fare,其中Survived, Pclass为离散型数值特征,Age,SibSp, Parch, Fare为连续型数值特征
文本型特征:Name, Sex, Cabin,Embarked, Ticket,其中Sex, Cabin, Embarked, Ticket为类别型文本特征。
数值型特征一般可以直接用于模型的训练,但有时候为了模型的稳定性及鲁棒性会对连续变量进行离散化。文本型特征往往需要转换成数值型特征才能用于建模分析。
3.1 对年龄进行分箱(离散化)处理
3.1.1 将连续变量Age平均分箱成5个年龄段,并分别用类别变量12345表示
df['AgeBand'] = pd.cut(df['Age'], 5,labels = [1,2,3,4,5])
df.head()
3.1.2 将连续变量Age划分为(0,5] (5,15] (15,30] (30,50] (50,80]五个年龄段,并分别用类别变量12345表示
df['AgeBand'] = pd.cut(df['Age'],[0,5,15,30,50,80],labels = [1,2,3,4,5])
df.head(3)
3.1.3 #将连续变量Age按20% 40% 60% 80% 五个年龄段,并用分类变量12345表示
df['AgeBand'] = pd.qcut(df['Age'],[0,0.2,0.4,0.6,0.8,1],labels = [1,2,3,4,5])
df.head()
3.2 对文本变量进行转换
3.2.1 查看文本变量名及种类
方法1:value_counts
df['Sex'].value_counts()
方法2:unique
df['Sex'].unique()
df['Sex'].nunique()
3.2.2 将文本变量Sex, Cabin ,Embarked用数值变量12345表示
方法1:replace
df['Sex_num'] = df['Sex'].replace(['male','female'],[1,2])
df.head()
方法2:map
for feat in ['Cabin', 'Ticket']:
label_dict = dict(zip(df[feat].unique(), range(df[feat].nunique())))
df[feat + "_labelEncode"] = df[feat].map(label_dict)
df.head()
df['Cabin_labelEncode'].value_counts()
方法3:使用sklearn.preprocessing的LabelEncoder
from sklearn.preprocessing import LabelEncoder
for feat in ['Cabin', 'Ticket']:
lbl = LabelEncoder()
df[feat + "_labelEncode"] = lbl.fit_transform(df[feat].astype(str))
df.head()
df['Cabin_labelEncode'].value_counts()
可以发现,采用 map 方法与采用 LabelEncoder 方法的编码结果还是有区别的,这是因为不同的方法给相同的文本变量赋予了不同的数值。
3.2.3 将文本变量Sex,Embarked用one-hot编码表示
方法1 get_dummies
for feat in ["Sex", "Embarked"]:
x = pd.get_dummies(df[feat], prefix=feat)
df = pd.concat([df, x], axis=1)
df.head()
方法2:OneHotEncoder
from sklearn.preprocessing import OneHotEncoder
onehot_model = OneHotEncoder(handle_unknown='ignore')
onehot_features= ["Sex", "Embarked"]
onehot_model.fit(df[onehot_features].astype(str))
after_onehot_features = onehot_model.get_feature_names(onehot_features)
data_train_onehot = pd.DataFrame(onehot_model.transform(df[onehot_features]).toarray(),columns=after_onehot_features)
df = df.join(data_train_onehot)
df.head()
注意,使用 get_dummies 得到的独热编码的数据类型为 uint8,
使用 OneHotEncoder 得到的独热编码的数据类型为 float64。
3.3 从纯文本Name特征里提取出Titles的特征(所谓的Titles就是Mr,Miss,Mrs等)
df['Title'] = df.Name.str.extract('([A-Za-z]+)\.', expand=False)
df.head()
参考资料
- Datawhale 开源文档:https://github.com/datawhalechina/hands-on-data-analysis
- https://gitee.com/datawhalechina/hands-on-data-analysis/tree/master