← All methods

Player Archetype Map

Summary

A league-wide 2D scatter that places every player-season near other player-seasons with similar profiles. The page at /analysis/players/map is the inverse of the player similarity feature: similarity answers "who is like X", the archetype map answers "where does X sit in the whole league". KMeans clusters running on top of the embedding give each region a short label so an unfamiliar viewer has something to read alongside the dots.

Data

  • Source: per-season percentile vectors from player_season_percentiles. Each row is a player-season with values on a 0-100 cohort-relative scale, one entry per metric.
  • Time range: 2019 onward. The model trains on the full cross-season pool so coordinates are comparable across years.
  • Minutes filter: inherited from the percentile pipeline (200 minutes by default, 90 in the 2020 Island Games). Players below the threshold do not appear on the map.
  • Career rows excluded. Only per-season rows are placed; career aggregates would distort the embedding.
  • Refresh: rebuilt as part of the nightly sync.

Choices

Feature matrix

The union of every percentile key across every player-season is used as the feature set, so a defender's defensive metrics and a forward's attacking metrics live in the same vector. Missing values fill to 50 (cohort median) so a thin profile sits near the middle of the embedding rather than being pushed to the edge by zeros.

Each column is then z-scored. Sparse keys (e.g. goalkeeper-only metrics) had wildly different distributions after the median fill; standardising keeps the t-SNE distances honest and stops a single high-variance metric from dominating the layout.

t-SNE for the 2D layout

t-SNE with PCA initialisation, perplexity 30, learning rate auto, seeded for reproducibility. PCA init gives a stable starting point and the seed keeps the layout reproducible across reruns; perplexity is auto-clamped to min(30, (n-1)/3) so very small datasets still embed. Alternatives considered (UMAP, plain PCA, an autoencoder) were all rejected: PCA loses the interesting non-linear structure, UMAP did not materially out-perform t-SNE on the validation work, and an autoencoder is over-engineering for ~1,200 player-seasons.

KMeans on the embedding, k = 6

Clustering runs on the t-SNE coordinates rather than the original feature space so the labelled regions match what the eye sees on the map. k = 6 was chosen by silhouette tuning over a range; k = 4 scores slightly higher but groups visibly distinct profiles together, while k = 8+ starts splitting goalkeepers across multiple clusters which reads as noise rather than signal.

Cluster labels

For each cluster, the original percentile vectors of its members are averaged and the three keys with the highest mean are used as the label. The keys then pass through a humanising dictionary so tackles_p90 becomes "Tackles" rather than the raw column name.

Failure modes

  • Type space, not quality space. Percentiles are cohort-relative. A 90th-percentile defensive midfielder in 2019 lands where a 90th- percentile defensive midfielder in 2025 lands. Two players sitting next to each other are similar in role, not necessarily in absolute output.
  • Cluster IDs are not stable run-to-run. Validation showed that cluster boundaries are roughly stable across seeds but the cluster IDs themselves shuffle. The labels under each cluster are what to trust; the integer ID is illustrative only.
  • t-SNE preserves local, not global, structure. Distance between far-apart clusters is not meaningful. Distance between nearby dots is.
  • Cluster labels can repeat a stat under two names when input metrics are correlated (e.g. a basic and an advanced version of tackles per 90 both ranking high in the same cluster). The underlying neighbourhood is still coherent; the label just looks redundant.
  • Layout shifts between refreshes. With a fixed seed the layout is deterministic for a given input matrix, but the input changes whenever percentiles recompute, so the layout drifts run-to-run.

What would make it better

  • Confidence regions instead of single dots. Player-seasons with a thin metric profile sit close to the centre after the median fill; a small band rather than a point would communicate that the position is more uncertain than the high-minutes players.
  • Trajectory view. Connecting a player's dots across seasons would let a reader see drift in role, not just the current snapshot.