पंडों DataFrame लागू करें () सभी कोर का उपयोग करें?


104

अगस्त 2017 तक, पंडस डेटाफैम.apply () दुर्भाग्य से अभी भी एक कोर के साथ काम करने तक सीमित है, जिसका अर्थ है कि मल्टी-कोर मशीन आपके चलाने के समय के अधिकांश कम्प्यूट-टाइम को बर्बाद कर देगा df.apply(myfunc, axis=1)

समानांतर में डेटाफ़्रेम पर लागू करने के लिए आप अपने सभी कोर का उपयोग कैसे कर सकते हैं?

जवाबों:


79

आप swifterपैकेज का उपयोग कर सकते हैं :

pip install swifter

यह पंडों के लिए एक प्लगइन के रूप में काम करता है, जिससे आप applyफ़ंक्शन को पुन : उपयोग कर सकते हैं:

import swifter

def some_function(data):
    return data * 10

data['out'] = data['in'].swifter.apply(some_function)

यह स्वचालित रूप से फ़ंक्शन को समानांतर करने के लिए सबसे कुशल तरीके का पता लगाएगा, भले ही यह वेक्टरकृत हो (जैसा कि ऊपर के उदाहरण में) या नहीं।

अधिक उदाहरण और एक प्रदर्शन तुलना GitHub पर उपलब्ध है। ध्यान दें कि पैकेज सक्रिय विकास के तहत है, इसलिए एपीआई बदल सकता है।

यह भी ध्यान दें कि यह स्ट्रिंग कॉलम के लिए स्वचालित रूप से काम नहीं करेगा । स्ट्रिंग्स का उपयोग करते समय, स्विफ्ट "सरल" पंडों में वापस आ जाएगी apply, जो समानांतर नहीं होगी। इस स्थिति में, इसका उपयोग करने के लिए मजबूर करने से भी daskप्रदर्शन में सुधार नहीं होगा, और आप अपने डेटासेट को मैन्युअल रूप से विभाजित करने और उपयोग करने के लिए समानांतरmultiprocessing करना बेहतर होगा ।


1
हमारी शुद्ध जिज्ञासा, क्या समानांतर लागू करते समय उपयोग की जाने वाली कोर की संख्या को सीमित करने का एक तरीका है? मेरे पास एक साझा सर्वर है इसलिए अगर मैं सभी 32 कोर हड़प लूं तो कोई भी खुश नहीं होगा।
मिकीसिम खितोविच

1
@MaximHaytovich मुझे नहीं पता। Swifter पृष्ठभूमि में dask का उपयोग करता है, इसलिए शायद यह इन सेटिंग्स का सम्मान करता है: stackoverflow.com/a/40633117/435093 - अन्यथा मैं GitHub पर एक मुद्दा खोलने की सलाह दूंगा। लेखक बहुत उत्तरदायी है।
19

@ धन्यवाद धन्यवाद! इसे थोड़ा और खोदेंगे। यह वैसे भी विंडोज़ सर्वर पर काम नहीं कर रहा है - बस खिलौना कार्य पर कुछ भी नहीं कर रहा है
मक्सिम खैतोविच

क्या आप इसका जवाब देने में मेरी मदद कर सकते हैं: - stackoverflow.com/questions/53561794/…
ak3191

2
स्ट्रिंग्स के लिए, बस allow_dask_on_strings(enable=True)इस तरह जोड़ें : df.swifter.allow_dask_on_strings(enable=True).apply(some_function) स्रोत: github.com/jmcarpenter2/swifter/issues/45
सुमित सिडाना

103

Dask के map_partitions का उपयोग करने का सबसे सरल तरीका है । आपको इन आयातों की आवश्यकता है (आपको इसकी आवश्यकता होगी pip install dask):

import pandas as pd
import dask.dataframe as dd
from dask.multiprocessing import get

और वाक्यविन्यास है

data = <your_pandas_dataframe>
ddata = dd.from_pandas(data, npartitions=30)

def myfunc(x,y,z, ...): return <whatever>

res = ddata.map_partitions(lambda df: df.apply((lambda row: myfunc(*row)), axis=1)).compute(get=get)  

(मेरा मानना ​​है कि यदि आपके पास 16 कोर हैं तो 30 उपयुक्त विभाजन है)। पूर्णता के लिए, मैंने अपनी मशीन (16 कोर) पर अंतर समयबद्ध किया:

data = pd.DataFrame()
data['col1'] = np.random.normal(size = 1500000)
data['col2'] = np.random.normal(size = 1500000)

ddata = dd.from_pandas(data, npartitions=30)
def myfunc(x,y): return y*(x**2+1)
def apply_myfunc_to_DF(df): return df.apply((lambda row: myfunc(*row)), axis=1)
def pandas_apply(): return apply_myfunc_to_DF(data)
def dask_apply(): return ddata.map_partitions(apply_myfunc_to_DF).compute(get=get)  
def vectorized(): return myfunc(data['col1'], data['col2']  )

t_pds = timeit.Timer(lambda: pandas_apply())
print(t_pds.timeit(number=1))

28.16970546543598

t_dsk = timeit.Timer(lambda: dask_apply())
print(t_dsk.timeit(number=1))

2.708152851089835

t_vec = timeit.Timer(lambda: vectorized())
print(t_vec.timeit(number=1))

.010668013244867325

पांडा से जाने वाले 10 स्पीडअप का एक कारक देते हुए विभाजन पर डस्क लागू होते हैं। बेशक, यदि आपके पास एक फ़ंक्शन है जिसे आप वेक्टर कर सकते हैं, तो आपको चाहिए - इस मामले में फ़ंक्शन ( y*(x**2+1)) तुच्छ रूप से वेक्टरीकृत है, लेकिन ऐसी बहुत सी चीजें हैं जो वेक्टर करना असंभव हैं।


2
जानकर बहुत अच्छा लगा, पोस्ट करने के लिए धन्यवाद। क्या आप बता सकते हैं कि आपने 30 विभाजन क्यों चुने? क्या इस मूल्य को बदलते समय प्रदर्शन बदल जाता है?
एंड्रयू एल

4
@AndrewL मुझे लगता है कि प्रत्येक विभाजन एक अलग प्रक्रिया द्वारा सेवित है, और 16 कोर के साथ मैं यह मानता हूं कि 16 या 32 प्रक्रियाएं एक साथ चल सकती हैं। मैंने इसे आज़माया, और प्रदर्शन 32 विभाजनों में सुधार करने के लिए लगता है, लेकिन आगे बढ़ने का कोई लाभकारी प्रभाव नहीं है। मुझे लगता है कि क्वाड-कोर मशीन के साथ आप 8 विभाजन चाहते हैं, आदि। ध्यान दें कि मैंने 16 और 32 के बीच कुछ सुधार पर ध्यान दिया था, इसलिए मुझे लगता है कि आप वास्तव में 2x $ NUM_PROCESSORS चाहते हैं
रोको मिजिक

9
केवल एक चीज हैThe get= keyword has been deprecated. Please use the scheduler= keyword instead with the name of the desired scheduler like 'threads' or 'processes'
wordsforthewise

6
Dask v0.20.0 और उसके लिए, ddata.map_partitions (lambda df: df.apply (lambda row: myfunc (* row)), धुरी = 1) का उपयोग करें। कंप्यूट (अनुसूचक = 'प्रक्रियाओं'), या एक में से एक का उपयोग करें। अन्य अनुसूचक विकल्प। वर्तमान कोड "TypeError: the get = कीवर्ड हटा दिया गया है। कृपया वांछित शेड्यूलर जैसे 'थ्रेड्स' या 'प्रोसेस' के नाम के बजाय शेड्यूलर = कीवर्ड का उपयोग करें
mork

1
सुनिश्चित करें कि इससे पहले कि आप ऐसा करें, डेटाफ्रेम में कोई डुप्लिकेट इंडेक्स नहीं है क्योंकि यह फेंकता है ValueError: cannot reindex from a duplicate axis। उसके चारों ओर जाने के लिए, या तो आपको डुप्लिकेट इंडेक्स को हटा देना चाहिए df = df[~df.index.duplicated()]या अपने इंडेक्स को रीसेट करना होगा df.reset_index(inplace=True)
हबीब करबासियन

24

आप pandarallelइसके बजाय कोशिश कर सकते हैं : अपने सभी सीपीयू (लिनक्स और मैकओएस पर) अपने पंडों के संचालन को समानांतर करने के लिए एक सरल और कुशल उपकरण

  • समानांतरकरण की एक लागत है (नई प्रक्रियाओं को संस्थापित करना, साझा मेमोरी, आदि के माध्यम से डेटा भेजना ...), इसलिए समानांतरीकरण केवल तभी प्रभावी होता है जब समानांतर करने के लिए गणना की मात्रा पर्याप्त होती है। डेटा की बहुत कम मात्रा के लिए, समानता का उपयोग करना हमेशा इसके लायक नहीं होता है।
  • लागू किए गए कार्य लंबोदर कार्य नहीं होने चाहिए।
from pandarallel import pandarallel
from math import sin

pandarallel.initialize()

# FORBIDDEN
df.parallel_apply(lambda x: sin(x**2), axis=1)

# ALLOWED
def func(x):
    return sin(x**2)

df.parallel_apply(func, axis=1)

देख https://github.com/nalepae/pandarallel


हैलो, मैं एक समस्या को हल कर सकता हूं, इसमें pandarallel का उपयोग करके एक एरर है: AttributeError: स्थानीय ऑब्जेक्ट 'pick_worker' को अचार नहीं कर सकता। <स्थानीय>। क्या आप इसमें मेरी मदद कर सकते हैं?
एलेक्स कैम

@ Alex Sry मैं उस मॉड्यूल का डेवलपर नहीं हूं। आपके कोड क्या दिखते हैं? आप अपने "अंदर के कार्यों" को वैश्विक घोषित करने का प्रयास कर सकते हैं? (सिर्फ अनुमान)
G_KOBELIEF

@AlexCam आपके फ़ंक्शन को अन्य फ़ंक्शन के बाहर परिभाषित किया जाना चाहिए ताकि अजगर इसे मल्टीप्रोसेसिंग के लिए अचार कर सके
केनन

1
@G_KOBELIEF पायथन के साथ> 3.6 हम pandaparallel के साथ लंबो फंक्शन का उपयोग कर सकते हैं
user110244

18

यदि आप देशी अजगर में रहना चाहते हैं:

import multiprocessing as mp

with mp.Pool(mp.cpu_count()) as pool:
    df['newcol'] = pool.map(f, df['col'])

डेटाफ़्रेम के fस्तंभ colके समानांतर फैशन में फ़ंक्शन लागू करेगाdf


इस मैं की तरह एक दृष्टिकोण के बाद एक मिल ValueError: Length of values does not match length of indexसे __setitem__में pandas/core/frame.py। सुनिश्चित नहीं है कि मैंने कुछ गलत किया है, या यदि असाइन करना df['newcol']थ्रेडसेफ़ नहीं है।
राठल

2
यदि आप लंबाई df के साथ मेल खाते हैं, और फिर df ['newcol'] = temp_result कर रहे हैं, तो आप जाँच करने की अनुमति देने के लिए एक मध्यस्थ temp_result सूची में पूल लिख सकते हैं।
ओलिवियर क्रूचेंट सेप

आप नए स्तंभ बनाने का मतलब है? आप क्या उपयोग करेंगे?
ओलिवियर क्रूचेंट

हां, डेटाफ्रेम के नए कॉलम में नक्शे के परिणाम को निर्दिष्ट करना। फ़ंक्शन च को भेजे गए प्रत्येक चंक के परिणाम की सूची को मैप नहीं करता है? तो क्या होता है जब आप उस कॉलम 'newcol' को असाइन करते हैं? पंडों और पायथन 3 का उपयोग करना
मीना

यह वास्तव में वास्तव में चिकनी काम करता है! क्या तुमने कोशिश की? यह df की समान लंबाई की सूची बनाता है, जैसा कि भेजा गया था वैसा ही आदेश। यह शाब्दिक रूप से समानांतर फैशन में c2 = f (c1) करता है। अजगर में बहु-प्रक्रिया का कोई सरल तरीका नहीं है। प्रदर्शन-वार ऐसा लगता है कि रे अच्छी चीजें भी कर सकते हैं (जैसा कि ddasascience.com/… ), लेकिन यह उतना परिपक्व नहीं है और स्थापना हमेशा मेरे अनुभव में आसानी से नहीं होती है
ओलिवियर क्रुचेंट

2

यहां स्केलेर बेस ट्रांसफार्मर का एक उदाहरण दिया गया है, जिसमें पांडा लागू होते हैं

import multiprocessing as mp
from sklearn.base import TransformerMixin, BaseEstimator

class ParllelTransformer(BaseEstimator, TransformerMixin):
    def __init__(self,
                 n_jobs=1):
        """
        n_jobs - parallel jobs to run
        """
        self.variety = variety
        self.user_abbrevs = user_abbrevs
        self.n_jobs = n_jobs
    def fit(self, X, y=None):
        return self
    def transform(self, X, *_):
        X_copy = X.copy()
        cores = mp.cpu_count()
        partitions = 1

        if self.n_jobs <= -1:
            partitions = cores
        elif self.n_jobs <= 0:
            partitions = 1
        else:
            partitions = min(self.n_jobs, cores)

        if partitions == 1:
            # transform sequentially
            return X_copy.apply(self._transform_one)

        # splitting data into batches
        data_split = np.array_split(X_copy, partitions)

        pool = mp.Pool(cores)

        # Here reduce function - concationation of transformed batches
        data = pd.concat(
            pool.map(self._preprocess_part, data_split)
        )

        pool.close()
        pool.join()
        return data
    def _transform_part(self, df_part):
        return df_part.apply(self._transform_one)
    def _transform_one(self, line):
        # some kind of transformations here
        return line

अधिक जानकारी के लिए देखें https://towardsdatascience.com/4-easy-steps-to-improve-your-machine-learning-code-performance-88a0b0eeffa8


0

सभी (भौतिक या तार्किक) कोर का उपयोग करने के लिए, आप और के mapplyविकल्प के रूप में प्रयास कर सकते हैं ।swifterpandarallel

आप कोर की राशि (और चंचल व्यवहार) को init पर सेट कर सकते हैं:

import pandas as pd
import mapply

mapply.init(n_workers=-1)

...

df.mapply(myfunc, axis=1)

डिफ़ॉल्ट रूप से ( n_workers=-1), पैकेज सिस्टम पर उपलब्ध सभी भौतिक सीपीयू का उपयोग करता है। यदि आपका सिस्टम हाइपर-थ्रेडिंग का उपयोग करता है (आमतौर पर भौतिक सीपीयू की मात्रा दोगुनी होती है), mapplyतो सिस्टम पर अन्य प्रक्रियाओं पर मल्टीप्रोसेसिंग पूल को प्राथमिकता देने के लिए एक अतिरिक्त कार्यकर्ता को स्पॉन करेगा।

आपकी परिभाषा के आधार पर all your cores, आप इसके बजाय सभी तार्किक कोर का उपयोग कर सकते हैं (सावधान रहें कि इस तरह सीपीयू-बाउंड प्रक्रियाएं भौतिक सीपीयू के लिए लड़ रही होंगी, जो आपके ऑपरेशन को धीमा कर सकती हैं):

import multiprocessing
n_workers = multiprocessing.cpu_count()

# or more explicit
import psutil
n_workers = psutil.cpu_count(logical=True)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.