Дата публикации May 3, 2019
Традиционно, каждый раз, когда вам нужно изменить вывод ячеек вашего ноутбука, вам нужно изменить код и повторно запустить затронутые ячейки. Это может быть громоздким, неэффективным и подверженным ошибкам, а в случае нетехнического пользователя это может быть даже неосуществимым. Это гдеipywidgetsвступить в игру: они могут быть встроены в ноутбук и обеспечитьудобныйинтерфейс для сбора пользовательского ввода и просмотра влияния изменений на данные / результаты без необходимости взаимодействия с кодом; Ваши ноутбуки могут быть преобразованы из статических документов в динамические информационные панели - идеально для демонстрации вашей истории данных!
⚠️ Область применения:Ресурсы по ipywidgets ограничены, и очень немногие учебники либо неполные, либо посвящены
interact
Функция / декоратор. Этополный учебнико том, как вы можете получить полный контроль над виджетами для создания мощных панелей.Мы начнем с основ: добавление виджета и объяснение того, как работают события, и мы постепенно разработаем панель инструментов.
Я буду вести вас шаг за шагом, опираясь на примеры по ходу дела.
Если вы когда-либо создалиграфический интерфейс пользователя(GUI), тогда вы уже знаете, что такое виджет. Но давайте дадим определение быстро в любом случае:
Виджет представляет собой графический элемент, такие как кнопку, выпадающий список или текстовое поле, которое постоянно находится в браузере и позволяет нам контролировать код и данные, реагируя на события и вызов указанных обработчиков.
Эти элементы GUI могут быть собраны и настроены для создания сложных панелей мониторинга.
В этой статье мы увидим некоторые из них в действии.
Готовы? 🏁
Для того, чтобы начать использовать библиотеку нам нужно установитьipywidgetsрасширение. Если используется conda, мы набираем эту команду в терминале:
conda install -c conda-forge ipywidgets
Для pip это будет двухэтапный процесс: 1. установить и 2. включить:
pip install ipywidgetsjupyter nbextension enable --py widgetsnbextension
Чтобы включить виджеты в блокнот, мы должны импортировать модуль, как показано ниже:
import ipywidgets as widgets
Чтобы добавить слайдер, мы можем определить минимальное и максимальное значения, размер интервала (шаг), описание и начальное значение:
widgets.IntSlider(
min=0,
max=10,
step=1,
description='Slider:',
value=3
)
display()
Функция предоставляет объект виджета в ячейке ввода.
Первый импорт:
from IPython.display import display
Затем передайте виджет в качестве параметра в функцию display ():
slider = widgets.IntSlider()
display(slider)
Чтобы прочитать значение виджета, мы запросим егоvalue
свойство. Кроме того, мы можем установить значение виджета:
Мы можем синхронизировать значения двух виджетов, используяjslink()
функция.
slider = widgets.IntSlider()
text = widgets.IntText()
display(slider, text)widgets.jslink((slider, 'value'), (text, 'value'))
Для полного списка виджетов вы можете проверитьдокументацияИли выполните следующую команду:
print(dir(widgets))
Виджеты могут реагировать на события, которые возникают, когда пользователь взаимодействует с ними. Простой пример - нажатие кнопки - мы ожидаем, что действие произойдет.
Давайте посмотрим, как это работает ...
В зависимости от своих особенностей каждый виджет предоставляет различные события.обработчик событиябудет выполняться каждый раз, когда событие.
Обработчик событий - это функция обратного вызова в ответ на событие, которая работает асинхронно и обрабатывает полученные входные данные.
Здесь мы создадим простую кнопку под названиемbtn
,on_click
метод вызывается при нажатии на кнопку.
Наш обработчик событий,btn_eventhandler
, напечатает короткое сообщение с заголовком кнопки - обратите внимание, что входной аргумент обработчика,obj
, это сам объект кнопки, который позволяет нам получить доступ к его свойствам.
Чтобы связать событие с обработчиком, мы назначаем последнее кнопкеon_click
метод.
btn = widgets.Button(description='Medium')
display(btn)def btn_eventhandler(obj):
print('Hello from the {} button!'.format(obj.description))btn.on_click(btn_eventhandler)
Что приятно приведет нас к следующему разделу, так это то, что вывод появляется вта же клеткакак сама кнопка. Итак, давайте посмотрим, как мы можем повысить гибкость нашего ноутбука!
В этом разделе мы рассмотрим, как использовать виджеты дляуправлять кадром данных, Образец набора данных я выбрал это «Количество международных посетителей в Лондон», Который показывает итоговые посетителей Лондона касаемо ночей, посещений и расходов, с разбивкой по годам, кварталам, цели, продолжительность, режим и страны.
Изначально мы получим данные и загрузим их в фрейм данных:
import pandas as pd
import numpy as npurl = "https://data.london.gov.uk/download/number-international-visitors-london/b1e0f953-4c8a-4b45-95f5-e0d143d5641e/international-visitors-london-raw.csv"df_london = pd.read_csv(url)
Предположим, что мы хотели быфильтрdataframe погод, Сначала мы определим выпадающий список и заполним его списком уникальных значений года.
Для того, чтобы сделать это, мы создадим обобщенную функцию,unique_sorted_values_plus_ALL
, Который будет найти уникальные значения, сортировать их, а затем добавитьALL
Пункт в начале, так что пользователь может удалить фильтр
ALL = 'ALL'def unique_sorted_values_plus_ALL(array):
unique = array.unique().tolist()
unique.sort()
unique.insert(0, ALL)
return unique
Теперь мы инициализируем выпадающий список:
dropdown_year = widgets.Dropdown(options = unique_sorted_values_plus_ALL(df_london.year))
Выпадающий виджет предоставляетobserve
метод, который принимает функцию, которая будет вызываться при изменении значения раскрывающегося списка. Таким образом, мы затем создадим обработчик наблюдателя для фильтрации кадра данных по выбранным значениям - обратите внимание, что входной аргумент обработчика,change
, содержит информацию об изменениях, которые произошли, что позволяет нам получить доступ кnew
ценность (change.new
).
Если новое значениеALL
мы удаляем фильтр, иначе применяем его:
def dropdown_year_eventhandler(change):
if (change.new == ALL):
display(df_london)
else:
display(df_london[df_london.year == change.new])
Затем мы свяжем обработчик с выпадающим списком:
dropdown_year.observe(dropdown_year_eventhandler, names='value')
Пока все хорошо, но вывод всех запросовнакопленияв этой самой клетке; т.е. если мы выберем новый год из выпадающего списка, новый фрейм данных будет отображаться под первым, в той же ячейке.
Желаемое поведение, хотя, этообновлениесодержимое кадра данных каждый раз.
Решением этой проблемы является захват вывода ячейки в особый виджет, а именноOutput
, а затем отобразить его в другой ячейке.
Мы немного подправим код:
Output
output_year = widgets.Output()
clear_output
метод в обработчике событий, чтобы очистить предыдущий выбор на каждой итерации и захватить выходные данные кадра данных вwith
блок.def dropdown_year_eventhandler(change):
output_year.clear_output()
with output_year:
display(df_london[df_london.year == change.new])
Затем мы отобразим вывод в новой ячейке:
display(output_year)
Вот как это работает:
Как вы можете видеть, выходные данные отображаются в новой ячейке, и фильтрация работает, как и ожидалось! 👍
Продолжая предыдущий пример, давайте предположим, что мы также хотели бы отфильтровать поцельслишком.
Если мы продолжим и добавим еще одно раскрывающееся меню, мы быстро поймем, что информационный фрейм реагирует на фильтр только тем раскрывающимся списком, который был недавно изменен. Что нам нужно сделать, этоссылкадва вместе, так что он может работать на оба значения (то есть год и цель).
Давайте посмотрим, как это должно работать:
Во-первых, нам нужен общий вывод для обоих выпадающих списков:
output = widgets.Output()
Вот два выпадающих списка:
dropdown_year = widgets.Dropdown(options = unique_sorted_values_plus_ALL(df_london.year))dropdown_purpose = widgets.Dropdown(options = unique_sorted_values_plus_ALL(df_london.purpose))
Затем мы создаем новую функцию,common_filtering
, который будет вызываться обоими обработчиками событий. Эта функция будет применять фильтр к данным на оба годаAND
цель:
Мы очищаем вывод, затем мы проверяем, является ли какое-либо из значенийALL
, в этом случае мы считаем, что соответствующий фильтр удален. Когда оба фильтра присутствуют, вelse
Заявление, мы применяем&
работа в обоих фильтрах. Наконец мы фиксируем вывод:
def common_filtering(year, purpose):
output.clear_output()
if (year == ALL) & (purpose == ALL):
common_filter = df_london
elif (year == ALL):
common_filter = df_london[df_london.purpose == purpose]
elif (purpose == ALL):
common_filter = df_london[df_london.year == year]
else:
common_filter = df_london[(df_london.year == year) &
(df_london.purpose == purpose)]
with output:
display(common_filter)
Мы изменяем обработчики событий, чтобы вызватьcommon_filtering
функционировать и передатьchange.new
значение, а также текущийvalue
другого выпадающего списка:
def dropdown_year_eventhandler(change):
common_filtering(change.new, dropdown_purpose.value)def dropdown_purpose_eventhandler(change):
common_filtering(dropdown_year.value, change.new)
Мы привязываем обработчики к выпадающим спискам, и на этом все!
dropdown_year.observe(
dropdown_year_eventhandler, names='value')dropdown_purpose.observe(
dropdown_purpose_eventhandler, names='value')
Фрагмент кода:
Вот демо:
Мы заложили основу для нашей панели мониторинга, отфильтровав и отобразив данные лондонского набора данных. Мы продолжим, раскрасив числовые значения в зависимости от выбранного пользователем значения.
Полезный числовой виджетBoundedFloatText
; мы дадим емуmin
,max
и начальныйvalue
и инкрементныйstep
,
bounded_num = widgets.BoundedFloatText(
min=0, max=100000, value=5, step=1)
Чтобы закрасить ячейки данных, мы определим эту функцию:
def colour_ge_value(value, comparison):
if value >= comparison:
return 'color: red'
else:
return 'color: black'
Теперь мы минимально исправимcommon_filtering
функция для:
num
входной параметр:def common_filtering(year, purpose, num):
colour_ge_value
функция для трех числовых столбцов:with output:
display(common_filter
.style.applymap(
lambda x: colour_ge_value(x, num),
subset=['visits','spend', 'nights']))
Существующие обработчики событий должны быть скорректированы, чтобы передатьbounded_num.value
:
def dropdown_year_eventhandler(change):
common_filtering(change.new, dropdown_purpose.value,
bounded_num.value)def dropdown_purpose_eventhandler(change):
common_filtering(dropdown_year.value, change.new,
bounded_num.value)
И, наконец, мы подключим обработчик событий нового виджета:
def bounded_num_eventhandler(change):
common_filtering(dropdown_year.value, dropdown_purpose.value,
change.new)bounded_num.observe(bounded_num_eventhandler, names='value')
Фрагмент кода:
Вот демо:
Далее мы добавим новый график, чтобы построить базовую одномерную плотность числа посещений (KDE → Оценка плотности ядра). Мы будем использовать seaborn, поэтому давайте импортируем библиотеки:
import seaborn as sns
import matplotlib.pyplot as plt
Продолжая предыдущий вариант использования, мы запишем график в новой выходной переменной:
plot_output = widgets.Output()
Теперь мы изменимcommon_filtering
Функция для построения новой диаграммы:
plot_output.clear_output()
kdeplot
Метод морского происхождения путем передачи количества посещений:with plot_output:
sns.kdeplot(common_filter['visits'], shade=True)
plt.show()
Наконец, единственное, что нам нужно сделать, это отобразить выходные данные в новой ячейке:
display(output)
display(plot_output)
Фрагмент кода:
Вот демо:
До сих пор наш пользовательский интерфейс функционировал, но занимал много недвижимости.
Мы будем первымиорганизоватьвиджеты ввода по горизонтали.HBox
добавим виджеты к нему по одному слева направо:
input_widgets = widgets.HBox(
[dropdown_year, dropdown_purpose, bounded_num])display(input_widgets)
Далее мы создадимконтейнердля выхода.Tab
отлично подходит для этого. На первой вкладке будет размещен фрейм данных, а на второй - график.
tab = widgets.Tab([output, plot_output])
tab.set_title(0, 'Dataset Exploration')
tab.set_title(1, 'KDE Plot')display(tab)
Наконец, мы сложим входные виджеты и вкладку друг над другом с помощьюVBox
,
dashboard = widgets.VBox([input_widgets, tab])display(dashboard)
Это выглядит немного «застрявшим», поэтому в качестве последнего шага мыпольскийнаша панель инструментов, добавив немного места Мы определимLayout
давая 50px поле между пунктами.
item_layout = widgets.Layout(margin='0 0 50px 0')
Мы будем называть этот макет для каждого элемента:
input_widgets = widgets.HBox(
[dropdown_year, dropdown_purpose, bounded_num],
layout=item_layout)tab = widgets.Tab([output, plot_output],
layout=item_layout)
и та да ... Наша готовая панель приборов:
PSВ целях демонстрации в некоторых из этих демонстраций я использовал подмножество набора данных, т.е.
df_london = df_london.sample(250)
,
Вы также можете использовать несколько сторонних виджетов, среди которых наиболее популярными являются:
• 2D графика:bqplot
• 3-D визуализация:pythreejsа такжеipyvolume
• Картирование:ipyleafletа такжеGMaps,
Вы также можете создавать свои собственные виджеты! Для получения дополнительной информации посмотритеВот,
Мы увидели довольно широкий спектр виджетов в действии, но мы все еще здесь только немного поцарапали - мы можем создавать действительно сложные и обширные графические интерфейсы с использованием ipywidgets. Я надеюсь, что вы все согласны с тем, что они заслуживают места в любом наборе инструментов Data Scientist, поскольку они повышают нашу производительность и повышают ценность при исследовании данных.
Спасибо за чтение!
Я регулярно пишу о технологии и данных на среднем - если вы хотите прочитать мои будущие сообщения, пожалуйста,'Подписывайтесь на меня!
© www.machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map