จากความคิดเห็นด้านบนดูเหมือนว่าจะมีการวางแผนไว้pandas
ระยะหนึ่งแล้ว (นอกจากนี้ยังมีrosetta
โครงการที่น่าสนใจซึ่งฉันเพิ่งสังเกตเห็น)
อย่างไรก็ตามจนกว่าทุกฟังก์ชันการทำงานแบบขนานจะรวมอยู่ในpandas
ฉันสังเกตเห็นว่ามันง่ายมากที่จะเขียนการขยายแบบขนานที่มีประสิทธิภาพและไม่ใช่หน่วยความจำเพื่อคัดลอกpandas
โดยตรงโดยใช้cython
+ OpenMPและ C ++
นี่คือตัวอย่างสั้น ๆ ของการเขียน 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
unordered_map
ประเภทC ++ ใช้สำหรับการรวมโดยเธรดเดียวและ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