如何只用一行代碼讓Pandas加速四倍?

Pandas雖然是Python中用於數據處理的庫,但它不是爲高性能數據處理而打造的。本文將帶你瞭解最近新推出的代碼庫Modin,它是專爲Pandas分佈式計算而開發的,能夠加速處理數據。

Pandas是Python中處理數據的首選庫,它使用起來很容易,非常靈活,能夠處理不同類型和大小的數據,而且它有大量的函數,這讓操作數據簡直是小菜一碟。

歷年來Python開發包的受歡迎程度。來源:https://stackoverflow.blog/2017/09/14/python-growing-quickly/

Pandas默認是在單個CPU核上,採用單進程執行函數,這在小數據集上運行得很好,因爲你可能覺察不到太多性能上的差異。但是,對於較大數據集,需要做更多的計算,這時如果還只使用單個CPU核,就會開始感覺到性能受到影響了。對於具有百萬行甚至數十億行的數據集,Pandas每次只計算一個數。

但是,大多數用於數據科學的現代化機器都至少有2個CPU核,這意味着,在有2個CPU核的機器上,使用Pandas的默認配置時,至少有50%的計算機算力都被閒置了。如果你有4個核(現代的Intel i5)或者6個核(現代的Intel i7),情況那就更糟糕了,因爲Pandas就不是爲有效利用多核算力而設計的。

Modin是新出的一個庫,通過自動化地將計算分佈到系統所有可用的CPU核上,來加速Pandas。Modin宣稱,通過這個技術,對於任何大小的Pandas數據幀,它都能夠獲得和系統CPU核數幾乎成正比的性能增長。

讓我們來看下Modin都是怎樣運行的,然後看幾個代碼用例。

Modin是怎樣用Pandas做並行處理的

在Pandas中生成了一個數據幀後,我們的目標是用最快的方式執行一些計算或者處理工作,比如可能是要求解每列的平均數(使用mean()函數)、根據groupby字段對數據分組、移除所有重複數據(使用drop_duplicates()函數),或者是Pandas中其他內建的函數。

在前面一節中,我們提到了Pandas只用一個CPU核做數據處理的方式。很自然,這成了一個大大的瓶頸,特別是對於較大的數據幀,缺少計算資源會給性能帶來較大影響。

理論上講,並行計算是很容易的,只需要把數據集不同部分的計算應用到每個可用的CPU核上。對於Pandas的數據幀,基本的想法就是把這個數據幀分成好幾塊,塊數和你機器上的CPU核數量相等,讓每一個CPU覈計算其中一塊。最後,我們再把計算結果彙總,這個彙總操作計算量並不大。

多核系統怎樣加速處理數據。左圖:單核處理方式,10個任務都由單個計算節點處理。右圖:雙核處理方式,每個節點處理5個任務,於是處理速度就加倍了。

這正是Modin所採用的方式,它把數據幀切割成不同部分,每個部分都會被送到不同的CPU核。Modin會同時從行和列兩個維度對數據幀切分。這使得Modin的並行處理可以適應任何形狀的數據幀

想象一下這個例子,你有一個數據幀,它有很多列,卻只有寥寥幾行。一些庫只會在行這個維度做切分,在這個例子中並行度就不夠了,因爲我們的列數大於行數。但是有了Modin,由於它會在兩個維度進行切分,所以不管數據幀是寬的(列數較多)還是長條形的(行數較多),或者兩類情況兼具時,其對這些數據幀的並行處理就都很高效了。

Pandas數據幀(左圖)作爲整塊存儲,且只發送到一個CPU核。Modin數據幀(右圖)在行和列方向上被切分成了小塊,每塊都可以被髮送到不同的CPU核(可發送到的核數取決於系統中最大核數)。

上圖是一個簡單示例。Modin實際上採用了一個分塊管理器,它可以基於操作類型來改變分塊大小和形狀。例如,有個操作需要完整的行或者列。在這個情況下,分塊管理器)會以它能發現的最優方式執行切分,並把分塊分散發送到CPU核上,它是很靈活的。

爲了在執行並行處理時完成大量繁重的工作,Modin可以使用Dask或者Ray。這兩個庫都是用Python API寫的並行計算庫,你可以在運行時選擇其一與Modin一起使用。Ray是目前爲止最安全的,因爲它更加穩定——而Dask後端還是實驗性質的。

說到這裏,理論部分已經介紹得足夠多啦。讓我們來看看代碼和性能的基準測試吧!

給Modin性能做基準測試

安裝和運行Modin最簡單的方法是通過pip來進行。以下命令用來安裝Modin、Ray以及所有相關依賴。

pip install modin[ray]

本文接下來的例子和基準測試,我們打算使用來自Kaggle的CS:GO Competitive Matchmaking Data數據集。CSV文件中的每一行都包含了CS:GO比賽中的一輪數據。

我們從現在起都只使用上面數據集裏最大的CSV文件來做實驗(一共有好幾個CSV文件),該文件名爲esea_master_dmg_demos.part1.csv,大小爲1.2GB。用這麼大的文件進行實驗,我們應該可以看到Pandas是怎樣拖慢速度的,Modin又是怎樣幫我們解決這個問題的。測試環境,我會使用一個i7-8700k CPU,它有6個物理核和12個線程。

我們要做的第一個測試就是簡單地用read_csv()函數讀取數據。使用Pandas或者Modin實現這個功能的代碼是完全一樣的。

### Read in the data with Pandas
import pandas as pd

s = time.time()
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
e = time.time()
print("Pandas Loading Time = {}".format(e-s))

### Read in the data with Modin
import modin.pandas as pd

s = time.time()
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
e = time.time()
print("Modin Loading Time = {}".format(e-s))

爲了測試速度,我導入了time這個模塊,在read_csv()函數前後調用了time.time()。結果,Pandas花了8.38秒從CSV文件中載入數據到內存,而Modin僅花了3.22秒,Modin實現了2.6倍的加速。只要修改導入庫名稱就可以實現這樣的加速,不要太爽了!

讓我們在數據幀上做一些計算量大的操作看下。將幾個數據幀連接起來是Pandas中的一個常用操作——我們的數據可能包含在幾個或者更多的CSV文件中,我們不得不一次讀入一個文件,再進行數據幀連接。我們在Pandas和Modin中只需調用pd.concat()函數就可以很容易做到這點。

我們預期Modin對這類操作將會運行得很好,因爲它能夠處理大量的數據。代碼如下所示:

import pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")

s = time.time()
df = pd.concat([df for _ in range(5)])
e = time.time()
print("Pandas Concat Time = {}".format(e-s))

import modin.pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")

s = time.time()
df = pd.concat([df for _ in range(5)])
e = time.time()
print("Modin Concat Time = {}".format(e-s))

以上代碼中,我們將一個數據幀複製了5次進行連接。Pandas可以在3.56秒內完成這個連接操作,而Modin只花了0.041秒,Modin實現了86.83倍的加速!看起來即使我們只有6個CPU核,數據幀的分塊對加速也起了很大的作用。

Pandas中經常使用的數據幀清理函數是.fillna()函數。這個函數搜索數據幀中值爲NaN的元素,將其值替換爲你指定的值,這其中有大量的操作。Pandas不得不遍歷每一行每一列來找到NaN值然後替換它們。這裏使用Modin來操作就再適合不過了,因爲我們這裏是對一個簡單操作重複很多次。

import pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")

s = time.time()
df = df.fillna(value=0)
e = time.time()
print("Pandas Concat Time = {}".format(e-s))

import modin.pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")

s = time.time()
df = df.fillna(value=0)
e = time.time()
print("Modin Concat Time = {}".format(e-s))

這次,Pandas運行.fillna()用了1.8秒,而Modin僅用了0.21秒,實現了8.57倍的加速!

預警以及最後的基準測試

但是Modin總是這麼快嗎?

嗯,其實並不總是這麼快。

有些情況下,Pandas實際上會比Modin運行得更快,即使在這個有着5,992,097(幾乎600萬)行的大數據集上。下面表格展示了Pandas和Modin在一些實驗上的運行時間。

你可以看到,有些操作,Modin明顯更快,通常是讀取數據和查找數據。其他操作,比如進行統計計算,Pandas會快很多。

對Modin的操作建議

Modin還是一個非常新的庫,開發和擴展都在不斷進行中。所以,不是所有的Pandas函數都得到了完全加速。如果你使用了Modin中還沒有加速的函數,它會默認使用Pandas函數版本,所以這樣就不會產生任何bug或者錯誤。想查看Modin中支持的Pandas函數加速的完整列表,請瀏覽該頁面

默認情況下,Modin會使用你的機器上所有可用的CPU核。可能有些情況下,你希望限制Modin使用的CPU核數量,特別是當你還想在別的地方使用這些核的算力的時候。我們可以通過Ray模塊中的初始化設置來限制Modin能使用的CPU核數量,因爲Modin會在後臺使用Ray配置。

import ray
ray.init(num_cpus=4)
import modin.pandas as pd

當處理大數據時,通常情況下,數據集的大小不會超出系統內存(RAM)的大小。但是,Modin還有一個特別的標記,通過把這個標記設置爲true,我們可以啓動核外(out of core)模式。核外模式是指當內存不夠用時,Modin會使用硬盤空間,這樣就使你可以處理比內存大小更大的數據集。我們設置如下的環境變量來開啓這個功能:

export MODIN_OUT_OF_CORE=true

結論

好了,你已經掌握了Modin模塊!這是一篇Modin加速Pandas函數的使用指南。只需要修改import導入語句即可實現加速。希望至少在某些情況下,你會發現Modin對加速Pandas函數有所幫助。

原文鏈接:

https://www.kdnuggets.com/2019/11/speed-up-pandas-4x.html

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