Быстрые дашборды на Python с помощью DashExpress
Сегодня дашборды используются повсеместно: от быстрых отчетов «на лету» до демонстрации возможностей AI.
Существует множество замечательных продуктов для создания дашбордов: PowerBI, Tableu, Apache SuperSet и много других со своими плюсами и минусами. Однако все они являются отдельными приложениями в которые нужно дополнительно загружать данные, что часто бывает неудобно, если нужно быстро проанализировать датасет. И, если мы говорим про аналитиков, как правило все используют Python для анализа и ad-hoc исследований.
Для Python существует open source фреймворк построения дашбордов: PLotly Dash. Но те, кто его использовал, знают, насколько сложным может быть создание симпатичного интерфейса, и как скучно писать функции обратного вызова для каждого графика и фильтра. С этим еще можно мириться, если вы делаете дашборд для постоянного длительного использования, но с ad-hoc аналитикой этот вариант не годится.
Осознав проблему, я решил написать обертку над Dash, позволяющую создавать полноценные дашборды в 10–15 строк и запускать их прямо в jupiter-notebook и при этом отказаться от необходимости писать callback-функции. Так родился DashExpress. Документация
Концепция
Библиотека DashExpress в основном предназначена для ускорения разработки приложений Dash, но так же содержит ряд оптимизаций для более быстрой работы.
DashExpress опирается на 4 крупных проекта:
Plotly Dash — для web-части
Pandas — для хранения данных
Dash Mantine — для симпатичного интерфейса
Dash Leaflet — для построения карт
Установка
Библиотека устанавливается стандартно, через pip:
pip install dash-express
Это также установит компоненты Plotly Dash, Pandas, Dash Mantine Components и Dash Leaflet.
Создание DashExpress-приложения
Вот код минимального приложения с одним графиком и тремя фильтрами:
import pandas as pd
import plotly.graph_objects as go
import dash_mantine_components as dmc
from dash_express import DashExpress, Page
# Получаем данные
get_df = lambda: pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')
# Инициализируем приложение
app = DashExpress(logo='DashExpress')
# Создаем страницу дашборда
page = Page(
app=app, # DashExpress app
url_path='/', # Путь страницы
name='Owerview', # Название старницы
get_df=get_df, # Функция получения дашборда
)
# Функция построения графика
def bar_func(df):
pv = pd.pivot_table(df, index='continent', values='lifeExp').reset_index()
fig = go.Figure([go.Bar(x=pv['continent'], y=pv['lifeExp'])])
return fig
# Размечаем макет
page.layout = dmc.SimpleGrid(
page.add_graph(h='calc(100vh - 138px)',render_func=bar_func)
)
# По каким колонкам фильтруем
page.add_autofilter('continent', multi=True)
page.add_autofilter('country', multi=True)
page.add_autofilter('lifeExp', multi=True)
app.run()
Запущенное приложение будет выглядеть так:
Минимальное приложение
Приложение так же можно запустить прямо в jupiter-notebook, только перед этим нужно установить дополнительный пакет:
pip install jupyter-dash
Jupiter
Теперь расскажу подробнее о некоторых шагах и начну с получения данных.
Получение таблицы с данными
В параметр get_df нужно передать функцию получения таблицы. По умолчанию результат функции DashExpress куширует на 1 час.
Если вы исследуете данные в jupiter notebook и у вас уже есть таблица, которую не нужно обновлять, просто передайте ее через функцию:
get_df = lambda: your_df
Если у вас большой датасет используйте оптимизации на стороне pandas:
def get_df():
# Загружаем только нужные колонки
df = pd.read_csv('titanic.csv', usecols=['survived', 'age', 'class', 'who', 'alone'])
# Преобразуем к оптимальным форматам
df.age = df.age.fillna(0)
df = df.astype(
{
'survived': 'int8',
'age': 'int8',
'class': 'category',
'who': 'category',
'alone': 'bool'
}
)
return df
Разметка страницы
Разметьте страницу с помощью grid сетки, и вставьте графики, карты и KPI c помощью методов .add_graph, .add_map и .add_kpi. Рекомендую использовать компаненты dash mantine. (dmc.Grid & dmc.SimpleGrid)
page.layout = dmc.SimpleGrid(
...
)
KPI cards
KPI карточки являются основной частью мониторинга эффективности бизнеса и отслеживания актуальной информации. Любая карта состоит из постоянной части (контейнера) и переменной части (показателя KPI).
Система рендеринга KPI основана на использовании класса KPI, который содержит контейнерное представление и логику расчета показателя. Простейшая реализация KPI с автоматической генерацией функции расчета представлена в классе FastKPI:
app.add_kpi(FastKPI('survived', agg_func=np.mean)
Графики Plotly
Библиотека построения графиков Plotly содержит более 50 типов диаграмм на выбор. Чтобы встроить их в приложение Dash Express, вам нужно ответить на 2 вопроса:
Где находится график
Как построить график
Ответ на первый вопрос закладывается при разработке макета, путем вызова метода page.add_graph (…) в расположении графика, простой пример:
dmc.SimpleGrid(
[
page.add_graph(render_func=bar_chart),
page.add_graph(render_func=line_chart),
],
cols=2
)
На второй вопрос отвечает параметр render_func, представляющий собой функцию принимающую DataFrame, и возвращающую график plotly.
Карты Leaflet
Если вы используете GeoPandas, вы можете добавлять карты на свою панель мониторинга, это так же просто, как добавить график:
dmc.SimpleGrid(
[
page.add_map(geojson_func=None),
],
cols=1
)
geojson_func — должен возвращать geojson для построения карты. Если вам не нужны какие-либо дополнительные преобразования, не указывайте этот параметр, DashExpress все сделает за вас.
def geojson_func(gdf):
gdf = gdf[gdf.geometry.geom_type == 'Polygon']
return gdf.__geo_interface__
Фильтрация
Последним действием является добавление фильтров, которое выполняется простым вызовом метода page.add_filter и указанием столбца фильтрации.
page.add_autofilter('continent', multi=True)
page.add_autofilter('country', multi=True)
page.add_autofilter('lifeExp', multi=True)
Заключение
DashExpress не только быстрое решение, но и гибкое: Вы можете полностью изменить внешний вид интерфейса переопределив класс BaseAppShell. Подробнее об этом расскажу в одной из следующих статей.
Помимо этого Dash Express заботится о повышении производительности вместо вас, вот способы, встроенные по умолчанию:
Использование обратных вызовов на стороне клиента — Большинство обратных вызовов реализовано на стороне клиента, а не на сервере в Python.
Частичные обновления свойств — Функции создания графиков автоматически преобразуются в Patch объекты, обновляя только те части свойства, которые вы хотите изменить.
Кэширование — Dash Express использует библиотеку Flask-Caching, которая сохраняет результаты в базе данных с общей памятью, такой как Redis, или в виде файла в вашей файловой системе.
Сериализация данных с помощью orson — Dash Express использует or json для ускорения сериализации в JSON и, в свою очередь, повышения производительности обратного вызова
Спасибо за прочтение!