01.5.2
処理の進捗を表示
apply で 1 行ずつ関数を掛けると、終わるまで何分かかるのか見えないまま待つことになります。tqdm.pandas() を一度呼ぶと Series と DataFrame に progress_apply メソッドが追加され、apply をそれに置き換えるだけで 100%|████| 100/100 [00:01<00:00, 78.99it/s] のようなバーが出ます。表示には処理済み件数、経過時間、残り時間の見積もり、1 秒あたりの件数が含まれるので、途中で中断するか待つかを判断できます。このページでは sleep でわざと時間のかかる関数を 100 行に掛けて、バーが進む様子を確かめます。直感 #
df["人口総数"].apply(f) は 1 行ずつ f を呼ぶだけなので、全体の行数と「今何行目か」が分かれば残り時間は割り算で出ます。tqdm はこの数え上げを肩代わりする薄い包み紙で、progress_apply は内部で apply を呼びながら 1 件ごとにカウンタを進め、経過時間から残り時間を見積もって表示します。計算そのものは apply と同じなので、結果の DataFrame は変わりません。
Due to popular demand we’ve added support for pandas – here’s an example for DataFrame.progress_apply and DataFrameGroupBy.progress_apply:
サンプルデータ #
sample.csv の先頭 100 行だけを使います。行数を 100 に絞るのは、1 行あたり最大 0.02 秒待つ関数を掛けても全体が 1〜2 秒で終わるようにするためです。
| |
| 元号 | 和暦 | 西暦 | 人口総数 | 町名 | |
|---|---|---|---|---|---|
| 0 | 大正 | 9.0 | 1920.0 | 394748 | A町 |
| 1 | 大正 | 9.0 | 1920.0 | 31421 | B町 |
| 2 | 大正 | 9.0 | 1920.0 | 226993 | C町 |
| 3 | 大正 | 9.0 | 1920.0 | 253689 | D町 |
| 4 | 大正 | 9.0 | 1920.0 | 288602 | E町 |
処理の進捗を表示する #
rand_with_sleep(x: int) は 0〜0.02 秒のランダムな時間だけ sleep して、待った秒数を文字列で返す「時間のかかる処理」の代わりです。これを df["人口総数"] の 100 行に progress_apply で掛けると、各行の値が待機秒数に置き換わり、その間コンソールにプログレスバーが伸びていきます。
| |
100%|█████████████████████████████████████| 100/100 [00:01<00:00, 78.99it/s]
| 元号 | 和暦 | 西暦 | 人口総数 | 町名 | |
|---|---|---|---|---|---|
| 0 | 大正 | 9.0 | 1920.0 | 0.00290391291282283 sec | A町 |
| 1 | 大正 | 9.0 | 1920.0 | 0.015489350572629845 sec | B町 |
| 2 | 大正 | 9.0 | 1920.0 | 0.007857787674584393 sec | C町 |
| 3 | 大正 | 9.0 | 1920.0 | 0.016366338258575495 sec | D町 |
| 4 | 大正 | 9.0 | 1920.0 | 0.017993046099223576 sec | E町 |
使いどころと落とし穴 #
progress_apply が効くのは 1 行ずつ Python の関数を呼ぶ apply 系の処理で、df["人口総数"] * 2 のようなベクトル演算は一瞬で終わるためバーを付ける意味がありません。groupby(...).progress_apply にすればグループ数を分母にしたバーが出て、集計関数の進み具合も追えます。ログをファイルに流す環境では、バーの書き換えがキャリッジリターンの行として大量に残るので、tqdm.pandas(disable=True) か mininterval=5 で更新頻度を落とします。