長い処理を実行している時、いまどのくらい進んでいるのか分からないと不安になります。
Pythonの進捗バー定番ライブラリがtqdmです。
たった1行足すだけでループに美しい進捗バーが表示され、残り時間(ETA)や処理速度も分かります。
初心者でも実務でもすぐ使える、基本から実用パターンまでまとめます。
tqdmとは?
tqdmは、ループ処理などの進捗を端末やJupyterに見やすく表示してくれるPythonライブラリです。
名前はアラビア語の「進捗」を意味し、英語の「taqaddum」に由来します。
tqdmは何ができる?
tqdmは、イテラブル(例えばrangeやリスト)を包むだけで、バーの進行率、経過時間、残り時間、処理速度(例: it/sやB/s)を自動で表示します。
説明文(desc)や単位(unit)、合計件数(total)などを指定して、視覚的に分かりやすいバーにカスタマイズできます。
Jupyter専用のリッチ表示にも対応しています。
どんな時に使う?
- データ前処理や特徴量生成のループ
- WebスクレイピングやAPI呼び出しの繰り返し
- 大量ファイルの読み書き、ダウンロードの進捗
- 機械学習の学習ループの進捗や指標表示処理時間が数秒以上かかる繰り返しには、まずtqdmを付けるという習慣が役立ちます。
インストールと基本の使い方
pipでtqdmをインストールする
Pythonが動く環境なら1コマンドで導入できます。
環境ごとのpipの違いを避けるため、python -m pip
の形をおすすめします。
# インストール
python -m pip install -U tqdm
# バージョン確認
python -c "import tqdm, sys; print(tqdm.__version__)"
仮想環境(venv)を使っている場合は、有効化したシェルで実行してください。
基本の使い方
既存のforループのイテラブルをtqdmで包むだけです。
# 基本: ループをtqdmで包む
from time import sleep
from tqdm import tqdm # 端末向けの基本版
for i in tqdm(range(50)): # 0〜49の50回ループ
# ここに時間のかかる処理を書く
sleep(0.05) # デモ用に50ms待つ
出力例(端末):
100%|████████████████████████████████████████| 50/50 [00:02<00:00, 19.99it/s]
trangeの使い方
range
を直接置き換えるショートカットがtrange
です。
意味は同じで、見た目がすっきりします。
# trangeはrangeのtqdm版ショートカット
from time import sleep
from tqdm import trange
for i in trange(30): # range(30) と同じ個数
sleep(0.05)
100%|████████████████████████████████████████| 30/30 [00:01<00:00, 20.01it/s]
説明文(desc)と単位(unit)を付ける
処理内容や単位を表示すると、何をどれだけ進めているかが一目で分かります。
from time import sleep
from tqdm import tqdm
# descで説明、unitで単位(件、ファイル、Bなど)を指定
for _ in tqdm(range(100), desc="画像をリサイズ中", unit="枚"):
sleep(0.02)
# バイト単位のときはunit="B"とunit_scale=Trueで自動スケール(KB/MB)
for _ in tqdm(range(200), desc="ダウンロード中", unit="B", unit_scale=True):
sleep(0.01)
画像をリサイズ中: 100%|██████████████| 100/100 [00:02<00:00, 45.10枚/s]
ダウンロード中: 100%|██████████████| 200/200 [00:02<00:00, 89.3kB/s]
件数が分かる時はtotalを指定する
イテラブル側で件数が分からない(ジェネレータなど)場合でも、合計件数が分かるならtotal
を与えるとETAが正確になります。
# 件数が分かるが、イテラブルのlenが取れないケース
from time import sleep
from tqdm import tqdm
def generate_items(n):
for i in range(n):
sleep(0.01)
yield i
N = 120
for _ in tqdm(generate_items(N), total=N, desc="前処理", unit="件"):
pass
前処理: 100%|██████████████████████████████| 120/120 [00:01<00:00, 95.2件/s]
totalを使わず手動更新(update)する方法
アイテムごとに進捗量が異なる場合は、tqdm(total=...)
とupdate(n)
で手動更新できます。
# 手動でupdateする例(可変ステップ)
from time import sleep
from tqdm import tqdm
sizes = [5, 2, 3, 10] # 合計進捗量をtotalに
with tqdm(total=sum(sizes), desc="可変ステップ", unit="step") as pbar:
for s in sizes:
sleep(0.2) # 何らかの処理
pbar.update(s) # sだけ進める
可変ステップ: 100%|███████████████████████| 20/20 [00:01<00:00, 16.4step/s]
バーを残さない(leave=False)などの簡単カスタマイズ
終わったバーを消したい時はleave=False
にします。
幅の自動調整はdynamic_ncols=True
が便利です。
Windowsの古い端末などで崩れる場合はascii=True
の疑似バーにできます。
from time import sleep
from tqdm import tqdm
for _ in tqdm(range(80), desc="一時的な処理", leave=False, dynamic_ncols=True, ascii=True):
sleep(0.01)
主なパラメータの早見表:
パラメータ | 役割 | 例 |
---|---|---|
desc | 左端に出す説明文 | desc=”読み込み” |
total | 合計件数(既知なら指定) | total=1000 |
unit, unit_scale | 単位と自動スケール | unit=”B”, unit_scale=True |
leave | 終了後にバーを残すか | leave=False |
ascii | ASCIIバーにする | ascii=True |
dynamic_ncols | 端末幅に合わせて調整 | dynamic_ncols=True |
ncols | バーの固定幅 | ncols=100 |
colour | バー色(一部環境) | colour=”green” |
mininterval, miniters | 更新頻度の抑制 | mininterval=0.2 |
disable | バー表示を無効化 | disable=条件式 |
position | 複数バーの縦位置 | position=1 |
一部環境では色(colour)や絵文字/全角の表示に制限があります。
Jupyterとスクリプトでの表示
スクリプトはtqdm.autoで環境を自動判定
端末やJupyterなど環境に応じて最適表示に切り替えてくれるのがtqdm.auto
です。
迷ったらfrom tqdm.auto import tqdm, trange
を基本にすると安全です。
# どの環境でも最適表示にしたい場合
from time import sleep
from tqdm.auto import tqdm, trange
for i in trange(100, desc="auto"):
sleep(0.01)
Jupyterはtqdm.notebookが見やすい
Jupyterではtqdm.notebook
がipywidgetsベースのリッチなバーを表示します。
表示が出ない場合、ipywidgets
のインストールを確認します。
python -m pip install -U ipywidgets
# Jupyter Notebook/Lab での推奨
from time import sleep
from tqdm.notebook import tqdm
for _ in tqdm(range(50), desc="Notebookバー", unit="it"):
sleep(0.02)
Jupyterでもtqdm.auto
は使えますが、表示品質はtqdm.notebook
が最適です。
出力が崩れる時の対処
printとtqdmの行が混ざると崩れやすいです。
以下のコツで安定します。
- 普通の
print
の代わりにtqdm.write()
を使う(バーを壊さずにログを出せます) - 端末幅による折り返しは
dynamic_ncols=True
やncols=…
で調整 - 複数バーは
position
で縦位置をずらす - どうしても崩れるなら
ascii=True
にする
from time import sleep
from tqdm import tqdm
for i in tqdm(range(5), desc="処理"):
sleep(0.2)
# printの代わりにtqdm.writeを使う
from datetime import datetime
tqdm.write(f"[{datetime.now().isoformat(timespec='seconds')}] {i}件目が完了")
処理: 20%|██ | 1/5 [00:00<00:00, 4.85it/s]
[2025-09-20T12:34:56] 0件目が完了
処理: 100%|██████████████████| 5/5 [00:01<00:00, 4.85it/s]
よく使う実用例とコツ
enumerateやリスト処理にtqdmを組み合わせる
インデックスが必要ならenumerate
と併用します。
合計件数が分かる場合はtotal
を付けるとETAが安定します。
from time import sleep
from tqdm import tqdm
items = ["a", "b", "c", "d"]
for idx, item in enumerate(tqdm(items, total=len(items), desc="処理", unit="件")):
# ここでitemを処理
sleep(0.1)
リスト内包表記や生成式でも包めます。
from time import sleep
from tqdm import tqdm
def slow_square(x):
sleep(0.02)
return x * x
data = list(range(100))
# list(...)で具象化すると全件処理しつつ進捗が見える
squares = [slow_square(x) for x in tqdm(data, desc="二乗", unit="件")]
ファイル処理
大量ファイルの処理やバイト単位の進捗を見たい時に有用です。
フォルダ内の全ファイルを処理
from time import sleep
from pathlib import Path
from tqdm import tqdm
folder = Path(".")
files = list(folder.rglob("*.txt")) # 先に一覧化して総数を把握
for p in tqdm(files, desc="テキスト処理", unit="ファイル"):
# 各ファイルに対して何らかの処理
sleep(0.01)
ダウンロードのバイト進捗を表示(requests)
バイト単位はunit="B"
とunit_scale=True
が定番です。
wrapattr
を使うとストリームのread
をラップして自動更新できます。
import requests
from tqdm import tqdm
# 100KBのファイルをダウンロードして進捗を表示
url = "https://httpbin.org/bytes/102400"
dest = "download.bin"
chunk_size = 8192 # 任意のチャンクサイズ
with requests.get(url, stream=True) as r:
r.raise_for_status()
total = int(r.headers.get("content-length", 0))
with tqdm.wrapattr(r.raw, "read", total=total, desc="DL", unit="B", unit_scale=True) as f_in:
with open(dest, "wb") as f_out:
while chunk := f_in.read(chunk_size):
f_out.write(chunk)
DL: 100%|██████████████████| 100k/100k [00:00<00:00, 191kB/s]
pandasでprogress_applyを使う
pandasのapply
に進捗を出したい時はtqdm.pandas()
を一度呼ぶと、progress_apply
が使えるようになります。
import time
import pandas as pd
from tqdm.auto import tqdm
tqdm.pandas(desc="行処理中") # 1回呼び出して登録
df = pd.DataFrame({"x": range(50)})
def slow_op(v):
time.sleep(0.02)
return v * 2
# 通常のapplyの代わりにprogress_applyを使う
df["y"] = df["x"].progress_apply(slow_op)
print(df.head())
行処理中: 100%|██████████████████████████████| 50/50 [00:01<00:00, 44.3it/s]
x y
0 0 0
1 1 2
2 2 4
3 3 6
4 4 8
Jupyterでリッチ表示が出ない場合はipywidgets
を導入してください。
ネストしたループの進捗
外側と内側の2段ループを同時に表示するにはposition
とleave
を使います。
外側バーを上(position=0)、内側バーを下(position=1)に配置します。
from time import sleep
from tqdm import tqdm
outer = range(3)
inner = range(5)
outer_bar = tqdm(outer, desc="エポック", position=0)
for i in outer_bar:
# 内側バーは毎エポックごとに新しく作成し、終わったら消す
for j in tqdm(inner, desc=f"バッチ", position=1, leave=False):
sleep(0.05)
# 指標を動的に表示
outer_bar.set_postfix(epoch=i, batch=j)
# 明示的に閉じてもOK: outer_bar.close()
出力例(概形):
エポック: 67%|████████████████████████ | 2/3 [00:00<00:00, 8.9it/s, epoch=1, batch=4]
遅い処理の見積もり
<tqdm>は自動でETAを出しますが、精度を上げる鍵はtotal
を正しく与えることです。
処理時間がばらつく場合は、更新頻度を抑えて落ち着いたETAにできます。
# 変動が大きい処理のETAを見やすくする
import random
from time import sleep
from tqdm import tqdm
N = 100
for i in tqdm(range(N), desc="推定", unit="件", mininterval=0.3, smoothing=0.1):
# 0.0〜0.1秒のランダムな処理
sleep(random.random() * 0.1)
さらに、学習の損失やカスタム情報をset_postfix
で右側に表示できます。
from time import sleep
from tqdm import trange
for i in trange(20, desc="学習", unit="it"):
loss = 1.0 / (i + 1)
sleep(0.05)
# 小数点桁数などは辞書で渡せば自動整形
trange.set_postfix = None # 型ヒント対策(実行時は不要)
# 正しい使い方の例(インスタンスに対して呼ぶ)
from time import sleep
from tqdm import trange
bar = trange(20, desc="学習", unit="it")
for i in bar:
sleep(0.05)
loss = 1.0 / (i + 1)
bar.set_postfix(loss=f"{loss:.3f}")
学習: 55%|███████████████▌ | 11/20 [00:00<00:00, 18.8it/s, loss=0.083]
処理が極端に遅くてバー自体の描画が重い場合はmininterval
やminiters
で更新頻度を下げると快適です。
まとめ
tqdmは「forを包むだけ」で進捗バーが手に入る、軽量で強力なユーティリティです。
基本はtqdm(range(...))
、説明はdesc
、単位はunit
、合計が分かればtotal
を指定します。
スクリプトならtqdm.auto
、Jupyterならtqdm.notebook
が見やすく、tqdm.write
でログも安全に併用できます。
ファイルI/O、pandas、ネストしたループ、手動update
まで押さえれば、実務の大半の「長い処理」を安心して回せるようになります。
まずは小さなループから導入し、便利さを体感してみてください。