diff --git a/apps/dash-medical-provider-charges/.gitignore b/apps/dash-medical-provider-charges/.gitignore
deleted file mode 100644
index 943677076..000000000
--- a/apps/dash-medical-provider-charges/.gitignore
+++ /dev/null
@@ -1,109 +0,0 @@
-# Created by .ignore support plugin (hsz.mobi)
-### Python template
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-.hypothesis/
-.pytest_cache/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# pyenv
-.python-version
-
-# celery beat schedule file
-celerybeat-schedule
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-
-./idea/
-
diff --git a/apps/dash-medical-provider-charges/README.md b/apps/dash-medical-provider-charges/README.md
index f16f94e92..e823b604d 100644
--- a/apps/dash-medical-provider-charges/README.md
+++ b/apps/dash-medical-provider-charges/README.md
@@ -46,7 +46,7 @@ Select state, cost metric and region to visualize average charges or payments(fo
## Screenshot
-
+
## Resources
* [Dash](https://dash.plot.ly/)
diff --git a/apps/dash-medical-provider-charges/app.py b/apps/dash-medical-provider-charges/app.py
index 00b474e9c..789a760aa 100644
--- a/apps/dash-medical-provider-charges/app.py
+++ b/apps/dash-medical-provider-charges/app.py
@@ -1,14 +1,35 @@
import dash
-import dash_table
-import dash_core_components as dcc
-import dash_html_components as html
+from dash import (
+ html,
+ Input,
+ Output,
+ State,
+ callback,
+)
import plotly.graph_objs as go
-from dash.dependencies import State, Input, Output
-from dash.exceptions import PreventUpdate
+
import pandas as pd
import os
+from utils.helper_functions import (
+ generate_aggregation,
+ get_lat_lon_add,
+ region_dropdown,
+ checklist,
+ procedure_stats,
+)
+
+from utils.components import header, build_upper_left_panel, map_card, procedure_card
+
+from utils.figures import (
+ generate_geo_map,
+ generate_procedure_plot,
+ hospital_datatable,
+ update_geo_map,
+ update_procedure_plot,
+)
+
app = dash.Dash(
__name__,
meta_tags=[
@@ -18,396 +39,13 @@
}
],
)
-app.title = "Medical Provider Charges"
+# app.title = "Medical Provider Charges"
server = app.server
app.config["suppress_callback_exceptions"] = True
-# Plotly mapbox token
-mapbox_access_token = "pk.eyJ1IjoicGxvdGx5bWFwYm94IiwiYSI6ImNrOWJqb2F4djBnMjEzbG50amg0dnJieG4ifQ.Zme1-Uzoi75IaFbieBDl3A"
-
-state_map = {
- "AK": "Alaska",
- "AL": "Alabama",
- "AR": "Arkansas",
- "AZ": "Arizona",
- "CA": "California",
- "CO": "Colorado",
- "CT": "Connecticut",
- "DC": "District of Columbia",
- "DE": "Delaware",
- "FL": "Florida",
- "GA": "Georgia",
- "HI": "Hawaii",
- "IA": "Iowa",
- "ID": "Idaho",
- "IL": "Illinois",
- "IN": "Indiana",
- "KS": "Kansas",
- "KY": "Kentucky",
- "LA": "Louisiana",
- "MA": "Massachusetts",
- "MD": "Maryland",
- "ME": "Maine",
- "MI": "Michigan",
- "MN": "Minnesota",
- "MO": "Missouri",
- "MS": "Mississippi",
- "MT": "Montana",
- "NC": "North Carolina",
- "ND": "North Dakota",
- "NE": "Nebraska",
- "NH": "New Hampshire",
- "NJ": "New Jersey",
- "NM": "New Mexico",
- "NV": "Nevada",
- "NY": "New York",
- "OH": "Ohio",
- "OK": "Oklahoma",
- "OR": "Oregon",
- "PA": "Pennsylvania",
- "RI": "Rhode Island",
- "SC": "South Carolina",
- "SD": "South Dakota",
- "TN": "Tennessee",
- "TX": "Texas",
- "UT": "Utah",
- "VA": "Virginia",
- "VT": "Vermont",
- "WA": "Washington",
- "WI": "Wisconsin",
- "WV": "West Virginia",
- "WY": "Wyoming",
-}
-
-state_list = list(state_map.keys())
-
-# Load data
-data_dict = {}
-for state in state_list:
- p = os.getcwd().split(os.path.sep)
- csv_path = "data/processed/df_{}_lat_lon.csv".format(state)
- state_data = pd.read_csv(csv_path)
- data_dict[state] = state_data
-
-# Cost Metric
-cost_metric = [
- "Average Covered Charges",
- "Average Total Payments",
- "Average Medicare Payments",
-]
-
-init_region = data_dict[state_list[1]][
- "Hospital Referral Region (HRR) Description"
-].unique()
-
-
-def generate_aggregation(df, metric):
- aggregation = {
- metric[0]: ["min", "mean", "max"],
- metric[1]: ["min", "mean", "max"],
- metric[2]: ["min", "mean", "max"],
- }
- grouped = (
- df.groupby(["Hospital Referral Region (HRR) Description", "Provider Name"])
- .agg(aggregation)
- .reset_index()
- )
-
- grouped["lat"] = grouped["lon"] = grouped["Provider Street Address"] = grouped[
- "Provider Name"
- ]
- grouped["lat"] = grouped["lat"].apply(lambda x: get_lat_lon_add(df, x)[0])
- grouped["lon"] = grouped["lon"].apply(lambda x: get_lat_lon_add(df, x)[1])
- grouped["Provider Street Address"] = grouped["Provider Street Address"].apply(
- lambda x: get_lat_lon_add(df, x)[2]
- )
-
- return grouped
-
-
-def get_lat_lon_add(df, name):
- return [
- df.groupby(["Provider Name"]).get_group(name)["lat"].tolist()[0],
- df.groupby(["Provider Name"]).get_group(name)["lon"].tolist()[0],
- df.groupby(["Provider Name"])
- .get_group(name)["Provider Street Address"]
- .tolist()[0],
- ]
-
-
-def build_upper_left_panel():
- return html.Div(
- id="upper-left",
- className="six columns",
- children=[
- html.P(
- className="section-title",
- children="Choose hospital on the map or procedures from the list below to see costs",
- ),
- html.Div(
- className="control-row-1",
- children=[
- html.Div(
- id="state-select-outer",
- children=[
- html.Label("Select a State"),
- dcc.Dropdown(
- id="state-select",
- options=[{"label": i, "value": i} for i in state_list],
- value=state_list[1],
- ),
- ],
- ),
- html.Div(
- id="select-metric-outer",
- children=[
- html.Label("Choose a Cost Metric"),
- dcc.Dropdown(
- id="metric-select",
- options=[{"label": i, "value": i} for i in cost_metric],
- value=cost_metric[0],
- ),
- ],
- ),
- ],
- ),
- html.Div(
- id="region-select-outer",
- className="control-row-2",
- children=[
- html.Label("Pick a Region"),
- html.Div(
- id="checklist-container",
- children=dcc.Checklist(
- id="region-select-all",
- options=[{"label": "Select All Regions", "value": "All"}],
- value=[],
- ),
- ),
- html.Div(
- id="region-select-dropdown-outer",
- children=dcc.Dropdown(
- id="region-select", multi=True, searchable=True,
- ),
- ),
- ],
- ),
- html.Div(
- id="table-container",
- className="table-container",
- children=[
- html.Div(
- id="table-upper",
- children=[
- html.P("Hospital Charges Summary"),
- dcc.Loading(children=html.Div(id="cost-stats-container")),
- ],
- ),
- html.Div(
- id="table-lower",
- children=[
- html.P("Procedure Charges Summary"),
- dcc.Loading(
- children=html.Div(id="procedure-stats-container")
- ),
- ],
- ),
- ],
- ),
- ],
- )
-
-
-def generate_geo_map(geo_data, selected_metric, region_select, procedure_select):
- filtered_data = geo_data[
- geo_data["Hospital Referral Region (HRR) Description"].isin(region_select)
- ]
- colors = ["#21c7ef", "#76f2ff", "#ff6969", "#ff1717"]
-
- hospitals = []
-
- lat = filtered_data["lat"].tolist()
- lon = filtered_data["lon"].tolist()
- average_covered_charges_mean = filtered_data[selected_metric]["mean"].tolist()
- regions = filtered_data["Hospital Referral Region (HRR) Description"].tolist()
- provider_name = filtered_data["Provider Name"].tolist()
-
- # Cost metric mapping from aggregated data
-
- cost_metric_data = {}
- cost_metric_data["min"] = filtered_data[selected_metric]["mean"].min()
- cost_metric_data["max"] = filtered_data[selected_metric]["mean"].max()
- cost_metric_data["mid"] = (cost_metric_data["min"] + cost_metric_data["max"]) / 2
- cost_metric_data["low_mid"] = (
- cost_metric_data["min"] + cost_metric_data["mid"]
- ) / 2
- cost_metric_data["high_mid"] = (
- cost_metric_data["mid"] + cost_metric_data["max"]
- ) / 2
-
- for i in range(len(lat)):
- val = average_covered_charges_mean[i]
- region = regions[i]
- provider = provider_name[i]
-
- if val <= cost_metric_data["low_mid"]:
- color = colors[0]
- elif cost_metric_data["low_mid"] < val <= cost_metric_data["mid"]:
- color = colors[1]
- elif cost_metric_data["mid"] < val <= cost_metric_data["high_mid"]:
- color = colors[2]
- else:
- color = colors[3]
-
- selected_index = []
- if provider in procedure_select["hospital"]:
- selected_index = [0]
-
- hospital = go.Scattermapbox(
- lat=[lat[i]],
- lon=[lon[i]],
- mode="markers",
- marker=dict(
- color=color,
- showscale=True,
- colorscale=[
- [0, "#21c7ef"],
- [0.33, "#76f2ff"],
- [0.66, "#ff6969"],
- [1, "#ff1717"],
- ],
- cmin=cost_metric_data["min"],
- cmax=cost_metric_data["max"],
- size=10
- * (1 + (val + cost_metric_data["min"]) / cost_metric_data["mid"]),
- colorbar=dict(
- x=0.9,
- len=0.7,
- title=dict(
- text="Average Cost",
- font={"color": "#737a8d", "family": "Open Sans"},
- ),
- titleside="top",
- tickmode="array",
- tickvals=[cost_metric_data["min"], cost_metric_data["max"]],
- ticktext=[
- "${:,.2f}".format(cost_metric_data["min"]),
- "${:,.2f}".format(cost_metric_data["max"]),
- ],
- ticks="outside",
- thickness=15,
- tickfont={"family": "Open Sans", "color": "#737a8d"},
- ),
- ),
- opacity=0.8,
- selectedpoints=selected_index,
- selected=dict(marker={"color": "#ffff00"}),
- customdata=[(provider, region)],
- hoverinfo="text",
- text=provider
- + "
"
- + region
- + "
Average Procedure Cost:"
- + " ${:,.2f}".format(val),
- )
- hospitals.append(hospital)
-
- layout = go.Layout(
- margin=dict(l=10, r=10, t=20, b=10, pad=5),
- plot_bgcolor="#171b26",
- paper_bgcolor="#171b26",
- clickmode="event+select",
- hovermode="closest",
- showlegend=False,
- mapbox=go.layout.Mapbox(
- accesstoken=mapbox_access_token,
- bearing=10,
- center=go.layout.mapbox.Center(
- lat=filtered_data.lat.mean(), lon=filtered_data.lon.mean()
- ),
- pitch=5,
- zoom=5,
- style="mapbox://styles/plotlymapbox/cjvppq1jl1ips1co3j12b9hex",
- ),
- )
-
- return {"data": hospitals, "layout": layout}
-
-
-def generate_procedure_plot(raw_data, cost_select, region_select, provider_select):
- procedure_data = raw_data[
- raw_data["Hospital Referral Region (HRR) Description"].isin(region_select)
- ].reset_index()
-
- traces = []
- selected_index = procedure_data[
- procedure_data["Provider Name"].isin(provider_select)
- ].index
-
- text = (
- procedure_data["Provider Name"]
- + "
"
- + ""
- + procedure_data["DRG Definition"].map(str)
- + "/
"
- + "Average Procedure Cost: $ "
- + procedure_data[cost_select].map(str)
- )
-
- provider_trace = go.Box(
- y=procedure_data["DRG Definition"],
- x=procedure_data[cost_select],
- name="",
- customdata=procedure_data["Provider Name"],
- boxpoints="all",
- jitter=0,
- pointpos=0,
- hoveron="points",
- fillcolor="rgba(0,0,0,0)",
- line=dict(color="rgba(0,0,0,0)"),
- hoverinfo="text",
- hovertext=text,
- selectedpoints=selected_index,
- selected=dict(marker={"color": "#FFFF00", "size": 13}),
- unselected=dict(marker={"opacity": 0.2}),
- marker=dict(
- line=dict(width=1, color="#000000"),
- color="#21c7ef",
- opacity=0.7,
- symbol="square",
- size=12,
- ),
- )
-
- traces.append(provider_trace)
-
- layout = go.Layout(
- showlegend=False,
- hovermode="closest",
- dragmode="select",
- clickmode="event+select",
- xaxis=dict(
- zeroline=False,
- automargin=True,
- showticklabels=True,
- title=dict(text="Procedure Cost", font=dict(color="#737a8d")),
- linecolor="#737a8d",
- tickfont=dict(color="#737a8d"),
- type="log",
- ),
- yaxis=dict(
- automargin=True,
- showticklabels=True,
- tickfont=dict(color="#737a8d"),
- gridcolor="#171b26",
- ),
- plot_bgcolor="#171b26",
- paper_bgcolor="#171b26",
- )
- # x : procedure, y: cost,
- return {"data": traces, "layout": layout}
+# def header(app, header_color, header, subheader=None, header_background_color="transparent"):
app.layout = html.Div(
@@ -417,8 +55,9 @@ def generate_procedure_plot(raw_data, cost_select, region_select, provider_selec
id="banner",
className="banner",
children=[
- html.H6("Dash Clinical Analytics"),
- html.Img(src=app.get_asset_url("plotly_logo_white.png")),
+ header(
+ app, "black", "Dash Clinical Analytics", "Medical Provider Charges"
+ ),
],
),
html.Div(
@@ -426,309 +65,77 @@ def generate_procedure_plot(raw_data, cost_select, region_select, provider_selec
className="row",
children=[
build_upper_left_panel(),
- html.Div(
- id="geo-map-outer",
- className="six columns",
- children=[
- html.P(
- id="map-title",
- children="Medicare Provider Charges in the State of {}".format(
- state_map[state_list[0]]
- ),
- ),
- html.Div(
- id="geo-map-loading-outer",
- children=[
- dcc.Loading(
- id="loading",
- children=dcc.Graph(
- id="geo-map",
- figure={
- "data": [],
- "layout": dict(
- plot_bgcolor="#171b26",
- paper_bgcolor="#171b26",
- ),
- },
- ),
- )
- ],
- ),
- ],
- ),
- ],
- ),
- html.Div(
- id="lower-container",
- children=[
- dcc.Graph(
- id="procedure-plot",
- figure=generate_procedure_plot(
- data_dict[state_list[1]], cost_metric[0], init_region, []
- ),
- )
+ map_card(),
],
),
+ procedure_card(),
],
)
-@app.callback(
- [
- Output("region-select", "value"),
- Output("region-select", "options"),
- Output("map-title", "children"),
- ],
- [Input("region-select-all", "value"), Input("state-select", "value"),],
+@callback(
+ Output("region-select", "value"),
+ Output("region-select", "options"),
+ Output("map-title", "children"),
+ Input("region-select-all", "value"),
+ Input("state-select", "value"),
)
def update_region_dropdown(select_all, state_select):
- state_raw_data = data_dict[state_select]
- regions = state_raw_data["Hospital Referral Region (HRR) Description"].unique()
- options = [{"label": i, "value": i} for i in regions]
-
- ctx = dash.callback_context
- if ctx.triggered[0]["prop_id"].split(".")[0] == "region-select-all":
- if select_all == ["All"]:
- value = [i["value"] for i in options]
- else:
- value = dash.no_update
- else:
- value = regions[:4]
- return (
- value,
- options,
- "Medicare Provider Charges in the State of {}".format(state_map[state_select]),
- )
+ return region_dropdown(select_all, state_select)
-@app.callback(
+@callback(
Output("checklist-container", "children"),
- [Input("region-select", "value")],
- [State("region-select", "options"), State("region-select-all", "value")],
+ Input("region-select", "value"),
+ State("region-select", "options"),
+ State("region-select-all", "value"),
)
def update_checklist(selected, select_options, checked):
- if len(selected) < len(select_options) and len(checked) == 0:
- raise PreventUpdate()
-
- elif len(selected) < len(select_options) and len(checked) == 1:
- return dcc.Checklist(
- id="region-select-all",
- options=[{"label": "Select All Regions", "value": "All"}],
- value=[],
- )
-
- elif len(selected) == len(select_options) and len(checked) == 1:
- raise PreventUpdate()
+ return checklist(selected, select_options, checked)
- return dcc.Checklist(
- id="region-select-all",
- options=[{"label": "Select All Regions", "value": "All"}],
- value=["All"],
- )
-
-@app.callback(
+@callback(
Output("cost-stats-container", "children"),
- [
- Input("geo-map", "selectedData"),
- Input("procedure-plot", "selectedData"),
- Input("metric-select", "value"),
- Input("state-select", "value"),
- ],
+ Input("geo-map", "selectedData"),
+ Input("procedure-plot", "selectedData"),
+ Input("metric-select", "value"),
+ Input("state-select", "value"),
)
def update_hospital_datatable(geo_select, procedure_select, cost_select, state_select):
- state_agg = generate_aggregation(data_dict[state_select], cost_metric)
- # make table from geo-select
- geo_data_dict = {
- "Provider Name": [],
- "City": [],
- "Street Address": [],
- "Maximum Cost ($)": [],
- "Minimum Cost ($)": [],
- }
-
- ctx = dash.callback_context
- if ctx.triggered:
- prop_id = ctx.triggered[0]["prop_id"].split(".")[0]
-
- # make table from procedure-select
- if prop_id == "procedure-plot" and procedure_select is not None:
-
- for point in procedure_select["points"]:
- provider = point["customdata"]
-
- dff = state_agg[state_agg["Provider Name"] == provider]
-
- geo_data_dict["Provider Name"].append(point["customdata"])
- city = dff["Hospital Referral Region (HRR) Description"].tolist()[0]
- geo_data_dict["City"].append(city)
-
- address = dff["Provider Street Address"].tolist()[0]
- geo_data_dict["Street Address"].append(address)
-
- geo_data_dict["Maximum Cost ($)"].append(
- dff[cost_select]["max"].tolist()[0]
- )
- geo_data_dict["Minimum Cost ($)"].append(
- dff[cost_select]["min"].tolist()[0]
- )
-
- if prop_id == "geo-map" and geo_select is not None:
-
- for point in geo_select["points"]:
- provider = point["customdata"][0]
- dff = state_agg[state_agg["Provider Name"] == provider]
+ return hospital_datatable(geo_select, procedure_select, cost_select, state_select)
- geo_data_dict["Provider Name"].append(point["customdata"][0])
- geo_data_dict["City"].append(point["customdata"][1].split("- ")[1])
- address = dff["Provider Street Address"].tolist()[0]
- geo_data_dict["Street Address"].append(address)
-
- geo_data_dict["Maximum Cost ($)"].append(
- dff[cost_select]["max"].tolist()[0]
- )
- geo_data_dict["Minimum Cost ($)"].append(
- dff[cost_select]["min"].tolist()[0]
- )
-
- geo_data_df = pd.DataFrame(data=geo_data_dict)
- data = geo_data_df.to_dict("rows")
-
- else:
- data = [{}]
-
- return dash_table.DataTable(
- id="cost-stats-table",
- columns=[{"name": i, "id": i} for i in geo_data_dict.keys()],
- data=data,
- filter_action="native",
- page_size=5,
- style_cell={"background-color": "#242a3b", "color": "#7b7d8d"},
- style_as_list_view=False,
- style_header={"background-color": "#1f2536", "padding": "0px 5px"},
- )
-
-
-@app.callback(
+@callback(
Output("procedure-stats-container", "children"),
- [
- Input("procedure-plot", "selectedData"),
- Input("geo-map", "selectedData"),
- Input("metric-select", "value"),
- ],
- [State("state-select", "value")],
+ Input("procedure-plot", "selectedData"),
+ Input("geo-map", "selectedData"),
+ Input("metric-select", "value"),
+ State("state-select", "value"),
)
def update_procedure_stats(procedure_select, geo_select, cost_select, state_select):
- procedure_dict = {
- "DRG": [],
- "Procedure": [],
- "Provider Name": [],
- "Cost Summary": [],
- }
-
- ctx = dash.callback_context
- prop_id = ""
- if ctx.triggered:
- prop_id = ctx.triggered[0]["prop_id"].split(".")[0]
-
- if prop_id == "procedure-plot" and procedure_select is not None:
- for point in procedure_select["points"]:
- procedure_dict["DRG"].append(point["y"].split(" - ")[0])
- procedure_dict["Procedure"].append(point["y"].split(" - ")[1])
-
- procedure_dict["Provider Name"].append(point["customdata"])
- procedure_dict["Cost Summary"].append(("${:,.2f}".format(point["x"])))
-
- # Display all procedures at selected hospital
- provider_select = []
+ return procedure_stats(procedure_select, geo_select, cost_select, state_select)
- if prop_id == "geo-map" and geo_select is not None:
- for point in geo_select["points"]:
- provider = point["customdata"][0]
- provider_select.append(provider)
- state_raw_data = data_dict[state_select]
- provider_filtered = state_raw_data[
- state_raw_data["Provider Name"].isin(provider_select)
- ]
-
- for i in range(len(provider_filtered)):
- procedure_dict["DRG"].append(
- provider_filtered.iloc[i]["DRG Definition"].split(" - ")[0]
- )
- procedure_dict["Procedure"].append(
- provider_filtered.iloc[i]["DRG Definition"].split(" - ")[1]
- )
- procedure_dict["Provider Name"].append(
- provider_filtered.iloc[i]["Provider Name"]
- )
- procedure_dict["Cost Summary"].append(
- "${:,.2f}".format(provider_filtered.iloc[0][cost_select])
- )
-
- procedure_data_df = pd.DataFrame(data=procedure_dict)
-
- return dash_table.DataTable(
- id="procedure-stats-table",
- columns=[{"name": i, "id": i} for i in procedure_dict.keys()],
- data=procedure_data_df.to_dict("rows"),
- filter_action="native",
- sort_action="native",
- style_cell={
- "textOverflow": "ellipsis",
- "background-color": "#242a3b",
- "color": "#7b7d8d",
- },
- sort_mode="multi",
- page_size=5,
- style_as_list_view=False,
- style_header={"background-color": "#1f2536", "padding": "2px 12px 0px 12px"},
- )
-
-
-@app.callback(
+@callback(
Output("geo-map", "figure"),
- [
- Input("metric-select", "value"),
- Input("region-select", "value"),
- Input("procedure-plot", "selectedData"),
- Input("state-select", "value"),
- ],
+ Input("metric-select", "value"),
+ Input("region-select", "value"),
+ Input("procedure-plot", "selectedData"),
+ Input("state-select", "value"),
)
-def update_geo_map(cost_select, region_select, procedure_select, state_select):
- # generate geo map from state-select, procedure-select
- state_agg_data = generate_aggregation(data_dict[state_select], cost_metric)
-
- provider_data = {"procedure": [], "hospital": []}
- if procedure_select is not None:
- for point in procedure_select["points"]:
- provider_data["procedure"].append(point["y"])
- provider_data["hospital"].append(point["customdata"])
-
- return generate_geo_map(state_agg_data, cost_select, region_select, provider_data)
+def return_updated_geo_map(cost_select, region_select, procedure_select, state_select):
+ return update_geo_map(cost_select, region_select, procedure_select, state_select)
-@app.callback(
+@callback(
Output("procedure-plot", "figure"),
- [
- Input("metric-select", "value"),
- Input("region-select", "value"),
- Input("geo-map", "selectedData"),
- Input("state-select", "value"),
- ],
+ Input("metric-select", "value"),
+ Input("region-select", "value"),
+ Input("geo-map", "selectedData"),
+ Input("state-select", "value"),
)
-def update_procedure_plot(cost_select, region_select, geo_select, state_select):
- # generate procedure plot from selected provider
- state_raw_data = data_dict[state_select]
-
- provider_select = []
- if geo_select is not None:
- for point in geo_select["points"]:
- provider_select.append(point["customdata"][0])
- return generate_procedure_plot(
- state_raw_data, cost_select, region_select, provider_select
- )
+def return_updated_procedure_plot(cost_select, region_select, geo_select, state_select):
+ return update_procedure_plot(cost_select, region_select, geo_select, state_select)
if __name__ == "__main__":
diff --git a/apps/dash-medical-provider-charges/assets/base.css b/apps/dash-medical-provider-charges/assets/base.css
index f6c30a588..a0656709b 100644
--- a/apps/dash-medical-provider-charges/assets/base.css
+++ b/apps/dash-medical-provider-charges/assets/base.css
@@ -40,81 +40,172 @@
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
- box-sizing: border-box; }
+ box-sizing: border-box;
+}
+
.column,
.columns {
width: 100%;
float: left;
- box-sizing: border-box; }
+ box-sizing: border-box;
+}
/* For devices larger than 400px */
@media (min-width: 400px) {
.container {
width: 85%;
- padding: 0; }
+ padding: 0;
+ }
}
/* For devices larger than 550px */
@media (min-width: 550px) {
.container {
- width: 80%; }
+ width: 80%;
+ }
+
.column,
.columns {
- margin-left: 2%; }
+ margin-left: 2%;
+ }
+
.column:first-child,
.columns:first-child {
- margin-left: 2%; }
+ margin-left: 2%;
+ }
.one.column,
- .one.columns { width: 4.66666666667%; }
- .two.columns { width: 13.3333333333%; }
- .three.columns { width: 22%; }
- .four.columns { width: 30.6666666667%; }
- .five.columns { width: 39.3333333333%; }
- .six.columns { width: 48%; }
- .seven.columns { width: 56.6666666667%; }
- .eight.columns { width: 65.3333333333%; }
- .nine.columns { width: 74.0%; }
- .ten.columns { width: 82.6666666667%; }
- .eleven.columns { width: 91.3333333333%; }
- .twelve.columns { width: 100%; margin-left: 0; }
-
- .one-third.column { width: 30.6666666667%; }
- .two-thirds.column { width: 65.3333333333%; }
-
- .one-half.column { width: 48%; }
+ .one.columns {
+ width: 4.66666666667%;
+ }
+
+ .two.columns {
+ width: 13.3333333333%;
+ }
+
+ .three.columns {
+ width: 22%;
+ }
+
+ .four.columns {
+ width: 30.6666666667%;
+ }
+
+ .five.columns {
+ width: 39.3333333333%;
+ }
+
+ .six.columns {
+ width: 48%;
+ }
+
+ .seven.columns {
+ width: 56.6666666667%;
+ }
+
+ .eight.columns {
+ width: 65.3333333333%;
+ }
+
+ .nine.columns {
+ width: 74.0%;
+ }
+
+ .ten.columns {
+ width: 82.6666666667%;
+ }
+
+ .eleven.columns {
+ width: 91.3333333333%;
+ }
+
+ .twelve.columns {
+ width: 100%;
+ margin-left: 0;
+ }
+
+ .one-third.column {
+ width: 30.6666666667%;
+ }
+
+ .two-thirds.column {
+ width: 65.3333333333%;
+ }
+
+ .one-half.column {
+ width: 48%;
+ }
/* Offsets */
.offset-by-one.column,
- .offset-by-one.columns { margin-left: 8.66666666667%; }
+ .offset-by-one.columns {
+ margin-left: 8.66666666667%;
+ }
+
.offset-by-two.column,
- .offset-by-two.columns { margin-left: 17.3333333333%; }
+ .offset-by-two.columns {
+ margin-left: 17.3333333333%;
+ }
+
.offset-by-three.column,
- .offset-by-three.columns { margin-left: 26%; }
+ .offset-by-three.columns {
+ margin-left: 26%;
+ }
+
.offset-by-four.column,
- .offset-by-four.columns { margin-left: 34.6666666667%; }
+ .offset-by-four.columns {
+ margin-left: 34.6666666667%;
+ }
+
.offset-by-five.column,
- .offset-by-five.columns { margin-left: 43.3333333333%; }
+ .offset-by-five.columns {
+ margin-left: 43.3333333333%;
+ }
+
.offset-by-six.column,
- .offset-by-six.columns { margin-left: 52%; }
+ .offset-by-six.columns {
+ margin-left: 52%;
+ }
+
.offset-by-seven.column,
- .offset-by-seven.columns { margin-left: 60.6666666667%; }
+ .offset-by-seven.columns {
+ margin-left: 60.6666666667%;
+ }
+
.offset-by-eight.column,
- .offset-by-eight.columns { margin-left: 69.3333333333%; }
+ .offset-by-eight.columns {
+ margin-left: 69.3333333333%;
+ }
+
.offset-by-nine.column,
- .offset-by-nine.columns { margin-left: 78.0%; }
+ .offset-by-nine.columns {
+ margin-left: 78.0%;
+ }
+
.offset-by-ten.column,
- .offset-by-ten.columns { margin-left: 86.6666666667%; }
+ .offset-by-ten.columns {
+ margin-left: 86.6666666667%;
+ }
+
.offset-by-eleven.column,
- .offset-by-eleven.columns { margin-left: 95.3333333333%; }
+ .offset-by-eleven.columns {
+ margin-left: 95.3333333333%;
+ }
.offset-by-one-third.column,
- .offset-by-one-third.columns { margin-left: 34.6666666667%; }
+ .offset-by-one-third.columns {
+ margin-left: 34.6666666667%;
+ }
+
.offset-by-two-thirds.column,
- .offset-by-two-thirds.columns { margin-left: 69.3333333333%; }
+ .offset-by-two-thirds.columns {
+ margin-left: 69.3333333333%;
+ }
.offset-by-one-half.column,
- .offset-by-one-half.columns { margin-left: 52%; }
+ .offset-by-one-half.columns {
+ margin-left: 52%;
+ }
}
@@ -125,30 +216,82 @@
html is set to 62.5% so that all the REM measurements throughout Skeleton
are based on 10px sizing. So basically 1.5rem = 15px :) */
html {
- font-size: 62.5%; }
+ font-size: 62.5%;
+}
+
body {
- font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */
+ font-size: 1.5em;
+ /* currently ems cause chrome bug misinterpreting rems on body element */
line-height: 1.6;
font-weight: 400;
font-family: "Open Sans", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
- color: rgb(50, 50, 50); }
+ color: rgb(50, 50, 50);
+}
/* Typography
–––––––––––––––––––––––––––––––––––––––––––––––––– */
-h1, h2, h3, h4, h5, h6 {
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
margin-top: 0;
margin-bottom: 0;
- font-weight: 300; }
-h1 { font-size: 4.5rem; line-height: 1.2; letter-spacing: -.1rem; margin-bottom: 2rem; }
-h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; margin-bottom: 1.8rem; margin-top: 1.8rem;}
-h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; margin-bottom: 1.5rem; margin-top: 1.5rem;}
-h4 { font-size: 2.6rem; line-height: 1.35; letter-spacing: -.08rem; margin-bottom: 1.2rem; margin-top: 1.2rem;}
-h5 { font-size: 2.2rem; line-height: 1.5; letter-spacing: -.05rem; margin-bottom: 0.6rem; margin-top: 0.6rem;}
-h6 { font-size: 2.0rem; line-height: 1.6; letter-spacing: 0; margin-bottom: 0.75rem; margin-top: 0.75rem;}
+ font-weight: 300;
+}
+
+h1 {
+ font-size: 4.5rem;
+ line-height: 1.2;
+ letter-spacing: -.1rem;
+ margin-bottom: 2rem;
+}
+
+h2 {
+ font-size: 3.6rem;
+ line-height: 1.25;
+ letter-spacing: -.1rem;
+ margin-bottom: 1.8rem;
+ margin-top: 1.8rem;
+}
+
+h3 {
+ font-size: 3.0rem;
+ line-height: 1.3;
+ letter-spacing: -.1rem;
+ margin-bottom: 1.5rem;
+ margin-top: 1.5rem;
+}
+
+h4 {
+ font-size: 2.6rem;
+ line-height: 1.35;
+ letter-spacing: -.08rem;
+ margin-bottom: 1.2rem;
+ margin-top: 1.2rem;
+}
+
+h5 {
+ font-size: 2.2rem;
+ line-height: 1.5;
+ letter-spacing: -.05rem;
+ margin-bottom: 0.6rem;
+ margin-top: 0.6rem;
+}
+
+h6 {
+ font-size: 2.0rem;
+ line-height: 1.6;
+ letter-spacing: 0;
+ margin-bottom: 0.75rem;
+ margin-top: 0.75rem;
+}
p {
- margin-top: 0; }
+ margin-top: 0;
+}
/* Blockquotes
@@ -167,9 +310,12 @@ blockquote {
a {
color: #1EAEDB;
text-decoration: underline;
- cursor: pointer;}
+ cursor: pointer;
+}
+
a:hover {
- color: #0FA0CE; }
+ color: #0FA0CE;
+}
/* Buttons
@@ -195,7 +341,9 @@ input[type="button"] {
border-radius: 4px;
border: 1px solid #bbb;
cursor: pointer;
- box-sizing: border-box; }
+ box-sizing: border-box;
+}
+
.button:hover,
button:hover,
input[type="submit"]:hover,
@@ -208,7 +356,9 @@ input[type="reset"]:focus,
input[type="button"]:focus {
color: #333;
border-color: #888;
- outline: 0; }
+ outline: 0;
+}
+
.button.button-primary,
button.button-primary,
input[type="submit"].button-primary,
@@ -216,7 +366,9 @@ input[type="reset"].button-primary,
input[type="button"].button-primary {
color: #FFF;
background-color: #33C3F0;
- border-color: #33C3F0; }
+ border-color: #33C3F0;
+}
+
.button.button-primary:hover,
button.button-primary:hover,
input[type="submit"].button-primary:hover,
@@ -229,7 +381,8 @@ input[type="reset"].button-primary:focus,
input[type="button"].button-primary:focus {
color: #FFF;
background-color: #1EAEDB;
- border-color: #1EAEDB; }
+ border-color: #1EAEDB;
+}
/* Forms
@@ -244,14 +397,18 @@ input[type="password"],
textarea,
select {
height: 38px;
- padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
+ padding: 6px 10px;
+ /* The 6px vertically centers text on FF, ignored by Webkit */
background-color: #fff;
border: 1px solid #D1D1D1;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
font-family: inherit;
- font-size: inherit; /*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/}
+ font-size: inherit;
+ /*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/
+}
+
/* Removes awkward default styles on some inputs for iOS */
input[type="email"],
input[type="number"],
@@ -262,12 +419,16 @@ input[type="url"],
input[type="password"],
textarea {
-webkit-appearance: none;
- -moz-appearance: none;
- appearance: none; }
+ -moz-appearance: none;
+ appearance: none;
+}
+
textarea {
min-height: 65px;
padding-top: 6px;
- padding-bottom: 6px; }
+ padding-bottom: 6px;
+}
+
input[type="email"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
@@ -282,36 +443,53 @@ textarea:focus,
label,
legend {
display: block;
- margin-bottom: 0px; }
+ margin-bottom: 0px;
+}
+
fieldset {
padding: 0;
- border-width: 0; }
+ border-width: 0;
+}
+
input[type="checkbox"],
input[type="radio"] {
- display: inline; }
-label > .label-body {
+ display: inline;
+}
+
+label>.label-body {
display: inline-block;
margin-left: .5rem;
- font-weight: normal; }
+ font-weight: normal;
+}
/* Lists
–––––––––––––––––––––––––––––––––––––––––––––––––– */
ul {
- list-style: circle inside; }
+ list-style: circle inside;
+}
+
ol {
- list-style: decimal inside; }
-ol, ul {
+ list-style: decimal inside;
+}
+
+ol,
+ul {
padding-left: 0;
- margin-top: 0; }
+ margin-top: 0;
+}
+
ul ul,
ul ol,
ol ol,
ol ul {
margin: 1.5rem 0 1.5rem 3rem;
- font-size: 90%; }
+ font-size: 90%;
+}
+
li {
- margin-bottom: 1rem; }
+ margin-bottom: 1rem;
+}
/* Tables
@@ -319,52 +497,72 @@ li {
table {
border-collapse: collapse;
}
+
th,
td {
padding: 12px 15px;
text-align: left;
- border-bottom: 1px solid #E1E1E1; }
+ border-bottom: 1px solid #E1E1E1;
+}
+
th:first-child,
td:first-child {
- padding-left: 0; }
+ padding-left: 0;
+}
+
th:last-child,
td:last-child {
- padding-right: 0; }
+ padding-right: 0;
+}
/* Spacing
–––––––––––––––––––––––––––––––––––––––––––––––––– */
button,
.button {
- margin-bottom: 0rem; }
+ margin-bottom: 0rem;
+}
+
input,
textarea,
select,
fieldset {
- margin-bottom: 0rem; }
+ margin-bottom: 0rem;
+}
+
pre,
dl,
figure,
table,
form {
- margin-bottom: 0rem; }
+ margin-bottom: 0rem;
+}
+
p,
ul,
ol {
- margin-bottom: 0.75rem; }
+ margin-bottom: 0.75rem;
+}
/* Utilities
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.u-full-width {
width: 100%;
- box-sizing: border-box; }
+ box-sizing: border-box;
+}
+
.u-max-full-width {
max-width: 100%;
- box-sizing: border-box; }
+ box-sizing: border-box;
+}
+
.u-pull-right {
- float: right; }
+ float: right;
+}
+
.u-pull-left {
- float: left; }
+ float: left;
+}
/* Misc
@@ -373,7 +571,8 @@ hr {
margin-top: 3rem;
margin-bottom: 3.5rem;
border-width: 0;
- border-top: 1px solid #E1E1E1; }
+ border-top: 1px solid #E1E1E1;
+}
/* Clearing
@@ -385,7 +584,8 @@ hr {
.u-cf {
content: "";
display: table;
- clear: both; }
+ clear: both;
+}
/* Media Queries
diff --git a/apps/dash-medical-provider-charges/screenshot.png b/apps/dash-medical-provider-charges/assets/github/screenshot.png
similarity index 100%
rename from apps/dash-medical-provider-charges/screenshot.png
rename to apps/dash-medical-provider-charges/assets/github/screenshot.png
diff --git a/apps/dash-medical-provider-charges/assets/images/plotly-logo-dark-theme.png b/apps/dash-medical-provider-charges/assets/images/plotly-logo-dark-theme.png
new file mode 100644
index 000000000..984dd57ab
Binary files /dev/null and b/apps/dash-medical-provider-charges/assets/images/plotly-logo-dark-theme.png differ
diff --git a/apps/dash-medical-provider-charges/assets/plotly_logo_white.png b/apps/dash-medical-provider-charges/assets/plotly_logo_white.png
deleted file mode 100644
index 489ed362f..000000000
Binary files a/apps/dash-medical-provider-charges/assets/plotly_logo_white.png and /dev/null differ
diff --git a/apps/dash-medical-provider-charges/constants.py b/apps/dash-medical-provider-charges/constants.py
new file mode 100644
index 000000000..230bef51e
--- /dev/null
+++ b/apps/dash-medical-provider-charges/constants.py
@@ -0,0 +1,80 @@
+import os
+import pandas as pd
+
+# Plotly mapbox token
+mapbox_access_token = "pk.eyJ1IjoicGxvdGx5bWFwYm94IiwiYSI6ImNrOWJqb2F4djBnMjEzbG50amg0dnJieG4ifQ.Zme1-Uzoi75IaFbieBDl3A"
+
+state_map = {
+ "AK": "Alaska",
+ "AL": "Alabama",
+ "AR": "Arkansas",
+ "AZ": "Arizona",
+ "CA": "California",
+ "CO": "Colorado",
+ "CT": "Connecticut",
+ "DC": "District of Columbia",
+ "DE": "Delaware",
+ "FL": "Florida",
+ "GA": "Georgia",
+ "HI": "Hawaii",
+ "IA": "Iowa",
+ "ID": "Idaho",
+ "IL": "Illinois",
+ "IN": "Indiana",
+ "KS": "Kansas",
+ "KY": "Kentucky",
+ "LA": "Louisiana",
+ "MA": "Massachusetts",
+ "MD": "Maryland",
+ "ME": "Maine",
+ "MI": "Michigan",
+ "MN": "Minnesota",
+ "MO": "Missouri",
+ "MS": "Mississippi",
+ "MT": "Montana",
+ "NC": "North Carolina",
+ "ND": "North Dakota",
+ "NE": "Nebraska",
+ "NH": "New Hampshire",
+ "NJ": "New Jersey",
+ "NM": "New Mexico",
+ "NV": "Nevada",
+ "NY": "New York",
+ "OH": "Ohio",
+ "OK": "Oklahoma",
+ "OR": "Oregon",
+ "PA": "Pennsylvania",
+ "RI": "Rhode Island",
+ "SC": "South Carolina",
+ "SD": "South Dakota",
+ "TN": "Tennessee",
+ "TX": "Texas",
+ "UT": "Utah",
+ "VA": "Virginia",
+ "VT": "Vermont",
+ "WA": "Washington",
+ "WI": "Wisconsin",
+ "WV": "West Virginia",
+ "WY": "Wyoming",
+}
+
+state_list = list(state_map.keys())
+
+# Load data
+data_dict = {}
+for state in state_list:
+ p = os.getcwd().split(os.path.sep)
+ csv_path = "data/processed/df_{}_lat_lon.csv".format(state)
+ state_data = pd.read_csv(csv_path)
+ data_dict[state] = state_data
+
+# Cost Metric
+cost_metric = [
+ "Average Covered Charges",
+ "Average Total Payments",
+ "Average Medicare Payments",
+]
+
+init_region = data_dict[state_list[1]][
+ "Hospital Referral Region (HRR) Description"
+].unique()
diff --git a/apps/dash-medical-provider-charges/gitignore b/apps/dash-medical-provider-charges/gitignore
new file mode 100644
index 000000000..d8e187da3
--- /dev/null
+++ b/apps/dash-medical-provider-charges/gitignore
@@ -0,0 +1,191 @@
+# .gitignore specifies the files that shouldn't be included
+# in version control and therefore shouldn't be included when
+# deploying an application to Dash Enterprise
+# This is a very exhaustive list!
+# This list was based off of https://github.com/github/gitignore
+
+# Ignore data that is generated during the runtime of an application
+# This folder is used by the "Large Data" sample applications
+runtime_data/
+data/
+
+# Omit SQLite databases that may be produced by dash-snapshots in development
+*.db
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+
+# Jupyter Notebook
+
+.ipynb_checkpoints
+*/.ipynb_checkpoints/*
+
+# IPython
+profile_default/
+ipython_config.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+
+# macOS General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# History files
+.Rhistory
+.Rapp.history
+
+# Session Data files
+.RData
+
+# User-specific files
+.Ruserdata
+
+# Example code in package build process
+*-Ex.R
+
+# Output files from R CMD check
+/*.Rcheck/
+
+# RStudio files
+.Rproj.user/
+
+# produced vignettes
+vignettes/*.html
+vignettes/*.pdf
+
+# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
+.httr-oauth
+
+# knitr and R markdown default cache directories
+*_cache/
+/cache/
+
+# Temporary files created by R markdown
+*.utf8.md
+*.knit.md
+
+# R Environment Variables
+.Renviron
+
+# Linux
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+# VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# SublineText
+# Cache files for Sublime Text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+
+# Workspace files are user-specific
+*.sublime-workspace
+
+# Project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using Sublime Text
+# *.sublime-project
+
+# SFTP configuration file
+sftp-config.json
+
+# Package control specific files
+Package Control.last-run
+Package Control.ca-list
+Package Control.ca-bundle
+Package Control.system-ca-bundle
+Package Control.cache/
+Package Control.ca-certs/
+Package Control.merged-ca-bundle
+Package Control.user-ca-bundle
+oscrypto-ca-bundle.crt
+bh_unicode_properties.cache
+
+# Sublime-github package stores a github token in this file
+# https://packagecontrol.io/packages/sublime-github
+GitHub.sublime-settings
\ No newline at end of file
diff --git a/apps/dash-medical-provider-charges/requirements.txt b/apps/dash-medical-provider-charges/requirements.txt
index d9c4b1df2..980b4ef72 100644
--- a/apps/dash-medical-provider-charges/requirements.txt
+++ b/apps/dash-medical-provider-charges/requirements.txt
@@ -1,6 +1,5 @@
-dash==1.12.0
-gunicorn>=19.9.0
-numpy==1.16.2
-pandas==0.24.2
+dash==2.4.1
+pandas==1.4.2
+gunicorn==20.1.0
uszipcode==0.2.2
-plotly==4.7.1
+
diff --git a/apps/dash-medical-provider-charges/runtime.txt b/apps/dash-medical-provider-charges/runtime.txt
new file mode 100644
index 000000000..cfa660c42
--- /dev/null
+++ b/apps/dash-medical-provider-charges/runtime.txt
@@ -0,0 +1 @@
+python-3.8.0
\ No newline at end of file
diff --git a/apps/dash-medical-provider-charges/utils/components.py b/apps/dash-medical-provider-charges/utils/components.py
new file mode 100644
index 000000000..afc882c62
--- /dev/null
+++ b/apps/dash-medical-provider-charges/utils/components.py
@@ -0,0 +1,163 @@
+from dash import html, dcc
+from constants import state_list, cost_metric, state_map, data_dict, init_region
+from utils.figures import generate_procedure_plot
+
+
+def header(
+ app, header_color, header, subheader=None, header_background_color="transparent"
+):
+ left_headers = html.Div(
+ [
+ html.Div(header, className="header-title"),
+ html.Div(subheader, className="subheader-title"),
+ ],
+ style={"color": header_color},
+ )
+
+ logo = html.Img(src=app.get_asset_url("images/plotly-logo-dark-theme.png"))
+ logo_link = html.A(logo, href="https://plotly.com/get-demo/", target="_blank")
+ demo_link = html.A(
+ "LEARN MORE",
+ href="https://plotly.com/dash/",
+ target="_blank",
+ className="demo-button",
+ )
+ right_logos = html.Div([demo_link, logo_link], className="header-logos")
+
+ return html.Div(
+ [left_headers, right_logos],
+ className="header",
+ style={"background-color": header_background_color},
+ )
+
+
+def build_upper_left_panel():
+ return html.Div(
+ id="upper-left",
+ className="six columns",
+ children=[
+ html.P(
+ className="section-title",
+ children="Choose hospital on the map or procedures from the list below to see costs",
+ ),
+ html.Div(
+ className="control-row-1",
+ children=[
+ html.Div(
+ id="state-select-outer",
+ children=[
+ html.Label("Select a State"),
+ dcc.Dropdown(
+ id="state-select",
+ options=[{"label": i, "value": i} for i in state_list],
+ value=state_list[1],
+ ),
+ ],
+ ),
+ html.Div(
+ id="select-metric-outer",
+ children=[
+ html.Label("Choose a Cost Metric"),
+ dcc.Dropdown(
+ id="metric-select",
+ options=[{"label": i, "value": i} for i in cost_metric],
+ value=cost_metric[0],
+ ),
+ ],
+ ),
+ ],
+ ),
+ html.Div(
+ id="region-select-outer",
+ className="control-row-2",
+ children=[
+ html.Label("Pick a Region"),
+ html.Div(
+ id="checklist-container",
+ children=dcc.Checklist(
+ id="region-select-all",
+ options=[{"label": "Select All Regions", "value": "All"}],
+ value=[],
+ ),
+ ),
+ html.Div(
+ id="region-select-dropdown-outer",
+ children=dcc.Dropdown(
+ id="region-select",
+ multi=True,
+ searchable=True,
+ ),
+ ),
+ ],
+ ),
+ html.Div(
+ id="table-container",
+ className="table-container",
+ children=[
+ html.Div(
+ id="table-upper",
+ children=[
+ html.P("Hospital Charges Summary"),
+ dcc.Loading(children=html.Div(id="cost-stats-container")),
+ ],
+ ),
+ html.Div(
+ id="table-lower",
+ children=[
+ html.P("Procedure Charges Summary"),
+ dcc.Loading(
+ children=html.Div(id="procedure-stats-container")
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+
+def map_card():
+ return html.Div(
+ id="geo-map-outer",
+ className="six columns",
+ children=[
+ html.P(
+ id="map-title",
+ children="Medicare Provider Charges in the State of {}".format(
+ state_map[state_list[0]]
+ ),
+ ),
+ html.Div(
+ id="geo-map-loading-outer",
+ children=[
+ dcc.Loading(
+ id="loading",
+ children=dcc.Graph(
+ id="geo-map",
+ figure={
+ "data": [],
+ "layout": dict(
+ plot_bgcolor="#171b26",
+ paper_bgcolor="#171b26",
+ ),
+ },
+ ),
+ )
+ ],
+ ),
+ ],
+ )
+
+
+def procedure_card():
+ return html.Div(
+ id="lower-container",
+ children=[
+ dcc.Graph(
+ id="procedure-plot",
+ figure=generate_procedure_plot(
+ data_dict[state_list[1]], cost_metric[0], init_region, []
+ ),
+ )
+ ],
+ )
diff --git a/apps/dash-medical-provider-charges/utils/figures.py b/apps/dash-medical-provider-charges/utils/figures.py
new file mode 100644
index 000000000..cf583d970
--- /dev/null
+++ b/apps/dash-medical-provider-charges/utils/figures.py
@@ -0,0 +1,298 @@
+import plotly.graph_objs as go
+from constants import mapbox_access_token
+import dash
+from dash import dash_table
+import pandas as pd
+from utils.helper_functions import generate_aggregation
+from constants import data_dict, cost_metric
+
+
+def generate_geo_map(geo_data, selected_metric, region_select, procedure_select):
+ filtered_data = geo_data[
+ geo_data["Hospital Referral Region (HRR) Description"].isin(region_select)
+ ]
+
+ colors = ["#21c7ef", "#76f2ff", "#ff6969", "#ff1717"]
+
+ hospitals = []
+
+ lat = filtered_data["lat"].tolist()
+ lon = filtered_data["lon"].tolist()
+ average_covered_charges_mean = filtered_data[selected_metric]["mean"].tolist()
+ regions = filtered_data["Hospital Referral Region (HRR) Description"].tolist()
+ provider_name = filtered_data["Provider Name"].tolist()
+
+ # Cost metric mapping from aggregated data
+
+ cost_metric_data = {}
+ cost_metric_data["min"] = filtered_data[selected_metric]["mean"].min()
+ cost_metric_data["max"] = filtered_data[selected_metric]["mean"].max()
+ cost_metric_data["mid"] = (cost_metric_data["min"] + cost_metric_data["max"]) / 2
+ cost_metric_data["low_mid"] = (
+ cost_metric_data["min"] + cost_metric_data["mid"]
+ ) / 2
+ cost_metric_data["high_mid"] = (
+ cost_metric_data["mid"] + cost_metric_data["max"]
+ ) / 2
+
+ for i in range(len(lat)):
+ val = average_covered_charges_mean[i]
+ region = regions[i]
+ provider = provider_name[i]
+
+ if val <= cost_metric_data["low_mid"]:
+ color = colors[0]
+ elif cost_metric_data["low_mid"] < val <= cost_metric_data["mid"]:
+ color = colors[1]
+ elif cost_metric_data["mid"] < val <= cost_metric_data["high_mid"]:
+ color = colors[2]
+ else:
+ color = colors[3]
+
+ selected_index = []
+ if provider in procedure_select["hospital"]:
+ selected_index = [0]
+
+ hospital = go.Scattermapbox(
+ lat=[lat[i]],
+ lon=[lon[i]],
+ mode="markers",
+ marker=dict(
+ color=color,
+ showscale=True,
+ colorscale=[
+ [0, "#21c7ef"],
+ [0.33, "#76f2ff"],
+ [0.66, "#ff6969"],
+ [1, "#ff1717"],
+ ],
+ cmin=cost_metric_data["min"],
+ cmax=cost_metric_data["max"],
+ size=10
+ * (1 + (val + cost_metric_data["min"]) / cost_metric_data["mid"]),
+ colorbar=dict(
+ x=0.9,
+ len=0.7,
+ title=dict(
+ text="Average Cost",
+ font={"color": "#737a8d", "family": "Open Sans"},
+ ),
+ titleside="top",
+ tickmode="array",
+ tickvals=[cost_metric_data["min"], cost_metric_data["max"]],
+ ticktext=[
+ "${:,.2f}".format(cost_metric_data["min"]),
+ "${:,.2f}".format(cost_metric_data["max"]),
+ ],
+ ticks="outside",
+ thickness=15,
+ tickfont={"family": "Open Sans", "color": "#737a8d"},
+ ),
+ ),
+ opacity=0.8,
+ selectedpoints=selected_index,
+ selected=dict(marker={"color": "#ffff00"}),
+ customdata=[(provider, region)],
+ hoverinfo="text",
+ text=provider
+ + "
"
+ + region
+ + "
Average Procedure Cost:"
+ + " ${:,.2f}".format(val),
+ )
+ hospitals.append(hospital)
+
+ layout = go.Layout(
+ margin=dict(l=10, r=10, t=20, b=10, pad=5),
+ plot_bgcolor="#171b26",
+ paper_bgcolor="#171b26",
+ clickmode="event+select",
+ hovermode="closest",
+ showlegend=False,
+ mapbox=go.layout.Mapbox(
+ accesstoken=mapbox_access_token,
+ bearing=10,
+ center=go.layout.mapbox.Center(
+ lat=filtered_data.lat.mean(), lon=filtered_data.lon.mean()
+ ),
+ pitch=5,
+ zoom=5,
+ style="mapbox://styles/plotlymapbox/cjvppq1jl1ips1co3j12b9hex",
+ ),
+ )
+
+ return {"data": hospitals, "layout": layout}
+
+
+def generate_procedure_plot(raw_data, cost_select, region_select, provider_select):
+ procedure_data = raw_data[
+ raw_data["Hospital Referral Region (HRR) Description"].isin(region_select)
+ ].reset_index()
+
+ traces = []
+ selected_index = procedure_data[
+ procedure_data["Provider Name"].isin(provider_select)
+ ].index
+
+ text = (
+ procedure_data["Provider Name"]
+ + "
"
+ + ""
+ + procedure_data["DRG Definition"].map(str)
+ + "/
"
+ + "Average Procedure Cost: $ "
+ + procedure_data[cost_select].map(str)
+ )
+
+ provider_trace = go.Box(
+ y=procedure_data["DRG Definition"],
+ x=procedure_data[cost_select],
+ name="",
+ customdata=procedure_data["Provider Name"],
+ boxpoints="all",
+ jitter=0,
+ pointpos=0,
+ hoveron="points",
+ fillcolor="rgba(0,0,0,0)",
+ line=dict(color="rgba(0,0,0,0)"),
+ hoverinfo="text",
+ hovertext=text,
+ selectedpoints=selected_index,
+ selected=dict(marker={"color": "#FFFF00", "size": 13}),
+ unselected=dict(marker={"opacity": 0.2}),
+ marker=dict(
+ line=dict(width=1, color="#000000"),
+ color="#21c7ef",
+ opacity=0.7,
+ symbol="square",
+ size=12,
+ ),
+ )
+
+ traces.append(provider_trace)
+
+ layout = go.Layout(
+ showlegend=False,
+ hovermode="closest",
+ dragmode="select",
+ clickmode="event+select",
+ xaxis=dict(
+ zeroline=False,
+ automargin=True,
+ showticklabels=True,
+ title=dict(text="Procedure Cost", font=dict(color="#737a8d")),
+ linecolor="#737a8d",
+ tickfont=dict(color="#737a8d"),
+ type="log",
+ ),
+ yaxis=dict(
+ automargin=True,
+ showticklabels=True,
+ tickfont=dict(color="#737a8d"),
+ gridcolor="#171b26",
+ ),
+ plot_bgcolor="#171b26",
+ paper_bgcolor="#171b26",
+ )
+ # x : procedure, y: cost,
+ return {"data": traces, "layout": layout}
+
+
+def hospital_datatable(geo_select, procedure_select, cost_select, state_select):
+ state_agg = generate_aggregation(data_dict[state_select], cost_metric)
+ # make table from geo-select
+ geo_data_dict = {
+ "Provider Name": [],
+ "City": [],
+ "Street Address": [],
+ "Maximum Cost ($)": [],
+ "Minimum Cost ($)": [],
+ }
+
+ ctx = dash.callback_context
+ if ctx.triggered:
+ prop_id = ctx.triggered[0]["prop_id"].split(".")[0]
+
+ # make table from procedure-select
+ if prop_id == "procedure-plot" and procedure_select is not None:
+
+ for point in procedure_select["points"]:
+ provider = point["customdata"]
+
+ dff = state_agg[state_agg["Provider Name"] == provider]
+
+ geo_data_dict["Provider Name"].append(point["customdata"])
+ city = dff["Hospital Referral Region (HRR) Description"].tolist()[0]
+ geo_data_dict["City"].append(city)
+
+ address = dff["Provider Street Address"].tolist()[0]
+ geo_data_dict["Street Address"].append(address)
+
+ geo_data_dict["Maximum Cost ($)"].append(
+ dff[cost_select]["max"].tolist()[0]
+ )
+ geo_data_dict["Minimum Cost ($)"].append(
+ dff[cost_select]["min"].tolist()[0]
+ )
+
+ if prop_id == "geo-map" and geo_select is not None:
+
+ for point in geo_select["points"]:
+ provider = point["customdata"][0]
+ dff = state_agg[state_agg["Provider Name"] == provider]
+
+ geo_data_dict["Provider Name"].append(point["customdata"][0])
+ geo_data_dict["City"].append(point["customdata"][1].split("- ")[1])
+
+ address = dff["Provider Street Address"].tolist()[0]
+ geo_data_dict["Street Address"].append(address)
+
+ geo_data_dict["Maximum Cost ($)"].append(
+ dff[cost_select]["max"].tolist()[0]
+ )
+ geo_data_dict["Minimum Cost ($)"].append(
+ dff[cost_select]["min"].tolist()[0]
+ )
+
+ geo_data_df = pd.DataFrame(data=geo_data_dict)
+ data = geo_data_df.to_dict(orient="records")
+
+ else:
+ data = [{}]
+
+ return dash_table.DataTable(
+ id="cost-stats-table",
+ columns=[{"name": i, "id": i} for i in geo_data_dict.keys()],
+ data=data,
+ filter_action="native",
+ page_size=5,
+ style_cell={"background-color": "#242a3b", "color": "#7b7d8d"},
+ style_as_list_view=False,
+ style_header={"background-color": "#1f2536", "padding": "0px 5px"},
+ )
+
+
+def update_geo_map(cost_select, region_select, procedure_select, state_select):
+ # generate geo map from state-select, procedure-select
+ state_agg_data = generate_aggregation(data_dict[state_select], cost_metric)
+
+ provider_data = {"procedure": [], "hospital": []}
+ if procedure_select is not None:
+ for point in procedure_select["points"]:
+ provider_data["procedure"].append(point["y"])
+ provider_data["hospital"].append(point["customdata"])
+
+ return generate_geo_map(state_agg_data, cost_select, region_select, provider_data)
+
+
+def update_procedure_plot(cost_select, region_select, geo_select, state_select):
+ # generate procedure plot from selected provider
+ state_raw_data = data_dict[state_select]
+
+ provider_select = []
+ if geo_select is not None:
+ for point in geo_select["points"]:
+ provider_select.append(point["customdata"][0])
+ return generate_procedure_plot(
+ state_raw_data, cost_select, region_select, provider_select
+ )
diff --git a/apps/dash-medical-provider-charges/utils/helper_functions.py b/apps/dash-medical-provider-charges/utils/helper_functions.py
new file mode 100644
index 000000000..6b93ceec6
--- /dev/null
+++ b/apps/dash-medical-provider-charges/utils/helper_functions.py
@@ -0,0 +1,148 @@
+from constants import state_map, data_dict
+import dash
+from dash.exceptions import PreventUpdate
+from dash import dash_table
+import pandas as pd
+
+
+def generate_aggregation(df, metric):
+ aggregation = {
+ metric[0]: ["min", "mean", "max"],
+ metric[1]: ["min", "mean", "max"],
+ metric[2]: ["min", "mean", "max"],
+ }
+ grouped = (
+ df.groupby(["Hospital Referral Region (HRR) Description", "Provider Name"])
+ .agg(aggregation)
+ .reset_index()
+ )
+
+ grouped["lat"] = grouped["lon"] = grouped["Provider Street Address"] = grouped[
+ "Provider Name"
+ ]
+ grouped["lat"] = grouped["lat"].apply(lambda x: get_lat_lon_add(df, x)[0])
+ grouped["lon"] = grouped["lon"].apply(lambda x: get_lat_lon_add(df, x)[1])
+ grouped["Provider Street Address"] = grouped["Provider Street Address"].apply(
+ lambda x: get_lat_lon_add(df, x)[2]
+ )
+
+ return grouped
+
+
+def get_lat_lon_add(df, name):
+ return [
+ df.groupby(["Provider Name"]).get_group(name)["lat"].tolist()[0],
+ df.groupby(["Provider Name"]).get_group(name)["lon"].tolist()[0],
+ df.groupby(["Provider Name"])
+ .get_group(name)["Provider Street Address"]
+ .tolist()[0],
+ ]
+
+
+def region_dropdown(select_all, state_select):
+ state_raw_data = data_dict[state_select]
+ regions = state_raw_data["Hospital Referral Region (HRR) Description"].unique()
+ options = [{"label": i, "value": i} for i in regions]
+
+ ctx = dash.callback_context
+ if ctx.triggered[0]["prop_id"].split(".")[0] == "region-select-all":
+ if select_all == ["All"]:
+ value = [i["value"] for i in options]
+ else:
+ value = dash.no_update
+ else:
+ value = regions[:4]
+ return (
+ value,
+ options,
+ "Medicare Provider Charges in the State of {}".format(state_map[state_select]),
+ )
+
+
+def checklist(selected, select_options, checked):
+ if len(selected) < len(select_options) and len(checked) == 0:
+ raise PreventUpdate()
+
+ elif len(selected) < len(select_options) and len(checked) == 1:
+ return dcc.Checklist(
+ id="region-select-all",
+ options=[{"label": "Select All Regions", "value": "All"}],
+ value=[],
+ )
+
+ elif len(selected) == len(select_options) and len(checked) == 1:
+ raise PreventUpdate()
+
+ return dcc.Checklist(
+ id="region-select-all",
+ options=[{"label": "Select All Regions", "value": "All"}],
+ value=["All"],
+ )
+
+
+def procedure_stats(procedure_select, geo_select, cost_select, state_select):
+ procedure_dict = {
+ "DRG": [],
+ "Procedure": [],
+ "Provider Name": [],
+ "Cost Summary": [],
+ }
+
+ ctx = dash.callback_context
+ prop_id = ""
+ if ctx.triggered:
+ prop_id = ctx.triggered[0]["prop_id"].split(".")[0]
+
+ if prop_id == "procedure-plot" and procedure_select is not None:
+ for point in procedure_select["points"]:
+ procedure_dict["DRG"].append(point["y"].split(" - ")[0])
+ procedure_dict["Procedure"].append(point["y"].split(" - ")[1])
+
+ procedure_dict["Provider Name"].append(point["customdata"])
+ procedure_dict["Cost Summary"].append(("${:,.2f}".format(point["x"])))
+
+ # Display all procedures at selected hospital
+ provider_select = []
+
+ if prop_id == "geo-map" and geo_select is not None:
+ for point in geo_select["points"]:
+ provider = point["customdata"][0]
+ provider_select.append(provider)
+
+ state_raw_data = data_dict[state_select]
+ provider_filtered = state_raw_data[
+ state_raw_data["Provider Name"].isin(provider_select)
+ ]
+
+ for i in range(len(provider_filtered)):
+ procedure_dict["DRG"].append(
+ provider_filtered.iloc[i]["DRG Definition"].split(" - ")[0]
+ )
+ procedure_dict["Procedure"].append(
+ provider_filtered.iloc[i]["DRG Definition"].split(" - ")[1]
+ )
+ procedure_dict["Provider Name"].append(
+ provider_filtered.iloc[i]["Provider Name"]
+ )
+ procedure_dict["Cost Summary"].append(
+ "${:,.2f}".format(provider_filtered.iloc[0][cost_select])
+ )
+
+ procedure_data_df = pd.DataFrame(data=procedure_dict)
+
+ return dash_table.DataTable(
+ id="procedure-stats-table",
+ columns=[{"name": i, "id": i} for i in procedure_dict.keys()],
+ data=procedure_data_df.to_dict(orient="records"),
+ filter_action="native",
+ sort_action="native",
+ style_cell={
+ "textOverflow": "ellipsis",
+ "background-color": "#242a3b",
+ "color": "#7b7d8d",
+ },
+ sort_mode="multi",
+ page_size=5,
+ style_as_list_view=False,
+ style_header={"background-color": "#1f2536", "padding": "2px 12px 0px 12px"},
+ )