Here is a question I could not answer from the headlines: which European countries are actually losing people the fastest, in absolute terms or per capita? Those are two different questions, and they give two different answers. So I pulled the open data and ran the numbers.
The headline figure
Across the 19 European countries in the 2024 dataset, 17 recorded a net loss of native-born residents. Only two were net positive. So the "brain drain" story is not a handful of outliers, it is the default state of the continent.
But the interesting part is who tops the ranking, because it depends entirely on how you measure.
Load the data yourself
The dataset is public on GitHub (CC BY 4.0). Every number below is reproducible with a few lines of pandas. No download, no API key, it reads the raw CSV straight from the repo:
import pandas as pd
url = (
"https://raw.githubusercontent.com/DatapulseResearch/"
"brain-drain-eu/main/data/net_migration_native_born_2024.csv"
)
df = pd.read_csv(url)
print(df.shape) # (19, 3)
print(df.columns.tolist()) # ['country', 'net_migration', 'per_1000_residents']
# How many countries lost native-born residents?
losers = (df["net_migration"] < 0).sum()
print(f"{losers} of {len(df)} countries had a net loss") # 17 of 19
net_migration is the raw count for 2024 (negative means a net loss of native-born residents). per_1000_residents is the same flow normalized by population size.
The absolute ranking: Germany runs away with it
Sort by the raw count and one country dominates:
worst_absolute = df.sort_values("net_migration").head(5)
print(worst_absolute[["country", "net_migration"]])
country net_migration
0 Germany -91067
...
Germany loses -91,067 native-born residents, far more than anyone else in absolute terms. If you stop reading here, the story writes itself: "Germany, Europe's biggest brain drain." Plenty of coverage did exactly that.
The counterintuitive finding: the ranking inverts per capita
Now normalize by population and sort by per_1000_residents. The leaderboard is unrecognizable:
worst_rate = df.sort_values("per_1000_residents").head(5)
print(worst_rate[["country", "per_1000_residents"]])
country per_1000_residents
Luxembourg -2.35
Belgium -1.27
Sweden -1.23
...
By rate, the hardest hit are Luxembourg (-2.35), Belgium (-1.27) and Sweden (-1.23). Germany, the absolute "winner," is nowhere near the top of this list, because -91,067 out of a large population is a modest per-capita rate. A big country losing a big number is not the same as a country bleeding a large share of its people.
And the only two net-positive countries, the ones actually gaining native-born residents, are Bulgaria (+0.88) and Lithuania (+2.67), both places long framed as classic emigration stories. That is the reversal that made me want to write this up.
You can see the whole flip in one frame by putting both ranks side by side:
df["rank_absolute"] = df["net_migration"].rank().astype(int)
df["rank_per_capita"] = df["per_1000_residents"].rank().astype(int)
flip = df.sort_values("net_migration")[
["country", "net_migration", "per_1000_residents",
"rank_absolute", "rank_per_capita"]
]
print(flip.to_string(index=False))
Germany sits at rank 1 by absolute loss and drops far down the per-capita rank. Luxembourg does the opposite. Same data, two honest stories, and the one you tell depends only on which column you sort.
Data and method
- Scope: 19 European countries, native-born residents, net migration for 2024. Negative values mean a net loss.
-
Two views: raw count (
net_migration) and population-normalized rate (per_1000_residents). The point of the piece is that these are not interchangeable. - Open dataset (CC BY 4.0): the full CSVs live in the DatapulseResearch brain-drain-eu repository on GitHub. A second file, german_emigrant_destinations_2024.csv, breaks the German outflow down by destination country if you want to go a level deeper.
- Source study: the full analysis, charts and methodology are in Brain Drain in Europe 2024 by DataPulse.
Takeaway for anyone working with data
This is a clean, small example of a trap that scales to almost any comparative dataset: the metric picks the winner. "Most X in total" and "highest rate of X" answer different questions, and a chart that only shows one of them is not wrong, it is just incomplete. When you see a country-ranking headline, the first thing worth asking is whether it was normalized, because normalizing here does not shade the story, it inverts it.
The data is right there in the repo. Clone it, re-sort it, and check whether your own intuition survives the per-capita column. Mine did not.
Top comments (0)