How my expected goals model works
A shot is not a shot. This is how I put a number on the difference.
September 20, 2026 · Keith Wilcox
Shots on goal is the stat everyone quotes and it's close to useless on its own. A wrist shot from the top of the crease and a point shot through four bodies both count as one. Everybody watching the game knows they aren't the same thing, and the scoresheet doesn't care.
Expected goals is the fix. Every unblocked shot gets a number between 0 and 1: the share of times a shot like that one goes in, based on every other shot like it in my database. Add them up and you get what a player or a team should have scored, which is a much better description of a night than the final score.
It's the model everything else on this site runs on. The shot maps size their dots by it. The goalie cards use it to work out saves above expected. The finishing card compares a shooter's goals to it.
So here's what goes into it, how well it works, and the parts it gets wrong.
What it's looking at
Every unblocked shot attempt since 2023-24: shots on goal, and shots that missed the net. Blocked shots are left out on purpose, because the NHL records a block where the blocker was standing, not where the shot came from, and feeding the model a location that isn't the shot location is worse than having no row at all.
Shootout attempts and penalty shots are out too. They're not the same game.
Empty net shots get a number, but they don't train the model and they don't count toward a player's totals. An empty net is a different sport.