समानांतर में एक समूह पंडों DataFrame के लिए एक कार्य को लागू करने में कुशलता से


89

मुझे अक्सर एक बहुत बड़े DataFrame(मिश्रित डेटा प्रकारों) के समूहों में एक फ़ंक्शन लागू करने की आवश्यकता होती है और कई कोर का लाभ उठाना चाहते हैं।

मैं समूहों से एक इटरेटर बना सकता हूं और मल्टीप्रोसेसिंग मॉड्यूल का उपयोग कर सकता हूं, लेकिन यह कुशल नहीं है क्योंकि प्रक्रियाओं के बीच संदेश के लिए प्रत्येक समूह और फ़ंक्शन के परिणामों को चुना जाना चाहिए।

क्या अचार से बचने या यहां तक ​​कि DataFrameपूरी तरह से नकल से बचने का कोई तरीका है ? ऐसा लगता है कि मल्टीप्रोसेसिंग मॉड्यूल के साझा मेमोरी फ़ंक्शन numpyसरणियों तक सीमित हैं । क्या कोई अन्य विकल्प भी हैं?


जहां तक ​​मुझे पता है, मनमानी वस्तुओं को साझा करने का कोई तरीका नहीं है। मुझे आश्चर्य हो रहा है, यदि अचार मल्टीप्रोसेसिंग के माध्यम से प्राप्त होने की तुलना में अधिक समय लेता है। हो सकता है कि आपको रिश्तेदार अचार के समय को कम करने के लिए प्रत्येक प्रक्रिया के लिए बड़े वर्क-पैकेज बनाने की संभावना की तलाश करनी चाहिए। जब आप समूह बनाते हैं तो मल्टीप्रोसेसिंग का उपयोग करने की एक और संभावना होगी।
सेबस्टियन वर्कर

3
मैं ऐसा कुछ करता हूं, लेकिन UWSGI, फ्लास्क और प्रीफ़ॉर्किंग का उपयोग करते हुए: मैं पांडा डेटाफ़्रेम को एक प्रक्रिया में लोड करता हूं, इसे x बार फोर्क करता हूं (इसे एक साझा मेमोरी ऑब्जेक्ट बनाता हूं) और फिर उन प्रक्रियाओं को किसी अन्य अजगर से कॉल करता हूं जहां मैं परिणाम छुपाता हूं। एटीएम मैं JSON का उपयोग एक संचार प्रक्रिया के रूप में करता हूं, लेकिन यह आ रहा है (फिर भी अत्यधिक प्रयोगात्मक अभी भी): pandas.pydata.org/pandas-docs/dev/io.html#msgpack-experimental
Carst

वैसे, क्या आपने कभी एचडीएफ 5 को चैंकिंग के साथ देखा? (HDF5 समवर्ती लेखन के लिए नहीं बचा है, लेकिन आप अलग-अलग फ़ाइलों और अंत में
संक्षिप्त

7
इसे 0.14 के लिए लक्षित किया जाएगा, इस मुद्दे को देखें: github.com/pydata/pandas/issues/5751
Jeff

4
@ जेफ को 0.15 = (
अजगर

जवाबों:


12

उपरोक्त टिप्पणियों से, ऐसा लगता है कि यह pandasकुछ समय के लिए योजनाबद्ध है (एक दिलचस्प-दिखने वाली rosettaपरियोजना भी है जिसे मैंने अभी देखा है)।

हालाँकि, जब तक कि हर समानांतर कार्यक्षमता को शामिल नहीं किया जाता pandas, मैंने देखा कि pandasसीधे cython+ ओपनएमपी और सी ++ का उपयोग करके कुशल और गैर-मेमोरी-समानांतर समानांतर वृद्धि लिखना बहुत आसान है ।

यहाँ एक समानांतर समूह-योग लिखने का एक छोटा उदाहरण है, जिसका उपयोग कुछ इस प्रकार है:

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.