Skip to main content

Model Accuracy

Does the forecast work when it has never seen the answer? The headline number is out-of-sample.

Out-of-Sample Backtest — the honest number

How the model does when it has never seen the 2025 result — predicting it from the 2021 election and the pre-election polls alone.

Ridings Called Correctly

88.0%

301 of 342 ridings 343 ridings total. Terrebonne (QC) is excluded; its result was nullified by the courts after the election.

vs. Naive Baseline

+4.7pp

over “2021 winner holds” (83.3%)

National Vote — Average Error

0.87pp

how far each party’s national share was off, on average

Chance of a Liberal Majority

73%

seats 182 (158205); actual 168

What “out-of-sample” means. This is the test that matters. The model is given only what a forecaster would have had before the 2025 vote: each riding's 2021 result as a starting point, plus the final pre-election national polling average. It applies the poll-implied swing and projects a winner in every riding. No 2025 data ever touches a prediction — the certified result is used only to mark the answers.

Because the model beats a naive “last winner holds” baseline (83.3% → 88.0%), the extra accuracy is genuine skill, not the churn of a stable map. The projection also correctly treated an LPC majority as likely-but-not-certain; the Liberals landed at 168, four short, well inside the projected seat range.

Method: GE44 (2021) riding baseline · Apr 27, 2025 national poll average · uniform swing · 10,000 Monte-Carlo iterations. Source: simulation/backtest_ge45_oos.py.

Out-of-Sample Accuracy by Region

Where the 2021→2025 backtest was strong and where it was hard: Quebec and the Prairies held up best; three-way races and the territories are hardest.

Atlantic
27/3284.4%
Quebec
72/7793.5%
Ontario
102/12283.6%
Prairies
61/6593.8%
BC
37/4386.0%
Northsmall sample
2/366.7%

By-Election Track Record

Top-two margin predicted within 7.7pp on average

Winner calls: 4/4 correct

University—Rosedale

ON · 2026-04-13

LPC wonCalled correctly
PartyPredictedActualError
Liberal64.0%64.4%+0.5pp
Conservative16.4%12.4%-4.0pp
NDP12.8%18.9%+6.1pp
Green3.6%2.9%-0.7pp
LiberalConservative marginPredicted: 47.6ppActual: 52.0ppError: +4.4pp

Predicted = 2025 election result for this riding + national polling swing as of 2026-04-12.

Scarborough Southwest

ON · 2026-04-13

LPC wonCalled correctly
PartyPredictedActualError
Liberal61.5%69.6%+8.1pp
Conservative23.5%18.8%-4.7pp
NDP7.9%5.9%-2.0pp
Green3.3%2.5%-0.8pp
LiberalConservative marginPredicted: 38.0ppActual: 50.8ppError: +12.8pp

Predicted = 2025 election result for this riding + national polling swing as of 2026-04-12.

Terrebonne

QC · 2026-04-13

LPC wonCalled correctly
Non-trivial call: Terrebonne was a statistical coin-flip going in (BQ–LPC polling within the margin of error). The model called the BQ–LPC margin within 1.3pp.
PartyPredictedActualError
Liberal38.6%48.3%+9.6pp
Conservative11.1%3.3%-7.8pp
NDP5.5%0.5%-5.0pp
Bloc Québécois38.5%46.9%+8.4pp
Green2.9%0.4%-2.5pp
LiberalBloc Québécois marginPredicted: 0.1ppActual: 1.4ppError: +1.3pp

Predicted = 2025 election result for this riding + national polling swing as of 2026-04-12.

Battle River—Crowfoot

AB · 2025-08-18

CPC wonCalled correctly
PartyPredictedActualError
Liberal11.8%4.3%-7.5pp
Conservative76.2%80.9%+4.8pp
NDP6.8%2.1%-4.7pp
Green2.0%0.2%-1.8pp
ConservativeLiberal marginPredicted: 64.3ppActual: 76.6ppError: +12.3pp

Predicted = 2025 election result for this riding + national polling swing as of 2025-08-17.

Why margins, not vote shares? By-election turnout and local candidate effects shift absolute vote-share levels in ways that national polling can't anticipate. But the relative positioning of parties, which is what determines seat outcomes, is captured by the national swing prior. Margin accuracy is therefore the better signal: it tests exactly what the model is optimised for. Four margin errors averaging single-digit accuracy is a more informative validation sample than four binary winner calls.

In-Sample Reproduction & Poll Diagnostics

Detailed 2025 breakdown — vote-share error, seat bands, and the missed ridings. The riding-call figure here is in-sample; read it as a consistency check, not the headline.

Reproduction check, not a headline. The figures below start each riding from its actual 2025 result and apply the final-day poll swing — a calibration/consistency check, not an out-of-sample forecast. It runs hot (a very small swing over the true result) and is shown for full transparency. For the honest measure of predictive skill, see the out-of-sample backtest above.

Ridings Called Correctly

97.7%

334 of 342 ridings 343 ridings total. Terrebonne (QC) is excluded; its result was nullified by the courts after the election.

Vote Share — Average Error

0.9pp

Mean absolute error across 6 parties

80% Bands Hit

5/5

Parties whose actual seats fell in projected range

CPC / LPC Seat Swing

CPC +15 · LPC -14

Seats vs. median projection

Why 80% intervals? We report 80% bands (not 95%) because they're easier to validate against real results. A 95% interval almost never misses, making it hard to test whether the model is actually calibrated. With a single election, a model can pass a 95% CI test by accident; an 80% CI is genuinely informative. See methodology →

National Vote Share

Predicted = model simulation output using the poll average as of 2025-04-27 (not the raw poll average). Actual = certified 2025 election result.

PartyPredictedActualError
Liberal43.4%44.0%+0.6pp
Conservative39.1%41.5%+2.4pp
NDP7.7%6.3%-1.3pp
Bloc Québécois6.2%6.3%+0.1pp
Green1.4%1.2%-0.2pp
People's1.3%0.7%-0.6pp

Error = actual − predicted. Positive = party outperformed the forecast; negative = underperformed.

Model Forecast

LPC Majority
68.4%
LPC Minority
22.3%
CPC Minority
7.5%
CPC Majority
1.9%

Actual Result

LPC Minority

LPC: 168 seats · CPC: 144 seats

Majority threshold: 172 seats · LPC fell 4 short

Seat Projection

Monte Carlo distribution from 10,000 simulated elections using the pre-election poll average (validation runs use 10,000; live scenario runs on the map use 2,000). Brackets show the 80% range (p10–p90).

PartyMedian80% rangeActualError
Liberal182156–212168-14
Conservative129101–155144+15
NDP85–127-1
Bloc Québécois2217–27220
Green10–210
People's00–00

Actual seat counts in green fell within the 80% projected range; red fell outside. Error = actual − median. Positive = party won more seats than projected.

Why vote share accuracy and seat accuracy can differ

A small miss on vote share can produce a large miss on seats, or the reverse. Both are normal.

Comparing the vote share table above to the seat projection table can be confusing. The model might predict a party's national vote share within half a point but still miss several seats, or hit the seat count almost exactly while overestimating the vote by a full percentage point. Both outcomes are common under first-past-the-post.

Three structural reasons account for the gap. First, vote share is a national number but seats are decided riding by riding: a party can be off by a point nationally with most of that error landing in regions where the seat count doesn't change. Second, three-way races are sensitive to where second- and third-place support sits, so a small shift in NDP support can flip several Liberal versus Conservative marginals without moving the headline LPC and CPC numbers. Third, the model applies regional adjustments (Alberta CPC floor, BC NDP baseline, Atlantic variance multiplier) that reflect historical patterns and partially decouple national swing from local outcomes.

This is why we publish vote share error and riding call accuracy separately. They measure different things, and a forecast is only credible if it gets both roughly right. See methodology for the full mechanism →

Regional Accuracy (in-sample)

In-sample riding-call accuracy broken down by region.

Atlantic
31/3296.9%
Quebec
77/77100.0%
Ontario
120/12298.4%
Prairies
64/6598.5%
BC
40/4393.0%
Northsmall sample
2/366.7%

Analysis & Commentary

A written breakdown of where the model was right, where it was wrong, and why.

Going into election night, the model gave the Liberals a 68.4% chance of a majority. They fell 4 seats short, landing at 168, just below the 172-seat majority threshold and near the lower edge of the model's 80% range (156–212). That was the headline miss: the model leaned majority, but a narrow minority was the outcome.

The CPC miss is the key residual error. Using the final day's polls, the model showed them at 39.1%; they ended up at 41.5%, a 2.4pp gap that is entirely attributable to polling error. The polls hadn't fully closed on the late CPC surge, even by April 27. Critically, the model itself didn't amplify this: the simulation reproduced the poll average faithfully and added no further CPC underestimation. CPC's actual 144 seats landed comfortably inside the 80% band (101–155).

Everywhere else, the model was sharp. LPC vote share landed within 0.6pp of the forecast. BQ, NDP, GPC, and PPC were all inside their calibrated error bands. Starting from the certified 2025 result, this reproduction check called 334 of 342 ridings correctly (97.7%) — but that figure is in-sample: it begins from the actual riding outcomes and applies only a small poll swing, so it flatters the model. The honest measure is the out-of-sample backtest above, which predicts 2025 from the 2021 map and pre-election polls alone and calls 88.0% of ridings.

This reproduction check uses the final-day poll average from April 27, 2025, one day before voting, applied on top of the certified 2025 riding results. The 0.87pp MAE reflects residual polling error even with fully up-to-date inputs; the simulation layer added no noise beyond what the polls contained. It is a consistency check — not a forecast made in advance.

Missed Ridings (8)

Ridings where the model called the wrong winner, sorted by actual certified margin — closest calls first.

RidingModel called (win prob.)Actual winnerMargin
Windsor—Tecumseh—LakeshoreONLPC51.9%CPC0.1pp
NunavutNULPC60.4%NDP0.6pp
Acadie—AnnapolisNSLPC71.8%CPC1.1pp
Markham—UnionvilleONLPC62.8%CPC3.6pp
New Westminster—Burnaby—MaillardvilleBCNDP50.8%LPC3.6pp
Cowichan—Malahat—LangfordBCNDP55%CPC4.6pp
Edmonton RiverbendABLPC55%CPC5.4pp
North Island—Powell RiverBCNDP49.7%CPC6.1pp

% = model's win probability for the party it (wrongly) called. Margin = certified vote share gap between 1st and 2nd place; red = under 1pp, amber = under 3pp.

Reproduce these numbers yourself

The scoring harness is public; you don't have to take our word for it. Clone the repo and run:

python scoring/evaluate.py

github.com/Northern-Vibe/scoring → · Neither 338 nor Poliwave publishes an equivalent harness.