In this notebook we develop a simple dashboard with the opioid data obtained from CDC Drug Overdose Dataset (https://wonder.cdc.gov/ucd-icd10.html). The dataset is from the Underlying Cause of Death database contains mortality and population counts for all U.S. counties. Data are based on death certificates for U.S. residents. Each death certificate identifies a single underlying cause of death and demographic data.
From this data we obtained the Drug/Alcohol Induced causes data for 1999-2019 across all counties in US. https://wonder.cdc.gov/wonder/help/ucd.html#Drug/Alcohol%20Induced%20Causes)
python - https://www.python.org/download/releases/3.0/plotly - https://plotly.com/pandas - https://pandas.pydata.org/dash - https://plotly.com/dash/Goal: To have a user interactive dashboard which displays opioid overdose per capita cases by category of drug use.
Sub-Objectives
### importing relevant libraries and setting configuration
%matplotlib inline
#data exploration libraries
import pandas as pd
import numpy as np
#visualization libraries
import matplotlib.pyplot as plt
import plotly as py
import plotly.figure_factory as ff
import plotly.express as px
import chart_studio.plotly as py
from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go
from bubbly.bubbly import bubbleplot
from bokeh.palettes import viridis
colors = viridis(4)
init_notebook_mode(connected=True)
import cufflinks as cf
cf.go_offline()
cf.set_config_file(offline=False, world_readable=True)
# Dashboard libraries
from jupyter_dash import JupyterDash
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_table
import math
import glob
# Set some Pandas options
pd.set_option('display.notebook_repr_html', False)
pd.set_option('display.max_columns', 20)
pd.set_option('display.max_rows', 50)
Read data in
f = glob.glob('../data/Drug_Overdose_Mortality/by_category/by_years/*.txt')
dfs = []
for each in f:
df_temp = pd.read_csv(each, delimiter='\t',dtype={'County Code': str})
dfs.append(df_temp)
df = pd.concat(dfs)
df['State'] = df['County'].str[-2:]
df['Norm_Deaths'] = ((df['Deaths']/df['Population'])*100000).apply(np.ceil)
df.head(5)
Notes County County Code Year Year Code \
0 NaN Baldwin County, AL 01003 2014 2014
1 NaN Baldwin County, AL 01003 2015 2015
2 NaN Baldwin County, AL 01003 2016 2016
3 NaN Baldwin County, AL 01003 2017 2017
4 NaN Baldwin County, AL 01003 2018 2018
Drug/Alcohol Induced Cause \
0 Drug poisonings (overdose) Unintentional (X40-...
1 Drug poisonings (overdose) Unintentional (X40-...
2 Drug poisonings (overdose) Unintentional (X40-...
3 Drug poisonings (overdose) Unintentional (X40-...
4 Drug poisonings (overdose) Unintentional (X40-...
Drug/Alcohol Induced Cause Code Deaths Population Crude Rate State \
0 D1 32 200111 16.0 AL
1 D1 40 203709 19.6 AL
2 D1 25 208563 12.0 AL
3 D1 23 212628 10.8 AL
4 D1 24 218022 11.0 AL
Norm_Deaths
0 16.0
1 20.0
2 12.0
3 11.0
4 12.0
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
def generate_table(dataframe, max_rows=10):
dataframe = dataframe[['County', 'State', 'County Code', 'Year', 'Drug/Alcohol Induced Cause',
'Deaths', 'Population', 'Crude Rate', 'Norm_Deaths']]
return html.Table([
html.Thead(
html.Tr([html.Th(col) for col in dataframe.columns])
),
html.Tbody([
html.Tr([
html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
]) for i in range(min(len(dataframe), max_rows))
])
])
available_states = df['State'].unique()
available_cause = df['Drug/Alcohol Induced Cause'].unique()
# Create server variable with Flask server object for use with gunicorn
server = app.server
app.layout = html.Div(
[
html.Div([
html.H1(children='Opioid Mortality Dashboard', style={
'textAlign': 'center',
}),
# html.Div(children='''
# A simple dashboard for viewing opioid cases by county for specific states. The dashboard allows you to select State and the category of drug mortality.
# We display a scatter plot where the X axis is the Population and the Y axis is the number of cases per 100K population. The size of the bubble is the unemployment rate.
# '''),
html.Div(html.P(['''A simple dashboard for viewing opioid cases by county for specific states.
The dashboard allows you to select State and the category of drug mortality.
We display a scatter plot where the X axis is the Population and the Y axis is the number of cases per 100K population.
The size of the bubble is the unemployment rate.
''', html.Br(),html.Br(),html.Br(),]
))
]),
html.Div(children=[
dcc.Dropdown(
id='xaxis-column',
options=[{'label': i, 'value': i} for i in available_states],
value='NC'
),
dcc.RadioItems(
id='xaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
],
style={'width': '48%', 'display': 'inline-block'}),
html.Div(children=[
dcc.Dropdown(
id='yaxis-column',
options=[{'label': i, 'value': i} for i in available_cause],
value='Drug poisonings (overdose) Unintentional (X40-X44)'
),
dcc.RadioItems(
id='yaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
),
],style={'width': '48%', 'float': 'right', 'display': 'inline-block'}),
dcc.Slider(
id='year--slider',
min=df['Year'].min(),
max=df['Year'].max(),
value=df['Year'].max(),
marks={str(year): str(year) for year in df['Year'].unique()},
step=None
),
html.H5(children='Graph', style={
'textAlign': 'center',
}),
dcc.Graph(id='indicator-graphic',
figure={
'layout':{
'title':'Scatter Plot Opioid Mortality by State',
'xaxis':{
'title':'Population'
},
'yaxis':{
'title':'Number of opioid deaths per 100K population'
}
}
}
),
html.H5(children='Data Table', style={
'textAlign': 'center',
}),
dash_table.DataTable(
id='table',
data=df.to_dict('records'),
sort_action="native",
sort_mode="multi",
page_action="native",
page_current= 0,
page_size= 10,
columns=[{"name": i, "id": i} for i in df.columns],
style_cell_conditional=[
{
'if': {'column_id': c},
'textAlign': 'left'
} for c in ['Date', 'Region']
],
style_data_conditional=[
{
'if': {'row_index': 'odd'},
'backgroundColor': 'rgb(248, 248, 248)'
}
],
style_header={
'backgroundColor': 'rgb(230, 230, 230)',
'fontWeight': 'bold'
}
),
# html.Div(children=[
# html.H4(children='Opoid Mortality Data by State'),
# generate_table(df)
# ])
])
@app.callback(
Output('indicator-graphic', 'figure'),
Input('xaxis-column', 'value'),
Input('yaxis-column', 'value'),
Input('xaxis-type', 'value'),
Input('yaxis-type', 'value'),
Input('year--slider', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
dff = df[(df['Year'] == year_value) & (df['State']==xaxis_column_name) & (df['Drug/Alcohol Induced Cause'] == yaxis_column_name) ]
fig = px.scatter(dff,
x=dff['Population'],
y=dff['Norm_Deaths'],
color=dff["State"],
hover_name=dff["County"],
size=dff["Deaths"],
labels={
"State": "State Code",
"Year Code": "Years",
"Population": "Population (Log Normalized)",
"Norm_Deaths": "Number of opioid deaths per 100K population",
},
color_discrete_sequence=["#FDAB5A","#5283AD"],
)
# fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
# y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
# hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
fig.update_layout({
'yaxis' : {
'showgrid': True,
'showline': True
},
'plot_bgcolor': 'rgba(0, 0, 0, 0)',
'paper_bgcolor': 'rgba(0, 0, 0, 0)',
'xaxis_title': 'Population',
'yaxis_title': 'Number of opioid deaths per 100K population'
})
fig.update_xaxes(#showline=True, linecolor='black',gridcolor='grey',
type='linear' if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(#showline=True, linecolor='black',gridcolor='grey',
type='linear' if yaxis_type == 'Linear' else 'log')
return fig
@app.callback(
Output('table', 'data'),
Input('xaxis-column', 'value'),
Input('yaxis-column', 'value'),
Input('xaxis-type', 'value'),
Input('yaxis-type', 'value'),
Input('year--slider', 'value'))
def update_table(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
tmp_df = dff = df[(df['Year'] == year_value) & (df['State']==xaxis_column_name) & (df['Drug/Alcohol Induced Cause'] == yaxis_column_name)]
return tmp_df.to_dict('records')
Once we run the code above, a dashboard is created with the link below. The dashboard is running on your local computer and is not accessible to the internet. We use a browser to view it.
app.run_server(port=8052)
Dash app running on http://127.0.0.1:8052/
Dashboard screenshot
