Repository: CodeForPhilly/chime
Pull request #183 (2020-03-21 23:50:49)

Add background info to readme
- Add background info (copied from new user docs)
- Organize community/contribute/docs links a bit, more to come


Patch:
diff --git a/README.md b/README.md
index b99eb12..2a8a5d0 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,24 @@
 # CHIME
 
-The **C**OVID-19 **H**ospital **I**mpact **M**odel for **E**pidemics
+The **C**OVID-19 **H**ospital **I**mpact **M**odel for **E**pidemics ([penn-chime.phl.io](http://penn-chime.phl.io/))
 
 [![CHIME](https://user-images.githubusercontent.com/1069047/76693244-5e07e980-6638-11ea-9e02-1c265c86fd2b.gif)](http://predictivehealthcare.pennmedicine.org/chime)
 
-Try it out online at [penn-chime.phl.io](http://penn-chime.phl.io/)
+## Background
+The [CHIME](https://penn-chime.phl.io/) (COVID-19 Hospital Impact Model for Epidemics) Application is designed to assist hospitals and public health officials with understanding hospital capacity needs as they relate to the COVID pandemic. CHIME enables capacity planning by providing estimates of total daily (i.e. new) and running totals of (i.e. census) inpatient hospitalizations, ICU admissions, and patients requiring ventilation. These estimates are generated using a [SIR (Susceptible, Infected, Recovered)](https://mathworld.wolfram.com/SIRModel.html) model, a standard epidemiological modeling technique. Our model has been validated by several epidemiologists including [Michael Z. Levy, PhD](https://www.dbei.med.upenn.edu/bio/michael-z-levy-phd), Associate Professor of Epidemiology, Department of Biostatistics, Epidemiology and Informatics at the Perelman School of Medicine.
 
-Interested in [contributing](https://codeforphilly.github.io/chime/CONTRIBUTING.html)?
+Originally developed in `github.com/pennsignals/chime`, active development is now at `github.com/pennsignals/chime`.
 
-Join our [Code For Philly](https://codeforphilly.org/projects/chime) project or our [Slack workspace](https://codeforphilly.org/chat) in the [#covid19-chime-penn](https://codeforphilly.org/chat/covid19-chime-penn) channel if you'd like to chat with us. We'd appreciate your [feedback](http://predictivehealthcare.pennmedicine.org/contact/).
-
-## Documentation
+### Documentation
 
 - [User Documentation](https://code-for-philly.gitbook.io/chime/)
-- [Getting Started: Try Online](https://codeforphilly.github.io/chime/getting-started/try-online.html)
-- [Getting Started: Run Locally](https://codeforphilly.github.io/chime/getting-started/run-locally.html)
+- [Developer Documentation](https://codeforphilly.github.io/chime/)
+
+## Contributing
+
 - [Contributing: Overview](https://codeforphilly.github.io/chime/CONTRIBUTING.html)
-- [Contributing: Application Development](https://codeforphilly.github.io/chime/contributing/app-dev.html)
+
+Join our [Code For Philly](https://codeforphilly.org/projects/chime) project or our [Slack workspace](https://codeforphilly.org/chat) in the [#covid19-chime-penn](https://codeforphilly.org/chat/covid19-chime-penn) channel if you'd like to chat with us. We'd appreciate your [feedback](http://predictivehealthcare.pennmedicine.org/contact/).
 
 ## Thanks
 
diff --git a/src/app.py b/src/app.py
index ceb8f04..bb1eabf 100644
--- a/src/app.py
+++ b/src/app.py
@@ -1,10 +1,11 @@
 """App."""
 
-import altair as alt  # type: ignore
-import streamlit as st  # type: ignore
+import altair as alt
+import streamlit as st
 
 from penn_chime.presentation import (
-    #additional_projections_chart,
+    additional_projections_chart,
+    admitted_patients_chart,
     display_header,
     display_sidebar,
     display_n_days_slider,
@@ -12,14 +13,16 @@ from penn_chime.presentation import (
     draw_projected_admissions_table,
     draw_raw_sir_simulation_table,
     hide_menu_style,
+    new_admissions_chart,
     show_additional_projections,
     show_more_info_about_this_tool,
     write_definitions,
     write_footer,
 )
-from penn_chime.settings import DEFAULTS 
-from penn_chime.models import sim_sir_df, build_admissions_df, build_census_df
-from penn_chime.charts import additional_projections_chart, admitted_patients_chart, new_admissions_chart
+from penn_chime.utils import build_admissions_df, build_census_df
+from penn_chime.settings import DEFAULTS
+
+
 # This is somewhat dangerous:
 # Hide the main menu with "Rerun", "run on Save", "clear cache", and "record a screencast"
 # This should not be hidden in prod, but removed
@@ -28,15 +31,35 @@ st.markdown(hide_menu_style, unsafe_allow_html=True)
 
 p = display_sidebar(st, DEFAULTS)
 
-display_header(st, p)
-
+display_header(
+    st,
+    total_infections=p.infected,
+    initial_infections=p.known_infected,
+    detection_prob=p.detection_probability,
+    current_hosp=p.current_hospitalized,
+    hosp_rate=p.hospitalized.rate,
+    S=p.susceptible,
+    market_share=p.market_share,
+    recovery_days=p.recovery_days,
+    r_naught=p.r_naught,
+    doubling_time=p.doubling_time,
+    relative_contact_rate=p.relative_contact_rate,
+    r_t=p.r_t,
+    doubling_time_t=p.doubling_time_t,
+)
 if st.checkbox("Show more info about this tool"):
     notes = "The total size of the susceptible population will be the entire catchment area for Penn Medicine entities (HUP, PAH, PMC, CCH)"
     show_more_info_about_this_tool(
         st=st,
-        parameters=p,
+        recovery_days=p.recovery_days,
+        doubling_time=p.doubling_time,
+        r_naught=p.r_naught,
+        relative_contact_rate=p.relative_contact_rate,
+        doubling_time_t=p.doubling_time_t,
+        r_t=p.r_t,
         inputs=DEFAULTS,
         notes=notes
+
     )
 
 # PRESENTATION
@@ -45,36 +68,30 @@ as_date = st.checkbox(label="Present result as dates instead of days", value=Fal
 display_n_days_slider(st, p, DEFAULTS)
 
 # begin format data
-admissions_df = build_admissions_df(p=p) # p.n_days, *p.dispositions)
-census_df = build_census_df(admissions_df, parameters=p)
+projection_admits = build_admissions_df(p.n_days, *p.dispositions)
+census_df = build_census_df(projection_admits, *p.lengths_of_stay)
 # end format data
 
 st.subheader("New Admissions")
 st.markdown("Projected number of **daily** COVID-19 admissions at Penn hospitals")
 st.altair_chart(
-    new_admissions_chart(alt, admissions_df, parameters=p, as_date=as_date), use_container_width=True
+    new_admissions_chart(alt, projection_admits, p.n_days - 10, as_date=as_date, max_y_axis=p.max_y_axis), use_container_width=True
 )
 if st.checkbox("Show Projected Admissions in tabular form"):
-    draw_projected_admissions_table(st, admissions_df, as_date=as_date)
+    draw_projected_admissions_table(st, projection_admits, as_date=as_date)
 st.subheader("Admitted Patients (Census)")
 st.markdown(
     "Projected **census** of COVID-19 patients, accounting for arrivals and discharges at Penn hospitals"
 )
-st.altair_chart(
-    admitted_patients_chart(alt=alt, census=census_df, parameters=p, as_date=as_date), use_container_width=True
-)
+st.altair_chart(admitted_patients_chart(alt, census_df, p.n_days - 10, as_date=as_date, max_y_axis=p.max_y_axis), use_container_width=True)
 if st.checkbox("Show Projected Census in tabular form"):
     draw_census_table(st, census_df, as_date=as_date)
 st.markdown(
     """**Click the checkbox below to view additional data generated by this simulation**"""
 )
 if st.checkbox("Show Additional Projections"):
-    show_additional_projections(
-        st, alt,
-        additional_projections_chart,
-        parameters=p,
-        as_date=as_date)
+    show_additional_projections(st, alt, additional_projections_chart, p.infected_v, p.recovered_v, as_date=as_date, max_y_axis=p.max_y_axis)
     if st.checkbox("Show Raw SIR Simulation Data"):
-        draw_raw_sir_simulation_table(st, parameters=p, as_date=as_date)
+        draw_raw_sir_simulation_table(st, p.n_days, p.susceptible_v, p.infected_v, p.recovered_v, as_date=as_date)
 write_definitions(st)
 write_footer(st)
diff --git a/src/penn_chime/charts.py b/src/penn_chime/charts.py
deleted file mode 100644
index fb23935..0000000
--- a/src/penn_chime/charts.py
+++ /dev/null
@@ -1,125 +0,0 @@
-
-from altair import Chart  # type: ignore
-import pandas as pd  # type: ignore
-import numpy as np  # type: ignore
-
-from .parameters import Parameters
-from .utils import add_date_column
-
-
-def new_admissions_chart(
-    alt,
-    projection_admits: pd.DataFrame,
-    parameters: Parameters,
-    as_date: bool = False,
-) -> Chart:
-    """docstring"""
-    plot_projection_days = parameters.n_days - 10
-    max_y_axis = parameters.max_y_axis
-
-    y_scale = alt.Scale()
-
-    if max_y_axis is not None:
-        y_scale.domain = (0, max_y_axis)
-        y_scale.clamp = True
-
-    tooltip_dict = {False: "day", True: "date:T"}
-    if as_date:
-        projection_admits = add_date_column(projection_admits)
-        x_kwargs = {"shorthand": "date:T", "title": "Date"}
-    else:
-        x_kwargs = {"shorthand": "day", "title": "Days from today"}
-
-    return (
-        alt.Chart(projection_admits.head(plot_projection_days))
-        .transform_fold(fold=["Hospitalized", "ICU", "Ventilated"])
-        .mark_line(point=True)
-        .encode(
-            x=alt.X(**x_kwargs),
-            y=alt.Y("value:Q", title="Daily admissions", scale=y_scale),
-            color="key:N",
-            tooltip=[
-                tooltip_dict[as_date],
-                alt.Tooltip("value:Q", format=".0f", title="Admissions"),
-                "key:N",
-            ],
-        )
-        .interactive()
-    )
-
-
-def admitted_patients_chart(
-    alt,
-    census: pd.DataFrame,
-    parameters: Parameters,
-    as_date: bool = False
-) -> Chart:
-    """docstring"""
-
-    plot_projection_days = parameters.n_days - 10
-    max_y_axis = parameters.max_y_axis
-    if as_date:
-        census = add_date_column(census)
-        x_kwargs = {"shorthand": "date:T", "title": "Date"}
-        idx = "date:T"
-    else:
-        x_kwargs ={"shorthand": "day", "title": "Days from today"}
-        idx = "day"
-
-    y_scale = alt.Scale()
-
-    if max_y_axis:
-        y_scale.domain = (0, max_y_axis)
-        y_scale.clamp = True
-
-    return (
-        alt.Chart(census.head(plot_projection_days))
-        .transform_fold(fold=["Hospitalized Census", "ICU Census", "Ventilated Census"])
-        .mark_line(point=True)
-        .encode(
-            x=alt.X(**x_kwargs),
-            y=alt.Y("value:Q", title="Census", scale=y_scale),
-            color="key:N",
-            tooltip=[
-                idx,
-                alt.Tooltip("value:Q", format=".0f", title="Census"),
-                "key:N",
-            ],
-        )
-        .interactive()
-    )
-
-
-def additional_projections_chart(
-    alt,
-    i: np.ndarray,
-    r: np.ndarray,
-    as_date: bool = False,
-    max_y_axis: int = None
-) -> Chart:
-    dat = pd.DataFrame({"Infected": i, "Recovered": r})
-    dat["day"] = dat.index
-    if as_date:
-        dat = add_date_column(dat)
-        x_kwargs = {"shorthand": "date:T", "title": "Date"}
-    else:
-        x_kwargs = {"shorthand": "day", "title": "Days from today"}
-
-    y_scale = alt.Scale()
-
-    if max_y_axis is not None:
-        y_scale.domain = (0, max_y_axis)
-        y_scale.clamp = True
-
-    return (
-        alt.Chart(dat)
-        .transform_fold(fold=["Infected", "Recovered"])
-        .mark_line()
-        .encode(
-            x=alt.X(**x_kwargs),
-            y=alt.Y("value:Q", title="Case Volume", scale=y_scale),
-            tooltip=["key:N", "value:Q"],
-            color="key:N",
-        )
-        .interactive()
-    )
diff --git a/src/penn_chime/models.py b/src/penn_chime/models.py
index a578ad3..be31331 100644
--- a/src/penn_chime/models.py
+++ b/src/penn_chime/models.py
@@ -2,8 +2,8 @@
 
 from typing import Generator, Tuple
 
-import numpy as np  # type: ignore
-import pandas as pd  # type: ignore
+import numpy as np
+import pandas as pd
 
 
 def sir(
@@ -58,13 +58,13 @@ def sim_sir(
     )
 
 
-def sim_sir_df(p) -> pd.DataFrame:
-    """Simulate the SIR model forward in time.
-
-    p is a Parameters instance. for circuluar dependency reasons i can't annotate it.
-    """
+def sim_sir_df(
+    s: float, i: float, r: float,
+    beta: float, gamma: float, n_days: int
+) -> pd.DataFrame:
+    """Simulate the SIR model forward in time."""
     return pd.DataFrame(
-        data=gen_sir(p.susceptible, p.infected, p.recovered, p.beta, p.gamma, p.n_days),
+        data=gen_sir(s, i, r, beta, gamma, n_days),
         columns=("Susceptible", "Infected", "Recovered"),
     )
 
@@ -74,51 +74,3 @@ def get_dispositions(
 ) -> Tuple[np.ndarray, ...]:
     """Get dispositions of infected adjusted by rate and market_share."""
     return (*(infected * rate * market_share for rate in rates),)
-
-
-
-def build_admissions_df(p) -> pd.DataFrame:
-    """Build admissions dataframe from Parameters."""
-    days = np.array(range(0, p.n_days + 1))
-    data_dict = dict(zip(["day", "Hospitalized", "ICU", "Ventilated"],
-                         [days] + [disposition for disposition in p.dispositions]
-    ))
-    projection = pd.DataFrame.from_dict(data_dict)
-    # New cases
-    projection_admits = projection.iloc[:-1, :] - projection.shift(1)
-    projection_admits[projection_admits < 0] = 0
-    projection_admits["day"] = range(projection_admits.shape[0])
-    return projection_admits
-
-
-def build_census_df(
-        projection_admits: pd.DataFrame,
-        parameters
-) -> pd.DataFrame:
-    """ALOS for each category of COVID-19 case (total guesses)"""
-    n_days = np.shape(projection_admits)[0]
-    hosp_los, icu_los, vent_los = parameters.lengths_of_stay
-    los_dict = {
-        "Hospitalized": hosp_los,
-        "ICU": icu_los,
-        "Ventilated": vent_los,
-    }
-
-    census_dict = dict()
-    for k, los in los_dict.items():
-        census = (
-            projection_admits.cumsum().iloc[:-los, :]
-            - projection_admits.cumsum().shift(los).fillna(0)
-        ).apply(np.ceil)
-        census_dict[k] = census[k]
-
-    census_df = pd.DataFrame(census_dict)
-    census_df["day"] = census_df.index
-    census_df = census_df[["day", "Hospitalized", "ICU", "Ventilated"]]
-    census_df = census_df.head(n_days)
-    census_df = census_df.rename(
-        columns={disposition: f"{disposition} Census"
-                 for disposition
-                 in ("Hospitalized", "ICU", "Ventilated")}
-    )
-    return census_df
diff --git a/src/penn_chime/parameters.py b/src/penn_chime/parameters.py
index 38438d7..8678afa 100644
--- a/src/penn_chime/parameters.py
+++ b/src/penn_chime/parameters.py
@@ -1,6 +1,6 @@
 """Parameters."""
 
-from numpy import log2  # type: ignore
+from numpy import log2
 
 from .utils import RateLos
 from .models import (
diff --git a/src/penn_chime/presentation.py b/src/penn_chime/presentation.py
index 4d7c8aa..7744243 100644
--- a/src/penn_chime/presentation.py
+++ b/src/penn_chime/presentation.py
@@ -1,15 +1,12 @@
-"""effectful functions for streamlit io"""
-
-from typing import Optional
-
-import altair as alt  # type: ignore
-import numpy as np  # type: ignore
-import pandas as pd  # type: ignore
+import altair as alt
+import numpy as np
+import pandas as pd
 
 from .defaults import Constants, RateLos
 from .utils import add_date_column
 from .parameters import Parameters
 
+
 DATE_FORMAT = "%b, %d"  # see https://strftime.org
 
 
@@ -25,11 +22,26 @@ hide_menu_style = """
 ########
 
 
-def display_header(st, p):
+def display_header(
+    st,
+    total_infections,
+    initial_infections,
+    detection_prob,
+    current_hosp,
+    hosp_rate,
+    S,
+    market_share,
+    recovery_days,
+    r_naught,
+    doubling_time,
+    relative_contact_rate,
+    r_t,
+    doubling_time_t,
+):
 
     detection_prob_str = (
-        "{detection_prob:.0%}".format(detection_prob=p.detection_probability)
-        if p.detection_probability
+        "{detection_prob:.0%}".format(detection_prob=detection_prob)
+        if detection_prob is not None
         else "unknown"
     )
     st.markdown(
@@ -63,19 +75,19 @@ An initial doubling time of **{doubling_time}** days and a recovery time of **{r
 **Mitigation**: A **{relative_contact_rate:.0%}** reduction in social contact after the onset of the
 outbreak reduces the doubling time to **{doubling_time_t:.1f}** days, implying an effective $R_t$ of **${r_t:.2f}$**.
 """.format(
-            total_infections=p.infected,
-            initial_infections=p.known_infected,
+            total_infections=total_infections,
+            initial_infections=initial_infections,
             detection_prob_str=detection_prob_str,
-            current_hosp=p.current_hospitalized,
-            hosp_rate=p.hospitalized.rate,
-            S=p.susceptible,
-            market_share=p.market_share,
-            recovery_days=p.recovery_days,
-            r_naught=p.r_naught,
-            doubling_time=p.doubling_time,
-            relative_contact_rate=p.relative_contact_rate,
-            r_t=p.r_t,
-            doubling_time_t=p.doubling_time_t,
+            current_hosp=current_hosp,
+            hosp_rate=hosp_rate,
+            S=S,
+            market_share=market_share,
+            recovery_days=recovery_days,
+            r_naught=r_naught,
+            doubling_time=doubling_time,
+            relative_contact_rate=relative_contact_rate,
+            r_t=r_t,
+            doubling_time_t=doubling_time_t,
         )
     )
 
@@ -240,7 +252,12 @@ def display_n_days_slider(st, p: Parameters, d: Constants):
 
 def show_more_info_about_this_tool(
     st,
-    parameters,
+    recovery_days,
+    doubling_time,
+    r_naught,
+    relative_contact_rate,
+    doubling_time_t,
+    r_t,
     inputs: Constants,
     notes: str = ''
 ):
@@ -281,7 +298,7 @@ $$\\beta$$ can be interpreted as the _effective contact rate_:
 $\\gamma$ is the inverse of the mean recovery time, in days.  I.e.: if $\\gamma = 1/{recovery_days}$, then the average infection will clear in {recovery_days} days.
 
 An important descriptive parameter is the _basic reproduction number_, or $R_0$.  This represents the average number of people who will be infected by any given infected person.  When $R_0$ is greater than 1, it means that a disease will grow.  Higher $R_0$'s imply more rapid growth.  It is defined as """.format(
-            recovery_days=int(parameters.recovery_days)
+            recovery_days=int(recovery_days)
         )
     )
     st.latex("R_0 = \\beta /\\gamma")
@@ -312,12 +329,12 @@ We need to express the two parameters $\\beta$ and $\\gamma$ in terms of quantit
 - $\\gamma$:  the CDC is recommending 14 days of self-quarantine, we'll use $\\gamma = 1/{recovery_days}$.
 - To estimate $$\\beta$$ directly, we'd need to know transmissibility and social contact rates.  since we don't know these things, we can extract it from known _doubling times_.  The AHA says to expect a doubling time $T_d$ of 7-10 days. That means an early-phase rate of growth can be computed by using the doubling time formula:
 """.format(
-            doubling_time=parameters.doubling_time,
-            recovery_days=parameters.recovery_days,
-            r_naught=parameters.r_naught,
-            relative_contact_rate=parameters.relative_contact_rate,
-            doubling_time_t=parameters.doubling_time_t,
-            r_t=parameters.r_t,
+            doubling_time=doubling_time,
+            recovery_days=recovery_days,
+            r_naught=r_naught,
+            relative_contact_rate=relative_contact_rate,
+            doubling_time_t=doubling_time_t,
+            r_t=r_t,
         )
     )
     st.latex("g = 2^{1/T_d} - 1")
@@ -365,26 +382,149 @@ def write_footer(st):
     st.markdown("© 2020, The Trustees of the University of Pennsylvania")
 
 
+##########
+# Charts #
+##########
+
+
+def new_admissions_chart(
+    alt,
+    projection_admits: pd.DataFrame,
+    plot_projection_days: int,
+    as_date: bool = False,
+    max_y_axis: int = None
+) -> alt.Chart:
+    """docstring"""
+    projection_admits = projection_admits.rename(
+        columns={"hosp": "Hospitalized", "icu": "ICU", "vent": "Ventilated"}
+    )
+
+    y_scale = alt.Scale()
+
+    if max_y_axis is not None:
+        y_scale.domain = (0, max_y_axis)
+        y_scale.clamp = True
+
+    tooltip_dict = {False: "day", True: "date:T"}
+    if as_date:
+        projection_admits = add_date_column(projection_admits)
+        x_kwargs = {"shorthand": "date:T", "title": "Date"}
+    else:
+        x_kwargs = {"shorthand": "day", "title": "Days from today"}
+
+    return (
+        alt.Chart(projection_admits.head(plot_projection_days))
+        .transform_fold(fold=["Hospitalized", "ICU", "Ventilated"])
+        .mark_line(point=True)
+        .encode(
+            x=alt.X(**x_kwargs),
+            y=alt.Y("value:Q", title="Daily admissions", scale=y_scale),
+            color="key:N",
+            tooltip=[
+                tooltip_dict[as_date],
+                alt.Tooltip("value:Q", format=".0f", title="Admissions"),
+                "key:N",
+            ],
+        )
+        .interactive()
+    )
+
+
+def admitted_patients_chart(
+    alt,
+    census: pd.DataFrame,
+    plot_projection_days: int,
+    as_date: bool = False,
+    max_y_axis: int = None
+) -> alt.Chart:
+    """docstring"""
+    census = census.rename(
+        columns={
+            "hosp": "Hospital Census",
+            "icu": "ICU Census",
+            "vent": "Ventilated Census",
+        }
+    )
+    tooltip_dict = {False: "day", True: "date:T"}
+    if as_date:
+        census = add_date_column(census)
+        x_kwargs = {"shorthand": "date:T", "title": "Date"}
+    else:
+        x_kwargs ={"shorthand": "day", "title": "Days from today"}
+
+    y_scale = alt.Scale()
+
+    if max_y_axis is not None:
+        y_scale.domain = (0, max_y_axis)
+        y_scale.clamp = True
+
+    return (
+        alt.Chart(census.head(plot_projection_days))
+        .transform_fold(fold=["Hospital Census", "ICU Census", "Ventilated Census"])
+        .mark_line(point=True)
+        .encode(
+            x=alt.X(**x_kwargs),
+            y=alt.Y("value:Q", title="Census", scale=y_scale),
+            color="key:N",
+            tooltip=[
+                tooltip_dict[as_date],
+                alt.Tooltip("value:Q", format=".0f", title="Census"),
+                "key:N",
+            ],
+        )
+        .interactive()
+    )
+
+
+def additional_projections_chart(
+    alt,
+    i: np.ndarray,
+    r: np.ndarray,
+    as_date: bool = False,
+    max_y_axis: int = None
+) -> alt.Chart:
+    dat = pd.DataFrame({"Infected": i, "Recovered": r})
+    dat["day"] = dat.index
+    if as_date:
+        dat = add_date_column(dat)
+        x_kwargs = {"shorthand": "date:T", "title": "Date"}
+    else:
+        x_kwargs = {"shorthand": "day", "title": "Days from today"}
+
+    y_scale = alt.Scale()
+
+    if max_y_axis is not None:
+        y_scale.domain = (0, max_y_axis)
+        y_scale.clamp = True
+
+    return (
+        alt.Chart(dat)
+        .transform_fold(fold=["Infected", "Recovered"])
+        .mark_line()
+        .encode(
+            x=alt.X(**x_kwargs),
+            y=alt.Y("value:Q", title="Case Volume", scale=y_scale),
+            tooltip=["key:N", "value:Q"],
+            color="key:N",
+        )
+        .interactive()
+    )
+
 
 def show_additional_projections(
     st,
     alt,
     charting_func,
-    parameters,
+    i,
+    r,
     as_date: bool = False,
+    max_y_axis: int = None
 ):
     st.subheader(
         "The number of infected and recovered individuals in the hospital catchment region at any given moment"
     )
 
-    st.altair_chart(
-        charting_func(
-            alt,
-            parameters.infected_v,
-            parameters.recovered_v,
-            as_date=as_date,
-            max_y_axis=parameters.max_y_axis),
-        use_container_width=True)
+    st.altair_chart(charting_func(alt, i, r, as_date=as_date, max_y_axis=max_y_axis), use_container_width=True)
 
 
 ##########
@@ -429,13 +569,10 @@ def draw_census_table(st, census_df: pd.DataFrame, as_date: bool = False):
     return None
 
 
-def draw_raw_sir_simulation_table(
-        st,
-        parameters,
-        as_date: bool = False):
-    days = np.arange(0, parameters.n_days + 1)
-    data_list = [days, parameters.susceptible_v, parameters.infected_v, parameters.recovered_v]
-    data_dict = dict(zip(["day", "Susceptible", "Infections", "Recovered"], data_list))
+def draw_raw_sir_simulation_table(st, n_days, s, i, r, as_date: bool = False):
+    days = np.arange(0, n_days + 1)
+    data_list = [days, s, i, r]
+    data_dict = dict(zip(["day", "susceptible", "infections", "recovered"], data_list))
     projection_area = pd.DataFrame.from_dict(data_dict)
     infect_table = (projection_area.iloc[::7, :]).apply(np.floor)
     infect_table.index = range(infect_table.shape[0])
diff --git a/src/penn_chime/utils.py b/src/penn_chime/utils.py
index 731a63e..5a3c4e0 100644
--- a/src/penn_chime/utils.py
+++ b/src/penn_chime/utils.py
@@ -4,10 +4,8 @@ from collections import namedtuple
 from datetime import datetime, timedelta
 from typing import Optional
 
-import numpy as np  # type: ignore
-import pandas as pd  # type: ignore
-
-# from .parameters import Parameters
+import numpy as np
+import pandas as pd
 
 
 # (0.02, 7) is 2%, 7 days
@@ -15,6 +13,51 @@ import pandas as pd  # type: ignore
 RateLos = namedtuple('RateLos', ('rate', 'length_of_stay'))
 
 
+def build_admissions_df(
+    n_days,
+    hosp,
+    icu,
+    vent,
+) -> pd.DataFrame:
+    """Build admis dataframe."""
+    days = np.array(range(0, n_days + 1))
+    data_dict = dict(zip(["day", "hosp", "icu", "vent"], [days, hosp, icu, vent]))
+    projection = pd.DataFrame.from_dict(data_dict)
+    # New cases
+    projection_admits = projection.iloc[:-1, :] - projection.shift(1)
+    projection_admits[projection_admits < 0] = 0
+    projection_admits["day"] = range(projection_admits.shape[0])
+    return projection_admits
+
+
+def build_census_df(
+    projection_admits,
+    hosp_los,
+    icu_los,
+    vent_los,
+) -> pd.DataFrame:
+    """ALOS for each category of COVID-19 case (total guesses)"""
+    n_days = np.shape(projection_admits)[0]
+    los_dict = {
+        "hosp": hosp_los,
+        "icu": icu_los,
+        "vent": vent_los,
+    }
+
+    census_dict = dict()
+    for k, los in los_dict.items():
+        census = (
+            projection_admits.cumsum().iloc[:-los, :]
+            - projection_admits.cumsum().shift(los).fillna(0)
+        ).apply(np.ceil)
+        census_dict[k] = census[k]
+
+    census_df = pd.DataFrame(census_dict)
+    census_df["day"] = census_df.index
+    census_df = census_df[["day", "hosp", "icu", "vent"]]
+    census_df = census_df.head(n_days)
+    return census_df
+
 
 def add_date_column(
     df: pd.DataFrame,


Test patch:
diff --git a/src/test_app.py b/src/test_app.py
index faa4abb..5cfc2dd 100644
--- a/src/test_app.py
+++ b/src/test_app.py
@@ -1,28 +1,16 @@
 """Tests."""
 
-import pytest  # type: ignore
-import pandas as pd  # type: ignore
-import numpy as np  # type: ignore
+import pytest
+import pandas as pd
+import numpy as np
 
-from app import (admissions_df, alt)
+from app import (projection_admits, alt)
 from penn_chime.models import sir, sim_sir, sim_sir_df
 from penn_chime.parameters import Parameters
-from penn_chime.presentation import display_header
-from penn_chime.charts import new_admissions_chart
+from penn_chime.presentation import display_header, new_admissions_chart
 from penn_chime.settings import DEFAULTS
 from penn_chime.defaults import RateLos
-PARAM = Parameters(
-        current_hospitalized=100,
-        doubling_time=6.0,
-        known_infected=5000,
-        market_share=0.05,
-        relative_contact_rate=0.15,
-        susceptible=500000,
-        hospitalized=RateLos(0.05, 7),
-        icu=RateLos(0.02, 9),
-        ventilated=RateLos(0.01, 10),
-        n_days=60
-    )
+
 
 # set up
 
@@ -54,7 +42,7 @@ st = MockStreamlit()
 
 def test_penn_logo_in_header():
     penn_css = '<link rel="stylesheet" href="https://www1.pennmedicine.org/styles/shared/penn-medicine-header.css">'
-    display_header(st, PARAM)
+    display_header(st, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
     assert len(
         list(filter(lambda s: penn_css in s, st.render_store))
     ), "The Penn Medicine header should be printed"
@@ -76,7 +64,7 @@ def test_header_fail():
     Just proving to myself that these tests work
     """
     some_garbage = "ajskhlaeHFPIQONOI8QH34TRNAOP8ESYAW4"
-    display_header(st, PARAM)
+    display_header(st, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
     assert len(
         list(filter(lambda s: some_garbage in s, st.render_store))
     ), "This should fail"
@@ -157,32 +145,32 @@ def test_sim_sir():
         assert isinstance(v, np.ndarray)
 
 
-## THIS function never gets called in the app so i'm commenting out its test
-#ef test_sim_sir_df():
-#   """
-#   Rounding to move fast past decimal place issues
-#   """
-#
-#   df = sim_sir_df(PARAM)
-#   first = df.iloc[0]
-#   last = df.iloc[-1]
-#   assert round(first[0], 0) == 5
-#   assert round(first[1], 2) == 6
-#   assert round(first[2], 0) == 7
-#   assert round(last[0], 2) == 0
-#   assert round(last[1], 2) == 0.18
-#   assert round(last[2], 2) == 17.82
+
+def test_sim_sir_df():
+    """
+    Rounding to move fast past decimal place issues
+    """
+
+    df = sim_sir_df(5, 6, 7, 0.1, 0.1, 40)
+    first = df.iloc[0]
+    last = df.iloc[-1]
+    assert round(first[0], 0) == 5
+    assert round(first[1], 2) == 6
+    assert round(first[2], 0) == 7
+    assert round(last[0], 2) == 0
+    assert round(last[1], 2) == 0.18
+    assert round(last[2], 2) == 17.82
 
 
 def test_new_admissions_chart():
-    chart = new_admissions_chart(alt, admissions_df, PARAM)
+    chart = new_admissions_chart(alt, projection_admits, 60 - 10)
     assert isinstance(chart, alt.Chart)
     assert chart.data.iloc[1].Hospitalized < 1
     # assert round(chart.data.iloc[49].ICU, 0) == 43
     with pytest.raises(TypeError):
         new_admissions_chart()
 
-    empty_chart = new_admissions_chart(alt, pd.DataFrame(), PARAM)
+    empty_chart = new_admissions_chart(alt, pd.DataFrame(), -1)
     assert empty_chart.data.empty