Вы когда-нибудь оказывались в ситуации, когда скрипт на Python выполняется очень долго и вы задаетесь вопросом, происходит ли вообще что-нибудь за экраном?
Такая неопределенность может побудить вас прервать почти завершенное выполнение или бесконечно ждать окончания работы скрипта, хотя его работа уже прервалась.
Библиотека tqdm решает эту проблему, предоставляя индикаторы выполнения для ваших скриптов.
Tqdm — это библиотека Python, которая предоставляет быстрые, расширяемые индикаторы выполнения для циклов и итерируемых объектов. Такие индикаторы позволяют отслеживать продвижение задач, требующих много времени.
Название библиотеки означает «прогресс» на арабском (taqadum, تقدّم) и является сокращением от «я тебя очень люблю» на испанском (te quiero demasiado).
Tqdm отслеживает прогресс и обновляет индикатор выполнения, подсчитывая количество итераций, вычисляя прошедшее и оставшееся время, а также визуализируя общий прогресс путем заполнения полосы.
Библиотека использует интеллектуальные алгоритмы для прогнозирования оставшегося времени и пропускает ненужные отображения итераций, чтобы минимизировать накладные расходы.
Использование tqdm дает ряд преимуществ:
Как и для большинства библиотек Python, самый простой способ установить tqdm — использовать менеджер пакетов pip.
pip install tqdm
Чтобы создать индикатор выполнения, мы оборачиваем наш итерируемый объект в функцию tqdm(), которую импортируем из модуля tqdm.
Давайте рассмотрим простой пример. Функция time.sleep(0.01) служит плейсхолдером для кода, который должен выполняться в каждой итерации.
from tqdm import tqdm
for i in tqdm(range(1000)):
time.sleep(0.01)
# … В качестве сокращения для tdqm(range(n)) можно также использовать trange(n). Следующий код вернет точно такой же результат, как и предыдущий:
from tqdm import tqdm
for i in trange(1000):
time.sleep(0.01)
# … Давайте присмотримся к индикатору выполнения, чтобы узнать, какую информацию он предоставляет:
Tqdm предлагает различные возможности настройки внешнего вида и поведения индикаторов выполнения. Мы рассмотрим наиболее важные параметры, а затем изучим более сложные варианты использования tqdm.
Одна из распространенных настроек — добавление описательной метки с помощью параметра desc. Описание делает индикатор выполнения более информативным и помогает следить за разными итерируемыми объектами.
Например, добавление параметра desc=”Processing large range” выведет заголовок «Processing large range» слева от индикатора выполнения.
for _ in tqdm(range(20000000), desc="Processing large range"): continue
Попробуйте запустить приведенный выше код в своей среде с параметром desc и без него и обратите внимание на разницу.
Параметр total задает общее количество итераций в цикле, что позволяет tqdm предоставлять более точные оценки оставшегося времени и процента завершения.
В базовом примере с использованием tqdm(range()) или trange() в этом нет необходимости, поскольку количество итераций уже заключено в скобки. Однако есть два основных сценария, в которых добавление параметра total=n (где n — общее число) может быть полезным:
Для итерируемых объектов без метода len(), например генераторов с неизвестным количеством элементов, необходимо указать значение total вручную. Без этого параметра tqdm() покажет только количество завершенных итераций. Обратите внимание, что если фактическое количество элементов превышает указанное значение total, индикатор выполнения перезапускается.
from tqdm import tqdm
import time
import random
# Function generating random number between 1 and 100
def generate_random_numbers():
while True:
yield random.randint(1, 100)
# Without total: tqdm() only shows number of iterations, does not know total
for num in tqdm(generate_random_numbers(), desc=’Random Iteration’):
time.sleep(0.01)
# … # With total (assuming you know the desired number of iterations): tqdm() shows progress num_iterations = 1000 for num in tqdm(generate_random_numbers(), total=num_iterations, desc="Random Iteration"): time.sleep(0.01) # …
Если мы обновляем индикатор выполнения внутри функции вручную с помощью метода update(), то для корректного отслеживания выполнения необходимо указать значение total. Метод update() позволяет, например, реагировать на динамически меняющиеся процессы или реализовать пользовательское отслеживание прогресса. К этому мы вернемся позже.
Чтобы изменить порядок размещения элементов индикатора, можно установить предпочтительный формат строки в качестве значения для параметра bar_format. Таким образом можно управлять размещением различных элементов, таких как процентное отношение, прошедшее время и символ заполнения полосы. За более подробной информацией вы можете обратиться к документации.
Еще одна настройка внешнего вида может быть выполнена с помощью параметра colour. Можно использовать как слова, например «green», так и шестнадцатеричный код в строковом виде, например «#00ff00».
Параметр leave определяет, останется ли полоса прогресса видимой после завершения работы. Если установить значение True, то индикатор сохранится после завершения цикла, а если установить значение False, то он исчезнет.
Давайте рассмотрим визуальные различия на примере. Следующий код создает три индикатора выполнения: один с настройками по умолчанию, во втором изменены порядок элементов и цвет, а третий настроен на исчезновение после завершения. Результат можно видеть в GIF ниже.
from tqdm import tqdm
import time
# Progress bar 1: Default settings
for i in tqdm(range(300)):
time.sleep(0.01)
# Progress bar 2: Customized bar format and color
for i in tqdm(range(300), bar_format='[{elapsed}<{remaining}] {n_fmt}/{total_fmt} | {l_bar}{bar} {rate_fmt}{postfix}', colour='yellow'):
time.sleep(0.01)
# Progress bar 3: Customized bar format and color, leave=False
for i in tqdm(range(300), bar_format='[{elapsed}<{remaining}] {n_fmt}/{total_fmt} | {l_bar}{bar} {rate_fmt}{postfix}', colour='red', leave=False):
time.sleep(0.01) Теперь, когда мы знаем, как создать и настроить простой индикатор выполнения, можно перейти к более сложным случаям.
Вложенные циклы — это циклы внутри других циклов. Соответственно, вложенные индикаторы выполнения — это прогресс-бары для циклов, содержащихся внутри других циклов. Чтобы создать их, оберните каждый цикл функцией tqdm() и добавьте описательные метки для каждого итерируемого объекта.
Следующий код содержит три вложенных цикла и может служить примером того, как будут выглядеть вложенные индикаторы выполнения:
from tqdm import trange
import time
for i in trange(3, desc='outer loop'):
for j in trange(2, desc='middle loop'):
for k in trange(6, desc='inner loop'):
time.sleep(0.01) В итоговом результате можно заметить закономерность в обновлении индикаторов выполнения. Tqdm всегда будет начинать с самого внешнего цикла и идти до самого внутреннего.
Предположим, что у нас есть три вложенных цикла, как в примере. После начальной внутренней итерации tqdm перейдет к среднему циклу, обновит его как завершивший одну итерацию, а затем вернется к внутреннему итерируемому циклу. Этот процесс повторяется до тех пор, пока средний цикл не будет отмечен как завершенный. После этого в индикаторе внешнего цикла появится одна завершенная итерация.
То же самое происходит между внешним и средним циклом. В итоге последний внутренний цикл завершает среднюю итерацию, которая, в свою очередь, завершает последнюю внешнюю итерацию.
Обновление индикаторов выполнения вручную может быть полезно в нескольких сценариях:
Для ручного обновления индикатора выполнения tqdm важно указать параметр total как оценку максимального ожидаемого количества итераций. Затем, по мере обработки новых элементов, нужно обновлять индикатор выполнения с помощью метода update(). Значение update должно представлять количество итераций, обработанных с момента последнего обновления.
Допустим, мы ожидаем, что наш итерируемый объект будет содержать до 750 элементов. В данном примере фактическая длина — это случайное число между 100 и 1000, которое нам неизвестно. Мы инициируем progress_bar, устанавливая estimated_total равным 750. Затем мы итерируем данные, обновляя индикатор выполнения после обработки каждой точки.
from tqdm import tqdm def process_data(data): time.sleep(0.01) # simulate processing data processed_data = data return processed_data # Generate an iterable with random length between 100 and 1,000 random_length = random.randint(100, 1000) data_list = [i for i in range(random_length)] # Define estimated maximum number of iterations estimated_total = 750 # Define the progress bar using the estimated_total progress_bar = tqdm(total=estimated_total) # Iterating through data list of unknown length for data in data_list: processed_data = process_data(data) progress_bar.update(1)
Параллелизм и многопоточность — это методы, используемые для одновременного выполнения задач и повышающие производительность и скорость отклика. В таких сценариях бывает сложно отследить ход выполнения отдельных задач или общий ход параллельного выполнения. Tqdm может стать ценным инструментом для обеспечения визуальной обратной связи и контроля за ходом выполнения параллельных операций.
Модуль tqdm.contrib.concurrent предоставляет специализированные функции для создания индикаторов выполнения в контекстах многопроцессной или многопоточной обработки. Эти функции управляют синхронизацией и взаимодействием между главным процессом и рабочими процессами или потоками, обеспечивая корректное обновление индикатора выполнения. Они предназначены для работы с concurrent.futures API с использованием функции ProcessPoolExecutor() или ThreadPoolExecutor().
Вот пример использования модуля tqdm.contrib.concurrent.futures:
import concurrent.futures
from tqdm.contrib.concurrent import process_map
def process_data(data):
for i in tqdm(range(100), desc=f"Processing {data['name']}"):
# Process data
time.sleep(0.01)
if __name__ == '__main__':
with concurrent.futures.ProcessPoolExecutor() as executor:
results = process_map(process_data, [
{'name': 'dataset1'},
{'name': 'dataset2'},
# …
]) В этом примере функция process_data() содержит прогресс-бар tqdm для отслеживания хода выполнения. Функция process_data() будет выполняться параллельно для каждого элемента данных в списке. Это означает, что одновременно будет отображаться несколько индикаторов, каждый из которых будет представлять ход выполнения отдельного процесса. Параметр desc задается для динамического создания описания для каждого индикатора выполнения на основе имени соответствующего набора данных, что поможет различать разные индикаторы.
Модуль tqdm.pandas предоставляет удобный способ добавления индикаторов выполнения к операциям pandas. Это особенно полезно для трудоемких операций над большими датафреймами, так как обеспечивает визуальную обратную связь о ходе выполнения задачи. Мы можем применить декоратор tqdm.pandas() к любой функции pandas, работающей со строками или столбцами.
Для начала мы определим случайный DataFrame со 100 000 строк и вызовем декоратор tqdm.pandas(). Если мы хотим настроить индикатор выполнения, сейчас самое время это сделать, поскольку функции progress_apply() и progress_map() не принимают параметры tqdm(). Здесь мы хотим дать имена индикаторам выполнения, поэтому указываем параметр desc.
import pandas as pd import numpy as np from tqdm import tqdm df = pd.DataFrame(np.random.randint(0, 10, (100000, 6))) tqdm.pandas(desc='DataFrame Operation')
Теперь мы можем применять функции к строкам, столбцам или всему DataFrame. Вместо того чтобы использовать функцию apply() или map(), вызовите progress_apply() или progress_map(), и индикатор выполнения будет отображен. Помните, что apply() и progress_apply() можно применять к DataFrame, строкам или столбцам, а map() и progress_map() — только к Series или столбцам. Например:
# Halving each value in the DataFrame using progress_apply() Result_apply = df.progress_apply(lambda x: x / 2) # Doubling each element of the first column using progress_map() result_map = df[0].progress_map(lambda x: x * 2)
Давайте обсудим некоторые распространенные проблемы, возникающие при использовании tqdm, и способы их устранения.
Одна из самых распространенных проблем — не обновляющийся индикатор выполнения. Это часто происходит из-за проблем с буферизацией, особенно в таких средах, как Jupyter Notebooks. Когда вывод буферизируется, индикатор выполнения может не отображаться или немедленно обновляться, что приводит к ощущению замороженного или неотвечающего процесса.
Использование модуля tqdm.notebook позволяет решить проблемы с буферизацией и обеспечить корректное обновление индикатора в Jupyter Notebooks. Этот модуль предоставляет графический индикатор выполнения, специально разработанный для сред Jupyter. Кроме того, он предлагает удобные для пользователя цветовые подсказки (синий: нормально, зеленый: завершено, красный: ошибка/прерывание).
Если мы прервем код из нашего примера с вложенным индикатором выполнения, то он будет выглядеть следующим образом:
Еще один эффективный способ устранения неполадок с необновляющимися индикаторами выполнения — явный сброс (англ. flush) потока вывода. Когда данные записываются в стандартный поток вывода (например, с помощью функции print()), они обычно буферизируются перед отправкой на устройство вывода. Сброс потока вывода заставляет интерпретатор Python немедленно отправить все буферизованные данные на устройство вывода, гарантируя, что данные будут отображены или записаны без задержки.
Чтобы сбросить вывод, используйте метод flush() стандартного потока вывода. Для более отзывчивого вывода рассмотрите возможность сбрасывать поток вывода почаще, возможно, после нескольких итераций или через определенное время. Но не забывайте, что это может привести к дополнительным накладным расходам, так что нужно искать компромисс.
Вот один из примеров применения этого метода в простом процессе tqdm:
import sys import time from tqdm import tqdm for i in tqdm(range(100)): time.sleep(0.1) sys.stdout.flush() # Flush the output stream after each iteration
Хотя tqdm в целом совместима с большинством сред и библиотек Python, иногда могут возникать проблемы с совместимостью или неожиданное поведение. К числу распространенных сценариев, о которых следует знать, относятся:
При возникновении проблем с совместимостью можно воспользоваться следующими обходными путями:
Имея в виду эти потенциальные проблемы совместимости и пути их решения (обхода), мы сможем эффективно устранять любые неурядицы, возникающие при использовании tqdm в наших Python-проектах.
В заключение можно сказать, что tqdm — это библиотека Python, которая предоставляет нам индикаторы выполнения и полезную статистику, облегчая мониторинг и управление выполнением кода.
Если вы выполняете итерации над большими наборами данных, обучаете модели машинного обучения или выполняете любую другую трудоемкую операцию, tqdm предлагает простой, но мощный способ отслеживать прогресс и быть в курсе состояния вашего кода.
Для дальнейшего изучения не стесняйтесь ознакомиться с документацией библиотеки tqdm и ее исходным кодом и Readme на GitHub.
Перевод статьи “Tqdm Python: A Guide With Practical Examples”.
Если вы ищете работу на Python, на Talanto.work сейчас есть варианты для junior, middle, senior…
Рынок Python-вакансий большой, но удобным его назвать сложно. Формально вакансий много, однако в реальном поиске…
Срезы в Python — это больше, чем a[2:7]. В статье — быстрый разбор полезных приёмов:…
В этой статье мы рассмотрим возведение в степень в Python с помощью различных методов, от…
Дубликаты в списке Python — частая проблема. В статье показано, как быстро убрать повторы, в…
Услуга виртуального хостинга на SpaceWeb позволяет арендовать часть сервера (shared-сервера) и разместить на нём свой…