Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TalentOrigin

Predicting which countries produce more elite soccer players than their population and economy would suggest, and looking at what drives the gap.

Live results: krishiv111.github.io/TalentOrigin

Why I built this

I have played soccer for 14 years. Every World Cup I end up thinking about the teams that are not there.

Not the favorites. The countries that have never qualified once, or have not been back in decades. I wanted to know whether you could look at a country and tell if it was ever going to produce enough talent to get there. And if the answer was no, I wanted to know what was actually holding it back.

That turns into a question you can measure. Given a country's population, wealth, and history, how many elite players should it be producing? Once you have that number, the interesting countries are the ones that miss it in either direction.

What it does

Takes 41,734 real players, groups them by the country they were born in and the five year window they were born into, and counts how many became elite. Divides by population so big countries do not win by default.

Then it trains four models to predict that rate and compares them. The gap between predicted and actual is the part worth looking at.

Results

Trained on birth cohorts 1985 to 1995, scored on the 2000 cohort, which no model sees during training.

Model MAE RMSE
Repeat last cohort (not a model) 1.40 3.20 0.793
XGBoost 2.38 4.78 0.539
Random Forest 2.49 4.93 0.509
PyTorch neural net 2.70 5.19 0.457
Linear Regression 2.98 4.18 0.648
Always guess the mean (not a model) 4.85 7.23 -0.052

The finding: more model capacity did not help. Linear regression through to a neural network covers a huge range of flexibility, and none of them beat a one line rule that just repeats what a country produced in the previous cohort. One input, prior generation output, carries most of the signal, so there is very little nonlinear structure left for a bigger model to find.

That is a real result rather than a failed one. On 414 training rows with 6 features, the honest answer is that the extra capacity has nothing to learn.

Ranked by MAE rather than RMSE on purpose. The biggest values in this dataset come from very small countries, where one or two players swing the per million rate enormously, and RMSE would push the models to chase that noise.

How it works

Data. Transfermarkt player records joined to World Bank indicators. The two sources name countries differently, so there is a mapping layer that matches 193 of 211 and deliberately excludes the rest, which are defunct states and territories.

Defining elite. A player counts as elite if they played in any country's first tier league. I validated that against real World Cup rosters from 2006 to 2026 and it covers 99.9 percent of actual World Cup players.

Two earlier definitions got thrown out first:

  1. Ten or more international caps. That field is only reliably recorded for players on a current national roster, so it silently returned zero for every retired player. I caught it when Miroslav Klose, the all time World Cup top scorer, came back with zero caps.
  2. Played in a top five European league. Circular. Between 88 and 97 percent of players from Spain, Italy, France, Germany and England only ever played in their own domestic league, so it mostly measured where someone happened to be born.

Splitting. By time, never at random. Train on earlier cohorts, test on a later one. A random split would put a country's 2000 row in training while its 1995 row sits in test, and since the 2000 row carries the 1995 value as a feature, the model would be reading the answer straight out of its own input.

Cohorts are limited to 1985 through 2000. Earlier ones have a 99 percent elite rate, which is not because footballers were better in 1975, it is because Transfermarkt only recorded players from that era if they succeeded. Later ones cover players who are currently 12 to 21 and have not had careers yet.

Built with

Python, pandas, scikit-learn, XGBoost, PyTorch, MLflow, Plotly, World Bank API

Repo layout

src/
  ingest_worldbank.py        pull indicators from the World Bank API
  build_country_mapping.py   match Transfermarkt country names to World Bank ones
  build_cohorts.py           bucket players into 5 year birth cohorts, build the target
  build_features.py          lag features, club density, inequality
  evaluate.py                shared harness: load, time split, metrics
  train_linear.py            model 1, linear regression baseline
  train_forest.py            model 2, random forest
  train_xgb.py               model 3, gradient boosting
  train_nn.py                model 4, PyTorch net with a hand written training loop
  export_results.py          write docs/results.json for the site
docs/                        the static results site on GitHub Pages
notebooks/00_eda.ipynb       distributions, correlations, world heatmap
PLAN.md                      full phase by phase build plan
PHASE3_STEPS.md              the modelling steps and every data decision, with reasons

Every model imports the same three functions from evaluate.py, so all four see identical rows, identical features, and identical metrics. Without that the comparison would not mean anything.

Running it

python -m venv venv && venv/bin/pip install -r requirements.txt
venv/bin/python -m src.ingest_worldbank
venv/bin/python -m src.build_country_mapping
venv/bin/python -m src.build_cohorts
venv/bin/python -m src.build_features
venv/bin/python -m src.train_linear
venv/bin/mlflow ui

XGBoost needs OpenMP on macOS: brew install libomp.

Data is not tracked in git. The scripts above regenerate it.

What I would fix with more time

  • The target uses country of birth, but players often develop somewhere else, so part of what the model sees is migration rather than talent production.
  • Very small countries dominate the error. In a country of 47,000 people one player is 21 per million and there is no value in between, so the target is a coin flip multiplied by 21.
  • Income inequality was dropped at 15 percent coverage. Club counts were dropped because the source only ships 31 top flight leagues across 178 countries, and filling the other 147 with zero would claim Uruguay and Nigeria have no clubs.
  • The right statistical fix for the small country problem is predicting raw player counts with population as an offset instead of predicting a rate. That is the next thing I would build.
  • Nothing here is causal. It finds patterns, not reasons.

About

Predicting which countries produce more elite soccer talent than their population and economy suggest. Four models compared, with a live results map.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages