Man United best players in the 2024/25 season (before Amorim)
After the current International break Ruben Amorim will officially take over as Manchester United manager. There are already a few articles, speculating about the tactics that he is going to implement once he joins the club, but I think it’s interesting to focus on the players he will be coaching, since he will probably need to partially adapt his system to fit United best players, and not the opposite.
In this article I will focus on United offensive production in terms of goals, assists and expected goals and assists, and I will also look at who played the most minutes under Ten-Haag and Van Nistelroy in the first part of the season. I am going to provide all the code, so you can just follow along to reproduce the results of the analysis.
Getting the data
I am going to focus exclusively on the Premier League data, and I will use a python package called understat
, which will allow me to extract data from the popular website understat.com.
First, let’s install the package
pip install understat
Once the package is installed, we need to import a few modules to be able to make asynchronous requests to the understat API and get the data
import asyncio
import json
import aiohttp
from understat import Understat
At this point, we are ready to get the information about all Manchester United players for the current season.
# get all United players (2024)
manutd_pla = []
async def main():
async with aiohttp.ClientSession() as session:
understat = Understat(session)
data = await understat.get_league_players("epl", 2024, {"team_title": "Manchester United"})
manutd_pla.append(data)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Here we have defined the main
function, that calls the get_league_players
method. This method accepts as parameters the name of the league (in our case epl
), the season (2024 is the current season) and a dictionary where we can specify the team name with the team_title
key.
We run this function by defining an asynchronous loop and calling the method run_until_complete
. The result is saved in the manutd_pla
list, which will contain all the statistics about Manchester United players so far this season.
Ranking the players
In the manutd_pla
list we have a few interesting statistics, we can explore them by simply printing the list
print(manutd_pla)
# result
[[{'id': '10552',
'player_name': 'Alejandro Garnacho',
'games': '11',
'time': '710',
'goals': '3',
'xG': '4.1065501645207405',
'assists': '1',
'xA': '0.9675212446600199',
'shots': '31',
'key_passes': '10',
'yellow_cards': '1',
'red_cards': '0',
'position': 'M S',
'team_title': 'Manchester United',
'npg': '3',
'npxG': '4.1065501645207405',
'xGChain': '6.250516567379236',
'xGBuildup': '1.3122572544962168'},
...
]]
We notice that the list contains the expected goals (both including and excluding penalties), the expected assists, the shots taken, minutes played and many more interesting metrics.
As a first step, we can have a look at who played the most minutes so far, simply by ranking the list by the highest minutes.
players = manutd_pla[0]
# most minutes
players.sort(key=lambda x: float(x['time']), reverse=True)
for p in players[0:10]:
print(p['player_name'])
print(float(p['time']))
Here we take the first element of the manutd_pla
list and assign it to a new list called players
. This list is then sorted by time
, using the method sort
and specifying a simple one-line lambda function as the key to sort by. We finally print the player name and the minutes he has played.
Player | Minutes played |
---|---|
André Onana | 990 |
Diogo Dalot | 959 |
Bruno Fernandes | 931 |
Lisandro Martínez | 891 |
Noussair Mazraoui | 837 |
Marcus Rashford | 783 |
Matthijs de Ligt | 763 |
Alejandro Garnacho | 710 |
Casemiro | 624 |
Kobbie Mainoo | 577 |
It seems clear that the key players for this Man United side, are Dalot and Bruno Fernandes. Both have played more tha 900 minutes in the Premier League this season and only Onana has played more than them in the first 11 matches of the season. Lisandro Martinez has been on the pitch for longer than his defensive partner de Ligt, but both have been pretty nailed in the first part of the season, as Mazraoui who is the 5th player with the most minutes. The less nailed player seem to be Mainoo, Casemiro and Garnacho. We can probably expect some reshuffle there, with Ugarte likely to enter the competition for a spot in the middle of the pitch, and maybe Garnacho getting some more game time at the expense of Rashford.
Let’s look now at how well have players performed, from the attacking point of view. One of the best metrics to measure a player’s impact on the offensive production of the team is the so called xGi, or expected goal involvement, which is the sum of xG (expected goals) and xA (expected assists). To rank the top five players of Man United by that metrics we can do
# best players by xGi
players.sort(key=lambda x: float(x['xA'])+float(x['xG']), reverse=True)
for p in players[0:5]:
print(p['player_name'])
print(float(p['xG']) + float(p['xA']))
print(float(p['goals']) + float(p['assists']))
Here, we use the sort
method and as a key (the metric used to sort the list) we specify a one-line function that takes each element and calculates the sum of xA and xG. We also specify that we want to sort the list from the highest to the lowest value by writing reverse=True
. Finally, we print both the xGi and the actual number of goals and assists for each of the top 5 players.
Player | xGi | Goals+Assists |
---|---|---|
Bruno Fernandes | 7.56 | 5 |
Alejandro Garnacho | 5.07 | 4 |
Joshua Zirkzee | 3.81 | 2 |
Casemiro | 2.77 | 1 |
Marcus Rashford | 2.77 | 2 |
We can immediately see that the two best players, according to this metric, are Bruno Fernandes and Garnacho. Those are also the team top scorers. Even tough they have accumulated almost 13 xGi combined, their total number of goals+assists is just 9. This means they have under-performed in this first part of the season. According to this table, all top 5 players for xGi have under-performed, with Rashford being the less wasteful among the top 5.
In terms of goals, you can clearly see from the shot map how wasteful has Bruno Fernandes been first part of the season. He tends to shoot from distance quite a lot, with the average shot distance being 19 yards, but only scored one goal from outside the box. The other goal came from a shot inside the box, but we can see that he missed quite a few high quality chances as well. In total, he has 34 shots towards the goal this season, and only 4.41 xG, which makes it 0.13 xG/shot.
If you are interested in this type of analysis, I have written a few books where I go into the details of how to get the data, visualize and train a model to predict football results for the Premier League, La Liga, Serie A, Bundesliga and the other major European national tournaments, complete with code examples.