Время прочтения: 9 мин.

Если вы хотите сделать ваш Jupyter Notebook более «живым» или подготовить небольшую форму для обработки данных для людей, которые не знают Python, то ipywidgets будет очень полезна. С помощью ipywidgets можно добавить различные интерактивные элементы в блокнот: поля для ввода различных данных, счетчики, checkbox, различные кнопки, полосу прогресса и многое другое.

Рассмотрим использование библиотеки на нескольких задачах.

Задача 1.

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

Выполняем импорт библиотек.

import ipywidgets as pw
from IPython.display import display
import pandas as pd
from ipywidgets import interact
import time

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

Чтобы при нажатии на кнопку выполнялись действия, добавим вызов функции on_click() у кнопки. В качестве параметра в функцию передается другая функция, где прописано, какие действия нужно выполнить при нажатии на кнопку.

# функция обработчик события при нажатии на кнопку
def get_columns(e):
    # путь до файла
    path_file = text_area.value
    if path_file != '':
        path_file = path_file.replace('"', '')
        df = pd.read_excel(path_file)
        list_col = df.columns.to_list()
        @interact
        def show_unique_values(column=list_col):
            return df[column].drop_duplicates()            
    else:
        print('Введите путь до файла!')   

# создание текстового поля
text_area = pw.Text(value='', placeholder='Путь до файла')
display(text_area)

# создание кнопки
button = pw.Button(description='Получить колонки')
display(button)
button.on_click(get_columns)

Интерактивная функция show_unique_values() принимает в качестве параметров список с названиями колонок и создает выпадающий список. Каждый раз, выбирая колонку в списке, на экране будут появляться уникальные значения выбранной колонки (рисунок 1).

Рисунок 1. Вывод уникальных значений выбранной колонки

У каждого элемента есть свои параметры, и чтобы узнать их полный список воспользуемся методом keys. Параметры для кнопки представлены на рисунке 2.

Рисунок 2. Параметры кнопки

Для каждого элемента можно создать свой стиль, задать нужные размеры, изменить шрифт или цвет. Рассмотрим, как это можно сделать на примере кнопки. У кнопки есть атрибут style, который принимает на вход словарь с ключами и значениями.

Если необходимо изменить размеры элемента, задать отступы и позицию на экране, то будем использовать атрибут layout. Можно создать отдельную переменную с настройками, чтобы использовать их для различных элементов блокнота. На рисунке 3 представлена кнопка, у которой изменили высоту и ширину, границы, сделали отступ слева, изменили шрифт и цвет кнопки.

layout = pw.Layout(width='150px', height='60px', border='solid green', left='50px')
button = pw.Button(description='Получить колонки', 
                   style={'button_color':'khaki', 'font_weight':'bold'}, 
                  layout=layout)
Рисунок 3. Стилизация кнопки

Задача 2.

Добавить визуализацию прогресса расчета калорийности продуктов.

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

У нас имеется файл, содержащий данные о количестве белков, жиров и углеводов на 100 грамм. Рассчитаем по формуле калорийность каждого продукта и добавим визуализацию выполнения расчета (рисунок 4).

path_file = r"D:\Файлы\Продукты.xlsx"
df = pd.read_excel(path_file)
df['Калорийность'] = ''
# создание полосы прогресса 
progress = pw.IntProgress(value=1, min=1, max=len(df), step=1, 
                          description='Расчет:', bar_style='info', 
                         orientation='horizontal')
display(progress)
count = 0
# подсчитываем калорийность
for index, row in df.iterrows():
    count += 1
    df.at[index, 'Калорийность'] = row['Белки'] * 4 + row['Жиры'] * 9 + row['Углеводы'] * 4
    progress.value = count
    time.sleep(1)   
df
Рисунок 4. Прогресс выполнения расчетов

В самом элементе IntProgress() можно задать минимальное, максимальное значения, шаг, описание, стиль и ориентацию. Задержка time.sleep() была добавлена специально для наглядности. В коде создаю и вывожу полосу прогресса на экран до того, как задаем цикл. В самом же цикле только меняю текущее значение value у элемента progress.

Задача 3.

Построить диаграмму, которая показывает количество калорий, жиров, углеводов или белков для продуктов на выбор. Предусмотреть выбор цвета графика и количество выводимых данных на диаграмму.

Для построения диаграммы будем использовать библиотеку matplotlib. Создадим элемент ColorPicker(), который позволяет выбирать цвет из палитры, а также два элемента, которые позволяют устанавливать количество выводимых колонок на диаграмму. Элемент IntSlider() позволяет выбирать значение с помощью бегунка, но это не всегда бывает удобно. Поэтому создадим элемент IntText(), который позволяет ввести нужное значение. Чтобы оба элемента показывали одинаковые значения, свяжем их между собой. Теперь пользователь может использовать любой элемент на свое усмотрение.

import matplotlib.pyplot as plt

path_file = r"D:\Файлы\Продукты.xlsx"
df = pd.read_excel(path_file)
list_col = df.columns.to_list()[1:]

# создание элемента для выбора цвета
color_picker = pw.ColorPicker(concise=False, description='Цвет:', value='green', 
					layout=pw.Layout(width='200px'))
# с элементы для выбора количества колонок
int_slider = pw.IntSlider(value=10, min=1, max=100, step=1, description='Количество:', 
                          orientation='horizontal')
int_text = pw.IntText(value=10, layout=pw.Layout(width='100px'))

# связываем между собой значения двух элементов
link = pw.link((int_slider, 'value'), (int_text, 'value'))

Выводить элементы на экран можно последовательно, тогда каждый виджет будет на новой строке. А можно объединить элементы в группу и расположить их в виде таблицы или строки. Воспользуемся элементом GridBox(), который принимает на вход список элементов, которые нужно расположить в виде таблицы, и ширину колонок. Так как у нас всего три элемента (ColorPicker(), IntSlider(), IntText()), то расположим их в одной строке, а в параметре grid_template_columns укажем ширину каждой колонки. Если необходимо создать несколько колонок одинаковой ширины, то в параметре grid_template_columns можно записать ‘repeat(3, 300px)’, и тогда будет создано три колонки по 300px.

# объединяем элементы в виде таблицы
grid_box = pw.GridBox([color_picker, int_slider, int_text], 
                      layout=pw.Layout(grid_template_columns="400px 350px 150px")) 
display(grid_box)

Теперь создаем интерактивную функцию, которая будет рисовать диаграмму в зависимости от выбранной колонки. По оси Y будет название продукта, по оси Х значение из выбранной колонки. Выпадающий список с названиями колонок будет создан автоматически, количество элементов для вывода на диаграмму будет взято из элемента IntText(), а цвет колонок – из ColorPicker() (рисунок 5).

@interact
def show_bar(column=list_col):
    count = int_text.value
    x = df['Название продукта'].to_list()[:count]
    y = df[column].to_list()[:count]
    return plt.barh(x, y, color=color_picker.value)
Рисунок 5. Пример диаграммы

При необходимости можно создать интерактивную функцию с двумя параметрами на входе – названием колонки и количеством элементов. Объявление функции будет выглядеть тогда так: def show_bar(column=list_col, count=10). Создавать элементы IntSlider(), IntText() и GridBox() больше нет необходимости – интерактивная функция сама создаст IntSlider() (рисунок 6).

Рисунок 6. Пример диаграммы

Задача 4.

Организовать фильтрацию данных из таблицы с использованием различных виджетов.

В качестве набора данных возьмем датасет Mall Customers Dataset. В нем содержаться данные о клиентах: id, пол, возраст, доход и рейтинг трат. Чтобы организовать фильтрацию данных будем использовать следующие элементы:

  • Checkbox для выбора пола;
  • RadioButton для указания диапазона трат;
  • IntRangeSlider для указания диапазона доходов.

Так как я хочу использовать определенные виджеты , то использовать интерактивную функцию для отображения данных не получится, так как она автоматически сама создает определенные виджеты. Поэтому вывод данных будет происходить при нажатии на кнопку.

Для выбора пола создаем два Checkbox и объединяем их, чтобы элементы были друг под другом. Создаем радио-кнопки с диапазонами рейтингов и элемент для указания диапазона доходов. Чтобы все элементы смотрелись красиво на экране, объединим их в таблицу и добавим подписи в виде Labels (рисунок 7). Добавляем кнопку и создаем функцию, выполняющую действия при ее нажатии.

path_file = r"D:\Файлы\Mall Customers Dataset.csv"
df = pd.read_csv(path_file, sep=',', encoding='utf-8')

layout=pw.Layout(width='200px')
# создаем Checkbox для выбора пола
male_checkBox = pw.Checkbox(value=False, description='Male', layout=layout)
female_checkBox = pw.Checkbox(value=False, description='Female', layout=layout)

# объединяем Checkbox в вертикальную «коробку»
vBox = pw.VBox([male_checkBox, female_checkBox])

# создаем радио-кнопки
radio_buttons = pw.RadioButtons(options=['1-15', '16-30', '31-45', '46-60', '61-75', '75-99'], 
                               value = '1-15')
# создаем элемент для выбора диапазона
int_range_slider = pw.IntRangeSlider(value=[10, 15], min=df['Annual Income'].min(), 
                                    max=df['Annual Income'].max(), step=1)

label_male = pw.Label(value='Пол')
label_rate = pw.Label(value='Рейтинг трат')
label_money = pw.Label(value='Доходы')
# объединяем элементы в таблицу
grid_box = pw.GridBox([label_male, label_rate, label_money, vBox, radio_buttons, int_range_slider], 
                      layout=pw.Layout(grid_template_columns="300px 300px 150px")) 
display(grid_box)

# создаем кнопку
button = pw.Button(description='Получить данные', layout=layout)
display(button)
button.on_click(get_data)
Рисунок 7. Виджеты для фильтрации данных.

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

def get_data(e):
    # получаем значения с виджетов
    male_check = male_checkBox.value
    female_check = female_checkBox.value
    rate = radio_buttons.value
    rate_min, rate_max = rate.split('-')
    money_min = int_range_slider.value[0]
    money_max = int_range_slider.value[1]
    df_loc = pd.DataFrame()
    # формируем выборку
    if male_check and not female_check:
        df_loc = df.loc[(df['Gender'] == 'Male') & 
                        (df['Annual Income'] >= int(money_min)) & 
                        (df['Annual Income'] <= int(money_max)) & 
                        (df['Spending Score'] >= int(rate_min)) & 
                        (df['Spending Score'] <= int(rate_max))]
    elif not male_check and female_check:
        df_loc = df.loc[(df['Gender'] == 'Female') & 
                        (df['Annual Income'] >= int(money_min)) & 
                        (df['Annual Income'] <= int(money_max)) & 
                        (df['Spending Score'] >= int(rate_min)) & 
                        (df['Spending Score'] <= int(rate_max))]
    elif male_check and female_check:
        df_loc = df.loc[(df['Annual Income'] >= int(money_min)) & 
                        (df['Annual Income'] <= int(money_max)) & 
                        (df['Spending Score'] >= int(rate_min)) & 
                        (df['Spending Score'] <= int(rate_max))]
    elif not male_check and not female_check:
        print('Выберите пол')

    # вывод данных
    if len(df_loc) == 0 and (male_check or female_check):
        print('Данные по условию не найдены')
    else:
        print(df_loc)

Выберем лица женского пола с рейтингом трат 61-75 и доходами от 70 до 87, как показано на рисунке 7. Получаем вывод данных (рисунок 8).

Рисунок 8. Фильтрация данных

Я рассмотрела использование некоторых виджетов и создание интерактивной функции на примере четырех задач. Но это только небольшая часть функционала библиотеки, который можно использовать. Ipywidgets поможет оживить ваш Notebook в зависимости от задачи, которая стоит перед вами.