文章目录
- 5-9 利用list去重
- 5-10 利用set的特性去重
- 5-11 使用drop_duplicates方法对菜品名称去重
- 5-12 使用drop_duplicates方法对多列去重
- 5-13 求出counts和amounts两列数据的kendall法相似度矩阵
- 5-14 求出dishes_name,counts和amounts这3个特征的pearson法相似度矩阵
- 5-15 使用DataFrame.equals方法去重
- 5-16 通过遍历的方式进行数据筛选
- 5-17 isnull和notnull用法
- 5-18 使用dropna方法删除确实值
- 5-19 使用fillna方法替换缺失值
- 5-9 利用list去重
- 5-21 使用3σ原则识别异常值
- 5-22 菜品售价根据箱线图识别异常值
- 5-23 订单详情表的样本去重去特征去重
- 5-24 订单详情表的缺失值检测与处理
- 5-25 订单详情表异常值检测与处理
任务 5.2 清洗数据
5.2.1 检测与处理重复值
import numpy as np
import pandas as pd
from sqlalchemy import create_engine
%%html
<img src = './image/5-2-1.png',width=700,height=400>
<img src = ‘./image/5-2-1.png’,width=700,height=400>
5-9 利用list去重
# 代码 5-9
import pandas as pd
detail = pd.read_csv('../data/detail.csv',
index_col=0,encoding = 'gbk')
##方法一
##定义去重函数
def delRep(list1):
list2=[]
for i in list1:
if i not in list2:
list2.append(i)
return list2
## 去重
dishes=list(detail['dishes_name']) ##将dishes_name从数据框中提取出来
print('去重前菜品总数为:',len(dishes))
dish = delRep(dishes) ##使用自定义的去重函数去重
print('方法一去重后菜品总数为:',len(dish))
去重前菜品总数为: 10037
方法一去重后菜品总数为: 145
5-10 利用set的特性去重
##方法二
print('去重前菜品总数为:',len(dishes))
dish_set = set(dishes) ##利用set的特性去重
print('方法二去重后菜品总数为:',len(dish_set))
去重前菜品总数为: 10037
方法二去重后菜品总数为: 145
%%html
<img src = './image/5-2-2.png',width=700,height=400>
<img src = ‘./image/5-2-2.png’,width=700,height=400>
5-11 使用drop_duplicates方法对菜品名称去重
##方法二
print('去重前菜品总数为:',len(dishes))
dish_set = set(dishes) ##利用set的特性去重
print('方法二去重后菜品总数为:',len(dish_set))
去重前菜品总数为: 10037
方法二去重后菜品总数为: 145
5-12 使用drop_duplicates方法对多列去重
print('去重之前订单详情表的形状为:', detail.shape)
shapeDet = detail.drop_duplicates(subset = ['order_id','emp_id']).shape
print('依照订单编号,会员编号去重之后订单详情表大小为:', shapeDet)
去重之前订单详情表的形状为: (10037, 18)
依照订单编号,会员编号去重之后订单详情表大小为: (942, 18)
%%html
<img src = './image/5-2-3.png',width=700,height=400>
<img src = ‘./image/5-2-3.png’,width=700,height=400>
5-13 求出counts和amounts两列数据的kendall法相似度矩阵
但是通过相似度矩阵去重存在一个弊端,该方法只能对数值型重复特征去重,类别型特征之间无法通过计算相似系数来衡量相似度。
## 求取销量和售价的相似度
corrDet = detail[['counts','amounts']].corr(method='kendall')
print('销量和售价的kendall相似度为:\n',corrDet)
销量和售价的kendall相似度为:
counts amounts
counts 1.000000 -0.229968
amounts -0.229968 1.000000
5-14 求出dishes_name,counts和amounts这3个特征的pearson法相似度矩阵
# 代码 5-14
corrDet1 = detail[['dishes_name','counts',
'amounts']].corr(method='pearson')
print('菜品名称,销量和售价的pearson相似度为:\n',corrDet1)
菜品名称,销量和售价的pearson相似度为:
counts amounts
counts 1.000000 -0.159264
amounts -0.159264 1.000000
除了使用相似度矩阵进行特征去重之外,可以通过DataFrame.equals的方法进行特征去重。
5-15 使用DataFrame.equals方法去重
##定义求取特征是否完全相同的矩阵的函数
def FeatureEquals(df):
dfEquals=pd.DataFrame([],columns=df.columns,index=df.columns)
for i in df.columns:
for j in df.columns:
dfEquals.loc[i,j]=df.loc[:,i].equals(df.loc[:,j])
return dfEquals
## 应用上述函数
detEquals=FeatureEquals(detail)
print('detail的特征相等矩阵的前5行5列为:\n',detEquals.iloc[:5,:5])
detail的特征相等矩阵的前5行5列为:
order_id dishes_id logicprn_name parent_class_name \
order_id True False False False
dishes_id False True False False
logicprn_name False False True True
parent_class_name False False True True
dishes_name False False False False
dishes_name
order_id False
dishes_id False
logicprn_name False
parent_class_name False
dishes_name True
5-16 通过遍历的方式进行数据筛选
##遍历所有数据
lenDet = detEquals.shape[0]
dupCol = []
for k in range(lenDet):
for l in range(k+1,lenDet):
if detEquals.iloc[k,l] & (detEquals.columns[l] not in dupCol):
dupCol.append(detEquals.columns[l])
##进行去重操作
print('需要删除的列为:',dupCol)
detail.drop(dupCol,axis=1,inplace=True)
print('删除多余列后detail的特征数目为:',detail.shape[1])
需要删除的列为: ['parent_class_name', 'cost', 'discount_amt', 'discount_reason', 'kick_back', 'add_info', 'bar_code', 'add_inprice']
删除多余列后detail的特征数目为: 10
5.2.2 检测与处理缺失值
数据中的某个或某些特征的值是不完整的,这些值称为缺失值。
5-17 isnull和notnull用法
print('detail每个特征缺失的数目为:\n',detail.isnull().sum())
print('detail每个特征非缺失的数目为:\n',detail.notnull().sum())
detail每个特征缺失的数目为:
order_id 0
dishes_id 0
logicprn_name 10037
dishes_name 0
itemis_add 0
counts 0
amounts 0
place_order_time 0
picture_file 0
emp_id 0
dtype: int64
detail每个特征非缺失的数目为:
order_id 10037
dishes_id 10037
logicprn_name 0
dishes_name 10037
itemis_add 10037
counts 10037
amounts 10037
place_order_time 10037
picture_file 10037
emp_id 10037
dtype: int64
结合sum函数和isnull、notnull函数,可以检测数据中缺失值的分布以及数据中一共含有多少缺失值。
isnull和notnull之间结果正好相反,因此使用其中任意一个都可以判断出数据中缺失值的位置。
%%html
<img src = './image/5-2-4.png',width=700,height=400>
<img src = ‘./image/5-2-4.png’,width=700,height=400>
5-18 使用dropna方法删除确实值
print('去除缺失的列前detail的形状为:', detail.shape)
print('去除缺失的列后detail的形状为:',
detail.dropna(axis = 1,how ='any').shape)
去除缺失的列前detail的形状为: (10037, 10)
去除缺失的列后detail的形状为: (10037, 9)
5-19 使用fillna方法替换缺失值
替换法是指用一个特定的值替换缺失值。
特征可分为数值型和类别型,两者出现缺失值时的处理方法也是不同的。
缺失值所在特征为数值型时,通常利用其均值、中位数和众数等描述其集中趋势的统计量来代替缺失值。
缺失值所在特征为类别型时,则选择使用众数来替换缺失值。
%%html
<img src = './image/5-2-5.png',width=700,height=400>
<img src = ‘./image/5-2-5.png’,width=700,height=400>
detail = detail.fillna(-99)
print('detail每个特征缺失的数目为:\n',detail.isnull().sum())
detail每个特征缺失的数目为:
order_id 0
dishes_id 0
logicprn_name 0
dishes_name 0
itemis_add 0
counts 0
amounts 0
place_order_time 0
picture_file 0
emp_id 0
dtype: int64
5-9 利用list去重
3. 插值法
删除法简单易行,但是会引起数据结构变动,样本减少;替换法使用难度较低,但是会影响数据的标准差,导致信息量变动。在面对数据缺失问题时,除了这两种方法之外,还有一种常用的方法—插值法。
常用的插值法有线性插值、多项式插值和样条插值等:
线性插值是一种较为简单的插值方法,它针对已知的值求出线性方程,通过求解线性方程得到缺失值。
多项式插值是利用已知的值拟合一个多项式,使得现有的数据满足这个多项式,再利用这个多项式求解缺失值,常见的多项式插值法有拉格朗日插值和牛顿插值等。
样条插值是以可变样条来作出一条经过一系列点的光滑曲线的插值方法,插值样条由一些多项式组成,每一个多项式都是由相邻两个数据点决定,这样可以保证两个相邻多项式及其导数在连接处连续。
## 线性插值
from scipy.interpolate import interp1d
x=np.array([1,2,3,4,5,8,9,10]) ##创建自变量x
y1=np.array([2,8,18,32,50,128,162,200]) ##创建因变量y1
y2=np.array([3,5,7,9,11,17,19,21]) ##创建因变量y2
LinearInsValue1 = interp1d(x,y1,kind='linear') ##线性插值拟合x,y1
LinearInsValue2 = interp1d(x,y2,kind='linear') ##线性插值拟合x,y2
print('当x为6、7时,使用线性插值y1为:',LinearInsValue1([6,7]))
print('当x为6、7时,使用线性插值y2为:',LinearInsValue2([6,7]))
当x为6、7时,使用线性插值y1为: [ 76. 102.]
当x为6、7时,使用线性插值y2为: [13. 15.]
## 拉格朗日插值
from scipy.interpolate import lagrange
LargeInsValue1 = lagrange(x,y1) ##拉格朗日插值拟合x,y1
LargeInsValue2 = lagrange(x,y2) ##拉格朗日插值拟合x,y2
print('当x为6,7时,使用拉格朗日插值y1为:',LargeInsValue1([6,7]))
print('当x为6,7时,使用拉格朗日插值y2为:',LargeInsValue2([6,7]))
当x为6,7时,使用拉格朗日插值y1为: [72. 98.]
当x为6,7时,使用拉格朗日插值y2为: [13. 15.]
##样条插值
import scipy.interpolate as spi
##样条插值拟合x,y1
ipo1 = spi.splrep(x,y1,k=1) #样本点导入,生成参数
SplineInsValue1 = spi.splev([6,7],ipo1)
##样条插值拟合x,y2
ipo2 = spi.splrep(x,y2,k=1) #样本点导入,生成参数
SplineInsValue2 = spi.splev([6,7],ipo2)
print('当x为6,7时,使用样条插值y1为:',SplineInsValue1)
print('当x为6,7时,使用样条插值y2为:',SplineInsValue2)
当x为6,7时,使用样条插值y1为: [ 76. 102.]
当x为6,7时,使用样条插值y2为: [13. 15.]
从拟合结果可以看出多项式插值和样条插值在两种情况下拟合都非常出色,线性插值法只在自变量和因变量为线性关系的情况下拟合才较为出色。
而在实际分析过程中,自变量与因变量的关系是线性的情况非常少见,所以在大多数情况下,多项式插值和样条插值是较为合适的选择。
SciPy库中的interpolate模块除了提供常规的插值法外,还提供了例如在图形学领域具有重要作用的重心坐标插值(BarycentricInterpolator)等。在实际应用中,需要根据不同的场景,选择合适的插值方法
5.2.3 检测与处理异常值
异常值是指数据中个别值的数值明显偏离其余的数值,有时也称为离群点,检测异常值就是检验数据中是否有录入错误以及是否含有不合理的数据。
异常值的存在对数据分析十分危险,如果计算分析过程的数据有异常值,那么会对结果会产生不良影响,从而导致分析结果产生偏差乃至错误。
常用的异常值检测主要为3σ原则和箱线图分析两种方法。
%%html
<img src = './image/5-3-1.png',width=700,height=400>
<img src = ‘./image/5-3-1.png’,width=700,height=400>
5-21 使用3σ原则识别异常值
## 定义3σ拉依达准则识别异常值函数
def outRange(Ser1):
boolInd = (Ser1.mean()-3*Ser1.std()>Ser1) | \
(Ser1.mean()+3*Ser1.var()< Ser1)
index = np.arange(Ser1.shape[0])[boolInd]
outrange = Ser1.iloc[index]
return outrange
outlier = outRange(detail['counts'])
print('使用拉依达准则判定异常值个数为:',outlier.shape[0])
print('异常值的最大值为:',outlier.max())
print('异常值的最小值为:',outlier.min())
使用拉依达准则判定异常值个数为: 209
异常值的最大值为: 10
异常值的最小值为: 3
5-22 菜品售价根据箱线图识别异常值
2.箱线图分析
箱型图提供了识别异常值的一个标准,即异常值通常被定义为小于QL-1.5IQR或大于QU+1.5IQR的值。
QL称为下四分位数,表示全部观察值中有四分之一的数据取值比它小。
QU称为上四分位数,表示全部观察值中有四分之一的数据取值比它大。
IQR称为四分位数间距,是上四分位数QU与下四分位数QL之差,其间包含了全部观察值的一半。
箱线图依据实际数据绘制,真实、直观地表现出了数据分布的本来面貌,且没有对数据做任何限制性要求,其判断异常值的标准以四分位数和四分位数间距为基础。
四分位数给出了数据分布的中心、散布和形状的某种指示,具有一定的鲁棒性,即25%的数据可以变得任意远而不会很大地扰动四分位数,所以异常值通常不能对这个标准施加影响。鉴于此,箱线图识别异常值的结果比较客观,因此在识别异常值方面具有一定的优越性。
import matplotlib.pyplot as plt
plt.figure(figsize=(10,8))
p = plt.boxplot(detail['counts'].values,notch=True) ##画出箱线图
outlier1 = p['fliers'][0].get_ydata() ##fliers为异常值的标签
plt.savefig('../tmp/菜品异常数据识别.png')
plt.show()
print('销售量数据异常值个数为:',len(outlier1))
print('销售量数据异常值的最大值为:',max(outlier1))
print('销售量数据异常值的最小值为:',min(outlier1))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cHDCMvad-1608192562686)(output_43_0.png)]
销售量数据异常值个数为: 516
销售量数据异常值的最大值为: 10
销售量数据异常值的最小值为: 2
5.2.4 任务实现
5-23 订单详情表的样本去重去特征去重
detail = pd.read_csv('../data/detail.csv',
index_col=0,encoding = 'gbk')
print('进行去重操作前订单详情表的形状为:',detail.shape)
##样本去重
detail.drop_duplicates(inplace = True)
##特征去重
def FeatureEquals(df):
##定义求取特征是否完全相同的矩阵的函数
dfEquals=pd.DataFrame([],columns=df.columns,index=df.columns)
for i in df.columns:
for j in df.columns:
dfEquals.loc[i,j]=df.loc[:,i].equals(df.loc[:,j])
return dfEquals
detEquals=FeatureEquals(detail)## 应用上述函数
##遍历所有数据
lenDet = detEquals.shape[0]
dupCol = []
for k in range(lenDet):
for l in range(k+1,lenDet):
if detEquals.iloc[k,l] & (detEquals.columns[l] not in dupCol):
dupCol.append(detEquals.columns[l])
##删除重复列
detail.drop(dupCol,axis=1,inplace=True)
print('进行去重操作后订单详情表的形状为:',detail.shape)
进行去重操作前订单详情表的形状为: (10037, 18)
进行去重操作后订单详情表的形状为: (10037, 10)
5-24 订单详情表的缺失值检测与处理
##统计各个特征的缺失率
naRate = (detail.isnull().sum()/ \
detail.shape[0]*100).astype('str')+'%'
print('detail每个特征缺失的率为:\n',naRate)
##删除全部均为缺失的列
detail.dropna(axis = 1,how = 'all',inplace = True)
print('经过缺失值处理后订单详情表各特征缺失值的数目为:\n',
detail.isnull().sum())
detail每个特征缺失的率为:
order_id 0.0%
dishes_id 0.0%
logicprn_name 100.0%
dishes_name 0.0%
itemis_add 0.0%
counts 0.0%
amounts 0.0%
place_order_time 0.0%
picture_file 0.0%
emp_id 0.0%
dtype: object
经过缺失值处理后订单详情表各特征缺失值的数目为:
order_id 0
dishes_id 0
dishes_name 0
itemis_add 0
counts 0
amounts 0
place_order_time 0
picture_file 0
emp_id 0
dtype: int64
5-25 订单详情表异常值检测与处理
def outRange(Ser1):
QL = Ser1.quantile(0.25)
QU = Ser1.quantile(0.75)
IQR = QU-QL
Ser1.loc[Ser1>(QU+1.5*IQR)] = QU
Ser1.loc[Ser1<(QL-1.5*IQR)] = QL
return Ser1
## 处理销售量和售价的异常值
detail['counts'] = outRange(detail['counts'])
detail['amounts'] = outRange(detail['amounts'])
##查看处理后的销售量和售价的最小值,最大值
print('销售量最小值为:', detail['counts'].min())
print('销售量最大值为:', detail['counts'].max())
print('售价最小值为:', detail['amounts'].min())
print('售价最大值为:', detail['amounts'].max())
销售量最小值为: 1.0
销售量最大值为: 1.0
售价最小值为: 1.0
售价最大值为: 99.0
D:\Study\anaconda\lib\site-packages\pandas\core\indexing.py:670: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
self._setitem_with_indexer(indexer, value)