数据预处理
数据预处理
1.非数值型数据处理
机器学习建模的时候处理的数据都是数值类型,然而实际生活中我们通常获取的数据往往会包含非数值型的数据,最常见的就是文本类型的数据。例如:“男和女”,处理时可以用查找、替换的思路,将文本转换为数值1和0。但是如果类别很多,不止有2个怎么办呢?有两个方法————Get_dummies 哑变量处理和 LabelEncoding 编号处理。
1.1Get_dummies哑变量处理
哑变量又叫虚拟变量,通常取值0和1。
# 例子:
import pandas as pd
df = pd.DataFrame({'客户编号':[1, 2, 3],
'性别':['男','女','男']})
# 将性别列变为哑变量
df = pd.get_dummies(df,columns=['性别'])
df
“性别"已经变为"性别_女”,“性别_男”,数字1表示符合列名,数字0表示不符合列名。例如,第二行的客户为女性,所以在"性别_女"列中的数字是1,在"性别_男"列中的数字是0。
虽然现在已经将文本类型的数据转化为数字,但是"性别_女"和"性别_男"这两列存在多重共线性,也就是当你知道其中一列的内容,另一列的内容也就知道了,用公式来表示就是:“性别_女”=1-“性别_男”;多重共线性会带来很多问题,所以我们要删去其中一列。
# 删去一列
df = df.drop(columns='性别_女')
# 重命名
df = df.rename(columns={'性别_男':'性别'})
df
复杂点的例子:
# 复杂点的例子:
import pandas as pd
df = pd.DataFrame({'房子编号':[1, 2, 3, 4, 5],
'朝向':['东', '南', '西', '北', '南']})
df = pd.get_dummies(df, columns=['朝向'])
df
上面数据也存在多重共线性,即知道3个朝向的数字就能判断第4个朝向的数字是0还是1,所以可以删去1个哑变量,假设删去"朝向_西"
df = df.drop(columns=['朝向_西'])
df
构造了哑变量容易产生高维数据,所以通常和 PCA(主成分分析)一起使用,也就是先构造哑变量产生高维数据然后采用PCA进行降维。
1.2LabelEncoding编号处理
除了使用 get_dummies()函数进行非数值类型数据处理外,还可以使用 LabelEncoding 进行编号处理,也就是使用 LabelEncoding()函数将文本类型的数据转换为数字。
# 例子:
df = pd.DataFrame({'编号':[1, 2, 3, 4, 5],
'城市':['北京', '上海', '广州', '深圳', '北京']})
df
# 将"城市"列文本转化为不同的数字
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
label = le.fit_transform(df['城市'])
label
# 可以看到 北京 变为 1, 上海 变为 0, 广州 变为 2,深圳 变为 3
df['城市'] = label
df
上面使用 LabelEncoding处理后产生了一个奇怪的现象,就是上海和广州的平均值是北京,然而这个是没有啥现实意义的,这也是LabelEncoding的一个缺点,可能产生一些没有意义的关系。不过对于 树模型(如决策树、随机森林、XGBoost等集成算法)能很好处理这种转换,所以对于树模型来说这并不会影响结果。
LabelEncoding()函数生成的数字是随机的,如果想按照特定的内容进行替换,可以使用replace()函数,这两种处理方式对于建模来说并不会有太大影响。
# 例子:
df = pd.DataFrame({'编号':[1, 2, 3, 4, 5],
'城市':['北京', '上海', '广州', '深圳', '北京']})
df
# 先看看各个值出现的次数
df['城市'].value_counts()
# 用replace()进行替换
df['城市'] = df['城市'].replace({'北京': 0, '上海': 1, '广州': 2, '深圳': 3})
df
LabelEncoding()函数是对文本内容随机编号,而replace()函数可以将文本内容替换成自己定义的值,不过当分类较多时,还需要用 value_counts()函数获取各类的名称,而且步骤稍微繁琐。
总的来说,Get_dummies的优点是它的值只有0和1,缺点是类别的数量很多时,特征维度会很高,需要进行降维。如果数量不多,可以优先考虑使用Get_dummies,其次考虑使用LabelEncoding或者replace函数;但是如果是基于树模型的机器学习模型,用LabelEncoding也没有太大关系。
2.重复值、缺失值及异常值处理
在数据的录入和处理过程中,不可避免会产生重复值、缺失值以及异常值,下面将介绍如何处理重复值、缺失值及异常值。
2.1重复值处理
import pandas as pd
data = pd.DataFrame([[1,2,3],[1,2,3],[4,5,6]],columns=['c1','c2','c3'])
print(data)
可以看到第一行和第二行是重复的,可以用 duplicated()查询重复的内容
data[data.duplicated()]
# 计算重复的数量
data.duplicated().sum()
# 删除重复的行
data = data.drop_duplicates()
data
注意上面删除了重复的行之后,并没有改变原表的结构,所以drop_duplicates()函数并不会改变原表的结构,所以需要重新赋值
import pandas as pd
data = pd.DataFrame([[1,2,3],[1,2,3],[4,5,6]],columns=['c1','c2','c3'])
# 对‘c1’列出现重复的内容进行去重操作,这样是会删除掉一行的数据
# 这样的去重方法不如上面的去重方法(全部一样才去重)严格
data = data.drop_duplicates('c1')
data
2.2缺失值处理
# 例子
import numpy as np
import pandas as pd
data = pd.DataFrame([[1, np.nan, 3],
[np.nan, 2, np.nan],
[1, np.nan, 0]], columns=['c1', 'c2', 'c3'])
data
# 可以使用 isnull()函数或者 isna() 函数来查看空值
data.isnull()
# 对单列查看空值
data['c1'].isnull()
# 如果数据量较大,可以通过如下代码筛选出某列中内容为空值的行
# 这里筛选出 c1列有空值的行,也就是第二行
data[data['c1'].isnull()]
data.isna()
# 对于空值的处理方式有两种,一种是删除空值,一种的填补空值
# 删除空值
# 下面这种方法比较激进,只要是含有空值的行都会被删除,因为每行都有空值,所以都删除了
a = data.dropna()
a
# 也可以对上述函数设置 thresh 参数,例如将其设置为 n ,表示如果一行中的非空值少于 n 个则删除该行
# 这里表示:如果一行中的 非空值 少于 2 个则删除改行,而第一行和第三行都有2个非空值,所以保留了
a = data.dropna(thresh=2)
a
# 用 fillna() 可以填补空值
# 这里采用的是 均值填补法,也就是把每一列的均值填补到该列的空值中,也可以用其它替换比如 中位数
b = data.fillna(data.mean())
b
c = data.fillna(data.median())
c
# 也可以用空值上方或者下方的值来替换空值
# 这里的 pad 表示用空值上方的值对空值进行替换,如果上方不存在或者也是空值,则不替换
c = data.fillna(method='pad')
c
# 这里是用下方的值填补空值, bfill 和 backfill 都可以
d = data.fillna(method='backfill')
d
2.3异常值处理
# 例子:
data = pd.DataFrame({'c1':[3, 10, 5, 7, 1, 9, 69],
'c2':[15, 16, 14, 100, 19, 11, 8],
'c3':[20, 15, 18, 21, 120, 27, 29]}, columns=['c1', 'c2', 'c3'])
data
可以看到很明显 c1 列中 69,c2 列中 100,c3 列中 120 都是很明显的异常值。我们可以利用箱体图和标准差来进行异常值检测,箱线图有5条横线,自上往下分别为:上界、上四分位数、中位数、下四分位数、下界,在上下界之外的都是异常值。
下四分位数记为 Q 1 Q_1 Q1 表示样本中仅有 25 % 25\% 25% 的数据小于 Q 1 Q_1 Q1 ,
上四分位数记为 Q 3 Q_3 Q3 表示样本中仅有 25 % 25\% 25% 的数据大于 Q 3 Q_3 Q3 ,
上四分位数和下四分位数的差记为: I Q R = Q 3 − Q 1 IQR = Q_3-Q_1 IQR=Q3−Q1,
上界为 Q 3 + 1.5 × I Q R Q_3+1.5\times IQR Q3+1.5×IQR,
下界为 Q 1 − 1.5 × I Q R Q_1-1.5\times IQR Q1−1.5×IQR。
data.boxplot()
利用标准差检测,当数据服从标准正态分布时, 99 % 99\% 99% 的数值与均值的距离应该在 3 个标准差之内,也就是 3 σ 3\sigma 3σ原则, 95 % 95\% 95% 的数值与均值的距离应该在 2 个标准差之内,也就是 2 σ 2\sigma 2σ原则,有时候 3 个标准差可能过于严格,所以有时候会将阈值设定为 2 个标准差,即认为当数值与均值的距离超过两个标准差则可以认为它是异常值。
a = pd.DataFrame()
for i in data.columns:
z = (data[i] - data[i].mean()) / data[i].std()
a[i] = abs(z) > 2
a
这里相当于进行了标准化,这里标准化之后的好处就是数据变为服从
N
(
0
,
1
)
N(0,1)
N(0,1)的正态分布,此时标准差就为1,如果标准化之后的数值大于标准正态分布的标准差 1 的 2倍,那么该数值为异常值,返回布尔值 True ,否则返回布尔值 False,检测到异常值后,如果异常值较少或影响不大,也可以不处理,如果需要处理,可以采用如下几种常见的方式:
(1) 删除含有异常值的记录;
(2) 将异常值视为缺失值;
(3) 数据分箱的方法进行处理。