56  Exercise 6: DESpite Everything, It’s Still You

Let’s now make our app really powerful! We’re going to work with a new Streamlit app that works out the estimated demand for an area given its demographic features1, make it more efficient, and then incorporate it into our main app.

import streamlit as st
import geopandas
import pandas as pd
import folium
import random
from streamlit_folium import st_folium
import time

st.set_page_config(layout="wide")

st.title("Clinic Demand Estimator")

lsoa_demographics = geopandas.read_file("lsoa_demand_demographics.geojson")
lsoa_demographics["Region"] = lsoa_demographics["LSOA21NM"].str.replace("( \d{3})\w", "", regex=True).str.strip()

new_col = True

df_display = lsoa_demographics.drop(
    columns = ["BNG_E", "BNG_N", "LONG", "LAT", "GlobalID", "geometry"]
)

df_display.insert(loc=2, column='Include', value=new_col)

selected_regions = st.multiselect(
    "Select Regions to Include",
    lsoa_demographics["Region"].drop_duplicates().sort_values().tolist(),
    default=["Exeter"]
)

edited_df = st.data_editor(
    df_display[df_display["Region"].isin(selected_regions)]
    )

lsoa_demographics = pd.merge(
    lsoa_demographics,
    edited_df[edited_df["Include"] == True][["LSOA21CD"]],
    how="inner"
    )

demand_calls = lsoa_demographics['Projected Average Daily Demand'].sum()*0.2
demand_walkins = lsoa_demographics['Projected Average Daily Demand'].sum()*0.8

iat_calls = 480/(lsoa_demographics['Projected Average Daily Demand'].sum()*0.2)
iat_walkins = 480/(lsoa_demographics['Projected Average Daily Demand'].sum()*0.8)

st.write(f"Projected Daily Demand - Calls: {demand_calls:.1f}")
st.write(f"Average IAT: {iat_calls:.1f} minutes (assuming 480 minute day)")

st.write(f"Projected Daily Demand - Walk-ins: {demand_walkins:.1f}")
st.write(f"Average IAT - Walk-ins: {iat_walkins:.1f} minutes (assuming 480 minute day)")

#create base map
demand_demographic_map_interactive = folium.Map(
    location=[50.71671, -3.50668],
    zoom_start=9,
    tiles='cartodbpositron'
    )

# create and add choropleth map
choropleth = folium.Choropleth(
    geo_data=lsoa_demographics, # dataframe with geometry in it
    data=lsoa_demographics, # dataframe with data in - may be the same dataframe or a different one
    columns=['LSOA21CD', 'Projected Average Daily Demand'], # [key (field for geometry), field to plot]
    key_on='feature.properties.LSOA21CD',
    fill_color='OrRd',
    fill_opacity=0.4,
    line_weight=0.3,
    legend_name='Projected Average Daily Demand',
    highlight=True, # highlight the LSOA shape when mouse pointer enters it
    smooth_factor=0
    )

choropleth = choropleth.add_to(demand_demographic_map_interactive)

choropleth = choropleth.geojson.add_child(
    folium.features.GeoJsonTooltip(
        ['LSOA21CD', 'Projected Average Daily Demand'],
        labels=True
        )
)

st_folium(demand_demographic_map_interactive,
          use_container_width=True)

st.divider()

st.subheader("Complex Calculation Unrelated to the Map!")

st.write("Long-running calculation being calculated...")

time.sleep(10)

st.write("Long-running calculation complete!")

st.write(f"The answer is {random.randint(100, 500)}")

You will also need to make sure the following geojson is available: lsoa_demand_demographics.geojson

56.1 Tasks

  • The LSOA demographics file that new app loads in is really big!

    • Switch to loading it in using the @st.cache_data decorator
  • There’s a long-running calculation in the second half of that page that isn’t anything to do with the map.

    • Use the @st.fragment decorator so that changing the parameters of the map doesn’t trigger this calculation to rerun
  • Add this new app as an extra page in your multipage DES app from the previous exercise

  • Use session state to save the caller and patient IAT figures from that new page

    • then remove the ability for the user to specify the IAT for callers and patients
    • replace the IAT used for the simulation with the IAT that you saved into the session state

56.1.1 Challenge Activity

Try saving some key outputs from each model run to the session state and use this to display a comparison of the outputs across these multiple runs


  1. This is just a dummy app to show what’s possible - it’s not using a proper method to estimate the demand!↩︎