Всем привет!
Сегодня предлагаю погрузиться в один из удобнейших веб-фреймворков в связке c Python под названием Dash. Появился он не так давно, пару лет назад благодаря разработчикам фреймворка plotly. Сам Dash является связкой Flask, React.Js, HTML и CSS.
Выступление Криса Пармера на PLOTCON 2016
Давайте сразу установим фреймворк. Обновленные версии уточняйте тут.
pip install dash==0.31.1 # The core dash backend
pip install dash-html-components==0.13.2 # HTML components
pip install dash-core-components==0.38.1 # Supercharged components
pip install dash-table==3.1.7 # Interactive DataTable component (new!)
Друзья, если вы действительно хотите разобраться в данном фреймворке, читайте публикации до конца, так как зачастую сначала следуют примеры, а уже после детальный обзор кода. Если вам все равно непонятно — советую читать документацию по Dash на английском языке в оригинале. Также в рунете есть несколько статей, которые объясняют концепции, которые я решил пропустить в данном туториале.
Начнем.
Приложения Dash состоят из двух частей. Первая часть — «layout» описывает то, как выглядит наше приложение. Вторая часть описывает интерактивность приложения, о ней мы поговорим в следующей статье.
Dash предоставляет Python классы для всех визуальных компонентов приложения. Разработчики предоставляют набор компонентов в так называемых
dash_core_components
и dash_html_components
. Но также вы можете построить свой компонент используя JavaScript и React.js.Важно
В
dash_core_components
содержатся различные динамические формы такие как, например, выпадающие списки, графики и чек-боксы.В
dash_html_components
содержатся html конструкции, которыми можно завернуть наши формы. Например Div блоки или теги заголовков H1, H2, и так далее. Разработчики предоставляют нам некую абстракцию от html с помощью словарей Python.Чтобы начать разбираться, создадим файл
app.py
, в котором будет содержаться следующее:# -*- coding: utf-8 -*-
# Загрузим необходимые пакеты
import dash
import dash_core_components as dcc
import dash_html_components as html
# Объяснение данных строк пока опускается, будет объяснено далее
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Graph(
id='example-graph',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montreal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
)
])
if __name__ == '__main__':
app.run_server(debug=True)
И запустим его из текущей директории командой:
$ python app.py
...Running on http://127.0.0.1:8050/ (Press CTRL+C to quit)
Видим, что сервер запустился и готов принимать запросы на порт 8050 (у вас может быть другой порт).
Переходим по адресу
http://127.0.0.1:8050/
и видим:
Примечание
- Компонент
layout
состоит из дерева «компонентов», которые содержаться вdash_html_components
. Например блоков Div.
dash_html_components
имеет компонент для каждого html тэга.html.H1(children='Hello Dash')
компонент генерирует HTML элемент
в вашем приложении.<h1>Hello Dash</h1>
- Не все компоненты фреймворка являются HTML компонентами.
dash_core_components
генерирует более высокоуровневые элементы и интерактивные элементы, используя связку JS, HTML, CSS и React.Js.
- Каждый компонент описывается полностью через атрибуты ключевых слов. Dash является декларативным: в первую очередь вы будете описывать свое приложение через эти атрибуты.
- Атрибут
children
немного особенный. По соглашению, он всегда идет первым, что означает, что вы можете заменитьhtml.H1(children='Hello Dash')
наhtml.H1('Hello Dash')
.
На заметку
Dash содержит привычную веб разработчика фичу:
hot-reloading
. Она активируется в тот момент, когда запускается функция app.run_server(debug=True)
. Эта фича обновляет ваш браузер всякий раз, когда вы делаете правки в коде и сохраняете результат. Таким образом нет нужды каждый раз перезапускать сервер.Как мы помним, Dash содержит компонент для каждого тега HTML. Но также он может принимать все аргументы ключевых слов, как и элементы HTML.
Давайте немного изменим наш код:
# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
colors = {
'background': '#111111',
'text': '#7FDBFF'
}
app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
html.H1(
children='Hello Dash',
style={
'textAlign': 'center',
'color': colors['text']
}
),
html.Div(children='Dash: A web application framework for Python.', style={
'textAlign': 'center',
'color': colors['text']
}),
dcc.Graph(
id='example-graph-2',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montreal'},
],
'layout': {
'plot_bgcolor': colors['background'],
'paper_bgcolor': colors['background'],
'font': {
'color': colors['text']
}
}
}
)
])
if __name__ == '__main__':
app.run_server(debug=True)
Обновляем страницу, и видим:
В этом примере мы изменили стили
html.Div
и html.H1
с помощью свойства style
.html.H1('Hello Dash', style={'textAlign': 'center', 'color': '#7FDBFF'})
отрендерится в приложении Dash как: <h1 style="text-align: center; color: #7FDBFF">Hello Dash</h1>
Но есть несколько важных замечаний:
- Свойства
style
в HTML это разделенная точкой с запятой строка. В Dash вы можете просто передать словарь.
- Ключи в
style
словаре немного различаются в написании относительно HTML. Вместоtext-align
мы пишемtextAlign
.
- Дочерние классы каждого элемента-тэга в Dash (класса) передаются в массиве через аргумент
children
.
Многоразовые компоненты
Продолжая, представим, что нам нужны некоторые элементы, которые будут меняться, например в зависимости от входных данных пользователя нашего приложения. Для этого в Dash предусмотрены так называемые
reusable components
. Рассмотрим их на примере таблицы, данные для которой будут загружаться из Pandas dataframe.import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
df = pd.read_csv(
'https://gist.githubusercontent.com/chriddyp/'
'c78bf172206ce24f77d6363a2d754b59/raw/'
'c353e8ef842413cae56ae3920b8fd78468aa4cb2/'
'usa-agricultural-exports-2011.csv')
def generate_table(dataframe, max_rows=10):
return html.Table(
# Header
[html.Tr([html.Th(col) for col in dataframe.columns])] +
# Body
[html.Tr([
html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
]) for i in range(min(len(dataframe), max_rows))]
)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(children=[
html.H4(children='US Agriculture Exports (2011)'),
generate_table(df)
])
if __name__ == '__main__':
app.run_server(debug=True)
Немного о таблицах
Давайте вспомним что из себя представляет таблица в HTML.
HTML таблица определяется тэгом table.
Каждая строка таблица определяется тэгом tr. Хедер таблица определяется тэгом th. A ячейки таблицы заполняются с помощью тэга td.
Получается такая структура:
<table style="width:100%">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>80</td>
</tr>
</table>
И выглядит она так:
Firstname | Lastname | Age |
---|---|---|
Jill | Smith | 50 |
Eve | Jackson | 94 |
John | Doe | 80 |
Компоненты ядра (Основные компоненты)
Как мы уже сказали ранее,
dash_core_components
включает в себя высокоуровнвые элементы. Такие, как: выпадающее меню, графики и прочее.Вы можете ознакомиться с визуальной стороной этих элементов, каждый из которых сопровождается кодом (очень удобно, вселенский респект разрабочикам из Plot.ly) здесь.
Для того, чтобы разработчик, а именно Вы, могли в коде различать все элементы, принято для каждого компонента ядра писать лейбл. Это что-то типа названия нашего элемента. Это не обязательно, но просто облегчит отладку. А пользователю даст возможность быстрее разобраться в вашем интерфейсе. Далее вы поймете о чем я.
Давайте рассмотрим следующий код:
import dash
import dash_core_components as dcc
import dash_html_components as html
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
html.Label('Dropdown'),
dcc.Dropdown(
options=[
{'label': 'New York City', 'value': 'NYC'},
{'label': u'Montreal', 'value': 'MTL'},
{'label': 'San Francisco', 'value': 'SF'}
],
value='MTL'
),
html.Label('Multi-Select Dropdown'),
dcc.Dropdown(
options=[
{'label': 'New York City', 'value': 'NYC'},
{'label': u'Montreal', 'value': 'MTL'},
{'label': 'San Francisco', 'value': 'SF'}
],
value=['MTL', 'SF'],
multi=True
),
html.Label('Radio Items'),
dcc.RadioItems(
options=[
{'label': 'New York City', 'value': 'NYC'},
{'label': u'Montreal', 'value': 'MTL'},
{'label': 'San Francisco', 'value': 'SF'}
],
value='MTL'
),
html.Label('Checkboxes'),
dcc.Checklist(
options=[
{'label': 'New York City', 'value': 'NYC'},
{'label': u'Montreal', 'value': 'MTL'},
{'label': 'San Francisco', 'value': 'SF'}
],
values=['MTL', 'SF']
),
html.Label('Text Input'),
dcc.Input(value='MTL', type='text'),
html.Label('Slider'),
dcc.Slider(
min=0,
max=9,
marks={i: 'Label {}'.format(i) if i == 1 else str(i) for i in range(1, 6)},
value=5,
),
], style={'columnCount': 2})
if __name__ == '__main__':
app.run_server(debug=True)
Тут мы видим, что мы создали как обычно один общий Div блок, в котором содержатся наши различные компоненты ядра. Выглядит это как-то так:
Остались вопросы?
Разработчики подготовили очень подробную документацию, прочитать вы ее можете типичной Python командой для каждого класса:
>>> help(dcc.Dropdown)
Summary
Ранее нами изученый
layout
описывает то, как выглядит наше приложение. По сути он содержит древовидную иерархию HTML тэгов и высокоуровневых элементов ядра Dash, которые содержатся в dash_core_components
.В следующей части мы изучим то, как сделать нашу страничку интерактивной. Если вам понравился данный туториал, ставьте плюсик и подписывайтесь на меня.
* Здесь скоро будет ссылка на следующую часть *
Комментарии (6)
S_A
02.12.2018 11:17+2Я вот из реальной практики использования скажу, что отказался от Dash — да и от plotly тоже.
Причина первая и достаточная — он «деревянный». Шаг влево-вправо от того что он позволяет… оборачивается желанием все переписать самому. А позволяет он не всё.
Причина вторая и скрытая — нужно писать длинные лапшевидные конструкции, которые трудно отлаживать и искать «ну почему не сработало, вроде ж правильно написал» (а оказалось, нужно было еще в список завернуть параметр, вместо просто значения по ключу).
Причина третья и побочная — он не очень быстр. Реактовые компоненты и plotly сам — слегка тугодумные.
Вывод из моей практики — серьезный дашборд не сделать. Тем более дизайнерский. А вот какие-то сервисы для внутреннего пользования, скажем внутри подразделения — можно (и это достаточно быстро).da0c
02.12.2018 16:46+1Соглашусь с вашими доводами, dash весьма деревянный. Плюс в том, что он динамично развивается, и к тому же, достаточно просто расширить до того, что нужно, дописав на react. Дорабатывали под себя dash_table_experiments.
Сейчас находимся в стадии размышления — дописывать Dash, с потенциальным контрибьютом в сообщество, или же все-таки уйти на что-то другое. На что в итоге перешли вы, отказавшись от dash?S_A
02.12.2018 17:02nuxt/vuetify + разные js charts libraries. Это все на так быстро как с dash, но как-то попродуктовее, что ли. Да, знаю что можно дописывать компоненты… Это тут уж кому как покатит подход.
В целом dash хорош для быстрых решений, я бы сказал для небольшого круга пользователей, для разовых целей он вообще замечателен, но к сожалению, пока не для чего-то крупного.da0c
02.12.2018 19:58Спасибо за ответ, но это все-таки совсем «low level» js. Для рабочей визуализации данных хочется чего-то более питонячьего, и, полностью с вами согласен, Dash в этом хорош.
Dash подкупает тем, что органично встроен в питонячий data science стэк на pandas, и тем, что у него действительно хорошее покрытие понятными, логичными примерами.
Но что-то вне основного logical flow — получается коряво. Чего стоят input-output callback-и. Хотелось бы списать все на молодость Dash, сейчас развитие идет по направлению к большей гибкости, так что можно надеяться…
buzzroll
Выглядит очень мимими.