Rishav Bhattarai
Case study · AMR-Climate Atlas

AMR-Climate Atlas

Does a hotter year make antibiotic resistance worse across Europe? A causal panel built from four government data sources, a mandatory placebo test on every result, a live MCP server, and a CI pipeline built to catch its own next bug before a human has to.

29EU/EEA countries
2000-23years covered
200+permutations per gate
2live MCP data sources
The problem

Heat and antibiotic resistance are both rising, and it's tempting to link them: warmer years, more bacterial growth and horizontal gene transfer, more resistance. Raw correlations across countries and years will show you almost anything you want. What's missing is a test built to say no: a model that starts from the assumption there's nothing here, and makes the data work to overturn it.

This project builds that test for four major pathogen-antibiotic combinations across Europe, then does the harder thing: reports what it finds, including the parts of its own analysis that were wrong the first time.

What I built
  1. A 29-country causal panel

    Joined four government and research data sources (ECDC resistance surveillance, NASA weather reanalysis, EU antibiotic-consumption reporting, and a global health-index confounder) into a single country-year panel spanning 2000-2023, for four pathogen-antibiotic combinations.

  2. A causal model with a mandatory placebo test

    Two-way fixed-effects regression, country and year, clustered standard errors. Every coefficient gets refit on 200 permuted datasets before it's allowed to be called significant. Not a footnote, a gate. A one-year-lag check runs alongside it, since resistance effects plausibly lag their trigger by a season.

  3. An independent genomic cross-check

    The one finding that survived the placebo test was checked again against a completely different data source: gene-level resistance markers from sequenced bacterial isolates, pulled live via BigQuery, rather than the phenotypic lab measurements the main model uses.

  4. A live MCP server

    Built on Anthropic's official MCP SDK, exposing live weather and live genomic-resistance data (not the frozen CSV) to any MCP-compatible client. Tested against a real external process, not just an in-process call.

  5. An eval-gated CI pipeline

    Turned the placebo test from a step someone has to remember to run into a gate that refits every model, checks the permutation shuffle itself isn't broken, and flags drift from a stored baseline, wired to run on every push.

What went wrong, and what I did about it

A causal claim is only as trustworthy as the QA process that tried to break it. Two bugs shipped in an earlier draft of this analysis before independent review caught them. Both are documented here rather than quietly fixed and forgotten.

Confounder bug

A confounder that was never in the model

An earlier version selected the antibiotic-consumption confounder column by position, not name. It picked a country label instead of the actual consumption figure: a bug with no error message, because the wrong column got silently absorbed into the model's own fixed effects instead of crashing. Every "consumption-adjusted" result in that draft was never adjusted for consumption. Full debug log →

QA gap

A missing placebo test, caught before publication

An independent testing pass found that the consumption-adjusted model had skipped the mandatory 200-permutation check entirely, the exact safeguard the rest of the project treats as non-negotiable. Caught and closed before any result relying on it was reported.

Undocumented API behavior

Two data sources that silently reject the obvious approach

ECDC's resistance-data export looks manual-UI-only but is scriptable once you notice its download-token parameter isn't validated server-side. Harvard Dataverse's API, separately, 403s the default Python HTTP client's user-agent string with no documentation saying so. Both found by testing directly against the live services, not by trusting their docs.

Model-fit caveat

Flagging a zero-inflated outcome before it gets overtrusted

The one combination that shows a real effect has resistance rates sitting exactly at zero for roughly a fifth of its rows, a shape a linear model isn't built for. The finding's direction and significance are corroborated by two independent checks, but its exact magnitude is flagged as provisional rather than reported with false precision.

The result
Main panel, all 4 combinations: null p = 0.51-0.89

Across the full 2000-2023 panel, no pathogen-antibiotic combination shows a statistically significant weather effect. Every parametric p-value sits well above 0.05 before the placebo test is even run.

One narrow exception: real, not confirmatory p = 0.0084

K. pneumoniae/carbapenem resistance, in a 2010-2022 subsample: a real, leave-one-country-out-stable effect, directionally confirmed by independent genomic data. It does not appear in the full panel, and the confounder meant to explain it isn't itself significant.

Read plainly: three of four combinations show nothing, anywhere, at any lag. The fourth shows something real but narrow: a pattern specific to one sample window, corroborated by an independent data source, not a confirmed climate-drives-resistance mechanism. That's the honest headline, and it's the one the write-up leads with.

Why this matters for the role

This project pairs two things a deployed engineering role needs: production infrastructure (a live MCP server, a CI-gated model pipeline that catches its own regressions) and the discipline to report a mostly-null result instead of the more exciting one, including the two bugs that shipped before independent review caught them.

Building the pipeline is the easy half. Trusting it enough to publish what it says, bugs and all, is the harder one, and the one a customer-facing engineering role holds every delivered result to.