Dash является довольно интересным Веб-фреймворком для визуализации данных и имеет в семе довольно много полезных функций в сочетании с простотой их применения.
Сам Dash это некий коллаб HTML, React.Js, Flask и CSS и предоставляет python классы для всех своих визуальных компонентов.
В качестве демонстративного датасета я возьму датасет diamonds с сайта kaggle (https://www.kaggle.com/shivam2503/diamonds)
Если описывать полностью все функции, которые предоставляет dash, уйдет довольно много времени, исходя из этого, предлагаю в качестве простого примера визуализировать более камерную задачу. Допустим, вывести гистограмму количества драгоценных камней в зависимости от нескольких факторов: качество огранки, уровень чистоты и цвет. При этом выбор нужной гистограммы происходит непосредственно в веб интерфейсе.
Привожу пример сэмпла датасета
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pd
from dash.dependencies import Input, Output
Для начала отметим что работу в dash можно разделить на две части внешнюю(layout) то есть создание визуальной составляющей дашборда, и внутреннюю(callbacks) в которых мы описываем логику взаимодействия с объектами на дашборде.
В начале инициализируем класс dash, а также подгружаем dataset
app = dash.Dash(__name__)
df = pd.read_csv('C://Users//User//Desktop//Учеба//diam.csv',sep = ';')
Далее создадим заголовок.
Для более красивого отображения, я использовал эти стили (https://github.com/matyushkin/lessons/blob/master/visualization/dash/avocado_analytics/apps/app_2/assets/style.css)
Определяем компонент html.Div а также вносим в него заголовок(html.h1) и абзац(html.P)
app.layout = html.Div(children=[
html.Div(
children=[
html.H1(children='Analysis diamonds dataset', className= 'header-title'),
html.P(children='maybe this demo will be useful to someone (:', className= 'header-description')и
], className= 'header')])
ClassName является ссылкой на css файл
Который можно подгрузить внутрь проекта создав для него папку assets.
Вид структуры должен быть подобный
И запустим наше приложение
if __name__ == '__main__':
app.run_server(debug=True)
Заголовок готов, теперь можно приступить к созданию объекта dropdown
Это будет меню для выбора необходимой гистограммы.
Данный элемент в коде будет выглядеть вот так
html.Div([
dcc.Dropdown(
id='demo_drop',
options=[
{'label': 'Огранка', 'value': 'cut'},
{'label': 'Ясность(чистота)', 'value': 'clarity'},
{'label': 'Цвет', 'value': 'color'}
],
value='cut', className="dropdown"
)])
В данном случае id означает уникальный идентификатор объекта он нам понадобится, когда будем описывать логику. Как можно понять из кода функционал данного объекта можно описать через словарь. Атрибут value означает дефолтное выбранное значение.
Далее добавляем графический объект
dcc.Graph(id='output_graph')],className="card")
Пока что он пуст, но это только пока.
Далее нам нужно сделать так что бы наше приложение реагировало на запрос пользователя и для этого нужно использовать функции обратного вызова. В них и будет описана связь между объектами Dropdown и graph.
@app.callback(
Output(component_id='output_graph', component_property='figure'),
[ Input(component_id='demo_drop', component_property='value')]
)
В данном случае в input у нас заносится уникальный идентификатор dropdown объекта и его значения, которые мы описывали в словаре атрибута options.
А output возвращает гистограмму и ее значения.
Остается только прописать логику для обработки информации.
Что бы понять сколько было драгоценных камней для каждого уровня огранки, нужно сгруппировать данные по уровням огранки и вычислить количество строк внутри группы.
Для pandas это будет выглядеть вот так
h = df.groupby(['cut'], as_index=False, sort=False)['carat'].count()
Теперь вплетаем данное вычисление в функцию
def update_output(value):
if value == 'cut':
h = df.groupby(['cut'], as_index=False, sort=False)['carat'].count()
elif value == 'clarity':
h = df.groupby(['clarity'], as_index=False, sort=False)['carat'].count()
elif value == 'color':
h = df.groupby(['color'], as_index=False, sort=False)['carat'].count()
fig = px.bar(h, x=value, y="carat", labels = {"carat" : "Count"} )
return fig
То есть если описывать, то каждая вариация выбора пользователя связана со значением, которое используется в алгоритме.
А переменная fig создает гистограмму
Таким образом у нас выходит такого вида интерфейс с выбором параметра группировки
Итоговый код
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pd
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
df = pd.read_csv('C://Users//User//Desktop//Учеба//diam.csv',sep = ';')
app.layout = html.Div(children=[
html.Div(
children=[
html.H1(children='Analysis diamonds dataset', className= 'header-title'),
html.P(children='maybe this demo will be useful to someone (:', className= 'header-description')
], className= 'header'),
#html.Label('Количество'),
html.Div([
dcc.Dropdown(
id='demo_drop',
options=[
{'label': 'Огранка', 'value': 'cut'},
{'label': 'Ясность(чистота)', 'value': 'clarity'},
{'label': 'Цвет', 'value': 'color'}
],
value='cut', className="dropdown"
), dcc.Graph(id='output_graph')],className="card")
])
@app.callback(
Output(component_id='output_graph', component_property='figure'),
[Input(component_id='demo_drop', component_property='value')]
)
def update_output(value):
if value == 'cut':
h = df.groupby(['cut'], as_index=False, sort=False)['carat'].count()
elif value == 'clarity':
h = df.groupby(['clarity'], as_index=False, sort=False)['carat'].count()
elif value == 'color':
h = df.groupby(['color'], as_index=False, sort=False)['carat'].count()
fig = px.bar(h, x=value, y="carat", labels = {"carat" : "Count"} )
return fig
if __name__ == '__main__':
app.run_server(debug=True)
Данный фреймворк включает в себя невероятное множество функций и вариантов применения, надеюсь своей статьей я смог заинтересовать и обратить на него внимание и надеюсь, что статья будет полезна читателю в его работе.
Комментарии (10)
mattroskin
15.11.2021 14:26Полезно было бы почитать про ограничения бесплатной версии. Столкнулись вы с какими-то важными возможностями, которые только в платной работают?
NewTechAudit Автор
03.12.2021 13:01На сколько я знаю dash Enterprise предоставляет компаниям услуги поддержки, хостинг, развертывания и аутентификации в приложениях dash. Но вроде как эти функции существуют за пределами экосистемы. Так dash вроде как выпущен под лицензией MIT и является бесплатным
YAKOROLEVAZAMKA
Я вот пытался сделать мультистраничный даш и у меня вопрос - как с одной страницы передавать аргументы на другую (у меня на первой и второй страницах выбор фильтров, а на третьей соответственно должны отображаться соответствующие данные), коллбеки такое (как я понял) не обрабатывают, единственный (найденный мною) вариант - глобальные переменные (передаю уже отфильтрованный DataFrame), но это как-то совсем уж плохо.
Код примерно такой:
ПС интересуюсь для самообразования, вижу Dash очень мощным инструментом (и в то же время простым и удобным).
george3
Если dash кажется простым и удобным, то возможно большое удивление на фоне https://github.com/Claus1/unigui
YAKOROLEVAZAMKA
Выглядит действительно интересно, спасибо! Покопаюсь на досуге (хотя я уже перенес все свои "недодаши" в метабазу :))
twistfire92
Попробуйте использовать dcc.Store. Позволяет сохранять данные на стороне клиента. Хотя не факт, что получится это провернуть с многостраничным приложением.
YAKOROLEVAZAMKA
пробовал - даже нашел как сохранять некие данные в кэше браузера (как раз с помощью dcc.Store если не ошибаюсь), но с мультистраницой это не сработало :(
twistfire92
А какой storage_type выбирали? Все пробовали? И memory и local и session? И куда вы его добавляли? В sidebar или в content