web-dev-qa-db-ja.com

pandas操作中の進行状況インジケーター

私は定期的にpandas 1500万行以上のデータフレームに対して操作を実行します。特定の操作の進行状況インジケーターにアクセスしたいと思います。

pandas split-apply-combine操作のテキストベースの進行状況インジケータは存在しますか?

たとえば、次のようなものです:

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

どこ feature_rollupは、多くのDF列を取り、さまざまな方法で新しいユーザー列を作成する、やや複雑な関数です。これらの操作は、大きなデータフレームでは時間がかかることがあります。 iPythonノートブックにテキストベースの出力を表示して、進捗状況を更新することができます。

これまでのところ、Pythonの標準ループ進行インジケーターを試しましたが、意味のある方法でpandasと対話しません。

pandasライブラリ/ドキュメントで見落としているものがあることを望んでいます。これにより、分割適用結合の進行状況を知ることができます。 apply関数が機能しているデータフレームサブセットのうち、これらのサブセットの完了した割合として進捗を報告します。

これはおそらくライブラリに追加する必要があるものですか?

97
cwharland

一般的な需要により、tqdmpandasのサポートを追加しました。他の回答とは異なり、このはそれほど遅くなりませんpandas down-ここに_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)
_

これがどのように機能するか(および独自のコールバック用に変更する方法)に興味がある場合は、 githubの例pypiの完全なドキュメント 、またはimportモジュールを実行し、help(tqdm)を実行します。

[〜#〜] edit [〜#〜]


元の質問に直接回答するには、次を置き換えます。

_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())
_
172
casper.dcl

ジェフの答えを微調整する(そしてこれを再利用可能な関数として持つ)。

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]: 
...

通常どおり、これをメソッドとしてgroupbyオブジェクトに追加できます。

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

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

コメントで述べたように、これはコアpandasが実装に興味を持つ機能ではありません。しかしpython多くのpandasオブジェクト/メソッドのためにこれらを作成することができます(そうすることはかなり手間がかかりますが、このアプローチを一般化できるはずですが)。

13
Andy Hayden

Jupyter/ipythonノートブックでこれを使用する方法のサポートが必要な場合は、私がしたように、役に立つガイドと 関連記事 へのソースがあります:

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のimportステートメントのアンダースコアに注意してください。参照記事が言及しているように、開発はベータ段階の後半にあります。

7
Victor Vulovic

これはデコレータで簡単にできます

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)

次に、modified_functionを使用します(印刷するタイミングを変更します)。

4
Jeff

カスタムの並列pandas-applyコードにtqdmを適用しようとしている人向け。

(私は長年にわたって並列化のためにいくつかのライブラリを試しましたが、主に適用機能のために100%の並列化ソリューションを見つけることはありませんでした。常に「手動」コードのために戻ってきました。)

df_multi_core-これはあなたが呼び出すものです。受け入れます:

  1. Dfオブジェクト
  2. 呼び出したい関数名
  3. 関数を実行できる列のサブセット(時間/メモリの削減に役立ちます)
  4. 並列に実行するジョブの数(-1またはすべてのコアで省略)
  5. Dfの関数が受け入れる他のkwargs(「軸」など)

_ df_split-これは、実行中のモジュールに対してグローバルに配置する必要がある内部ヘルパー関数です(Pool.mapは「配置依存」です)、それ以外の場合はI ' d内部に配置します。

ここに私の Gist からのコードがあります(さらにpandas機能テストを追加します):

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

以下は、tqtm "progress_apply"を使用した並列化applyのテストコードです。

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

if __== '__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つの進行状況バーと、並列化を実行する場合のコアごとの進行状況バーが表示されます。わずかな接続があり、コアの残りの部分が一度に表示されることもありますが、それでもコアごとの進行状況の統計情報を取得できるので便利です(例:秒/秒および合計レコード)。

enter image description here

このすばらしいライブラリをありがとう@abcdaa!

2
mork

Jeff's answer を変更して合計を含めることで、進捗状況と変数を追跡して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

iPythonにない場合、Andy Haydenの答えはそれなしでそれをします

0
Filipe Silva