Dallas Animal Shelter Analytics Dashboard

Install Libraries

import dash                     # pip install dash
from dash.dependencies import Input, Output, State
from dash import dcc
from dash import html
import plotly.express as px     # pip install plotly==5.2.2
import pandas as pd             # pip install pandas

Get Data

# Data: https://www.dallasopendata.com/Services/Animals-Inventory/qgg6-h4bd
df = pd.read_csv("https://raw.githubusercontent.com/Coding-with-Adam/Dash-by-Plotly/master/Analytic_Web_Apps/Excel_to_Dash_Animal_Shelter/Animals_Inventory.csv")
df["intake_time"] = pd.to_datetime(df["intake_time"])
df["intake_time"] = df["intake_time"].dt.hour
print(df.head())
  Animal_Id animal_type animal_breed Kennel_Number Kennel_Status  Tag_Type  \
0  A1068084        BIRD      CHICKEN        BAY 09     LIVESTOCK       NaN   
1  A1068362         CAT  DOMESTIC SH         IC 11     IMPOUNDED       NaN   
2  A1063396         CAT  DOMESTIC SH      PSCAT 06     AVAILABLE       NaN   
3  A1060859         CAT  DOMESTIC SH        PCC 03     AVAILABLE       NaN   
4  A1060858         CAT  DOMESTIC SH        PCC 03     AVAILABLE       NaN   

  Activity_Number  Activity_Sequence Source_Id  Census_Tract  ...  \
0      A19-173681                  1  P9991184       11701.0  ...   
1      A19-174019                  1  P0884601        2500.0  ...   
2             NaN                  1  P0879085       20500.0  ...   
3      A19-160016                  1  P0765433       20500.0  ...   
4      A19-160016                  1  P0765433       20500.0  ...   

      chip_status     Animal_Origin Additional_Information animal_stay_days  \
0    SCAN NO CHIP             SWEEP                    NaN                4   
1  UNABLE TO SCAN             FIELD                    NaN                2   
2       SCAN CHIP  OVER THE COUNTER                    NaN               10   
3    SCAN NO CHIP  OVER THE COUNTER                    NaN                6   
4    SCAN NO CHIP  OVER THE COUNTER                    NaN                6   

    Month     Year  Unnamed: 33 Unnamed: 34 Unnamed: 35 Unnamed: 36  
0  FY2017  NEXT_FY          NaN         NaN         NaN         NaN  
1  FY2017  NEXT_FY          NaN         NaN         NaN         NaN  
2  FY2017  NEXT_FY     1.107143         NaN         NaN         NaN  
3  FY2017  NEXT_FY          NaN         NaN         NaN         NaN  
4  FY2017  NEXT_FY          NaN         NaN         NaN         NaN  

[5 rows x 37 columns]

Form and App Layout

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
    html.H1("Analytics Dashboard of Dallas Animal Shelter (Dash Plotly)", style={"textAlign":"center"}),
    html.Hr(),
    html.P("Choose animal of interest:"),
    html.Div(html.Div([
        dcc.Dropdown(id='animal-type', clearable=False,
                     value="DOG",
                     options=[{'label': x, 'value': x} for x in
                              df["animal_type"].unique()]),
    ],className="two columns"),className="row"),

    html.Div(id="output-div", children=[]),
])

Callback and Create Graphs

@app.callback(Output(component_id="output-div", component_property="children"),
              Input(component_id="animal-type", component_property="value"),
)
def make_graphs(animal_chosen):
    # HISTOGRAM
    df_hist = df[df["animal_type"]==animal_chosen]
    fig_hist = px.histogram(df_hist, x="animal_breed")
    fig_hist.update_xaxes(categoryorder="total descending")

    # STRIP CHART
    fig_strip = px.strip(df_hist, x="animal_stay_days", y="intake_type")

    # SUNBURST
    df_sburst = df.dropna(subset=['chip_status'])
    df_sburst = df_sburst[df_sburst["intake_type"].isin(["STRAY", "FOSTER", "OWNER SURRENDER"])]
    fig_sunburst = px.sunburst(df_sburst, path=["animal_type", "intake_type", "chip_status"])

    # Empirical Cumulative Distribution
    df_ecdf = df[df["animal_type"].isin(["DOG","CAT"])]
    fig_ecdf = px.ecdf(df_ecdf, x="animal_stay_days", color="animal_type")

    # LINE CHART
    df_line = df.sort_values(by=["intake_time"], ascending=True)
    df_line = df_line.groupby(
        ["intake_time", "animal_type"]).size().reset_index(name="count")
    fig_line = px.line(df_line, x="intake_time", y="count",
                       color="animal_type", markers=True)
    
    return [
        html.Div([
            html.Div([dcc.Graph(figure=fig_hist)], className="six columns"),
            html.Div([dcc.Graph(figure=fig_strip)], className="six columns"),
        ], className="row"),
        html.H2("All Animals", style={"textAlign":"center"}),
        html.Hr(),
        html.Div([
            html.Div([dcc.Graph(figure=fig_sunburst)], className="six columns"),
            html.Div([dcc.Graph(figure=fig_ecdf)], className="six columns"),
        ], className="row"),
        html.Div([
            html.Div([dcc.Graph(figure=fig_line)], className="twelve columns"),
        ], className="row"),
    ]
if __name__ == '__main__':
    app.run_server(debug=False)
Dash is running on http://127.0.0.1:8050/

 * Serving Flask app "__main__" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:8050/ (Press CTRL+C to quit)
127.0.0.1 - - [11/Nov/2021 08:24:24] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [11/Nov/2021 08:24:24] "GET /_favicon.ico?v=2.0.0 HTTP/1.1" 200 -
127.0.0.1 - - [11/Nov/2021 08:24:24] "GET /_dash-layout HTTP/1.1" 200 -
127.0.0.1 - - [11/Nov/2021 08:24:24] "GET /_dash-dependencies HTTP/1.1" 200 -
127.0.0.1 - - [11/Nov/2021 08:24:24] "GET /_dash-component-suites/dash/dcc/async-dropdown.js HTTP/1.1" 200 -
127.0.0.1 - - [11/Nov/2021 08:24:27] "POST /_dash-update-component HTTP/1.1" 200 -
127.0.0.1 - - [11/Nov/2021 08:24:27] "GET /_dash-component-suites/dash/dcc/async-graph.js HTTP/1.1" 200 -
127.0.0.1 - - [11/Nov/2021 08:24:27] "GET /_dash-component-suites/dash/dcc/async-plotlyjs.js HTTP/1.1" 200 -