def get_dataframe(regions, days, targets, columns, duration): models = get_models(regions, targets, duration) frames = [] prev_day = 0 for day in days: delta = (day, prev_day) for target in targets: frame = pd.DataFrame( { col: get_column(models, regions, target, col, delta, duration) for col in columns } ).astype(int) names = ("days", "isolation", "data") prepend = ( _("{n} days").format(n=day), _("isolation {pc}").format(pc=pc(target / 100)), ) cols = ((*prepend, c) for c in frame.columns) frame.columns = pd.MultiIndex.from_tuples(cols, names=names) frames.append(frame) prev_day = day df = pd.concat(frames, axis=1) extra = df.mundi["numeric_code", "short_code", "name"] extra = extra.astype(str) # streamlit bug? extra.columns = pd.MultiIndex.from_tuples(("info", x, "") for x in extra.columns) df = pd.concat([extra, df], axis=1) return df.sort_values(df.columns[0])
def collect_inputs(parent_region="BR", where=st.sidebar): """ Collect input parameters for the app to run. Returns: Dictionary with the following keys: parent_region, regions, columns, targets, days """ st = where kind = st.selectbox(_("Select scenario"), list(REGIONS_TYPES[parent_region])) query = REGIONS_TYPES[parent_region][kind] regions = get_regions(**query) msg = _("Columns") columns = st.multiselect(msg, COLUMNS, default=COLUMNS_DEFAULT) msg = _("Isolation scores") kwargs = {"default": TARGETS_DEFAULT, "format_func": lambda x: f"{x}%"} targets = st.multiselect(msg, TARGETS, **kwargs) msg = _("Show values for the given days") days = st.multiselect(msg, DAYS, default=DAYS_DEFAULT) return { "parent_region": parent_region, "regions": regions, "columns": columns, "targets": targets, "days": days, }
def explore_object(name, obj, where=st): """ Explore methods of object. """ fn = select_method(name, obj, where=where) args, kwargs = select_arguments(fn, where=where) t0 = time.time() msg = st.empty() result = fn(*args, **kwargs) msg.info(_("Method executed in {} seconds.").format(fmt(time.time() - t0))) st.line() st.subheader(_("Method help and signature")) st.help(fn) if result is not None: st.line() st.subheader(_("Function output")) if isinstance(result, plt.Axes): st.markdown(_("Function returned a matplotlib **ax** object. Showing it...")) st.pyplot(result.get_figure()) else: st.write(result)
def show_results( parent_region, regions, columns, targets, days, scenario, disease=covid19, transpose=False, ): """ Show results from user input. """ parent_region = mundi.region(parent_region) parent_region.ui.cases_and_deaths(disease=disease, grid=True, logy=True) if days and targets and columns: info = scenario["info"] info_cols = tuple(info) df = get_dataframe( regions, tuple(days), tuple(targets), tuple(columns), info_cols=info_cols ) get = {**COL_NAMES, **info}.get df.columns = pd.MultiIndex.from_tuples( [tuple(_(get(x, x) for x in t)) for t in df.columns.to_list()] ) if transpose: df = df.T st.subheader(_("Download results")) st.dataframe_download(df, name="report-brazil.{ext}")
def ask(self, parent_region="BR", where=st.sidebar): st = where scenario_kind = st.selectbox(_("Select scenario"), list(REGIONS_TYPES[parent_region])) query = REGIONS_TYPES[parent_region][scenario_kind] regions = self.__datahandler.get_regions(**query) message = _("Columns") columns = st.multiselect(message, COLUMNS, default=COLUMNS_DEFAULT) message = _("Isolation scores") kwargs = {"default": TARGETS_DEFAULT, "format_func": lambda x: f"{x}%"} targets = st.multiselect(message, TARGETS, **kwargs) message = _("Show values for the given days") days = st.multiselect(message, DAYS, default=DAYS_DEFAULT) self.__datahandler.user_inputs = { "parent_region": parent_region, "regions": regions, "columns": columns, "targets": targets, "days": days, "disease": "covid-19" }
def run(self): super().run() models = ["crude", "delay", "overflow"] kind = st.sidebar.selectbox(_("Clinical model"), models) m = SEAIR(region="BR", disease=covid19) cm = m.clinical(kind) cm.ui.summary_table(subheader=_("Parameters table"))
def show(self): """ Show results from user input. """ parent_region = self.user_inputs["parent_region"] regions = self.user_inputs["regions"] columns = self.user_inputs["columns"] targets = self.user_inputs["targets"] days = self.user_inputs["days"] scenario = self.user_inputs["scenario"] transpose = self.user_inputs["transpose"] disease = self.user_inputs["disease"] parent_region = mundi.region(parent_region) parent_region.ui.cases_and_deaths(disease=disease, grid=True, logy=True) if days and targets and columns: info = scenario["info"] info_cols = tuple(info) df = self.get_dataframe( regions, tuple(days), tuple(targets), tuple(columns), info_cols=info_cols ) get = {**COL_NAMES, **info}.get df.columns = pd.MultiIndex.from_tuples( [tuple(_(get(x, x) for x in t)) for t in df.columns.to_list()] ) if transpose: df = df.T st.subheader(_("Download results")) st.dataframe_download(df, name="report-brazil.{ext}")
def get_dataframe(self, days, targets, columns): regions = self.user_inputs["regions"] frames = [] prev_day = 0 for day in days: delta = (day, prev_day) for target in targets: frame = pd.DataFrame( { column: self.__get_column(regions, target, column, delta) for column in columns } ).astype(int) columns_names = ("days", "isolation", "data") prepend = ( _("{n} days").format(n=day), _("isolation {pc}").format(pc=pc(target / 100)), ) cols = ((*prepend, col) for col in frame.columns) frame.columns = pd.MultiIndex.from_tuples(cols, names=columns_names) frames.append(frame) prev_day = day df = pd.concat(frames, axis=1) extra_info = df.mundi["numeric_code", "short_code", "name"] extra_info = extra_info.astype(str) # streamlit bug? extra_info.columns = pd.MultiIndex.from_tuples(("info", extra_col, "") for extra_col in extra.columns) df = pd.concat([extra_info, df], axis=1) return df.sort_values(df.columns[0])
def report_intro(self, region): name = _(region.name) return _( """{name} is in a **(good|bad|ugly)** state, yadda, yadda, yadda. The plot bellow shows the progression of cases and deaths. """).format(**locals())
def ask(self): """ Ask for user input and save values as properties in the app. """ self.where.header(("Options")) self.region = self.where.text_input(_("Mundi region code"), value="BR") try: self.region = mundi.region(self.region) except LookupError: self.where.error("Invalid mundi region code.") return options = { "model": _("A Pydemic Model"), "region": _("A Mundi Region"), "components": _("Input components"), } message = _("What do you want to explore?") option = self.where.radio(message, list(options), format_func=options.get) self.option = option self.object = self.handle_explore_option(option)
def sidebar(where=st.sidebar): st = where st.subheader(_("Section")) msg = _("Which section do you want to see?") key = st.radio(msg, list(APPS), format_func=lambda x: APPS[x].DISPLAY_NAME) app = APPS[key] opts = app.options(where=where) return app.show, opts
def ask(self, parent_region="BR", where=st.sidebar): st = where scenario_kind = st.selectbox(_("Select scenario"), list(REGIONS_TYPES[parent_region])) scenario = REGIONS_TYPES[parent_region][scenario_kind] regions = self.__datahandler.get_regions(**scenario["query"]) if "filter" in scenario: filtering = scenario["filter"] format_func = filtering.pop("format_func", None) if format_func is not None: function = format_func def format_func(x): if x == "all": return _("All") return function(x) field, message = filtering.popitem() groups = sk.group_by(lambda x: getattr(x, field), regions) key = st.selectbox(message, ["all", *groups], format_func=format_func) if key != "all": regions = groups[key] message = _("Columns") columns = st.multiselect(message, COLUMNS, default=COLUMNS_DEFAULT) message = _("Isolation scores") kwargs = {"default": TARGETS_DEFAULT, "format_func": lambda x: f"{x}%"} targets = st.multiselect(message, TARGETS, **kwargs) message = _("Show values for the given days") days = st.multiselect(message, DAYS, default=DAYS_DEFAULT) if any(not isinstance(d, int) for d in days): day_max = sk.pipe( days, sk.remove(lambda d: isinstance(d, int)), sk.map(lambda d: int(NUMBER.search(d).groups()[0])), max, ) days = list(range(1, day_max + 1)) message = _("Transpose data") transpose = st.checkbox(message, value=False) self.__datahandler.user_inputs = { "parent_region": parent_region, "regions": regions, "columns": columns, "targets": targets, "days": days, "scenario": scenario, "transpose": transpose, "disease": "covid-19" }
def main(embed=False): if not embed: st.css(keep_menu=True) st.sidebar.logo() models = ["crude", "delay", "overflow"] kind = st.sidebar.selectbox(_("Clinical model"), models) m = SEAIR(region="BR", disease=covid19) cm = m.clinical(kind) cm.ui.summary_table(subheader=_("Parameters table"))
def show(self): by_region, by_state = self.tables self.st.markdown(self.message.format(self=self)) self.st.subheader(_("Epidemic situation, by region")) self.st.markdown(_("Cases and deaths by 100k people.")) self.show_table(by_region) self.st.subheader(_("Epidemic situation, by state")) self.st.markdown(_("Cases and deaths by 100k people.")) self.show_table(by_state)
def progression_cards(deaths, color="st-blue"): deaths = deaths.values[-60:] st.cards( { _("7 days"): fmt(deaths[6]), _("15 days"): fmt(deaths[14]), _("30 days"): fmt(deaths[29]), _("60 days"): fmt(deaths[59]), }, color=color, )
def collect_inputs(region="BR", where=st.sidebar): states = mundi.regions(region, type="state") highlight = where.selectbox(_("Select a state to highlight"), states.index) msg = _("Which kind of curve to you want to analyze?") which = where.selectbox(msg, ["cases", "deaths"]) return { "loc": highlight, "idx": int(which == "deaths"), "states": states, "which": which, }
def explore_function_output(self, result): if result is not None: self.where = st self.where.line() self.where.subheader(_("Function output")) if isinstance(result, plt.Axes): self.where.markdown( _("Function returned a matplotlib **ax** object. Showing it..." )) self.where.pyplot(result.get_figure()) else: self.where.write(result)
def select_arguments(fn, where=st): """ Select arguments from a callable object. This method only works if we are able to introspect the object signature. """ st = where sig = inspect.Signature.from_callable(fn) args = [] kwargs = {} show_title = True for k, v in sig.parameters.items(): if k == "where": continue if show_title: st.subheader(_("Arguments")) show_title = False if v.annotation == str: kwargs[k] = st.text_input(k) elif v.annotation == bool: kwargs[k] = st.checkbox(k) elif v.annotation == float: kwargs[k] = st.number_input(k) elif v.annotation == int: kwargs[k] = st.number_input(k) else: st.text(k) return args, kwargs
def show(self, model, title=_("Hospital pressure calculator")): """ Create default output from model. """ if title: st.title(title) model.ui.summary_cards() st.pause() model.ui.hospitalizations_chart() st.pause() model.ui.available_beds_chart() st.line() ui.population_info_chart(model.age_pyramid) st.pause() model.ui.deaths_chart() st.line() model.ui.healthcare_parameters() st.pause() model.ui.ppe_demand_table() st.pause() model.ui.epidemiological_parameters() st.pause() st.footnotes()
def get_models(regions, targets, duration) -> dict: models = {} for region in regions: with st.spinner(_("Processing {name}").format(name=region.name)): result = process_region(region, targets, duration) models.update({(region, k): v for k, v in result.items()}) return models
def sidebar( region="BR", disease=covid19, where=st.sidebar, secret_date=None, secret_function=None ): """ Calculator sidebar element. It receives a region and a disease (defaults to Covid-19) and return a dictionary of parameters that might be useful to configure a simulation. """ st = where st.logo() region = st.region_input(region, sus_regions=True, arbitrary=True) try: params = st.simulation_params(region, disease, secret_date=secret_date) except RuntimeError: st = globals()["st"] st.title(_("Secret area for beta testers")) secret_function() return return { "region": region, **params, **st.healthcare_params(region), **st.epidemiological_params(region, disease), **{"runner": st.intervention_runner_input(params["period"])}, }
def get_dataframe(self, days, columns, info_cols=()): regions = self.user_inputs["regions"] steps = len(self.user_inputs["regions"]) duration = max(days) days_ranges = np.array([0, *days]) columns = list(columns) progress_bar = st.progress(0) with st.spinner(_("Running simulations")): rows = {} for i, region in enumerate(regions): base, group = self.__run_simulations(region, duration) progress_bar.progress(int(100 * i / steps)) cols = {} for a, b in sk.window(2, days_ranges): day = b a += base.time + 1 b += a - 1 renames = dict(zip(itertools.count(), columns)) name = _("{} days").format(day) cols[name] = ( pd.DataFrame(group[columns, a:b].max(0)) .T.rename(columns=renames) .rename(index={0: region.id}) .astype(int) ) keys = [*cols] cols_data = [*cols.values()] rows[region.id] = pd.concat(cols_data, axis=1, names=[_("days")], keys=keys) progress_bar.empty() cols_data = pd.concat(list(rows.values())) cols_data.index = rows.keys() if info_cols: extra_info = cols_data.mundi[info_cols] extra_info = extra_info.astype(object) # streamlit bug? extra_info.columns = pd.MultiIndex.from_tuples(("", "info", x) for x in extra_info.columns) data = pd.concat([extra_info, cols_data], axis=1) return cols_data.sort_values(cols_data.columns[0]) else: return cols_data.sort_index()
def list_top(data: pd.Series): *head, last = sk.pipe( data.sort_values(ascending=False).index[:top], sk.map(mundi.region), sk.map("{0.name}".format), ) head = ", ".join(head) return _(" and ").join([head, last])
def _other_regions(self, col): data, _ = self.tables names = data.sort_values(col).mundi["name"] *other, last = names.iloc[1:] other = ", ".join(other) if other: return _(" and ").join([other, last]) return last
def hospitalization_chart(model, shift=0): df = model[["severe", "critical"]] df.index = df.index - shift df.plot(grid=grid) plt.mark_y(model.hospital_surge_capacity, "--", color=plt.color(2), label=_("Hospital beds")) plt.mark_y(model.icu_surge_capacity, "--", color=plt.color(2), label=_("ICU")) if shift: plt.mark_x(0, "k--") plt.legend() plt.tight_layout("x") st.pyplot()
def __get_models(self) -> dict: models = {} regions = self.user_inputs["regions"] for region in regions: with st.spinner(_("Processing {name}").format(name=region.name)): result = self.__process_region(region) models.update({(region, k): v for k, v in result.items()}) return models
def main(embed=False, disease=covid19): if not embed: ui.css(keep_menu=True) if not embed: ui.logo(where=st.sidebar) st.title(_("Projections for COVID-19 evolution in Brazil")) inputs = collect_inputs(where=st if embed else st.sidebar) show_results(disease=disease, **inputs)
def explore_object(self, name, obj): """ Explore methods of object. """ self.where = st.sidebar method = self.select_method(name, obj) args, kwargs = self.select_arguments(method) start = time.time() message = st.empty() result = method(*args, **kwargs) message.info( _("Method executed in {} seconds.").format(fmt(time.time() - start))) self.where.line() self.where.subheader(_("Method help and signature")) self.where.help(method) self.explore_function_output(result)
def natural_date(x): """ Friendly representation of dates. String inputs are returned as-is. """ if isinstance(x, str): return x elif x is None: return _("Not soon...") else: return format_date(x, format="short")
def information_message(self, results, params, epidemiology, clinical, model, clinical_model): if results: self.st.html('<div style="height: 15rem"></div>') self.st.html('<h2 id="debug">{}</h2>'.format(_("Debug information"))) self.st.subheader(_("Generic parameters")) self.st.write(params) self.st.subheader(_("Epidemiological parameters")) self.st.write(epidemiology) self.st.subheader(_("Clinical parameters")) self.st.write(clinical) self.st.subheader(_("Output")) self.st.write(results) if model: self.st.line_chart(model.data) if clinical_model: self.st.line_chart( clinical_model[["infectious", "severe", "critical"]]) self.st.subheader(_("Distribution of deaths")) df = clinical_model[DEATH_DISTRIBUTION_COLUMNS] df.columns = [DEATH_DISTRIBUTION_COL_NAMES[k] for k in df.columns] self.st.area_chart(df)