1. 程式人生 > >Pandas高速低記憶體計算技巧

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()