taqadum: progress (from Arabic: تقدّم).
tqdm: I love you so much (from Spanish: te quiero demasiado).
Instantly make your loops show a smart progress meter - just wrap any
iterable with tqdm(iterable)
, and you're done!
Casper O. da Costa-Luis. PyData London, May 2019.
*source code, documentation, examples, wiki, release history, issue tracker, divine inspiration, solution to global warming
from time import sleep
for i in range(100):
sleep(0.01)
print("Er, how long did that take?")
Er, how long did that take?
from tqdm import tqdm # <-- yes
from time import sleep
for i in tqdm(range(100)): # <-- magic
sleep(0.01)
100%|██████████| 100/100 [00:01<00:00, 97.05it/s]
trange(N)
can be also used as a convenient shortcut for
tqdm(xrange(N))
.
from tqdm import trange
from time import sleep
for i in trange(100):
sleep(0.01)
100%|██████████| 100/100 [00:01<00:00, 96.91it/s]
from tqdm import trange
from time import sleep
for i in trange(100, desc="hello", unit="epoch"):
sleep(0.01)
hello: 100%|██████████| 100/100 [00:01<00:00, 96.89epoch/s]
It can also be executed as a module with pipes:
! seq 999999 | python3 -m tqdm | wc -l
999999it [00:00, 2330224.81it/s] 999999
! seq 9999999 | python3 -m tqdm --bytes | wc -l
75.2MB [00:00, 236MB/s] 9999999
! seq 9999999 | python3 -m tqdm --bytes --total 80000000 | wc -l
99%|██████████████████████████████████████▍| 75.2M/76.3M [00:00<00:00, 245MB/s] 9999999
(It's versatile)
Wrap tqdm()
around any iterable:
text = ""
for char in tqdm(["a", "b", "c", "d"]):
sleep(0.25)
text = text + char
100%|██████████| 4/4 [00:01<00:00, 3.97it/s]
Instantiation outside of the loop allows for manual control over tqdm()
:
pbar = tqdm(["a", "b", "c", "d"])
for char in pbar:
sleep(0.25)
pbar.set_description("Processing %s" % char)
Processing d: 100%|██████████| 4/4 [00:01<00:00, 3.94it/s]
Manual control on tqdm()
updates by using a with
statement:
with tqdm(total=100) as pbar:
for i in range(10):
sleep(0.1)
pbar.update(10)
100%|██████████| 100/100 [00:01<00:00, 98.28it/s]
total
(or an iterable with len()
), predictive stats are displayed.with
is also optional (but don't forget to del
or close()
at the end)pbar = tqdm(total=100)
for i in range(10):
sleep(0.1)
pbar.update(10)
pbar.close()
100%|██████████| 100/100 [00:01<00:00, 98.11it/s]
Simply inserting tqdm
(or python3 -m tqdm
) between pipes will pass
through all stdin
to stdout
while printing progress to stderr
.
Note that the usual arguments for tqdm
can also be specified.
# count lines of code in all *.py
find . -name '*.py' -type f -exec cat \{} \; \
| wc -l
# ... wait ages ...
4318982
# the tqdm way
find . -name '*.py' -type f -exec cat \{} \; | tqdm --unit loc --unit-scale \
| wc -l
4.32Mloc [00:06, 705kloc/s]
4318982
# boring backup
tar -zcf - ~/dloads > backup.tgz
# ... are we there yet? ...
# with tqdm
tar -zcf - ~/dloads | tqdm --bytes --total `du -sb ~/dloads | cut -f1` \
> backup.tgz
32%|██████████▍ | 8.89G/27.9G [00:42<01:31, 223MB/s]
7z a backup.7z docs/ | grep Compressing | \
tqdm --total $(find docs/ -type f | wc -l) --unit files >> backup.log
100%|███████████████████████████████▉| 8014/8014 [01:37<00:00, 82.29files/s]
from tqdm import tqdm
help(tqdm)
tqdm?
! python3 -m tqdm --help
Usage: tqdm [--help | options] Options: -h, --help Print this help and exit. -v, --version Print version and exit. --desc=<desc> : str, optional Prefix for the progressbar. --total=<total> : int or float, optional The number of expected iterations. If unspecified, len(iterable) is used if possible. If float("inf") or as a last resort, only basic progress statistics are displayed (no ETA, no progressbar). If `gui` is True and this parameter needs subsequent updating, specify an initial arbitrary large positive number, e.g. 9e9. --leave : bool, optional If [default: True], keeps all traces of the progressbar upon termination of iteration. If `None`, will leave only if `position` is `0`. --ncols=<ncols> : int, optional The width of the entire output message. If specified, dynamically resizes the progressbar to stay within this bound. If unspecified, attempts to use environment width. The fallback is a meter width of 10 and no limit for the counter and statistics. If 0, will not print any meter (only stats). --mininterval=<mininterval> : float, optional Minimum progress display update interval [default: 0.1] seconds. --maxinterval=<maxinterval> : float, optional Maximum progress display update interval [default: 10] seconds. Automatically adjusts `miniters` to correspond to `mininterval` after long display update lag. Only works if `dynamic_miniters` or monitor thread is enabled. --miniters=<miniters> : int or float, optional Minimum progress display update interval, in iterations. If 0 and `dynamic_miniters`, will automatically adjust to equal `mininterval` (more CPU efficient, good for tight loops). If > 0, will skip display of specified number of iterations. Tweak this and `mininterval` to get very efficient loops. If your progress is erratic with both fast and slow iterations (network, skipping items, etc) you should set miniters=1. --ascii=<ascii> : bool or str, optional If unspecified or False, use unicode (smooth blocks) to fill the meter. The fallback is to use ASCII characters " 123456789#". --disable : bool, optional Whether to disable the entire progressbar wrapper [default: False]. If set to None, disable on non-TTY. --unit=<unit> : str, optional String that will be used to define the unit of each iteration [default: it]. --unit-scale=<unit_scale> : bool or int or float, optional If 1 or True, the number of iterations will be reduced/scaled automatically and a metric prefix following the International System of Units standard will be added (kilo, mega, etc.) [default: False]. If any other non-zero number, will scale `total` and `n`. --dynamic-ncols : bool, optional If set, constantly alters `ncols` and `nrows` to the environment (allowing for window resizes) [default: False]. --smoothing=<smoothing> : float, optional Exponential moving average smoothing factor for speed estimates (ignored in GUI mode). Ranges from 0 (average speed) to 1 (current/instantaneous speed) [default: 0.3]. --bar-format=<bar_format> : str, optional Specify a custom bar string formatting. May impact performance. [default: '{l_bar}{bar}{r_bar}'], where l_bar='{desc}: {percentage:3.0f}%|' and r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' '{rate_fmt}{postfix}]' Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, rate, rate_fmt, rate_noinv, rate_noinv_fmt, rate_inv, rate_inv_fmt, postfix, unit_divisor, remaining, remaining_s, eta. Note that a trailing ": " is automatically removed after {desc} if the latter is empty. --initial=<initial> : int or float, optional The initial counter value. Useful when restarting a progress bar [default: 0]. If using float, consider specifying `{n:.3f}` or similar in `bar_format`, or specifying `unit_scale`. --position=<position> : int, optional Specify the line offset to print this bar (starting from 0) Automatic if unspecified. Useful to manage multiple bars at once (eg, from threads). --postfix=<postfix> : dict or *, optional Specify additional stats to display at the end of the bar. Calls `set_postfix(**postfix)` if possible (dict). --unit-divisor=<unit_divisor> : float, optional [default: 1000], ignored unless `unit_scale` is True. --write-bytes : bool, optional If (default: None) and `file` is unspecified, bytes will be written in Python 2. If `True` will also write bytes. In all other cases will default to unicode. --lock-args=<lock_args> : tuple, optional Passed to `refresh` for intermediate output (initialisation, iterating, and updating). --nrows=<nrows> : int, optional The screen height. If specified, hides nested bars outside this bound. If unspecified, attempts to use environment height. The fallback is 20. --colour=<colour> : str, optional Bar colour (e.g. 'green', '#00ff00'). --delay=<delay> : float, optional Don't display until [default: 0] seconds have elapsed. --delim=<delim> : chr, optional Delimiting character [default: '\n']. Use '\0' for null. N.B.: on Windows systems, Python converts '\n' to '\r\n'. --buf-size=<buf_size> : int, optional String buffer size in bytes [default: 256] used when `delim` is specified. --bytes : bool, optional If true, will count bytes, ignore `delim`, and default `unit_scale` to True, `unit_divisor` to 1024, and `unit` to 'B'. --tee : bool, optional If true, passes `stdin` to both `stderr` and `stdout`. --update : bool, optional If true, will treat input as newly elapsed iterations, i.e. numbers to pass to `update()`. Note that this is slow (~2e5 it/s) since every input must be decoded as a number. --update-to : bool, optional If true, will treat input as total elapsed iterations, i.e. numbers to assign to `self.n`. Note that this is slow (~2e5 it/s) since every input must be decoded as a number. --null : bool, optional If true, will discard input (no stdout). --manpath=<manpath> : str, optional Directory in which to install tqdm man pages. --comppath=<comppath> : str, optional Directory in which to place tqdm completion. --log=<log> : str, optional CRITICAL|FATAL|ERROR|WARN(ING)|[default: 'INFO']|DEBUG|NOTSET.
help()
;Custom information can be displayed and updated dynamically on tqdm
bars
with the desc
and postfix
arguments:
from tqdm import trange
from random import random, randint
from time import sleep
with trange(10) as t:
for i in t:
t.set_description('GEN %i' % i)
# formatted automatically based on argument's datatype
t.set_postfix(loss=random(), gen=randint(1,999), str='h', lst=[1, 2])
sleep(0.1)
GEN 9: 100%|██████████| 10/10 [00:01<00:00, 9.45it/s, gen=396, loss=0.439, lst=[1, 2], str=h]
with tqdm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}",
postfix=["Batch", dict(value=0)]) as t:
for i in range(10):
sleep(0.1)
t.postfix[1]["value"] = i / 2
t.update()
Batch 4
tqdm
supports nested progress bars. Here's an example:
from tqdm import trange
from time import sleep
for i in trange(4, desc='1st loop'):
for j in trange(5, desc='2nd loop'):
for k in trange(50, desc='3nd loop', leave=False):
sleep(0.01)
position=n
tqdm
can easily support callbacks/hooks and manual updates.
import urllib, os
urllib = getattr(urllib, 'request', urllib)
eg_link = "https://caspersci.uk.to/matryoshka.zip"
urllib.urlretrieve(eg_link, filename=os.devnull, data=None);
class TqdmUpTo(tqdm):
def update_to(self, blocks_so_far=1, block_size=1, total=None):
if total is not None:
self.total = total
# will also set self.n = blocks_so_far * block_size
self.update(blocks_so_far * block_size - self.n)
with TqdmUpTo(unit='B', unit_scale=True, miniters=1,
desc=eg_link.split('/')[-1]) as t: # all optional kwargs
urllib.urlretrieve(eg_link, filename=os.devnull,
reporthook=t.update_to, data=None)
t.total = t.n
matryoshka.zip: 100%|██████████| 262k/262k [00:00<00:00, 556kB/s]
It is recommend to use miniters=1
whenever there is potentially
large differences in iteration speed (e.g. downloading a file over
a patchy connection).
from tensorflow import keras
model = keras.Sequential()
# ... etc ...
model.fit(x, y, epochs=10);
Epoch 1/10 1000/1000 [==============================] - 1s 1ms/step - loss: 123450.4777 - mean_absolute_error: 351.3478 Epoch 2/10 1000/1000 [==============================] - 0s 44us/step - loss: 122634.9961 - mean_absolute_error: 350.1859 Epoch 3/10 1000/1000 [==============================] - 0s 52us/step - loss: 121823.3951 - mean_absolute_error: 349.0256 Epoch 4/10 1000/1000 [==============================] - 0s 47us/step - loss: 121018.2029 - mean_absolute_error: 347.8706 Epoch 5/10 1000/1000 [==============================] - 0s 52us/step - loss: 120219.2435 - mean_absolute_error: 346.7207 Epoch 6/10 1000/1000 [==============================] - 0s 49us/step - loss: 119424.1497 - mean_absolute_error: 345.5727 Epoch 7/10 1000/1000 [==============================] - 0s 45us/step - loss: 118632.8348 - mean_absolute_error: 344.4263 Epoch 8/10 1000/1000 [==============================] - 0s 45us/step - loss: 117845.8773 - mean_absolute_error: 343.2822 Epoch 9/10 1000/1000 [==============================] - 0s 56us/step - loss: 117062.4701 - mean_absolute_error: 342.1397 Epoch 10/10 1000/1000 [==============================] - 0s 45us/step - loss: 116284.3545 - mean_absolute_error: 341.0009
from tqdm.keras import TqdmCallback
model.fit(x, y, epochs=10, verbose=0, callbacks=[TqdmCallback()]);
100%|██████████| 10/10 [00:00<00:00, 24.24epoch/s, loss=1.09e+5, mean_absolute_error=330]
Due to popular demand (ergh).
import pandas as pd
import numpy as np
from tqdm import tqdm
df = pd.DataFrame(np.random.rand(5, 10))
# Register `pandas.progress_apply`, `pandas.Series.map_apply`, etc with `tqdm`
# (can use `tqdm_gui`, `tqdm_notebook`, optional kwargs, etc.)
tqdm.pandas(desc="my bar!")
# `map` => `progress_map`
# `apply` => `progress_apply`
df.progress_apply(lambda x: x**2)
# can also groupby:
# df.groupby(0).progress_apply(lambda x: x**2)
my bar!: 100%|██████████| 10/10 [00:00<00:00, 1626.01it/s]
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0.343456 | 0.333026 | 0.013214 | 0.109925 | 0.027551 | 0.015859 | 0.084797 | 0.055252 | 0.276558 | 0.538887 |
1 | 0.491100 | 0.007598 | 0.008333 | 0.127863 | 0.325472 | 0.157241 | 0.555851 | 0.028682 | 0.145064 | 0.956230 |
2 | 0.002674 | 0.481197 | 0.693777 | 0.396851 | 0.129166 | 0.317659 | 0.007226 | 0.009895 | 0.678273 | 0.434440 |
3 | 0.002297 | 0.267686 | 0.163257 | 0.919152 | 0.340434 | 0.820713 | 0.495618 | 0.879623 | 0.688926 | 0.474775 |
4 | 0.589205 | 0.077341 | 0.020816 | 0.439388 | 0.063140 | 0.003705 | 0.061417 | 0.029562 | 0.473743 | 0.424798 |
IPython/Jupyter is supported via the tqdm_notebook
submodule:
from tqdm.notebook import tnrange, tqdm_notebook
from time import sleep
for i in tnrange(3, desc='1st loop'):
for j in tqdm_notebook(range(100), desc='2nd loop'):
sleep(0.01)
tqdm
automatically choose between CLI or notebook versionsfrom tqdm.autonotebook import tqdm # may raise warning about Jupyter
from tqdm.auto import tqdm # who needs warnings
tqdm.pandas()
jupyter notebook
and jupyter console