Task02:数据清洗即特征处理

数据清洗及特征处理

通常直接获取的数据会存在缺失值、重复值或其他异常情况,需要首先进行数据清洗,然后再进行分析或建模。

缺失值的处理

观察缺失值

处理缺失值前需要观察数据中的缺失值。

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 编码等操作。

1赞
浙ICP备19012682号