Quick Python Dashboards with DashExpress

Today, dashboards are used everywhere: from quick on-the-fly reports to demonstrating AI capabilities.

There are many great dashboarding products out there: PowerBI, Tableu, Apache SuperSet, and many others with their pros and cons. However, all of them are separate applications in which you need to additionally load data, which is often inconvenient if you need to quickly analyze the dataset. And, if we are talking about analysts, as a rule, everyone uses Python for analysis and ad-hoc research.

For Python, there is an open source framework for building dashboards: PLotly Dash. But those who have used it know how difficult it can be to create a pretty interface, and how boring it is to write callback functions for each plot and filter. You can still put up with this if you are making a dashboard for permanent long-term use, but this option is not suitable for ad-hoc analytics.

Realizing the problem, I decided to write a wrapper over Dash that allows you to create full-fledged dashboards in 10-15 lines and run them directly in jupiter-notebook and at the same time give up the need to write callback functions. Thus, Dash Express was born. Documentation

Concept

The DashExpress library is primarily designed to speed up the development of Dash applications, but also contains a number of optimizations for faster performance.

DashExpress relies on 4 major projects:

  1. Plotly Dash – for the web part

  2. Pandas – for data storage

  3. Dash Mantine – for a pretty interface

  4. Dash Leaflet – for building maps

Installation

The library is installed as standard, via pip:

pip install dash-express

This will also install Plotly Dash, Pandas, Dash Mantine Components and Dash Leaflet.

Creating a DashExpress Application

Here is the code for a minimal application with one graph and three filters:

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()

The running application will look like this:

Minimum Application

Minimum Application

The application can also be run directly in jupiter-notebook, but before that you need to install an additional package:

pip install jupyter-dash
Jupiter

Jupiter

Now I’ll go into more detail about some of the steps and start by getting the data.

Getting a table with data

In the get_df parameter, you need to pass the function to get the table. By default, the result of the DashExpress function caches for 1 hour.
If you are exploring data in jupiter notebook and you already have a table that doesn’t need to be updated, just pass it through a function:

get_df = lambda: your_df

If you have a large dataset, use optimizations on the pandas side:

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

Page layout

Layout the page with a grid, and insert graphs, maps, and KPIs using the .add_graph, .add_map, and .add_kpi methods. I recommend using dash mantine components. (dmc.Grid & dmc.SimpleGrid)

page.layout = dmc.SimpleGrid(
    ...
    )

KPI cards

KPI cards are the main part of business performance monitoring and tracking of up-to-date information. Any map consists of a constant part (container) and a variable part (KPI indicator).

The KPI rendering system is based on the use of the KPI class, which contains the container representation and logic for calculating the indicator. The simplest implementation of KPI with automatic generation of the calculation function is presented in the FastKPI class:

app.add_kpi(FastKPI('survived', agg_func=np.mean)

Plotly Charts

The Plotly plotting library contains over 50 chart types to choose from. To embed them in the Dash Express app, you need to answer 2 questions:

  1. Where is the chart

  2. How to build a graph

The answer to the first question is laid down when designing the layout, by calling the page.add_graph(…) method on the graph location, a simple example:

dmc.SimpleGrid(
    [
        page.add_graph(render_func=bar_chart),
        page.add_graph(render_func=line_chart),
    ],
    cols=2
    )

The second question is answered by the render_func parameter, which is a function that takes a DataFrame and returns a plotly plot.

Leaflet cards

If you are using GeoPandas you can add maps to your dashboard, it’s as easy as adding a graph:

dmc.SimpleGrid(
    [
        page.add_map(geojson_func=None),
    ],
    cols=1
    )

geojson_func – must return geojson to build a map. If you do not need any additional conversions, do not specify this parameter, DashExpress will do everything for you.

def geojson_func(gdf):
    gdf = gdf[gdf.geometry.geom_type == 'Polygon']
    return gdf.__geo_interface__

Filtration

The last step is to add filters, which is done by simply calling the page.add_filter method and specifying the filter column.

page.add_autofilter('continent', multi=True)
page.add_autofilter('country', multi=True)
page.add_autofilter('lifeExp', multi=True)

Conclusion

DashExpress is not only fast, but also flexible: You can completely change the appearance of the interface by overriding the BaseAppShell class. I will tell you more about this in one of the following articles.

Other than that, Dash Express takes care of performance for you, here are the default methods:

  1. Using callbacks on the client side – Most callbacks are implemented on the client side, not on the server side in Python.

  2. Partial property updates – Plotting functions are automatically converted to Patch objects, updating only those parts of the property that you want to change.

  3. caching – Dash Express uses the Flask-Caching library, which stores the results in a shared memory database like Redis or as a file on your file system.

  4. Serializing data with orson – Dash Express uses or json to speed up JSON serialization and in turn improve callback performance

Thanks for reading!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *