亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

加速大型數組和數據集的操作(Pandas 慢,Numpy 更好,進一步改進?)

加速大型數組和數據集的操作(Pandas 慢,Numpy 更好,進一步改進?)

森林海 2022-06-14 15:34:45
我有一個包含數百萬行和大約 6 列的大型數據集。數據當前位于 Pandas 數據框中,我正在尋找對其進行操作的最快方法。例如,假設我想刪除一列中值為“1”的所有行。這是我的最小工作示例:# Create dummy data arrays and pandas dataframearray_size = int(5e6)array1 = np.random.rand(array_size)array2 = np.random.rand(array_size)array3 = np.random.rand(array_size)array_condition = np.random.randint(0, 3, size=array_size)df = pd.DataFrame({'array_condition': array_condition, 'array1': array1, 'array2': array2, 'array3': array3})def method1():    df_new = df.drop(df[df.array_condition == 1].index)編輯:正如 Henry Yik 在評論中指出的,更快的 Pandas 方法是這樣的:def method1b():    df_new = df[df.array_condition != 1]我相信 Pandas 在這種事情上可能會很慢,所以我還使用 numpy 實現了一個方法,將每一列作為一個單獨的數組處理:def method2():    masking = array_condition != 1    array1_new = array1[masking]    array2_new = array2[masking]    array3_new = array3[masking]    array_condition_new = array_condition[masking]    結果:%timeit method1()625 ms ± 7.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)%timeit methodb()158 ms ± 7.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)%timeit method2()138 ms ± 3.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)因此,我們確實看到使用 numpy 的性能略有顯著提升。然而,這是以可讀性低得多的代碼為代價的(即必須創建一個掩碼并將其應用于每個數組)。這種方法似乎不像我有 30 列數據那樣可擴展,我需要很多代碼行來將掩碼應用于每個數組!此外,允許可選列會很有用,因此此方法可能會在嘗試對空數組進行操作時失敗。因此,我有兩個問題:1)在numpy中是否有更清潔/更靈活的方法來實現這一點?2)或者更好,我可以在這里使用任何更高性能的方法嗎?例如 JIT(numba?)、Cython 還是其他?PS,在實踐中,可以使用就地操作,一旦數據被丟棄,就用新數組替換舊數組
查看完整描述

2 回答

?
慕的地8271018

TA貢獻1796條經驗 獲得超4個贊

第 1 部分:Pandas 和(也許)Numpy

比較您的method1bmethod2

  • method1b生成一個DataFrame,這可能是你想要的,

  • method2生成一個Numpy 數組,因此要獲得完全可比較的結果,您應該隨后從中生成一個DataFrame。

所以我將您的方法2更改為:

def method2():

    masking = array_condition != 1

    array1_new = array1[masking]

    array2_new = array2[masking]

    array3_new = array3[masking]

    array_condition_new = array_condition[masking]

    df_new = pd.DataFrame({ 'array_condition': array_condition[masking],

        'array1': array1_new, 'array2': array2_new, 'array3': array3_new})

然后比較執行時間(使用%timeit)。

結果是我的method2 (擴展)版本的執行時間 比method1b長約5%(請自行檢查)。

所以我的觀點是,只要是單一的操作,可能還是和Pandas在一起比較好。

但是,如果您想在源 DataFrame 上按順序執行幾個操作和/或您對Numpy數組的結果感到滿意,那么值得:

  • 調用arr = df.values以獲取底層Numpy數組。

  • 使用Numpy方法對其執行所有必需的操作。

  • (可選)從最終結果創建一個 DataFrame。

我嘗試了method1b的Numpy版本:

def method3():
    a = df.values
    arr = a[a[:,0] != 1]

但執行時間要長約40%。

原因可能是Numpy數組具有相同類型的所有元素,因此array_condition列被強制浮動,然后創建整個Numpy數組,這需要一些時間。

第 2 部分:Numpy 和 Numba

要考慮的替代方法是使用Numba包 - 一種即時 Python 編譯器。

我做了這樣的測試:

創建了一個Numpy數組(作為初步步驟):

a = df.values

原因是 JIT 編譯的方法能夠使用Numpy方法和類型,但不能使用Pandas的方法和類型。

為了執行測試,我使用了與上面幾乎相同的方法,但使用了@njit注釋(需要來自 numba import njit):

@njit
def method4():
    arr = a[a[:,0] != 1]

這次:

  • 執行時間約為method1b時間的 45% 。

  • 但由于a = df.values已經在測試循環之前執行過,因此這個結果是否與之前的測試有可比性存在疑問。

無論如何,自己嘗試Numba,也許這對您來說是一個有趣的選擇。


查看完整回答
反對 回復 2022-06-14
?
慕妹3242003

TA貢獻1824條經驗 獲得超6個贊

您可能會發現在這里使用numpy.where很有用。它將布爾掩碼轉換為數組索引,使生活更便宜。將其與 numpy.vstack 結合可以實現一些內存便宜的操作:


def method3():

    wh = np.where(array_condition == 1)

    return np.vstack(tuple(col[wh] for col in (array1, array2, array3)))

這給出了以下時間:


>>> %timeit method2()

180 ms ± 6.66 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit method3()

96.9 ms ± 2.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

元組解包允許該操作在內存上相當輕,因為當對象被 vstack-ed 重新組合在一起時,它會更小。如果您需要直接從 DataFrame 中獲取列,則以下代碼段可能有用:


def method3b():

    wh = np.where(array_condition == 1)

    col_names = ['array1','array2','array3']

    return np.vstack(tuple(col[wh] for col in tuple(df[col_name].to_numpy() 

        for col_name in col_names)))

這允許人們從 DataFrame 中按名稱獲取列,然后在運行中對這些列進行元組解包。速度差不多:


>>> %timeit method3b()

96.6 ms ± 3.09 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)


查看完整回答
反對 回復 2022-06-14
  • 2 回答
  • 0 關注
  • 141 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號