В этой серии статей в качестве основной платформы для Dashboarding используется Dash от Plotly.

Введение

Dash от Plotly — это веб-фреймворк, построенный на основе Plotly.js, React и Flask, который можно использовать для создания аналитических веб-приложений. Пользователи Python могут воспользоваться библиотекой пользовательского интерфейса Dash для создания современных элементов интерфейса, таких как диаграммы, таблицы и интерактивные функции ввода и вывода.

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

В этой серии статей мы расскажем о том, как определенные веб-элементы, доступные в приложении Dash, можно использовать для создания интерактивной, детализированной и информативной панели с динамическими параметрами и табличными разделами. Эти инструменты необходимы для создания иерархичной организационной структуры, представленной в виде веб-приложения. Кроме того, мы выявим возможные ошибки и представим разбор их обходных путей, которые могут быть полезны при написании эффективного и надежного кода для управления панелью индикаторов. В конце серии мы сравним Dash с некоторыми из наиболее известных инструментов для работы с панелью индикаторов, такими как R Shiny и Bokeh.

Примечание: Во всех примерах кода, представленных далее, элементы html.Div находятся в html-элементах макета приложения (см. «Компоненты Dash»), в то время как другие функции (например, функции обратного вызова) определены отдельно.

Компоненты Dash

Каждое веб-приложение на основе Dash создается с использованием кода, который можно разделить на три основных раздела: макет, функции интерактивности и основная функция. Ниже мы рассмотрим каждый из них.

Макет

Макет в Dash содержит компоненты HTML и CSS, поддерживаемые библиотеками Dash. Этот раздел определяет, как будет выглядеть приложение на конечной веб-странице. Библиотеки dash_core_components и dash_html_components предоставляют веб-элементы, которые можно использовать в макете. Существует также библиотека graph_objs, которая предоставляет пользователю доступ к дополнительным графическим компонентам. Более того, как было сказано ранее, всегда можно создавать свои собственные компоненты, используя React.js и Javascript. Раздел макета выглядит как:

app = dash.Dash(__name__, 
       external_stylesheets=<external_stylesheets>)

app.layout = html.Div(children=[ < Здесь будут находиться ваши веб-компоненты > ])

Ниже приведены основные модули, необходимые для реализации всех графических элементов серии:

import dash
import dash_core_components
import dash_html_components
import dash.dependencies

Ниже приведен базовый пример простого линейного графика, указанного в макете кода Dash:

html.Div(children=[   
	html.Div([
    	dash_core_components.Graph(
        	id='example-graph',
        	figure={
            	'data': [ {'x': data['date'],'y': data['Sales'],'type': 'line'} ],
            	'layout': go.Layout(
                        	xaxis={"title":"Time"}, yaxis={"title":"Sales"}) } )
    		], style={"display":"inline-block", "width":"35%"})
	])

Где data — DataFrame таблица библиотеки Pandas, содержащая столбцы date и Sales для осей x и y соответственно.

Интерактивность

Dash поддерживает веб-приложения с интерактивными компонентами, где пользователи могут изменять входные данные таких элементов, как графики и таблицы. Бэкенд-код, поддерживающий такое удобство использования, входит в раздел интерактивности, а базовая структура для этого кода состоит из двух подкомпонентов:

1) функция @app.callback() — где input idэлемента dash вызывает изменение, в результате чего изменяется output idэлемента dash.

2) Тело функции, которая запускается при каждом изменении ввода, должно быть определено.

Давайте воспользуемся приведенным выше примером и изменим диапазон дат по оси X графика продаж, используя компонент ввода Date Range picker:

dash_core_components.DatePickerRange( id='date-picker-range', 
	start_date = datetime(2018, 5, 3), end_date=datetime(2018, 10, 29) )

Выше приведен макет элементов селектора диапазона дат, поддерживаемых библиотекой dash_core_components. Ниже мы проиллюстрируем, как вводные и выходные данные указываются в функции app.callback(), где функция определена для изменения оси x (где отмечены даты) на основе входного диапазона дат.

@app.callback(
    dash.dependencies.Output(component_id='example-graph', component_property='figure'),
    [dash.dependencies.Input(component_id='date-picker-range', component_property='start_date'),
    dash.dependencies.Input(component_id='date-picker-range', component_property='end_date')]
)

def update_output_div(start_date,end_date):
    return {
    'data': [go.Scatter(
                x=data[(data['date']>=start_date)&(data['date' <=end_date)]['date'],
                y=data[(data['date']>=start_date)&(data['date']<=end_date)]['Sales'])]
            ,
    "layout": go.Layout(
               xaxis={"title":"Time"}, yaxis={"title":"Sales"}
             )
        }

Вывод:

Совет: Устранение ошибок в компонентах таблиц и графиков

Один из аспектов разработки элементов Dash, таких как графики и таблицы, который часто упускается из виду, заключается в том, что они способныотображать любую ранее сохраненную информацию, если происходит ошибка. Распространенной проблемой является то, что графики продолжают отображать информацию из последнего обратного вызова, если текущий обратный вызов не может быть выполнен как запланировано (возможно, из-за неверного входного параметра). Поскольку пользователь на странице не знает об этом факте, это процесс приводит к ошибочной интерпретации показанных данных. Простое решение — добавить условия ошибки в функцию обратного вызова следующими способами:

  • Если возникает ошибка на графике, то возвращаем пустой граф {‘data’: [], ‘layout’:[]}.
  • Если произошла ошибка в таблице, возвращаем пустую таблицу {}.

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

Основная функция

Как и любой другой код Python, бэкенд-код, который поддерживает веб-приложение на основе Dash, также содержит основную функцию, которую можно использовать для вызова сервера, запускающего результирующее приложение по локальному URL-адресу и порту. Если порт не указан, по умолчанию возвращается 127.0.0.1:8050. Ниже приведен пример:

# Порт 8051 выбран произвольно
if __name__ == '__main__':
	app.run_server(debug=True,port=8051)

Идеи для работы с панелью индикаторов с использованием Dash для продвинутых пользователей

1) Графики и интерактивные таблицы

1.1) Цветовое форматирование области диаграммы

Для графического представления метрики во времени могут потребоваться указания на самом графике, представляющие некоторое разделение метрики в определенных временных диапазонах (или любую другую метрику на оси x). Это визуальное обозначение возможно благодаря тому, что указанный график может отображать различные цвета в области диаграммы для определенных временных диапазонов по оси X. Dash учитывает такую ​​функциональность, изменяя часть макета построенного графика. Реализовать это можно с использованием библиотеки graph_objs от plotly.

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

Переменные оси X, оси Y и обозначения диапазонов сезонов на оси X определены следующим образом:

colors = ['rgba(240,230,140,0.6)', 'rgba(175,238,238,0.6)', 'rgba(255,192,203,0.6)']
Xaxis  = ['2018-05-03','2018-06-02','2018-08-31','2018-10-30','2018-11-29','2018-12-29']
Yaxis = [3485, 1114,   1893,   4027,   1520,   2653]
Seasons = [('2018-05-03','2018-08-25'),('2018-08-25','2018-11-29'),('2018-11-29','2018-12-29')]

Для отображения изменяющегося цвета области диаграммы модифицируем раздел макета графика:

html.Div(children=[
	html.H1(children=' Chart area with varying colors'),
	dash_core_components.Graph(
    	id='example-graph',
    	figure={
        	'data': [ {'x': Xaxis,'y': Yaxis,'type': 'line','name': 'Sales'} ],
            'layout': go.Layout(
            	xaxis={"title":"Time"}, yaxis={"title":"Sales Qty"}, width=300,
            	shapes=[
                    {
                    	'type':'rect', 'xref':'x','yref':'paper',
                    	'x0':exp[0],'y0':0,'x1':exp[1],'y1':1,
                    	'fillcolor': colors[index%3],
                    	'opacity': 0.30,
                    	'line': {
                        	'width': 0,
                    	}  
	           } for index,exp in enumerate(Seasons)]
           ) 
        } 
    )
])

Выходные данные этого кода отображают в области диаграммы три временных диапазона, выделенных разным цветом:

1.2) Выборочно видимые веб-элементы

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

Это настраивается с помощью использования следующих двух элементов:

  1. html.Details([ … ]): внутри скобок помещаются функции панели индикаторов, которые пользователь предпочел скрыть и отображение которых регулируется с помощью тумблера. Расположение этого элемента будет ориентировано на app.layout пользователя.
  2. html.Summary([String]): с использованием элемента summary после элемента Details над тумблером в приложении выводится оповещение (сообщение вида String). Это повышает читабельность вашего приложения и помогает определить тумблер (если их несколько в приложении).

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

Для этого используем следующий код:

html.Details([
	html.Summary('Display sales quantity',style={"font-size":"22"}),
	html.Div([
    	dash_core_components.Graph(
        	id='example-graph1',
        	figure={
            	'data': [ {'x': Xaxis,'y': Yaxis,'type': 'line'} ],
            	'layout': go.Layout(
                	xaxis={"title":"Time"}, yaxis={"title":"Sales Qty"}) } )
        	], style={"display":"inline-block", "width":"33%", "font-size":"12"})
   ]),

html.Div([
	dash_core_components.Graph(
    	id='example-graph2',
    	figure={
        	'data': [ {'x': Xaxis,'y': Yaxis_revenue,'type': 'bar'} ],
        	'layout': go.Layout(
            	xaxis={"title":"Time"}, yaxis={"title":"Sales Revenue"}, width=150) } )
	], style={"display":"inline-block", "width":"33%", "font-size":"12"})

Вывод:

Этот инструмент еще раз подчеркивает функциональность и удобство использования Dash, предоставляя встроенные пакеты для улучшения организационных факторов приложения. Недостатком является то, что скрытые контейнеры не создаются с помощью клика, более того, они постоянно модифицируются при изменении их входных параметров. Аспект Details только позволяет определить, должны ли отображаться данные.

1.3) Загрузка содержимого таблицы в файл

Dash включает функцию загрузки любых данных из бэкенд-кода в виде плоского файла. Благодаря этому пользователь имеет возможность не только просматривать графики и таблицы, но и загружать лежащие в их основе данные для дальнейшего анализа. Функциональность загрузки не зависит от данных, отображаемых в веб-приложении, и даёт возможность загружать любые данные, которые могут быть доступны в бэкенд-коде Python.

Эта функциональность может быть реализована путем развертывания на панели индикаторов триггера, который вызывает загрузку файла, и назначения этого элемента триггера с определенной гиперссылкой. Для демонстрации мы будем использовать элемент button. Кроме того, бэкенд-код будет иметь функцию, которая вызывается, когда сервер направляется на эту гиперссылку (при нажатии кнопки), и выполнять функцию send_file с данными для загрузки, передаваемыми в качестве параметра. Это предопределенная функция в библиотеке Flask, которая отправляет содержимое файла клиенту, который в нашем случае является лицом, использующим веб-приложение Dash.

Пример кода:

# Элемент макета приложения
html.Div([
    	html.A(html.Button('Download Graph Data'), href='/dash/data_download')
	],style={"textAlign":"left","font-size":"20"})


# Функция обратного вызова
# Примечание: 'directory' указывает местоположение файла raw_graph_data.csv
@app.server.route("/dash/data_download")
def download_data():
	return flask.send_file(directory + 'raw_graph_data.csv',
                    	mimetype='text/csv',
                    	attachment_filename='raw_graph_data.csv',
                    	as_attachment=True

Вывод:

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

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Drimik Roy: A Python Programmers’ Guide to Dashboarding — Part 1

Предыдущая статьяКогда параллелизм превосходит конкурентность
Следующая статьяПусть говорят… расходящиеся гистограммы!