pandas--数据预处理

合并数据

堆叠合并数据

横向堆叠

  • 将两个表在x轴向拼接在一起。语法:pandas.concat(objs,axis=0,join_axes=None,ignore_index=False,keys=None,levels=None,names=None,verify_integrity=False,copy=True)
参数名称说明
objs接收多个Series、DataFrame、Panel的组合。表示参与连接的pandas对象的列表的组合。无默认
axis接收0或1。表示连接的轴向,默认为0
join接收inner或outer。表示其他轴向上的索引是按交集还是并集进行合并。默认为outer
join_axes接收Index对象。表示用于其他n-1条轴上的索引,不执行并集/交集运算
ignore_index接收boolean。表示是否不保留连接轴上的索引,产生一组新索引range(total_length)。默认为False
keys接收sequence。表示与连接对象有关的值,用于形成连接轴向上的层次化索引。默认为None
levels接收包含多个sequence的list。表示在指定keys参数后,指定用作层次化索引各级别上的索引。默认为None
names接收list。表示在设置了keys和levels参数后,用于创建分层级别的名称。默认为None
verify_integrity接收boolean。检查新连接的轴是否包含重复项。如果发现重复项,则引发异常。默认为False

在内连接的情况下,仅仅返回索引重叠部分;在外连接的情况下,则显示索引的并集部分数据,不足的地方使用空值填补。

  • 索引完全相同时的横向堆叠
import numpy as np
import pandas as pd
from sqlalchemy import create_engine
conn = create_engine('mysql+pymysql://root:1234@\
127.0.0.1:3306/testdb?charset=utf8')
detail1 = pd.read_sql('meal_order_detail1',conn)
df1 = detail1.iloc[:,:10] ##取出detail1的前10列数据
df2 = detail1.iloc[:,10:] ##取出detail1的后9列数据
print('合并df1的大小为%s,df2的大小为%s。'%(df1.shape,df2.shape))
print('外连接合并后的数据框大小为:',pd.concat([df1,df2],
        axis=1,join='inner').shape)
print('内连接合并后的数据框大小为:',pd.concat([df1,df2],
        axis=1,join='outer').shape)
合并df1的大小为(2779, 10),df2的大小为(2779, 9)。
外连接合并后的数据框大小为: (2779, 19)
内连接合并后的数据框大小为: (2779, 19)

纵向堆叠

  • 将两个数据表在y轴向上拼接。
  1. concat函数:axis=0
  2. append方法:两张表的列名需要完全一致!!!语法:pandas.DateFrame.append(self,other,ignore_index=False,verify_integrity=False)
参数名称说明
other接收DataFrame或Series。表示要添加的新数据。无默认
ignore_index接收boolean。如果输入True,就会对新生成的DataFrame使用新的索引(自动产生),默认为False
verify_integrity接收boolean。如果输入True,那么当ignore_index为False时,会检查添加的数据索引是否冲突,如果冲突,则会添加失败。默认为False
  • 表名完全相同时的concat纵向堆叠
df3 = detail1.iloc[:1500,:] ##取出detail1前1500行数据
df4 = detail1.iloc[1500:,:] ##取出detail1的1500后的数据
print('合并df3的大小为%s,df4的大小为%s。'%(df3.shape,df4.shape))
print('内连接纵向合并后的数据框大小为:',pd.concat([df3,df4],
        axis=1,join='inner').shape)
print('外连接纵向合并后的数据框大小为:',pd.concat([df3,df4],
        axis=1,join='outer').shape)
合并df3的大小为(1500, 19),df4的大小为(1279, 19)。
内连接纵向合并后的数据框大小为: (0, 38)
外连接纵向合并后的数据框大小为: (2779, 38)
  • 使用append方法进行纵向表堆叠
print('堆叠前df3的大小为%s,df4的大小为%s。'%(df3.shape,df4.shape))
print('append纵向堆叠后的数据框大小为:',df3.append(df4).shape)
堆叠前df3的大小为(1500, 19),df4的大小为(1279, 19)。
append纵向堆叠后的数据框大小为: (2779, 19)

主键合并数据

  • 通过一个或多个键将两个数据集的行连接起来
  1. merge函数:可以左连接、内连接、外连接语法:pandas.merge(left,right,how='inner',on=None,left_on=None,right_on=None,left_index=False,right_index=False,sort=False,suffixes=('_x','_y'),copy=True,indicator=False)
  2. join方法:两个主键的名字必需相同。语法:pandas.DataFrame.join(self,other,on=None,how='left',lsuffix='',rsuffix='',sort=False)

merge函数的参数及其说明

参数名称说明
left接收DataFrame或Series。表示要添加的新数据1。无默认
right接收DataFrame或Series。表示要添加的新数据2。无默认
how接收inner、outer、left、right。表示数据的连接方式。默认为inner
on接收string或sequence。表示两个数据合并的主键(必须一致)。默认为None
left_on接收string或sequence。表示left参数接收数据用于合并的主键。默认为None
right_on接收string或sequence。表示right参数接收数据用于合并的主键。默认为None
sort接收boolean。表示是否根据连接键对合并后的数据进行排序。默认为False
suffixes接收tuple。表示用于追加到left和right参数接收数据列名相同时的后缀。默认为(‘_x’,‘_y’)

join方法的参数及其说明

参数名称说明
other接收DataFrame、Series或者包含了多个DataFrame的list。表示参与李连杰的其他DataFrame。无默认
on接收列名或者包含列名的list或tuple。表示用于连接的列名。默认为None
how接收特定string。取值为”inner“时代表内连接;取值为”outer“时代表外连接;取值为”left“时代表左连接;取值为”right“时代表右连接。默认为”inner“
lsuffix接收string。表示用于追加到左侧重叠列名的尾缀。无默认
rsuffix接收string。表示用于追加到右侧重叠列名的尾缀。无默认
sort接收boolean。根据连接键对合并后的数据进行排序,默认为False
  • 使用merge函数合并数据表
order = pd.read_csv('./data/meal_order_info.csv',sep=',',encoding='gb18030') ##读取订单信息表
##info_id转换为字符串格式,为合并做准备
order['info_id'] = order['info_id'].astype('str') 
## 订单详情表和订单信息表都有订单编号
##在订单详情表中为order_id,在订单信息表中为info_id
order_detail = pd.merge(detail1,order,
        left_on='order_id',right_on = 'info_id')
print('detail1订单详情表的原始形状为:',detail1.shape)
print('order订单信息表的原始形状为:',order.shape)
print('订单详情表和订单信息表主键合并后的形状为:',order_detail.shape)
detail1订单详情表的原始形状为: (2779, 19)
order订单信息表的原始形状为: (945, 21)
订单详情表和订单信息表主键合并后的形状为: (2779, 40)
  • 使用join方法实现主键合并
order.rename({'info_id':'order_id'},inplace=True)
order_detail1 = detail1.join(order,on='order_id',rsuffix='1')
print('订单详情表和订单信息表join合并后的形状为:',order_detail1.shape)
订单详情表和订单信息表join合并后的形状为: (2779, 40)

重复合并数据

  • 当两张数据表内容基本一致,但是各有些缺失时,可以采用combine_first进行重叠数据合并。语法:pandas.DataFrame.combine_first(other)

combine_first方法的参数及其说明

参数名称说明
other接收DataFrame。表示参与重叠合并的另一个DataFrame。无默认
  • 重叠合并
#建立两个字典,除了ID外,别的特征互补
dict1 = {'ID':[1,2,3,4,5,6,7,8,9],
         'System':['win10','win10',np.nan,'win10',
                np.nan,np.nan,'win7','win7','win8'],
      'cpu':['i7','i5',np.nan,'i7',np.nan,np.nan,'i5','i5','i3']}

dict2 = {'ID':[1,2,3,4,5,6,7,8,9],
         'System':[np.nan,np.nan,'win7',np.nan,
                'win8','win7',np.nan,np.nan,np.nan],
        'cpu':[np.nan,np.nan,'i3',np.nan,'i7',
                'i5',np.nan,np.nan,np.nan]}
## 转换两个字典为DataFrame
df5 = pd.DataFrame(dict1)
df6 = pd.DataFrame(dict2)
print('经过重叠合并后的数据为:\n',df5.combine_first(df6))
经过重叠合并后的数据为:
    ID System cpu
0   1  win10  i7
1   2  win10  i5
2   3   win7  i3
3   4  win10  i7
4   5   win8  i7
5   6   win7  i5
6   7   win7  i5
7   8   win7  i5
8   9   win8  i3

任务实现

  • 堆叠不同时间的订单详情表
import numpy as np
import pandas as pd
from sqlalchemy import create_engine
## 创建数据库连接
conn = create_engine('mysql+pymysql://root:1234@\
127.0.0.1:3306/testdb?charset=utf8')
## 读取数据
detail1 = pd.read_sql('meal_order_detail1',conn)
detail2 = pd.read_sql('meal_order_detail2',conn)
detail3 = pd.read_sql('meal_order_detail3',conn)
## 纵向堆叠三张表
detail = detail1.append(detail2)
detail = detail.append(detail3)
print('三张订单详情表合并后的形状为:', detail.shape)
三张订单详情表合并后的形状为: (10037, 19)
  • 主键合并订单详情表、订单信息表和客户信息表
order = pd.read_csv('./data/meal_order_info.csv',sep=',',encoding='gb18030') ##读取订单信息表
user = pd.read_excel('./data/users_info.xlsx') ##读取用户信息表
## 数据类型转换,存储部分数据
order['info_id'] = order['info_id'].astype('str')
order['emp_id'] = order['emp_id'].astype('str')
user['USER_ID'] = user['USER_ID'].astype('str')
data = pd.merge(detail,order,left_on=['order_id','emp_id'],
        right_on = ['info_id','emp_id'])
data = pd.merge(data,user,left_on='emp_id',
        right_on = 'USER_ID',how = 'inner')
print('三张表数据主键合并后的大小为:',data.shape)
三张表数据主键合并后的大小为: (14713, 76)

清洗数据

检测和处理重复值

记录重复数据

  • 一个或者多个特征的某几条记录的值完全相同
  1. 利用列表list去重
  2. 利用集合set原色的唯一特性去重
  3. 使用drop_duplicates方法去重。语法:pandas.DataFrame(Series).drop_duplicates(self,subset=None,keep='first',inplace=False);不仅支持单一特征的数据去重,还能够依据DataFrame的其中一个或者几个特征进行去重操作。

drop_duplicates方法的参数及其说明

参数名称说明
subset接收string或sequence。表示进行去重的列。默认为None,表示全部列
keep接收特定string。表示重复时保留第几个数据 ”first“:保留第一个;“last”:保留最后一个;False:只要有重复都不保留;默认为“first”
inplace接收boolean。表示是否在原表上进行操作。默认为False
  • 利用list去重
import pandas as pd
detail = pd.read_csv('./data/detail.csv',index_col=0,encoding = 'utf-8')

#方法一
#定义去重函数
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
方法一去重后菜品总数为: 157
  • 利用set的特性去重
print('去重前菜品总数为:',len(dishes)) 
dish_set = set(dishes) ##利用set的特性去重
print('方法二去重后菜品总数为:',len(dish_set))
去重前菜品总数为: 10037
方法二去重后菜品总数为: 157
  • 使用drop_duplicates方法对菜品名称去重
dishes_name = detail['dishes_name'].drop_duplicates()
print('drop_duplicates方法去重之后菜品总数为:',len(dishes_name))
drop_duplicates方法去重之后菜品总数为: 157
  • 使用drop_duplicates方法对多列去重
print('去重之前订单详情表的形状为:', detail.shape)
shapeDet = detail.drop_duplicates(subset = ['order_id',
    'emp_id']).shape
print('依照订单编号,会员编号去重之后订单详情表大小为:', shapeDet)
print('去重之前订单详情表的形状为:', detail.shape)
shapeDet = detail.drop_duplicates(subset = ['order_id',
    'emp_id']).shape
print('依照订单编号,会员编号去重之后订单详情表大小为:', shapeDet)

特征重复

  • 一个或者多个特征名称不同,但是数据完全相同
  1. 利用特征间的相似度将两个相似度为1的特征取出一个。相似度的计算方法为corr,默认使用pearson法,可以通过method参数调节。该方法只能对数值型重读特征去重
  2. DataFrame.equals方法
  • 求出counts和amounts两列数据的kendall法相似度矩阵
# 求取销量和售价的相似度
corrDet = detail[['counts','amounts']].corr(method='kendall')
print('销量和售价的kendall相似度为:\n',corrDet)
# 求取销量和售价的相似度
corrDet = detail[['counts','amounts']].corr(method='kendall')
print('销量和售价的kendall相似度为:\n',corrDet)
  • 求出dishes_name、counts和amounts这3个特征的pearson法相似度矩阵
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方法去重
#定义求取特征是否完全相同的矩阵的函数
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列为:
                   detail_id order_id dishes_id logicprn_name parent_class_name
detail_id              True    False     False         False             False
order_id              False     True     False         False             False
dishes_id             False    False      True         False             False
logicprn_name         False    False     False          True              True
parent_class_name     False    False     False          True              True
  • 通过遍历的方式进行数据删选
#遍历所有数据
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的特征数目为: 11

检测与处理缺失值

  • 缺失值:数据中的某个或者某些特征的值是不完整的。isnull:识别缺失值的方法;notnull:识别非缺失值的方法。
  • isnull和notnull用法
print('detail每个特征缺失的数目为:\n',detail.isnull().sum())
print('detail每个特征非缺失的数目为:\n',detail.notnull().sum())
detail每个特征缺失的数目为:
 detail_id               0
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每个特征非缺失的数目为:
 detail_id           10037
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

删除法

  • 删除观测记录、删除特征,通过减少样本量来换取信息完整度的一张方法,一种最简单的缺失值处理。语法:pandas,DataFrame.dropna(self,axis=0,how='any',thresh=None,subset=None,inlace=False)

dropna方法的参数及其说明

参数名称说明
axis接收0或1.表示轴向,0为删除观测记录(行),1为删除特征(列)。默认为0
how接收特定string。表示删除的形式。取值为“any”时表示只要有缺失值存在就执行删除操作;取值为“all”时表示当且仅当全部为缺失值时才执行删除操作。默认为“any”
subset接收array。表示进行去重的列/行。默认为None,表示所有列/行
inplace接收boolean。表示是否在原表上进行操作。默认为False

当how参数为any时,删除了一个特征,说明这个特征存在缺失值。若how参数不取any这个默认值,而是取all,则表示整个特征全部为缺失值时才会执行删除操作。

  • 使用dropna方法删除缺失值
print('去除缺失的列前detail的形状为:', detail.shape)
print('去除缺失的列后detail的形状为:',
    detail.dropna(axis = 1,how ='any').shape)
去除缺失的列前detail的形状为: (10037, 11)
去除缺失的列后detail的形状为: (10037, 10)

替换法

  • 用一个特定的值替换缺失值。缺失值为数值型时,通常利用其均值、中位数和众数等描述其集中趋势的统计量来代替缺失值;类别型,选择使用众数来替换缺失值。语法:pandas.DataFrame.fillna(value=None,method=None,axis=None,inplace=False,limit=None)

fillna方法的参数及其说明

参数名称说明
value接收scalar、dict、Series或者DataFrame。表示用来替换缺失值的值。无默认
method接收指定string,取值为”backfill“或”bfill“时表示使用下一个非缺失值来填补缺失值,取值为”pad“或”ffill“时表示使用上一个非缺失值来填补缺失值。默认为None
axis接收0或1.表示轴向。默认为1
inplace接收boolean。表示是否在原表上进行操作。默认为False
limit接收int。表示填补缺失值个数上限,超过则不进行填补。默认为None
  • 使用fillna方法替换缺失值
detail = detail.fillna(-99)
print('detail每个特征缺失的数目为:\n',detail.isnull().sum())
detail每个特征缺失的数目为:
 detail_id           0
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

插值法

  • 删除法简单易行,但是会引起数据结构变动,样本减少;替换法使用难度较低,但是会影响数据的标准差,导致信息量变动。常见的插值法有线性插值、多项式插值和样条插值等。线性插值是一种较为简单的插值方法,针对已知的值求出线性方程,通过求解线性方程得到缺失值。多项式插值是利用已知的值拟合一个多项式,使得现有的数据满足这个多项式,再利用这个多项式求解缺失值,常见的多项式插值有拉格朗日插值和牛顿插值等。氧化铁插值是以可变样条来做出一条经过一系列点的光滑曲线的插值方法。插值样条由一些多项式组成,每一个多项式都由相邻的两个数据点决定,可以保证两个相邻多项式及其倒数在连接处连续。
# 线性插值
import numpy as np
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]))

# 拉格朗日插值
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]))

# 样条插值
from scipy.interpolate import make_interp_spline
# 样条插值拟合x,y1
SplineInsValue1 = make_interp_spline(x,y1)(np.array([6,7]))
# 样条插值拟合x,y2
SplineInsValue2 = make_interp_spline(x,y2)(np.array([6,7]))
print('当x为6,7时,使用样条插值y1为:',SplineInsValue1)
print('当x为6,7时,使用样条插值y2为:',SplineInsValue2)
当x为6、7时,使用线性插值y1为: [ 76. 102.]
当x为6、7时,使用线性插值y2为: [13. 15.]
当x为6,7时,使用拉格朗日插值y1为: [72. 98.]
当x为6,7时,使用拉格朗日插值y2为: [13. 15.]
当x为6,7时,使用样条插值y1为: [72. 98.]
当x为6,7时,使用样条插值y2为: [13. 15.]

x、y1:y1=2x^2
x、y2:y2=2x+1
冲你和结果可以看出,多样式插值和样条插值在两种情况下的你和都非常出色,线性插值法旨在自变量和因变量为线性关系的情况下你和才较为出色。但是在实际情况中,自变量和因变量的关系是线性的情况非常少见,所以,在大多数情况下,多项式插值和样条插值是较为合是的选择。

检测与处理异常值

  • 异常值:数据中个别值的数值明显偏离其余的数值,检测异常就是检验数据中是否由输入错误以及是否含有不合理的数据。555

3σ原则–拉依达原则

  • 先假设一组检测数据只含有随机误差,对原始数据进行计算处理得到标准差,然后按一定的概率确定一个区间,认为误差超过这个区间就属于异常,仅适用于对正态或者近似正态分布的样本数据进行处理。

正态分布数据的拉依达原则

数值分布在数据中的占比
(μ-σ,μ+σ)0.6827
(μ-2σ,μ+2σ)0.9545
(μ-3σ,μ+3σ)0.9973

根据小概率事件,可以认为超出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
detail['counts']=detail['counts'].astype(int)
outlier = outRange(detail['counts'])
print('使用拉依达准则判定异常值个数为:',outlier.shape[0])
print('异常值的最大值为:',outlier.max())
print('异常值的最小值为:',outlier.min())
使用拉依达准则判定异常值个数为: 209
异常值的最大值为: 10
异常值的最小值为: 3

箱线图分析

  • 标准:小于 下四分位数QL-1.5四分位数间距IQR 或 大于上四分位数QU+0.5四分位数间距IQR 为异常值。箱线图依据实际数据绘制,真实、直观地表现除了数据分布的本来面貌。
  • 菜品售价根据箱线图识别异常值
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('./菜品异常数据识别.png')
plt.show()
print('销售量数据异常值个数为:',len(outlier1))
print('销售量数据异常值的最大值为:',max(outlier1))
print('销售量数据异常值的最小值为:',min(outlier1))

在这里插入图片描述

销售量数据异常值个数为: 516
销售量数据异常值的最大值为: 10
销售量数据异常值的最小值为: 2

任务实现

  • 对订单详情表进行去重
import pandas as pd
detail = pd.read_csv('./data/detail.csv',index_col=0,encoding = 'utf-8')
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)
  • 处理订单详情表缺失值
# 统计各个特征的缺失率
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每个特征缺失的率为:
 detail_id           0.0%
order_id            0.0%
dishes_id           0.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
经过缺失值处理后订单详情表各特征缺失值的数目为:
 detail_id           0
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
  • 处理菜品销售数据异常值
# 定义异常值识别与处理函数
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

标准化数据

不同特征之间往往具有不同的量纲,由此所造成的数值间的差异可能很大,在设计空间距离计算或梯度下降法等情况时,不对其进行处理会影响到数据分析结果的准确性。所以要进行标准化处理,也就是规范化处理。

离差标准化数据

  • 对原始数据进行线性变换,结果时将原始的数值映射到[0,1]区间。公式为x=(X-min)/(max-min)离差标准化保留了初始数据值之间的联系,时消除量纲和数据取值范围影响最简单的方法。
  • 缺点:若数据集中的某个数值很大,则离差标准化的值就会接近于0,并且相互之间差别不大。
  • 离差标准化
import pandas as pd
import numpy as np
detail = pd.read_csv('./data/detail.csv',index_col=0,encoding = 'utf-8')
# 自定义离差标准化函数
def MinMaxScale(data):
    data=(data-data.min())/(data.max()-data.min())
    return data
# 对菜品订单表售价和销量做离差标准化
data1=MinMaxScale(detail['counts'])
data2=MinMaxScale(detail ['amounts'])
data3=pd.concat([data1,data2],axis=1)
print('离差标准化之前销量和售价数据为:\n',
    detail[['counts','amounts']].head())
print('离差标准化之后销量和售价数据为:\n',data3.head())
离差标准化之前销量和售价数据为:
    counts  amounts
0       1       49
1       1       48
2       1       30
3       1       25
4       1       13
离差标准化之后销量和售价数据为:
    counts   amounts
0     0.0  0.271186
1     0.0  0.265537
2     0.0  0.163842
3     0.0  0.135593
4     0.0  0.067797

标准差标准化数据

  • 零均值标准化或z分数标准化,经过该方法处理的数据均值为0,标准差为1。公式:x=(X-X1)/YX1:原始数据的均值;Y:数据的标准差
  • 标准差标准化
# 自定义标准差标准化函数
def StandardScaler(data):
    data=(data-data.mean())/data.std()
    return data
# 对菜品订单表售价和销量做标准化
data4=StandardScaler(detail['counts'])
data5=StandardScaler(detail['amounts'])
data6=pd.concat([data4,data5],axis=1)
print('标准差标准化之前销量和售价数据为:\n',
    detail[['counts','amounts']].head())
print('标准差标准化之后销量和售价数据为:\n',data6.head())
标准差标准化之前销量和售价数据为:
    counts  amounts
0       1       49
1       1       48
2       1       30
3       1       25
4       1       13
标准差标准化之后销量和售价数据为:
      counts   amounts
0 -0.177571  0.116671
1 -0.177571  0.088751
2 -0.177571 -0.413826
3 -0.177571 -0.553431
4 -0.177571 -0.888482

标准差标准化和离差标准化一样不会改变数据的分布情况。

小数定标标准化数据

  • 通过移动数据的小数位数,将数据映射到区间[-1,1],移动的小鼠位数取决于数据绝对值的最大值。公式:x=X/10^k
  • 小鼠定标标准化
# 自定义小数定标差标准化函数
def DecimalScaler(data):
    data=data/10**np.ceil(np.log10(data.abs().max()))
    return data
# 对菜品订单表售价和销量做标准化
data7=DecimalScaler(detail['counts'])
data8=DecimalScaler(detail['amounts'])
data9=pd.concat([data7,data8],axis=1)
print('小数定标标准化之前的销量和售价数据:\n',
    detail[['counts','amounts']].head())
print('小数定标标准化之后的销量和售价数据:\n',data9.head())
小数定标标准化之前的销量和售价数据:
    counts  amounts
0       1       49
1       1       48
2       1       30
3       1       25
4       1       13
小数定标标准化之后的销量和售价数据:
    counts  amounts
0     0.1    0.049
1     0.1    0.048
2     0.1    0.030
3     0.1    0.025
4     0.1    0.013
  • 3种标准化方法各有其优势。离差标准化方法简单,便于理解,标准化后的数据限定在[0,1]区间内。标准差标准化受数据限定在[0,1]区间内。标准差标准化受数据分布的影响较小。小数定标标准化方法的适用范围非常广,并且受数据分布的影响较小,相比较于前两种方法,该方法适用程度适中。

任务实现

  • 对订单详情表中的数值型数据做标准差标准化
# 自定义标准差标准化函数
def StandardScaler(data):
    data=(data-data.mean())/data.std()
    return data
# 对菜品订单表售价和销量做标准化
data4=StandardScaler(detail['counts'])
data5=StandardScaler(detail['amounts'])
data6=pd.concat([data4,data5],axis = 1)
print('标准差标准化之后销量和销量数据为:','\n',data6.head(10))
标准差标准化之后销量和销量数据为: 
      counts   amounts
0 -0.177571  0.116671
1 -0.177571  0.088751
2 -0.177571 -0.413826
3 -0.177571 -0.553431
4 -0.177571 -0.888482
5 -0.177571  1.205587
6 -0.177571  0.284197
7 -0.177571  1.205587
8 -0.177571  0.088751
9 -0.177571 -0.357984

转换数据

  • 数据经过了清洗、合并和标准化,依旧不能直接拿来做分析建模,所以还需要对数据进行变换。

哑变量处理类别型数据

  • 有一些算法模型需要输入的特征为数值型,但是实际数据中,不一定只有数值型,还有一部分的类比性,这部分的特征要经过哑变量处理才可以放入模型之中。变成哑变量也就是将数据变成了稀疏矩阵,加快了算法模型的运算速度。语法:pandas.get_dummies(data,prefix=None,prefix_sep='_',dummy_na=False,columns=None,sparse=False,drop_first=False)

get_dummies函数的参数及说明

参数名称说明
data接收array、DataFrame或者Series。表示需要哑变量处理的数据。无默认
prefix接收string、string的列表或者string的dict。表示哑变量化后列名的前缀。默认为None
prefix_sep接收string。表示前缀的连接符。默认为‘_’
dummy_na接收boolean。表示是否为NaN值添加一列。默认为False
columns接收类似list的数据。表示DataFrame中需要编码的列名。默认为None,表示对所有的object和category类型进行编码
sparse接收boolean。表示虚拟列是否是稀疏的。默认为False
drop_first接收boolean。表示是否通过从k个分类级别中删除第一级来获得k-1个分类级别。默认为False
  • 哑变量处理
import pandas as pd
import numpy as np
detail = pd.read_csv('./data/detail.csv',encoding = 'utf-8')
data=detail.loc[0:5,'dishes_name']  # 抽取部分数据做演示
print('哑变量处理前的数据为:\n',data)
print('哑变量处理后的数据为:\n',pd.get_dummies(data))
哑变量处理前的数据为:
 0                       蒜蓉生蚝
1    蒙古烤羊腿\r\r\n\r\r\n\r\r\n
2                       大蒜苋菜
3                      芝麻烤紫菜
4                        蒜香包
5                        白斩鸡
Name: dishes_name, dtype: object
哑变量处理后的数据为:
    大蒜苋菜  白斩鸡  芝麻烤紫菜  蒙古烤羊腿\r\r\n\r\r\n\r\r\n  蒜蓉生蚝  蒜香包
0     0    0      0                        0     1    0
1     0    0      0                        1     0    0
2     1    0      0                        0     0    0
3     0    0      1                        0     0    0
4     0    0      0                        0     0    1
5     0    1      0                        0     0    0

离散化连续型数据

等宽法

  • 将数据的值域分成具有相同宽度的区间,区间的个数由数据本身的特点决定或者由用户指定,与制作频率分布表类似。语法:pandas.cut(x,bins,right=True,labels=None,retbins=False,precision=3,include_lowest=False)

cut函数的参数及说明

参数名称说明
x接收array或Series。代表需要进行离散化处理的数据。无默认
bins接收int、list、array和tuple。若为int,则代表离散化后的类别数目;若为序列类型的数据,则表示进行切分的区间,每两个数间隔为一个区间。无默认
right接收boolean。代表右侧是否为闭区间。默认为True
labels接收list、array。代表离散化后各个类别标签的名称。默认为空
retbins接收boolean。代表是否返回区间标签。默认为False
precision接收int。显示标签的精度。默认为3
  • 等宽法离散化
price = pd.cut(detail['amounts'],5)
print('离散化后5条记录售价分布为:\n' ,price.value_counts())
离散化后5条记录售价分布为:
 (0.823, 36.4]     5461
(36.4, 71.8]      3157
(71.8, 107.2]      839
(142.6, 178.0]     426
(107.2, 142.6]     154
Name: amounts, dtype: int64

等频法

  • cut不能直接实现等频离散化,但可以通过定义将相同数量的记录放进每个区间。
  • 等频法离散化
# 自定义等频法离散化函数
def SameRateCut(data,k):
    w=data.quantile(np.arange(0,1+1.0/k,1.0/k))
    data=pd.cut(data,w)
    return data
result=SameRateCut(detail['amounts'],5).value_counts()  # 菜品售价等频法离散化
print('菜品数据等频法离散化后各个类别数目分布状况为:','\n',result)
菜品数据等频法离散化后各个类别数目分布状况为: 
 (18.0, 32.0]     2107
(39.0, 58.0]     2080
(32.0, 39.0]     1910
(1.0, 18.0]      1891
(58.0, 178.0]    1863
Name: amounts, dtype: int64

聚类分析法

  • 一维聚类的方法包括两个步骤,首先将连续性数据用聚类算法进行聚类,然后处理聚类得到的簇,为合并到一个簇的连续性数据做同一种标记。聚类分析的离散化方法需要用户指定簇的个数,用来决定产生的区间数。
  • 基于聚类分析的离散化
def KmeanCut(data,k):
    from sklearn.cluster import KMeans  # 引入KMeans
    kmodel=KMeans(n_clusters=k)  # 建立模型
    kmodel.fit(data.values.reshape((len(data), 1)))  # 训练模型
    c=pd.DataFrame(kmodel.cluster_centers_).sort_values(0)  # 输出聚类中心并排序
    w=c.rolling(2).mean().iloc[1:]  # 相邻两项求中点,作为边界点
    w=[0]+list(w[0])+[data.max()]  # 把首末边界点加上
    data=pd.cut(data,w)
    return data
# 菜品售价等频法离散化
result=KmeanCut(detail['amounts'],5).value_counts()
print('菜品售价聚类离散化后各个类别数目分布状况为:','\n',result)
菜品售价聚类离散化后各个类别数目分布状况为: 
 (22.31, 43.51]       3690
(43.51, 73.945]      2474
(0.0, 22.31]         2454
(73.945, 131.858]     993
(131.858, 178.0]      426
Name: amounts, dtype: int64

任务实现

  • 对菜品dishes_name特征进行哑变量处理
data=detail.loc[:,'dishes_name']
print('哑变量处理前的数据为:\n',data.iloc[:5])
print('哑变量处理后的数据为:\n',pd.get_dummies(data).iloc[:5,:5])
哑变量处理前的数据为:
 0                       蒜蓉生蚝
1    蒙古烤羊腿\r\r\n\r\r\n\r\r\n
2                       大蒜苋菜
3                      芝麻烤紫菜
4                        蒜香包
Name: dishes_name, dtype: object
哑变量处理后的数据为:
     42度海之蓝   北冰洋汽水   38度剑南春   50度古井贡酒  52度泸州老窖 
0        0        0        0        0         0
1        0        0        0        0         0
2        0        0        0        0         0
3        0        0        0        0         0
4        0        0        0        0         0
  • 对菜品售价使用等频法离散化
def SameRateCut(data,k):
    w=data.quantile(np.arange(0,1+1.0/k,1.0/k))
    data=pd.cut(data,w)
    return data
result=SameRateCut(detail['amounts'],5).value_counts()  # 菜品售价等频法离散化
print('菜品数据等频法离散化后各个类别数目分布状况为:','\n',result)
菜品数据等频法离散化后各个类别数目分布状况为: 
 (18.0, 32.0]     2107
(39.0, 58.0]     2080
(32.0, 39.0]     1910
(1.0, 18.0]      1891
(58.0, 178.0]    1863
Name: amounts, dtype: int64
  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值