Repository: CodeForPhilly/chime (python)

Issue:
Fix calculation of daily new infections
Current daily new infections are being calculated as $I_{t+1} - I_{t}$, which doesn't account for the infected cases transitioning into recovered during that time step. The calculations should be $I_{new} = (I_{t+1} - I_{t}) + (R_{t+1} - R_{t})$

Pull request:
Fix calculation of new infected cases
Closes #189 

Patch:
diff --git a/src/penn_chime/charts.py b/src/penn_chime/charts.py
index fb23935..c29bd5b 100644
--- a/src/penn_chime/charts.py
+++ b/src/penn_chime/charts.py
@@ -74,7 +74,7 @@ def admitted_patients_chart(
 
     return (
         alt.Chart(census.head(plot_projection_days))
-        .transform_fold(fold=["Hospitalized Census", "ICU Census", "Ventilated Census"])
+        .transform_fold(fold=["Hospitalized", "ICU", "Ventilated"])
         .mark_line(point=True)
         .encode(
             x=alt.X(**x_kwargs),
diff --git a/src/penn_chime/models.py b/src/penn_chime/models.py
index a578ad3..62219b1 100644
--- a/src/penn_chime/models.py
+++ b/src/penn_chime/models.py
@@ -70,11 +70,10 @@ def sim_sir_df(p) -> pd.DataFrame:
 
 
 def get_dispositions(
-    infected: np.ndarray, rates: Tuple[float, ...], market_share: float = 1.0
+    patient_state: np.ndarray, rates: Tuple[float, ...], market_share: float = 1.0
 ) -> Tuple[np.ndarray, ...]:
     """Get dispositions of infected adjusted by rate and market_share."""
-    return (*(infected * rate * market_share for rate in rates),)
-
+    return (*(patient_state * rate * market_share for rate in rates),)
 
 
 def build_admissions_df(p) -> pd.DataFrame:
@@ -86,7 +85,6 @@ def build_admissions_df(p) -> pd.DataFrame:
     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
 
@@ -117,7 +115,7 @@ def build_census_df(
     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"
+        columns={disposition: f"{disposition}"
                  for disposition
                  in ("Hospitalized", "ICU", "Ventilated")}
     )
diff --git a/src/penn_chime/parameters.py b/src/penn_chime/parameters.py
index 38438d7..37b3a4c 100644
--- a/src/penn_chime/parameters.py
+++ b/src/penn_chime/parameters.py
@@ -112,7 +112,13 @@ class Parameters:
         )
         self.susceptible_v, self.infected_v, self.recovered_v = s_v, i_v, r_v
 
-        self.dispositions = hospitalized_v, icu_v, ventilated_v = \
+        i_hospitalized_v, i_icu_v, i_ventilated_v = \
             get_dispositions(i_v, self.rates, self.market_share)
+        r_hospitalized_v, r_icu_v, r_ventilated_v = \
+            get_dispositions(r_v, self.rates, self.market_share)
+
+        self.dispositions = (i_hospitalized_v+r_hospitalized_v, 
+            i_icu_v+r_icu_v, i_ventilated_v+r_ventilated_v)
+
         self.hospitalized_v, self.icu_v, self.ventilated_v = \
-            hospitalized_v, icu_v, ventilated_v
+            i_hospitalized_v, i_icu_v, i_ventilated_v
diff --git a/src/penn_chime/presentation.py b/src/penn_chime/presentation.py
index 4d7c8aa..4b37660 100644
--- a/src/penn_chime/presentation.py
+++ b/src/penn_chime/presentation.py
@@ -44,6 +44,7 @@ def display_header(st, p):
     """,
         unsafe_allow_html=True,
     )
+    st.markdown("""**IMPORTANT NOTICE**: Admissions and Census calculations were previously **undercounting**. Please update your reports. See more about this issue [here](https://github.com/CodeForPhilly/chime/pull/190).""")
     st.markdown(
         """*This tool was developed by the [Predictive Healthcare team](http://predictivehealthcare.pennmedicine.org/) at
     Penn Medicine. For questions on how to use this tool see the [User docs](https://code-for-philly.gitbook.io/chime/). For questions and comments please see our


Test patch:
diff --git a/tests/test_app.py b/tests/test_app.py
index fc51e6a..73df291 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -6,7 +6,7 @@ import numpy as np  # type: ignore
 import altair as alt  # type: ignore
 
 from src.penn_chime.charts import new_admissions_chart, admitted_patients_chart
-from src.penn_chime.models import sir, sim_sir
+from src.penn_chime.models import sir, sim_sir, build_admissions_df
 from src.penn_chime.parameters import Parameters
 from src.penn_chime.presentation import display_header
 from src.penn_chime.settings import DEFAULTS
@@ -228,8 +228,14 @@ def test_parameters():
     assert round(param.infected_v[1], 0) == 43735
     assert round(param.recovered_v[30], 0) == 224048
     assert [d[0] for d in param.dispositions] == [100.0, 40.0, 20.0]
-    assert [round(d[-1], 0) for d in param.dispositions] == [115.0, 46.0, 23.0]
+    assert [round(d[-1], 0) for d in param.dispositions] == [1182.0, 473.0, 236.0]
 
     # change n_days, make sure it cascades
     param.n_days = 2
     assert len(param.susceptible_v) == len(param.infected_v) == len(param.recovered_v) == param.n_days + 1 == 3
+
+    # test that admissions are being properly calculated (thanks @PhilMiller)
+    admissions = build_admissions_df(param)
+    cumulative_admissions = admissions.cumsum()
+    diff = cumulative_admissions["Hospitalized"][1:-1] - (0.05*0.05 * (param.infected_v[1:-1] + param.recovered_v[1:-1]) - 100)
+    assert (diff.abs() < 0.1).all()