Skip to main content

areaFluForecast

Documentation: https://areafluforecast.readthedocs.io · PyPI: https://pypi.org/project/areaFluForecast/

Neighborhood-scale influenza forecasts, 1 to 4 weeks ahead, for public health departments. You bring weekly flu rates for your areas (neighborhoods, ZIP-code groups, districts) and their boundaries. The package trains three models, checks each against the simplest possible forecast on your own past seasons, and only then gives you forecasts, with honest uncertainty bands.

The three models:

  • gnn_st: a graph neural network that uses which areas border each other and whose flu curves move together. This is the model from the accompanying paper.
  • lstm: a recurrent network that sees every area's series but no map.
  • colagnn: Cola-GNN (Deng et al. 2020), a published graph model for flu. This is our reimplementation from the paper, not the authors' code.

Every model is compared with persistence, "next week looks like this week". A forecaster that can't beat that on your data has learned nothing you can use. The package tells you, for each model and horizon, whether it did.

What the evidence says. In Boston the graph model's error was 18–28% below persistence on seasons it never saw. It tied a carefully tuned LSTM at 1 week and led it on average at 2–4 weeks. In Columbus, with only one unseen season, its error was 12–23% below persistence on average, but that lead was not statistically clear. Your city may be either, and the backtest is how you find out.

Install

pip install areaFluForecast
pip install "areaFluForecast[plot]"    # adds plotting

Python 3.10+, CPU only. Installing the package also installs PyTorch and PyTorch Geometric.

Try it on Boston

import areafluforecast as af

city = af.load_sample("boston")
print(city.summary())
bt = af.backtest(city, seasons=1, horizons=(1, 2), configs={"gnn_st": {"n_seeds": 3}})
fc = af.forecast(city, horizons=(1, 2))
fc.summary()

Or from the shell: areaflu backtest --sample boston --seasons 1 --quick.

Worked examples: examples/ has five notebooks, saved with their outputs:

  • a Boston quickstart
  • bringing your own files
  • a city with only two years of data
  • reading the results and choosing a model
  • a real city (Chicago) built from public open data

Use your own data

You need two things.

1. Weekly rates, as a CSV. Either long format:

week area rate
2024-01-07 Riverside 41.2
2024-01-07 Old Town 28.9

or wide format, with one column per area and the week start date first. Rates per 100,000 residents are expected. Leave missing or suppressed weeks blank. Don't fill them with 0. You need at least two years of data, and three or more is better. If you have counts, af.rates_from_counts(counts, population) converts them.

2. The map, given one of two ways:

  • a GeoJSON boundary file whose features carry the area name (id_col). The package works out which areas share a border.
  • or a CSV edge list with one row per pair of bordering areas.
city = af.City.from_files("rates.csv", areas="areas.geojson", id_col="NAME",
                          flu_months=(10, 11, 12, 1, 2, 3))
city.fetch_weather()          # optional: Open-Meteo, free, no API key
print(city.summary())         # check this before training

Southern-Hemisphere cities pass their own flu months, e.g. (4, 5, 6, 7, 8, 9).

Optional inputs:

  • Anchors are neighbouring towns or water that border your areas but have no data. List them in anchors=[...], and the edge list or boundary file supplies their borders.
  • Static area characteristics (a CSV with an area column). They are off by default, because in the paper they did not help.

The workflow

bt = af.backtest(city)        # would it have worked here? (last 2 complete seasons)
bt.report()                   # each model vs persistence, per horizon
fc = af.forecast(city)        # weeks 1-4 after your last observed week
fc.to_csv("forecast.csv")     # median, 95% band, 23 FluSight quantiles per area
fc.to_flusight()              # FluSight hub layout
fc.plot("gnn_st")

forecast() reads the most recent backtest of the same data. It adds a trust column and raises a TrustWarning for any model or horizon that did not clearly beat persistence, was never backtested, or was backtested with other settings.

How to read the backtest report:

  • WIS (weighted interval score) asks how good the whole forecast was, the band as well as the point. Lower is better, and it's in rate units. CDC's FluSight ranks forecasts by it.
  • Relative WIS is the model's WIS divided by persistence's, on the same weeks. Below 1 means the model beat persistence.
  • p (Holm) asks whether the weekly gap could be week-to-week noise. It is tested over weeks, not area-weeks, because neighbouring areas move together, and it is corrected for the number of comparisons. "Better than persistence" means a relative WIS below 1 and p below 0.05.
  • Coverage is how often the truth fell inside the 95% band. It should be near 95%.

How long it takes

On a laptop CPU, for about 15 areas and 8 years of weekly data:

  • One graph-model ensemble member takes about 3 minutes, so the default 10-member ensemble takes about 30 minutes per horizon.
  • The LSTM and Cola-GNN take a few minutes per horizon.
  • A default backtest (2 seasons × 4 horizons) takes about 4–5 hours.
  • A forecast takes about 2–3 hours.

To go faster, use configs={"gnn_st": {"n_seeds": 3}}, fewer horizons, or --quick to check the pipeline end to end. Everything is single-threaded, so separate horizons or cities can run in parallel. tune=True picks each model's settings from 12 candidates on validation weeks, the way the paper did, and takes about 12× longer.

Starting from Boston's model

A department with only a season or two of data can start from a model trained elsewhere:

weights = af.pretrained("boston")          # bundled: one model per horizon, 1-4 weeks
bt = af.backtest(city, models=["gnn_st_noadapt"], init_from=weights, zero_shot=True)
fc = af.forecast(city, init_from=weights, zero_shot=True)

Or from the shell: areaflu backtest --city city.json --weather --pretrained boston --zero-shot.

In the paper, a Boston model applied to Columbus with no Columbus training tied a model trained on Columbus itself and beat persistence. The reverse direction (Columbus to Boston, with a third of the history) was worse at 2 and 4 weeks.

Limits:

  • The inputs must match. The pretrained model uses weather and no static characteristics, so call city.fetch_weather() first.
  • Backtest it on your data before trusting it. Applied untrained to Columbus, the bundled Boston models were clearly better than persistence only at 1 week. The forecast's trust label comes from that backtest.

Limits to know

  • The forecasts run late. Every model here, like persistence, rises after the curve rises and peaks after it peaks. It runs about h weeks behind at h weeks ahead. The models beat persistence by getting the size of the epidemic right, not by seeing turns earlier. This happens because the models only see past rates, and a turn isn't visible in weekly data until after it happens. A growth-rate feature and a median loss were tested and didn't change it. The backtest report has a timing section that measures this on your data. Earlier warning would need inputs that move before ED visits do, such as wastewater or a shorter reporting delay.
  • Reporting delays are not simulated. A 1-week forecast assumes last week's data is complete. If your data arrives L weeks late, treat the h-week forecast as roughly an (h + L)-week one.
  • The backtest scores the models, not your data pipeline. If your counts get revised after the fact, the backtest saw final numbers and your real-time forecasts will not.
  • One season is one season. Two or more backtest seasons are better than one.

Reproducing the paper

scripts/parity/ reruns the paper's tuned Boston backtest with this package and compares the forecasts week by week. tests/test_research_parity.py checks, against the research code, that the inputs, graph, initial weights and trained forecasts are identical, down to the last bit for training.

Data availability and credits

  • The bundled Boston sample comes from the Boston Public Health Commission's public dashboard.
  • Weather data is by Open-Meteo.com (CC BY 4.0).
  • The bundled Chicago sample (load_sample("chicago")) comes from the Chicago Department of Public Health's "Influenza Risk Level by ZIP Code" on the City of Chicago Data Portal. It is a 1-10 activity level, not a rate; its README carries the portal's required disclaimer.
  • Pretrained Boston models (af.pretrained("boston")) hold only model weights, trained on Boston Public Health Commission weekly neighborhood data. No case or visit data are in them.
  • Nothing else is bundled. The package contains no data from any other health department.
  • Paper data. The Columbus data used in the accompanying paper are described in its data availability statement. Columbus weekly area-level counts are public in the research repository. Columbus Public Health's visit-level extracts are available from them on request.
  • Your data stays with you. The package sends nothing anywhere. Its only network call is the optional weather fetch, which sends area centroids to Open-Meteo.
  • This work was supported by Boston Public Health Commission, Columbus Public Health, the PEAK Trailblazer fellowship, and Prof. Ariel Hamlin (Northeastern University).

Citing

Please cite the version you used. The citation details are in CITATION.cff, and GitHub's Cite this repository button formats them as APA or BibTeX. Each release is archived on Zenodo, which gives it a DOI.

Metadata

Release files for areaFluForecast 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for areaFluForecast 1.0.0
File Size Uploaded
areafluforecast-1.0.0.tar.gz 2.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for areaFluForecast 1.0.0
File Interpreter ABI Platform
areafluforecast-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 4.8 MB

Release files / areafluforecast-1.0.0.tar.gz

Download URL areafluforecast-1.0.0.tar.gz
Size 2.4 MB
Tags Source
SHA-256 checksum
How to use checksums
31cf5b7fabdc7b1455fec3cb7b8941d2fd0d602b0495d2ca76c09f0b86935577
BLAKE2b-256 checksum
How to use checksums
18fdf49041432d2bf7b90f192db6a1b26376c8bd4e7f9e05cff94d2968527f91
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / areafluforecast-1.0.0-py3-none-any.whl

Download URL areafluforecast-1.0.0-py3-none-any.whl
Size 2.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
9bba067a2afdad423a1aa94018afb260da6e5a32ca61f3c6357aea60198c8c36
BLAKE2b-256 checksum
How to use checksums
e5b0539f919016a7f3bf3d2f6a799c4df71f336cd97d51d48b99aef493844288
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page