机器学习之EDA和预处理, since 2020-12-30

(2020.12.30 Wed)
Exploratory Data Analysis, aka EDA,探索性数据分析,皆在增强对使用者对数据的认识和理解,包括数据的分布、极值、分位点、相关性等。

以python Pandas为例,用到如下指令

>> import pandas as pd
>> import matplotlib.pyplot as plt
>> data = pd.read_csv('xxx.csv')

>> shape = data.shape #数据的行数、列数
>> data.dtypes # 各个列的数据类型

>> data.describe() #返回各个列的min/max/mean/median/25&75quantile
>> data['afield'].value_counts() # 计算某列中各个值的分布
>> data.duplicated().value_counts() # 判断是否有重复值
>> data.apply(lambda x: sum(x.isnull()), axis=0) # 查看各列是否有缺失值,axis=1时显示各行的结果。
>> data.corr()  # 查看各变量间的相关性,确定是否存在多重共线性.

>> plt.scatter(data['x1'], data['y']) #变量x1与y的散点图
>> data.plot.box() #查看箱线图
>> data['y'].hist(bins=50) # 查看直方图

(2022.06.24 Fri)

对数据的分析包括质量分析特征分析。后文的主要内容如下:

  • 数据质量分析
    • 缺失值处理
    • 异常值处理
    • 不一致的值(一致性分析)等
  • 数据特征分析
    • 分布分析
    • 对比分析
    • 统计量分析
    • 周期性分析
    • 贡献度分析
    • 相关性分析

数据的预处理包括

  • 数据清洗
    • 缺失值处理
    • 异常值处理
  • 数据集成
    • 实体识别
    • 冗余属性识别
  • 数据变换
    • 函数变换
    • 规范化
    • 连续属性离散化
    • 属性构造
    • 小波变换
  • 数据规约
    • 属性规约 (参考feature engineering)
    • 数值规约

数据质量分析

质量分析主要是检查原始数据中是否存在脏数据,即不符合要求不能直接进行相应分析的数据,包括:

  • 缺失值missing value
  • 异常值outliers
  • 不一致的值inconsistent value
  • 重复数据 duplicate
  • 特殊符号 #&¥

缺失值的处理

产生缺失值原因多样,比如信息无法获取或获取成本过高,信息遗漏,比如人为因素、设备故障、存储介质故障、传输媒介故障等,或属性值根本不存在,比如未婚者的配偶姓名、儿童的固定收入等。

因为数值缺失,可能导致丢失大量有用信息;模型表现出的不确定性显著增加,其中的规律难以把握;空值数据使建模过程陷入混乱,输出不可靠。

分析缺失值,包括统计含有缺失值属性的个数、每个属性的缺失值个数、缺失数和缺失率。

判断是否为空值

df.isnull() # 返回True or False的df或series
df.notnull() # 返回True or False的df或series,与.isnull()结果相反

常见的缺失值处理方式包括

  1. 删除记录
  2. 数据插补
  3. 不处理

删除记录的命令

df.dropna(axis=0, how='any', replace=True, subset=None, threshold=None)

  • 默认axis=0,则删除有空值的行,axis=1删除有空值的列
  • 默认how='any',则该行/列有空值就删除,how='all',则该行/列全为空值才删除
  • 默认replace=False,则返回新的数据集,replace=True,则对原数据集操作,改变原数据
  • 默认subset=None,则从所有子集中做dropna操作,subset=['a']在指定子集dropna,比如axis=0, subset=['a']时按行删除空值只删除a列的空值,其他列即便有空值也保留
  • 默认thresh=None,表示有效数据量的最小要求,thresh=,要求该行或该列至少有n个不是NaN值时将其保留
>> df = pd.DataFrame([[0, np.nan, 1],[2,3,4],[np.nan, 3,5],[np.nan, np.nan, np.nan]])
>> df
     0    1    2
0  0.0  NaN  1.0
1  2.0  3.0  4.0
2  NaN  3.0  5.0
3  NaN  NaN  NaN
>> df.dropna() # 默认axis=0,how='any',replace=False,
# 则按行删除,有空值就删除,返回新数据
>> df.dropna()
     0    1    2
1  2.0  3.0  4.0
>> df.dropna(axis=1, how='any') #按列删除,有空值就删除,结果是全删
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3]
>> df.dropna(axis=0, how='all', inplace=True) # 按行删除,一行都是nan才删除,并替换原数据
>> df
     0    1    2
0  0.0  NaN  1.0
1  2.0  3.0  4.0
2  NaN  3.0  5.0
>> df.columns=['a','b','c']
>> df.dropna(axis=0, how='any', subset=['b']) # 按行找空值,但只查b列
     a    b    c
1  2.0  3.0  4.0
2  NaN  3.0  5.0

缺失值的填充

df.fillna(inplace=False, value=value, axis=0)

  • 默认value=None,以字典的形式指定有空值的列被填充的值
  • 默认axis=0,则按行填充
  • 默认inplace=False,生成新数据,True则在原数据之上修改
>> v={'b':1983}
>> df.fillna(value=v) # b列的空值填为1983
     a       b    c
0  0.0  1983.0  1.0
1  2.0     3.0  4.0
2  NaN     3.0  5.0

值的替换

df.replace(, , inplace=False)

  • 不同值的替换:输入df.replace({'ori': 'new', 'ori1': 'new2'}),或输入两个参数df.replace(['ori1', 'ori2'], ['new1', 'new2'])
  • 不同值替换为同一个值:df.replace(['ori1', 'ori2', 'ori3'], 'new1'),则ori1等都被替换为new1

(2022.06.25 Sat)

异常值处理 outliers

异常值分析是检测数据是否有录入错误、不合理等情况。异常值是样本中的个别值,其数值明显偏离其余的观测值,也成为离群点。

方法:

  1. 简单统计量分析
    对变量做描述性统计,查看数据有哪些不合理。常用的是minmax,看是否超出合理范围。
  2. 原则
    如果数据服从正态分布,在原则下,异常值被定义为测定值中与均值的偏差超过3倍标准差(standard deviation)的值。在正态分布的假设下,距离均值3倍标准差外的值出现概率为,属于小概率事件。如果数据不服从正态分布,可以用远离均值多少倍的标准差来描述。
  3. 箱型图分析
    箱型图中的异常值标准:小于或大于的值,其中是下四分位数,表示全部观察值中有四分之一比它小,是上四分位数,是四分位数间距,定义为,的范围包括了全部观察值的一半。
    箱型图不对数据分布做假设;以四分位数和四分位距为标准,具有一定的鲁棒性robustness:多大25%的数据可以变得任意远而不会很大的扰动四分位数,异常值因此很难对这个标准施加影响。

箱型图的查看可通过matplotlib.pyplot.boxplot方法,也可通过pandas.boxplot方法。

import pandas as pd
import numpy as np
tmpnp = np.random.normal(5, 2, 100)
tmp = pd.DataFrame(tmpnp)
print(tmp)
tmp.boxplot() 
boxplot by pandas
fig = plt.figure()
view = plt.boxplot(tmpnp)
plt.show()
boxplot by matplotlib

异常值的处理有如下几种方式

处理方法 描述
删除含有异常值的记录 直接删除
视为缺失值 利用缺失值的处理方法进行处理
平均值修正 用前后两个观测值的平均值修正该异常值
不处理 直接使用,有风险

很多情况下,先分析异常值出现的可能原因,再判断异常值是否应舍弃,如果是正确的数据,则直接在异常值的数据集上进行数据挖掘。

一致性分析

数据不一致指的是数据的矛盾性、不相容性。不一致数据的产生主要发生在数据集成过程中,可能是由于数据来自于不同的数据源、对于重复存放的数据未能进行一致性更新造成的。比如两张表都存储了用户的电话号,但在用户修改号码时只对其中一张表做了修改,两张表就产生了不一致的数据。

数据集成过程中,来自多个数据源的现实世界实体的表达形式未必是一样的,可能不匹配,需要考虑实体识别问题(entity recognisation)和属性冗余问题(feature redundancy),从而将源数据在最低层上加以转换、提炼和集成。

实体识别

指的是从不同数据源识别出现实世界的实体,它的任务是统一不同数据源的矛盾之处,形式有

  1. 同名异义
    数据源A的id和源B的id分别描述菜品编号和订单编号,即描述的是不同的实体
  2. 异名同义
    数据源A的sales_date和源B的sale_dt描述的都是销售日期,有A.sales_date=B.sale_dt
  3. 单位不统一
    比如不同数据源分别使用国标、英标和国际标来标识不同的数据单位

检测和解决这些冲突是实体识别的任务。

冗余属性识别 feature redundancy

比如

  1. 同一个属性出现多次
  2. 同一个属性命名不一致

冗余属性检测到之后删除即可。

有的冗余属性可以用相关分析检测。给定两个数值型的属性a和b,用相关系数度量一个属性在多大程度上蕴含另一个属性。

数据特征分析

对数据质量有过分析之后,就可以对数据特征做分析。数据特征分析主要是如下几个方面

  1. 分布分析
  2. 对比分析
  3. 统计量分析
  4. 周期性分析
  5. 贡献度分析
  6. 相关性分析

分布分析

用于揭示数据的分布特征和分布类型。可分为定量数据和定性数据两类进行分析。定量数据需要了解其分布形式是对称还是非对称,发现特大和特小的可疑值,绘制频率分布表和直方图、茎叶图进行直观的分析;定性数据可用饼图和条形图直观显示分布情况。

定量数据的分布分析需要了解如下几个方面

  1. 求极差,即max-minpandas.max/min
  2. 决定组距和组数,即对数据按大小分组/分bin
  3. 决定分点,即决定组/bin的分界点,指令比如pandas.cut/qcut
  4. 列出频率分布表,即统计每个组/bin的频率和出现频次
  5. 绘制频率直方图,根据频率分布表,画出直方图histogram

遵循的原则

  1. 各组之间必须互相排斥
  2. 各组必须将所有的数据包含在内
  3. 各组的组宽最好相等

定组/分箱的操作常用的有等宽法、等频法和(一维)聚类。

  1. 等宽法
    将属性的值域分成具有相同宽度的区间,区间的个数由数据本身的特点决定,或用户指定,类似于制作频率分布表。缺点在于对离群点敏感,倾向于不均匀的把属性值分布到各个区间。区间之间数据的个数不均衡,会严重损坏建立的决策模型。
  2. 等频法
    将相同数量的记录放在每个区间。避免了等宽法中区间的数据个数不均衡的问题,但是也有可能导致相同的数据分在不同的区间,仅仅为了保证不同区间的数据个数相同。

上面这两种方法简单易于操作,但需要人为的规定划分区间的个数。
定组/分箱操作,使用到了pandas.cut/qcut方法。
cut指令用于将指定的序列按数量排序分组形成分位点,保证每组数值覆盖的范围大致相同。其中用于执行cut操作的对象只能是array-list data,不能是一个DataFrame;参数bins可以指定为一个标量(表示范围均分成几份,而不是每一个bin中的数据个数相同),或一个数组(手动指定范围);参数labels用于给每个bin加名字,如果不指定则以该bin的范围表示;参数right表示是否包含bin的右部,默认是True表示bin的范围是左开右闭型即包含右部,False表示左闭右开型即不含右部;include_lowest表示是否包含左边界值,True or False

>>> df1 = pd.DataFrame({'number': np.random.randint(1, 100, 1000)}) # 1000个随机数,分布于1到1000
>> df1['bins'] = pd.cut(df1['number'], bins=5, labels=['a', 'b', 'c', 'd', 'e']) # 对number数列的数字范围进行均分,每个bin分别冠以a到e的值
# df1['bins'] = pd.cut(df1['number'], 5, labels=['a', 'b', 'c', 'd', 'e']) # 与上一条命令等价
>> df1['bins'].value_counts() # 统计每个bin出现次数
d    204
c    203
a    200
e    200
b    193
Name: bins, dtype: int64

将bin的范围固定。

>> df= pd.DataFrame({'number':np.random.randint(1, 100, 5)}) # 10个随机数,分布于1到100
>> df['bins'] = pd.cut(x=df['number'], bins=[1, 20, 40, 60, 80, 100]) # 将随机数分配到指定的bins,并生成新的列
>> df
    number bins
0   59  (40, 60]
1   37  (20, 40]
2   91  (80, 100]
3   5   (1, 20]
4   82  (80, 100]

设定bin的左闭右开。

>> df= pd.DataFrame({'number':np.random.randint(1, 100, 5)}) 
>> df['bins'] = pd.cut(x=df['number'], bins=[1,20,40,60,80,100], \ 
                       right=False) # 设定左闭右开
df
    number  bins
0   2   [1, 20)
1   21  [20, 40)
2   96  [80, 100)
3   87  [80, 100)
4   36  [20, 40)

qcut指令用于得到序列的分位值,即从数量上均分bin,或每个bin的数据个数相同/相差不多。参数部分与cut类似;retbins参数如果为True将返回bins的范围,默认False不返回bins结果。

>> df2 = pd.DataFrame({'number':np.random.randint(0, 100, 1000)}) 
>> df2['bins'], tmp_bins = pd.qcut(x=df2['number'], q=5, labels=['a','b','c','d','e'], retbins=True)
>> df2['bins'].value_counts()
b    214
a    202
d    202
e    198
c    184
Name: bins, dtype: int64
>> tmp_bins
array([ 0. , 18. , 40. , 58.4, 77. , 99. ])
  1. 基于聚类的方法
    首先将连续的属性值用聚类算法,e.g., K-means,进行聚类,然后再将聚类得到的簇进行处理,合并成一个簇的连续属性值并做同一标记。聚类分析的离散方法也需要用户指定簇的个数,从而决定产生的区间数。

下面的案例中,首先手动生成一个序列,由三个正态分布的序列拼接而成,中心分别是1,3,5,标准差同为0.4,用K-Means通过对数据做聚类实现离散化。

tmp1 = np.random.normal(1, 0.4, 100)
tmp2 = np.random.normal(3, 0.4, 100)
tmp3 = np.random.normal(5, 0.4, 100)
# 生成有三个中心的正态分布序列
tmppd = pd.DataFrame(np.hstack([tmp1, tmp2, tmp3]))
from sklearn.cluster import KMeans as kmeans
k = 3
# 训练K-means模型
kmodel = kmeans(n_clusters=k)
kmodel.fit(tmppd)
# 找出模型得到的中心点,并排序,得到序列c
c = pd.DataFrame(kmodel.cluster_centers_).sort_values(0)
# 利用pandas内置的滑动窗口方法,计算模型中心点的相邻两点的均值,用于作为分界点
w = tmppd.rolling(2).mean()
# 生成分界点,注意上一步结果的w,其第一项是`nan`,用于占位
w = [tmppd.min()[0]]+w[0].to_list()[1:]+[tmppd.max()[0]]
d = pd.cut(tmppd[0].values, w, labels=range(k))
>> print(d)
[0, 0, 0, 0, 0, ..., 2, 2, 2, 2, 2]
Length: 300
Categories (3, int64): [0 < 1 < 2]
# 画图
plt.figure(figsize=(8,3))
for j in range(0, 3):
    plt.plot(tmppd[0][d3==j], [j for i in d3[d3==j]], 'o')
plt.show()
Distribution analysis with clustering method

(2022.06.27 Mon)

对比分析

对比分析是把两个相互联系的指标进行比较,从数量上展示和说明研究对象规模的大小、水平的高低、速度的快慢等关系是否协调。

对比的两种形式:

  • 绝对数比较
  • 相对数比较
    由有联系的指标对比计算的,反应客观现象之间数量联系程度的综合指标。分为以下几种
    • 结构相对数:部分数值和全部数值对比求得比重,比如占比,合格率等
    • 比例相对数:同一总体内不同部分的比较,如人口性别比例、投资消费比例等
    • 比较相对数:同一时期两个性质相同的指标数值对比,说明同类现象在不同空间条件下的对比,如不同地区商品价格对比、不同行业、企业间某项指标对比
    • 强度相对数:性质不同但有一定联系的总量指标进行对比,说明现象的强度、密度、普遍程度,如人均GPD用元/人表示,人口密度表示,人口出生率表示
    • 动态相对数:同一现象在不同时期的指标数值进行对比,用以说明发展方向和变化速度,如发展速度、增长速度

统计量分析

从集中趋势和离中趋势两个方面进行分析。平均水平的指标是对个体集中趋势的度量,最广泛使用的是均值、中位数;反映变异程度的指标则是对个体离开平均水平的度量,使用的是标准差(方差)、四分位间距

集中趋势度量有均值、中位数和众数。均值的问题在于对极端值很敏感。为了消除少数极端值的影响,可以使用截断均值或中位数来度量几种趋势。截断均值是去除掉高、低极端值之后的平均数。众数是数据集中出现最频繁的值,并不经常用来度量定量变量的中心位置,更适合定性变量,不具有唯一性,一般用于离散变量。

离中趋势的度量包括

  1. 极差,max-min,对极端值非常敏感,也忽略了最大小之间的数据分布
  2. 标准差,数据偏离均值的程度,
  3. 变异系数,读两遍准差相对于均值的离中趋势,变异系数用来比较多个不同单位或不同波动幅度的数据集的离中趋势
  4. 四分位数间距,包括上四分位数和下四分位数。将所有数值从大到小排列并分成四等分,处于第一个分割点位置的数值是下四分位数,第二个分割点位置是中位数,第三个分割点位置的数值是上四分位数。
    四分位数间距是,包含了全部观察值的一半,其值越大,说明数据的变异程度越大,反之越小。

以上的统计量,使用pandas的describe方法都可以得到

import numpy as np
import pandas as pd
datanp = np.random.normal(10, 8, 100) # mean=10, sd=8的正态分布,100个样本
data = pd.DataFrame(datanp)
stat = data.describe()
stat
0
count   100.000000
mean    9.948244
std 8.451430
min -15.555554
25% 4.420053
50% 10.359842
75% 15.811739
max 31.110254

调用describe的结果

max_min_gap = stat.loc['max'] - stat.loc['min']
cv = stat.loc['std'] / stat.loc['mean']
IQR = stat['75%'] - stat['25%']

周期性分析

探索某个变量是否随着时间变化而呈现某种周期变化的趋势。有年度周期趋势、季节性周期趋势,较短的有月度周期性趋势、周度周期性趋势,甚至有天、小时的趋势。

一般用时序图来做周期性分析。

贡献度分析

又称帕累托分析,原理是帕累托法则,又称二八定律。同样的投入放在不同的地方会产生不同的效益。贡献度分析目的在于查看各分部之间的贡献程度。

相关性分析

分析连续变量之间线性相关程度的强弱,并用适当的统计指标表示出来。方法如下:

  1. 直接绘制散点图
  2. 散点图矩阵
  3. 计算相关系数
    常见的有Pearson相关系数和Spearman秩相关系数和判定系数。
    Pearson相关系数要求连续变量的取值服从正态分布。范围在-1到1之间,反映了不同程度的线性相关性。公式如
    Spearman秩相关系数用于给出不服从正态分布的变量、分类和等级变量之间的关联性,也称等级相关系数。公式如
    对两个变量成对的取值分别按从小到大或者反过来的顺序编秩,代表的秩次,代表的秩次。因为一个变量的相同的取值必须有相同的秩次,在计算中采用的秩次是排序后所在位置的平均值。只要两个变量具有严格单调的函数关系,那么他们就是完全Spearman相关的,与Pearson不同的是,Pearson相关只有在变量具有线性关系时才是完全相关的。
    在实际应用计算中,上述相关系数都要对其进行假设检验,使用t检验方法检验其显著性水平以确定相关程度。正态分布下,Spearman秩相关系数和Pearson系数效率上等价,对于连续测量数据,更适合P相关系数。
    另有判定系数,是相关系数的平方,用表示,用来衡量回归方程对的解释程度。取值范围在0到1之间,越接近于1,表明与的相关性越强,接近于0,则没有直线相关关系。

相关性的计算在pandas中可使用corr方法。

pandas.corr(method='pearson')
series_1.corr(series_2, method='pearson')

调用corr方法时,method默认是Pearson方法,支持spearman系数和kendall肯德尔系数。

数据预处理

数据清晰和集成部分参考前文数据分析的部分

数据变换

数据变换是对数据进行规范化处理,将数据转换成适当的形式,以适用于挖掘任务和算法的需要。

函数变换

常用的包括平方、开方、取对数、差分运算。常用来将不具有正态分布的数据变换成具有正态分布的数据。在时间序列分析中,对数变换或者差分运算可以将非平稳序列转换成平稳序列。对数变换可以将数值的区间压缩。

规范化/归一化normalisation

为了消除指标之间的量纲和取值范围差异的影响,需要使用标准化处理,将数据按照比例缩放,使其落入特定的区域。

规范化包括

  1. min-max规范化
    离差标准化,对原始数据的线性变换,将数值映射到之间,公式为

    这种方法简单易行,缺点是若数值集中且某个数值很大,则规范化后各值都会接近于0.
  2. 零-均值规范化
    也称标准差标准化,经过处理后的数据均值为0,标准差为1,转化公式为
  3. 小数定标规范化
    通过移动属性值的小数位数,将属性映射到[-1,1]之间,移动的小数位取决于属性绝对值的最大值,公式为

    实现方法为data/10**np.ceil(np.log10(data.abs().max()))

(2022.06.30 Thur)
并非所有算法都需要对数据进行归一化。

  • 不需要归一化:概率模型,因其不关心变量的值,而关心变量的分布和变量间的条件概率,如决策树、随机森林、朴素贝叶斯等;
  • 需要归一化:最优化问题,归一化后加快了梯度下降求最优解的速度,并有可能提高精度,如SVM、线性回归、Adaboost、logistic regression、k-means,是否归一化主要在于是否关心变量取值;神经网络标准化处理需要做归一化,用于弱化某些变量的值较大而产生意想不到的影响;k-NN算法需要归一化,避免小数量级的解释变量对结果产生的影响太小。

连续属性离散化

参考数据EDA部分

属性构造、小波分析

数据规约

在大数据集上进行复杂的数据分析和挖掘需要很长时间,数据规约产生更小但保持原数据完整性的新数据集。在规约后的新数据集上处理将更有效率。

属性规约

同feature engineering,参考feature engineering文章。

数值规约

数值规约指的是通过选择替代的、较小的数据来减少数据量,包括有参方法和无参方法。有参方法是使用一个模型来评估数据,只需要存放参数,而不需要存放实际数据,例如回归和对数线性模型。无参方法就需要存放实际数据,如直方图、聚类、抽样。

直方图使用分箱/bin来近似数据分布,是流行的数据规约形式。比如统计各地油价,从5元到20元不等且只为整数,初始的直方图在每个整数上建立一个组/bin,通过直方图规约,可将范围扩大,即每个bin的范围是5,则数据从15组变为3组,即5-10,11-15,16-20。数据量较少

聚类,将不同的数据聚为不同的簇,用簇的值代替该簇中所有数据的值,该技术有赖于簇的定义是否符合数据的分布性质。

抽样,分为有放回抽样和放回抽样,聚类抽样,分层抽样(stratified sampling)。

Reference

1 Python数据分析和挖掘实战,张良均等,机械工业出版社

你可能感兴趣的:(机器学习之EDA和预处理, since 2020-12-30)