पंडों के संचालन के दौरान प्रगति सूचक


158

मैं नियमित रूप से 15 मिलियन या उससे अधिक पंक्तियों में डेटा फ़्रेम पर पांडा संचालन करता हूं और मुझे विशेष संचालन के लिए प्रगति संकेतक तक पहुंच पसंद है।

पंडों के विभाजन-लागू-गठबंधन संचालन के लिए एक पाठ आधारित प्रगति सूचक मौजूद है?

उदाहरण के लिए, कुछ इस तरह से:

df_users.groupby(['userID', 'requestDate']).apply(feature_rollup)

जहां feature_rollupएक कुछ शामिल फ़ंक्शन है जो कई DF कॉलम लेते हैं और विभिन्न तरीकों से नए उपयोगकर्ता कॉलम बनाते हैं। इन ऑपरेशनों में बड़े डेटा फ्रेम के लिए थोड़ा समय लग सकता है इसलिए मैं जानना चाहता हूं कि क्या एक iPython नोटबुक में टेक्स्ट आधारित आउटपुट होना संभव है जो मुझे प्रगति पर अपडेट करता है।

अब तक, मैंने पायथन के लिए विहित लूप प्रगति संकेतक की कोशिश की है, लेकिन वे किसी भी सार्थक तरीके से पांडा के साथ बातचीत नहीं करते हैं।

मुझे उम्मीद है कि कुछ ऐसा है जिसे मैंने पंडों के पुस्तकालय / प्रलेखन में अनदेखा किया है जो एक विभाजन-लागू-संयोजन की प्रगति को जानने की अनुमति देता है। एक साधारण कार्यान्वयन संभवतः डेटा फ़्रेम सबसेट की कुल संख्या को देखेगा, जिस पर applyफ़ंक्शन काम कर रहा है और उन सबसेट के पूर्ण किए गए अंश के रूप में प्रगति की रिपोर्ट करता है।

क्या यह शायद कुछ ऐसा है जिसे पुस्तकालय में जोड़ने की आवश्यकता है?


क्या आपने कोड पर% prun (प्रोफ़ाइल) किया है? कभी-कभी आप पूरे फ्रेम पर संचालन कर सकते हैं इससे पहले कि आप अड़चनों को खत्म करने के लिए आवेदन करें
जेफ़

@ जेफ़: आप शर्त लगाते हैं, मैंने इससे पहले हर अंतिम प्रदर्शन को निचोड़ने के लिए किया था। मुद्दा वास्तव में छद्म मानचित्र को कम करने की सीमा पर आता है क्योंकि मैं लाखों की संख्या में पंक्तियों पर काम कर रहा हूं, इसलिए मुझे उम्मीद नहीं है कि सुपर गति बढ़ जाती है बस प्रगति पर कुछ प्रतिक्रिया चाहते हैं।
cwharland

साइथनिंग पर विचार करें: pandas.pydata.org/pandas-docs/dev/…
एंडी हेडन

@AndyHayden - जैसा कि मैंने आपके उत्तर पर टिप्पणी की है कि आपका कार्यान्वयन काफी अच्छा है और समग्र नौकरी के लिए बहुत कम समय जोड़ता है। मैंने फीचर रोलअप के अंदर तीन ऑपरेशनों को भी साइंटिफ़ाइ किया, जो उस समय के सभी पुन: प्राप्त हुए जो अब रिपोर्टिंग की प्रगति को समर्पित है। इसलिए मैं अंत में शर्त लगाता हूं कि अगर मैं पूरे समारोह में साइथन के साथ चलूं तो कुल प्रसंस्करण समय में कमी के साथ प्रगति की पट्टियां रखूंगा।
cwharland

जवाबों:


277

लोकप्रिय मांग के कारण, के tqdmलिए समर्थन जोड़ा गया है pandas। अन्य उत्तरों के विपरीत, यह निस्संदेह पंडों को धीमा नहीं करेगा - यहाँ एक उदाहरण है DataFrameGroupBy.progress_apply:

import pandas as pd
import numpy as np
from tqdm import tqdm
# from tqdm.auto import tqdm  # for notebooks

df = pd.DataFrame(np.random.randint(0, int(1e8), (10000, 1000)))

# Create and register a new `tqdm` instance with `pandas`
# (can use tqdm_gui, optional kwargs, etc.)
tqdm.pandas()

# Now you can use `progress_apply` instead of `apply`
df.groupby(0).progress_apply(lambda x: x**2)

यदि आप इस बात में रुचि रखते हैं कि यह कैसे काम करता है (और अपने कॉलबैक के लिए इसे कैसे संशोधित किया जाए), तो जीथब पर उदाहरण देखें , पीपीआई पर पूर्ण प्रलेखन , या मॉड्यूल आयात करें और चलाएं help(tqdm)

संपादित करें


मूल प्रश्न का सीधे उत्तर देने के लिए:

df_users.groupby(['userID', 'requestDate']).apply(feature_rollup)

साथ में:

from tqdm import tqdm
tqdm.pandas()
df_users.groupby(['userID', 'requestDate']).progress_apply(feature_rollup)

नोट: tqdm <= v4.8 : 4.8 के नीचे tqdm के संस्करणों के लिए, इसके बजाय tqdm.pandas()आपको करना था:

from tqdm import tqdm, tqdm_pandas
tqdm_pandas(tqdm())

5
tqdmमूल रूप से सिर्फ सादे पुनरावृत्तियों के लिए वास्तव में बनाया गया था: from tqdm import tqdm; for i in tqdm( range(int(1e8)) ): passपांडा का समर्थन हाल ही में मेरे द्वारा किए गए हैक था :)
casper.dcl

6
Btw, यदि आप Jupyter पुस्तिकाओं का उपयोग करते हैं, तो आप एक prettier बार प्राप्त करने के लिए tqdm_notebooks का उपयोग भी कर सकते हैं। पंडों के साथ मिलकर आपको वर्तमान में इसे from tqdm import tqdm_notebook; tqdm_notebook().pandas(*args, **kwargs) देखने की
grinsbaeckchen

2
संस्करण के रूप में 4.8.1 - इसके बजाय tqdm.pandas () का उपयोग करें। github.com/tqdm/tqdm/commit/…
mork

1
धन्यवाद, @ मॉर्क सही है। हम tqdmv5 की ओर (धीरे) काम कर रहे हैं जो चीजों को और अधिक संशोधित बनाता है।
कैस्पर। डीएल

1
हाल ही में वाक्य रचना की अनुशंसा के लिए, यहाँ देखें tqdm Pandas प्रलेखन: pypi.python.org/pypi/tqdm#pandas-integration
Manu CJ

18

जेफ के जवाब को ट्विस्ट करने के लिए (और इसे पुन: प्रयोज्य फ़ंक्शन के रूप में देखें)।

def logged_apply(g, func, *args, **kwargs):
    step_percentage = 100. / len(g)
    import sys
    sys.stdout.write('apply progress:   0%')
    sys.stdout.flush()

    def logging_decorator(func):
        def wrapper(*args, **kwargs):
            progress = wrapper.count * step_percentage
            sys.stdout.write('\033[D \033[D' * 4 + format(progress, '3.0f') + '%')
            sys.stdout.flush()
            wrapper.count += 1
            return func(*args, **kwargs)
        wrapper.count = 0
        return wrapper

    logged_func = logging_decorator(func)
    res = g.apply(logged_func, *args, **kwargs)
    sys.stdout.write('\033[D \033[D' * 4 + format(100., '3.0f') + '%' + '\n')
    sys.stdout.flush()
    return res

नोट: लागू प्रगति प्रतिशत अद्यतन इनलाइन । यदि आपका कार्य रुक जाता है तो यह काम नहीं करेगा।

In [11]: g = df_users.groupby(['userID', 'requestDate'])

In [12]: f = feature_rollup

In [13]: logged_apply(g, f)
apply progress: 100%
Out[13]: 
...

हमेशा की तरह आप इसे अपने समूह की वस्तुओं में एक विधि के रूप में जोड़ सकते हैं:

from pandas.core.groupby import DataFrameGroupBy
DataFrameGroupBy.logged_apply = logged_apply

In [21]: g.logged_apply(f)
apply progress: 100%
Out[21]: 
...

जैसा कि टिप्पणियों में उल्लेख किया गया है, यह एक विशेषता नहीं है कि कोर पांडा को लागू करने में रुचि होगी। लेकिन अजगर आपको कई पांडा वस्तुओं / विधियों के लिए इन्हें बनाने की अनुमति देता है (ऐसा करना काफी काम का है ... हालांकि आपको इस दृष्टिकोण को सामान्य करने में सक्षम होना चाहिए)।


मैं कहता हूं "काफी काम", लेकिन आप शायद इस पूरे समारोह को एक (अधिक सामान्य) डेकोरेटर के रूप में फिर से लिख सकते हैं।
एंडी हेडन

जेफ के पोस्ट पर विस्तार के लिए धन्यवाद। मैंने दोनों को लागू किया है और प्रत्येक के लिए मंदी काफी कम है (एक ऑपरेशन में कुल 1.1 मिनट जोड़ा गया है जिसे पूरा करने के लिए 27 मिनट लगते हैं)। इस तरह से मैं प्रगति को देख सकता हूं और इन कार्यों की तदर्थ प्रकृति को देखते हुए मुझे लगता है कि यह स्वीकार्य धीमा है।
cwharland

बहुत बढ़िया, खुशी हुई कि इससे मदद मिली। मैं वास्तव में धीमी गति से हैरान था (जब मैंने एक उदाहरण की कोशिश की), तो मुझे उम्मीद थी कि यह बहुत बुरा होगा।
एंडी हेडन

1
पोस्ट किए गए तरीकों की दक्षता में और इजाफा करने के लिए, मैं डेटा आयात के बारे में आलसी हो रहा था (गंदा सीएसवी को संभालने में बहुत अच्छा है।) और मेरी कुछ प्रविष्टियों (~ 1%) ने पूरी तरह से सम्मिलन को समाप्त कर दिया था (संपूर्ण विचार करें) !! एकल क्षेत्रों में सम्मिलित रिकॉर्ड)। विभाजन रोल-अप-संयोजन कार्यों के दौरान क्या करना है, इसके बारे में कोई अस्पष्टता नहीं थी, क्योंकि ये सुविधा रोलअप में एक बड़े पैमाने पर तेजी का कारण बनते हैं।
cwharland

1
मैं 8 मिनट के लिए नीचे हूँ ... लेकिन मैंने फीचर रोलअप (कुछ विशेषताएं -> बेहतर AUC!) में कुछ और चीजें जोड़ीं। यह 8 मिनट 12 मिलियन पंक्तियों के पड़ोस में प्रत्येक चंक के साथ प्रति चंक (दो कुल मिलाकर अभी) है। तो हाँ ... 16 मिनट HDFStore (और फीचर रोलअप में nltk सामान है) का उपयोग कर 24 मिलियन पंक्तियों पर भारी संचालन करने के लिए। काफी अच्छा। चलिए आशा करते हैं कि इंटरनेट मुझे शुरुआती अज्ञानता या
गड़बड़ी

11

इस मामले में आपको ज्यूपिटर / आईपाइथॉन नोटबुक में इसका उपयोग करने के लिए समर्थन की आवश्यकता है, जैसा कि मैंने किया था, यहां प्रासंगिक लेख के लिए एक उपयोगी मार्गदर्शिका और स्रोत है :

from tqdm._tqdm_notebook import tqdm_notebook
import pandas as pd
tqdm_notebook.pandas()
df = pd.DataFrame(np.random.randint(0, int(1e8), (10000, 1000)))
df.groupby(0).progress_apply(lambda x: x**2)

के लिए आयात विवरण में अंडरस्कोर नोट करें _tqdm_notebook। जैसा कि संदर्भित लेख में उल्लेख किया गया है, विकास देर से बीटा चरण में है।


8

किसी के लिए जो अपने कस्टम समानांतर पांडा-कोड कोड पर tqdm लागू करना चाहते हैं।

(मैंने पिछले कुछ वर्षों में समानांतरकरण के लिए कुछ पुस्तकालयों की कोशिश की, लेकिन मुझे कभी भी 100% समानांतर समाधान नहीं मिला, मुख्य रूप से लागू फ़ंक्शन के लिए, और मुझे हमेशा अपने "मैनुअल" कोड के लिए वापस आना पड़ा।)

df_multi_core - यह वह है जिसे आप कॉल करते हैं। यह स्वीकार करता है:

  1. आपकी df ऑब्जेक्ट
  2. वह फ़ंक्शन नाम जिसे आप कॉल करना चाहते हैं
  3. स्तंभों के सबसेट को फ़ंक्शन पर किया जा सकता है (समय / मेमोरी कम करने में मदद करता है)
  4. समानांतर में चलने वाली नौकरियों की संख्या (-1 या सभी कोर के लिए छोड़ दें)
  5. कोई भी अन्य df का फ़ंक्शन स्वीकार करता है (जैसे "अक्ष")

_df_split - यह एक आंतरिक सहायक फ़ंक्शन है जिसे विश्व स्तर पर चल रहे मॉड्यूल में रखा जाना है (Pool.map "प्लेसमेंट निर्भर" है), अन्यथा मैं इसे आंतरिक रूप से ढूँढूँगा।

यहाँ मेरे जीस्ट से एक कोड है (मैं वहां और पंडों के समारोह परीक्षण जोड़ूंगा):

import pandas as pd
import numpy as np
import multiprocessing
from functools import partial

def _df_split(tup_arg, **kwargs):
    split_ind, df_split, df_f_name = tup_arg
    return (split_ind, getattr(df_split, df_f_name)(**kwargs))

def df_multi_core(df, df_f_name, subset=None, njobs=-1, **kwargs):
    if njobs == -1:
        njobs = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=njobs)

    try:
        splits = np.array_split(df[subset], njobs)
    except ValueError:
        splits = np.array_split(df, njobs)

    pool_data = [(split_ind, df_split, df_f_name) for split_ind, df_split in enumerate(splits)]
    results = pool.map(partial(_df_split, **kwargs), pool_data)
    pool.close()
    pool.join()
    results = sorted(results, key=lambda x:x[0])
    results = pd.concat([split[1] for split in results])
    return results

Bellow tqdm "progress_apply" के साथ समानांतर लागू होने के लिए एक परीक्षण कोड है ।

from time import time
from tqdm import tqdm
tqdm.pandas()

if __name__ == '__main__': 
    sep = '-' * 50

    # tqdm progress_apply test      
    def apply_f(row):
        return row['c1'] + 0.1
    N = 1000000
    np.random.seed(0)
    df = pd.DataFrame({'c1': np.arange(N), 'c2': np.arange(N)})

    print('testing pandas apply on {}\n{}'.format(df.shape, sep))
    t1 = time()
    res = df.progress_apply(apply_f, axis=1)
    t2 = time()
    print('result random sample\n{}'.format(res.sample(n=3, random_state=0)))
    print('time for native implementation {}\n{}'.format(round(t2 - t1, 2), sep))

    t3 = time()
    # res = df_multi_core(df=df, df_f_name='apply', subset=['c1'], njobs=-1, func=apply_f, axis=1)
    res = df_multi_core(df=df, df_f_name='progress_apply', subset=['c1'], njobs=-1, func=apply_f, axis=1)
    t4 = time()
    print('result random sample\n{}'.format(res.sample(n=3, random_state=0)))
    print('time for multi core implementation {}\n{}'.format(round(t4 - t3, 2), sep))

आउटपुट में आप समानांतरकरण के बिना चलने के लिए 1 प्रगति बार देख सकते हैं, और समानांतर-चलने के साथ प्रति-कोर प्रगति बार। एक मामूली हिचप है और कभी-कभी बाकी कोर एक ही बार में दिखाई देते हैं, लेकिन फिर भी मुझे लगता है कि जब से आप प्रगति के आंकड़े प्राप्त करते हैं, तब से यह कोर (प्रति सेकंड / कुल रिकॉर्ड और पूर्व के लिए)

यहाँ छवि विवरण दर्ज करें

इस महान पुस्तकालय के लिए @abcdaa धन्यवाद!


1
धन्यवाद @mork - github.com/tqdm/tqdm/wiki/How-to-make-a-great-Progress-Bar में जोड़ने के लिए स्वतंत्र महसूस करें या github.com/tqdm-tqdm/wiki
casper

धन्यवाद, लेकिन इन हिस्से को बदलना पड़ा: try: splits = np.array_split(df[subset], njobs) except ValueError: splits = np.array_split(df, njobs)क्योंकि ValueError के बजाय KeyError अपवाद के कारण, सभी मामलों को संभालने के लिए अपवाद में बदलें।
मारियस

धन्यवाद @mork - यह उत्तर अधिक होना चाहिए।
एंडी

5

आप इसे डेकोरेटर के साथ आसानी से कर सकते हैं

from functools import wraps 

def logging_decorator(func):

    @wraps
    def wrapper(*args, **kwargs):
        wrapper.count += 1
        print "The function I modify has been called {0} times(s).".format(
              wrapper.count)
        func(*args, **kwargs)
    wrapper.count = 0
    return wrapper

modified_function = logging_decorator(feature_rollup)

इसके बाद बस संशोधित_फंक्शन का उपयोग करें (और जब आप इसे प्रिंट करना चाहते हैं तो बदल दें)


1
स्पष्ट चेतावनी यह आपके कार्य को धीमा कर देगा! तुम भी यह हो सकता है अद्यतन के साथ अद्यतन stackoverflow.com/questions/5426546/… उदाहरण के लिए गिनती / प्रतिशत के रूप में लेन।
एंडी हेडन

हां - आपके पास ऑर्डर (समूहों की संख्या) होगी, इसलिए आपकी अड़चन के आधार पर यह अंतर हो सकता है
जेफ

शायद करने के लिए सहज बात यह है कि इसे एक logged_apply(g, func)फ़ंक्शन में लपेटें , जहां आप ऑर्डर करने के लिए एक्सेस करेंगे, और शुरुआत से लॉग इन कर सकते हैं।
एंडी हेडन

मैंने अपने उत्तर में उपरोक्त किया है, यह भी चुटीले प्रतिशत अद्यतन। वास्तव में मैं तुम्हारा काम नहीं कर सकता ... मुझे लगता है कि रैप्स बिट के साथ। यदि आपका इसे लागू करने के लिए उपयोग कर रहा है तो यह वैसे भी महत्वपूर्ण नहीं है।
एंडी हेडन

1

मैंने जेफ के जवाब को बदल दिया है , जिसमें कुल शामिल हैं, ताकि आप प्रगति को ट्रैक कर सकें और हर X पुनरावृत्तियों को प्रिंट करने के लिए एक चर (यह वास्तव में प्रदर्शन में बहुत सुधार करता है, अगर "print_at" बहुत अधिक है)

def count_wrapper(func,total, print_at):

    def wrapper(*args):
        wrapper.count += 1
        if wrapper.count % wrapper.print_at == 0:
            clear_output()
            sys.stdout.write( "%d / %d"%(calc_time.count,calc_time.total) )
            sys.stdout.flush()
        return func(*args)
    wrapper.count = 0
    wrapper.total = total
    wrapper.print_at = print_at

    return wrapper

Clear_output () फ़ंक्शन से है

from IPython.core.display import clear_output

यदि नहीं तो आइपीथॉन एंडी हेडन के जवाब के बिना ऐसा करता है

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