数据清洗及特征处理
通常直接获取的数据会存在缺失值、重复值或其他异常情况,需要首先进行数据清洗,然后再进行分析或建模。
缺失值的处理
观察缺失值
处理缺失值前需要观察数据中的缺失值。
df.info()
获取数据的基本信息,其中包含每列的非空值数量,但是该方法不能直观的统计每列中存在的缺失值数量:
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
如果要统计每列/行中缺失值的数量,可以使用 isna()
方法后进行布尔简化:
# 获取每列缺失值的数量
>>> df.isna().sum()
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
dtype: int64
同样,利用布尔简化可以查看存在缺失值的行:
>>> df[df.isna().any(1)].head(3)
PassengerId Survived Pclass Name ... Ticket Fare Cabin Embarked
0 1 0 3 Braund, Mr. Owen Harris ... A/5 21171 7.250 NaN S
2 3 1 3 Heikkinen, Miss. Laina ... STON/O2. 3101282 7.925 NaN S
4 5 0 3 Allen, Mr. William Henry ... 373450 8.050 NaN S
处理缺失值
处理缺失值一般有两种方式:丢弃存在缺失值的数据(dropna)或对缺失值进行替换(fillna)。
dropna(how='any', subset=None)
用于丢弃存在缺失值的列,其中 how
参数用于控制如何根据缺失值删除数据,默认 ‘any’ 只要某行数据存在一个缺失值就删除该行,‘all’ 则需要整行数据都是缺失值才会删除。subset
控制检测缺失值的标签,例如对于泰尼克号数据集,可以只根据 ‘Age’ 删除缺失值:
>>> df.dropna(subset='Age').head(3)
PassengerId Survived Pclass ... Fare Cabin Embarked
0 1 0 3 ... 7.2500 NaN S
1 2 1 1 ... 71.2833 C85 C
2 3 1 3 ... 7.9250 NaN S
[3 rows x 12 columns]
fillna(values=None, method=None)
用于填充缺失值。values
参数控制填充值,一般为标量或字典,如果是标量则所有缺失值均填充为该值,如果是列标签和填充值构成的字典,则可以不同列分别填充不同的值。method
参数控制根据缺失值前面或后面的数据进行填充,‘ffill’ 以缺失值之前的数据填充,‘backfill’ 以缺失值之后的数据进行填充:
>>> df.fillna(0).head(3)
PassengerId Survived Pclass ... Fare Cabin Embarked
0 1 0 3 ... 7.2500 0 S
1 2 1 1 ... 71.2833 C85 C
2 3 1 3 ... 7.9250 0 S
重复值的处理
观察重复值
duplicated(subset=None, keep='first')
方法用于检测数据中是否存在重复数据。subset
参数控制根据哪些字段检测重复值。keep
参数控制标记重复值的方式,‘first’ 表示将第一次出现以外的数据标记为重复值,‘last’ 表示将最后一次出现以外的数据标记为重复值,False 表示将所有重复数据都标记为重复值。
检测重复值后,通过布尔索引可以获取并观察重复数据:
>>> df[df.duplicated()].head(3)
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
17 0 0 0 0 0 0.0 0 0 0 0.0 0 0
19 0 0 0 0 0 0.0 0 0 0 0.0 0 0
26 0 0 0 0 0 0.0 0 0 0 0.0 0 0
删除重复值
重复值一般直接删除,可以使用 drop_duplicates()
方法:
>>> df.drop_duplicates().head(3)
PassengerId Survived Pclass ... Fare Cabin Embarked
0 1 0 3 ... 7.2500 NaN S
1 2 1 1 ... 71.2833 C85 C
2 3 1 3 ... 7.9250 NaN S
特征观察与处理
数据一般可分为数值和文本两大类,其中数值又可以分为离散型数值和连续性数值。数值一般可直接用于模型训练,但有时为了稳定性和鲁棒性会将连续型数值离散化。而文本特征往往需要转换成数值才能进行模型训练。
离散化
数据分箱是将连续型的变量划分到离散区间,例如对于年龄,则是将其划分为几个年龄段。
分箱主要可以使用 pd.cut()
和 pd.qcut()
方法,分别根据数据大小和数据频率进行区间划分。
将年龄平均分成 5 个年龄段:
>>> pd.cut(df['Age'], 5, labels=[1,2,3,4,5]).head(3)
0 2
1 3
2 2
Name: Age, dtype: category
Categories (5, int64): [1 < 2 < 3 < 4 < 5]
将年龄划分为 [0,5) [5,15) [15,30) [30,50) [50,80) 五个年龄段:
>>> pd.cut(df['Age'], [0,5,15,30,50,80], right=False, labels=[1,2,3,4,5]).head(3)
0 3
1 4
2 3
Name: Age, dtype: category
Categories (5, int64): [1 < 2 < 3 < 4 < 5]
将年龄按 10% 30% 50% 70% 90% 五个年龄段:
>>> pd.qcut(df['Age'], [0,0.1,0.3,0.5,0.7,0.9], labels=[1,2,3,4,5]).head(3)
0 2
1 5
2 3
Name: Age, dtype: category
Categories (5, int64): [1 < 2 < 3 < 4 < 5]
文本变量处理
查看文本变量
处理文本变量前需要先了解某列中有哪些文本值,可以用 unique()
或 value_counts()
方法,后者除了显示非空重复值,还显示每个值出现的次数:
>>> df['Sex'].unique()
array(['male', 'female'], dtype=object)
>>> df['Sex'].value_counts()
male 453
female 261
Name: Sex, dtype: int64
将文本变量转换为数值编码
对于性别这类文本值很少的序列,可以直接使用 replace()
或 map()
方法进行替换:
>>> df['Sex_num'] = df['Sex'].replace(['male', 'female'], [1, 2])
>>> df.head(3)
PassengerId Survived Pclass Name ... Fare Cabin Embarked Sex_num
0 1 0 3 Braund, Mr. Owen Harris ... 7.2500 NaN S 1
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... ... 71.2833 C85 C 2
2 3 1 3 Heikkinen, Miss. Laina ... 7.9250 NaN S 2
>>> df['Sex_num'] = df['Sex'].map({'male': 1, 'female': 2})
>>> df.head(3)
PassengerId Survived Pclass Name Sex ... Ticket Fare Cabin Embarked Sex_num
0 1 0 3 Braund, Mr. Owen Harris male ... A/5 21171 7.2500 NaN S 1
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female ... PC 17599 71.2833 C85 C 2
2 3 1 3 Heikkinen, Miss. Laina female ... STON/O2. 3101282 7.9250 NaN S 2
而对于客舱,则有非常多的值,直接使用 replace()
方法进行替代会比较麻烦,此时可以借助 sklearn 包:
>>> from sklearn.preprocessing import LabelEncoder
>>> df['Cabin_num'] = LabelEncoder().fit_transform(df['Cabin'])
>>> df.head(3)
PassengerId Survived Pclass Name Sex ... Fare Cabin Embarked Sex_num Cabin_num
0 1 0 3 Braund, Mr. Owen Harris male ... 7.2500 NaN S 1 134
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female ... 71.2833 C85 C 2 73
2 3 1 3 Heikkinen, Miss. Laina female ... 7.9250 NaN S 2 134
[3 rows x 14 columns]
one-hot 编码
上面将文本数据转换成了数值编码,但数值编码存在一个问题,即数值编码存在大小,而很多模型会存在运算,一个类型的编码运算后可能会变成另一个类型的编码,导致模型出现问题。而 one-hot 编码对类别二进制化,结果只有 0、1,就不会出现这样的问题。
pd.get_dummies()
方法可以直接把文本数据转换为 one-hot 编码,其中 prefix
参数可以指定前缀:
>>> sex = pd.get_dummies(df['Sex'], prefix='Sex')
>>> df = pd.concat([df, sex], axis=1)
>>> df.head(3)
PassengerId Survived Pclass Name Sex ... Embarked Sex_num Cabin_num Sex_female Sex_male
0 1 0 3 Braund, Mr. Owen Harris male ... S 1 134 0 1
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female ... C 2 73 1 0
2 3 1 3 Heikkinen, Miss. Laina female ... S 2 134 1 0
文本数据特征提取
对于人名这类的数据,在模型分析时,具体的名字可能并没有意义,但是从姓名中提取出的身份特征后就可以进一步进行分析。从文本中提取部分特征文本可以使用 str.extract()
方法,该方法用到正则表达式中的分组捕获:
>>> df['Title'] = df.Name.str.extract('(\w+)\.')
>>> df.head(3)
PassengerId Survived Pclass Name Sex ... Sex_num Cabin_num Sex_female Sex_male Title
0 1 0 3 Braund, Mr. Owen Harris male ... 1 134 0 1 Mr
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female ... 2 73 1 0 Mrs
2 3 1 3 Heikkinen, Miss. Laina female ... 2 134 1 0 Miss
[3 rows x 17 columns]
# 查看下总共有哪些身份特征
>>> df['Title'].unique()
array(['Mr', 'Mrs', 'Miss', 'Master', 'Don', 'Rev', 'Dr', 'Mme', 'Ms',
'Major', 'Lady', 'Sir', 'Mlle', 'Col', 'Capt', 'Countess',
'Jonkheer'], dtype=object)
提取出身份特征后,可以再用上面的方法将文本转换成数值编码或 one-hot 编码。
总结
数据清洗和特征处理是具体模型分析前对数据进行预处理的步骤,将脏数据转换为满足数据质量要求的数据。
数据清洗主要对缺失值和重复值进行处理,对于缺失值,可采取将其填充为其他值或删除缺失数据两种方法;对于重复值,则一般直接删除。
特征处理主要对连续型数值变量和文本型变量进行处理,对于连续型数值变量,通常的做法是进行分箱处理;对于文本型变量,根据具体的数据类型,可执行特征提取、转换为数值编码或 one-hot 编码等操作。