Pandas高速低記憶體計算技巧
參加大資料競賽過程中,自己琢磨了一些pandas高速低記憶體計算的技巧,總結於此。
原則
(1)磨刀不誤砍柴工。對於運算時間過長的程式,一定要花功夫優化處理,提高執行速度。
(2)不求盡善盡美,以解決問題為導向。沒有必要花一個小時時間去優化一個本來需要30分鐘執行完的程式。
(3)運算時間較長、記憶體耗用較大的程式,要先執行小比例試驗,檢查程式正確性並估算總執行時間和峰值記憶體耗用,有準確估計後再執行完整程式。
(4)運算時間較長、記憶體耗用較大的程式,要有跑馬燈定量報時或定時報告機制,並開啟資源監視器跟蹤CPU和記憶體的使用,發現程式有問題或記憶體耗盡時及時中斷。
高速計算技巧:
高速計算技巧一:使用groupby()方法分組並行運算,避免元素遍歷
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)。
高速計算技巧二:必須遍歷時,使用apply()方法,避免使用迴圈
table_test['new']=np.nan
def func(df):
i=df.ID
ids=table_train.ID[table_train.ID<i].tail(1)
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核心分塊並行運算
對於可分塊執行的資料運算任務,可使用多執行緒類程式設計,呼叫多個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)
低記憶體技巧二:使用gc.collect()及時回收不用的記憶體
import gc
gc.collect()