python数据预处理之异常值、缺失值处理方法

      数据预处理是明确分析目标与思路以后进行数据分析的第一步,也是整个项目中最基础、花费时间较长的工做。除了互联网埋点的数据或企业内部的业务数据以外,每每咱们拿到的,好比说网上采集的数据并非那样规整,这类数据常常出现错误值、缺失值和异常值。python

1、异常值算法

       异常值是指样本中的个别值,其数值明显偏离其他的观测值。异常值也称离群点,异常值的分析也称为离群点的分析。app

       经常使用的异常值分析方法为3σ原则、箱型图分析、机器学习算法检测,通常状况下对异常值的处理都是删除和修正填补,即默认为异常值对整个项目的做用不大,只有当咱们的目的是要求准确找出离群点,并对离群点进行分析时有必要用到机器学习算法,其余状况下不用费精力去分析他们,今天不讨论基于机器学习算法的离群点检测和分析,改天单独出一个。dom

一、3σ原则机器学习

       若是数据服从正态分布,异常值被定义为一组测定值中与平均值的误差超过3倍的值 → p(|x - μ| > 3σ) ≤ 0.003函数

首先建立数据 学习

对数据进行正态性检验,p值为0.63,远远大于 0.05,认为服从正态分布。3d

接下来绘图查看数据和异常值code

#绘制数据密度曲线
fig= plt.figure(figsize=(10,6))
ax1=fig.add_subplot(2,1,1)
data.plot(kind='kde',style='--k',grid=True,title='密度曲线')
plt.axvline(3*std,hold=None,linestyle='--',color='r')
plt.axvline(-3*std,hold=None,linestyle='--',color='r')


#筛选出异常值和正常值
error = data[np.abs(data - u) > 3*std]
data_c = data[np.abs(data - u) <= 3*std]
ax2=fig.add_subplot(2,1,2)
plt.scatter(data_c.index,data_c,alpha=0.3)
plt.scatter(error.index,error,color='r',marker='o',alpha=0.8)

图中能够看出数据服从标准正态分布,且存在两个异常值blog

二、箱型图分析

   箱型图是很是适合作异常值观察的图形,箱型图的五根线分别表示最大值。最小值、上四分位、下四分位和中位数,箱型图的两个重要的概念是内限和外限,盒须的内限,最大值区间:上四分位+1.5IQR,最小值区间:下四分位-1.5IQR (IQR=上四分位-下四分位),在内限以外是中度异常,在外限以外是极度异常。下面之内限为界,查看异常数据

#箱型图
fig = plt.figure(figsize = (10,6))
ax1 = fig.add_subplot(2,1,1)
color = dict(boxes='DarkGreen', whiskers='DarkOrange', medians='DarkBlue', caps='Gray')
data.plot.box(vert=False, grid = True,color = color,ax = ax1,label = '样本数据')


s = data.describe()
print(s)
print('------')
# 基本统计量

q1 = s['25%']
q3 = s['75%']
iqr = q3 - q1
mi = q1 - 1.5*iqr
ma = q3 + 1.5*iqr
print('分位差为:%.3f,下限为:%.3f,上限为:%.3f' % (iqr,mi,ma))
print('------')
# 计算分位差



ax2 = fig.add_subplot(2,1,2)
error = data[(data < mi) | (data > ma)]
data_c = data[(data >= mi) & (data <= ma)]
print('异常值共%i条' % len(error))
# 筛选出异常值error、剔除异常值以后的数据data_c

plt.scatter(data_c.index,data_c,marker='.',alpha = 0.3)
plt.scatter(error.index,error,color = 'r',marker='.',alpha = 0.5)
plt.grid()

    从上图能够看出,该数据上限为2.799,下限为-2.741,异常值共6条,找出异常值以后通常状况下若是异常值很少,能够直接删除,或者当作缺失值处理。

 

2、缺失值

     对于缺失值最简单的处理方法即是删除,但有时不一样字段存在大量不一样的缺失值,处理起来比较麻烦,若是直接删除将会影响分析结果或者建模的准确率。对于特定的数据通常不直接删除,我把经常使用的缺失值插补方法分为两类,取名为单一插补法(均值填充、中位数填充、众数填充、特定值填充、临近值填充等)、插值法(拉格朗日插值法、多重插补法等)

一、均值/中位数/众数/特定值/临近值插补

# 建立数据
s = pd.Series([1,2,3,np.nan,3,4,5,5,5,5,np.nan,np.nan,6,6,7,12,2,np.nan,3,4])


u = s.mean()     # 均值
me = s.median()  # 中位数
mod = s.mode()   # 众数
print('均值为:%.2f, 中位数为:%.2f' % (u,me))
print('众数为:', mod.tolist())
print('------')
# 分别求出均值/中位数/众数

s.fillna(u,inplace = True)
# 用均值填补,可换为中位数、众数或特定的数值等

s1 = pd.Series([1,2,3,np.nan,3,4,5,5,5,5,np.nan,np.nan,6,6,7,12,2,np.nan,3,4])


s1.fillna(method = 'ffill',inplace = True)
# 用前值插补 ,后值为bfill

 

二、拉格朗日插值法

       拉格朗日插值法的数学原理是,平面上任意点能够拟合成下列多项式

      y=a_0+a_1x+a_2x^2+\cdots+a_{n-1}x^{n-1}

      当平面上只有两点时则是最简单的线性关系 ,三点时是二次方,为了根据新的x求出y,须要知道上述公式全部系数,由于n个点在以上多项式上,把n个点的坐标带入,可求出系数,最终求得插值。

from scipy.interpolate import lagrange
x = [3, 6, 9]
y = [10, 8, 4]
print(lagrange(x,y))
print(type(lagrange(x,y)))
# 输出值为的是多项式的n个系数
# 这里输出3个值,分别为a0,a1,a2
# y = a0 * x**2 + a1 * x + a2 → y = -0.11111111 * x**2 + 0.33333333 * x + 10

print('插值10为:%.2f' % lagrange(x,y)(10))

# -0.11111111*100 + 0.33333333*10 + 10 = -11.11111111 + 3.33333333 +10 = 2.22222222
# 插值10为:2.22

下面给出拉格朗日插值法和其余插补方法对比图

data = pd.Series(np.random.rand(100)*100)
data[3,6,33,56,45,66,67,80,90] = np.nan


data_c = data.fillna(data.median())  #  中位数填充缺失值
fig,axes = plt.subplots(1,4,figsize = (20,5))
data.plot.box(ax = axes[0],grid = True,title = '数据分布')
data.plot(kind = 'kde',style = '--r',ax = axes[1],grid = True,title = '删除缺失值',xlim = [-50,150])
data_c.plot(kind = 'kde',style = '--b',ax = axes[2],grid = True,title = '缺失值填充中位数',xlim = [-50,150])
# 密度图查看缺失值状况
def na_c(s,n,k=5):
    y = s[list(range(n-k,n+1+k))] # 取数
    y = y[y.notnull()]  # 剔除空值
    return(lagrange(y.index,list(y))(n))
# 建立函数,作插值,因为数据量缘由,以空值先后5个数据(共10个数据)为例作插值

na_re = []
for i in range(len(data)):
    if data.isnull()[i]:
        data[i] = na_c(data,i)
        print(na_c(data,i))
        na_re.append(data[i])
data.dropna(inplace=True)  # 清除插值后仍存在的缺失值
data.plot(kind = 'kde',style = '--k',grid = True,title = '拉格朗日插值后',xlim = [-50,150])
print('finished!')

     由于该数据缺失值很少,因此直接删除和用拉格朗日插值法插补进去效果差很少,但能够明显看出,用拉格朗日插值法比直接中位数填充拟合的要好。

    总体来讲,缺失值和异常值的处理虽然技术不难,但要具体状况具体分析,如何进行处理就要靠平常积累的数据分析经验,以上,仅供参考。