提速千倍!Pandas性能優化方法,讓你的pandas飛起來!

微信公衆號:「Python讀財」
如有問題或建議,請公衆號留言

Pandas是Python中用於數據處理與分析的屠龍刀,想必大家也都不陌生,但Pandas在使用上有一些技巧和需要注意的地方,尤其是對於較大的數據集而言,如果你沒有適當地使用,那麼可能會導致Pandas的運行速度非常慢。

對於程序猿/媛而言,時間就是生命,這篇文章給大家總結了一些pandas常見的性能優化方法,希望能對你有所幫助!

一、數據讀取的優化

讀取數據是進行數據分析前的一個必經環節,pandas中也內置了許多數據讀取的函數,最常見的就是用pd.read_csv()函數從csv文件讀取數據,那不同格式的文件讀取起來有什麼區別呢?哪種方式速度更快呢?我們做個實驗對比一下。

這裏採用的數據共59萬行,分別保存爲xlsx、csv、hdf以及pkl格式,每種格式進行10次讀取測試,得到下面的結果。

image

可以看到,對同一份數據,pkl格式的數據的讀取速度最快,是讀取csv格式數據的近6倍,其次是hdf格式的數據,速度最慘不忍睹的是讀取xlsx格式的數據(這僅僅是一份只有15M左右大小的數據集呀)。

所以對於日常的數據集(大多爲csv格式),可以先用pandas讀入,然後將數據轉存爲pkl或者hdf格式,之後每次讀取數據時候,便可以節省一些時間。代碼如下:

 import pandas as pd
 #讀取csv
 df = pd.read_csv('xxx.csv')

 #pkl格式
 df.to_pickle('xxx.pkl') #格式另存
 df = pd.read_pickle('xxx.pkl') #讀取

 #hdf格式
df.to_hdf('xxx.hdf','df') #格式另存
df = pd.read_hdf('xxx.pkl','df') #讀取

二、進行聚合操作時的優化

在使用aggtransform進行操作時,儘量使用Python的內置函數,能夠提高運行效率。(數據用的還是上面的測試用例)

1、agg+Python內置函數

image

2、agg+非內置函數

image
可以看到對 agg 方法,使用內置函數時運行效率提升了60%。

3、transform+Python內置函數

image

4、transform+非內置函數

image

transform方法而言,使用內置函數時運行效率提升了兩倍。

三、對數據進行逐行操作時的優化

假設我們現在有這樣一個電力消耗數據集,以及對應時段的電費價格,如下圖所示:

image

image

數據集記錄着每小時的電力消耗,如第一行代表2001年1月13日零點消耗了0.586kwh的電。不同使用時段的電費價格不一樣,我們現在的目的是求出總的電費,那麼就需要將對應時段的單位電費×消耗電量。下面給出了三種寫法,我們分別測試這三種處理方式,對比一下這三種寫法有什麼不同,代碼效率上有什麼差異。

#編寫求得相應結果的函數
def get_cost(kwh, hour):
    if 0 <= hour < 7:
        rate = 0.6
    elif 7 <= hour < 17:
        rate = 0.68
    elif 17 <= hour < 24:
        rate = 0.75
    else:
        raise ValueError(f'Invalid hour: {hour}')
    return rate * kwh

#方法一:簡單循環
def loop(df):
    cost_list = []
    for i in range(len(df)):
        energy_used = df.iloc[i]['energy_kwh']
        hour = df.iloc[i]['date_time'].hour
        energy_cost = get_cost(energy_used, hour)
        cost_list.append(energy_cost)
    df['cost'] = cost_list

#方法二:apply方法
def apply_method(df):
     df['cost'] = df.apply(
         lambda row: get_cost(
             kwh=row['energy_kwh'],
             hour=row['date_time'].hour),
         axis=1)

#方法三:採用isin篩選出各時段,分段處理
df.set_index('date_time', inplace=True)
def isin_method(df):
    peak_hours = df.index.hour.isin(range(17, 24))
    simple_hours = df.index.hour.isin(range(7, 17))
    off_peak_hours = df.index.hour.isin(range(0, 7))

    df.loc[peak_hours, 'cost'] = df.loc[peak_hours, 'energy_kwh'] * 0.75
    df.loc[simple_hours,'cost'] = df.loc[simple_hours, 'energy_kwh'] * 0.68
    df.loc[off_peak_hours,'cost'] = df.loc[off_peak_hours, 'energy_kwh'] * 0.6

測試結果:

image

可以看到,採用isin() 篩選出對應數據後分開計算的速度是簡單循環的近606倍,這並不是說 isin() 有多厲害,方法三速度快是因爲它採用了向量化的數據處理方式(這裏的isin() 是其中一種方式,還有其他方式,大家可以嘗試一下) ,這纔是重點。什麼意思呢?

這裏簡單畫了個圖,大家可以結合這個圖和代碼好好體會是一個一個處理快,還是把能進行相同操作的分開然後批量處理快。

image

四、使用numba進行加

如果在你的數據處理過程涉及到了大量的數值計算,那麼使用numba可以大大加快代碼的運行效率,numba使用起來也很簡單,下面給大家演示一下。(代碼處理不具有實際意義,只是展示一下效果)

首先需要安裝numba模塊

>>>pip install numba

我們用一個簡單的例子測試一下numba的提速效果

import numba

@numba.vectorize
def f_with_numba(x): 
    return x * 2

def f_without_numba(x): 
    return x * 2

#方法一:apply逐行操作
df["double_energy"] = df.energy_kwh.apply(f_without_numba)

#方法二:向量化運行
df["double_energy"] = df.energy_kwh*2

#方法三:運用numba加速
#需要以numpy數組的形式傳入
#否則會報錯
df["double_energy"] = f_with_numba(df.energy_kwh.to_numpy())

image

從測試結果來看,再次凸顯出向量化處理的優勢,同時numba對原本速度已經很快的向量化處理也能提高一倍多的效率。更多numba的使用方法請參考numba的使用文檔。

參考資料:

1、https://pandas.pydata.org/pandasdocs/stable/user_guide/enhancingperf.html

2、https://realpython.com/fast-flexible-pandas/

3、https://www.cnblogs.com/wkang/p/9794678.html

原創不易,如果覺得有點用,希望可以隨手點個贊,拜謝各位老鐵。

掃碼關注公衆號「Python讀財」,第一時間獲取乾貨,還可以加數據分析學習交流羣!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章