Skip to content

Commit 4cb13f2

Browse files
Merge pull request #176 from CodeForPhilly/move_parameters
Move parameters
2 parents 6e195a8 + 3af49ec commit 4cb13f2

File tree

8 files changed

+159
-139
lines changed

8 files changed

+159
-139
lines changed

src/app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""App."""
2+
13
import altair as alt
24
import streamlit as st
35

src/cli.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""CLI for CHIME."""
1+
"""Command line interface."""
22

33
from argparse import (
44
Action,
@@ -8,9 +8,8 @@
88

99
from pandas import DataFrame
1010

11-
from penn_chime.defaults import RateLos
12-
from penn_chime.models import Parameters
13-
from penn_chime.utils import build_admissions_df, build_census_df
11+
from penn_chime.parameters import Parameters
12+
from penn_chime.utils import build_admissions_df, build_census_df, RateLos
1413

1514

1615
class FromFile(Action):

src/penn_chime/defaults.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
#!/usr/bin/env python
2-
"""Set defaults for your fork/locality here
1+
"""Defaults."""
32

4-
after we merge this in we can set defaults in a `config/env` type of file
5-
"""
6-
7-
from collections import namedtuple
8-
9-
# (0.02, 7) is 2%, 7 days
10-
# be sure to multiply by 100 when using as a default to the pct widgets!
11-
RateLos = namedtuple('RateLos', ('rate', 'length_of_stay'))
3+
from .utils import RateLos
124

135

146
class Regions:
@@ -54,4 +46,3 @@ def __init__(
5446

5547
def __repr__(self) -> str:
5648
return f"Constants(susceptible_default: {self.region.susceptible}, known_infected: {self.known_infected})"
57-

src/penn_chime/models.py

Lines changed: 2 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,11 @@
1+
"""Models."""
2+
13
from typing import Generator, Tuple
24

35
import numpy as np
46
import pandas as pd
5-
import streamlit as st
6-
7-
from .defaults import RateLos
8-
9-
10-
class Parameters:
11-
12-
def __init__(
13-
self, *,
14-
current_hospitalized: int,
15-
doubling_time: float,
16-
known_infected: int,
17-
market_share: float,
18-
relative_contact_rate: float,
19-
susceptible: int,
20-
21-
hospitalized: RateLos,
22-
icu: RateLos,
23-
ventilated: RateLos,
24-
25-
max_y_axis: int = None,
26-
n_days: int = None
27-
):
28-
self.current_hospitalized = current_hospitalized
29-
self.doubling_time = doubling_time
30-
self.known_infected = known_infected
31-
self.market_share = market_share
32-
self.relative_contact_rate = relative_contact_rate
33-
self.susceptible = susceptible
34-
self._n_days = 0
35-
36-
self.hospitalized = hospitalized
37-
self.icu = icu
38-
self.ventilated = ventilated
39-
40-
self.max_y_axis = max_y_axis
41-
42-
self.rates = tuple(
43-
each.rate
44-
for each in (hospitalized, icu, ventilated)
45-
)
46-
self.lengths_of_stay = tuple(
47-
each.length_of_stay
48-
for each in (hospitalized, icu, ventilated)
49-
)
50-
51-
# Note: this should not be an integer.
52-
# We're appoximating infected from what we do know.
53-
# TODO market_share > 0, hosp_rate > 0
54-
self.infected = infected = \
55-
current_hospitalized / market_share / hospitalized.rate
56-
57-
self.detection_probability = \
58-
known_infected / infected if infected > 1.e-7 else None
59-
60-
# TODO missing initial recovered value
61-
self.recovered = 0.0
62-
63-
self.intrinsic_growth_rate = intrinsic_growth_rate = \
64-
2.0 ** (1.0 / doubling_time) - 1.0 if doubling_time > 0.0 else 0.0
65-
66-
# TODO make this configurable, or more nuanced
67-
self.recovery_days = recovery_days = 14.0
68-
69-
self.gamma = gamma = 1.0 / recovery_days
70-
71-
# Contact rate, beta
72-
self.beta = beta = (
73-
(intrinsic_growth_rate + gamma) /
74-
susceptible * (1.0 - relative_contact_rate)
75-
) # {rate based on doubling time} / {initial susceptible}
76-
77-
# r_t is r_0 after distancing
78-
self.r_t = beta / gamma * susceptible
79-
80-
# Simplify equation to avoid division by zero:
81-
# self.r_naught = r_t / (1.0 - relative_contact_rate)
82-
self.r_naught = (intrinsic_growth_rate + gamma) / gamma
83-
84-
# doubling time after distancing
85-
# TODO constrain values np.log2(...) > 0.0
86-
self.doubling_time_t = 1.0 / np.log2(beta * susceptible - gamma + 1)
87-
88-
self.dispositions = None
89-
self.susceptible_v = self.infected_v = self.recovered_v = None
90-
self.hospitalized_v = self.icu_v = self.ventilated_v = None
91-
92-
if n_days is not None:
93-
self.n_days = n_days
94-
95-
@property
96-
def n_days(self):
97-
return self._n_days
98-
99-
@n_days.setter
100-
def n_days(self, n_days: int):
101-
self._n_days = n_days
102-
103-
s_v, i_v, r_v = sim_sir(
104-
self.susceptible,
105-
self.infected,
106-
self.recovered,
107-
self.beta,
108-
self.gamma,
109-
n_days,
110-
)
111-
self.susceptible_v, self.infected_v, self.recovered_v = s_v, i_v, r_v
112-
113-
self.dispositions = hospitalized_v, icu_v, ventilated_v = \
114-
get_dispositions(i_v, self.rates, self.market_share)
115-
self.hospitalized_v, self.icu_v, self.ventilated_v = \
116-
hospitalized_v, icu_v, ventilated_v
1177

1188

119-
@st.cache
1209
def sir(
12110
s: float, i: float, r: float,
12211
beta: float, gamma: float, n: float
@@ -148,7 +37,6 @@ def gen_sir(
14837
s, i, r = sir(s, i, r, beta, gamma, n)
14938

15039

151-
@st.cache
15240
def sim_sir(
15341
s: float, i: float, r: float,
15442
beta: float, gamma: float, n_days: int
@@ -170,7 +58,6 @@ def sim_sir(
17058
)
17159

17260

173-
@st.cache
17461
def sim_sir_df(
17562
s: float, i: float, r: float,
17663
beta: float, gamma: float, n_days: int
@@ -182,7 +69,6 @@ def sim_sir_df(
18269
)
18370

18471

185-
@st.cache
18672
def get_dispositions(
18773
infected: np.ndarray, rates: Tuple[float, ...], market_share: float = 1.0
18874
) -> Tuple[np.ndarray, ...]:

src/penn_chime/parameters.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""Parameters."""
2+
3+
from numpy import log2
4+
5+
from .utils import RateLos
6+
from .models import (
7+
get_dispositions,
8+
sim_sir,
9+
)
10+
11+
12+
class Parameters:
13+
14+
def __init__(
15+
self, *,
16+
current_hospitalized: int,
17+
doubling_time: float,
18+
known_infected: int,
19+
market_share: float,
20+
relative_contact_rate: float,
21+
susceptible: int,
22+
23+
hospitalized: RateLos,
24+
icu: RateLos,
25+
ventilated: RateLos,
26+
27+
max_y_axis: int = None,
28+
n_days: int = None
29+
):
30+
self.current_hospitalized = current_hospitalized
31+
self.doubling_time = doubling_time
32+
self.known_infected = known_infected
33+
self.market_share = market_share
34+
self.relative_contact_rate = relative_contact_rate
35+
self.susceptible = susceptible
36+
self._n_days = 0
37+
38+
self.hospitalized = hospitalized
39+
self.icu = icu
40+
self.ventilated = ventilated
41+
42+
self.max_y_axis = max_y_axis
43+
44+
self.rates = tuple(
45+
each.rate
46+
for each in (hospitalized, icu, ventilated)
47+
)
48+
self.lengths_of_stay = tuple(
49+
each.length_of_stay
50+
for each in (hospitalized, icu, ventilated)
51+
)
52+
53+
# Note: this should not be an integer.
54+
# We're appoximating infected from what we do know.
55+
# TODO market_share > 0, hosp_rate > 0
56+
self.infected = infected = \
57+
current_hospitalized / market_share / hospitalized.rate
58+
59+
self.detection_probability = \
60+
known_infected / infected if infected > 1.e-7 else None
61+
62+
# TODO missing initial recovered value
63+
self.recovered = 0.0
64+
65+
self.intrinsic_growth_rate = intrinsic_growth_rate = \
66+
2.0 ** (1.0 / doubling_time) - 1.0 if doubling_time > 0.0 else 0.0
67+
68+
# TODO make this configurable, or more nuanced
69+
self.recovery_days = recovery_days = 14.0
70+
71+
self.gamma = gamma = 1.0 / recovery_days
72+
73+
# Contact rate, beta
74+
self.beta = beta = (
75+
(intrinsic_growth_rate + gamma) /
76+
susceptible * (1.0 - relative_contact_rate)
77+
) # {rate based on doubling time} / {initial susceptible}
78+
79+
# r_t is r_0 after distancing
80+
self.r_t = beta / gamma * susceptible
81+
82+
# Simplify equation to avoid division by zero:
83+
# self.r_naught = r_t / (1.0 - relative_contact_rate)
84+
self.r_naught = (intrinsic_growth_rate + gamma) / gamma
85+
86+
# doubling time after distancing
87+
# TODO constrain values np.log2(...) > 0.0
88+
self.doubling_time_t = 1.0 / log2(beta * susceptible - gamma + 1)
89+
90+
self.dispositions = None
91+
self.susceptible_v = self.infected_v = self.recovered_v = None
92+
self.hospitalized_v = self.icu_v = self.ventilated_v = None
93+
94+
if n_days is not None:
95+
self.n_days = n_days
96+
97+
@property
98+
def n_days(self):
99+
return self._n_days
100+
101+
@n_days.setter
102+
def n_days(self, n_days: int):
103+
self._n_days = n_days
104+
105+
s_v, i_v, r_v = sim_sir(
106+
self.susceptible,
107+
self.infected,
108+
self.recovered,
109+
self.beta,
110+
self.gamma,
111+
n_days,
112+
)
113+
self.susceptible_v, self.infected_v, self.recovered_v = s_v, i_v, r_v
114+
115+
self.dispositions = hospitalized_v, icu_v, ventilated_v = \
116+
get_dispositions(i_v, self.rates, self.market_share)
117+
self.hospitalized_v, self.icu_v, self.ventilated_v = \
118+
hospitalized_v, icu_v, ventilated_v

src/penn_chime/presentation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
from .defaults import Constants, RateLos
66
from .utils import add_date_column
7-
from .models import Parameters
8-
DATE_FORMAT = "%b, %d" # see https://strftime.org
7+
from .parameters import Parameters
8+
9+
10+
DATE_FORMAT = "%b, %d" # see https://strftime.org
911

1012

1113
hide_menu_style = """

src/penn_chime/utils.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
from typing import Optional
1+
"""Utils."""
2+
3+
from collections import namedtuple
24
from datetime import datetime, timedelta
5+
from typing import Optional
6+
37
import numpy as np
48
import pandas as pd
5-
import streamlit as st
69

7-
@st.cache
8-
def build_admissions_df(n_days, hosp, icu, vent) -> pd.DataFrame:
10+
11+
# (0.02, 7) is 2%, 7 days
12+
# be sure to multiply by 100 when using as a default to the pct widgets!
13+
RateLos = namedtuple('RateLos', ('rate', 'length_of_stay'))
14+
15+
16+
def build_admissions_df(
17+
n_days,
18+
hosp,
19+
icu,
20+
vent,
21+
) -> pd.DataFrame:
22+
"""Build admis dataframe."""
923
days = np.array(range(0, n_days + 1))
1024
data_dict = dict(zip(["day", "hosp", "icu", "vent"], [days, hosp, icu, vent]))
1125
projection = pd.DataFrame.from_dict(data_dict)
@@ -15,8 +29,13 @@ def build_admissions_df(n_days, hosp, icu, vent) -> pd.DataFrame:
1529
projection_admits["day"] = range(projection_admits.shape[0])
1630
return projection_admits
1731

18-
@st.cache
19-
def build_census_df(projection_admits, hosp_los, icu_los, vent_los) -> pd.DataFrame:
32+
33+
def build_census_df(
34+
projection_admits,
35+
hosp_los,
36+
icu_los,
37+
vent_los,
38+
) -> pd.DataFrame:
2039
"""ALOS for each category of COVID-19 case (total guesses)"""
2140
n_days = np.shape(projection_admits)[0]
2241
los_dict = {
@@ -39,6 +58,7 @@ def build_census_df(projection_admits, hosp_los, icu_los, vent_los) -> pd.DataFr
3958
census_df = census_df.head(n_days)
4059
return census_df
4160

61+
4262
def add_date_column(
4363
df: pd.DataFrame,
4464
drop_day_column: bool = False,

src/test_app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Tests."""
2+
13
import pytest
24
import pandas as pd
35

0 commit comments

Comments
 (0)