参加大数据竞赛过程中,自己琢磨了一些pandas高速低内存计算的技巧,总结于此。
(1)磨刀不误砍柴工。对于运算时间过长的程序,一定要花功夫优化处理,提高运行速度。
(2)不求尽善尽美,以解决问题为导向。没有必要花一个小时时间去优化一个本来需要30分钟运行完的程序。
(3)运算时间较长、内存耗用较大的程序,要先运行小比例试验,检查程序正确性并估算总运行时间和峰值内存耗用,有准确估计后再运行完整程序。
(4)运算时间较长、内存耗用较大的程序,要有跑马灯定量报时或定时报告机制,并打开资源监视器跟踪CPU和内存的使用,发现程序有问题或内存耗尽时及时中断。
table_byline['Stop'] = table_byline.O_NEXTSTATIONNO.groupby(table_byline.O_TERMINALNO).diff()
groupyby可以直接用的优化过的高速运算方法有count()、sum()、prod()、mean()、median()、min()、max()、std()、var()、first()、last()。因为groupby按指定列元素分组后成为一个个小的Series或DataFrame,所以也可以使用各种Series或DataFrame的方法。还可以使用自定义的函数,即grouped.apply(func)或grouped.agg(func)。
table_test['new']=np.nan
def func(df):
i=df.ID
ids=table_train.ID[table_train.ID1)
idx=table_train.ID[table_train.ID>i].head(1)
if len(ids)==0:
y=table_train.发电量.loc[idx].iloc[0]
elif len(idx)==0:
y=table_train.发电量.loc[ids].iloc[0]
else:
ids=ids.iloc[0]
idx=idx.iloc[0]
ys=table_train.发电量.loc[ids]
yx=table_train.发电量.loc[idx]
y=yx+(ys-yx)/(ids-idx)*(i-idx)
return y
table_test['new']=table_test.apply(func,axis=1)
在该程序示例中,如果for循环逐行遍历运行速度很慢。先把循环内容定义为函数func,再使用apply方法逐行遍历调用func,速度可按数量级提高。注意,虽然是逐行遍历,但需指定axis=1.
table_s=table_s.station_id.groupby([table_s.line,table_s.up,table_s.station_no]).min()
table_fine1['s_id']=np.nan
for upj in [0,1]:
s_lst=table_fine1.O_NEXTSTATIONNO[table_fine1.up==upj].drop_duplicates().tolist()
for sk in s_lst:
table_fine1['s_id'][(table_fine1.up==upj)&
(table_fine1.O_NEXTSTATIONNO==sk)]=table_s.loc[line,upj,sk]
本例中,先把table_s转为层次化索引的Series,然后按索引值直接检索数据,这比采用形如df[(df.col1==a)&(df.col2==b)]这种条件逻辑筛选法程序更简洁,运行也更快一些。
对于可分块运行的数据运算任务,可使用多线程类编程,调用多个CPU核心并行工作提高速度。如果不会多线程编程,也可以简单的在Jupyter Notebook中打开多个页面,每个页面跑一个程序,只把每个程序中需要运行的数据块手动设置好,也可以达到调用多个CPU核心的目的。
使用pd.read_csv()读取数据时,默认按64位精度读取数据,且有的数据读取为object类型,这些都极耗内存。可根据实际需要采用低位精度读取,并指定object类型为实际的类型或category类型,只读取需要处理的列。如
columns = ['O_LINENO','O_TERMINALNO','O_TIME','O_UP','O_NEXTSTATIONNO']
col_types = ['uint16','int32','category','int8','uint8']
columns_types = dict(zip(columns, col_types))
table_all = pd.read_csv(FOLDER + '\\' + 'table_all.csv',usecols=columns,dtype=columns_types)
table_all.O_TIME=pd.to_datetime(table_all.O_TIME)
import gc
gc.collect()