2022年 11月 5日

python数据预处理

一、预处理入门

(一)预处理作用:
1、创建特征、表和图
2、机器学习(无监督学习)
3、机器学习(有监督学习)
(二)预处理流程:
1、数据结构预处理
2、数据内容预处理
3、预处理步骤:记录数据–提取目标数据–连接主数据–数据聚合–转换数据内容–面向机器学习模型进行转换–拆分为训练数据和测试数据。
(三)读取数据
#使用pandas库的read_csv函数将customer.csv文件作为DataFrame读取
#通过encoding设置读取文件的字符编码

reserve_tb=pd.read_csv('data/reserve.csv',encoding='utf-8')
  • 1

二、数据结构预处理

(一)数据提取
1、提取指定的列
(1)#在reserve_tb中指定包含列名的字符串数组,提取相应的列

reserve_tb [ [ 'reserve_id','hotel_id','customer_id','reserve_datetime','checkin_date','checkin_time','checkout_date' ] ]
  • 1

(2)#将loc函数二维数组的第二维度指定为由列名组成的数组,提取相应的列

reserve_tb.loc [ : [ 'reserve_id','hotel_id','customer_id','reserve_datetime','checkin_date','checkin_time','checkout_date'  ] ]
  • 1

(3)#用drop函数删除不需要的列,axis=1 表示按列删除,inplace=True 表示使更改作用于reserve_tb

reserve_tb.drop( [ 'people_num','total_price' ] , axis=1,inplac=True )
  • 1
注:drop 函数用于删除指定的行或列。axis=0 表示按行删除,axis=1 表示按列删除。把inplace 设置为False 表示将已删除行或列的DataFrame 作为返回值返回,设置为True 则表示函数不返回值,而是直接在原来的DataFrame的基础上删除、更新行或列。
  • 1

2、按指定条件提取
(1)

reserve_tb.query( ' "2016-10-13" <= checkout_date <= "2016-10-14" ' )
  • 1

(2)

name='a'
reserve_tb.query( ' "xxx" == @name ' )
  • 1
  • 2

(3)

reserve_tb.query( ' "xxx" in [ 'xx','xx','xx' ] ' )
  • 1

(4)

reserve_tb.query( ' "xxx" = "a" | "xxx" = "b" ' )
  • 1
注:使用query函数,可以通过传入字符串形式的条件表达式来提取满足条件的数据行。当用and连接条件时使用 “&” ,当用 or 连接时使用“ | ”。此外,以@var_name 的形式在 “@” 后加上想引用的变量名,就可以调用python内存中的变量。
  • 1

3、不基于数据值的采样(采样方法分为任意采样和随机采样)
利用pandas库的sample函数进行采样。
#从reserve_tb中采样50%

reserve_tb.sample(frac=0.5)
  • 1
注:sample 函数是按行采样函数,用于对调用源**DataFrame**进行采样。将参数frac指定为采样比例,表示按比例采样。在指定要提取的数据量时,可通过参数n进行指定( reserve_tb.sample(n=100),表示要采样的数量为100个)
  • 1

4、基于聚合ID的采样
#reserve_tb[ ‘customer_id’ ].unique() 返回去重后的customer_id
#因sample函数的调用源为“DataFrame”,为使用sample函数,需要先把去重后的列值转换为pandas.series(pandas 的 list 对象)
#通过sample函数对客户ID进行采样
#通过 isin 函数提取customer_id 值与采样得到的客户ID的值一致的数据行 或 使用 query() 函数和 in 函数

target=pd.series( reserve_tb[ 'customer_id' ].unique() ).sample( frac=0.5 )
reserve_tb [ reserve_tb [ 'customer_id' ] . isin (target) ] 
									或
reserve_tb.query( ' customer_id  in @target ' )
  • 1
  • 2
  • 3
  • 4
注:unique函数用于对调用源series去重,并返回无重复值的pandas.series,series相当于DataFrame的一列,但可调用的函数及其行为不尽相同,要区别使用(一列的DataFrame和series相似但又有差异)。
isin函数用于提取与以参数形式传进来的列表中任意一个相匹配的列值。此外,将isin返回的结果数组指定给DataFrame,即可提取相应的数据行。
  • 1
  • 2

(二)数据聚合
1、计算数据条数和类型数
通过DataFrame调用groupby函数,并将聚合单元设置为参数,进而调用聚合函数即可。
计算数据条数的聚合函数size,唯一值计数的聚合函数nunique,agg函数可以实现同时执行所有聚合处理。

#使用agg函数统一指定聚合处理
#对reserve_id应用count函数
#对customer_id应用nunique函数
result = reserve_tb.groupby ( 'hotel_id' ).agg( { ' reserve_id ' : ' count ' , ' customer_id ' : ' nunique ' } )
#使用 reset_index 函数重新分配列号(由于 inplace=True ,程序将直接更新 result)
result.reset_index ( inplace = True )  #索引重置可单起,单起行需用inplace参数
result.columns = [ ' hotel_id ' , ' rev_cnt ' , ' cus_cnt ' ]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
注:agg函数可通过在参数中指定字典对象,统一指定聚合处理。字典对象的键设置为目标列名,值设置为聚合函数名。
**agg函数 与 groupby函数同在!**
  • 1
  • 2

2、计算合计值(sum函数)

#将聚合单元指定为hotel_id 和 people_num 的组合
#从聚合后的数据中去total_price,调用sum 函数计算住宿费总额
result = reserve_tb.groupby( [ ' hotel_id ',' people_num ' ] ).[ 'total_price' ].sum( ).reset_index() #索引重置如单起行,需用参数inplace,如不单起,则不需要参数inplace
#由于住宿费总额的列名为total_price,所以这里将其更改为price_sum
result.rename( columns={ 'total_price' : 'price_sum' },inplace=True )
  • 1
  • 2
  • 3
  • 4
  • 5
注:rename 函数可用于更改列名,columns 参数指定为字典对象,字典对象的键是更改前的列名,值是更改后的列名。
在仅有一个聚合处理的情况下,不使用agg函数能是代码更简洁。此外,在设置列名时,如果更改的列较多时,直接使用DataFrame.columns 更容易理解;
  • 1
  • 2

3、计算最值、代表值
max 函数用于求最大值,min 函数用于求最小值,mean 函数用于求平均值,median 函数用于求中位数,上述函数均为pandas 库;percentitle 函数用于求百分位数,该函数为numpy 库。

# 将max、min、mean、median 函数应用于total_price
# 在agg函数中进行聚合操作
# 指定numpy.percentile 函数计算百分位数(百分位数指定为20)
result = reserve_tb.groupby('hotel_id').agg( { 'total_price' : [ 'max' , 'min' , 'mean' , 'median' , lambda x: np.percentitle(x, q=20) ]  } ).reset_index()
result.columns = [ 'hotel_id','price_max','price_min','price_mean','price_median','price_20per' ]
  • 1
  • 2
  • 3
  • 4
  • 5
注:在agg函数内无法通过字符串指定百分位数的聚合处理,因此这里使用lambda 表达式指定聚合处理。所谓lambda 表达式,就是在“lambda x:” 之后加上处理x的函数表达式。这里的x即hotel_id 上聚合的total_price的列表。像这样使用 lambda 表达式,即可设置灵活的聚合函数。
在计算百分位数时,本段代码使用的是numpy 库中percentile 函数,其参数	q 为目标百分位。
  • 1
  • 2

4、计算离散程度
var 函数用于计算方差,std 函数用于计算标准差。

# 对total_price 列应用var 函数 和 std 函数
result = reserve_tb.groupby('hotel_id').agg( { 'total_price' : [ 'var','std' ] } ) . reset_index()
result.columns = [ 'hotel_id' , 'price_var','price_std' ]
#由于当数据条数为1时方差和标准差会变成na,所以这里需要将其替换为0
result.fillna(0, inplace=True)
  • 1
  • 2
  • 3
  • 4
  • 5
注:由于fillna 函数替换的范围是DataFrame 中全部的na,所以要注意不要替换无关的值。
可以通过向fillna 函数传递字典对象,指定替换列,防止替换无关的na,result.fillna( { 'price_var' : 0 , 'price_std' : 0 } , inplace=True )
  • 1
  • 2

5、计算众数
mode 函数用于计算众数

 # 通过round 函数进行四舍五入后,用mode 函数计算众数
 reserve_tb[ 'toral_price' ] . round(-3).mode()
  • 1
  • 2
注:round()函数的参数指定保留的小数位,mode 函数用于计算调用源的值的众数
  • 1

6、排序
rank函数无法对字符串排序。

#为使用rank函数进行排序,将数据类型由字符串转换为timestamp类型
reserve_tb[ ‘reserve_datetime’ ] = pd.to_datetime( reserve_tb[ ‘reserve_datetime’ ] , format = ‘%Y-%m-%d %H:%M%S’)
#添加新列 log_no
#使用 groupby 指定聚合单元
#按顾客统一生产 reserve_datetime,然后使用rank 函数生产位次
#把 ascending 设置为True ,即按升序排列(False 表示降序排列)
reserve_tb[ ‘log_no’ ] = reserve_tb.groupby( ‘customer_id’ )[ ‘reserve_datetime’ ].rank( ascending=True,method=‘first’ )
注:rank 函数用于排序。method参数可用于指定存在多个相同值是的排序方式,当存在多个相同值时按加载先后排序。ascending参数可设置升序和降序排列。

……(其他排序不想写了)

(三)数据连接
1、pandas库的merge函数用作执行连接处理,merge函数的第一个参数和第二个参数用于设置要连接的两张表,on 参数用来指定关联字段,how参数用来指定连接方式。
(1)on参数的使用:当两张表的关联字段名一致时可以直接使用 “ on=‘字段名’ ”,多字段关联时使用“on=[‘字段名’,‘字段名’]”;当两张表的关联字段名不一致时,使用left_on 和 right_on(示例:pd.merge(tb1,tb2,left_on=‘a’,right_on=‘b’),表示左表tb1的a字段和右表tb2的b字段关联,如果多字段关联时使用方法参考on
(2)how参数的使用:how参数用来指定连接方式,how=’inner’内连接,how=’left’左连接,how=’right’右连接,how=’outer’外连接,连接逻辑参考sql,未指定how参数时,默认内连接。
(3)做表关联时,为提高运行效率减少内存占用,在做关联之前先对表进行过滤压缩。
1、主表连接

pd.merge( reserve_tb.query('people_num ==1') , hotel_tb.query('is_business=="xxx"') , on ='hotel_id' , how='inner')
  • 1

2、切换按条件连接的主表

#用于垃圾回收的库(释放不必要的内存)
import gc
#按small_area_name 对酒店数进行统计
small_area_mat=hotel_tb.groupby([ 'big_area_name','small_area_name' ], as_index=False).size().reset_index()
small_area_mat.columns=['big_area_name','small_area_name','hotel_cnt']
#当酒店数大于或等于20时,将small_area_name作为join_area_id
#当酒店数不足20时,将big_area_name作为join_area_id
#-1表示推荐酒店中应去除该酒店自身
small_area_mat['join_area_id']=np.where(small_area_mst['hotel_cnt']-1>=20,small_area_mst['small_area_name'],small_area_mst['big_area_name'])
#去除不再需要的列
small_area_mst.drop(['hotel_cnt','big_area_name'],axis=1,inplace=True)
#将推荐源的酒店表与small_area_mst连接,设置join_area_id
base_hotel_mst=pd.merge(hotel_tb,small_area_mst,on='small_area_name').loc[:,['hotel_id','join_area_id']]
#根据需要释放内存(不是必要操作,可在内存不足时进行)
del small_area_mst
gc.collect()
#推荐候选表 recommond_hotel_mst
#将small_area_name作为join_area_id而得到的推荐候选的主数据
#将big_area_name作为join_area_id而得到的推荐候选的主数据
recommend_hotel_mst=pd.concat([hotel_tb[['small_area_name','hotel_id']].rename(columns={'small_area_name':'join_area_id'},inplace=Flase),hotel_tb[['big_area_name','hotel_id']].rename(columns={'big_area_name':'join_area_id'},inplace=False)])
#由于连接时hotel_id列重复,所以需要更改列名
recommend_hotel_mst.rename(columns={'hotel_id':'rec_hotel_id'},inplace=True)
#将recommend_hotel_mst与base_hotel_mst连接,加入推荐候选的信息
#通过query函数从推荐候选酒店中去除该酒店自身
pd.merge(base_hotel_mst,recommend_hotel_mst,on='join_area_id').loc[:,['hotel_id','rec_hotel_id']].query('hotel_id!=rec_hotel_id')
#注:numpy的where函数可根据条件更改返回值:如果满足第1个参数的条件,则返回第2个参数的值,否则返回第3个参数的值。python也无法一次性对3个表执行连接处理,因此处理流程也和R一样。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

3、连接历史数据
(1)获取往前数第n条记录的数据(shift函数为能够上下偏移n个数据行的函数)

  #对每个顾客按reserve_datetime进行排序
  #在使用groupby函数后通过apply函数对每个分组排序
  #通过sort_values函数对数据排序:axis为0表示按行排序,axis为1表示按列排序
  result=reserve_tb
  		.groupby('customer_id')
  		.apply(lambda group:
  					group.sort_values(by='reserve_datetime',axis=0,inplace=False))
  #result已经按customer_if分组
  #对每个顾客取其total_price列中往前数第2条记录的值,并将其保存为before_price
  #shift函数用于将数据行向下偏移,偏移行数即periods参数的值
  result['before_price']=result['total_price'].groupby('customer_id').shift(periods=2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注:sort_values 函数通过参数by指定的行名或列名对数据行或列排序。当axis为0时,通过指定的列名对数据行排序;当axis为1时,通过指定的行名对数据列排序。
shift函数用于将数据行向下偏移n行,其参数periods设置为n,当不存在相应数据时,默认返回NaN。
(2)前n条记录的合计值
python没有提供用于计算合计值的窗口函数,但提供了将数据分成窗口(多个数据集合)的rolling函数。通过该函数,可以将一般的聚合函数当成窗口函数使用。

#对每个customer_id按reserve_datetime进行排序
result=reserve_tb.groupby('customer_id').apply(lambda x: x.sort_values(by='reserve_datetime',ascending=True)).reset_index(drop=True)
#添加新列price_sum
result['price_sum']=pd.Series(
		#仅提取所需的数据列
		result.loc[:,["customer_id","total_price"]]
#对每个customer_id,把total_price 的窗口切分为3个并汇总,然后计算合计值	
		.groupby('customer_id')
		.rolling(center=False,window=3,min_periods=3).sum()
		#取消分组,同时取出total_price列
		.reset_index(drop=True)
		.loc[:,'total_price']
	                                            )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

注:rolling 函数是将数据切分为窗口的函数。window 参数表示包括自身在内,该窗口可包含多少个目标数据。min_periods参数表示仅当窗口中的记录数达到或超过指定数值时才进行计算,当数目不足时,则返回NAN。此外,当center为True时,表示选择的窗口会使当前行处于窗口正中间。在没有设置的情况下,就在当前行之后添加数据行,以使窗口大学为指定行数。
可以通过更改rolling函数后面的聚合上实现各种计算。除了这里的sum,还可以使用max,min,mean等聚合函数。
当center 设置为Turea时,类似居中对齐,不设置center参数,类似左对齐。
(3)前n条记录的平均值
(懒,不想写了……)
(4)过去n天的合计值
(不想写,懒……)
4、交叉连接
(木球用,省略……)
(四)数据拆分
(暂时省略,懒得写)
(五)数据生成
通过过采样 or 欠采样 两种方法处理数据不平衡的情况,具体省略,懒……
(六)数据扩展(透视表)
1、转换为横向显示(转换为透视表)
pandas库提供了可用于实现横向显示的函数(即透视表函数),pivot_table函数。

#用pivot_table 函数同时实现横向显示和聚合处理
#向aggfunc参数指定用于计算预订数的函数
pd.pivot_table(reserve_tb,index='customer_id',columns='people_num',
						 values='reserve_id',
						 aggfunc=lambda x: len(x) , fill_value=0 )
  • 1
  • 2
  • 3
  • 4
  • 5

注:pivot_table 函数的第1个参数指定的是对象表,index 参数指定的是表示数据集合的键值,
columns 参数指定的是表示数据元素类别的键值,而values 参数指定的是数据元素对应的对象列(可通过数组形式指定多个index 和 columns 参数)。此外,aggfunc参数指定的是用于将values 参数指定的列转行为数据元素值的函数。在fill_value参数中设置值,可以指定当相应列值不存在时数据元素的填充值。
2、转换为稀疏矩阵
省略,岚……

三、数据内容预处理

(一)数值型

(二)分类型
(三)日期时间型
(四)字符型
(五)位置信息型

四、预处理实战