Women’s Football Data Using Python
This article is about working with women’s football dataset and my findings.
First — What Is the Data?
The dataset is an Excel file with seven sheets. Each sheet is one year of FIFA — FIFA 16, FIFA 17,FIFA 18, FIFA 19, FIFA 20, FIFA 21, FIFA 22. Each row is one player, with columns like their name, country, overall rating, age, pace, shooting, passing, etc.
what is an overall rating
In fifa the game, every player gets a score out of 100. A score of 90 above means world class. 75 is good solid player. This dataset only has elite players, so the average is already 76.3 which is high.
The data set contain:
719 players total. But across seven years there are 2,244 rows — because the same player can appear in multiple years. For example, a player in FIFA 16, 17, and 18 counts as three rows but one unique person.
Step 1 — Loading the Data into a Database
The Excel file has seven sheets. Instead of working with all seven separately, I loaded everything into a SQLite database.
I loaded data using python code below:
XLSX_FILE = “Fifa16_22.xlsx”
DB_FILE = “fifa_women.db”
SHEETS = [“FIFA 16”, “FIFA 17”, “FIFA 18”, “FIFA 19”, “FIFA 20”, “FIFA 21”, “FIFA 22”]frames = []
for sheet in SHEETS:
df = pd.read_excel(XLSX_FILE, sheet_name=sheet)
df[“fifa_version”] = sheet
frames.append(df)all_data = pd.concat(frames, ignore_index=True)
Each sheet is read one by one and a new column called fifa version is added so we know which year that row belongs to. All seven sheets are then combined into one big data frame and saved into the database.
The database has four tables:
- players — one row per unique player with their name and nationality
- nations — the 17 countries in the dataset
- player_season — one row per player per FIFA edition, with all the ratings and stats
- player_positions — one row per player per edition per position, because one player can play multiple positions
The script is also smart about rebuilding. If the database already exists and is newer than the Excel file, it skips the loading step entirely. This saves time when running the script multiple times.
Step 2 — The Nine Queries and What They Show
After loading everything into the database, I wrote nine SQL queries. Each one answers a different question about the data. Let me explain each one.
Query 1 — Which Country Has the Best Players on Average?
This query groups all players by their nationality and calculates the average overall rating for each country. Only the top 15 countries are shown.
SELECT n.nationality_name, ROUND(AVG(ps.overall), 2) AS avg_overall
FROM player_season ps
JOIN players p ON ps.sofifa_id = p.sofifa_id
JOIN nations n ON p.nationality_id = n.nationality_id
GROUP BY n.nationality_name
ORDER BY avg_overall DESC LIMIT 15
The chart is a horizontal bar chart. United States and Germany came at the top. This makes sense because both nations have very strong women’s football programs and their leagues attract the best players in the world.
Query 2 — Has the Overall Rating Changed Year by Year?
This query looks at the average overall rating across all seven FIFA editions. It also counts how many unique players were in each edition.
SELECT fifa_version, ROUND(AVG(overall), 2) AS avg_overall,
COUNT(DISTINCT sofifa_id) AS player_count
FROM player_season
GROUP BY fifa_version
ORDER BY fifa_version
The chart shows a line for the average rating and grey bars for player count. What I found is that the average rating stayed mostly stable with a slight increase over the years. The number of players also grew — FIFA 22 had the most players in the dataset. This shows the game has been giving more coverage to women’s football each year.
Query 3 — Which Position Has the Most Room to Grow?
Get Thomasjosephkusa’s stories in your inbox
Join Medium for free to get updates from this writer.
In FIFA, every player has two ratings — their current overall and their potential. The potential is what the game thinks the player can reach. The gap between potential and overall tells us how much room a player has to improve.
SELECT pp.position, ROUND(AVG(ps.potential — ps.overall), 2) AS avg_gap
FROM player_season ps
JOIN player_positions pp ON ps.sofifa_id = pp.sofifa_id
AND ps.fifa_version = pp.fifa_version
WHERE pp.position NOT IN (‘GK’,’’)
GROUP BY pp.position
ORDER BY avg_gap DESC LIMIT 12
Goalkeepers were excluded because they have a very different development curve. The result shows that attacking midfielders and forwards tend to have the highest growth potential, meaning younger players in those positions are rated as having more upside in their careers.
Query 4 — Who Improved the Most From FIFA 16 to FIFA 22?
This query joins the FIFA 16 data and FIFA 22 data for the same player and calculates the difference in their overall rating. Only players who appeared in both editions are included.
SELECT p.long_name, early.overall AS rating_16, late.overall AS rating_22,
(late.overall — early.overall) AS growth
FROM players p
JOIN player_season early ON p.sofifa_id = early.sofifa_id
AND early.fifa_version = ‘FIFA 16’
JOIN player_season late ON p.sofifa_id = late.sofifa_id
AND late.fifa_version = ‘FIFA 22’
ORDER BY growth DESC LIMIT 30
One important thing I did here — I filtered out players whose names contain non-latin characters. This is because matplotlib has font rendering issues with scripts like Chinese or Arabic and the chart would show empty boxes instead of names. The horizontal bar chart shows the top 15 players by rating growth. These are the players who had the biggest career improvement over six years.
Query 5 — What Is the Age of Elite Players?
This query filters only the players with an overall rating of 80 or above — the elite group — and then looks at their average age, minimum age, and maximum age for each FIFA edition.
SELECT fifa_version, ROUND(AVG(age), 1) AS avg_age,
MIN(age) AS min_age, MAX(age) AS max_age
FROM player_season
WHERE overall >= 80
GROUP BY fifa_version
ORDER BY fifa_version
The chart shows a line for average age with a shaded band showing the full age range. What I found is that the peak age for elite women footballers is roughly between 25 and 28. The average stayed consistent across all seven editions, which tells us that the top level of the game is dominated by players in their mid to late twenties.
Query 6 — Which Nations Are Consistently Strong Across All Editions?
This query uses a window function called Row number () to rank players within each country for each FIFA edition. Then it takes only the top 10 players per country per year and averages their rating. This gives a measure of squad depth — not just one star player but how good the top 10 of each nation are.
SELECT n.nationality_name, ps.fifa_version,
ROUND(AVG(top10.overall), 2) AS top10_avg
FROM (
SELECT sofifa_id, fifa_version, overall,
ROW_NUMBER() OVER (
PARTITION BY fifa_version,
(SELECT nationality_id FROM players p2
WHERE p2.sofifa_id = player_season.sofifa_id)
ORDER BY overall DESC
) AS rn
FROM player_season
) top10
JOIN player_season ps ON top10.sofifa_id = ps.sofifa_id
AND top10.fifa_version = ps.fifa_version
JOIN players p ON ps.sofifa_id = p.sofifa_id
JOIN nations n ON p.nationality_id = n.nationality_id
WHERE top10.rn <= 10
GROUP BY n.nationality_name, ps.fifa_version
The result is shown as a heatmap. Darker color means higher average rating. United States and Germany show consistently dark colors across all seven years, confirming they are the most dominant nations not just in one year but across the entire period.
Query 7 — Is There a Link Between Wage and Overall Rating?
This query looks at the average wage in euros and the average overall rating for each FIFA edition. One important condition is applied — only players with a wage greater than zero are included, because many players in this dataset have no wage data recorded
SELECT fifa_version,
ROUND(AVG(overall), 2) AS avg_overall,
ROUND(AVG(wage_eur), 1) AS avg_wage
FROM player_season
WHERE wage_eur IS NOT NULL AND wage_eur > 0
GROUP BY fifa_version
ORDER BY fifa_version
One important note here — only 46 out of 2,244 records have wage data. This is a very small and biased sample, so the wage chart should be read carefully. It does not represent the full dataset. This is a known limitation of the data.
The chart uses a bar for wage on one axis and a line for overall rating on the other axis. The two axes allow comparison of two different scales on the same chart.
Query 8 — How Does Pace and Physicality Differ by Position in FIFA 22?
This query is pinned to FIFA 22 only. It looks at the average pace and average physicality rating for ten specific outfield positions. Goalkeepers and empty position strings are excluded.
SELECT pp.position, ROUND(AVG(ps.pace), 2) AS avg_pace,
ROUND(AVG(ps.physic), 2) AS avg_physic
FROM player_season ps
JOIN player_positions pp ON ps.sofifa_id = pp.sofifa_id
AND ps.fifa_version = pp.fifa_version
WHERE pp.position IN (‘ST’,’CF’,’LW’,’RW’,’LB’,’RB’,’CB’,’CM’,’CDM’,’CAM’)
AND ps.fifa_version = ‘FIFA 22’
GROUP BY pp.position
ORDER BY pp.position
The chart is a grouped bar chart with blue bars for pace and red bars for physicality. As expected, wide attackers like LW and RW have the highest pace. Centre backs and defensive midfielders have the highest physicality. This matches what we see in the real game — wide players need speed, defenders need strength.
Query 9 — How Have the Six Key Attributes Changed Across All Editions?
This is the most visual query. It takes the average of six attributes — Pace, Shooting, Passing, Dribbling, Defending, and Physicality — for each FIFA edition
SELECT fifa_version,
ROUND(AVG(pace), 1) AS Pace,
ROUND(AVG(shooting), 1) AS Shooting,
ROUND(AVG(passing), 1) AS Passing,
ROUND(AVG(dribbling), 1) AS Dribbling,
ROUND(AVG(defending), 1) AS Defending,
ROUND(AVG(physic), 1) AS Physic
FROM player_season
GROUP BY fifa_version
ORDER BY fifa_version
The chart is a grouped bar chart with seven groups — one per FIFA edition — and six bars per group. What I found is that dribbling and passing have increased steadily over the years, while defending has stayed more flat. This suggests that the game developers have been gradually improving the technical ratings of women players edition by edition.
Step 3— The dashboard
All nine charts are generated at startup using matplotlib. Each chart is saved to an in-memory buffer, encoded as a base64 PNG string, and embedded directly into the Dash app. This means no image files are written to disk.
The dashboard has two sections:
KPI Cards at the top — Five summary cards showing unique players, season records, nationalities, average overall rating, and number of FIFA editions.
Chart Grid — All nine charts displayed in a two-column grid.
Final Thoughts
This was an interesting dataset to work with. A few things stood out to me: The data is elite only — all 719 players are already good players, so the numbers are all high. This is not a full global dataset, it is a curated set of the best women’s footballers across seven years. The wage data is very sparse — only 46 players have wage data, so Query 7 should be treated as indicative only and not as a reliable finding. The growth query in Q4 only includes players who appeared in both FIFA 16 and FIFA 22. Many players who debuted after 2016 or retired before 2022 are not captured, so this is not a complete picture of player development. Overall, women’s football in FIFA has clearly grown — more players, slightly higher ratings, and a consistent elite core of nations at the top. The data tells a story of a game mode that has been steadily expanded and improved over seven years.



