如何设置 tqdm 的 r_bar 部分

How to set the r_bar part of tqdm

我使用 tqdm 为 hyperopt 的长时间 运行 优化过程打印进度条。 这个过程调用一个函数 500 次,每次调用大约需要 10 到 20 分钟,所以我开始让进度显示更细粒度,并在循环中添加一些 tqdm.update-statements,推进进度条分数方式以避免有两个嵌套的进度条,同时仍然能够立即看到到目前为止执行了多少函数调用。

现在丑陋的结果是这样的:

 15%|███▌                    | 73.69999999999993/500 [7:40:31<102:54:08, 868.98s/it, evaluating fold 2 of  2 folds...]Iteration 1, loss = 2.50358388

从上图可以看出,这是函数的第73次调用,第73次函数调用已经完成了大约70%。事实上,我只是估计了函数中子步骤的数量 m(这可能因调用而异)并使用分数 1/m 来更新进度条。然后在函数调用之后我只是将进度条同步回一个完整的整数以避免添加舍入错误。

当然,准确性在这里根本不是问题。但我想显示 73.70 而不是 73.69999999999993。 我已经尝试将我的更新值四舍五入到小数点后两位,这并不能解决问题,因为浮点数的精度问题,如果一个数字不能完全用浮点数表示,那么它又会变得丑陋。 根据 documentation of tqdm 这部分隐藏在整个格式字符串的 r_bar 部分中,但我找不到设置它的方法。你能帮我解决这个问题吗?

根据文档 r_bar 默认为:

r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, '

这是我的代码:

with tqdm(iterable=None, initial=num_trials, maxinterval=maxinterval, total=max_evals, ascii=False, disable=show_progressbar is False) as progress_bar:
    def fn_to_minimize(*args, **kwargs):
        return fn(*args, **kwargs, _progress_bar=progress_bar)

    for num_trials in range(num_trials, max_evals):
        progress_bar.n=float(num_trials)
        progress_bar.refresh()
        best = fmin(**kwargs, fn=fn_to_minimize, trials=trials, max_evals=num_trials+1)

# do some other stuff here

在调用的函数中(kwargs btw 中的条目之一)我像这样更新进度条:

_progress_bar.update(round(update_value, 2))

对于 tqdm 中的舍入问题,您可以直接编辑 r_bar 中的格式作为 bar_format 中的参数之一。例如:

from tqdm import trange
for i in trange(int(7e7), bar_format = "{desc}: {percentage:.3f}%|{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}"):
    pass

显示:

对于 2 位小数,您只需将 {n_fmt} 编辑为 {n:.2f} 即可。您还可以编辑其他参数,例如 {desc} 或向 percentage.

添加额外的小数位
from tqdm import trange
for i in trange(int(7e7), bar_format = "{desc}: {percentage:.10f}%|{bar}| {n:.2f}/{total_fmt} [{elapsed}<{remaining}"):
    pass

显示:

通过查看tqdm的源代码,n_fmt实际上指向str(n),因此传入n的格式化版本可以绕过其固有格式.

if unit_scale:
            n_fmt = format_sizeof(n, divisor=unit_divisor)
            total_fmt = format_sizeof(total, divisor=unit_divisor) \
                if total is not None else '?'
        else:
            n_fmt = str(n)
            total_fmt = str(total) if total is not None else '?'

        try:
            postfix = ', ' + postfix if postfix else ''
        except TypeError:
            pass