পান্ডাস অপারেশনের সময় অগ্রগতি সূচক


158

আমি নিয়মিতভাবে 15 মিলিয়ন বা তার বেশি সারি বেশি ডেটা ফ্রেমে পান্ডাস অপারেশন করি এবং আমি নির্দিষ্ট ক্রিয়াকলাপের জন্য একটি অগ্রগতি সূচক অ্যাক্সেস পেতে পছন্দ করি।

পান্ডাস স্প্লিট-প্রয়োগ-সম্মিলন ক্রিয়াকলাপগুলির জন্য কি কোনও পাঠ্য ভিত্তিক অগ্রগতি সূচক উপস্থিত রয়েছে?

উদাহরণস্বরূপ, কিছুতে:

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

যেখানে feature_rollupকিছুটা জড়িত ফাংশন যা বহু ডিএফ কলাম নেয় এবং বিভিন্ন পদ্ধতির মাধ্যমে নতুন ব্যবহারকারী কলাম তৈরি করে। এই ক্রিয়াকলাপগুলি বড় ডেটা ফ্রেমের জন্য কিছুটা সময় নিতে পারে তাই আমি জানতে চাই যে কোনও আইপিথন নোটবুকটিতে পাঠ্য ভিত্তিক আউটপুট পাওয়া সম্ভব যা আমাকে অগ্রগতি সম্পর্কে আপডেট করে।

এখনও অবধি, আমি পাইথনের পক্ষে ক্যানোনিকাল লুপ অগ্রগতি সূচকগুলি চেষ্টা করেছি তবে তারা কোনও অর্থবহ উপায়ে পান্ডাদের সাথে যোগাযোগ করে না।

আমি আশা করছি পান্ডাস লাইব্রেরি / ডকুমেন্টেশনে এমন কিছু আছে যা আমি উপেক্ষা করেছি যা একটি বিভক্ত-প্রয়োগ-সংযুক্তির অগ্রগতি জানতে পারে। একটি সাধারণ বাস্তবায়ন সম্ভবত applyফাংশনটি কীভাবে কাজ করছে এমন ডেটা ফ্রেম সাবটেটগুলির সংখ্যার দিকে নজর দেবে এবং সেই সাবসেটগুলির সম্পূর্ণ ভগ্নাংশ হিসাবে অগ্রগতির প্রতিবেদন করবে।

এটি সম্ভবত এমন কিছু যা লাইব্রেরিতে যুক্ত করা দরকার?


আপনি কোডটিতে একটি% প্রুন (প্রোফাইল) করেছেন? কখনও কখনও আপনি বাধাগুলি দূর করতে আবেদনের আগে পুরো ফ্রেমে অপারেশন করতে পারেন
জেফ

@ জেফ: আপনি বাজি ধরুন, আমি এর আগে প্রতিটি শেষ সম্পাদনের কাজ শেষ করে ফেলতে পেরেছিলাম to বিষয়টি সত্যই সিউডো ম্যাপ-হ্রাস সীমানায় নেমে আসে আমি যেদিকে কাজ করছি যেহেতু সারিগুলি কয়েক মিলিয়নতে রয়েছে তাই আমি আশা করি না যে উচ্চ গতির বৃদ্ধি কেবল অগ্রগতি সম্পর্কে কিছু প্রতিক্রিয়া চাই।
cwharland

সিথোনাইজিংয়ের বিষয়টি বিবেচনা করুন: পান্ডাস.পিডিটা.অর্গ / প্যান্ডাস
অ্যান্ডি হেডেন

@ অ্যান্ডি হেডেন - আমি আপনার উত্তরে মন্তব্য করেছিলাম আপনার বাস্তবায়নটি বেশ ভাল এবং সামগ্রিক কাজের ক্ষেত্রে অল্প পরিমাণে যোগ করেছে। আমি ফিচার রোলআপের ভিতরে তিনটি অপারেশন সাইথোনাইজড করেছি যা পুরোপুরি পুনরুদ্ধার করেছে যা এখন রিপোর্টিং অগ্রগতির জন্য নিবেদিত। সুতরাং আমি শেষ পর্যন্ত বাজি ধরলাম পুরো প্রসেসিংয়ের সময়টি যদি আমি সিথনের সাথে অনুসরণ করি তবে মোট প্রসেসিংয়ের সময় হ্রাস সহ আমি অগ্রগতি বার পাব।
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)

যদি আপনি কিভাবে এই কাজ (এবং কিভাবে আপনার নিজের callbacks জন্য এটি পরিবর্তন করা), দেখতে আগ্রহী হন উদাহরণ GitHub উপর , পূর্ণ ডকুমেন্টেশন pypi উপর , বা মডিউল এবং রান আমদানি 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: tqdm সংস্করণের জন্য নীচে 4.8 এর পরিবর্তে 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
পান্ডাস সমর্থনটি

6
বিটিডব্লিউ, আপনি যদি জপিটার নোটবুক ব্যবহার করেন, আপনি প্রিটিয়ার বার পেতে tqdm_notebooks ব্যবহার করতে পারেন। from tqdm import tqdm_notebook; tqdm_notebook().pandas(*args, **kwargs)
পান্ডার

2
সংস্করণ 4.8.1 হিসাবে - এর পরিবর্তে tqdm.pandas () ব্যবহার করুন। github.com/tqdm/tqdm/commit/…
মর্গ

1
ধন্যবাদ, @ মুরক সঠিক। আমরা tqdmv5 এর দিকে (ধীরে ধীরে) কাজ করছি যা জিনিসগুলিকে আরও সংশোধন করে।
casper.dcl

1
সাম্প্রতিক সিনট্যাক্সের সুপারিশের জন্য, এখানে tqdm পান্ডাস ডকুমেন্টেশন দেখুন: 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%) পুরোপুরি সন্নিবেশ ঘটিয়েছে (সম্পূর্ণ চিন্তা করুন) একক ক্ষেত্রে recordsোকানো রেকর্ড)। এই বিলোপটি ফিচার রোলআপে ব্যাপক গতি বাড়িয়ে তোলে কারণ স্প্লিট-প্রয়োগ-মিশ্রণ ক্রিয়াকলাপের সময় কী করা উচিত তা সম্পর্কে কোনও দ্বিধা ছিল না।
cwharland

1
আমার বয়স 8 মিনিটের নিচে ... তবে আমি বৈশিষ্ট্য রোলআপে আরও কিছু যোগ করেছি (আরও বৈশিষ্ট্য -> আরও ভাল এওসি!)। এই 8 মিনিট 12 মিলিয়ন সারি পাড়ার প্রতিটি অংশের সাথে প্রতি মুহুর্তে (এখনই দুটি খণ্ড) হ্যাঁ ... এইচডিএফএস স্টোর ব্যবহার করে 24 মিলিয়ন সারিগুলিতে 16 মিনিট বিশাল অপারেশন করতে হবে (এবং বৈশিষ্ট্য রোলআপে এনল্টক স্টাফ রয়েছে)। বেশ ভাল। আসুন আশা করি ইন্টারনেট
গণ্ডগোল

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_m মাল্টি_কোর - এটিই আপনি কল করেন। এটি গ্রহণ করে:

  1. আপনার ডিএফ অবজেক্ট
  2. আপনি যে ফাংশনটির নাম কল করতে চান
  3. কলামগুলির উপসেটটি ফাংশনটি সম্পাদন করা যায় (সময় / স্মৃতি হ্রাস করতে সহায়তা করে)
  4. সমান্তরালভাবে চালনার কাজের সংখ্যা (-1 বা সমস্ত কোরের জন্য বাদ)
  5. ডিএফ এর ক্রিয়াকলাপ অন্য কোনও কোয়ার্গাগুলি গ্রহণ করে ("অক্ষ" এর মতো)

_df_split - এটি একটি অভ্যন্তরীণ সহায়ক ফাংশন যা চলমান মডিউলে বিশ্বব্যাপী অবস্থান করতে হবে (পুল.ম্যাপ "প্লেসমেন্ট নির্ভর"), অন্যথায় আমি এটি অভ্যন্তরীণভাবে সনাক্ত করতে পারি ..

আমার সংক্ষেপে কোডটি এখানে (আমি সেখানে আরও পান্ডাস ফাংশন পরীক্ষা যুক্ত করব):

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

বেলো একটি সমান্তরাল প্রয়োগের জন্য একটি পরীক্ষা কোড tqdm "অগ্রগতি_ অ্যাপ্লিকেশন" এর সাথে ।

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)ভ্যালুআরারের পরিবর্তে কী-ইরর ব্যতিক্রমের কারণে, সমস্ত ক্ষেত্রে পরিচালনা করার জন্য এক্সেপশনটিতে পরিবর্তন করুন।
মারিয়াস

ধন্যবাদ @ মুরক - এই উত্তরটি আরও বেশি হওয়া উচিত।
অ্যান্ডি

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

আমি জেফের উত্তরটি সম্পূর্ণভাবে অন্তর্ভুক্ত করে পরিবর্তিত করেছি , যাতে আপনি প্রতিটি এক্স পুনরুক্তি মুদ্রণের জন্য অগ্রগতি এবং একটি পরিবর্তনশীল ট্র্যাক করতে পারেন ("প্রিন্ট_্যাট" যদি যুক্তিসঙ্গতভাবে উচ্চ হয় তবে এটি কার্যকারিতাটি অনেকটা উন্নত করে)

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

ক্লিয়ার_আউটপুট () ফাংশনটি এসেছে

from IPython.core.display import clear_output

আইপিথনে না থাকলে অ্যান্ডি হ্যাডেনের উত্তর এটি ছাড়া তা করে

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.