0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Краткое руководство по Dash — Python веб-фреймворк для создания дэшбордов

Краткое руководство по Dash — Python веб-фреймворк для создания дэшбордов. Installation + Dash Layout

Сегодня предлагаю погрузиться в один из удобнейших веб-фреймворков в связке c Python под названием Dash. Появился он не так давно, пару лет назад благодаря разработчикам фреймворка plotly. Сам Dash является связкой Flask, React.Js, HTML и CSS.

Выступление Криса Пармера на PLOTCON 2016

Давайте сразу установим фреймворк. Обновленные версии уточняйте тут.

Друзья, если вы действительно хотите разобраться в данном фреймворке, читайте публикации до конца, так как зачастую сначала следуют примеры, а уже после детальный обзор кода. Если вам все равно непонятно — советую читать документацию по 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 , в котором будет содержаться следующее:

И запустим его из текущей директории командой:
$ python app.py

. Running on http://127.0.0.1:8050/ (Press CTRL+C to quit)

Видим, что сервер запустился и готов принимать запросы на порт 8050 (у вас может быть другой порт).

Переходим по адресу http://127.0.0.1:8050/
и видим:

Примечание

На заметку

Dash содержит привычную веб разработчика фичу: hot-reloading . Она активируется в тот момент, когда запускается функция app.run_server(debug=True) . Эта фича обновляет ваш браузер всякий раз, когда вы делаете правки в коде и сохраняете результат. Таким образом нет нужды каждый раз перезапускать сервер.

Как мы помним, Dash содержит компонент для каждого тега HTML. Но также он может принимать все аргументы ключевых слов, как и элементы HTML.

Давайте немного изменим наш код:

Обновляем страницу, и видим:

В этом примере мы изменили стили html.Div и html.H1 с помощью свойства style .

html.H1(‘Hello Dash’, style=<'textAlign': 'center', 'color': '#7FDBFF'>) отрендерится в приложении Dash как:

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

Многоразовые компоненты

Продолжая, представим, что нам нужны некоторые элементы, которые будут меняться, например в зависимости от входных данных пользователя нашего приложения. Для этого в Dash предусмотрены так называемые reusable components . Рассмотрим их на примере таблицы, данные для которой будут загружаться из Pandas dataframe.

Немного о таблицах

Давайте вспомним что из себя представляет таблица в HTML.

HTML таблица определяется тэгом table.

Каждая строка таблица определяется тэгом tr. Хедер таблица определяется тэгом th. A ячейки таблицы заполняются с помощью тэга td.

Получается такая структура:

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

FirstnameLastnameAge
JillSmith50
EveJackson94
JohnDoe80

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

Как мы уже сказали ранее, dash_core_components включает в себя высокоуровнвые элементы. Такие, как: выпадающее меню, графики и прочее.

Вы можете ознакомиться с визуальной стороной этих элементов, каждый из которых сопровождается кодом (очень удобно, вселенский респект разрабочикам из Plot.ly) здесь.

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

Давайте рассмотрим следующий код:

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

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

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

Summary

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

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

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

). Все компоненты HTML находятся в модуле dash_html_components ;
  • Запускается приложение через run_server. Мы установили debug=True , чтобы иметь возможность отладить код. В случае если приложение готово для работы на сервере, то, в целях безопасности, флаг следует поставить в значение False.
  • Добавляем график

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

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

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

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

    Отображение остаётся все тем же: в тэге

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

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

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

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

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

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

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

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

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

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

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

    Сначала мы создаем папку для хранения всех файлов нашего проекта:

    ПРИМЕЧАНИЕ. Я лично предпочитаю работать либо с кодом Visual Studio, либо с Pycharm, но для обеспечения доступности этого пошагового руководства мы будем придерживаться создания этого приложения в терминале.

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

    (venv) должен появиться перед знаком $ в вашем терминале. Это означает, что вы сейчас используете свою виртуальную среду.

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

    Updating dcc.Location pathname #44

    tahouse commented Aug 1, 2017 •

    I’ve followed the updated urls example at https://plot.ly/dash/urls and have found it very helpful in setting up some rudimentary routing. I’d like to take it a bit further and allow changes made within a page to update the pathname.

    From the example:

    where the page-content is changed when the url pathname is updated. This works well, allowing me to set default UI states based on the pathname.

    What I’d like to do is reverse the situation — UI changes update the pathname — on input from one of several internal divs, update the url pathname to reflect the current settings being used. The Link component updates the pathname, but only on click. I’d like the update to be based on graph update callbacks.

    For instance (some made up pathname):

    This doesn’t seem to work however. I don’t get any errors or exceptions; but nothing happens.

    With this, someone could update the the UI widgets, adjust the output graphs, all the changes could reflect in the current path name like:
    http://plot_server:8050/plot/dataset=1/startDate=2017-06-07/endDate=2017-06-09/

    Passing this off to another user (email) would mean quick sharing of the same graph setup parameters, without having to go through tedious setup procedures (click this, wait for component to update, click next component, select multiple items from dropdown box, adjust date range 1, adjust date range 2). This seems to be inline with making dash apps more stateful, except in this case, for the user. To quote from @chriddyp on an unrelated issue:

    I think that in most cases, the Dash app developer shouldn’t need to know the order of events. If they do, it usually means that the component needs to be patched in a way to make it more stateful or the UI needs to be redesigned. I’ll leave this issue open to invite examples of good UIs that are impossible to create without supporting «order-of-events».

    A downside would be the need to prevent some feedback loop: user inputs a pathname, callback takes pathname and updates page content, page content update triggers pathname change, and so on.

    Other than that, I’m not knowledgeable enough with the underlying react js components to try changing them. The dcc.Location component appears simpler than other components that have update methods, but maybe I’m looking in the wrong spot:

    As of right now, the only option I can see for easy setup/testing urls is to generate a text string as described, but output it to some P tag on screen so that a user can copy it and paste it.

    Any ideas, or am I thinking about this all wrong? Thanks for any help and for the great package.

    The text was updated successfully, but these errors were encountered:

    chriddyp commented Aug 1, 2017 •

    Thanks for opening @tahouse ! Yes, this would be great and you’re correct that this isn’t supported right now.

    Dash doesn’t support «synced» components (there is a better name for this but I can’t recall what it is, maybe «two way bindings» would be a better way to describe it.)

    In the case for URLs, we need the following flows:
    URL -> Updates Component
    Component Changes -> Updates URL

    If you think about this in reactive terms like as in an Excel spreadsheet, this would cause an infinite loop — changing the URL updates the component which then updates the URL which then updates the component, etc.

    What Dash should do is update the components and then stop when it detects a loop like this. Or, allow the user to configure the number of loops that it should execute before stopping (like a discrete dynamical system that eventually converges).

    Then, you could write these relationships that depend on each other.

    This actually comes up a lot. If we want to enable users to write and share complex models with «what if» scenarios where every parameter is configurable, then we’ll need to support this. For example, pulling from Bret Victor’s Explorable Explanations (http://worrydream.com/ExplorableExplanations/):

    Suppose that an extra $17 was charged to 85% of vehicle registrations. Park admission would be free for those who paid the charge.
    This would collect an extra $338 million for a total state park budget of $738 million.

    In that case, every word that is in the code syntax would be variable: you could modify any of those parameters and see the rest of the parameters update. This would require a lot of coding — for each component (you’d need to solve for the inverse relationship of that parameter and your UI would have to fix some variables in those inverse relationships), but it might be pretty cool.

    Long story short, I’d like to support this use case. In the meantime, I’m not aware of any other workarounds.

    nkrumm commented Nov 5, 2017

    I would love this feature as well. I’ve tried playing with the internals dcc.Location component, but always ended up with various infinite loops or dead-ends.

    Recently just saw the addition of the state input callback, and thought that this might be a solution to the problem here. but before embarking on attempting that I thought I would circle back here and see if any progress or changes were planned to the URL/location component.

    chriddyp commented Nov 6, 2017

    So, I think that ideally we would allow circular relationships like this:

    With support for previous state (plotly/dash-renderer#25), we would be able to eventually do something like:

    Which might make the code a little bit cleaner (easier to add more items that depend on the current path). However, we still run into the issue of having circular dependencies.

    Spin45 commented Feb 28, 2018 •

    Just adding another vote for this feature.

    I believe this is generally referred to as «deep linking» (at least that’s what Adobe Flex called it) and when applied to a web application is specifically referring to RESTful invocation of an application’s state via its URL with the updating of the URL as the application is used, so as to continue to allow the back button in browsers to work (core usability) and states of the web application to be shared via URL as mentioned above.

    We are hoping to build a general purpose Plotly app for our Earth science data visualization applications that can be launched from our other web applications via RESTful URLS (I believe this already works). However, we want to make our Plotly app powerful and configurable and not being able to preserve the Plotly app’s state after many operations, via an updated URL is an issue.

    chriddyp commented Feb 28, 2018

    I believe this is generally referred to as «deep linking» (at least that’s what Adobe Flex called it) and when applied to a web application is specifically referring to RESTful invocation of an application’s state via its URL with the updating of the URL as the application is used, so as to continue to allow the back button in browsers to work (core usability) and states of the web application to be shared via URL as mentioned above.

    I think that this is actually a separate issue than circular states, in that it’s more of a built-in «save state in url» plotly/dash#188

    syamajala commented May 21, 2018 •

    Any updates on this issue?

    In the latest release, I tried to update a location pathname as the output of callback and ended up stuck in an infinite loop.

    bpostlethwaite commented May 22, 2018

    Hi @syamajala thanks for posting. Better Location handling is on the short-term roadmap but I can’t guarantee any particular dates right now. We’ll update this thread once we have more info

    glennlawyer commented Nov 6, 2018

    Upvoting this. My particular use case is selecting the URL from a dropdown, because we have more pages than fits in a menu bar. I was able to create the infinite loop pretty easily by having the dropdown update the url, and the url update the page (which re-loaded the header with the dropdown).

    Zylatis commented Jul 18, 2019

    Upvoting this as I would like to have a multi-user app where the URL is the user information I need to feed into the callbacks to display the correct things.

    NikTheGeek1 commented Nov 10, 2019

    Upvoting here as well

    simone-pignotti commented Nov 19, 2019

    Upvoting too! Thanks @chriddyp for the thorough explanation.

    usser123 commented Nov 20, 2019

    I have tried to redirect to a specific page after successful login. I encountered problems with both the ways that I tried:

    rsandler00 commented May 18, 2020

    Any updates on this? Would also love to have!

    jimkrooskos commented Jun 23, 2020

    Looking for a solution for this as well!

    codepatel commented Sep 28, 2020 •

    I came across this issue, when I was searching for the same feature in Dash API.
    I agree with @chriddyp on the Circular dependency between the URL.pathname and Layout Elements that are data-triggered and data-driven. I will share a workaround for the very common use-cases in sharing and collaborating Dashboards (also posted on this Medium story: https://medium.com/@hardiksp/how-to-solve-the-dependency-when-sharing-and-collaborating-on-useful-dashboard-apps-with-the-dash-d136a21023e0)

    1. You want the Dashboard author to share with Users the deployed app layout and data analysis presentation (with a custom URL served to get to the «State» of Dashboard) — This is done by parsing the URL and then using the Redis key/value pair as an in-memory data structure store. Key will be a session-based UUID and can optionally also have URL pieces to search for inspection or data-crunching.
    2. Now, users with this «State» of Dashboard will want to make more modifications like what-if scenarios on the inputs that will trigger output callbacks. To save this «new» what-if state of the Dashboard app, there is a user button, that will take the dcc.Store component’s saved output callback values, to save to a Redis data store.
    3. This user-action will generate a new URL shown with a dcc.Link component that can be used to rinse and repeat the cycle.

    Here are the links to:

    Note, I didn’t save all the output callback values in the data cache store in Redis, only the final output states of the data that was important and not output figures or tables. This can be easily done for figures’ state by also saving the dict for fig.data and fig.layout in a dcc.Store component.
    Ideally, if dcc has a future way to make this process more abstracted with all output callbacks in the app layout, rather than the user having to pick and choose output callback to save to a cache, then any Dashboard state can be restored given a unique, custom URL in sharing and collaboration.
    (I used Redis to cache data as I was new to learning it, but a filesystem cache can also be used with the dict key/pair management).

    Please let me know if any feedback on the above approach for suggestions.

    Ссылка на основную публикацию
    Статьи c упоминанием слов:
    Adblock
    detector