def __init__(self, fips, model_ensemble, county_outputs, filename, summary, xlim=(0, 360)): self.fips = fips self.county_outputs = county_outputs self.model_ensemble = model_ensemble self.county_metadata = load_data.load_county_metadata_by_fips(fips) self.summary = summary timeseries = combined_datasets.get_timeseries_for_fips( fips, columns=[CommonFields.CASES, CommonFields.DEATHS], min_range_with_some_value=True ) self.county_case_data = timeseries.data self.report = PDFReport(filename=filename) self.xlim = xlim
def __init__(self, fips, model_ensemble, county_outputs, filename, summary, xlim=(0, 360)): self.fips = fips self.county_outputs = county_outputs self.model_ensemble = model_ensemble self.county_metadata = load_data.load_county_metadata_by_fips(fips) self.summary = summary _county_case_data = load_data.load_county_case_data() self.county_case_data = _county_case_data[_county_case_data['fips'] == fips] self.report = PDFReport(filename=filename) self.xlim = xlim
def generate_report(self): """ Generate a full report for the state. """ report = PDFReport(filename=self.filename) for compartment in self.plot_compartments: fig = self.plot_compartment(compartment) report.add_figure(fig=fig) report.close() self.generate_surge_spreadsheet()
class CountyReport: """ Generate a county level report showing detailed data on different scenarios. Parameters ---------- fips: str County fips code. model_ensemble: list(SEIRModel) Models from the ensemble. county_outputs: dict Dictionary of structure (in yaml syntax) suppression_policy__0.5: t_list: array of timestamps HICU: For compartment S we have a bunch of properties S_ci50: the median posterior model. Other condidence intervals are available. ... peak_value_ciXX: peak_time_ciXX: ... # For HICU, HVent, HGen, capacities are relevant, so we also calculate surge windows. capacity: estimated capacity for this compartment. surge_start: array surge window starts for each model in the ensemble surge_end: filename: str Where to save the report. summary: dict Summary attrs to print on the first page of the report. xlim: tuple Limits in days since simulation start to plot. """ def __init__(self, fips, model_ensemble, county_outputs, filename, summary, xlim=(0, 360)): self.fips = fips self.county_outputs = county_outputs self.model_ensemble = model_ensemble self.county_metadata = load_data.load_county_metadata_by_fips(fips) self.summary = summary _county_case_data = load_data.load_county_case_data() self.county_case_data = _county_case_data[_county_case_data['fips'] == fips] self.report = PDFReport(filename=filename) self.xlim = xlim def generate_and_save(self): """ Name says it all. """ self.write_header_pages() self.plot_seir_distributions() self.report.close() def write_header_pages(self): """ Generate header pages with the county summary and PDF summaries. """ self.report.write_text_page( self.summary, title= f'PySEIR COVID19 Estimates\n{self.county_metadata["county"]} County, {self.county_metadata["state"]}', page_heading=f'Generated {self.summary["date_generated"]}', body_fontsize=6, title_fontsize=12) self.report.write_text_page(inspect.getsource( ParameterEnsembleGenerator.sample_seir_parameters), title='PySEIR Model Ensemble Parameters') def plot_seir_distributions(self, xlim=(0, 360)): """ Generate plots for each suppression policy containing distributions and peak information for each model compartment. Parameters ---------- xlim: tuple Limits in days since simulation start to plot. """ # Add a sample model from the ensemble. fig = self.model_ensemble[0].plot_results(xlim=xlim) fig.suptitle( f'PySEIR COVID19 Estimates: {self.county_metadata["county"]} County, {self.county_metadata["state"]}. ' f'SAMPLE OF MODEL ENSEMBLE', fontsize=16) self.report.add_figure(fig) for suppression_policy, output in self.county_outputs.items(): compartments = list(output.keys()) compartments.remove('t_list') # ----------------------------------- # Plot each compartment distribution # ----------------------------------- fig = plt.figure(figsize=(20, 24)) fig.suptitle( f'PySEIR COVID19 Estimates: {self.county_metadata["county"]} County, {self.county_metadata["state"]}. ' f'\nSupression Policy={suppression_policy} (1=No Suppression)', fontsize=16) for i_plot, compartment in enumerate(compartments): plt.subplot(5, 5, i_plot + 1) plt.plot(output['t_list'], output[compartment]['ci_50'], color='steelblue', linewidth=3, label=compartment_to_name_map[compartment]) plt.fill_between(output['t_list'], output[compartment]['ci_32'], output[compartment]['ci_68'], alpha=.3, color='steelblue') plt.fill_between(output['t_list'], output[compartment]['ci_5'], output[compartment]['ci_95'], alpha=.3, color='steelblue') plt.yscale('log') plt.ylim(1e0) plt.xlim(0, 360) plt.grid(True, which='both', alpha=0.3) plt.xlabel('Days Since Case 0') # Circular import :( from pyseir.ensembles.ensemble_runner import compartment_to_capacity_attr_map if compartment in compartment_to_capacity_attr_map: percentiles = np.percentile([ getattr(m, compartment_to_capacity_attr_map[compartment]) for m in self.model_ensemble ], (5, 32, 50, 68, 95)) plt.hlines(percentiles[2], *plt.xlim(), label='ICU Capacity', color='darkseagreen') plt.hlines([percentiles[0], percentiles[4]], *plt.xlim(), color='darkseagreen', linestyles='-.', alpha=.4) plt.hlines([percentiles[1], percentiles[3]], *plt.xlim(), color='darkseagreen', linestyles='--', alpha=.2) # Plot data if compartment in ['D', 'total_deaths' ] and len(self.county_case_data) > 0: plt.errorbar((self.county_case_data.date - self.summary['t0']).dt.days, self.county_case_data.deaths, yerr=np.sqrt(self.county_case_data.deaths), linestyle='-', label='Deaths Observed', marker='o', markersize=4) if compartment in ['I'] and len(self.county_case_data) > 0: plt.errorbar((self.county_case_data.date - self.summary['t0']).dt.days, self.county_case_data.cases, yerr=np.sqrt(self.county_case_data.cases), linestyle='-', label='Cases Observed', marker='o', markersize=4, color='firebrick') plt.legend() self._plot_dates(log=False) # ----------------------------- # Plot peak Timing # ----------------------------- color_cycle = plt.rcParams['axes.prop_cycle'].by_key( )['color'] + list('bgrcmyk') marker_cycle = ['o', 's', '+', 'd', 'o'] * 4 plt.subplot(5, 5, len(compartments) + 1) for i, compartment in enumerate([ 'E', 'A', 'I', 'HGen', 'HICU', 'HVent', 'general_admissions_per_day', 'icu_admissions_per_day', 'direct_deaths_per_day', 'total_deaths_per_day' ]): median = output[compartment]['peak_time_ci50'] ci5, ci95 = output[compartment]['peak_time_ci5'], output[ compartment]['peak_time_ci95'] ci32, ci68 = output[compartment]['peak_time_ci32'], output[ compartment]['peak_time_ci68'] plt.scatter(median, i, label=compartment_to_name_map[compartment], c=color_cycle[i], marker=marker_cycle[i]) plt.fill_betweenx( [i - .3, i + .3], [ci32, ci32], [ci68, ci68], alpha=.3, color=color_cycle[i], ) plt.fill_betweenx([i - .1, i + .1], [ci5, ci5], [ci95, ci95], alpha=.3, color=color_cycle[i]) self._plot_dates(log=False) plt.legend(loc=(1.05, 0.0)) plt.grid(True, which='both', alpha=0.3) plt.xlabel('Peak Time After $t_0(C=5)$ [Days]') plt.yticks([]) # ----------------------------- # Plot peak capacity # ----------------------------- plt.subplot(5, 5, len(compartments) + 3) for i, compartment in enumerate([ 'E', 'A', 'I', 'R', 'D', 'total_deaths', 'direct_deaths_per_day', 'total_deaths_per_day', 'HGen', 'HICU', 'HVent', 'HGen_cumulative', 'HICU_cumulative', 'HVent_cumulative', 'general_admissions_per_day', 'icu_admissions_per_day' ]): median = output[compartment]['peak_value_ci50'] ci5, ci95 = output[compartment]['peak_value_ci5'], output[ compartment]['peak_value_ci95'] ci32, ci68 = output[compartment]['peak_value_ci32'], output[ compartment]['peak_value_ci68'] plt.scatter(median, i, label=compartment_to_name_map[compartment], c=color_cycle[i], marker=marker_cycle[i]) plt.fill_betweenx([i - .3, i + .3], [ci32, ci32], [ci68, ci68], alpha=.3, color=color_cycle[i]) plt.fill_betweenx([i - .1, i + .1], [ci5, ci5], [ci95, ci95], alpha=.3, color=color_cycle[i]) plt.xscale('log') plt.vlines(self.county_metadata['total_population'], *plt.ylim(), label='Entire Population', alpha=0.5, color='g') plt.vlines(self.county_metadata['total_population'] * 0.65, *plt.ylim(), label='Approx. Herd Immunity', alpha=0.5, color='purple', linestyles='--', linewidths=2) plt.legend(loc=(1, -0.1)) plt.grid(True, which='both', alpha=0.3) plt.xlabel('Value at Peak') plt.yticks([]) self.report.add_figure(fig) def _plot_dates(self, log=True): """ Helper function to add date plots. Parameters ---------- log: bool If True, shift y-positioning of labels based on a log scale. """ low_limit = plt.ylim()[0] if log: upp_limit = 1 * np.log(plt.ylim()[1]) else: upp_limit = 1 * plt.ylim()[1] for month in range(4, 11): dt = datetime.datetime(day=1, month=month, year=2020) offset = (dt - self.summary['t0']).days plt.vlines(offset, low_limit, upp_limit, color='firebrick', alpha=.4, linestyles=':') plt.text(offset, low_limit * 1.3, dt.strftime('%B'), rotation=90, color='firebrick', alpha=0.6)
class CountyReport: """ Generate a county level report showing detailed data on different scenarios. Parameters ---------- fips: str County fips code. model_ensemble: list(SEIRModel) Models from the ensemble. county_outputs: dict Dictionary of structure (in yaml syntax) suppression_policy__0.5: t_list: array of timestamps HICU: For compartment S we have a bunch of properties S_ci50: the median posterior model. Other condidence intervals are available. ... peak_value_ciXX: peak_time_ciXX: ... # For HICU, HVent, HGen, capacities are relevant, so we also calculate surge windows. capacity: estimated capacity for this compartment. surge_start: array surge window starts for each model in the ensemble surge_end: filename: str Where to save the report. summary: dict Summary attrs to print on the first page of the report. xlim: tuple Limits in days since simulation start to plot. """ def __init__(self, fips, model_ensemble, county_outputs, filename, summary, xlim=(0, 360)): self.fips = fips self.county_outputs = county_outputs self.model_ensemble = model_ensemble self.county_metadata = load_data.load_county_metadata_by_fips(fips) self.summary = summary timeseries = combined_datasets.get_timeseries_for_fips( fips, columns=[CommonFields.CASES, CommonFields.DEATHS], min_range_with_some_value=True ) self.county_case_data = timeseries.data self.report = PDFReport(filename=filename) self.xlim = xlim def generate_and_save(self): """ Name says it all. """ self.write_header_pages() self.plot_seir_distributions() self.report.close() def write_header_pages(self): """ Generate header pages with the county summary and PDF summaries. """ self.report.write_text_page( self.summary, title=f'PySEIR COVID19 Estimates\n{self.county_metadata["county"]} County, {self.county_metadata["state"]}', page_heading=f'Generated {self.summary["date_generated"]}', body_fontsize=6, title_fontsize=12, ) self.report.write_text_page( inspect.getsource(ParameterEnsembleGenerator.sample_seir_parameters), title="PySEIR Model Ensemble Parameters", ) def plot_seir_distributions(self, xlim=(0, 360)): """ Generate plots for each suppression policy containing distributions and peak information for each model compartment. Parameters ---------- xlim: tuple Limits in days since simulation start to plot. """ # Add a sample model from the ensemble. fig = self.model_ensemble[0].plot_results(xlim=xlim) fig.suptitle( f'PySEIR COVID19 Estimates: {self.county_metadata["county"]} County, {self.county_metadata["state"]}. ' f"SAMPLE OF MODEL ENSEMBLE", fontsize=16, ) self.report.add_figure(fig) suppression_policies = [ key for key in self.county_outputs.keys() if key.startswith("suppression_policy") ] for suppression_policy in suppression_policies: output = self.county_outputs[suppression_policy] compartments = list(output.keys()) compartments.remove("t_list") # ----------------------------------- # Plot each compartment distribution # ----------------------------------- fig = plt.figure(figsize=(20, 24)) fig.suptitle( f'PySEIR COVID19 Estimates: {self.county_metadata["county"]} County, {self.county_metadata["state"]}. ' f"\nSupression Policy={suppression_policy} (1=No Suppression)", fontsize=16, ) for i_plot, compartment in enumerate(compartments): plt.subplot(5, 5, i_plot + 1) plt.plot( output["t_list"], output[compartment]["ci_50"], color="steelblue", linewidth=3, label=compartment_to_name_map[compartment], ) plt.fill_between( output["t_list"], output[compartment]["ci_32"], output[compartment]["ci_68"], alpha=0.3, color="steelblue", ) plt.fill_between( output["t_list"], output[compartment]["ci_5"], output[compartment]["ci_95"], alpha=0.3, color="steelblue", ) plt.yscale("log") plt.ylim(1e0) plt.xlim(0, 360) plt.grid(True, which="both", alpha=0.3) plt.xlabel("Days Since Case 0") # Circular import :( from pyseir.ensembles.ensemble_runner import compartment_to_capacity_attr_map if compartment in compartment_to_capacity_attr_map: percentiles = np.percentile( [ getattr(m, compartment_to_capacity_attr_map[compartment]) for m in self.model_ensemble ], (5, 32, 50, 68, 95), ) plt.hlines( percentiles[2], *plt.xlim(), label="ICU Capacity", color="darkseagreen" ) plt.hlines( [percentiles[0], percentiles[4]], *plt.xlim(), color="darkseagreen", linestyles="-.", alpha=0.4, ) plt.hlines( [percentiles[1], percentiles[3]], *plt.xlim(), color="darkseagreen", linestyles="--", alpha=0.2, ) # Plot data if compartment in ["D", "total_deaths"] and len(self.county_case_data) > 0: plt.errorbar( (self.county_case_data.date - self.summary["t0"]).dt.days, self.county_case_data.deaths, yerr=np.sqrt(self.county_case_data.deaths), linestyle="-", label="Deaths Observed", marker="o", markersize=4, ) if compartment in ["I"] and len(self.county_case_data) > 0: plt.errorbar( (self.county_case_data.date - self.summary["t0"]).dt.days, self.county_case_data.cases, yerr=np.sqrt(self.county_case_data.cases), linestyle="-", label="Cases Observed", marker="o", markersize=4, color="firebrick", ) plt.legend() self._plot_dates(log=False) # ----------------------------- # Plot peak Timing # ----------------------------- color_cycle = plt.rcParams["axes.prop_cycle"].by_key()["color"] + list("bgrcmyk") marker_cycle = ["o", "s", "+", "d", "o"] * 4 plt.subplot(5, 5, len(compartments) + 1) for i, compartment in enumerate( [ "E", "A", "I", "HGen", "HICU", "HVent", "general_admissions_per_day", "icu_admissions_per_day", "direct_deaths_per_day", "total_deaths_per_day", ] ): median = output[compartment]["peak_time_ci50"] ci5, ci95 = ( output[compartment]["peak_time_ci5"], output[compartment]["peak_time_ci95"], ) ci32, ci68 = ( output[compartment]["peak_time_ci32"], output[compartment]["peak_time_ci68"], ) plt.scatter( median, i, label=compartment_to_name_map[compartment], c=color_cycle[i], marker=marker_cycle[i], ) plt.fill_betweenx( [i - 0.3, i + 0.3], [ci32, ci32], [ci68, ci68], alpha=0.3, color=color_cycle[i], ) plt.fill_betweenx( [i - 0.1, i + 0.1], [ci5, ci5], [ci95, ci95], alpha=0.3, color=color_cycle[i] ) self._plot_dates(log=False) plt.legend(loc=(1.05, 0.0)) plt.grid(True, which="both", alpha=0.3) plt.xlabel("Peak Time After $t_0(C=5)$ [Days]") plt.yticks([]) # ----------------------------- # Plot peak capacity # ----------------------------- plt.subplot(5, 5, len(compartments) + 3) for i, compartment in enumerate( [ "E", "A", "I", "R", "D", "total_deaths", "direct_deaths_per_day", "total_deaths_per_day", "HGen", "HICU", "HVent", "HGen_cumulative", "HICU_cumulative", "HVent_cumulative", "general_admissions_per_day", "icu_admissions_per_day", ] ): median = output[compartment]["peak_value_ci50"] ci5, ci95 = ( output[compartment]["peak_value_ci5"], output[compartment]["peak_value_ci95"], ) ci32, ci68 = ( output[compartment]["peak_value_ci32"], output[compartment]["peak_value_ci68"], ) plt.scatter( median, i, label=compartment_to_name_map[compartment], c=color_cycle[i], marker=marker_cycle[i], ) plt.fill_betweenx( [i - 0.3, i + 0.3], [ci32, ci32], [ci68, ci68], alpha=0.3, color=color_cycle[i] ) plt.fill_betweenx( [i - 0.1, i + 0.1], [ci5, ci5], [ci95, ci95], alpha=0.3, color=color_cycle[i] ) plt.xscale("log") plt.vlines( self.county_metadata["total_population"], *plt.ylim(), label="Entire Population", alpha=0.5, color="g", ) plt.vlines( self.county_metadata["total_population"] * 0.65, *plt.ylim(), label="Approx. Herd Immunity", alpha=0.5, color="purple", linestyles="--", linewidths=2, ) plt.legend(loc=(1, -0.1)) plt.grid(True, which="both", alpha=0.3) plt.xlabel("Value at Peak") plt.yticks([]) self.report.add_figure(fig) def _plot_dates(self, log=True): """ Helper function to add date plots. Parameters ---------- log: bool If True, shift y-positioning of labels based on a log scale. """ low_limit = plt.ylim()[0] if log: upp_limit = 1 * np.log(plt.ylim()[1]) else: upp_limit = 1 * plt.ylim()[1] for month in range(4, 11): dt = datetime.datetime(day=1, month=month, year=2020) offset = (dt - self.summary["t0"]).days plt.vlines(offset, low_limit, upp_limit, color="firebrick", alpha=0.4, linestyles=":") plt.text( offset, low_limit * 1.3, dt.strftime("%B"), rotation=90, color="firebrick", alpha=0.6, )