12.24 一行代碼將Pandas加速4倍

編譯:ronghuaiyang

導讀

雖然Pandas的功能非常強大,但是對於大數據集來說,確實是很慢的。

雖然 panda 是 Python 中用於數據處理的庫,但它並不是真正為了速度而構建的。瞭解一下新的庫 Modin,Modin 是為了分佈式 panda 的計算來加速你的數據準備而開發的。

Pandas是處理 Python 數據的首選庫。它易於使用,並且在處理不同類型和大小的數據時非常靈活。它有大量的函數,使得操縱數據變得輕而易舉。

一行代碼將Pandas加速4倍

隨著時間的推移,各種Python包的流行程度

但是有一個缺點:對於較大的數據集來說,panda“慢”。

默認情況下,panda 使用單個 CPU 內核作為單個進程執行其函數。這對於較小的數據集工作得很好,因為你可能不會注意到速度上的差異。但是,隨著數據集越來越大,計算量越來越大,如果只使用單個 cpu 核,速度會受到很大的影響。它在數據集上同一時間只能計算一次,但該數據集可以有數百萬甚至數十億行。

然而,大多數用於數據科學的現代機器都有至少 2 個 CPU 核。這意味著,以 2 個 CPU 核為例,在使用 pandas 時,50%或更多的計算機處理能力在默認情況下不會執行任何操作。當你使用 4 核(現代 Intel i5)或 6 核(現代 Intel i7)時,情況會變得更糟。pandas 的設計初衷並不是為了有效利用這種計算能力。

Modin是一個新的庫,通過在系統所有可用的 CPU 核上自動分配計算來加速 pandas。有了它,對於任何尺寸的 pandas 數據數據集,Modin 聲稱能夠以 CPU 內核的數量得到近乎線性的加速。

讓我們看看它是如何工作的,並通過一些代碼示例進行說明。

Modin 如何用 Pandas 並行計算

給定 pandas 中的 DataFrame ,我們的目標是以儘可能快的方式對其執行某種計算或處理。可以用*.mean()取每一列的平均值,用groupby對數據進行分組,用drop_duplicates()*刪除所有重複項,或者使用其他任何內置的 pandas 函數。

在前一節中,我們提到了 pandas 如何只使用一個 CPU 核進行處理。自然,這是一個很大的瓶頸,特別是對於較大的 DataFrames,計算時就會表現出資源的缺乏。

理論上,並行計算就像在每個可用的 CPU 核上的不同數據點上應用計算一樣簡單。對於一個 pandas 的 DataFrame,一個基本的想法是將 DataFrame 分成幾個部分,每個部分的數量與你擁有的 CPU 內核的數量一樣多,並讓每個 CPU 核在一部分上運行計算。最後,我們可以聚合結果,這是一個計算上很 cheap 的操作。

一行代碼將Pandas加速4倍

多核系統如何更快地處理數據。對於單核進程(左),所有10個任務都放在一個節點上。對於雙核進程(右圖),每個節點承擔5個任務,從而使處理速度加倍。

這正是 Modin 所做的。它將 DataFrame 分割成不同的部分,這樣每個部分都可以發送到不同的 CPU 核。Modin 在行和列之間劃分 DataFrame。這使得 Modin 的並行處理可擴展到任何形狀的 DataFrame。

想象一下,如果給你一個列多行少的 DataFrame。有些庫只執行跨行分區,在這種情況下效率很低,因為我們的列比行多。但是對於 Modin 來說,由於分區是跨兩個維度進行的,所以並行處理對於所有形狀的數據流都是有效的,不管它們是更寬的(很多列)、更長的(很多行),還是兩者都有。

一行代碼將Pandas加速4倍

panda的DataFrame(左)存儲為一個塊,只發送到一個CPU核。Modin的DataFrame(右)跨行和列進行分區,每個分區可以發送到不同的CPU核上,直到用光系統中的所有CPU核。

上面的圖是一個簡單的例子。Modin 實際上使用了一個“分區管理器”,它可以根據操作的類型改變分區的大小和形狀。例如,可能有一個操作需要整個行或整個列。在這種情況下,“分區管理器”將以它能找到的最優方式執行分區和分配到 CPU 核上。它是非常靈活的。

為了在執行並行處理時完成大量繁重的工作,Modin 可以使用 Dask 或 Ray。它們都是使用 Python api 的並行計算庫,你可以選擇一個或另一個在運行時與 Modin 一起使用。Ray 目前是最安全的一個,因為它更穩定 —— Dask 後端是實驗性的。

已經有足夠的理論了。讓我們來看看代碼和速度基準測試!

Modin 速度基準測試

安裝 Modin 的最簡單的方法是通過 pip。下面的命令安裝 Modin、Ray 和所有相關的依賴項:

<code>pip install modin[ray]/<code>

對於我們下面的例子和 benchmarks,我們使用了 Kaggle 的 CS:GO Competitive Matchmaking Data。CSV 的每一行都包含了 CS:GO 比賽中的一輪數據。

現在,我們嘗試使用最大的 CSV 文件(有幾個),esea_master_dmg_demo .part1.csv,它有 1.2GB。有了這樣的體量,我們應該能夠看到 pandas 有多慢,以及 Modin 是如何幫助我們加速的。對於測試,我使用一個 i7-8700k CPU,它有 6 個物理內核和 12 個線程。

我們要做的第一個測試是使用 read_csv()讀取數據。Pandas 和 Modin 的代碼是完全一樣的。

<code>### 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))/<code>

為了測量速度,我導入了time模塊,並在read_csv()之前和之後放置了一個time()。panda 將數據從 CSV 加載到內存需要 8.38 秒,而 Modin 需要 3.22 秒。這是 2.6 倍的加速。對於只修改 import 語句來說,這不算太寒酸!

讓我們在 DataFrame 上做一些更復雜的處理。連接多個 DataFrames 是 panda 中的一個常見操作 — 我們可能有幾個或多個包含數據的 CSV 文件,然後必須一次讀取一個並連接它們。我們可以使用 panda 和 Modin 中的*pd.concat()*函數輕鬆做到這一點。

我們希望 Modin 能夠很好地處理這種操作,因為它要處理大量的數據。代碼如下所示。

<code>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))/<code>

在上面的代碼中,我們將 DataFrame 與自身連接了 5 次。pandas 在 3.56 秒內完成了連接操作,而 Modin 在 0.041 秒內完成,速度提高了 86.83 倍!看起來,即使我們只有 6 個 CPU 核心,DataFrame 的分區也有助於提高速度。

用於 DataFrame 清洗的 panda 函數是*.fillna()*函數。此函數查找 DataFrame 中的所有 NaN 值,並將它們替換為你選擇的值。panda 必須遍歷每一行和每一列來查找 NaN 值並替換它們。這是一個應用 Modin 的絕佳機會,因為我們要多次重複一個非常簡單的操作。

<code>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))/<code>

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

警告!

Modin 總是這麼快嗎?

並不是這樣。

在有些情況下,panda 實際上比 Modin 更快,即使在這個有 5,992,097(近 600 萬)行的大數據集上也是如此。下表顯示了我進行的一些實驗中 panda 與 Modin 的運行時間。

正如你所看到的,在某些操作中,Modin 要快得多,通常是讀取數據並查找值。其他操作,如執行統計計算,在 pandas 中要快得多。

一行代碼將Pandas加速4倍

使用 Modin 的實用技巧

Modin 仍然是一個相當新的庫,並在不斷地發展和擴大。因此,並不是所有的 pandas 功能都被完全加速了。如果你在 Modin 中嘗試使用一個還沒有被加速的函數,它將默認為 panda,因此不會有任何代碼錯誤或錯誤。

默認情況下,Modin 將使用計算機上所有可用的 CPU 內核。在某些情況下,你可能希望限制 Modin 可以使用的 CPU 內核的數量,特別是如果你希望在其他地方使用這種計算能力。我們可以通過 Ray 中的初始化設置來限制 Modin 可以訪問的 CPU 內核的數量,因為 Modin 在後端使用它。

<code>import ray
ray.init(num_cpus=4)
import modin.pandas as pd/<code>

在處理大數據時,數據集的大小超過系統上的內存(RAM)的情況並不少見。Modin 有一個特殊的標誌,我們可以設置為“true”,這將使其進入“out of core”模式。這意味著 Modin 將使用你的磁盤作為你的內存溢出存儲,允許你處理比你的 RAM 大得多的數據集。我們可以設置以下環境變量來啟用此功能:

<code>export MODIN_OUT_OF_CORE=true/<code>

總結

這就是使用 Modin 加速 panda 函數的指南。只需修改 import 語句就可以很容易地做到這一點。希望你發現 Modin 至少在一些情況下對加速 panda有用。

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


分享到:


相關文章: