Share @ LinkedIn Facebook  panel, matplotlib
How to create interactive dashboard using Python(Matplotlib and Panel)?

How to create a dashboard using Python(Matplotlib and Panel)

Table of Contents

Introduction

Dashboards are quite common nowadays as many people are sharing interactive dashboards to share information. There are lots of options to create dashboards like Tableau, Power BI, d3.js, react.js which are tools or based on javascript which will require an additional learning curve.

But what if you are a python developer and already experienced with python plotting libraries like matplotlib, bokeh, seaborn, plotly, etc who want to create dashboard using only python without learning javascript or new tools?.

We'll help you with that. We have designed this tutorial to get you started with developing a dashboard using matplotlib and a very interesting dashboard creation library Panel. We'll be providing step by step guide about creating a dashboard using only python which will have plots along with widgets to interact. Our tutorials will make use of a scikit-learn breast cancer dataset for plotting graphs. So let's get started without wasting time further.

We'll start with loading libraries like scikit-learn, pandas, numpy.

In [1]:
import sklearn
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer

import matplotlib.pyplot as plt

pd.set_option("display.max_columns", 32)

Loading Breast Cancer Dataset

We'll start loading breast cancer datasets available from scikit-learn. We suggest that you read about the dataset further over here) to get an idea about data. The dataset contains 30 features and a target variable telling whether a tumor is malignant(Class-0) or benign(Class-1). We'll be keeping breast cancer data as a pandas dataframe so that it becomes easily available as well as easy to manipulate.

In [2]:
breast_cancer = load_breast_cancer()

print("Feature Names", breast_cancer.feature_names)
print("Target : ", breast_cancer.target_names)
print("Dataset Size : ", breast_cancer.data.shape)

breast_cancer_df = pd.DataFrame(breast_cancer.data, columns=breast_cancer.feature_names)
breast_cancer_df["Target"] = breast_cancer.target
breast_cancer_df["Target"] = ['Malignant' if typ==0 else 'Benign' for typ in breast_cancer_df["Target"]]
breast_cancer_df.head()
Feature Names ['mean radius' 'mean texture' 'mean perimeter' 'mean area'
 'mean smoothness' 'mean compactness' 'mean concavity'
 'mean concave points' 'mean symmetry' 'mean fractal dimension'
 'radius error' 'texture error' 'perimeter error' 'area error'
 'smoothness error' 'compactness error' 'concavity error'
 'concave points error' 'symmetry error' 'fractal dimension error'
 'worst radius' 'worst texture' 'worst perimeter' 'worst area'
 'worst smoothness' 'worst compactness' 'worst concavity'
 'worst concave points' 'worst symmetry' 'worst fractal dimension']
Target :  ['malignant' 'benign']
Dataset Size :  (569, 30)
Out[2]:
mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry mean fractal dimension radius error texture error perimeter error area error smoothness error compactness error concavity error concave points error symmetry error fractal dimension error worst radius worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension Target
0 17.99 10.38 122.80 1001.0 0.11840 0.27760 0.3001 0.14710 0.2419 0.07871 1.0950 0.9053 8.589 153.40 0.006399 0.04904 0.05373 0.01587 0.03003 0.006193 25.38 17.33 184.60 2019.0 0.1622 0.6656 0.7119 0.2654 0.4601 0.11890 Malignant
1 20.57 17.77 132.90 1326.0 0.08474 0.07864 0.0869 0.07017 0.1812 0.05667 0.5435 0.7339 3.398 74.08 0.005225 0.01308 0.01860 0.01340 0.01389 0.003532 24.99 23.41 158.80 1956.0 0.1238 0.1866 0.2416 0.1860 0.2750 0.08902 Malignant
2 19.69 21.25 130.00 1203.0 0.10960 0.15990 0.1974 0.12790 0.2069 0.05999 0.7456 0.7869 4.585 94.03 0.006150 0.04006 0.03832 0.02058 0.02250 0.004571 23.57 25.53 152.50 1709.0 0.1444 0.4245 0.4504 0.2430 0.3613 0.08758 Malignant
3 11.42 20.38 77.58 386.1 0.14250 0.28390 0.2414 0.10520 0.2597 0.09744 0.4956 1.1560 3.445 27.23 0.009110 0.07458 0.05661 0.01867 0.05963 0.009208 14.91 26.50 98.87 567.7 0.2098 0.8663 0.6869 0.2575 0.6638 0.17300 Malignant
4 20.29 14.34 135.10 1297.0 0.10030 0.13280 0.1980 0.10430 0.1809 0.05883 0.7572 0.7813 5.438 94.44 0.011490 0.02461 0.05688 0.01885 0.01756 0.005115 22.54 16.67 152.20 1575.0 0.1374 0.2050 0.4000 0.1625 0.2364 0.07678 Malignant

1. Creating Individual Plot

We'll be creating the first various types of individual plots first before merging them to create a dashboard.

  • Scatter Plot showing a relationship between 2 features color-encoded by target(malignant/benign).
  • Bar chart showing average feature value by target(malignant/benign).
  • Histogram showing the distribution of a single feature.

1.1 Scatter Plot showing relationship between 2 features color-encoded by target(malignant/benign).

Our first plot consists of a simple scatter plot that shows a relationship between 2 variables. We'll be drawing a scatter plot of mean radius versus mean texture. We have even color-encoded each point according to tumor type(Malignant/Benign).

In [ ]:
with plt.style.context(("seaborn","ggplot")):
    color = {"Malignant" : "tab:red", "Benign":"tab:green"}
    plt.figure(figsize=(10,5))
    for tumor_typ in breast_cancer_df["Target"].unique():
        plt.scatter(breast_cancer_df[breast_cancer_df["Target"]==tumor_typ]["mean radius"],
                    breast_cancer_df[breast_cancer_df["Target"]==tumor_typ]["mean texture"],
                    c=color[tumor_typ],
                    s=200,
                    alpha=0.6,
                    label=tumor_typ)
    plt.xlabel("mean radius")
    plt.ylabel("mean texture")
    plt.title("mean radius vs mean texture Scatter Plot")
    plt.legend(title="Tumor Type",loc="best")

How to create a dashboard using Python(Matplotlib and Panel)

1.2. Bar chart showing average feature value by target(malignant/benign).

Our second plot is easy bar chart. Here we are taking average of mean radius for both tumor types(Malignant & Benign). This will let us know average radius for malignant tumor as well as benign tumor.

In [ ]:
with plt.style.context(("seaborn","ggplot")):
    avg_radius_per_tumor_typ = breast_cancer_df.groupby(by="Target").mean()[["mean radius"]]

    plt.figure(figsize=(5,5))
    plt.bar(avg_radius_per_tumor_typ.index,
            avg_radius_per_tumor_typ["mean radius"],
            color="tab:blue",
            width=0.6)
    plt.ylabel("mean radius")
    plt.title("Average mean radius per tumor type")

How to create a dashboard using Python(Matplotlib and Panel)

1.3. Histogram showing distribution of a single feature.

Our 3rd plot is simple histogram of mean radius which shows distribution of mean radius values.

In [ ]:
with plt.style.context(("seaborn","ggplot")):
    plt.figure(figsize=(6,4))
    plt.hist(breast_cancer_df["mean radius"], color="tab:orange", )
    plt.ylabel("mean radius")
    plt.title("mean radius distribution")

How to create a dashboard using Python(Matplotlib and Panel)

2. Merging Plots into Single Figure

We'll now merge all of individual graph plotted above to create combined figure with all graphs. It's how our dashboard will look in future. Currently it's static but when we'll convert it to dashboard, we'll have widgets which will let us modify graphs.

We are using matplotlib.gridspec to create layout of our figure.

In [6]:
import matplotlib.gridspec as gridspec

def create_figure(plot1_f1,plot1_f2, plot2_f,plot3_f):
    with plt.style.context(("seaborn","ggplot")):
        fig = plt.figure(constrained_layout=True, figsize=(10,8))
        specs = gridspec.GridSpec(ncols=2, nrows=2, figure=fig) ## Declaring 2x2 figure.

        ax1 = fig.add_subplot(specs[0, :]) ## First Row
        ax2 = fig.add_subplot(specs[1, 0]) ## Second Row First Column
        ax3 = fig.add_subplot(specs[1, 1]) ## Second Row Second Colums

        ## First Graph -  Scatter Plot
        color = {"Malignant" : "tab:red", "Benign":"tab:green"}
        for tumor_typ in breast_cancer_df["Target"].unique():
            ax1.scatter(breast_cancer_df[breast_cancer_df["Target"]==tumor_typ][plot1_f1],
                        breast_cancer_df[breast_cancer_df["Target"]==tumor_typ][plot1_f2],
                        c=color[tumor_typ],
                        s=200,
                        alpha=0.6,
                        label=tumor_typ)
        ax1.set_xlabel(plot1_f1)
        ax1.set_ylabel(plot1_f2)
        ax1.set_title("%s vs %s Scatter Plot"%(plot1_f1, plot1_f2))
        ax1.legend(title="Tumor Type",loc="best")

        ## Second Graph - Bar Chart
        avg_radius_per_tumor_typ = breast_cancer_df.groupby(by="Target").mean()[[plot2_f]]
        ax2.bar(avg_radius_per_tumor_typ.index,
                avg_radius_per_tumor_typ[plot2_f],
                color="tab:blue",
                width=0.6)
        ax2.set_ylabel(plot2_f)
        ax2.set_title("Average %s per tumor type"%(plot2_f))

        ## Third Graph - Histogram
        ax3.hist(breast_cancer_df[plot3_f], color="tab:orange", )
        ax3.set_ylabel(plot3_f)
        ax3.set_title("%s distribution"%(plot3_f))

        plt.close(fig)
        return fig
In [ ]:
create_figure("mean radius", "mean texture", "mean radius", "mean radius")

How to create a dashboard using Python(Matplotlib and Panel)

3. Creating First Dashboard

After going through above figure, we can clearly see the limits if we want to do further analysis. It's static figure. It does not let us analyze relationship between various features or see distribution of various features. We'll have to make code changes everytime if we want to analyze different attributes.

But what if we can create dashboard which can let us select attributes and based on that selection display updated graphs?

Thankfully, we have library called panel which makes our work quite easy to create dashboard. We'll start loading panel by importing it and then calling it's extension() method to load it. We'll then create dictionary which will have list of possible values for all parameters of create_figure() method. We can then convert our simple create_figure() function into dashboard by just calling pn.interact() method on it passing dictionary of all possible parameter values.

In [ ]:
import panel as pn
pn.extension()

kw = dict(plot1_f1=breast_cancer.feature_names,
          plot1_f2=breast_cancer.feature_names,
          plot2_f=breast_cancer.feature_names,
          plot3_f=breast_cancer.feature_names)

dash1 = pn.interact(create_figure, **kw)
dash1

How to create a dashboard using Python(Matplotlib and Panel)

We can see above that the pn.interact() method is creating slider for all list of parameter values passed to it and keeping all of them first before graphs. One now change values by sliding sliders and see that graphs are updated as well.

NOTE: Please make a not that interact() method layouts widgets/figures according to it's choice which we can modify according to our needs.

4. Organizing Components of Dashboard

As we are done with the creation of our first dashboard, we would like to dig deep further and look into a dashboard object to understand it further. We'll print dash1 to see what it consist of.

In [9]:
print(dash1)
Column
    [0] Column
        [0] DiscreteSlider(name='plot1_f1', options=['mean radius', ...], value='mean radius')
        [1] DiscreteSlider(name='plot1_f2', options=['mean radius', ...], value='mean radius')
        [2] DiscreteSlider(name='plot2_f', options=['mean radius', ...], value='mean radius')
        [3] DiscreteSlider(name='plot3_f', options=['mean radius', ...], value='mean radius')
    [1] Row
        [0] Matplotlib(Figure, name='interactive00006')

We can clearly see that it consist of a Column component which consists of 2 components(Column & Row). We can see that all widgets which are sliders in our case by default are part of the inner Column and Figure is part of the inner Row object. We can print individual components as well.

In [ ]:
dash1[0]

How to create a dashboard using Python(Matplotlib and Panel)

In [ ]:
dash1[1]

How to create a dashboard using Python(Matplotlib and Panel)

Now that we are well aware of individual components of the dashboard, we'll now try to organize it properly which can improve the look of the dashboard. We'll create a Column object with 3 components (Row, Row & Matplotlib Plot). First Row will hold two parameters responsible for the scatter plot. Second Row will hold parameters for bar chart and histogram.

In [ ]:
dash2 = pn.Column(
                    pn.Row(dash1[0][0],dash1[0][1], align="center"),
                    pn.Row(dash1[0][2],dash1[0][3], align="center"),
                    dash1[1]
                 )
dash2

How to create a dashboard using Python(Matplotlib and Panel)

5. Recreating Dashboard with Individual Components

We can clearly see from the above dashboard that slider widgets are not an ideal choice for a list of string values. interact() function by default is creating sliders for our list of features.

What if we want to modify this and want Dropdown instead of sliders as it would be a better choice for selection?

We can do that using the widgets module of panel. panel provides us with a list of widgets like sliders, radio boxes, checkboxes, integer slider, float slider, etc. We can create widgets according to our choice and link it to the parameter of our method.

In [13]:
import panel.widgets as pnw

We'll start by creating 4 dropdowns each of which will have all features as options. We also have given names to each dropdown about which parameter it belongs to.

In [14]:
f1 = pnw.Select(options=list(breast_cancer.feature_names), name="Scatter Plot Feature 1")
f2 = pnw.Select(options=list(breast_cancer.feature_names), name="Scatter Plot Feature 2")
f3 = pnw.Select(options=list(breast_cancer.feature_names), name="Bar Chart Feature")
f4 = pnw.Select(options=list(breast_cancer.feature_names), name="Histogram Feature")

We can link the above-mentioned widgets to a parameter of our method by creating wrapper function around it and giving it decorator(@pn.depends) as mentioned below. It'll link the value of these widgets with a value of a function.

In [15]:
@pn.depends(f1.param.value,f2.param.value,f3.param.value,f4.param.value)
def create_dash(f1,f2,f3,f4):
    return create_figure(f1,f2,f3,f4)
In [ ]:
heading = "<h3>Breast Cancer Data Analysis Dashboard</h3>\nPlease feel free to try various combinations of parameters to analyze results from different angles"
text = pnw.StaticText(value=heading, align="center")

dash3 = pn.Column(
                    text,
                    pn.Row(f1,f2, align="center"),
                    pn.Row(f3,f4, align="center"),
                    pn.Row(create_dash)
                )
dash3

How to create a dashboard using Python(Matplotlib and Panel)

In [19]:
print(dash3)
Column
    [0] StaticText(align='center', value='<h3>Breast Cancer D...)
    [1] Row(align='center')
        [0] Select(name='Scatter Plot Feature 1', options=['mean radius', ...], value='mean radius')
        [1] Select(name='Scatter Plot Feature 2', options=['mean radius', ...], value='mean radius')
    [2] Row(align='center')
        [0] Select(name='Bar Chart Feature', options=['mean radius', ...], value='mean radius')
        [1] Select(name='Histogram Feature', options=['mean radius', ...], value='mean radius')
    [3] Row
        [0] ParamFunction(function)

We can start the dashboard by calling method show() on dashboard object and it'll start dashboard in a new window on localhost. It accepts arguments like port(integer) and websocket_origin(str of list str which tells us which hosts can connect to the WebSocket).

In [18]:
dash3.show()
Out[18]:
<bokeh.server.server.Server at 0x7fcc329df470>

6. How to keep a server running indefinitely?

We can declare the dashboard object as servable by calling the servable() method on it which will mark it to serve. We can then go to a command line and run a notebook using the panel command which will keep running it indefinitely.

  • panel serve --show dash.ipynb
In [ ]:
dash3.servable()

We can even save the dashboard as html or png files by calling save() method and passing the filename to it.

In [19]:
dash3.save("dash.html")
In [20]:
dash3.save("dash.png")

This concludes our tutorial on creating a dashboard using matplotlib. Our main aim of creating this tutorial was to give people intro on how to create a basic interactive dashboard using matplotlib and panel. This kind of dashboard can be easily deployed using a flask server and made available to everyone on the internet to explore analysis.

References


Sunny Solanki  Sunny Solanki