Как создать параметризованные блокноты Jupyter Notebook с помощью Papermill

Введение

В программировании функции — это способы модульного разделения кода на автономные, организованные и многократно используемые блоки, используемые для выполнения конкретной задачи. Функции обычно принимают входные данные, обрабатывают данные и выводят результат. Некоторые преимущества написания кода в виде функций:

  • Улучшить возможность повторного использования кода
  • Разбейте проблему на более мелкие решаемые части
  • Держите более чистое пространство имен переменных
  • Возможность тестировать небольшие части кода изолированно

В этой статье мы рассмотрим, как мы можем параметризовать наш блокнот Jupyter, чтобы он работал как функция.

Подождите, но почему?

Мотивация

Давайте представим, что мы занимаемся недвижимостью, и у пользователей есть вопросы, связанные с продажей недвижимости за последние несколько лет. Мы будем использовать набор данных о ценах на перепродажу жилья в Сингапуре[1], который содержит записи о транзакциях по перепродаже домов Советом по развитию жилищного строительства (HDB). Вот пример набора данных.

Бизнес-пользователям интересно ответить на следующие вопросы для town Центрального района в период с month января 2017 г. по декабрь 2021 г.:

  1. Количество продаж в month
  2. resale_price через month
  3. Распределение resale_price по разным flat_type

Мы импортируем необходимые пакеты, считываем данные и фильтруем данные для правильного подмножества, указанного town и date. Имея правильное подмножество данных, мы теперь можем построить соответствующие диаграммы, чтобы ответить на вопросы.

Все сделано! Отчет для town «Центральной области», запрошенный бизнес-пользователями, готов.

Что делать, если бизнес-пользователи запрашивают ту же информацию для другого town?

Достаточно просто, давайте найдем и заменим! Мы просто находим все старые значения для town и заменяем их новыми значениями, запускаем записную книжку и сохраняем копию записной книжки.

Что, если бизнес-пользователи будут запрашивать одну и ту же информацию для каждого town доступного?

Всего 29 городов; возможно, не стоит использовать ручной метод «найти и заменить» 29 раз! Вот где Papermill[2] пригодится.

Бумажная фабрика

Papermill – это пакет Python с открытым исходным кодом, который параметризирует и запускает Jupyter Notebooks.

Параметризация — это процесс обобщения блока кода, чтобы сделать его пригодным для повторного использования, путем взятия значений из кода и превращения их в параметр этой функции. Вместо того, чтобы параметрировать небольшой блок кода, мы собираемся параметризовать весь блокнот Jupyter. Это позволяет нам легко запускать один и тот же ноутбук с разными параметрами. Давайте погрузимся в пример.

1. Настройка среды

pip install papermill

Это файловая структура проекта:

  • data : содержит входные данные
  • product: хранить копии выполненных Jupyter Notebooks
  • tasks: содержит блокноты Jupyter Notebook template.ipynb и driver.ipynb
  • template.ipynb: здесь находится код обработки и визуализации данных. Он похож на код, который мы видели ранее.
  • driver.ipynb: это блокнот, содержащий фактический набор параметров, из которого Papermill вставит ячейку параметров в template.ipynb, запустит и сохранит копию.

2. Параметризируйте ноутбук

Давайте посмотрим, какие значения в нашем коде можно параметризовать. Мы назовем параметризованный блокнот template.ipynb. В общем, мы можем параметризовать любые значения, если предвидим их будущие изменения. Простой способ начать — проверить, по каким столбцам фильтруется DataFrame и какие значения жестко закодированы.

df = pd.read_csv('data/resale-flat-prices.csv')
df['date'] = pd.to_datetime(df['month'], format = '%Y-%m')
df = df.loc[(df['town'] == 'CENTRAL AREA') & (df['date'].between('2017-01-01', '2021-12-01'))]

DataFrame фильтруется с помощью town и date, поэтому мы можем заменить значения town и date переменной вместо того, чтобы жестко кодировать ее. Если мы ожидаем изменения пути к файлу данных, мы также можем параметризовать его.

Вот еще части кода, которые можно параметризовать. Мы жестко закодировали значение town в заголовке диаграммы, которое мы можем заменить переменной.

sales_over_time = df.groupby('date', as_index = False).agg({'resale_price':'count'}).rename(columns = {'resale_price':'resale_count'})
fig, ax = plt.subplots(figsize = (10, 5)) ax = sns.lineplot(data = sales_over_time, x = 'date', y = 'resale_count') ax.set_xlabel('Month')
ax.set_ylabel('Sales Transaction')
ax.set_title('Number of Sales Over Time - Central Area')
plt.show()

Вот как выглядит код до и после параметризации. Жестко закодированные значения townи dateзаменяются переменными.

# before
df = pd.read_csv('data/resale-flat-prices.csv')
df['date'] = pd.to_datetime(df['month'], format = '%Y-%m')
df = df.loc[(df['town'] == 'CENTRAL AREA') & (df['date'].between('2017-01-01', '2021-12-01'))]
# after
df = pd.read_csv(input_path)
df['date'] = pd.to_datetime(df['month'], format = '%Y-%m')
df = df.loc[(df['town'] == town) & (df['date'].between(start_date, end_date))]

Название сюжета также параметризуется с помощью f-строк.

# before
..
ax.set_title('Number of Sales Over Time - Central Area')
# after
..
ax.set_title(f'Number of Sales Over Time - {town.title()}')

Для блокнота были созданы следующие параметры: input_path, town, start_date, end_date, и вот так теперь выглядит template.ipynb.

Никакие значения не были присвоены ни одной из переменных input_path, town, start_date, end_date. Следующая ячейка была бы включена, если бы эта записная книжка использовалась один раз или несколько раз.

# notebook cell
# Parameters
input_path = "../data/resale-flat-prices.csv"
start_date = "2017-01-01"
end_date = "2021-01-01"
town = "CENTRAL AREA"

Перед каждым запуском мы вручную меняли значения параметров в соответствии с запросами бизнес-пользователей.

Однако этот метод не подходит для ситуаций с большим количеством наборов параметров. В нашем случае 29 городов, и невозможно вручную изменить значение town 29 раз. Нам нужен способ автоматической передачи различных наборов параметров в template.ipynb, и об этом позаботится driver.ipynb.

3. Создайте driver.ipynb

Вот где происходит волшебство. В целях иллюстрации мы генерируем отчет только для 4 разных городов, создавая словарь параметров для каждого города и передавая его методу .execute_notebook() Papermill.

# driver.ipynb
import papermill as pm
# generate report for each town
towns = ['CENTRAL AREA', 'MARINE PARADE', 'WOODLANDS', 'QUEENSTOWN']
for town in towns:
    params = {'input_path': '../data/resale-flat-prices.csv',
            'start_date': '2017-01-01',
            'end_date': '2021-01-01',
            'town': town}
    
    nb_output = pm.execute_notebook('template.ipynb', f'../products/report_{town.title()}.ipynb', parameters = params)

Аргументы для .execute_notebook():

  • input_path: (str или path) путь к вводу Jupyter Notebook, который мы собираемся выполнить, в нашем случае это будет template.ipynb.
  • output_path: (str или path) путь для сохранения выполненного Jupyter Notebook. Мы сохраняем выполненную записную книжку с объединенным названием города.
  • parameters: (dict) параметры, переданные в Jupyter Notebook в input_path

.execute_notebook() делает следующее:

  1. Находит входной блокнот, т.е. template.ipynb

2. Вводит параметры в template.ipynb во время выполнения

3. Запускает template.ipynb с введенными параметрами

4. Сохраняет копию выполненной записной книжки вместе с выводами ячеек в output_path

4. Запустите driver.ipynb

При выполнении блокнот driver.ipynb передает разные наборы параметров в template.ipynb , выполняет template.ipynb и сохраняет копию выполненных блокнотов для разных наборов параметров в output_path, определенном в .execute_notebook().

На изображении ниже показаны сохраненные выходные блокноты.

Давайте взглянем на одну из сохраненных записных книжек. В report_Central Area.ipynb мы видим, что Papermill ввел набор параметров в первую ячейку.

Заключение

В этой статье мы мотивировали параметризацию Jupyter Notebook и продемонстрировали пример использования для анализа данных о том, как запустить Papermill через API Python. Сценарии использования Papermill не ограничиваются созданием отчетов. Другие пакеты с открытым исходным кодом, такие как Ploomber, используют Papermill для ввода параметров в ячейки для создания многоразовых конвейеров машинного обучения. Papermill также предлагает выполнение через интерфейс командной строки и позволяет использовать .yamlfile для хранения параметров. Посетите Papermill github для получения дополнительной информации.

Ссылка

[1] Содержит информацию из Resale Flat Prices, доступ к которой был получен 17 марта 2022 г. по адресуhttps://data.gov.sg/dataset/resale-flat-prices, который доступен в соответствии с условия Сингапурской лицензии на открытые данные версии 1.0https://data.gov.sg/open-data-licence. Разрешено для публичного использования (коммерческого и некоммерческого).

[2] Бумажная фабрика