Building a dashboard with Dash and Plotly

in STEMGeeks2 months ago

Why Dash?

Building a dashboard from scratch can be a tricky step for a data analyst. Our days, tools such as PowerBI and Tableau, can do the work for you, without the need of coding. But of course, those come with a significant price, and if you want to build your own projects it will be expensive. If you have web developer skills, you can build great dashboards using react, ruby, etc. But since I don't master these tools, I was looking for something that I could easily build with Python. Fortunately I was lucky to find the Dash library from Plotly. Which allows to build HTML and CSS components, that can easily be connected to Plotly graphs and other components. Some knowledge of HTML and CSS can speed up the coding, but beside that is all in Python!

Libraries and imports 📚

There are 4 core libraries to build a dashboard, the first one is obviously Dash, then Plotly to build the graphs, dash_core_components to build widgets, such as radio buttons and dropdown menus, and dash_html_components for all kind of HTML components. You can also add bootstrap components with dash_bootstrap_components and many other libraries to prettify your dashboard. Of course all of these components are customizable using HTML and CSS.

#main libraries
import dash
import plotly.graph_objects as go
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

#other libraries
import pandas as pd
import math

The project 📝

I was looking for some funny data, that wouldn't take too much time to clean and would be interesting to show of in a dashboard. Gladly I found the NASA exoplanet archive. It has cool data about exoplanets discovered since 1989. You can easily download the data as a .csv and start working on it:

df = pd.read_csv('planets.csv')

year_options = []
for year in df['disc_year'].sort_values(ascending = True).unique():
    year_options.append({'label':str(year), 'value':year})

yaxis_options = [
    {'label':'Distance (pc)', 'value':df.columns[72]},
    {'label':'Orbital Period', 'value':df.columns[8]},
    {'label':'Orbit Semi-Major Axis', 'value':df.columns[12]},
    {'label':'Planet Radius (Earth Radius)', 'value':df.columns[16]},
    {'label':'Jupyter Radius', 'value':df.columns[20]}]

I used pandas to read the CSV and convert to a Data Frame and I created two set of options to use in two different dropdown menus. One having the years, and another having different parameters for the y axis. Note that when implementing the options they need to follow the following structure:

{'label':<some_text>, 'value':<value_to_callback>}

To launch dash, and build the CSS and HTML structure see the code below:

app = dash.Dash()

app.layout = html.Div([
                html.Div([
                    html.Img(src='https://exoplanetarchive.ipac.caltech.edu/images/nsted_banner.jpg')]),
                html.Div([
                    dcc.Dropdown(
                        id='year-picker', 
                        options = year_options,
                        value = df['disc_year'].max())],
                    style={'width': '48%', 'display':'inline-block'}),
                html.Div([
                    dcc.Dropdown(
                        id='yaxis-picker', 
                        options = yaxis_options,
                        value = df.columns[72])],
                    style={'width': '48%', 'display':'inline-block'}),
                html.Div([
                    dcc.Graph(
                        id = 'graph')],
                    style= {'padding':20})
                    ])

The structure is composed of an image, two dropdown menus in the same row and a graph where the plots will be displayed.

It's now time for the interaction, and for that you need a decorator and a callback function:

@app.callback(
    Output('graph', 'figure'),
    [Input('year-picker', 'value'), 
    Input('yaxis-picker', 'value')]
)

This decorator is positioned just above the function, and it's used to define the Inputs and Outputs. The arguments for both are component_id and component_property.

Finally, the interaction is all expressed in the functions that follows the decorator:

def update_figure(selected_year, selected_yaxis):
    filt_df = df[df['disc_year'] == selected_year]
    traces = []
    for method in filt_df['discoverymethod'].unique():
        df_by_method = filt_df[filt_df['discoverymethod'] == method]
        traces.append(go.Scatter(
                    x=df_by_method['pl_name'],
                    y=df_by_method[selected_yaxis],
                    marker=dict(
                        size=df_by_method['pl_bmasse'].apply(\
                lambda x: 0 if math.isnan(x) else x),
                        sizeref = 80),
                    text=df_by_method['pl_bmasse'].apply(\
                lambda x: 0 if math.isnan(x) else x),
                    mode = 'markers',
                    meta = dict_yaxis[selected_yaxis],
                    hovertemplate=\
                        '<br>Planet: %{x}<br>%{meta}: %{y}<br>Mass: %{text}<br>',
                    name = method))

    return {
        'data': traces, 
        'layout':go.Layout(
            title= f"Exoplanets discovered in {selected_year}",
            xaxis = {
                'showgrid': False,
                'title': dict(text = 'Planets'),
                'title_standoff':40,
                'nticks':30
                },
            yaxis = {
                'title': dict_yaxis[selected_yaxis], 
                'showgrid': False,
                'automargin': True,
                'rangemode':'tozero'},
            legend_title_text='Discovery Method')}

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

The output 📊

The graphs are scatter plots, the y axis is interactive and so as the year. The exoplanets on the x axis, the colours are the discovery method, and the size of the bubbles is the mass of the planet, that is only displayed when hovering the planets.

image.png

image.png

image.png

Final thoughts

This dashboard can easily be implemented in a website or in a Heroku application afterwards.

Dash is extremely powerful, and definitely a great solution for the ones who don't have the means to use PowerBI or Tableau, but have a slight knowledge of python and web dev programming languages.

For the full code, and more projects, please feel free to visit my GitHub:

🤖 https://github.com/macrodrigues 🤖

Sort:  

Congratulations @macrodrigues! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You received more than 700 upvotes.
Your next target is to reach 800 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Support the HiveBuzz project. Vote for our proposal!

Dear @macrodrigues, we need your help!

The Hivebuzz proposal already got important support from the community. However, it lost its funding a few days ago and only needs a few more HP to get funded again.

May we ask you to support it so our team can continue its work this year?
You can do it on Peakd, ecency, Hive.blog or using HiveSigner.
https://peakd.com/me/proposals/199

Your support would be really helpful and you could make a difference.
Thank you!