Python数据处理心得--Pandas100秒处理一亿行数据


1. 
 背景-为啥要用pandas 

 公司的日常运营数据通过大数据平台(HIVE SQL)通过汇总后,推送给业务部门进行日常分析的数据仍然非常大。从数据量从PB&TB级降到了GB级,一般主要通过Mysql进行存储&聚合分析。 

 日或周的数据,mysql处理还是可以的。到月数据,超过10GB(1亿行),处理起来就开始吃力,数据吞吐特别慢,内存资源占用特别严重。 

 使用Pandas后,普通笔记本电脑可以很轻松地处理1亿行的数据,100秒内就能完成计算, 计算实现的成本非常低。 



 2. 
 Pandas介绍 

 很多方法可以解决这个问题,由于希望用单机版的开源软件,正好python日常用的也比较多,于是使用pandas来解决这个问题。 

 pandas是numpy的基础发展出来的, pandas的DataFrame数据结构相当于多个numpy series组合在一起。 

 为了更直观地了解pandas,打算通过一个模拟数据的分析过程来介绍pandas的用法。 



 使用软件的版本 

 anaconda3-4.1.1 (Python 3.5) 

 Jupiter notebook (4.2.1) 

 Pandas包 0.18.1 (anaconda自带的) 



 3. 
 生产模拟数据 

 我们先模拟一个一亿行的数据,然后测试用pandas的代码进行汇总分析,并且记录耗时&及内存&CUP的大致占用情况。 

 基本需要把数据切成小块处理,否则内存占用会特别高,内存不足,python无法运行下去。 

 如果电脑有32GB内存的,可以直接抽取1亿行样本数据,不用使用循环进行数据重复,这样的内存消耗是25GB。 

 如果用下面的循环,内存占用只有几百MB。 

 ############################################################################ 

 import pandas as pd 

 import numpy as np 

 import csv 



 date= ['2017-11-01', '2017-11-02','2017-11-03','2017-11-04','2017-11-05','2017-11-06','2017-11-07'] 

 #设置日期数据,为后面的np.random.choice引用 

 area= ['华北', '华东', '华南','西南','华中','东北','西北'] 

 order_type =[0, 1, 2, 3, 4 ,5 ,6 ,7 ,8, 9] 



 col1=np.random.choice(date, 1000000, p=[0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.1]) 

 #随机抽样100万次,各个日常出现的概率是P。 

 col2=np.random.choice(area, 1000000, p=[0.2, 0.2, 0.2, 0.1, 0.1, 0.1, 0.1]) 

 col3=np.random.choice(order_type, 1000000, p=[0.05, 0.2, 0.2, 0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 0.05]) 

 col4=np.random.choice(100, 1000000) 

 col5=np.random.choice(10000, 1000000) 



 df = pd.DataFrame({'date':col1, 'area':col2, 'order_type':col3, 'qty':col4, 'revenue':col5}) 

 df=df.set_index('date') 

 #合并各个numpy生产的随机数据成为Pandas的DataFrame 



 with open('E:\\mess_files\\sample_data.csv','w', newline='\n') as csvfile:  

     writer = csv.writer(csvfile) 

     #先写入columns_name 

     writer.writerow(['date','area','order_type','qty','revenue']) 



 #为了减少内存占用,没有直接在上面生成1亿行数据,先生产100万,然后循环100次。 

 for i in range(100): 

     i=i+1 

     df.to_csv ('E:\\mess_files\\sample_data.csv', encoding='gbk', header=False, mode='a') 

     print(i*1000000) 



 ############################################################################ 



 4. 
 数据分析代码 

 涉及的功能:读取数据,增加计算字段,group by ,merge( left join), index (set & reset), 输出数据(CSV & excel)。 



 ############################################################################ 

 import pandas as pd 

 import time 

 import csv 



 start = time.clock() 

 #开始计时 



 with open('E:\\mess_files\\pd_sum.csv','w', newline='\n') as csvfile:  

     writer = csv.writer(csvfile) 

     #先写入columns_name 

     writer.writerow(['date','area','order_type','qty','revenue']) 

 #为汇总的输出,建立一个CSV文件,并包含表头字段明。 

      

 #分块(每100万行)进行数据汇总, 并循环写入csv中     



 reader = pd.read_csv('E:\\mess_files\\sample_data.csv', encoding='gbk',sep=',',iterator=True) 

 i=0  

 while True: 

     try: 

         start2 = time.clock() 

         #每次循环开始时间 

          

         # 从csv文件迭代读取 

         df = reader.get_chunk(1000000) 

          

          

         mini_sum=df.groupby(['date','area','order_type']).sum() 

         #按date, area, order_type 进行汇总 

         mini_sum.to_csv('E:\\mess_files\\pd_sum.csv',mode='a',header=False) 

         #汇总结果写入CSV文件,'header=False' 避免重复写入表头。 

      

      

          

         # 计时 

         i=i+1 

         end2 = time.clock() 

         #每次循环结束时间 

         print('{} 秒: completed {} rows'.format(end2 - start2, i * 1000000)) 

     except StopIteration: 

         print("Iteration is stopped.") 

         #循环结束退出 

          

         break 







 df=pd.read_csv('E:\\mess_files\\pd_sum.csv', encoding='gbk',sep=',') 



 df=df.groupby(['date','area','order_type']).sum() 



 df=df.reset_index() 

 #pandas汇总时,会根据groupby的字段建立multi_index, 需要重置index。 



 df['date']=pd.to_datetime(df['date']) 

 #将date列 设置为日期类型 



 df['avg']=df['revenue']/df['qty'] 

 #增加一个计算字段 avg 平均客单价 



 df_sub=df[['date','area','qty']].groupby(['date','area']).sum().add_prefix('sum_') 

 #建立一个新DataFrame, 用于后面的left join 计算各个order_type的占比 



 df_merge=pd.merge(df, df_sub, how='outer', left_on=['date','area'], right_index=True) 

 #相当于SQL的left join 



 df_merge['type_qty%']=df_merge['qty']/df_merge['sum_qty'] 

 #增加计算字段 



 df_merge=df_merge.set_index('date') 



 output=pd.ExcelWriter('E:\\mess_files\\output_xls.xlsx') 

 df_merge.to_excel(output,'sheet1') 

 output.save() 

 #最终结果输出到excel 



 end = time.clock() 

 #最终使用时间计时 

 print('final{} 秒'.format(end - start)) 



 ############################################################################### 

 使用了两台机器进行数据运算,DELL R720 2U SAS硬盘 96GB内存的服务器,Thinkpad E450 SSD硬盘 i5 8G内存的笔记本电脑。 

 运行时,CUP占用率服务器5%,笔记本30%, 总内存占用都是约6GB,耗时也非常接近, 每处理100万行用时在 1秒种以内, 处理1亿行数据的运算还是很轻松的。 



 服务器循环每次计算100万行用时 0.8秒, 总用时79.3秒。 

 ######################## 



 0.789916201370346 秒: completed 90000000 rows 

 0.7889745154019323 秒: completed 91000000 rows 

 0.7875460356832349 秒: completed 92000000 rows 

 0.7883160047623932 秒: completed 93000000 rows 

 0.7929830807664189 秒: completed 94000000 rows 

 0.7884885093438072 秒: completed 95000000 rows 

 0.8129294153000615 秒: completed 96000000 rows 

 0.8298620396579395 秒: completed 97000000 rows 

 0.787222294208533 秒: completed 98000000 rows 

 0.7879615432937328 秒: completed 99000000 rows 

 0.7891974322811279 秒: completed 100000000 rows 

 Iteration is stopped. 

 final79.22691993069884 秒 



 ######################### 



 笔记本电脑循环每次计算100万行用时 0.83, 总用时85.1秒。 



 ######################### 



 0.817601222058812 秒: completed 92000000 rows 

 0.8092709856398557 秒: completed 93000000 rows 

 0.8277913177203118 秒: completed 94000000 rows 

 0.8203788228361191 秒: completed 95000000 rows 

 0.8211909342874009 秒: completed 96000000 rows 

 0.8238487924599838 秒: completed 97000000 rows 

 0.825806156394961 秒: completed 98000000 rows 

 0.8143844225134984 秒: completed 99000000 rows 

 0.8465947555305036 秒: completed 100000000 rows 

 Iteration is stopped. 

 final85.11640178604648 秒 



 #########################