簡體   English   中英

將功能有效地並行應用到已分組的熊貓DataFrame

[英]Efficiently applying a function to a grouped pandas DataFrame in parallel

我經常需要將函數應用於非常大的DataFrame (混合數據類型)的組,並想利用多個內核。

我可以從組中創建一個迭代器並使用多處理模塊,但是這樣做效率不高,因為必須對每個組和函數的結果進行腌制,以便在進程之間進行消息傳遞。

有什么方法可以避免酸洗,甚至完全避免復制DataFrame嗎? 看來,多處理模塊的共享內存功能僅限於numpy數組。 還有其他選擇嗎?

從上面的評論來看,這似乎是為pandas計划的一段時間(還有一個我剛剛注意到的有趣的rosetta項目 )。

但是,在將每種並行功能都整合到pandas ,我注意到,使用cython + OpenMP和C ++直接向pandas編寫高效且無內存復制的並行增強非常容易。

這是編寫並行groupby-sum的簡短示例,其用法如下所示:

import pandas as pd
import para_group_demo

df = pd.DataFrame({'a': [1, 2, 1, 2, 1, 1, 0], 'b': range(7)})
print para_group_demo.sum(df.a, df.b)

輸出為:

     sum
key     
0      6
1      11
2      4

注意毫無疑問,這個簡單示例的功能最終將成為pandas一部分。 但是,有些事情在C ++中進行並行化會更加自然,並且重要的是要意識到將其組合到pandas是多么容易。


為此,我編寫了一個簡單的單一源文件擴展名,其代碼如下。

它從一些導入和類型定義開始

from libc.stdint cimport int64_t, uint64_t
from libcpp.vector cimport vector
from libcpp.unordered_map cimport unordered_map

cimport cython
from cython.operator cimport dereference as deref, preincrement as inc
from cython.parallel import prange

import pandas as pd

ctypedef unordered_map[int64_t, uint64_t] counts_t
ctypedef unordered_map[int64_t, uint64_t].iterator counts_it_t
ctypedef vector[counts_t] counts_vec_t

C ++ unordered_map類型用於單個線程求和, vector用於所有線程求和。

現在到函數sum 它從鍵入的內存視圖開始以快速訪問:

def sum(crit, vals):
    cdef int64_t[:] crit_view = crit.values
    cdef int64_t[:] vals_view = vals.values

該函數通過將半等值除以線程(在此硬編碼為4),並使每個線程將其范圍內的條目相加來繼續:

    cdef uint64_t num_threads = 4
    cdef uint64_t l = len(crit)
    cdef uint64_t s = l / num_threads + 1
    cdef uint64_t i, j, e
    cdef counts_vec_t counts
    counts = counts_vec_t(num_threads)
    counts.resize(num_threads)
    with cython.boundscheck(False):
        for i in prange(num_threads, nogil=True): 
            j = i * s
            e = j + s
            if e > l:
                e = l
            while j < e:
                counts[i][crit_view[j]] += vals_view[j]
                inc(j)

線程完成后,該函數將所有結果(來自不同范圍)合並到一個unordered_map

    cdef counts_t total
    cdef counts_it_t it, e_it
    for i in range(num_threads):
        it = counts[i].begin()
        e_it = counts[i].end()
        while it != e_it:
            total[deref(it).first] += deref(it).second
            inc(it)        

剩下的就是創建一個DataFrame並返回結果:

    key, sum_ = [], []
    it = total.begin()
    e_it = total.end()
    while it != e_it:
        key.append(deref(it).first)
        sum_.append(deref(it).second)
        inc(it)

    df = pd.DataFrame({'key': key, 'sum': sum_})
    df.set_index('key', inplace=True)
    return df

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM