Skip to content

Commit c38c872

Browse files
authored
Add more forecast figures and boxplot of scores (#338)
Code that generates the figures (Figure 2, 3, 4, 5) in the report: The forecast for the entire season 2021/2022 made at the beginning of the season (Figure 2) The good and bad states for each model (Figure 3, 4) The boxplot of the end-of-season absolute difference across states for each forecast date (Figure 5). All figures can be reproduced via `uv run make CONFIG=scripts/config_full.yaml`.
1 parent 86f3815 commit c38c872

2 files changed

Lines changed: 131 additions & 15 deletions

File tree

scripts/plot_preds.py

Lines changed: 110 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,48 @@
1111
LINE_OPACITY = 0.4
1212

1313

14+
def plot_data_cone(
15+
chart_data: pl.DataFrame,
16+
facet_kwargs: dict,
17+
out_dir: str,
18+
filename: str,
19+
properties_kwargs: dict | None = None,
20+
config_legend_kwargs: dict | None = None,
21+
config_axis_kwargs: dict | None = None,
22+
):
23+
base = alt.Chart(chart_data).encode(
24+
alt.X("time_end", title=None, axis=alt.Axis(format="%b"))
25+
)
26+
fc_cone = base.mark_area(opacity=0.25).encode(
27+
alt.Y("pred_lci", title="", axis=AXIS_PERCENT),
28+
alt.Y2("pred_uci"),
29+
alt.Color("model"),
30+
)
31+
fc_points = base.mark_line(opacity=0.75).encode(
32+
alt.Y("pred_estimate"), alt.Color("model")
33+
)
34+
data_points = base.mark_point(color="black").encode(alt.Y("obs_estimate"))
35+
data_error = base.mark_rule(color="black").encode(
36+
alt.X2("time_end"), alt.Y("obs_lci"), alt.Y2("obs_uci")
37+
)
38+
39+
out_path = Path(out_dir) / filename
40+
chart = fc_cone + fc_points + data_points + data_error
41+
42+
if properties_kwargs:
43+
chart = chart.properties(**properties_kwargs)
44+
45+
chart = chart.facet(**facet_kwargs)
46+
47+
if config_axis_kwargs:
48+
chart = chart.configure_axis(**config_axis_kwargs)
49+
50+
if config_legend_kwargs:
51+
chart = chart.configure_legend(**config_legend_kwargs)
52+
53+
chart.save(out_path)
54+
55+
1456
if __name__ == "__main__":
1557
p = argparse.ArgumentParser()
1658
p.add_argument("--config", required=True)
@@ -89,24 +131,77 @@
89131
preds, on=["model", "geography", "forecast_date", "time_end"], how="left"
90132
)
91133

92-
base = alt.Chart(chart_data).encode(
93-
alt.X("time_end", title=None, axis=alt.Axis(format="%b"))
94-
)
95-
fc_cone = base.mark_area(opacity=0.25).encode(
96-
alt.Y("pred_lci", title="Coverage", axis=AXIS_PERCENT),
97-
alt.Y2("pred_uci"),
98-
alt.Color("model"),
134+
## forecast all ##
135+
plot_data_cone(
136+
chart_data=chart_data,
137+
facet_kwargs={
138+
"column": "forecast_date",
139+
"row": "geography",
140+
},
141+
out_dir=out_dir,
142+
filename="forecast.svg",
99143
)
100-
fc_points = base.mark_line(opacity=0.75).encode(
101-
alt.Y("pred_estimate"), alt.Color("model")
144+
145+
## forecast the entire season at the beginning of the season ##
146+
plot_data_cone(
147+
chart_data=chart_data.filter(
148+
pl.col("forecast_date") == pl.col("forecast_date").min()
149+
),
150+
facet_kwargs={
151+
"facet": alt.Facet(
152+
"geography", title="", header=alt.Header(labelFontSize=20)
153+
),
154+
"columns": 6,
155+
},
156+
properties_kwargs={"width": 300, "height": 200},
157+
config_axis_kwargs={"labelFontSize": 20, "titleFontSize": 24},
158+
config_legend_kwargs={"labelFontSize": 20, "title": None},
159+
out_dir=out_dir,
160+
filename="forecast_entire_season.svg",
102161
)
103-
data_points = base.mark_point(color="black").encode(alt.Y("obs_estimate"))
104-
data_error = base.mark_rule(color="black").encode(
105-
alt.X2("time_end"), alt.Y("obs_lci"), alt.Y2("obs_uci")
162+
163+
## forecast the good state and bad state in LPLModel ##
164+
plot_data_cone(
165+
chart_data=chart_data.filter(
166+
pl.col("geography").is_in(["South Dakota", "North Dakota"]),
167+
pl.col("model") == pl.lit("LPLModel"),
168+
),
169+
facet_kwargs={
170+
"row": alt.Row(
171+
"geography",
172+
header=alt.Header(labelFontSize=20),
173+
title="",
174+
sort=["South Dakota", "North Dakota"],
175+
),
176+
"column": alt.Column(
177+
"forecast_date", header=alt.Header(labelFontSize=20), title=""
178+
),
179+
},
180+
config_axis_kwargs={"labelFontSize": 20, "titleFontSize": 24},
181+
out_dir=out_dir,
182+
filename="examples_LPL.svg",
106183
)
107184

108-
(fc_cone + fc_points + data_points + data_error).facet(
109-
column="forecast_date", row="geography"
110-
).save(out_dir / "forecast.svg")
185+
## forecast the good state and bad state in RFModel ##
186+
plot_data_cone(
187+
chart_data=chart_data.filter(
188+
pl.col("geography").is_in(["Wyoming", "Vermont"]),
189+
pl.col("model") == pl.lit("RFModel"),
190+
),
191+
facet_kwargs={
192+
"row": alt.Row(
193+
"geography",
194+
header=alt.Header(labelFontSize=20),
195+
title="",
196+
sort=["Wyoming", "Vermont"],
197+
),
198+
"column": alt.Column(
199+
"forecast_date", header=alt.Header(labelFontSize=20), title=""
200+
),
201+
},
202+
config_axis_kwargs={"labelFontSize": 20, "titleFontSize": 24},
203+
out_dir=out_dir,
204+
filename="examples_RF.svg",
205+
)
111206

112207
out_flag.touch()

scripts/plot_scores.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,25 @@
4343

4444
line_chart.save(out_dir / "scores.svg")
4545

46+
## boxplot of scores across states by forecast date ##
47+
alt.Chart(scores).mark_boxplot(ticks=True).encode(
48+
x=alt.X(
49+
"model",
50+
title=None,
51+
axis=alt.Axis(labels=False, ticks=False),
52+
scale=alt.Scale(padding=1),
53+
),
54+
y=alt.Y("score_value", title="End-of-season Abs Diff"),
55+
color="model",
56+
column=alt.Column(
57+
"forecast_date",
58+
title="",
59+
header=alt.Header(orient="bottom", labelFontSize=20, format="%b"),
60+
),
61+
).properties(width=80, height=400).configure_facet(spacing=0).configure_view(
62+
stroke=None
63+
).configure_axis(labelFontSize=20, titleFontSize=24).configure_legend(
64+
labelFontSize=20, title=None
65+
).save(out_dir / "scores_boxplot.svg")
66+
4667
out_flag.touch()

0 commit comments

Comments
 (0)