Từ những nhận xét ở trên, có vẻ như điều này đã được lên kế hoạch cho pandas
một thời gian (cũng có một rosetta
dự án thú vị mà tôi vừa mới nhận thấy).
Tuy nhiên, cho đến khi mọi chức năng song song được tích hợp vào pandas
, tôi nhận thấy rằng rất dễ dàng để viết các phép tăng cường song song hiệu quả & không sao chép bộ nhớ để pandas
trực tiếp sử dụng cython
+ OpenMP và C ++.
Dưới đây là một ví dụ ngắn về cách viết một tổng của nhóm song song, có cách sử dụng như sau:
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)
và đầu ra là:
sum
key
0 6
1 11
2 4
Lưu ý Không nghi ngờ gì nữa, chức năng của ví dụ đơn giản này cuối cùng sẽ là một phần của pandas
. Tuy nhiên, một số thứ sẽ tự nhiên hơn khi song song hóa trong C ++ trong một thời gian, và điều quan trọng là phải biết việc kết hợp điều này vào dễ dàng như thế nào pandas
.
Để thực hiện việc này, tôi đã viết một phần mở rộng tệp nguồn đơn đơn giản có mã sau.
Nó bắt đầu với một số nhập khẩu và định nghĩa loại
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
Loại C ++ unordered_map
là để tính tổng theo một luồng duy nhất và vector
là để tính tổng theo tất cả các luồng.
Bây giờ đến chức năng sum
. Nó bắt đầu với các chế độ xem bộ nhớ đã nhập để truy cập nhanh:
def sum(crit, vals):
cdef int64_t[:] crit_view = crit.values
cdef int64_t[:] vals_view = vals.values
Hàm tiếp tục bằng cách chia nửa đều cho các luồng (ở đây được mã hóa cứng thành 4) và mỗi luồng tính tổng các mục trong phạm vi của nó:
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)
Khi các chủ đề đã hoàn thành, hàm hợp nhất tất cả các kết quả (từ các phạm vi khác nhau) thành một 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)
Tất cả những gì còn lại là tạo một DataFrame
và trả về kết quả:
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