Python dash руководство

Время на прочтение
7 мин

Количество просмотров 83K

image

Всем привет!

Сегодня предлагаю погрузиться в один из удобнейших веб-фреймворков в связке 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'Montréal'},
            ],
            '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/
и видим:

Примечание

  1. Компонент layout состоит из дерева «компонентов», которые содержаться в dash_html_components. Например блоков Div.
  2. dash_html_components имеет компонент для каждого html тэга. html.H1(children='Hello Dash') компонент генерирует HTML элемент
    <h1>Hello Dash</h1>

    в вашем приложении.

  3. Не все компоненты фреймворка являются HTML компонентами. dash_core_components генерирует более высокоуровневые элементы и интерактивные элементы, используя связку JS, HTML, CSS и React.Js.
  4. Каждый компонент описывается полностью через атрибуты ключевых слов. Dash является декларативным: в первую очередь вы будете описывать свое приложение через эти атрибуты.
  5. Атрибут 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'Montréal'},
            ],
            '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>

Но есть несколько важных замечаний:

  1. Свойства style в HTML это разделенная точкой с запятой строка. В Dash вы можете просто передать словарь.
  2. Ключи в style словаре немного различаются в написании относительно HTML. Вместо text-align мы пишем textAlign.
  3. Дочерние классы каждого элемента-тэга в 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>

И выглядит она так:

Компоненты ядра (Основные компоненты)

Как мы уже сказали ранее, 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'Montréal', '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'Montréal', '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'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'}
        ],
        value='MTL'
    ),

    html.Label('Checkboxes'),
    dcc.Checklist(
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': u'Montréal', '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 блок, в котором содержатся наши различные компоненты ядра. Выглядит это как-то так:

image

Остались вопросы?

Разработчики подготовили очень подробную документацию, прочитать вы ее можете типичной Python командой для каждого класса:

>>> help(dcc.Dropdown)

Summary

Ранее нами изученый layout описывает то, как выглядит наше приложение. По сути он содержит древовидную иерархию HTML тэгов и высокоуровневых элементов ядра Dash, которые содержатся в dash_core_components.

В следующей части мы изучим то, как сделать нашу страничку интерактивной. Если вам понравился данный туториал, ставьте плюсик и подписывайтесь на меня.

* Здесь скоро будет ссылка на следующую часть *

Данная публикация является незначительно сокращенным переводом статьи Дилана Кастильо Develop Data Visualization Interfaces in Python With Dash.

***

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

В этом руководстве мы рассмотрим:

  • Как создать приложение Dash
  • Основные компоненты библиотеки
  • Как настроить стиль приложения
  • Как сделать приложение интерактивным
  • Как развернуть приложение на удаленном сервере (на примере Heroku)

Dash ― это платформа с открытым исходным кодом для создания интерфейсов визуализации данных. После выпуска в 2017 году в виде библиотеки Python Dash вскоре был расширен для R и Julia.

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

В основе Dash лежат три технологии:

  1. Flask предоставляет функциональность веб-сервера.
  2. React отображает веб-интерфейс.
  3. Plotly.js генерирует диаграммы.

Не нужно беспокоиться о совместной работе этих технологий. Необходимо лишь написать код на Python, R или Julia и добавить немного CSS.

Если вы привыкли анализировать данные с помощью Python, Dash станет полезным дополнением вашего набора инструментов. Вот несколько практических примеров возможностей библиотеки:

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

Другие интересные варианты использования вы найдете в галерее приложений Dash.

Начинаем работу с Dash на Python

В качестве примера в этом руководстве мы шаг за шагом создадим информационную панель для набора данных Kaggle о продажах и ценах на авокадо в США за период с 2015 по 2018 год.

Настройка виртуального окружения

Для разработки приложения понадобится каталог для хранения кода и данных, а также чистая виртуальная среда Python 3. Чтобы их создать, следуйте следующим инструкциям для вашей операционной системы.

Windows. Откройте командную строку и выполните следующие команды:

        mkdir avocado_analytics && cd avocado_analytics
python -m venv venv
venvScriptsactivate.bat
    

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

macOS или Linux. Смысл следующих команд терминала идентичен командам для Windows:

        mkdir avocado_analytics && cd avocado_analytics
python3 -m venv venv
source venv/bin/activate
    

Далее необходимо установить в виртуальное окружение следующие библиотеки:

        python -m pip install dash==1.13.3 pandas==1.0.5
    

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

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

Сохраните файл с данными avocado.csv в корневом каталоге проекта. К настоящему моменту у вас должна быть виртуальная среда с необходимыми библиотеками и данными в корневой папке проекта. Структура проекта выглядит так:

        avocado_analytics/
├── venv/
└── avocado.csv
    

Как с помощью Dash создать приложение

Разобьем процесс создания приложения Dash на два этапа:

  1. Инициализируем приложение и определим внешний вид с помощью макета приложения (layout).
  2. Определим посредством обратных вызовов (callbacks), какие части приложения являются интерактивными и на что они реагируют.

Инициализируем Dash-приложение

Создадим пустой файл app.py. Далее мы будем шаг за шагом его заполнять и пояснять происходящее, а в конце раздела вы найдете его содержимое целиком.

Вот несколько первых строк app.py:

        import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

data = pd.read_csv("avocado.csv")
data = data.query("type == 'conventional' and region == 'Albany'")
data["Date"] = pd.to_datetime(data["Date"], format="%Y-%m-%d")
data.sort_values("Date", inplace=True)

app = dash.Dash(__name__)
    

Вначале мы импортируем необходимые библиотеки:

  • dash поможет инициализировать приложение
  • dash_core_components позволяет создавать интерактивные компоненты: графики, раскрывающиеся списки, диапазоны дат и т. д.
  • dash_html_components позволяет получить доступ к тегам HTML
  • pandas помогает читать и выводить данные в организованной форме

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

Если вы уже использовали Flask, то инициализация класса Dash вам уже знакома. Во Flask мы обычно инициализируем WSGI-приложение с помощью Flask(__name__). Для приложений Dash мы используем Dash(__name__).

Определение макета приложения Dash

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

        app.layout = html.Div(
    children=[
        html.H1(children="Avocado Analytics",),
        html.P(
            children="Analyze the behavior of avocado prices"
            " and the number of avocados sold in the US"
            " between 2015 and 2018",
        ),
        dcc.Graph(
            figure={
                "data": [
                    {
                        "x": data["Date"],
                        "y": data["AveragePrice"],
                        "type": "lines",
                    },
                ],
                "layout": {"title": "Average Price of Avocados"},
            },
        ),
        dcc.Graph(
            figure={
                "data": [
                    {
                        "x": data["Date"],
                        "y": data["Total Volume"],
                        "type": "lines",
                    },
                ],
                "layout": {"title": "Avocados Sold"},
            },
        ),
    ]
)
    

Этот код определяет свойство layout объекта app. Внешний вид приложения описывается с помощью древовидной структуры, состоящей из Dash-компонентов.

Мы начинаем с определения родительского компонента html.Div, затем в качестве дочерних элементов добавляем заголовок html.H1 и абзац html.P. Эти компоненты эквивалентны HTML-тегам div, h1 и p. Для изменения атрибутов или содержимого тегов используются аргументы компонентов. Например, чтобы указать, что находится внутри тега div, мы используем в html.Div аргумент children.

В компонентах есть и другие аргументы, такие как style, className или id, которые относятся к атрибутам HTML-тегов. В следующем разделе мы увидим, как использовать эти свойства для стилизации панели инструментов.

Таким образом, Python-код будет преобразован в следующий HTML-код:

        <div>
  <h1>Avocado Analytics</h1>
  <p>
    Analyze the behavior of avocado prices and the number
    of avocados sold in the US between 2015 and 2018
  </p>
  <!-- Остальная часть приложения -->
</div>
    

Далее описаны два компонента dcc.Graph. Первая диаграмма отображает средние цены на авокадо за период исследования, а вторая ― количество авокадо, проданных в США за тот же период.

Под капотом Dash использует для создания графиков Plotly.js. Компоненты dcc.Graph ожидают figure object или словарь Python, содержащий данные графика и layout, что мы и передаем в нашем случае.

Остались две строки кода, которые помогут запустить приложение:

        if __name__ == "__main__":
    app.run_server(debug=True,
                   host = '127.0.0.1')
    

Эти строки позволяют запускать приложение Dash локально, используя встроенный сервер Flask. Параметр debug = True из app.run_server разрешает горячую перезагрузку: когда мы вносим изменения в приложение, оно автоматически перезагружается без перезапуска сервера.

Наконец, полная версия app.py. Вы можете скопировать код в пустой app.py и проверить результат.

app.py
        import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

data = pd.read_csv("avocado.csv")
data = data.query("type == 'conventional' and region == 'Albany'")
data["Date"] = pd.to_datetime(data["Date"], format="%Y-%m-%d")
data.sort_values("Date", inplace=True)

app = dash.Dash(__name__)

app.layout = html.Div(
    children=[
        html.H1(children="Avocado Analytics",),
        html.P(
            children="Analyze the behavior of avocado prices"
            " and the number of avocados sold in the US"
            " between 2015 and 2018",
        ),
        dcc.Graph(
            figure={
                "data": [
                    {
                        "x": data["Date"],
                        "y": data["AveragePrice"],
                        "type": "lines",
                    },
                ],
                "layout": {"title": "Average Price of Avocados"},
            },
        ),
        dcc.Graph(
            figure={
                "data": [
                    {
                        "x": data["Date"],
                        "y": data["Total Volume"],
                        "type": "lines",
                    },
                ],
                "layout": {"title": "Avocados Sold"},
            },
        ),
    ]
)

if __name__ == "__main__":
    app.run_server(debug=True,
                   host = '127.0.0.1')
    

Пришло время запустить приложение. Откройте терминал в корневом каталоге проекта и в виртуальной среде проекта. Запустите python app.py, затем перейдите по адресу http://localhost:8050.

Панель управления должна выглядеть примерно так:

📊 Туториал: визуализация данных в вебе с помощью Python и Dash

Теперь у нас есть рабочая версия, но мы ее еще улучшим.

Управление оформлением панели

Dash очень гибок в настройке внешнего вида приложения. Мы можем использовать собственные файлы CSS или JavaScript, встраивать изображения и настраивать дополнительные параметры.

Как применить стиль к компонентам Dash

Стилизовать компоненты можно двумя способами:

  • Использовать аргумент style отдельных компонентов.
  • Предоставить внешний CSS-файл.

Аргумент style принимает словарь Python с парами ключ-значение, состоящими из имен свойств CSS и значений, которые мы хотим установить.

Примечание

При указании свойств CSS в аргументе style необходимо использовать синтаксис вида mixedCase вместо слов, разделенных дефисом. Например, чтобы изменить цвет фона элемента, необходимо указывать backgroundColor, а не background-color.

Захотев изменить размер и цвет элемента H1 в app.py, мы можем установить аргумент style следующим образом:

        html.H1(
    children="Avocado Analytics",
    style={"fontSize": "48px", "color": "red"},
),
    

В этом случае заголовок будет оформлен красным шрифтом размером в 48 пикселей.

Обратная сторона простоты использования style ― такой код будет всё труднее поддерживать по мере роста кодовой базы. Если на панели управления присутствует несколько одинаковых компонентов, большая часть кода будет повторяться. Вместо этого можно использовать CSS-файл.

Если вы хотите включить собственные локальные CSS- или JavaScript-файлы, необходимо создать в корневом каталоге проекта папку с именем assets/ и сохранить в ней необходимые файлы.

Затем вы можете использовать аргументы className или id компонентов, чтобы настроить с помощью CSS их стили. При преобразовании в HTML-теги эти аргументы соответствуют атрибутам class и id.

Захотев настроить размер шрифта и цвет текста элемента H1 в app.py, мы можем использовать аргумент className:

        html.H1(
    children="Avocado Analytics",
    className="header-title",
),

    

Установка аргумента className определяет атрибут класса для элемента H1. Затем в CSS-файле style.css в папке assets/ мы указываем, как хотим, чтобы он выглядел:

        .header-title {
  font-size: 48px;
  color: red;
}
    

Как улучшить внешний вид панели инструментов

Давайте узнаем, как настроить внешний вид панели инструментов. Внесем следующие улучшения:

  • Добавим иконку сайта (favicon) и title.
  • Изменим семейство шрифтов.
  • Используем внешний CSS-файл для стилизации компонентов Dash.

Добавление в приложение внешних ресурсов

Создадим папку assets/ в корневом каталоге проекта. Сохраним в ней значок favicon.ico и файл style.css.

К настоящему моменту структура проекта должна выглядеть так:

        avocado_analytics/
├── assets/
│   ├── favicon.ico
│   └── style.css
├── venv/
├── app.py
└── avocado.csv
    

app.py требует нескольких изменений. Необходимо включить внешнюю таблицу стилей, добавить заголовок на панель инструментов и стилизовать компоненты с помощью файла style.css:

        external_stylesheets = [
    {
        "href": "https://fonts.googleapis.com/css2?"
                "family=Lato:wght@400;700&display=swap",
        "rel": "stylesheet",
    },
]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.title = "Avocado Analytics: Understand Your Avocados!"
    

Здесь мы указываем CSS-файл и семейство шрифтов, которое хотим загрузить в приложение. Внешние файлы загружаются до загрузки тела приложения. Аргумент external_stylesheets используется для добавления внешних CSS-файлов, а external_scripts ― для внешних файлов JavaScript, таких как скрипт Google Analytics.

Настройка стилей компонентов

В приведенном ниже коде мы добавляем className с соответствующим селектором классов к каждому из компонентов, представляющих заголовок информационной панели:

        app.layout = html.Div(
    children=[
        html.Div(
            children=[
                html.P(children="🥑", className="header-emoji"),
                html.H1(
                    children="Avocado Analytics", className="header-title"
                ),
                html.P(
                    children="Analyze the behavior of avocado prices"
                    " and the number of avocados sold in the US"
                    " between 2015 and 2018",
                    className="header-description",
                ),
            ],
            className="header",
        ),
    

Класс header-description, назначенный компоненту абзаца, имеет соответствующий селектор в style.css:

        .header-description {
    color: #CFCFCF;
    margin: 4px auto;
    text-align: center;
    max-width: 384px;
}
    

Другое существенное изменение ― графики. Новый код для графика цены:

        html.Div(
    children=[
        html.Div(
            children=dcc.Graph(
                id="price-chart",
                config={"displayModeBar": False},
                figure={
                    "data": [
                        {
                            "x": data["Date"],
                            "y": data["AveragePrice"],
                            "type": "lines",
                            "hovertemplate": "$%{y:.2f}"
                                                "<extra></extra>",
                        },
                    ],
                    "layout": {
                        "title": {
                            "text": "Average Price of Avocados",
                            "x": 0.05,
                            "xanchor": "left",
                        },
                        "xaxis": {"fixedrange": True},
                        "yaxis": {
                            "tickprefix": "$",
                            "fixedrange": True,
                        },
                        "colorway": ["#17B897"],
                    },
                },
            ),
            className="card",
        ),
    

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

Так же мы настроили ось, цвет рисунка, формат заголовка в разделе макета графика. Еще мы обернули график в html.Div с классом card. Это придаст графику белый фон и добавит небольшую тень под ним. Аналогичные изменения внесены в графики продаж и объемов. Вот полный код обновленного app.py:

app.py
        import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

data = pd.read_csv("avocado.csv")
data = data.query("type == 'conventional' and region == 'Albany'")
data["Date"] = pd.to_datetime(data["Date"], format="%Y-%m-%d")
data.sort_values("Date", inplace=True)

external_stylesheets = [
    {
        "href": "https://fonts.googleapis.com/css2?"
        "family=Lato:wght@400;700&display=swap",
        "rel": "stylesheet",
    },
]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.title = "Avocado Analytics: Understand Your Avocados!"

app.layout = html.Div(
    children=[
        html.Div(
            children=[
                html.P(children="🥑", className="header-emoji"),
                html.H1(
                    children="Avocado Analytics", className="header-title"
                ),
                html.P(
                    children="Analyze the behavior of avocado prices"
                    " and the number of avocados sold in the US"
                    " between 2015 and 2018",
                    className="header-description",
                ),
            ],
            className="header",
        ),
        html.Div(
            children=[
                html.Div(
                    children=dcc.Graph(
                        id="price-chart",
                        config={"displayModeBar": False},
                        figure={
                            "data": [
                                {
                                    "x": data["Date"],
                                    "y": data["AveragePrice"],
                                    "type": "lines",
                                    "hovertemplate": "$%{y:.2f}"
                                                     "<extra></extra>",
                                },
                            ],
                            "layout": {
                                "title": {
                                    "text": "Average Price of Avocados",
                                    "x": 0.05,
                                    "xanchor": "left",
                                },
                                "xaxis": {"fixedrange": True},
                                "yaxis": {
                                    "tickprefix": "$",
                                    "fixedrange": True,
                                },
                                "colorway": ["#17B897"],
                            },
                        },
                    ),
                    className="card",
                ),
                html.Div(
                    children=dcc.Graph(
                        id="volume-chart",
                        config={"displayModeBar": False},
                        figure={
                            "data": [
                                {
                                    "x": data["Date"],
                                    "y": data["Total Volume"],
                                    "type": "lines",
                                },
                            ],
                            "layout": {
                                "title": {
                                    "text": "Avocados Sold",
                                    "x": 0.05,
                                    "xanchor": "left",
                                },
                                "xaxis": {"fixedrange": True},
                                "yaxis": {"fixedrange": True},
                                "colorway": ["#E12D39"],
                            },
                        },
                    ),
                    className="card",
                ),
            ],
            className="wrapper",
        ),
    ]
)

if __name__ == "__main__":
    app.run_server(debug=True)

    

Панель обновленной версии app.py выглядит так:

📊 Туториал: визуализация данных в вебе с помощью Python и Dash

Добавляем в Dash-приложение интерактивные элементы

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

Давайте сделаем панель управления интерактивной. Новая версия позволит пользователю взаимодействовать со следующими фильтрами:

  • Регион производства.
  • Тип авокадо.
  • Диапазон дат.

Начнем с замены локального app.py на новую версию.

        import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import numpy as np
from dash.dependencies import Output, Input

data = pd.read_csv("avocado.csv")
data["Date"] = pd.to_datetime(data["Date"], format="%Y-%m-%d")
data.sort_values("Date", inplace=True)

external_stylesheets = [
    {
        "href": "https://fonts.googleapis.com/css2?"
        "family=Lato:wght@400;700&display=swap",
        "rel": "stylesheet",
    },
]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.title = "Avocado Analytics: Understand Your Avocados!"

app.layout = html.Div(
    children=[
        html.Div(
            children=[
                html.P(children="🥑", className="header-emoji"),
                html.H1(
                    children="Avocado Analytics", className="header-title"
                ),
                html.P(
                    children="Analyze the behavior of avocado prices"
                    " and the number of avocados sold in the US"
                    " between 2015 and 2018",
                    className="header-description",
                ),
            ],
            className="header",
        ),
        html.Div(
            children=[
                html.Div(
                    children=[
                        html.Div(children="Region", className="menu-title"),
                        dcc.Dropdown(
                            id="region-filter",
                            options=[
                                {"label": region, "value": region}
                                for region in np.sort(data.region.unique())
                            ],
                            value="Albany",
                            clearable=False,
                            className="dropdown",
                        ),
                    ]
                ),
                html.Div(
                    children=[
                        html.Div(children="Type", className="menu-title"),
                        dcc.Dropdown(
                            id="type-filter",
                            options=[
                                {"label": avocado_type, "value": avocado_type}
                                for avocado_type in data.type.unique()
                            ],
                            value="organic",
                            clearable=False,
                            searchable=False,
                            className="dropdown",
                        ),
                    ],
                ),
                html.Div(
                    children=[
                        html.Div(
                            children="Date Range",
                            className="menu-title"
                            ),
                        dcc.DatePickerRange(
                            id="date-range",
                            min_date_allowed=data.Date.min().date(),
                            max_date_allowed=data.Date.max().date(),
                            start_date=data.Date.min().date(),
                            end_date=data.Date.max().date(),
                        ),
                    ]
                ),
            ],
            className="menu",
        ),
        html.Div(
            children=[
                html.Div(
                    children=dcc.Graph(
                        id="price-chart", config={"displayModeBar": False},
                    ),
                    className="card",
                ),
                html.Div(
                    children=dcc.Graph(
                        id="volume-chart", config={"displayModeBar": False},
                    ),
                    className="card",
                ),
            ],
            className="wrapper",
        ),
    ]
)


@app.callback(
    [Output("price-chart", "figure"), Output("volume-chart", "figure")],
    [
        Input("region-filter", "value"),
        Input("type-filter", "value"),
        Input("date-range", "start_date"),
        Input("date-range", "end_date"),
    ],
)
def update_charts(region, avocado_type, start_date, end_date):
    mask = (
        (data.region == region)
        & (data.type == avocado_type)
        & (data.Date >= start_date)
        & (data.Date <= end_date)
    )
    filtered_data = data.loc[mask, :]
    price_chart_figure = {
        "data": [
            {
                "x": filtered_data["Date"],
                "y": filtered_data["AveragePrice"],
                "type": "lines",
                "hovertemplate": "$%{y:.2f}<extra></extra>",
            },
        ],
        "layout": {
            "title": {
                "text": "Average Price of Avocados",
                "x": 0.05,
                "xanchor": "left",
            },
            "xaxis": {"fixedrange": True},
            "yaxis": {"tickprefix": "$", "fixedrange": True},
            "colorway": ["#17B897"],
        },
    }

    volume_chart_figure = {
        "data": [
            {
                "x": filtered_data["Date"],
                "y": filtered_data["Total Volume"],
                "type": "lines",
            },
        ],
        "layout": {
            "title": {"text": "Avocados Sold", "x": 0.05, "xanchor": "left"},
            "xaxis": {"fixedrange": True},
            "yaxis": {"fixedrange": True},
            "colorway": ["#E12D39"],
        },
    }
    return price_chart_figure, volume_chart_figure


if __name__ == "__main__":
    app.run_server(debug=True,
                   host='127.0.0.1')
    

Затем необходимо обновить style.css следующим кодом.

        body {
    font-family: "Lato", sans-serif;
    margin: 0;
    background-color: #F7F7F7;
}

.header {
    background-color: #222222;
    height: 288px;
    padding: 16px 0 0 0;
}

.header-emoji {
    font-size: 48px;
    margin: 0 auto;
    text-align: center;
}

.header-title {
    color: #FFFFFF;
    font-size: 48px;
    font-weight: bold;
    text-align: center;
    margin: 0 auto;
}

.header-description {
    color: #CFCFCF;
    margin: 4px auto;
    text-align: center;
    max-width: 384px;
}

.wrapper {
    margin-right: auto;
    margin-left: auto;
    max-width: 1024px;
    padding-right: 10px;
    padding-left: 10px;
    margin-top: 32px;
}

.card {
    margin-bottom: 24px;
    box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.18);
}

.menu {
    height: 112px;
    width: 912px;
    display: flex;
    justify-content: space-evenly;
    padding-top: 24px;
    margin: -80px auto 0 auto;
    background-color: #FFFFFF;
    box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.18);
}

.Select-control {
    width: 256px;
    height: 48px;
}

.Select--single > .Select-control .Select-value, .Select-placeholder {
    line-height: 48px;
}

.Select--multi .Select-value-label {
    line-height: 32px;
}

.menu-title {
    margin-bottom: 6px;
    font-weight: bold;
    color: #079A82;
}
    

Как создавать интерактивные компоненты

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

Вот как это выглядит в app.py:

        html.Div(
    children=[
        html.Div(
            children=[
                html.Div(children="Region", className="menu-title"),
                dcc.Dropdown(
                    id="region-filter",
                    options=[
                        {"label": region, "value": region}
                        for region in np.sort(data.region.unique())
                    ],
                    value="Albany",
                    clearable=False,
                    className="dropdown",
                ),
            ]
        ),
        html.Div(
            children=[
                html.Div(children="Type", className="menu-title"),
                dcc.Dropdown(
                    id="type-filter",
                    options=[
                        {"label": avocado_type, "value": avocado_type}
                        for avocado_type in data.type.unique()
                    ],
                    value="organic",
                    clearable=False,
                    searchable=False,
                    className="dropdown",
                ),
            ],
        ),
        html.Div(
            children=[
                html.Div(
                    children="Date Range",
                    className="menu-title"
                    ),
                dcc.DatePickerRange(
                    id="date-range",
                    min_date_allowed=data.Date.min().date(),
                    max_date_allowed=data.Date.max().date(),
                    start_date=data.Date.min().date(),
                    end_date=data.Date.max().date(),
                ),
            ]
        ),
    ],
    className="menu",
),
    

Раскрывающиеся списки и селектор диапазона дат служат в качестве меню для взаимодействия с данными:

📊 Туториал: визуализация данных в вебе с помощью Python и Dash

Первый компонент в меню ― это раскрывающийся список Region. Код компонента:

        html.Div(
    children=[
        html.Div(children="Region", className="menu-title"),
        dcc.Dropdown(
            id="region-filter",
            options=[
                {"label": region, "value": region}
                for region in np.sort(data.region.unique())
            ],
            value="Albany",
            clearable=False,
            className="dropdown",
        ),
    ]
),
    

Вот что означает каждый из параметров:

  • id ― идентификатор элемента.
  • options ― параметры, отображаемые при выборе раскрывающегося списка. Ожидает словарь с метками и значениями.
  • value ― значение по умолчанию при загрузке страницы.
  • clearable ― позволяет пользователю оставить поле пустым, если установлено значение True.
  • className ― селектор классов, используемый для применения стилей

Селекторы Type и Data Range имеют ту же структуру, что и раскрывающееся меню Region.

Теперь взглянем на компоненты dcc.Graphs:

        html.Div(
    children=[
        html.Div(
            children=dcc.Graph(
                id="price-chart", config={"displayModeBar": False},
            ),
            className="card",
        ),
        html.Div(
            children=dcc.Graph(
                id="volume-chart", config={"displayModeBar": False},
            ),
            className="card",
        ),
    ],
    className="wrapper",
),
    

По сравнению с предыдущей версией панели инструментов в компонентах отсутствует аргумент figure. Это связано с тем, что аргумент figure теперь будет генерироваться функцией обратного вызова с использованием входных данных, которые пользователь устанавливает с помощью селекторов Region, Type и Data Range.

Как определить обратные вызовы

Мы определили, как пользователь будет взаимодействовать с приложением. Теперь нужно заставить приложение реагировать на действия пользователя. Для этого мы воспользуемся функциями обратного вызова (callbacks).

Функции обратного вызова Dash ― это обычные функции Python с декоратором app.callback. При изменении ввода запускается функция обратного вызова, выполняет заранее определенные операции (например, фильтрация набора данных), и возвращает результат в приложение. По сути, обратные вызовы связывают в приложении входные и выходные данные.

Вот функция обратного вызова, используемая для обновления графиков:

        @app.callback(
    [Output("price-chart", "figure"), Output("volume-chart", "figure")],
    [
        Input("region-filter", "value"),
        Input("type-filter", "value"),
        Input("date-range", "start_date"),
        Input("date-range", "end_date"),
    ],
)
def update_charts(region, avocado_type, start_date, end_date):
    mask = (
        (data.region == region)
        & (data.type == avocado_type)
        & (data.Date >= start_date)
        & (data.Date <= end_date)
    )
    filtered_data = data.loc[mask, :]
    price_chart_figure = {
        "data": [
            {
                "x": filtered_data["Date"],
                "y": filtered_data["AveragePrice"],
                "type": "lines",
                "hovertemplate": "$%{y:.2f}<extra></extra>",
            },
        ],
        "layout": {
            "title": {
                "text": "Average Price of Avocados",
                "x": 0.05,
                "xanchor": "left",
            },
            "xaxis": {"fixedrange": True},
            "yaxis": {"tickprefix": "$", "fixedrange": True},
            "colorway": ["#17B897"],
        },
    }

    volume_chart_figure = {
        "data": [
            {
                "x": filtered_data["Date"],
                "y": filtered_data["Total Volume"],
                "type": "lines",
            },
        ],
        "layout": {
            "title": {
                "text": "Avocados Sold",
                "x": 0.05,
                "xanchor": "left"
            },
            "xaxis": {"fixedrange": True},
            "yaxis": {"fixedrange": True},
            "colorway": ["#E12D39"],
        },
    }
    return price_chart_figure, volume_chart_figure
    

Сначала мы определяем выходные данные с помощью объектов Output. Эти объекты принимают два аргумента:

  • Идентификатор элемента, который они изменят при выполнении функции.
  • Свойство изменяемого элемента Например, Output("price-chart", "figure") обновит свойство figure элемента "price-chart".

Затем мы определяем входы с помощью объектов Input, они также принимают два аргумента:

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

То есть Input("region-filter", "value") будет следить за изменениями элемента "region-filter" и примет его свойство value, если элемент изменится.

Примечание

Обсуждаемый здесь объектInputимпортирован изdash.dependencies. Не спутайте его с компонентом, поступающим изdash_core_components. Эти объекты не взаимозаменяемы и имеют разное назначение.

В последних строках приведенного блока мы определяем тело функции. В приведенном примере функция принимает входные данные (регион, тип авокадо и диапазон дат), фильтрует их и генерирует объекты для графиков цен и объемов.

Это последняя версия нашей панели инструментов. Мы сделали ее не только красивой, но и интерактивной. Единственный недостающий шаг ― сделать так, чтобы результатом было можно поделиться с другими.

Разворачиваем Dash-приложение на Heroku

Мы закончили сборку приложения. У нас есть красивая, полностью интерактивная панель инструментов. Теперь мы узнаем, как ее развернуть.

Фактически приложения Dash ― то же, что приложения Flask, поэтому они имеют те же возможности для развертывания. В этом разделе мы развернем приложение на хостинге Heroku (с бесплатным тарифным планом).

Прежде чем начать, убедитесь, что вы установили интерфейс командной строки Heroku (CLI) и Git. Чтобы убедиться, что обе программы присутствуют в системе, выполните в терминале команды проверки версий:

        git --version
heroku --version
    

Далее нам нужно внести небольшое изменение в app.py. После инициализации приложения добавим переменную с именем server:

        app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
    

Это дополнение необходимо для запуска приложения с использованием WSGI-сервера. Встроенный сервер Flask не рекомендуется использовать в производственной среде, поскольку он не может обрабатывать большой объем трафика.

В корневом каталоге проекта создадим файл с именем runtime.txt, в котором укажем версию Python для приложения Heroku:

        python-3.8.6
    

При развертывании Heroku автоматически определит, что это приложение Python, и будет использовать соответствующий пакет сборки. Если вы также предоставите файл runtime.txt, сервер определит версию Python, которую будет использовать приложение.

Затем в корневом каталоге проекта создадим файл requirements.txt, где перечислим библиотеки, необходимые для установки Dash-приложения на веб-сервере:

        dash==1.13.3
pandas==1.0.5
gunicorn==20.0.4
    

В файле requirements.txt есть пакет, о котором мы раньше не упоминали: gunicorn. Gunicorn ― это HTTP-сервер WSGI, который часто используется для развертывания приложений Flask в производственной среде.

Теперь создадим файл с именем Procfile со следующим содержимым:

        web: gunicorn app:server
    

Этот файл сообщает приложению Heroku, какие команды следует выполнить для запуска нашего приложения.

Затем нужно инициализировать репозиторий Git. Для этого перейдем в корневой каталог проекта и выполним следующую команду:

        git init
    

Эта команда инициирует создание репозитория Git для avocado_analytics/. То есть Git будет отслеживать изменения, которые мы вносим в файлы в этом каталоге.

Однако есть файлы, которые не стоит отслеживать с помощью Git. Например, обычно мы не хотим отслеживать содержимое каталога виртуальной среды, файлов с байт-кодом и файлов метаданных, таких как .DS_Store.

Создадим в корневом каталоге файл с именем .gitignore и следующим содержимым:

        venv
*.pyc
.DS_Store # Only if you are using macOS
    

Это гарантирует, что репозиторий не отслеживает ненужные файлы. Теперь зафиксируем состояние проекта:

        git add .
git commit -m 'Add dashboard files'
    

Перед последним шагом убедитесь, что всё на месте. Структура проекта должна выглядеть так:

        avocado_analytics/
├── assets/
│   ├── favicon.ico
│   └── style.css
├── venv/
├── app.py
├── avocado.csv
├── Procfile
├── requirements.txt
└── runtime.txt
    

Наконец, нужно создать приложение в Heroku, отправить туда свой код с помощью Git и запустить приложение на одном из бесплатных серверных вариантов Heroku. Для этого выполним следующие команды:

        heroku create APP-NAME  # Подставьте имя приложения
git push heroku master
heroku ps:scale web=1
    

Вот и всё. Мы создали и развернули панель управления данными. Чтобы получить доступ к приложению, достаточно скопировать ссылку https://APP-NAME.herokuapp.com/ и замените APP-NAME на имя, которое вы определили на предыдущем шаге.

Если вам интересно сравнить получившийся результат, взгляните на образец приложения.

Заключение

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

Интерактивные дашборды (аналитические панели) идеально подходят для наглядного представления разнообразных данных. В этом уроке по Plotly Dash мы вам покажем, как создавать их с помощью языка Python.
В обязанности специалистов по данным часто входит не только анализ, но и визуальное представление результатов. И очень здорово, если это не просто статический PDF-файл или вручную созданные слайды Powerpoint, а интерактивная аналитическая панель. С таким приложением пользователь может взаимодействовать, просматривая показатели и фильтруя их по своему вкусу. Кроме того, при появлении новых данных приложение обновляется. Если вам приходилось ежемесячно или еженедельно заниматься составлением отчётов в Excel или Powerpoint, вы сразу поймёте, о чём речь.

Программное обеспечение для создания дашбордов

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

  • Microsoft Power BI: довольно громоздкая программа, которая больше подходит для бизнес-аналитики, чем для продуманных дашбордов с высоким уровнем интерактивности. Версия для десктопных компьютеров распространяется бесплатно. Однако для того, чтобы с удобством делиться отчётами с другими людьми (не просто пересылая файлы туда-обратно), надо получить лицензию на облачное решение. С другой стороны, многие компании так или иначе платят за офисный пакет, в который входит PowerBI.
  • Qlik или QlikSense: Лично на мой вкус это довольно хороший вариант, но лицензия стоит дорого. Обширные возможности настройки с помощью скриптов являются одновременно проклятием и благословением программы. Они позволяют очень гибко управлять данными, но из-за того, что здесь приходится комбинировать навыки программирования и манипуляций мышкой, разобраться с ней непросто.
  • Tableau: Эта программа получила много похвальных отзывов в начальный период. Её сильная сторона в основном касается визуализации данных. Но при этом за использование также придётся заплатить.
  • Grafana: отличное бесплатное программное обеспечение, которое используется в основном для отслеживания временных рядов (мониторинг серверов и так далее)

Программирование аналитических панелей с помощью языка Python

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

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

Для тех, кто программирует на R, есть фреймворк Shiny – решение, мимо которого вы не пройдёте. Для Python есть два крупных фреймворка: Plotly Dash и Bokeh. Популярность Dash неуклонно растёт, при этом освоить его легче. Для работы с Bokeh во многих случаях может понадобиться знание Javascript. И поэтому данное руководство для начинающих мы посвятили Plotly Dash.

Что такое Plotly Dash?

Dash – это фреймворк программирования веб-приложений для анализа/визуализации данных на языках Python, R или Julia, созданный компанией Plotly. В основе Dash лежит известная Javascript-библиотека React и Flask, один из самых популярных решений для серверной части на языке Python.

Все, что реально сделать с помощью Dash, вы можете посмотреть в Dash App Galerie.
Dash – полностью бесплатный пакет с открытым исходным кодом. Однако компания Plotly предлагает и коммерческие решения для размещения веб-приложений. Сразу проясним один момент: для того, чтобы кто-то другой мог видеть ваш дашборд в браузере, вам нужен сервер с поддержкой Python. В сети есть несколько бесплатных предложений (pythonanywhere, Heroku), которые подойдут для начальных попыток. Если же вы хотите создать несколько приложений, лучше заплатить за WPS. К счастью, это совсем недорого. Например, Linode позволяет арендовать небольшую виртуальную машину за 5 долларов в месяц.

Установка Plotly Dash

Устанавливается библиотека очень просто с помощью либо pip, либо conda.

pip install dash

или

conda install dash

Первый дашборд на основе Plotly Dash

После импорта библиотеки Dash и связанных с ней HTML-функций перейдём к созданию самого приложения. Для начала определим макет страницы, который сейчас состоит только из заголовка H1 “Welcome to Dash!“. После этого у нас запускается сервер в основной подпрограмме.

import dash
import dash_html_components as html
 
app = dash.Dash(__name__)
 
app.layout = html.H1(children="Welcome to Dash!")
 
if __name__ == "__main__":
    app.run_server(debug=True)

Теперь при запуске этой программы в консоли должно появиться следующее. Таким образом, вы запустили у себя сервер Flask, который доступ через веб-браузер по адресу http://127.0.0.1:8050 (только с вашего компьютера, так как 127.0.0.1 – это адрес локального хоста, то есть самого компьютера)

Вот как это будет выглядеть в браузере:


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

Основной макет приложения определяется через app.layout, где можно указать все возможные детали и структуры HTML-кода, а также диаграммы и виджеты. Во втором примере мы его расширим. Никакого функционала пока нет, так как его мы собираемся написать в следующем разделе.

Элементы управления Plotly Dash, такие как выпадающие списки

Мы будем использовать некоторые элементы как из библиотеки HTML dash_html_components, так и из основной библиотеки Dash dash_core_components.

Сначала нам нужно их импортировать. Кроме того, мы создадим график с помощью plotly.express и ряд случайных чисел с помощью numpy.

import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
import numpy as np

Далее мы напишем функцию hist(distribution, n), которая извлекает выборку чисел на основе указанного варианта распределения и делает из неё гистограмму. В качестве параметра функция получает название распределения (normal, binomial или chisquared ) и размер выборки n. Затем с помощью np.random.xxx извлекается случайная выборка и с помощью функции px.histogram plotly express создаётся гистограмма.

def hist(distribution, n):
    data = None
    if distribution == "normal":
        data = np.random.normal(loc=0, scale=1, size=n)
    elif distribution == "binomial":
        data = np.random.binomial(n=10, p=0.5, size=n)
    elif distribution == "chisquared":
        data = np.random.chisquare(df=5, size=n)
    else:
        print("This type of distribution is not supported yet!")
        return
    return px.histogram(data)

После этого идет блок Dash:

app = dash.Dash(__name__)
 
app.layout = 
 
if __name__ == "__main__":
    app.run_server(debug=True)

Далее мы определим макет, который, как уже было сказано, состоит из частей dash_html_components и dash_core_components. В принципе, макет представляет собой просто список таких объектов. Кто хоть немного знаком с HTML, узнает эти компоненты: html.H1 и html.H2 для заголовков, html.Div для подразделов. К более сложным элементам относятся dcc.Dropdown (раскрывающийся список), dcc.Input (поле ввода) и dcc.Graph (график).

Компоненты можно форматировать с помощью CSS и параметра style. При этом в параметр передаётся словарь, где прописываются нужные CSS-стили. Правда синтаксис незначительно изменён, так как использовать дефисы нельзя. То есть, вместо “background-color” нужно писать “backgroundColor”.

Имейте в виду, что пока мы просто определили макет страницы. Раскрывающийся список с вариантами распределений и поле ввода размера выборки хотя и отображаются, но ещё не “подключены”,то есть, они ничего не делают.

Определение макета выглядит вот так:

app.layout = html.Div(
    children=[
        html.H1(children="Distributions"),
        html.Div(
            children=[
                html.H2("Inputs"),
                html.Div(
                    children=[
                        html.P("Distribution"),
                        dcc.Dropdown(
                            id="distribution-dropdown",
                            options=[
                                {"label": "Normal", "value": "normal"},
                                {"label": "Binomial", "value": "binomial"},
                                {"label": "Chi²", "value": "chisquared"},
                            ],
                            value="normal",
                        ),
                    ],
                ),
                html.Div(
                    children=[
                        html.P("Selection size"),
                        dcc.Input(
                            id="n-input",
                            placeholder="Selection size",
                            type="number",
                            value=100,
                        ),
                    ],
                ),
            ],
            # атрибут style позволяет добавлять форматирование с помощью CSS
            style={
                "backgroundColor": "#DDDDDD",
                "maxWidth": "800px",
                "padding": "10px 20px",
            },
        ),
        html.Div(
            children=[
                html.H2("Histogramm"),
                dcc.Graph(id="histogramm", figure=hist("normal", 100)),
            ],
            style={
                "backgroundColor": "#DDDDDD",
                "maxWidth": "800px",
                "marginTop": "10px",
                "padding": "10px 20px",
            },
        ),
    ]
)
),

И вот как наша аналитическая панель отображается в браузере:

Интерактивная часть панели Plotly Dash

Итак, постепенно мы подошли к самому интересному. Пришло время поговорить об интерактивной части. Она здесь реализована путем добавления декораторов к соответствующим функциям.
При этом, во-первых, определяется выход (Output), то есть какой элемент макета должен быть возвращен функцией. В нашем случае это параметр figure графика dcc.Graph. Для идентификации элементов мы используем параметры id из макета.

Затем нужно определить входные данные (Inputs), то есть элементы, при изменении которых будет вызываться функция. Входных элементов может быть несколько, поэтому они перечисляюся в виде списка. В нашем случае это выпадающий список с идентификатором “distribution-dropdown” и поле ввода с идентификатором “n-input”.

Итак, к нашей функции hist просто добавляется декоратор. Для этого еще нужно импортировать два компонента: Input и Output. Строку импорта вставляем в верхнюю часть скрипта, где находятся все остальные импорты.

from dash.dependencies import Input, Output
 
@app.callback(
    Output("histogramm", "figure"),
    [Input("distribution-dropdown", "value"), Input("n-input", "value")],
)
def hist(distribution, n):
    data = None
    if distribution == "normal":
        data = np.random.normal(loc=0, scale=1, size=n)
    elif distribution == "binomial":
        data = np.random.binomial(n=10, p=0.5, size=n)
    elif distribution == "chisquared":
        data = np.random.chisquare(df=5, size=n)
    return px.histogram(data)

Внесем небольшое изменение в макет. В функции dcc.Graph мы определили только id, поэтому параметр figure можно убрать. За счет декоратора функция hist в любом случае вызывается при запуске, а так мы избежим ее двойного вызова.

dcc.Graph(id="histogramm")

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

Развертывание Plotly Dash в облаке

Теперь надо сделать так, чтобы аналитическая панель открывалась не только на нашем компьютере. И каждый раз устанавливать Python с необходимыми библиотеками тоже довольно неудобно. Итак, мы хотим разместить дашборд Plotly в облаке, чтобы можно было легко получать к нему доступ через браузер. Теоретически вы также можете заставить свой собственный компьютер или Raspberry Pi выполнять роль сервера, но тогда он должен будет работать без перерывов. Поэтому облачное решение – более удачный вариант.

К сожалению, процесс “размещения в Интернете” (который также называют развертыванием) не так прост, потому что вам нужно будет создать подходящую среду с необходимыми пакетами и веб-сервером. Веб-сервер, который идёт в комплекте с Dash или Flask, для реальной работы не подходит.

Раньше хорошим бесплатным вариантом для начала был pythonanywhere. Но сейчас он не актуален, потому что у библиотеки numpy плохая совместимость с веб-сервером uWSGI. А дата-сайентистам почти невозможно отказаться от numpy и pandas.

Второй бесплатный сервис – Heroku. Но для его использования нужно установить специальное приложение с интерфейсом командной строки и разобраться, что такое git (у нас есть статья о нем). То есть, придется ознакомиться с еще одним руководством.

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

Ну вот и все. Удачи вам и радости от изучения бесчисленных возможностей Plotly Dash.

Interaktive Dashboards in Python – Plotly Dash Tutorial

Dash is a Python framework for building analytical web applications. Dash helps in building responsive web dashboards that is good to look at and is very fast without the need to understand complex front-end frameworks or languages such as HTML, CSS, JavaScript. Let’s build our first web dashboard using Dash.

Installation and Imports

Install the latest version of Dash

pip install dash

Basic Dashboard

In this section, we will make an app that shows a static(but responsive) graph on the web page using the dash.

Step 1: Importing all the required libraries
Now let’s import Dash, Dash Core Components(which has components like graph, inputs etc., ) and Dash HTML Components(which has HTML components like meta tags, body tags, paragraph tags etc., )

import dash

import dash_core_components as dcc    

import dash_html_components as html

Step 2: Designing a layout
HTML components are just like HTML. Here html.H1 refers to the h1 tag in HTML.
Then, we make a graph which has various parameters such as id(a unique ID to a particular graph), figure(the graph itself), layout(the basic layout, title of graph, X axis, Y axis data etc., ).

  • The figure parameter is essentially a dictionary which has elements like x, y, type, name.
  • x refers to the X-axis value(it can be a list or a single element), y is the same except it is associated with the Y-axis.
  • The type parameter refers to the type of the graph, it maybe line, bar.
  • The name parameter refers to the name associated with the axis of a graph

app = dash.Dash()

app.layout = html.Div(children =[

    html.H1("Dash Tutorial"),

    dcc.Graph(

        id ="example",

        figure ={

            'data':[

                       {'x':[1, 2, 3, 4, 5],

                        'y':[5, 4, 7, 4, 8],

                        'type':'line'

                        'name':'Trucks'},

                       {'x':[1, 2, 3, 4, 5], 

                        'y':[6, 3, 5, 3, 7], 

                        'type':'bar',

                        'name':'Ships'}

                   ],

            'layout':{

                'title':'Basic Dashboard'

            }

        }

    )

])

Step 3: Running the server
The dashboard is now ready, but it needs a server to run on. Thus we set up the server using the below code.

if __name__ == '__main__':

    app.run_server()

Open the app on the web browser in localhost and default port 8050.

http://127.0.0.1:8050/

Output:

Screenshot of the Basic Dash app.

Using Callbacks

The above teaches us a basic static app. But what if you want to let the user take the control. This is why we are going to use app callbacks provided in Dash. In this section, we are going to make a web app that takes in number from the user and return the square of the number

Step 1: Importing all the required libraries
Just like above we are going to import all the required libraries. Here we require an additional dash.dependencies.Input and dash.dependencies.Output to provide us with input and output callback functionality.

import dash

import dash_core_components as dcc    

import dash_html_components as html

from dash.dependencies import Input, Output 

Step 2: Designing a layout

We are going to design a simple textbox that will help out take input and a text label which will output the square of the number that is input or returns an error if the input is not a number.

app = dash.Dash()

app.layout = html.Div(children =[

    dcc.Input(id ='input'

              value ='Enter a number'

              type ='text'),

    html.Div(id ='output')

])

Step 3: Callbacks

A callback is like a trigger that does a certain function on the change of the state of the input. In this case, it executes the method update_value and takes in input_data as the parameter and returns the square of that number. If the input is not a number, then it returns an error statement.

@app.callback(

    Output(component_id ='output', component_property ='children'),

    [Input(component_id ='input', component_property ='value')]

)

def update_value(input_data):

    try:

        return str(float(input_data)**2)

    except:

        return "Error, the input is not a number"

Step 3: Running the server
Again, just like above, we are going to run the server.

if __name__ == '__main__':

    app.run_server()

Open the app on the web browser in local host and default port 8050.

http://127.0.0.1:8050/

Output:

Square of five using Python Dash callbacks.

Footnotes

The above two examples must be useful for you to understand the working of the Dash framework. Although the two examples written above might not be useful on itself, it does help in understanding the concepts of building web apps with Dash framework and that will help you in building a useful web app in the future using real data.

Last Updated :
10 May, 2020

Like Article

Save Article

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Data Visualization Interfaces in Python With Dash

In the past, creating analytical web applications was a task for seasoned developers that required knowledge of multiple programming languages and frameworks. That’s no longer the case. Nowadays, you can make data visualization interfaces using pure Python. One popular tool for this is Dash.

Dash gives data scientists the ability to showcase their results in interactive web applications. You don’t need to be an expert in web development. In an afternoon, you can build and deploy a Dash app to share with others.

In this tutorial, you’ll learn how to:

  • Create a Dash application
  • Use Dash core components and HTML components
  • Customize the style of your Dash application
  • Use callbacks to build interactive applications
  • Deploy your application on PythonAnywhere

You can download the source code, data, and resources for the sample application that you’ll make in this tutorial by clicking the link below:

What Is Dash?

Dash is an open-source framework for building data visualization interfaces. Released in 2017 as a Python library, it’s grown to include implementations for R, Julia, and F#. Dash helps data scientists build analytical web applications without requiring advanced web development knowledge.

Three technologies constitute the core of Dash:

  1. Flask supplies the web server functionality.
  2. React.js renders the user interface of the web page.
  3. Plotly.js generates the charts used in your application.

But you don’t have to worry about making all these technologies work together. Dash will do that for you. You just need to write Python, R, Julia, or F# and sprinkle in a bit of CSS.

Plotly, a Canada-based company, built Dash and supports its development. You may know the company from the popular graphing libraries that share its name. The company released Dash as open source under an MIT license, so you can use Dash at no cost.

Plotly also offers a commercial companion to Dash called Dash Enterprise. This paid service provides companies with support services such as hosting, deploying, and handling authentication on Dash applications. But these features live outside of Dash’s open-source ecosystem.

Dash will help you build dashboards quickly. If you’re used to analyzing data or building data visualizations using Python, then Dash will be a useful addition to your toolbox. Here are a few examples of what you can make with Dash:

  • A dashboard showing object detection for self-driving cars
  • A visualization of millions of Uber rides
  • An interactive tool for analyzing soccer match data

This is just a tiny sample. If you’d like to see other interesting use cases, then go check out the Dash App Gallery.

If you feel comfortable with the requirements and want to learn how to use Dash in your next project, then continue to the following section!

Get Started With Dash in Python

In this tutorial, you’ll go through the end-to-end process of building a dashboard using Dash. If you follow along with the examples, then you’ll go from a bare-bones dashboard on your local machine to a styled dashboard deployed on PythonAnywhere.

To build the dashboard, you’ll use a dataset of sales and prices of avocados in the United States between 2015 and 2018. Justin Kiggins compiled this dataset using data from the Hass Avocado Board.

How to Set Up Your Local Environment

To develop your app, you’ll need a new directory to store your code and data. You’ll also need a clean Python virtual environment. To create those, execute the commands below, choosing the version that matches your operating system:

  • Windows
  • Linux + macOS
PS> mkdir avocado_analytics
PS> cd avocado_analytics
PS> python -m venv venv
PS> venvScriptsactivate
$ mkdir avocado_analytics
$ cd avocado_analytics
$ python -m venv venv
$ source venv/bin/activate

The first two commands create a directory for your project and move your current location there. The next command creates a virtual environment in that location. The last command activates the virtual environment.

Next, you need to install the required libraries. You can do that using pip inside your virtual environment. Install the libraries as follows:

(venv) $ python -m pip install dash==2.8.1 pandas==1.5.3

This command will install Dash and pandas in your virtual environment. You’ll use specific versions of these packages to make sure that you have the same environment as the one used throughout this tutorial. Alongside Dash, pandas will help you handle reading and wrangling the data that you’ll use in your app.

Finally, you need some data to feed into your dashboard. You can download the data as well as the code you see throughout this tutorial by clicking the link below:

Save the data as avocado.csv in the root directory of the project. By now, you should have a virtual environment with the required libraries and the data in the root folder of your project. Your project’s structure should look like this:

avocado_analytics/
|
├── venv/
|
└── avocado.csv

You’re good to go! Next, you’ll build your first Dash application.

How to Build a Dash Application

For development purposes, it’s useful to think of the process of building a Dash application in three steps:

  1. Define the content of your application using the app’s layout.
  2. Style the looks of your app with CSS or styled components.
  3. Use callbacks to determine which parts of your app are interactive and what they react to.

In this section, you’ll learn about the layout. Next, you’ll learn about style, and in a later section, you’ll learn how to make your dashboard interactive. You’ll start by setting up everything you need to initialize your application, and then you’ll define the layout of your app.

Initializing Your Dash Application

Create an empty file named app.py in the root directory of your project, then review the code of app.py in this section. To make it easier for you to copy the full code, you’ll find the entire contents of app.py at the end of this section.

Here are the first few lines of app.py:

 1# app.py
 2
 3import pandas as pd
 4from dash import Dash, dcc, html
 5
 6data = (
 7    pd.read_csv("avocado.csv")
 8    .query("type == 'conventional' and region == 'Albany'")
 9    .assign(Date=lambda data: pd.to_datetime(data["Date"], format="%Y-%m-%d"))
10    .sort_values(by="Date")
11)
12
13app = Dash(__name__)

On lines 3 and 4, you import the required libraries: pandas and dash. You’ll use pandas to read and organize the data. You’re importing the following elements from dash:

  • Dash helps you initialize your application.
  • html, also called Dash HTML Components, lets you access HTML tags.
  • dcc, short for Dash Core Components, allows you to create interactive components like graphs, dropdowns, or date ranges.

On lines 6 to 11, you read the data and preprocess it for use in the dashboard. You filter some of the data because your dashboard isn’t interactive yet, and the plotted values wouldn’t make sense otherwise.

On line 13, you create an instance of the Dash class. If you’ve used Flask before, then initializing a Dash class may look familiar. In Flask, you usually initialize a WSGI application using Flask(__name__). Similarly, for a Dash app, you use Dash(__name__).

Defining the Layout of Your Dash Application

Next, you’ll define the layout property of your application. This property dictates the content of your app. In this case, you’ll use a heading with a description immediately below it, followed by two graphs. Here’s how you define it:

 1# app.py
 2
 3# ...
 4
 5app.layout = html.Div(
 6    children=[
 7        html.H1(children="Avocado Analytics"),
 8        html.P(
 9            children=(
10                "Analyze the behavior of avocado prices and the number"
11                " of avocados sold in the US between 2015 and 2018"
12            ),
13        ),
14        dcc.Graph(
15            figure={
16                "data": [
17                    {
18                        "x": data["Date"],
19                        "y": data["AveragePrice"],
20                        "type": "lines",
21                    },
22                ],
23                "layout": {"title": "Average Price of Avocados"},
24            },
25        ),
26        dcc.Graph(
27            figure={
28                "data": [
29                    {
30                        "x": data["Date"],
31                        "y": data["Total Volume"],
32                        "type": "lines",
33                    },
34                ],
35                "layout": {"title": "Avocados Sold"},
36            },
37        ),
38    ]
39)

With this code, you define the .layout property of the app object. This property determines the content of your application using a tree structure made of Dash components.

Dash components come prepackaged in Python libraries. Some of them come with Dash when you install it. You have to install the rest separately. You’ll see two sets of components in almost every app:

  1. The Dash HTML Components module provides you with Python wrappers for HTML elements. For example, you could use Dash HTML Components to create elements such as paragraphs, headings, or lists.
  2. The Dash Core Components module provides you with Python abstractions for creating interactive user interfaces. You can use these components to create interactive elements such as graphs, sliders, or dropdowns.

On lines 5 to 13, you can see the Dash HTML components in practice. You start by defining the parent component, html.Div. Then you add two more elements, a heading (html.H1) and a paragraph (html.P), as its children.

These components are equivalent to the <div>, <h1>, and <p> HTML tags. You can use the components’ arguments to modify attributes or the content of the tags. For example, to specify what goes inside the <div> tag, you use the children argument in html.Div.

There are also other arguments in the components, such as style, className, and id, that refer to attributes of the HTML tags. You’ll see how to use some of these properties to style your dashboard in the next section.

The part of the layout shown on lines 5 to 13 will get transformed into the following HTML code:

<div>
  <h1>Avocado Analytics</h1>
  <p>
    Analyze the behavior of avocado prices and the number
    of avocados sold in the US between 2015 and 2018
  </p>
  <!-- Rest of the app -->
</div>

This HTML code is rendered when you open your application in the browser. It follows the same structure as your Python code, with a <div> tag containing an <h1> and a <p> element.

On lines 14 and 26 in the layout code snippet, you can see the graph component from Dash Core Components in practice. There are two dcc.Graph components in app.layout. The first one plots the average prices of avocados during the period of study, and the second plots the number of avocados sold in the United States during the same period.

Under the hood, Dash uses Plotly.js to generate graphs. The dcc.Graph components expect a figure object or a Python dictionary containing the plot’s data and layout. In this case, you provide the latter.

Finally, these two lines of code help you run your application:

# app.py

# ...

if __name__ == "__main__":
    app.run_server(debug=True)

These lines make it possible to run your Dash application locally using Flask’s built-in server. The debug=True parameter enables the hot-reloading option in your application. This means that when you make a change to your app, it reloads automatically, without you having to restart the server.

You can expand the following box to see the complete source code in one listing:

# app.py

import pandas as pd
from dash import Dash, dcc, html

data = (
    pd.read_csv("avocado.csv")
    .query("type == 'conventional' and region == 'Albany'")
    .assign(Date=lambda data: pd.to_datetime(data["Date"], format="%Y-%m-%d"))
    .sort_values(by="Date")
)

app = Dash(__name__)

app.layout = html.Div(
    children=[
        html.H1(children="Avocado Analytics"),
        html.P(
            children=(
                "Analyze the behavior of avocado prices and the number"
                " of avocados sold in the US between 2015 and 2018"
            ),
        ),
        dcc.Graph(
            figure={
                "data": [
                    {
                        "x": data["Date"],
                        "y": data["AveragePrice"],
                        "type": "lines",
                    },
                ],
                "layout": {"title": "Average Price of Avocados"},
            },
        ),
        dcc.Graph(
            figure={
                "data": [
                    {
                        "x": data["Date"],
                        "y": data["Total Volume"],
                        "type": "lines",
                    },
                ],
                "layout": {"title": "Avocados Sold"},
            },
        ),
    ]
)

if __name__ == "__main__":
    app.run_server(debug=True)

This is the code for your bare-bones dashboard. It includes all the snippets of code that you reviewed earlier in this section.

Now it’s time to run your application. Open a terminal inside your project’s root directory with the project’s virtual environment activated. Run python app.py, then go to http://localhost:8050 using your preferred browser.

It’s ALIVE! Your dashboard should look like this:

Python Dash + Barebones Dashboard Example

The good news is that you now have a working version of your dashboard. The bad news is that there’s still some work to do before you can show this to others. The dashboard is far from visually pleasing, and you still need to add some interactivity to it.

But don’t worry—you’ll learn how to fix these issues in the next sections.

Style Your Dash Application

Dash provides you with a lot of flexibility to customize the look of your application. You can use your own CSS or JavaScript files, set a favicon—the small icon shown on tabs in the web browser—and embed images, among other advanced options.

In this section, you’ll learn how to apply custom styles to components, and then you’ll style the dashboard that you built in the previous section.

How to Apply a Custom Style to Your Components

You can style components in two ways:

  1. Using the style argument of individual components
  2. Providing an external CSS file

Using the style argument to customize your dashboard is straightforward. This argument takes a Python dictionary with key-value pairs consisting of the names of CSS properties and the values that you want to set.

If you wanted to change the size and color of the H1 element in app.py, then you could set the element’s style argument as follows:

html.H1(
    children="Avocado Analytics",
    style={"fontSize": "48px", "color": "red"},
),

Here, you provide to style a dictionary with the properties and the corresponding values that you want to set. In this case, the specified style is to have a red heading with a font size of 48 pixels.

The downside of using the style argument is that it doesn’t scale well as your codebase grows. If your dashboard has multiple components that you want to look the same, then you’ll end up repeating a lot of your code. Instead, you can use a custom CSS file.

If you want to include your own local CSS or JavaScript files, then you need to create a folder called assets/ in the root directory of your project and save the files that you want to add there. By default, Dash automatically serves any file included in assets/. This will also work for adding a favicon or embedding images, as you’ll see in a bit.

Then you can use the className or id arguments of the components to adjust their styles using CSS. These arguments correspond with the class and id attributes when they’re transformed into HTML tags.

If you wanted to adjust the font size and text color of the H1 element in app.py, then you could use the className argument as follows:

html.H1(
    children="Avocado Analytics",
    className="header-title",
),

Setting the className argument will define the class attribute for the <h1> element. You could then use a CSS file in the assets folder to specify how you want it to look:

.header-title {
  font-size: 48px;
  color: red;
}

You use a class selector to format the heading in your CSS file. This selector will adjust the heading format. You could also use it with another element that needs to share the format by setting className="header-title".

Next, you’ll style your dashboard.

How to Improve the Looks of Your Dashboard

You just covered the basics of styling in Dash. Now, you’ll learn how to customize your dashboard’s looks. You’ll make these improvements:

  • Add a favicon and title to the page.
  • Change the font family of your dashboard.
  • Use an external CSS file to style Dash components.

You’ll start by learning how to use external assets in your application. That’ll allow you to add a favicon, a custom font family, and a CSS style sheet. Then you’ll learn how to use the className argument to apply custom styles to your Dash components.

Adding External Assets to Your Application

Create a folder called assets/ in your project’s root directory. Download a favicon from the Twemoji open-source project and save it as favicon.ico in assets/. Finally, create a CSS file in assets/ called style.css and add the code in the collapsible section below:

/* assets/style.css */

body {
    font-family: "Lato", sans-serif;
    margin: 0;
    background-color: #F7F7F7;
}

.header {
    background-color: #222222;
    height: 256px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.header-emoji {
    font-size: 48px;
    margin: 0 auto;
    text-align: center;
}

.header-title {
    color: #FFFFFF;
    font-size: 48px;
    font-weight: bold;
    text-align: center;
    margin: 0 auto;
}

.header-description {
    color: #CFCFCF;
    margin: 4px auto;
    text-align: center;
    max-width: 384px;
}

.wrapper {
    margin-right: auto;
    margin-left: auto;
    max-width: 1024px;
    padding-right: 10px;
    padding-left: 10px;
    margin-top: 32px;
}

.card {
    margin-bottom: 24px;
    box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.18);
}

The assets/style.css file contains the styles that you’ll apply to components in your application’s layout. By now, your project structure should look like this:

avocado_analytics/
│
├── assets/
│   ├── favicon.ico
│   └── style.css
│
├── venv/
│
├── app.py
└── avocado.csv

Once you start the server, Dash will automatically serve the files located in assets/. You include two files, favicon.ico and style.css, in assets/. To set a default favicon, you don’t have to take any additional steps. To use the styles that you defined in style.css, you’ll need to use the className argument in Dash components.

You need to make a few changes in app.py. You’ll include an external style sheet, add a title to your dashboard, and style the components using the style.css file. Review the changes below. Then, in the last part of this section, you’ll find the full code for your updated version of app.py.

Here’s how you include an external style sheet and add a title to your dashboard:

# app.py

# ...

external_stylesheets = [
    {
        "href": (
            "https://fonts.googleapis.com/css2?"
            "family=Lato:wght@400;700&display=swap"
        ),
        "rel": "stylesheet",
    },
]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.title = "Avocado Analytics: Understand Your Avocados!"

# ...

In these code lines, you specify an external CSS file containing a font family, which you want to load in your application. You add external files to the head tag of your application, so they load before the body of your application loads. You use the external_stylesheets argument for adding external CSS files or external_scripts for external JavaScript files like Google Analytics.

You also set the title of your application. This is the text that appears in the title bar of your web browser, in Google’s search results, and in social media cards when you share your site.

Customizing the Styles of Components

To use the styles in style.css, you’ll need to use the className argument in Dash components. The code below adds a className with a corresponding class selector to each of the components in the header of your dashboard:

# app.py

# ...

app.layout = html.Div(
    children=[
        html.Div(
            children=[
                html.P(children="🥑", className="header-emoji"),
                html.H1(
                    children="Avocado Analytics", className="header-title"
                ),
                html.P(
                    children=(
                        "Analyze the behavior of avocado prices and the number"
                        " of avocados sold in the US between 2015 and 2018"
                    ),
                    className="header-description",
                ),
            ],
            className="header",

        # ...

In the highlighted lines, you can see that you’ve made three changes to the initial version of the dashboard:

  1. There’s a new <div> element that wraps all the header components.
  2. There’s a new paragraph element with an avocado emoji, 🥑, that’ll serve as a logo on the page.
  3. There’s a className argument in each component. These class names match a class selector in style.css, which defines the looks of each component.

For example, the header-description class assigned to the paragraph component starting with "Analyze the behavior of avocado prices" has a corresponding selector in style.css. In that file, you’ll see the following:

.header-description {
    color: #CFCFCF;
    margin: 4px auto;
    text-align: center;
    max-width: 384px;
}

These lines define the format for the header-description class selector. They’ll change the color, margin, alignment, and maximum width of any component with className="header-description". All the components have corresponding class selectors in the CSS file.

The other significant change is in the graphs. Here’s the new code for the price chart:

 1# app.py
 2
 3# ...
 4
 5app.layout = html.Div(
 6    children=[
 7        # ...
 8
 9        html.Div(
10            children=[
11                html.Div(
12                    children=dcc.Graph(
13                        id="price-chart",
14                        config={"displayModeBar": False},
15                        figure={
16                            "data": [
17                                {
18                                    "x": data["Date"],
19                                    "y": data["AveragePrice"],
20                                    "type": "lines",
21                                    "hovertemplate": (
22                                        "$%{y:.2f}<extra></extra>"
23                                    ),
24                                },
25                            ],
26                            "layout": {
27                                "title": {
28                                    "text": "Average Price of Avocados",
29                                    "x": 0.05,
30                                    "xanchor": "left",
31                                },
32                                "xaxis": {"fixedrange": True},
33                                "yaxis": {
34                                    "tickprefix": "$",
35                                    "fixedrange": True,
36                                },
37                                "colorway": ["#17b897"],
38                            },
39                        },
40                    ),
41                    className="card",
42                ),
43
44                # ...
45
46            ],
47            className="wrapper",
48        ),
49    ]
50)
51
52# ...

In this code, you define a className and a few customizations for the config and figure parameters of your chart. Here are the changes:

  • Line 14: You remove the floating toolbar that Plotly shows by default.
  • Lines 21 to 23: You set the hover template so that when users hover over a data point, it shows the price in dollars. Instead of 2.5, it’ll show as $2.5.
  • Lines 26 to 38: You adjust the axes, the color of the figure, and the title format in the layout section of the graph.
  • Lines 11 and 41: You wrap the graph in a <div> element with a "card" class. This will give the graph a white background and add a small shadow below it.
  • Lines 9 and 47: You add a <div> element that wraps the graph components with a wrapper class.

There are similar adjustments to the sales and volume charts. You can see those in the full code for the updated app.py in the collapsible section below:

# app.py

import pandas as pd
from dash import Dash, dcc, html

data = (
    pd.read_csv("avocado.csv")
    .query("type == 'conventional' and region == 'Albany'")
    .assign(Date=lambda data: pd.to_datetime(data["Date"], format="%Y-%m-%d"))
    .sort_values(by="Date")
)

external_stylesheets = [
    {
        "href": (
            "https://fonts.googleapis.com/css2?"
            "family=Lato:wght@400;700&display=swap"
        ),
        "rel": "stylesheet",
    },
]
app = Dash(__name__, external_stylesheets=external_stylesheets)
app.title = "Avocado Analytics: Understand Your Avocados!"

app.layout = html.Div(
    children=[
        html.Div(
            children=[
                html.P(children="🥑", className="header-emoji"),
                html.H1(
                    children="Avocado Analytics", className="header-title"
                ),
                html.P(
                    children=(
                        "Analyze the behavior of avocado prices and the number"
                        " of avocados sold in the US between 2015 and 2018"
                    ),
                    className="header-description",
                ),
            ],
            className="header",
        ),
        html.Div(
            children=[
                html.Div(
                    children=dcc.Graph(
                        id="price-chart",
                        config={"displayModeBar": False},
                        figure={
                            "data": [
                                {
                                    "x": data["Date"],
                                    "y": data["AveragePrice"],
                                    "type": "lines",
                                    "hovertemplate": (
                                        "$%{y:.2f}<extra></extra>"
                                    ),
                                },
                            ],
                            "layout": {
                                "title": {
                                    "text": "Average Price of Avocados",
                                    "x": 0.05,
                                    "xanchor": "left",
                                },
                                "xaxis": {"fixedrange": True},
                                "yaxis": {
                                    "tickprefix": "$",
                                    "fixedrange": True,
                                },
                                "colorway": ["#17b897"],
                            },
                        },
                    ),
                    className="card",
                ),
                html.Div(
                    children=dcc.Graph(
                        id="volume-chart",
                        config={"displayModeBar": False},
                        figure={
                            "data": [
                                {
                                    "x": data["Date"],
                                    "y": data["Total Volume"],
                                    "type": "lines",
                                },
                            ],
                            "layout": {
                                "title": {
                                    "text": "Avocados Sold",
                                    "x": 0.05,
                                    "xanchor": "left",
                                },
                                "xaxis": {"fixedrange": True},
                                "yaxis": {"fixedrange": True},
                                "colorway": ["#E12D39"],
                            },
                        },
                    ),
                    className="card",
                ),
            ],
            className="wrapper",
        ),
    ]
)

if __name__ == "__main__":
    app.run_server(debug=True)

This is the updated version of app.py. It has the required changes in the code to add a favicon and a page title, update the font family, and use an external CSS file. After these changes, your dashboard should look like this:

Python Dash + Dashboard With Styling Example

In the next section, you’ll learn how to add interactive components to your dashboard.

Add Interactivity to Your Dash Apps Using Callbacks

In this section, you’ll learn how to add interactive elements to your dashboard.

Dash’s interactivity is based on a reactive programming paradigm. This means that you can link components with elements of your app that you want to update. If a user interacts with an input component like a dropdown or a range slider, then the output, such as a graph, will react automatically to the changes in the input.

Now you’re going to make your dashboard interactive. This new version of your dashboard will allow the user to interact with the following filters:

  • Region
  • Type of avocado
  • Date range

The collapsible boxes below contain the full source code that you’ll be exploring in this section. Start by replacing your local app.py with the new version in the collapsible section below:

# app.py

import pandas as pd
from dash import Dash, Input, Output, dcc, html

data = (
    pd.read_csv("avocado.csv")
    .assign(Date=lambda data: pd.to_datetime(data["Date"], format="%Y-%m-%d"))
    .sort_values(by="Date")
)
regions = data["region"].sort_values().unique()
avocado_types = data["type"].sort_values().unique()

external_stylesheets = [
    {
        "href": (
            "https://fonts.googleapis.com/css2?"
            "family=Lato:wght@400;700&display=swap"
        ),
        "rel": "stylesheet",
    },
]
app = Dash(__name__, external_stylesheets=external_stylesheets)
app.title = "Avocado Analytics: Understand Your Avocados!"

app.layout = html.Div(
    children=[
        html.Div(
            children=[
                html.P(children="🥑", className="header-emoji"),
                html.H1(
                    children="Avocado Analytics", className="header-title"
                ),
                html.P(
                    children=(
                        "Analyze the behavior of avocado prices and the number"
                        " of avocados sold in the US between 2015 and 2018"
                    ),
                    className="header-description",
                ),
            ],
            className="header",
        ),
        html.Div(
            children=[
                html.Div(
                    children=[
                        html.Div(children="Region", className="menu-title"),
                        dcc.Dropdown(
                            id="region-filter",
                            options=[
                                {"label": region, "value": region}
                                for region in regions
                            ],
                            value="Albany",
                            clearable=False,
                            className="dropdown",
                        ),
                    ]
                ),
                html.Div(
                    children=[
                        html.Div(children="Type", className="menu-title"),
                        dcc.Dropdown(
                            id="type-filter",
                            options=[
                                {
                                    "label": avocado_type.title(),
                                    "value": avocado_type,
                                }
                                for avocado_type in avocado_types
                            ],
                            value="organic",
                            clearable=False,
                            searchable=False,
                            className="dropdown",
                        ),
                    ],
                ),
                html.Div(
                    children=[
                        html.Div(
                            children="Date Range", className="menu-title"
                        ),
                        dcc.DatePickerRange(
                            id="date-range",
                            min_date_allowed=data["Date"].min().date(),
                            max_date_allowed=data["Date"].max().date(),
                            start_date=data["Date"].min().date(),
                            end_date=data["Date"].max().date(),
                        ),
                    ]
                ),
            ],
            className="menu",
        ),
        html.Div(
            children=[
                html.Div(
                    children=dcc.Graph(
                        id="price-chart",
                        config={"displayModeBar": False},
                    ),
                    className="card",
                ),
                html.Div(
                    children=dcc.Graph(
                        id="volume-chart",
                        config={"displayModeBar": False},
                    ),
                    className="card",
                ),
            ],
            className="wrapper",
        ),
    ]
)

@app.callback(
    Output("price-chart", "figure"),
    Output("volume-chart", "figure"),
    Input("region-filter", "value"),
    Input("type-filter", "value"),
    Input("date-range", "start_date"),
    Input("date-range", "end_date"),
)
def update_charts(region, avocado_type, start_date, end_date):
    filtered_data = data.query(
        "region == @region and type == @avocado_type"
        " and Date >= @start_date and Date <= @end_date"
    )
    price_chart_figure = {
        "data": [
            {
                "x": filtered_data["Date"],
                "y": filtered_data["AveragePrice"],
                "type": "lines",
                "hovertemplate": "$%{y:.2f}<extra></extra>",
            },
        ],
        "layout": {
            "title": {
                "text": "Average Price of Avocados",
                "x": 0.05,
                "xanchor": "left",
            },
            "xaxis": {"fixedrange": True},
            "yaxis": {"tickprefix": "$", "fixedrange": True},
            "colorway": ["#17B897"],
        },
    }

    volume_chart_figure = {
        "data": [
            {
                "x": filtered_data["Date"],
                "y": filtered_data["Total Volume"],
                "type": "lines",
            },
        ],
        "layout": {
            "title": {"text": "Avocados Sold", "x": 0.05, "xanchor": "left"},
            "xaxis": {"fixedrange": True},
            "yaxis": {"fixedrange": True},
            "colorway": ["#E12D39"],
        },
    }
    return price_chart_figure, volume_chart_figure

if __name__ == "__main__":
    app.run_server(debug=True)

Next, replace style.css with the code in the collapsible section below:

body {
    font-family: "Lato", sans-serif;
    margin: 0;
    background-color: #F7F7F7;
}

.header {
    background-color: #222222;
    height: 288px;
    padding: 16px 0 0 0;
}

.header-emoji {
    font-size: 48px;
    margin: 0 auto;
    text-align: center;
}

.header-title {
    color: #FFFFFF;
    font-size: 48px;
    font-weight: bold;
    text-align: center;
    margin: 0 auto;
}

.header-description {
    color: #CFCFCF;
    margin: 4px auto;
    text-align: center;
    max-width: 384px;
}

.wrapper {
    margin-right: auto;
    margin-left: auto;
    max-width: 1024px;
    padding-right: 10px;
    padding-left: 10px;
    margin-top: 32px;
}

.card {
    margin-bottom: 24px;
    box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.18);
}

.menu {
    height: 112px;
    width: 912px;
    display: flex;
    justify-content: space-evenly;
    padding-top: 24px;
    margin: -80px auto 0 auto;
    background-color: #FFFFFF;
    box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.18);
}

.Select-control {
    width: 256px;
    height: 48px;
}

.Select--single>.Select-control .Select-value,
.Select-placeholder {
    line-height: 48px;
}

.Select--multi .Select-value-label {
    line-height: 32px;
}

.menu-title {
    margin-bottom: 6px;
    font-weight: bold;
    color: #079A82;
}

Now you’re ready to explore the interactive components that you’ve added to your application!

How to Create Interactive Components

First, you’ll learn how to create components that users can interact with. For that, you’ll include a new <div> element above your charts. It’ll include two dropdowns and a date range selector that the user can use to filter the data and update the graphs.

You start by changing how you process your data. You no longer filter the data when you read them. Instead you find the regions and avocado types that are present in your data:

# app.py

# ...

data = (
    pd.read_csv("avocado.csv")
    # Remove .query(...)
    .assign(Date=lambda data: pd.to_datetime(data["Date"], format="%Y-%m-%d"))
    .sort_values(by="Date")
)
regions = data["region"].sort_values().unique()
avocado_types = data["type"].sort_values().unique()

# ...

Next, you’ll use regions and avocado_types to populate a few dropdowns. Here’s how that looks in app.py:

 1# app.py
 2
 3# ...
 4
 5app.layout = html.Div(
 6    children=[
 7
 8        # ...
 9
10        html.Div(
11            children=[
12                html.Div(
13                    children=[
14                        html.Div(children="Region", className="menu-title"),
15                        dcc.Dropdown(
16                            id="region-filter",
17                            options=[
18                                {"label": region, "value": region}
19                                for region in regions
20                            ],
21                            value="Albany",
22                            clearable=False,
23                            className="dropdown",
24                        ),
25                    ]
26                ),
27                html.Div(
28                    children=[
29                        html.Div(children="Type", className="menu-title"),
30                        dcc.Dropdown(
31                            id="type-filter",
32                            options=[
33                                {
34                                    "label": avocado_type.title(),
35                                    "value": avocado_type,
36                                }
37                                for avocado_type in avocado_types
38                            ],
39                            value="organic",
40                            clearable=False,
41                            searchable=False,
42                            className="dropdown",
43                        ),
44                    ],
45                ),
46                html.Div(
47                    children=[
48                        html.Div(
49                            children="Date Range", className="menu-title"
50                        ),
51                        dcc.DatePickerRange(
52                            id="date-range",
53                            min_date_allowed=data["Date"].min().date(),
54                            max_date_allowed=data["Date"].max().date(),
55                            start_date=data["Date"].min().date(),
56                            end_date=data["Date"].max().date(),
57                        ),
58                    ]
59                ),
60            ],
61            className="menu",
62        ),
63
64        # ...

On lines 10 to 62, you define a <div> element above your graphs, consisting of two dropdowns and a date range selector. It’ll serve as a menu that the user will use to interact with the data:

Python Dash + Dropdowns and Date Range

The first component in the menu is the Region dropdown. Focus on the code for that component:

html.Div(
    children=[
        html.Div(children="Region", className="menu-title"),
        dcc.Dropdown(
            id="region-filter",
            options=[
                {"label": region, "value": region}
                for region in regions
            ],
            value="Albany",
            clearable=False,
            className="dropdown",
        ),
    ]
),

Here, you define the dropdown that users will use to filter the data by region. In addition to the title, it has a dcc.Dropdown component. Here’s what each of the parameters means:

  • id is the identifier of this element.
  • options indicates the options shown when the dropdown is selected. It expects a dictionary with labels and values.
  • value is the default value when the page loads.
  • clearable allows the user to leave this field empty if set to True.
  • className is a CSS class selector used for applying styles.

The Type and Date Range selectors follow the same structure as the Region dropdown. Feel free to review them on your own.

Next, take a look at the dcc.Graphs components:

# app.py

# ...

app.layout = html.Div(
    children=[

        # ...

        html.Div(
            children=[
                html.Div(
                    children=dcc.Graph(
                        id="price-chart",
                        config={"displayModeBar": False},
                    ),
                    className="card",
                ),
                html.Div(
                    children=dcc.Graph(
                        id="volume-chart",
                        config={"displayModeBar": False},
                    ),
                    className="card",
                ),
            ],
            className="wrapper",
        ),
    ]
)

# ...

In this part of the code, you define the dcc.Graph components. You may have noticed that, compared to the previous version of the dashboard, the components are missing the figure argument. That’s because a callback function will now generate the figure argument using the inputs that the user sets using the Region, Type, and Date Range selectors.

How to Define Callbacks

You’ve defined how the user will interact with your application. Now you need to make your application react to user interactions. For that, you’ll use callback functions.

Dash’s callback functions are regular Python functions with an app.callback decorator. In Dash, when an input changes, a callback function is triggered. The function performs some predetermined operations, like filtering a dataset, and returns an output to the application. In essence, callbacks link inputs and outputs in your app.

Here’s the callback function that’s used for updating the graphs:

 1# app.py
 2
 3# ..
 4
 5@app.callback(
 6    Output("price-chart", "figure"),
 7    Output("volume-chart", "figure"),
 8    Input("region-filter", "value"),
 9    Input("type-filter", "value"),
10    Input("date-range", "start_date"),
11    Input("date-range", "end_date"),
12)
13def update_charts(region, avocado_type, start_date, end_date):
14    filtered_data = data.query(
15        "region == @region and type == @avocado_type"
16        " and Date >= @start_date and Date <= @end_date"
17    )
18    price_chart_figure = {
19        "data": [
20            {
21                "x": filtered_data["Date"],
22                "y": filtered_data["AveragePrice"],
23                "type": "lines",
24                "hovertemplate": "$%{y:.2f}<extra></extra>",
25            },
26        ],
27        "layout": {
28            "title": {
29                "text": "Average Price of Avocados",
30                "x": 0.05,
31                "xanchor": "left",
32            },
33            "xaxis": {"fixedrange": True},
34            "yaxis": {"tickprefix": "$", "fixedrange": True},
35            "colorway": ["#17B897"],
36        },
37    }
38
39    volume_chart_figure = {
40        "data": [
41            {
42                "x": filtered_data["Date"],
43                "y": filtered_data["Total Volume"],
44                "type": "lines",
45            },
46        ],
47        "layout": {
48            "title": {"text": "Avocados Sold", "x": 0.05, "xanchor": "left"},
49            "xaxis": {"fixedrange": True},
50            "yaxis": {"fixedrange": True},
51            "colorway": ["#E12D39"],
52        },
53    }
54    return price_chart_figure, volume_chart_figure
55
56# ...

On lines 6 to 11, you define the inputs and outputs inside the app.callback decorator.

First, you define the outputs using Output objects. They take two arguments:

  1. The identifier of the element that they’ll modify when the function executes
  2. The property of the element to be modified

For example, Output("price-chart", "figure") will update the figure property of the "price-chart" element.

Then you define the inputs using Input objects. They also take two arguments:

  1. The identifier of the element that they’ll be watching for changes
  2. The property of the watched element that they’ll be watching for changes

So, Input("region-filter", "value") will watch the "region-filter" element and its value property for changes. The argument passed on to the callback function will be the new value of region-filter.value.

On line 13, you define the function that’ll be applied when an input changes. It’s worth noticing that the arguments of the function will correspond with the order of the Input objects supplied to the callback. There’s no explicit relationship between the names of the arguments in the function and the values specified in the Input objects.

Finally, on lines 14 to 54, you define the body of the function. In this case, the function takes the inputs (region, type of avocado, and date range), filters the data, and generates the figure objects for the price and volume charts.

That’s all! If you’ve followed along to this point, then your dashboard should look like this:

Way to go! That’s the final version of your dashboard. In addition to making it look beautiful, you also made it interactive. The only missing step is making it public so you can share it with others.

Deploy Your Dash Application to PythonAnywhere

You’re done building your application, and you have a beautiful, fully interactive dashboard. Now you’ll learn how to deploy it.

Dash apps are Flask apps, so both share the same deployment options. In this section, you’ll deploy your app on PythonAnywhere, which offers a free tier for hosting Python web applications in the cloud.

How to Create a Free PythonAnywhere Account

Before you get started, make sure you’ve signed up for a PythonAnywhere beginner account, which is completely free of charge and doesn’t require you to provide any payment details. That said, it comes with a few limitations that you should be aware of. The most important ones will prevent you from doing the following:

  • Running more than one web application at a time
  • Defining a custom domain name
  • Exceeding the available disk quota (512 MB)
  • Using the CPU for longer than 100 seconds per day
  • Making unrestricted HTTP requests from your app

For this tutorial, though, you won’t need any of that!

If you’re based in Europe, then consider signing up through eu.pythonanywhere.com instead of the www.pythonanywhere.com. It’ll ensure GDPR compliance for your data, which PythonAnywhere will store on servers in Germany. Because of that, you may also experience slightly faster response times. Finally, if you decide to become a paid customer one day, then you’ll be charged in euros instead of US dollars.

Feel free to follow either of the two PythonAnywhere links above if you don’t care about any of these features at the moment. Note, however, that once you register a username on one domain, then you won’t be able to reuse it on the other!

Another reason to think carefully about your username is that it must be unique, as it’ll become a part of your very own domain name, such as in these examples:

http://realpython.pythonanywhere.com/
http://realpython.eu.pythonanywhere.com/

Once you register a new account on PythonAnywhere, you must confirm your email address so that you can reset the password if you forget it. Also, it might be a good idea to enable two-factor authentication on the Security tab in your Account settings as an extra security measure.

If you’ve just created a new account, then you’re already good to go. But if you registered a PythonAnywhere account a while ago, then you might need to change your system image to a newer one, which comes with a more recent Python version and newer third-party libraries. At the time of writing, the latest image, called haggis, shipped with Python 3.10.5, pandas 1.3.5, and Dash 2.4.1.

With that out of the way, it’s time to create your first web app on PythonAnywhere!

How to Deploy Your Avocado Analytics App

Because Dash apps are Flask apps with some extra frills, you can take advantage of PythonAnywhere’s excellent support for this popular Python web framework.

When you’re logged in to your PythonAnywhere account, create a new Bash shell console, either from the Dashboard or the Consoles tab. This will throw you into an interactive prompt of the virtual server, letting you remotely execute commands straight from your web browser.

There are already several useful programs installed for you, including a Git client, which you’ll use to get your project’s source code into PythonAnywhere. You can also upload files in other ways, but using Git seems the most convenient. If you haven’t made your own repository yet, then you might clone Real Python’s materials repository with your sample Dash application in it:

$ git clone --depth=1 https://github.com/realpython/materials.git

The --depth=1 option tells Git only to clone the latest commit, which saves time and disk space. Note that if you don’t want to configure SSH keys for your PythonAnywhere machine, then you’ll have to clone a public repository using the HTTPS protocol. Since August 2021, cloning private repositories has been possible only after configuring a personal access token in GitHub.

When the repository is cloned, you can move and rename a subfolder with the finished avocado app to your home folder on PythonAnywhere, and then remove the rest of the materials:

$ mv materials/python-dash/avocado_analytics_3/ ~/avocado_analytics
$ rm -rf materials/

Remember that you only have 512 megabytes of disk space on the free tier at your disposal, and the materials take up a significant portion of that!

At this point, your home folder should look like this:

home/realpython/
│
└── avocado_analytics/
    │
    ├── assets/
    │   ├── favicon.ico
    │   └── style.css
    │
    ├── app.py
    └── avocado.csv

Of course, the username realpython will be different on your account, but the overall folder structure should remain the same.

Now, go the Web tab and click the button labeled Add a new web app. This will open a wizard, asking you a few questions. First, select Flask as the Python web framework of your choice:

Select Flask in PythonAnywhere

Next, you’ll see a specific Flask version running on top of the given Python interpreter. Select the latest version available:

Select Python Version in PythonAnywhere

In the next step, you’ll need to update the file path leading up to the main Python module with your Flask app:

Quickstart a New Flask Project in PythonAnywhere

While you can change it later, it’s much easier if you do it right now, so make sure to rename the default mysite/ folder with avocado_analytics/ to match your project’s name. At the same time, you want to keep the suggested flask_app.py filename intact. PythonAnywhere will generate this file and populate it with a demo app, so if you renamed it to app.py, then the code that you cloned from GitHub would get overwritten!

Once this is done, you’ll be presented with a number of configuration options for your new web app. First, you need to update the working directory of the app to be the same as the source code:

Specify the Working Directory in PythonAnywhere

This will ensure that Python can find your avocado.csv file at runtime and open it for reading.

Next, you’ll need to tweak the default WSGI server configuration, which is slightly different for Dash apps than it is for Flask. PythonAnywhere uses the uWSGI server behind the scenes, which reads the configuration from a special Python module located in the /var/www/ folder.

Click the WSGI configuration file option visible in the screenshot above to open it in an editor in your web browser:

 # This file contains the WSGI configuration required to serve up your
 # web application at http://<your-username>.pythonanywhere.com/
 # It works by setting the variable 'application' to a WSGI handler of some
 # description.
 #
 # The below has been auto-generated for your Flask project

 import sys

 # add your project directory to the sys.path
 project_home = '/home/realpython/avocado_analytics'
 if project_home not in sys.path:
     sys.path = [project_home] + sys.path

 # import flask app but need to call it "application" for WSGI to work
-from flask_app import app as application  # noqa
+from app import app
+application = app.server

You need to rename the flask_app module generated by the wizard to the actual app module that came with your avocado project. Besides that, you must expose the callable WSGI application through the Dash app’s .server field, as described in the official help page on PythonAnywhere. You might as well double-check if the path in your project_home variable is correct.

Finally, save the file by hitting Ctrl+S, go back to the Web tab, and click the green button to reload your web app:

Reload the Web App in PythonAnywhere

When you visit the corresponding URL of your web app deployed to PythonAnywhere, you should see the familiar interface:

Deployed Web App to PythonAnywhere

Avocado Analytics Web App Deployed to PythonAnywhere

That’s it! Note that you never installed Dash or pandas because they were already shipped with PythonAnywhere. Also, you didn’t have to configure static resources, which are typically served by the web server rather than Flask, because Dash takes care of them automatically.

You can now share your Dash apps with the world by deploying them to PythonAnywhere or other web hosting providers.

Conclusion

Congratulations! You just built, customized, and deployed your first dashboard using Dash. You went from a bare-bones dashboard to a fully interactive one deployed on PythonAnywhere.

With this knowledge, you can use Dash to build analytical applications to share with others. As more companies put more weight on the use of data, knowing how to use Dash will increase your impact in the workplace. What used to be a task only experts could perform, you can now do in an afternoon.

In this tutorial, you’ve learned:

  • How to create a dashboard using Dash
  • How to customize the styling of your Dash application
  • How to make your app interactive by using Dash components
  • What callbacks are and how you can use them to create interactive applications
  • How to deploy your application on PythonAnywhere

Now you’re ready to develop new Dash applications. Find a dataset, think of some exciting visualizations, and build another dashboard!

You can download the source code, data, and resources for the sample applications that you made in this tutorial by clicking the link below:

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Data Visualization Interfaces in Python With Dash

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

В настоящее время Dash можно загрузить, используя диспетчер пакетов Python, с помощью команды pip install dash. Dash распространяется с открытым исходным кодом и под лицензией MIT. На официальном сайте вы сможете ознакомиться с руководством по библиотеке, и на GitHub вы найдёте исходный код.

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

Dash значительно упрощает создание GUI (графических пользовательских интерфейсов) для анализа данных. Вот пример приложения на Dash из 43 строк кода, который связывает выпадающее меню с графиком D3.js. Когда пользователь выбирает значение в выпадающем списке, код динамически экспортирует данные из Google Finance в Pandas DataFrame:

Код Dash является декларативным и реактивным, что упрощает создание сложных приложений, содержащих множество интерактивных элементов. Вот пример с 5 входными данными, 3 — выходными и с перекрёстной фильтрацией. Это приложение было написано на Python, и в нём всего лишь 160 строк кода:

Приложение на Dash с несколькими входными и выходными данным.

Для каждого элемента приложения можно задать собственные параметры размера, расположения, цвета и шрифта. Приложения на Dash создаются и публикуются в Сети, поэтому к ним можно применить всё, на что способен CSS. Ниже иллюстрируется пример тонко настраиваемого интерактивного приложения отчётности на Dash, выполненного в стиле отчёта финансовой организации Goldman Sachs.

Тонко настраиваемое приложение Dash, созданное в стиле отчёта финансовой организации Goldman Sachs.

Вам не нужно писать какой-либо код на JavaScript или HTML, когда ваше приложение на Dash запущено в веб-браузере. Dash предоставляет богатый набор интерактивных веб-компонентов.

import dash_core_components as dcc
dcc.Slider(value=4, min=-10, max=20, step=0.5,
           labels={-5: '-5 Degrees', 0: '0', 10: '10 Degrees'})

Пример простого ползунка на Dash

Dash предоставляет простой реактивный декоратор для привязки вашего кода анализа данных к пользовательскому интерфейсу Dash.

@dash_app.callback(Output('graph-id', 'figure'),
                   [Input('slider-id', 'value')])
def your_data_analysis_function(new_slider_value):
    new_figure = your_compute_figure_function(new_slider_value)
    return new_figure

Когда изменяется входной элемент (например, при выборе элемента в выпадающем списке или при передвижении ползунка), декоратор Dash предоставляет вашему коду Python новое входное значение.

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

В качестве примера ниже представлено приложение на Dash, которое обновляет текстовый элемент при взаимодействии с графиком. Код приложения фильтрует данные в Pandas DataFrame на основе выбранной точки:

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

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

Архитектура

Flask и React.js

Приложения на Dash — веб-серверы, которые запускают Flask и связывают пакеты JSON через HTTP-запросы. Интерфейс Dash формирует компоненты, используя React.js.

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

React.js также великолепен, например, мы переписали всю нашу веб-платформу и наш онлайн-редактор диаграмм с помощью React. Но есть кое-что, что действительно радует насчёт React — активный и талантливый состав сообщества разработчиков, который опубликовал тысячи высококачественных компонентов, начиная с выпадающих списков и слайдеров, заканчивая календарями и интерактивными таблицами. И всё это публикуется с открытым исходным кодом!

Dash использует мощь Flask и React, подстраивая их под работу с Python для специалистов по анализу и обработке данных, которые могут не быть экспертами в области веб-разработки.

От React.js к компонентам Python

Компоненты Dash — это классы Python, которые кодируют свойства и значения конкретного компонента React и упорядочиваются как JSON. Dash предоставляет набор инструментов для лёгкой упаковки компонентов React в вид компонентов, которые могут быть использованы в Dash. Этот набор инструментов использует динамическое программирования для автоматического создания классов Python из аннотированного свойства React — propTypes. На выходе классы Python, которые представляют компоненты Dash, являются удобными для пользователя, так как они имеют автоматическую проверку аргументов, строк документации и прочее.

Вот пример динамически сгенерированной проверки ошибочного аргумента:

>>> import dash_core_components as dcc
>>> dcc.Dropdown(valu=3)
Ошибка: неизвестный ключевой аргумент `valu`
Допустимые аргументы: id, className, disabled, multi, options, placeholder, value

Пример динамически создаваемых строк документации:

 >>> help(dcc.Dropdown)
class Dropdown(dash.development.base_component.Component)
 |  Компонент выпадающего списка.
 |  Компонент выпадающего списка служит для выбора одного или более
 |  элементов.
 |  значения и названия элементов выпадающего списка определяются в `options`
 |  свойство и выбранный элемент(ы) определяются свойством `value`.
 |
 |  используйте выпадающий список, только если у вас много вариантов выбора (больше 5), или
 | когда вы ограничены пространством. В противном случае вы можете использовать переключатели или чекбоксы,
 |  Которые покажут сразу все элементы пользователю.
 |
 |  Аргументы ключевых слов:
 |  - id (строка; необязательный)
 |  - className (строка; необязательный)
 |  - disabled (логический тип; необязательный): если true, выбор блокируется
 |  - multi (логический тип; необязательный): если true, пользователь может выбрать несколько значений
 |  - options (список; необязательный)
 |  - placeholder (строка; необязательный): серый текст по умолчанию, если ничего не выбрано
 |  - value (строка | список; необязательный): значение поля ввода. Если `multi` false (по умолчанию),
 |  то value — строка, соответствующая своим значениям,
 |  указанным в свойстве `options`. Если `multi` — true, то
 |  можно выбрать сразу несколько значений, а `value` — 
 |  массив элементов со значениями, соответствующими в свойстве 
 |  `options`.
 |
 |  Доступные события: 'change

Полный набор HTML-тегов (наподобие div, img, table) также обрабатывается с помощью React, а их классы Python доступны через библиотеку dash_html_component. Основной набор интерактивных компонентов, таких как Dropdown, Graph, Slider, будет поддерживаться командой Dash через dash_core_components. Обе библиотеки используют стандартный набор инструментальных средств React-to-Dash с открытым исходным кодом, который вы могли бы использовать при необходимости написания своей собственной библиотеки компонентов.

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

Многопользовательские приложения

Свойства приложения на Dash хранятся в интерфейсе (в браузере). Это позволяет использовать приложения, написанные с использованием Dash, в многопользовательском режиме: может быть открыто несколько независимых друг от друга сессий, в которых действия одних пользователей не будут влиять на данные других пользователей. Код приложения на Dash является функциональным: он может считывать значения из глобальных свойств Python, но не может вносить в них изменения. Этот функциональный подход можно легко обосновать и протестировать — это просто входные и выходные данные без каких-либо побочных эффектов или свойств.

CSS и стили

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

Визуализация данных

Библиотека Dash поставляется с компонентом Graph, который отвечает за отображение диаграмм с помощью Plotly.js. Библиотека Plotly.js отлично подходит к Dash (отличное дополнение), так как она декларативна и имеет открытый исходный код. Кроме того, она поддерживает полный спектр научных, финансовых и деловых диаграмм. Она создана на основе D3.js (для диаграмм типографического качества и экспорта векторных изображений) и WebGL (для высокопроизводительной визуализации).

В библиотеке Dash элемент Graph использует тот же синтаксис, что и библиотека Plotly.py с открытым исходным кодом, что даёт вам возможность легко переключаться между ними. Компонент Graph подключается к системе событий Plotly.js, позволяя авторам писать приложения, которые реагируют на наведение курсора, щелчки и выбор определённых точек на графиках Plotly.

Репозитории с открытым исходным кодом

  • бэкенд библиотеки Dash;
  • фронтенд библиотеки Dash;
  • библиотека основных компонентов Dash;
  • библиотека HTML-компонентов Dash;
  • набор инструментальных средств React-to-Dash;
  • документация и руководство по Dash;
  • Plotly.js —JavaScript- библиотека, используемая Dash.

Прототипирование

Dash — это новая библиотека в среде Python, однако концепции и идеи, на которых строится Dash, существуют в течение десятилетий на разных языках и в разных приложениях.

Если вы разбираетесь в Excel, значит, вам будет проще разобраться и в Dash. Ведь они оба используют «реактивную» модель программирования. В Excel ячейки с выходными данными обновляются автоматически при изменении параметров ячеек с входными данными. Любая ячейка может быть входной или выходной или и тем, и другим. В ячейках с входными данными нет информации о том, какие ячейки с выходными данными зависят от них, что упрощает добавление новых ячеек с выходными данными или позволяет связать несколько ячеек. Вот пример Excel-приложения:

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

app.layout = html.Div([
    html.Label('Hours per Day'),
    dcc.Slider(id='hours', value=5, min=0, max=24, step=1),
    html.Label('Rate'),
    dcc.Input(id='rate', value=2, type='number'),
    html.Label('Amount per Day'),
    html.Div(id='amount'),
    html.Label('Amount per Week'),
    html.Div(id='amount-per-week')
])
@app.callback(Output('amount', 'children'),
              [Input('hours', 'value'), Input('rate', 'value')])
def compute_amount(hours, rate):
    return float(hours) * float(rate)
@app.callback(Output('amount-per-week', 'children'),
              [Input('amount', 'children')])
def compute_amount(amount):
    return float(amount) * 7

Некоторым разработчикам нравится этот пример, потому что Excel по-прежнему занимает доминирующее положение даже в технических вычислениях и в финансовой математике. Я не думаю, что доминирующее положение Excel — это технический вопрос. В конце концов, есть легионы программистов, которые изучили нюансы Excel, VBA и даже SQL.

Более того, таблицы Excel легче распространять, чем программы на Python, а ячейки Excel легче редактировать, чем аргументы командной строки.

Тем не менее, моделирование в Excel имеет известные ограничения: эти таблицы часто становятся слишком большими или уязвимыми, чтобы переводить их на производственный уровень, проводить экспертную оценку или тестировать и поддерживать. Вам ведь знаком случай со знаменитой опечаткой в 2013 году?

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

Фреймворк Shiny

Если вы программируете на R, вам повезло. Shiny — это реактивный фреймворк для создания веб-приложений на чистом R, и это отлично! Вы даже можете создавать интерактивные графики с библиотекой Shiny или Plotly для R. Dash и Shiny похожи, но Dash не стремится быть копией Shiny, так как философии Python и R достаточно различаются, что приводит к необходимости использования разного синтаксиса.

Интерактивное веб-приложение, созданное с помощью Shiny на языке R.

Структурирование данных с MATLAB

Если вы программируете на MATLAB, то вам, возможно, знакома GUIDE — библиотека пользовательского интерфейса для MATLAB. Компания Mathworks была одной из новаторов в области технических вычислений. GUIDE была написана в далёком 2004 году.

Приложение, созданное с помощью библиотеки GUIDE на MATLAB.

Если ваши данные структурированы в базе данных, вы могли бы использовать Tableau или любой другой BI-инструмент. Tableau — восхитительный инструмент. Компания установила новый вектор развития в своей отрасли, согласно которому, у конечного пользователя должна быть автономия, чтобы он мог иметь возможность исследовать данные внутри своей организации. Компания также помогла сделать популярнее концепции детализации данных и перекрёстной фильтрации.

Перекрёстная фильтрация в Tableau.

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

Виджеты Jupyter

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

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

Команде разработчиков Dash также очень нравится проект nteract, который действительно снижает порог вхождения в Python и Jupyter Notebook, позволяя упаковать Jupyter Notebook в виде настольного приложения.

Лицензирование и бизнес-модель с открытым исходным кодом

Стартап поддерживает библиотеки с открытым исходным кодом для Python, R и MATLAB, которые взаимодействуют с plotly.js. Компания также поддерживает веб-приложение для создания диаграмм и подключения их к базам данных (стыковочные библиотеки также распространяются с открытым исходным кодом).

Если вы используете локальную версию с открытым исходным кодом, в таком случае ограничений нет. Вы можете управлять развёртыванием Dash-приложений самостоятельно через платформы вроде Heroku или Digital Ocean.

Если вы ищите вдохновение для создания своих пользовательских интерфейсов в области технических вычислений, рекомендуем прочитать статью Брета Виктора

Вам также может понравиться проект Explorable Explanations, который специализируется на интерактивном обучении.

Перевод статьи «Create Reactive Web Apps in pure Python»

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

Если речь идет о данных, значит, это касается и Python. В частности, мы говорим о библиотеке Dash, которая построена на базе одной из самых популярных библиотек для построения графиков — Plotly.

Dash позволяет легко создавать и делиться результатами анализа данных с помощью интерактивных дашбордов, используя только код Python. Нет необходимости изучать HTML, CSS или сложные JavaScript-фреймворки, такие как React.js.

В этом руководстве вы получите представление о том, на что способен Dash, и как интегрировать его в свой рабочий процесс.

Установка Dash и Plotly

Вы можете установить Dash с помощью pip. Также необходимо инсталлировать библиотеку pandas для работы с наборами данных:

pip install dash pandas

Приведенная выше команда также выполняет установку plotly. Plotly известен своими интерактивными графиками, и Plotly и Dash созданы Plotly Software Foundation, поэтому библиотеки довольно хорошо работают вместе.

Требования по использованию Dash

У такого мощного фреймворка, как Dash, есть несколько требований. Во-первых, вы должны знать Plotly Python, поскольку Dash может отображать только интерактивные графики Plotly.

Далее, вам необходимо базовое понимание HTML и CSS. Dash похож на React, но на языке Python. Это шаблонный фреймворк, на котором вы можете создать сайт без JavaScript.

Дашборд содержит множество графики, и пользователю самому решать, как отобразить все на одной странице. Plotly обрабатывает изображения, но аспект компоновки зависит от Dash и его HTML-компонентов.

Создание Dash-приложения 

Давайте создадим наше Dash-приложение. После инсталляции импортируем следующие библиотеки:

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pd

dash – это глобальная библиотека, содержащая все основные функции. dash_core_components и dash_html_components – это библиотеки, которые устанавливаются с dash по умолчанию. Они включают специфические для Dash фичи и Python-представление HTML-компонентов (тегов). Подробнее о них позже.

Любое приложение Dash запускается с помощью следующей команды:

app = dash.Dash(name="my_first_dash_app")

>>> app
<dash.dash.Dash at 0x1ee6af51af0>

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

Сначала мы загрузим встроенный датасет из Plotly и создадим простую диаграмму рассеяния:

# Load dataset using Plotly
tips = px.data.tips()

fig = px.scatter(tips, x="total_bill", y="tip") # Create a scatterplot

Затем мы добавляем это изображение в атрибут layout нашего приложения внутри тега div с небольшими текстами:

app.layout = html.Div(children=[
   html.H1(children='Hello Dash'),  # Create a title with H1 tag

   html.Div(children='''
       Dash: A web application framework for your data.
   '''),  # Display some text

   dcc.Graph(
       id='example-graph',
       figure=fig
   )  # Display the Plotly figure
])

if __name__ == '__main__':
   app.run_server(debug=True) # Run the Dash app

Теперь мы создаем HTML-теги с помощью библиотеки dash_html_components (html), а изображение – используя основные компоненты (dcc) library .

За тегом заголовка H1 следует div, содержащий обычный текст, затем сам график с использованием функции Graph библиотеки dcc. Все это находится внутри атрибута children одного тега DIV.

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

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pd

# Create the app
app = dash.Dash(__name__)

# Load dataset using Plotly
tips = px.data.tips()

fig = px.scatter(tips, x="total_bill", y="tip") # Create a scatterplot

app.layout = html.Div(children=[
   html.H1(children='Hello Dash'),  # Create a title with H1 tag

   html.Div(children='''
       Dash: A web application framework for your data.
   '''),  # Display some text

   dcc.Graph(
       id='example-graph',
       figure=fig
   )  # Display the Plotly figure
])

if __name__ == '__main__':
   app.run_server(debug=True) # Run the Dash app

Поместите его в скрипт Python и запустите. В терминале вы получите сообщение о том, что необходимо перейти по этой ссылке: http://127.0.0.1:8050/.

Итак, поехали:

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


Создание app.layout

Давайте начнем с атрибута layout. Это единственный атрибут, который содержит все ваши HTML-компоненты и изображения. Вы должны передать ему всю вашу графику и HTML-теги в конечном теге DIV.

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

Например, вот как будет выглядеть приведенное выше приложение:

app = dash.Dash(name="app")

# Load dataset using Plotly
tips = px.data.tips()

fig = px.scatter(tips, x="total_bill", y="tip")  # Create a scatterplot

title = html.H1("Hello Dash!")
text_div = html.Div("Dash: A web application framework for your data.")
graph_to_display = dcc.Graph(id="scatter", figure=fig)

app.layout = html.Div(children=[title, text_div, graph_to_display])

Это гораздо аккуратнее и компактнее, и это одна из тех вещей, о которых вы не узнаете в документации Dash. Там куча вложенного кода вместо того, чтобы сделать вышеописанное.

Компоненты HTML и CSS в Dash

Давайте обсудим, как HTML и CSS работают в Dash. Подбиблиотека dash_html_components содержит наиболее распространенные теги HTML, такие как divs, кнопки, текстовые поля, названия, теги заголовков (H1-6) и т. д.

Они реализованы в коде Python под соответствующими именами как представление их HTML-аналогов. Итак, код, подобный приведенному, ниже:

import dash_html_components as html

html.Div([
   html.H1('Hello Dash'),
   html.Div([
       html.P('Dash converts Python classes into HTML'),
       html.P("This conversion happens behind the scenes by Dash's JavaScript frontend")
   ])
])

Будет интерпретироваться вашим браузером следующим образом:

<div>
   <h1>Hello Dash</h1>
   <div>
       <p>Dash converts Python classes into HTML</p>
       <p>This conversion happens behind the scenes by Dash's JavaScript front-end</p>
   </div>
</div>

Все HTML-теги этой подбиблиотеки содержат следующие основные аргументы:

  • id: то же, что и атрибут id HTML-тегов

  • className: то же самое, что и атрибут class тегов HTML

  • style: такой же, как атрибут style тегов HTML, но принимает только словарь стилей CSS

  • children: первый аргумент большинства компонентов HTML

Вот пример div с некоторыми кастомизированными настройками:

app = dash.Dash(name="app")

app.layout = html.Div(
   children=html.H3("Simple Div"),
   id="sample_div",
   className="red_div",
   style={"backgroundColor": "red"},
)

if __name__ == "__main__":
   app.run_server(debug=True)

Аргумент children уникален — он может принимать числа и строки, но чаще всего вы передаете другие компоненты HTML списком, если это тег-контейнер, например такой как div.

Замечание о стилях CSS: большинство атрибутов стиля CSS используют дефисы для разделения слов. Когда вы передаете их в аргумент style Dash, они должны применять camelCase, например backgroundColor вместо background-color.

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

Основные компоненты Dash

Другой важной частью Dash являются его основные компоненты. Библиотека dash_core_components содержит несколько других HTML-тегов, но в нее уже встроено немного CSS и JavaScript.

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

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

import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash(__name__)

app.layout = html.Div([
   dcc.Dropdown(
       options=[
           {'label': 'FC Barcelona', 'value': 'FCB'},
           {'label': 'Real Madrid', 'value': 'RM'},
           {'label': 'Manchester United', 'value': 'MU'}
       ],
       value='FCB' # The default value to display
   )
])

Выпадающий список с мультивыбором:

app = dash.Dash(__name__)

app.layout = html.Div([
   dcc.Dropdown(
       options=[
           {'label': 'FC Barcelona', 'value': 'FCB'},
           {'label': 'Real Madrid', 'value': 'RM'},
           {'label': 'Manchester United', 'value': 'MU'}
       ],
       multi=True,
       value="FCB"
   )
], style={"width": 200})

if __name__ == '__main__':
   app.run_server(debug=True)

Слайдер с помеченными точками останова:

import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash(__name__)

app.layout = html.Div([
   dcc.Slider(
       min=0,
       max=9,
       marks={i: 'Label{}'.format(i) for i in range(10)},
       value=5,
   )
])

if __name__ == '__main__':
   app.run_server(debug=True)

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

Полный список основных компонентов можно посмотреть здесь.

Создание финального интерфейса визуализации данных с помощью Python Dash

В качестве завершающего примера посмотрите на приведенное ниже приложение:

import seaborn as sns

app = dash.Dash(__name__)

diamonds = sns.load_dataset("diamonds")

scatter = px.scatter(
   data_frame=diamonds,
   x="price",
   y="carat",
   color="cut",
   title="Carat vs. Price of Diamonds",
   width=600,
   height=400,
)
histogram = px.histogram(
   data_frame=diamonds,
   x="price",
   title="Histogram of Diamond prices",
   width=600,
   height=400,
)
violin = px.violin(
   data_frame=diamonds,
   x="cut",
   y="price",
   title="Violin Plot of Cut vs. Price",
   width=600,
   height=400,
)

left_fig = html.Div(children=dcc.Graph(figure=scatter))
right_fig = html.Div(children=dcc.Graph(figure=histogram))

upper_div = html.Div([left_fig, right_fig], style={"display": "flex"})
central_div = html.Div(
   children=dcc.Graph(figure=violin),
   style={"display": "flex", "justify-content": "center"},
)
app.layout = html.Div([upper_div, central_div])

if __name__ == "__main__":
   app.run_server(debug=True)

Мы импортируем датасет Diamonds из Seaborn и создаем три графика: диаграмму рассеяния, гистограмму и скрипичную диаграмму. Мы хотим отобразить график рассеяния и гистограмму рядом друг с другом и поместить скрипичную диаграмму прямо под ними в центре.

Для этого мы создаем два div’а, содержащих график рассеяния и гистограмму, left_figure и right_figure. Затем для простоты эти два div помещаем в еще один div — upper_div.

Мы устанавливаем CSS-стиль flex-box для этого div, который размещает фигуры бок о бок.

Затем мы создаем центральный div, содержащий скрипичную диаграмму, и выравниваем его по центру с помощью flex-box и его атрибута justify-content.

Наконец, мы помещаем все в лейаут внутри последнего DIV и запускаем скрипт. Вот конечный результат:

Заключение

Вот краткое описание шагов по созданию базового Dash-приложения:

  1. Создайте приложение с помощью dash.Dash и дайте ему любое название.

  2. Набросайте лейаут своих графиков в дашборде, прежде чем писать код.

  3. Создайте графики, которые войдут в ваш дашборд

  4. Создайте шаблонный лейаут с помощью HTML-компонентов Dash

  5. Добавьте свои изображеня в соответствующие контейнеры

  6. Наконец, добавьте все HTML-компоненты к атрибуту лейаута

Несмотря на то, что мы рассмотрели многие базовые аспекты, такие как HTML, основные компоненты и лейаут приложения, это лишь поверхностное знакомство с возможностями Dash.

Я показал вам множество примеров интерактивных HTML-компонентов, но не рассказал, как интегрировать их в ваше приложение. Как вы можете обновлять графики на основе вводимых пользователем данных, таких как слайдеры, текст или что-то подобное?

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

Dash посвящает большой раздел в документации только для объяснения коллбеков, поскольку поначалу в них трудно разобраться. Я предлагаю вам обратиться к этой теме на следующем этапе.

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

Спасибо за прочтение!


В заключение приглашаем на открытое занятие «Работа с Power BI. Построение дашборда. Настройка и взаимодействие визуальных элементов». На уроке загрузим данные в Power BI Desktop из источника, построим небольшой дашборд, посмотрим на назначение и варианты настройки базовых визуальных элементов. Записаться можно по ссылке.

Визуализация данных является неотъемлемой частью Data Science. И сегодня мы поговорим о фреймворке визуализации данных на Python — Dash, который позволяет работать не только с интерактивными графиками, но и выводить их на веб-сайт. Читайте у нас: инициализация приложения, работа с графиками и данными, а также использование обратных вызовов (callback) в Dash.

Что такое Dash

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

  • 3D-изображение мозга человека [1],
  • детектирование объектов на изображении на основе методов глубокого обучения (Deep Learning) [2],
  • диаграммы word embeddings [3] и многое другое.

Установить Dash можно с помощью пакетного менеджера Pip, который дополнительно установит такие зависимости как Plotly и Flask:

pip install dash

Простое Dash-приложение

Напишем свое первое приложение с Dash. Ниже представлен код на Python, который нужно сохранить в отдельном файле и запустить его через терминал. Запускается приложение как обычный Python-файл: python my_file.py. После запуска будет выведен адрес, по которому нужно перейти через браузер.

import dash
import dash_html_components as html


app = dash.Dash('my_first_app')
app.layout = html.H1('Hello')
app.run_server(debug=True)

Это приложение лишь выводит на веб-странице слово Hello. Разберемся с кодом:

  • Инициализация приложение осуществляется через dash.Dash, где мы указываем название приложения;
  • Атрибут layout определяет, что будет на веб-странице. Здесь мы указали, что на странице будет отображён только заголовок h1 (тэг <h1>). Все компоненты HTML находятся в модуле dash_html_components;
  • Запускается приложение через run_server. Мы установили debug=True, чтобы иметь возможность отладить код. В случае если приложение готово для работы на сервере, то, в целях безопасности, флаг следует поставить в значение False.

Добавляем график

Поскольку Dash — веб-фреймворк для визуализации данных, добавим в наше приложение график. В модуле dash_core_components есть класс Graph, который отвечает за работу с диаграммами Plotly. Воспользуемся им, тогда Python-код будет выглядеть следующим образом:

import dash_core_components as dcc

app = dash.Dash('my_first_app')
app.layout = html.Div([
    html.H1('Hello'),
    Graph(
        figure={
            'data': [
                {'x': [1, 2], 'y': [3, 1]}
            ]
        },
    )
])
app.run_server(debug=True)

В тэге <div> будут располагаться заголовок h1 и график Plotly.

Заголовок и график Plotly в Dash

Веб-страница Dash

Добавляем данные

Выше мы составили очень простой линейный график, но в Data Science проектах часто приходится сталкиваться с данными, которые требуют разные виды диаграмм. Сымитируем данные с помощью Python-библиотеки Pandas, а затем построим столбчатую диаграмму с помощью Plotly. Чтобы построить столбчатую диаграмму, воспользуемся функцией bar из модуля plotly.express:

import plotly.express as px
import pandas as pd

df = pd.DataFrame({
    "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
    "Amount": [4, 1, 2, 2, 4, 5],
    "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
})

fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")

Отображение остаётся все тем же: в тэге <div> будет находится заголовок h1 и столбчатая диаграмма, которую мы создали из наших данных:

app.layout = html.Div([
    html.H1('Hello'),

    dcc.Graph(figure=fig)
])

Заголовок и столбчатая диаграмма Plotly в Dash

Веб-страница Dash со столбчатой диаграммой

Обратные вызовы (callbacks)

Главной особенностью Dash являются обратные вызовы (callbacks). В примере выше данные остаются неизменными, но в Data Science данные могут быть самыми разнообразными: показатели могут меняются в зависимости от атрибутов или данные могут обновляться с каждым днем и т.д. С таким видом данных в Dash можно работать, используя лишь специальный декоратор @app.callback, где app — название переменной приложения (во всех примерах выше мы называли её app).
Возьмем для примера датасет от Plotly, который содержит данные об ожидаемой продолжительности жизни в странах в разные периоды. А на веб-странице отобразим график и слайдер. Слайдер будет находиться в пределах минимального и максимального года, и в зависимости от текущего года на слайдере будет меняться соответственно график. Начальное значение слайдера определяется минимальным годом. У графика и слайдера будут уникальные идентификаторы (id), которые пригодятся в дальнейшем. Python-код выглядит следующим образом:

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')

app = dash.Dash('app')
app.layout = html.Div([
    dcc.Graph(id='my-graph'),
    dcc.Slider(
        id='year-slider',
        min=df['year'].min(),
        max=df['year'].max(),
        value=df['year'].min(),
        marks={str(year): str(year) for year in df['year'].unique()},
        step=None
    )
])

Теперь создадим функцию, внутри которой будем определять форму графика (scatter). Эта функция будет соответственно задекорирована:

from dash.dependencies import Input, Output
import plotly.express as px

@app.callback(
    Output('my-graph', 'figure'),
    [Input('year-slider', 'value')])
def update_figure(selected_year):
    filtered_df = df[df.year == selected_year]

    fig = px.scatter(filtered_df, x="gdpPercap", y="lifeExp",
                     size="pop", color="continent", hover_name="country",
                     log_x=True, size_max=55)
    return fig

Отметим основные положения:

  • Декоратор имеет Output (выход), Input (вход) из модуля dash.dependencies. Input принимает входной id и его значение, в нашем случае это id слайдера (year-slider), а значение — это год. Ouput принимает выходной id и его значение, в нашем случае это id графика (my-graph), а значение — сам график;
  • Декорируемая функция может иметь любое название и должна принимать столько аргументов, сколько значений в Input указано. В нашем случае Input имеет входное значение — выбранный на слайдере год;
  • Внутри декорируемой функции выбираются данные с выбранным годом на слайдере и строится график (scatter);
  • Декорируемая функция должна возвращать значение Output, как уже было сказано, это сам график.

В результате, мы получили график, который изменяется в зависимости от выбранного на слайдере года:

Интерактивный график Plotly в Dash

Веб-страница Dash с callback

Подобное использование декоратора, когда в ответ на изменение значения Input изменяется значения Output, называется Реактивным программированием. Его можно сравнить с созданием таблиц в MS Excel — когда значение одной ячейки изменяется, то вслед за ней обновляются зависимые ячейки. Реактивное программирование используется при создании пользовательских интрефейсов. Но имеет ограничение в виде значительной траты вычислительных ресурсов, поскольку нужно всегда следить за состоянием приложения.

В следующей статье поговорим о том, как в Dash работать со множеством Input и Output. А ещё больше подробностей о визуализации данных в Plotly и Dash для решения реальных задач Data Science, вы узнаете на специализированном курсе «VIP: Визуализация данных на языке Python» в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.

Источники

  1. https://dash-gallery.plotly.host/dash-brain-viewer/
  2. https://dash-gallery.plotly.host/self-driving/
  3. https://dash-gallery.plotly.host/dash-word-arithmetic/

Понравилась статья? Поделить с друзьями:
  • Карбофос от луковой мухи инструкция по применению
  • Руководство по регистрации промышленного образца
  • Минсвязи россии руководство
  • Кудесан q10 капли инструкция по применению цена отзывы аналоги
  • Руководство по стойке чпу