Blog

A text-focused feed — short essays and notes.

The Line Blender: Hypothetical Russian Olympic Lineups

2026-02-09

Unlike the lineups produced in my last post on Olympic lineups with announced rosters, which will eventually have a point of comparison, these hypothetical Russian lines will remain hypothetical. Using MoneyPuck data from both the 2024 and 2025 seasons, I've applied the same optimization methods (linear programming and greedy) to available Russian NHL players[^1].

Read more →


The Line Blender: Olympic Lineups with Announced Rosters

2026-02-01

Using the announced Olympic rosters, we can apply our GNN model to the players who will actually compete in Milan-Cortina. Unlike the previous Olympic roster post, where we considered all eligible NHL players, here we're constrained by the national team rosters.

Read more →


The Line Blender: Using GNN Embeddings for Player Rankings

2026-01-31

Throughout this series, we have explored how players interact within lines and teams. We’ve seen how to optimize a single roster and how to predict performance for unseen combinations. Here, we are inverting the logic: instead of asking how to build a team around players, we are asking which individual players provide the most value to any line they join.

Read more →


The Line Blender: Using a GNN To Produce Olympic Rosters

2026-01-31

In my previous post, we looked at all possible combinations of lines and pairings on a team's roster. But, where this is most interesting, is looking at combinations across teams. Typically this would be hamstrung by a lack of data; there's limited data on how players from different teams would play together. With the Olympics approaching, this presents a fascinating challenge. Searching the full pool of eligible players produces hundreds of thousands of potential combinations.

Read more →


The Line Blender: Embedding Line Performance Using a GNN

2026-01-31

Finding optimal combinations is, as it sounds, a combinatorial problem. A roster of 13 forwards and 7 defensemen, can yield 286 unique forward lines and 21 unique defensive pairings. If a coach wanted to see just 20 minutes of play from every forward combination, it would take approximately 95 games of icetime to cycle through them all.

To solve this, we can extrapolate from existing data to infer how unseen combinations would perform. By encoding players and their historical relationships into a Graph Neural Network (GNN), we can embed player information as "nodes" and their shared icetime as "edges". This allows the model to predict the expected goals percentage (xG%) for lines that may have never played a single shift together.

Read more →


The Line Blender: Optimizing Lineups Using MoneyPuck's Expected Goals Percentage (xG%)

2026-01-31

Fire up the line blender. Over this series of posts, I'm going to explore an approach to finding optimal lineups using data-driven modeling. I'll start with the fundamentals of optimization before moving into more complex territory, using Graph Neural Networks (GNN) to evaluate previously unseen lines.

Read more →


Why I added a /uses page to my site

2026-01-19

Earlier this week, I was sucked into this thread: Ask HN: Share your personal website. Paging through people's personal websites is great; there are plenty of examples of creativity and personality on the internet and outside of social media.

Read more →


Replacing Plotly with D3

2026-01-12

I began using Plotly in 2016 to add interactivity to what were previously static matplotlib charts. When I launched my personal website in 2017 and began publishing data visualizations, Plotly was the default tool I chose. And for the last eight years, my charts lived on in Plotly's "Chart Studio".

Read more →