├── Euro22_who_controlled_territory.py ├── generate_player_shot_data.py ├── pl_territory_control.py └── player_shots_assists.py /Euro22_who_controlled_territory.py: -------------------------------------------------------------------------------- 1 | from statsbombpy import sb 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from operator import itemgetter 5 | from adjustText import adjust_text 6 | from highlight_text import ax_text,fig_text 7 | from mplsoccer import Pitch 8 | from matplotlib import colors 9 | 10 | #Get all matches for Euro 2022 11 | comp = sb.competitions() 12 | matches = sb.matches(competition_id = 55, season_id = 43) 13 | #convert DataFrame to Dictionary. I prefer working values in a Dict instead of a DF 14 | matches_dict = matches.to_dict(orient='records') 15 | sorted_matches = sorted(matches_dict, key=itemgetter('match_date')) 16 | 17 | #Create a list of all teams competing at Euro 2022 18 | column_values = matches[['home_team']].values.ravel() 19 | teams = pd.unique(column_values) 20 | teams.sort() 21 | 22 | ###Collect all touch data###################################################### 23 | #initiate a list to keep a track of all touches made in the tournament 24 | #1st dimension of the list represents teams 25 | #2nd dimension of the list makes the team the 'home team' & all opponents the 'away team' 26 | #3rd dimnension holds the X & Y values of each touch. 27 | tournament_touches = [] 28 | 29 | for team in range(len(teams)): 30 | 31 | team_name = teams[team] 32 | home_touches = [] 33 | away_touches = [] 34 | 35 | for match in range(len(sorted_matches)): 36 | if sorted_matches[match]['home_team'] == team_name \ 37 | or sorted_matches[match]['away_team'] == team_name: 38 | match = sb.events(match_id=sorted_matches[match]['match_id']) 39 | match_dict = match.to_dict('records') 40 | 41 | for touch in range(len(match_dict)): 42 | if match_dict[touch]['possession_team'] == team_name: 43 | #try and except is used to deal with Nan values 44 | try: 45 | if pd.isna(match_dict[touch]['location']) == False: 46 | home_touches.append(match_dict[touch]['location']) 47 | except: 48 | home_touches.append(match_dict[touch]['location']) 49 | else: 50 | try: 51 | if pd.isna(match_dict[touch]['location']) == False: 52 | away_touches.append(match_dict[touch]['location']) 53 | except: 54 | away_touches.append(match_dict[touch]['location']) 55 | 56 | #statsbomb automatically record data as teams playing from left to right 57 | #this swaps values so that the away team pays right to left. 58 | for i in range(len(away_touches)): 59 | away_touches[i][0] = 120 - away_touches[i][0] 60 | away_touches[i][1] = 80 - away_touches[i][1] 61 | 62 | tournament_touches.append([home_touches,away_touches]) 63 | 64 | ###specifically sort the X & Y values so they can be plotted onto the pich##### 65 | tournament_x_y_touches = [] 66 | 67 | for i in range(len(tournament_touches)): 68 | 69 | home_x_y_touches = [[],[]] 70 | for j in range(len(tournament_touches[i][0])): 71 | home_x_y_touches[0].append(tournament_touches[i][0][j][0]) 72 | home_x_y_touches[1].append(tournament_touches[i][0][j][1]) 73 | 74 | away_x_y_touches = [[],[]] 75 | for j in range(len(tournament_touches[i][1])): 76 | away_x_y_touches[0].append(tournament_touches[i][1][j][0]) 77 | away_x_y_touches[1].append(tournament_touches[i][1][j][1]) 78 | 79 | tournament_x_y_touches.append([home_x_y_touches,away_x_y_touches]) 80 | 81 | 82 | #using only 3 colours forces the heatmpa function to plot solif colours rather than a traditional heatmap sliding scale 83 | cmap = colors.ListedColormap(['#CCCCCC','#9656A2','#B02C3F']) #custom color map. 84 | pitch = Pitch(pitch_type='statsbomb', 85 | pad_top = 10, 86 | line_zorder=2, 87 | line_color='#FFFFFF', 88 | pitch_color='#131722') 89 | fig, axs = pitch.grid(nrows=6, 90 | ncols=4, 91 | grid_width=0.88, 92 | left=0.025, 93 | figheight=30, 94 | endnote_height=0.06, 95 | endnote_space=0, 96 | axis=False, 97 | title_space=0.02, 98 | title_height=0.06, 99 | grid_height=0.8) 100 | bins = (6, 4) 101 | 102 | for i,ax in enumerate(axs['pitch'].flat): 103 | 104 | #Values for the bins need to be generated to do a comparison to see which team controls the terratory for that bin. 105 | home_touch_zones = pitch.bin_statistic(tournament_x_y_touches[i][0][0], 106 | tournament_x_y_touches[i][0][1], 107 | statistic='count', 108 | bins=bins) 109 | away_touch_zones = pitch.bin_statistic(tournament_x_y_touches[i][1][0], 110 | tournament_x_y_touches[i][1][1], 111 | statistic='count', 112 | bins=bins) 113 | 114 | #Iterate over each bin and reassign their values. 115 | #100 represent home team territory 116 | #50 represents contested territory 117 | #0 represents away team territory 118 | for j in range(len(home_touch_zones['statistic'])): 119 | for k in range(len(home_touch_zones['statistic'][j])): 120 | total_touches = home_touch_zones['statistic'][j][k] + away_touch_zones['statistic'][j][k] 121 | if home_touch_zones['statistic'][j][k] < (total_touches * 0.55) and home_touch_zones['statistic'][j][k] > (total_touches * 0.45): 122 | home_touch_zones['statistic'][j][k] = 100 123 | elif home_touch_zones['statistic'][j][k] > away_touch_zones['statistic'][j][k]: 124 | home_touch_zones['statistic'][j][k] = 50 125 | else: 126 | home_touch_zones['statistic'][j][k] = 0 127 | 128 | #plot the heatmap 129 | hm = pitch.heatmap(home_touch_zones, ax=ax, cmap=cmap, edgecolors = '#131722') 130 | #title for each plot on the grid 131 | ax_text(0, -10, teams[i], ha='left', va='center', fontsize=30, ax=ax, color = "#ADAEB1") 132 | 133 | fig.set_facecolor("#131722") 134 | 135 | #Graphic text 136 | axs['title'].text(0.475, 137 | 0.65, 138 | 'Who Controlled Territory? Euro 2020', 139 | fontsize=40, 140 | va='center', 141 | ha='right', 142 | color = "#ADAEB1") 143 | SUB_TEXT = 'Posession by zone in Euro 2020' 144 | axs['title'].text(0.3615, 145 | 0.35, 146 | SUB_TEXT, 147 | fontsize=35, 148 | va='center', 149 | ha='right', 150 | color = "#ADAEB1") 151 | ax_text(0.6125, 152 | 14.75, 153 | ', Opponent, Less Than 55% & More Than 45%', 154 | fontsize=30, 155 | va='center', 156 | ha='right', 157 | color = "#ADAEB1", 158 | highlight_textprops=[{"color": '#9656A2'},{"color": '#B02C3F'}]) 159 | axs['endnote'].text(0.65, 160 | 0.09, 161 | 'Made by PUT NAME HERE. Data from StatsBomb.', 162 | size=16, 163 | color = "#ADAEB1") 164 | 165 | plt.savefig("PUT NAME OF FILE HERE.jpg", bbox_inches = 'tight') 166 | -------------------------------------------------------------------------------- /generate_player_shot_data.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import json 3 | import aiohttp 4 | from understat import Understat 5 | from fpl import FPL 6 | import matplotlib.pyplot as plt 7 | import datetime 8 | from operator import itemgetter 9 | import unidecode 10 | from mplsoccer import VerticalPitch, Pitch 11 | from mplsoccer.utils import FontManager 12 | import pandas as pd 13 | from matplotlib.patches import Patch 14 | from matplotlib.lines import Line2D 15 | import matplotlib.font_manager as fm 16 | 17 | season = 2022 18 | date_6_gw_ago = datetime.datetime(2022,8,30) # year, month, day 19 | 20 | #get all the players in the EPL who have played more than 600 21 | 22 | async def all_understat_players(): 23 | async with aiohttp.ClientSession() as session: 24 | understat = Understat(session) 25 | players = await understat.get_league_players( 26 | "epl", 27 | 2022, 28 | ) 29 | return json.dumps(players) 30 | 31 | players = asyncio.run(all_understat_players()) 32 | players = json.loads(players) 33 | players = list(players) 34 | 35 | #print(players[0]) 36 | 37 | ################################################################################################################ 38 | 39 | player_xg_data = [] 40 | 41 | for i in range(len(players)): 42 | data = [] 43 | if players[i]['position'] != 'GK' and int(players[i]['shots']) >= 1: 44 | data.append(players[i]['id']) 45 | data.append(players[i]['player_name']) 46 | data.append(float(players[i]['npxG'])/int(players[i]['shots'])) 47 | data.append(int(players[i]['shots'])) 48 | player_xg_data.append(data) 49 | 50 | print(player_xg_data[0]) 51 | 52 | ################################################################################################################ 53 | 54 | async def gt_player_shots(player_id): 55 | async with aiohttp.ClientSession() as session: 56 | understat = Understat(session) 57 | player_shots = await understat.get_player_shots(player_id, {'season':str(season)}) 58 | return json.dumps(player_shots) 59 | 60 | 61 | 62 | #gather all the shots. manually find out who assisted each shot to save on time consuming requests. 63 | all_player_shots = [] 64 | 65 | for i in range(len(player_xg_data)): 66 | just_player_shots = asyncio.run(gt_player_shots(player_xg_data[i][0])) #7230 = ESR/Arsenal 67 | just_player_shots = json.loads(just_player_shots) 68 | just_player_shots = list(just_player_shots) 69 | all_player_shots.append(just_player_shots) 70 | 71 | #print(len(all_player_shots)) 72 | #print(all_player_shots[0][0]) 73 | 74 | ######################################################################################################################################## 75 | 76 | for player in range(len(player_xg_data)): 77 | assists = 0 78 | xA = 0 79 | last_6_shots = 0 80 | last_6_xg = 0 81 | last_6_assisted = 0 82 | last_6_xa = 0 83 | for p_shots in range(len(all_player_shots)): 84 | for shots in range(len(all_player_shots[p_shots])): 85 | format_date = datetime.datetime.strptime(str(all_player_shots[p_shots][shots]['date']), '%Y-%m-%d %H:%M:%S') 86 | if all_player_shots[p_shots][shots]['player_id'] == player_xg_data[player][0] and all_player_shots[p_shots][shots]['situation'] != 'Penalty': 87 | if format_date > date_6_gw_ago: 88 | last_6_shots += 1 89 | last_6_xg = last_6_xg + float(all_player_shots[p_shots][shots]['xG']) 90 | 91 | if all_player_shots[p_shots][shots]['situation'] != 'Penalty': 92 | if all_player_shots[p_shots][shots]['player_assisted'] == player_xg_data[player][1]: 93 | assists += 1 94 | xA = xA + float(all_player_shots[p_shots][shots]['xG']) 95 | if format_date > date_6_gw_ago: 96 | last_6_assisted += 1 97 | last_6_xa = last_6_xa + float(all_player_shots[p_shots][shots]['xG']) 98 | 99 | if assists > 0: 100 | xG_per_assist = float(xA/assists) 101 | else: 102 | xG_per_assist = 0 103 | 104 | if last_6_shots > 0: 105 | last_6_xg_per_shot = float(last_6_xg/last_6_shots) 106 | else: 107 | last_6_xg_per_shot = 0 108 | 109 | if last_6_assisted > 0: 110 | last_6_xa_per_shot = float(last_6_xa/last_6_assisted) 111 | else: 112 | last_6_xa_per_shot = 0 113 | 114 | 115 | player_xg_data[player].append(assists) 116 | player_xg_data[player].append(xG_per_assist) 117 | player_xg_data[player].append(last_6_shots) 118 | player_xg_data[player].append(last_6_xg_per_shot) 119 | player_xg_data[player].append(last_6_assisted) 120 | player_xg_data[player].append(last_6_xa_per_shot) 121 | 122 | print(player_xg_data[0]) 123 | 124 | df = pd.DataFrame(player_xg_data, columns = ['id', 'name', 'xg_ps', 'shots', 'assists', 'xg_pa', 'l6_shots', 'l6_xgps', 'l6_assists', 'l6_xg_pa']) 125 | df.to_pickle("D:\\$ Personal\\FPL\\13). Player Shots & Assist Maps\\player_shot_data.pkl") 126 | -------------------------------------------------------------------------------- /pl_territory_control.py: -------------------------------------------------------------------------------- 1 | import json 2 | import pandas as pd 3 | import requests 4 | from bs4 import BeautifulSoup 5 | import matplotlib.pyplot as plt 6 | from matplotlib import rcParams, colors 7 | from mplsoccer import Pitch, VerticalPitch, FontManager 8 | from highlight_text import ax_text,fig_text 9 | import matplotlib.image as mpimg 10 | import asyncio 11 | import aiohttp 12 | from mplsoccer.utils import FontManager 13 | from highlight_text import ax_text,fig_text 14 | from fpl import FPL 15 | import unidecode 16 | 17 | #required to run asynchronous functions 18 | import nest_asyncio 19 | nest_asyncio.apply() 20 | 21 | with open('D:\\$ Personal\\FPL\\whoscored.json', "r") as json_file: 22 | ws_data = json.load(json_file) 23 | 24 | players = ws_data['playerIdNameDictionary'] 25 | h_team = ws_data['home']['teamId'] 26 | a_team = ws_data['away']['teamId'] 27 | h_team_name = ws_data['home']['name'] 28 | a_team_name = ws_data['away']['name'] 29 | highlight_h_team_name = '<' + h_team_name + '>' 30 | highlight_a_team_name = '<' + a_team_name + '>' 31 | score = ws_data['score'] 32 | h_score = score[0] 33 | a_score = score[-1] 34 | 35 | async def all_players(): 36 | async with aiohttp.ClientSession() as session: 37 | fpl = FPL(session) 38 | players = await fpl.get_players(return_json=True) 39 | return players 40 | 41 | async def all_teams(): 42 | async with aiohttp.ClientSession() as session: 43 | fpl = FPL(session) 44 | teams = await fpl.get_teams(return_json=True) 45 | return teams 46 | 47 | player_result = asyncio.run(all_players()) 48 | teams_result = asyncio.run(all_teams()) 49 | 50 | #team name check function 51 | def team_name_convert(team_to_check): 52 | if team_to_check == "Manchester City": 53 | team_to_check = "Man City" 54 | elif team_to_check == "Manchester United": 55 | team_to_check = "Man Utd" 56 | elif team_to_check == "Newcastle United": 57 | team_to_check = "Newcastle" 58 | elif team_to_check == "Tottenham": 59 | team_to_check = "Spurs" 60 | elif team_to_check == "Wolverhampton Wanderers": 61 | team_to_check = "Wolves" 62 | elif team_to_check == "Nottingham Forest": 63 | team_to_check = "Nott'm Forest" 64 | return team_to_check 65 | 66 | h_fpl_team_id = 0 67 | a_fpl_team_id = 0 68 | 69 | for i in range(len(teams_result)): 70 | if str(teams_result[i]['name']) == team_name_convert(h_team_name): 71 | h_fpl_team_id = teams_result[i]['id'] 72 | elif str(teams_result[i]['name']) == team_name_convert(a_team_name): 73 | a_fpl_team_id = teams_result[i]['id'] 74 | 75 | player_images = [] 76 | for i in players: 77 | player_images.append("") 78 | match_count = 0 79 | 80 | player_result = list(player_result) 81 | for i in range(len(player_result)): 82 | 83 | if player_result[i]['team'] == h_fpl_team_id or player_result[i]['team'] == a_fpl_team_id: 84 | matched = 0 85 | for player in players: 86 | fpl_name = player_result[i]['web_name'].split() 87 | fpl_surname = fpl_name[-1].split(".") 88 | fpl_first_name = fpl_name[0] 89 | name_to_check = players[player].split() 90 | fpl_official_surname = player_result[i]['second_name'] 91 | 92 | if unidecode.unidecode(fpl_surname[-1].lower()) == unidecode.unidecode(name_to_check[-1].lower()) and 'reece' == unidecode.unidecode(name_to_check[0].lower()): 93 | player_index = list(players).index(player) 94 | player_photo = player_result[i]['photo'].split('.') 95 | player_photo_string = player_photo[0] 96 | player_images[player_index] = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p'+player_photo_string+'.png' 97 | match_count+=1 98 | 99 | elif unidecode.unidecode(fpl_surname[-1].lower()) == unidecode.unidecode(name_to_check[-1].lower()): 100 | #print("FPL: " + player_result[i]['web_name'] + " MATCHED " + players[player]) 101 | player_index = list(players).index(player) 102 | player_photo = player_result[i]['photo'].split('.') 103 | player_photo_string = player_photo[0] 104 | player_images[player_index] = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p'+player_photo_string+'.png' 105 | match_count+=1 106 | 107 | elif unidecode.unidecode(fpl_first_name.lower()) == unidecode.unidecode(name_to_check[-1].lower()): 108 | matched = 1 109 | #print("FPL: " + player_result[i]['web_name'] + " MATCHED " + players[player]) 110 | player_index = list(players).index(player) 111 | player_photo = player_result[i]['photo'].split('.') 112 | player_photo_string = player_photo[0] 113 | player_images[player_index] = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p'+player_photo_string+'.png' 114 | match_count+=1 115 | 116 | elif unidecode.unidecode(fpl_first_name.lower()) == unidecode.unidecode(name_to_check[0].lower()): 117 | matched = 1 118 | #print("FPL: " + player_result[i]['web_name'] + " MATCHED " + players[player]) 119 | player_index = list(players).index(player) 120 | player_photo = player_result[i]['photo'].split('.') 121 | player_photo_string = player_photo[0] 122 | player_images[player_index] = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p'+player_photo_string+'.png' 123 | match_count+=1 124 | 125 | elif unidecode.unidecode(fpl_official_surname.lower()) == unidecode.unidecode(name_to_check[-1].lower()): 126 | matched = 1 127 | #print("FPL: " + player_result[i]['web_name'] + " MATCHED " + players[player]) 128 | player_index = list(players).index(player) 129 | player_photo = player_result[i]['photo'].split('.') 130 | player_photo_string = player_photo[0] 131 | player_images[player_index] = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p'+player_photo_string+'.png' 132 | match_count+=1 133 | 134 | elif len(name_to_check) > 1: 135 | if unidecode.unidecode(fpl_official_surname.lower()) == unidecode.unidecode(name_to_check[1].lower()): 136 | matched = 1 137 | #print("FPL: " + player_result[i]['web_name'] + " MATCHED " + players[player]) 138 | player_index = list(players).index(player) 139 | player_photo = player_result[i]['photo'].split('.') 140 | player_photo_string = player_photo[0] 141 | player_images[player_index] = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p'+player_photo_string+'.png' 142 | match_count+=1 143 | 144 | #there were 2 x James's in Chel V Leeds. I therfore just manually overwrote one of them. 145 | player_images[-7] = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p225796.png' 146 | 147 | 148 | h_touches = [] 149 | a_touches = [] 150 | 151 | for event in ws_data['events']: 152 | if event['isTouch'] == True: 153 | if event['teamId'] == h_team: 154 | h_touches.append(event) 155 | else: 156 | a_touches.append(event) 157 | 158 | #playertouches - similar to home & away touches 159 | player_touches = [] 160 | for i in players: 161 | player_touches.append([]) 162 | 163 | for event in ws_data['events']: 164 | if event['isTouch'] == True: 165 | for player in players: 166 | try: 167 | if int(event['playerId']) == int(player): 168 | player_index = list(players).index(player) 169 | player_touches[player_index].append(event) 170 | except: 171 | pass 172 | 173 | count = 0 174 | for player in players: 175 | #print(players[player] + ": " + str(len(player_touches[list(players).index(player)]))) 176 | count+=1 177 | 178 | h_touches_x_y = [[],[]] 179 | for i in range(len(h_touches)): 180 | h_touches_x_y[0].append(float(h_touches[i]['x'])) 181 | h_touches_x_y[1].append(float(h_touches[i]['y'])) 182 | 183 | a_touches_x_y = [[],[]] 184 | for i in range(len(a_touches)): 185 | a_touches_x_y[0].append(100 - float(a_touches[i]['x'])) 186 | a_touches_x_y[1].append(100 - float(a_touches[i]['y'])) 187 | 188 | player_x_y_touches = [] 189 | 190 | for i in range(len(player_touches)): 191 | x_y = [[],[]] 192 | for j in range(len(player_touches[i])): 193 | if player_touches[i][j]['teamId'] == a_team: 194 | x_y[0].append(100 - float(player_touches[i][j]['x'])) 195 | x_y[1].append(100 - float(player_touches[i][j]['y'])) 196 | else: 197 | x_y[0].append(float(player_touches[i][j]['x'])) 198 | x_y[1].append(float(player_touches[i][j]['y'])) 199 | player_x_y_touches.append(x_y) 200 | 201 | league_logo = mpimg.imread('D:\\$ Personal\\FPL\\$ Logos\\prem_logo_edited.png') 202 | 203 | fm_rubik = FontManager(('https://github.com/google/fonts/blob/main/ofl/bebasneue/' 204 | 'BebasNeue-Regular.ttf?raw=true')) 205 | text_colour = '#989898' 206 | cmap = colors.ListedColormap(['#FF005A','#05F1FF','#ffc505']) #custom color map normal = ['#35cada','#da4535','#f27127'] 207 | pitch = Pitch(pitch_type='opta', pad_top = 10, line_zorder=2, line_color='#FFFFFF', pitch_color='#1A1A1A') 208 | fig, axs = pitch.draw(figsize=(15,12)) 209 | fig.set_facecolor('#1A1A1A') 210 | bins = (6,4) 211 | 212 | home_touch_zones = pitch.bin_statistic(h_touches_x_y[0],h_touches_x_y[1], statistic ='count', bins = bins) 213 | away_touch_zones = pitch.bin_statistic(a_touches_x_y[0],a_touches_x_y[1], statistic ='count', bins = bins) 214 | 215 | #alters the statistics so the colour is solid & not gradient 216 | for j in range(len(home_touch_zones['statistic'])): 217 | for k in range(len(home_touch_zones['statistic'][j])): 218 | total_touches = home_touch_zones['statistic'][j][k] + away_touch_zones['statistic'][j][k] 219 | if home_touch_zones['statistic'][j][k] < (total_touches * 0.55) and home_touch_zones['statistic'][j][k] > (total_touches * 0.45): 220 | home_touch_zones['statistic'][j][k] = 100 221 | elif home_touch_zones['statistic'][j][k] > away_touch_zones['statistic'][j][k]: 222 | home_touch_zones['statistic'][j][k] = 50 223 | else: 224 | home_touch_zones['statistic'][j][k] = 0 225 | hm = pitch.heatmap(home_touch_zones, ax=axs, cmap=cmap, edgecolors = '#131722') 226 | 227 | player_touch_zones = [] 228 | for i in range(len(player_x_y_touches)): 229 | player_touch_zones.append(pitch.bin_statistic(player_x_y_touches[i][0], player_x_y_touches[i][1], statistic ='count', bins = bins)) 230 | 231 | players_list = list(players) 232 | 233 | highest_values = [] 234 | player_ids = [] 235 | player_names = [] 236 | for i in range(4): 237 | to_add = [] 238 | to_add2 = [] 239 | to_add_3 = [] 240 | for i in range(6): 241 | to_add.append(0) 242 | to_add2.append(0) 243 | to_add_3.append("") 244 | highest_values.append(to_add) 245 | player_ids.append(to_add2) 246 | player_names.append(to_add_3) 247 | 248 | for player in range(len(player_touch_zones)): 249 | for i in range(len(player_touch_zones[player]['statistic'])): 250 | for j in range(len(player_touch_zones[player]['statistic'][i])): 251 | #print(player_touch_zones[0]['statistic'][i][j]) 252 | if player_touch_zones[player]['statistic'][i][j] > highest_values[i][j]: 253 | highest_values[i][j] = player_touch_zones[player]['statistic'][i][j] 254 | p_id = players_list[player] 255 | name = players[p_id].split() #= names 256 | player_names[i][j] = name[-1] 257 | player_ids[i][j] = player_images[player] 258 | elif player_touch_zones[player]['statistic'][i][j] == highest_values[i][j]: 259 | player_ids[i][j] = "Players of same touches" 260 | player_names[i][j] = ">1 Player\nwith Highest\nNo of Touches" 261 | 262 | #player_touches[list(players).index(player) 263 | #rearrange these so the plot from bottom to top, not top to bottom. 264 | x_pos = 0.04 265 | x_pos_add = 0.155 266 | y_pos = 0.1095 267 | y_pos_add = 0.1885 268 | img_count = 0 269 | 270 | img_list = [] 271 | for i in range(24): 272 | img_list.append([]) 273 | 274 | img_row = 0 275 | img_col = 0 276 | 277 | for i in range(len(img_list)): 278 | img_count += 1 279 | img_list[i] = fig.add_axes([x_pos,y_pos, 0.075, 0.1]) 280 | img_list[i].axis('off') 281 | print(player_ids[img_row][img_col]) 282 | if player_ids[img_row][img_col] != "Players of same touches": 283 | if player_ids[img_row][img_col] != "No Image Found": 284 | print(player_ids[img_row][img_col]) 285 | im = plt.imread(player_ids[img_row][img_col]) 286 | img_list[i].imshow(im) 287 | img_col += 1 288 | x_pos += x_pos_add 289 | if img_count % 6 == 0: 290 | y_pos += y_pos_add 291 | x_pos = 0.04 292 | img_row += 1 293 | img_col = 0 294 | 295 | name_height = 0.225 296 | name_col = 0.05 297 | name_count = 0 298 | name_row = 0 299 | name_column = 0 300 | 301 | for i in range(24): 302 | name_count += 1 303 | fig.text(x=name_col, y=name_height, s=player_names[name_row][name_column], 304 | fontproperties=fm_rubik.prop, 305 | fontsize=20, 306 | color='#1A1A1A', 307 | va='center', ha='left') 308 | name_col += 0.155 309 | name_column += 1 310 | if name_count % 6 == 0: 311 | name_height += 0.188 312 | name_col = 0.05 313 | name_row += 1 314 | name_column = 0 315 | 316 | ax_league_logo = fig.add_axes([0.825,0.9,0.175,0.175]) 317 | ax_league_logo.axis('off') 318 | ax_league_logo.imshow(league_logo) 319 | 320 | fig.text(x=0.03, y=1.06, s='Who Controlled Where in the match', 321 | fontproperties=fm_rubik.prop, 322 | fontsize=60, 323 | color=text_colour, 324 | va='center', ha='left') 325 | 326 | fig.text(x=0.03, y=1, s=h_team_name + ": " + h_score, 327 | fontproperties=fm_rubik.prop, 328 | fontsize=60, 329 | color='#05F1FF', 330 | va='center', ha='left') 331 | 332 | fig.text(x=0.03, y=0.94, s=a_team_name + ": " + a_score, 333 | fontproperties=fm_rubik.prop, 334 | fontsize=60, 335 | color='#FF005A', 336 | va='center', ha='left') 337 | 338 | fig_text(x=0.03, y=0.89, fontsize=30, s=' Where no team has more than 55% of total touches', 339 | va='center', 340 | ha='left', 341 | color = text_colour, 342 | weight='bold', 343 | fontproperties=fm_rubik.prop, 344 | highlight_textprops=[{"color": '#ffc505'}], 345 | ax=axs) 346 | 347 | 348 | 349 | fig.text(0.6755 ,0.07,'Made by Andrew Brown | @casualfantasypl | Data from Opta', fontproperties=fm_rubik.prop, fontsize=15, color = "#ADAEB1") 350 | fig.text(0.628,0.05,'Heavily inspired by @johnspacemuller, @Odriozolite & @petermckeever', fontproperties=fm_rubik.prop, fontsize=15, color = "#ADAEB1") 351 | 352 | plt.savefig("D:\\$ Personal\\FPL\\15) Single Game Territory\\PL_Territory.jpg", facecolor = '#1A1A1A', bbox_inches='tight') -------------------------------------------------------------------------------- /player_shots_assists.py: -------------------------------------------------------------------------------- 1 | #program starts on line 503. Up to line 500 are functions that are used within the program. 2 | 3 | 4 | import asyncio 5 | import json 6 | import aiohttp 7 | from understat import Understat 8 | from fpl import FPL 9 | import matplotlib.pyplot as plt 10 | import datetime 11 | from operator import itemgetter 12 | import unidecode 13 | from mplsoccer import VerticalPitch, Pitch 14 | from mplsoccer.utils import FontManager 15 | import pandas as pd 16 | import matplotlib.patches as patches 17 | from matplotlib.lines import Line2D 18 | import matplotlib.font_manager as fm 19 | from highlight_text import ax_text,fig_text 20 | 21 | ##################################################################################################################### 22 | #these values are changed each time that the program is run 23 | single_players_understat = [7752] #add more than one understat ID to create shot/assisted shot maps for more than 1 player 24 | date_6_gw_ago = datetime.datetime(2022,8,30) #alter this to the GW that is 6 GWS ago from the current GW 25 | today = '19-10-22' #the date that the visualisation is created 26 | season = 2022 #used in understat calls 27 | gameweek = 12 #used to calculate the below values 28 | prev_count = gameweek-6 29 | next_count = gameweek 30 | 31 | ##################################################################################################################### 32 | 33 | #font that is used through the viz 34 | fm_rubik = FontManager(('https://github.com/google/fonts/blob/main/ofl/bebasneue/' 35 | 'BebasNeue-Regular.ttf?raw=true')) 36 | #understat calls 37 | #returns a json file containing all of the shots that a given player has taken this season 38 | async def gt_player_shots(player_id): 39 | async with aiohttp.ClientSession() as session: 40 | understat = Understat(session) 41 | player_shots = await understat.get_player_shots(player_id, {'season':str(season)}) 42 | return json.dumps(player_shots) 43 | 44 | #returns a json file containing all of the shots for other players where the focus player has been credited with the assist 45 | async def gt_assisted_player_shots(player_id,focused_player): 46 | async with aiohttp.ClientSession() as session: 47 | understat = Understat(session) 48 | player_shots = await understat.get_player_shots(player_id, {'season':'2022','player_assisted':focused_player}) 49 | return json.dumps(player_shots) 50 | 51 | #required to go thorugh all other player for the same team as the focused player 52 | async def get_team_mates(team_name): 53 | async with aiohttp.ClientSession() as session: 54 | understat = Understat(session) 55 | results = await understat.get_team_players( 56 | team_name, 57 | season, 58 | ) 59 | return json.dumps(results) 60 | 61 | #reduces the need to check if EVERY player in the squad has had a shot. Small amount of time saved. 62 | async def player_with_no_shots(player_id): 63 | async with aiohttp.ClientSession() as session: 64 | understat = Understat(session) 65 | grouped_stats = await understat.get_player_grouped_stats(player_id) 66 | return json.dumps(grouped_stats) 67 | 68 | ####################################################################################################################### 69 | 70 | #retrievs ONLY that data that is required later 71 | async def retrieve_player_shots(p_id): 72 | json_data = await gt_player_shots(p_id) 73 | json_data = json.loads(json_data) 74 | p_shts = list(json_data) 75 | 76 | if len(p_shts) > 0: 77 | player_name = json_data[0]['player'] 78 | if p_shts[-1]['h_a'] == 'h': #-1 takes into account the latest team player has played for this season. This helps if a player transfers midway through a season. 79 | player_team = p_shts[-1]['h_team'] 80 | else: 81 | player_team = p_shts[-1]['a_team'] 82 | 83 | p_shts_v2 = [] 84 | for i in range(len(p_shts)): 85 | for j in range(len(pl_teams)): 86 | if p_shts[i]['h_team'] == pl_teams[j]['title']: 87 | p_shts_v2.append(p_shts[i]) 88 | 89 | #from the player ID, you can get the player name, the team they play for and their shot data 90 | return [player_name,player_team,p_shts_v2] 91 | 92 | async def retrieve_team_mates(t_id): 93 | json_data = await get_team_mates(t_id) 94 | json_data = json.loads(json_data) 95 | t_mates = list(json_data) 96 | 97 | return t_mates #returns a list of the team mates for our focus player 98 | 99 | async def retrieve_assisted_shots(team_mate_list,focused_player_id): 100 | assissted_shots = [] 101 | 102 | for i in range(len(team_mate_list)): 103 | player_id = team_mate_list[i]['id'] 104 | json_data = await gt_assisted_player_shots(player_id,focused_player_id) 105 | json_data = json.loads(json_data) 106 | assissted_shts = list(json_data) 107 | 108 | for j in range(len(assissted_shts)): 109 | assissted_shots.append(assissted_shts[j]) 110 | 111 | return assissted_shots 112 | 113 | #whole function to determine which data belowng in the last 6 GW's. This serves as a focus 114 | def previous_6_gws(p_shts, assissted_shots): 115 | prev_six_gw_date = date_6_gw_ago 116 | 117 | prev_6_gw_shots = [] 118 | prev_6_gw_assisted_shots = [] 119 | 120 | player_shots = [] 121 | player_assisted_shots = [] 122 | 123 | player_goals = [] 124 | assisted_goals = [] 125 | 126 | prev_6_player_goals = [] 127 | prev_6_assisted_goals = [] 128 | 129 | for i in range(len(p_shts)): 130 | p_shts[i]['X'] = float(p_shts[i]['X'])*100 131 | p_shts[i]['Y'] = float(p_shts[i]['Y'])*100 132 | format_date = datetime.datetime.strptime(str(p_shts[i]['date']), '%Y-%m-%d %H:%M:%S') 133 | if p_shts[i]['result'] == 'Goal': 134 | if format_date >= prev_six_gw_date: 135 | prev_6_player_goals.append(p_shts[i]) 136 | else: 137 | player_goals.append(p_shts[i]) 138 | else: 139 | if format_date >= prev_six_gw_date: 140 | prev_6_gw_shots.append(p_shts[i]) 141 | else: 142 | player_shots.append(p_shts[i]) 143 | 144 | for i in range(len(assissted_shots)): 145 | assissted_shots[i]['X'] = float(assissted_shots[i]['X'])*100 146 | assissted_shots[i]['Y'] = float(assissted_shots[i]['Y'])*100 147 | format_date = datetime.datetime.strptime(str(assissted_shots[i]['date']), '%Y-%m-%d %H:%M:%S') 148 | if assissted_shots[i]['result'] == 'Goal': 149 | if format_date >= prev_six_gw_date: 150 | prev_6_assisted_goals.append(assissted_shots[i]) 151 | else: 152 | assisted_goals.append(assissted_shots[i]) 153 | else: 154 | if format_date >= prev_six_gw_date: 155 | prev_6_gw_assisted_shots.append(assissted_shots[i]) 156 | else: 157 | player_assisted_shots.append(assissted_shots[i]) 158 | 159 | return([prev_6_gw_shots, 160 | prev_6_gw_assisted_shots, 161 | player_shots, 162 | player_assisted_shots, 163 | player_goals, 164 | assisted_goals, 165 | prev_6_player_goals, 166 | prev_6_assisted_goals]) 167 | 168 | ########################################################################################################################### 169 | #needed to check for transfers in since start of season. Keep shot data for pl to pl team transfers but not outside of pl 170 | #only need to run once to find 20 teams as all players should look for the same 20 teams 171 | #would waste processing time doing this again & again 172 | async def all_pl_teams(): 173 | async with aiohttp.ClientSession() as session: 174 | understat = Understat(session) 175 | results = await understat.get_teams( 176 | "epl", 177 | 2022 178 | ) 179 | return json.dumps(results) 180 | 181 | teams_json_data = asyncio.run(all_pl_teams()) 182 | teams_json_data = json.loads(teams_json_data) 183 | pl_teams = list(teams_json_data) 184 | 185 | 186 | #functions for plotting shots############################################################################################### 187 | 188 | def make_a_plot(dataframe,main_colour,hatching,edges,symbol,legend_text,where_to_plot): 189 | pitch.scatter(dataframe.X, dataframe.Y, 190 | s=(dataframe.xG * 5400) + 600, 191 | c=main_colour, 192 | hatch = hatching, 193 | edgecolors = edges, 194 | marker = symbol, 195 | label = legend_text, 196 | ax=where_to_plot) 197 | 198 | def make_a_plot_6_weeks(dataframe,main_colour,edges,symbol,legend_text,where_to_plot): 199 | pitch.scatter(dataframe.X, dataframe.Y, 200 | s=(dataframe.xG * 5400) + 600, 201 | c=main_colour, 202 | edgecolors = edges, 203 | marker = symbol, 204 | label = legend_text, 205 | ax=where_to_plot) 206 | 207 | def make_a_plot_player_2(dataframe,main_colour,hatching,edges,symbol,where_to_plot): 208 | pitch.scatter(dataframe.X, dataframe.Y, 209 | s=(dataframe.xG * 5400) + 600, 210 | c=main_colour, 211 | hatch = hatching, 212 | edgecolors = edges, 213 | marker = symbol, 214 | ax=where_to_plot) 215 | 216 | def make_a_plot_6_weeks_player_2(dataframe,main_colour,edges,symbol,where_to_plot): 217 | pitch.scatter(dataframe.X, dataframe.Y, 218 | s=(dataframe.xG * 5400) + 600, 219 | c=main_colour, 220 | edgecolors = edges, 221 | marker = symbol, 222 | ax=where_to_plot) 223 | 224 | #shot & assists stats################################################################################################# 225 | #data is imported from a pickle file to reduce the number of calls made to understat. 226 | #I found i could run this program once to create a file and then use the file as many times as needed. 227 | #as the season progressess, it could take up to 20 minutes to produce the file. You do not want to spend 20 minutes per player creating a chart. 228 | df = pd.read_pickle("D:\\$ Personal\\FPL\\13). Player Shots & Assist Maps\\player_shot_data.pkl") 229 | stats_list = df.values.tolist() 230 | 231 | def ranking(player_id): 232 | 233 | season_shots = 0 234 | last_6_shots = 0 235 | season_xg = 0 236 | last_6_xg = 0 237 | season_assists = 0 238 | last_6_assists = 0 239 | season_xa = 0 240 | last_6_xa = 0 241 | 242 | #sort for shots rank 243 | shots_rank = sorted(stats_list,key = itemgetter(3), reverse = True) 244 | s_rank = 0 245 | for i in range(len(shots_rank)): 246 | if int(shots_rank[i][0]) == player_id: 247 | s_rank = i+1 248 | season_shots = shots_rank[i][3] 249 | last_6_shots = shots_rank[i][6] 250 | season_xg = round(shots_rank[i][2],3) 251 | last_6_xg = round(shots_rank[i][7],3) 252 | season_assists = shots_rank[i][4] 253 | last_6_assists = shots_rank[i][8] 254 | season_xa = round(shots_rank[i][5],3) 255 | last_6_xa = round(shots_rank[i][9],3) 256 | 257 | #sort for last_6 shot rank 258 | l6_shot_rank = sorted(stats_list,key = itemgetter(6), reverse = True) 259 | l6s_rank = 0 260 | for i in range(len(l6_shot_rank)): 261 | if int(l6_shot_rank[i][0]) == player_id: 262 | l6s_rank = i+1 263 | 264 | l6s_rank_colour = '' 265 | if l6s_rank < s_rank: 266 | l6s_rank_colour = '#4EFF83' 267 | elif l6s_rank > s_rank: 268 | l6s_rank_colour ='#FF055B' 269 | else: 270 | l6s_rank_colour ='#AFB0B3' 271 | 272 | #sort season xg rank 273 | shot_xg_rank = sorted(stats_list,key = itemgetter(2), reverse = True) 274 | xg_rank = 0 275 | for i in range(len(shot_xg_rank)): 276 | if int(shot_xg_rank[i][0]) == player_id: 277 | xg_rank = i+1 278 | 279 | #sort last 6 xg rank 280 | l6_shot_xg_rank = sorted(stats_list,key = itemgetter(7), reverse = True) 281 | l6_xg_rank = 0 282 | for i in range(len(l6_shot_xg_rank)): 283 | if int(l6_shot_xg_rank[i][0]) == player_id: 284 | l6_xg_rank = i+1 285 | 286 | l6s_xg_rank_colour = '' 287 | if l6_xg_rank < xg_rank: 288 | l6s_xg_rank_colour = '#4EFF83' 289 | elif l6_xg_rank > xg_rank: 290 | l6s_xg_rank_colour ='#FF055B' 291 | else: 292 | l6s_xg_rank_colour ='#AFB0B3' 293 | 294 | #sort season assist rank 295 | sa_rank = sorted(stats_list,key = itemgetter(4), reverse = True) 296 | a_rank = 0 297 | for i in range(len(sa_rank)): 298 | if int(sa_rank[i][0]) == player_id: 299 | a_rank = i+1 300 | 301 | #sort assists in last 6 weeks rank 302 | l6_sa_rank = sorted(stats_list,key = itemgetter(8), reverse = True) 303 | l6_a_rank = 0 304 | for i in range(len(l6_sa_rank)): 305 | if int(l6_sa_rank[i][0]) == player_id: 306 | l6_a_rank = i+1 307 | 308 | l6s_sa_rank_colour = '' 309 | if l6_a_rank < a_rank: 310 | l6s_sa_rank_colour = '#4EFF83' 311 | elif l6_a_rank > a_rank: 312 | l6s_sa_rank_colour ='#FF055B' 313 | else: 314 | l6s_sa_rank_colour ='#AFB0B3' 315 | 316 | #sort season xa 317 | season_xa_rank = sorted(stats_list,key = itemgetter(5), reverse = True) 318 | xa_rank = 0 319 | for i in range(len(season_xa_rank)): 320 | if int(season_xa_rank[i][0]) == player_id: 321 | xa_rank = i+1 322 | 323 | #sort last 6 gw xa rank 324 | last6_xa_rank = sorted(stats_list,key = itemgetter(9), reverse = True) 325 | l6_xa_rank = 0 326 | for i in range(len(last6_xa_rank)): 327 | if int(last6_xa_rank[i][0]) == player_id: 328 | l6_xa_rank = i+1 329 | 330 | l6s_xa_rank_colour = '' 331 | if l6_xa_rank < xa_rank: 332 | l6s_xa_rank_colour = '#4EFF83' 333 | elif l6_xa_rank > xa_rank: 334 | l6s_xa_rank_colour ='#FF055B' 335 | else: 336 | l6s_xa_rank_colour ='#AFB0B3' 337 | 338 | return([season_shots, last_6_shots, season_xg, last_6_xg, season_assists, last_6_assists, season_xa, last_6_xa, 339 | s_rank, l6s_rank, xg_rank, l6_xg_rank, a_rank, l6_a_rank, xa_rank, l6_xa_rank, 340 | l6s_rank_colour, l6s_xg_rank_colour, l6s_sa_rank_colour, l6s_xa_rank_colour]) 341 | 342 | #get FPL ID for those selected from understat################################################################################# 343 | 344 | async def off_fpl_players(): 345 | async with aiohttp.ClientSession() as session: 346 | fpl = FPL(session) 347 | player = await fpl.get_players(return_json=True) 348 | return player 349 | 350 | async def all_teams_info(): 351 | async with aiohttp.ClientSession() as session: 352 | fpl = FPL(session) 353 | teams = await fpl.get_teams(return_json=True) 354 | return teams 355 | 356 | fpl_players = asyncio.run(off_fpl_players()) 357 | fpl_players_list = list(fpl_players) 358 | fpl_teams = asyncio.run(all_teams_info()) 359 | 360 | def find_fpl_data(all_fpl_players, all_fpl_teams, name_to_convert, team_to_check): 361 | 362 | #understat & fpl have different names for teams 363 | if team_to_check == "Manchester City": 364 | team_to_check = "Man City" 365 | elif team_to_check == "Manchester United": 366 | team_to_check = "Man Utd" 367 | elif team_to_check == "Newcastle United": 368 | team_to_check = "Newcastle" 369 | elif team_to_check == "Tottenham": 370 | team_to_check = "Spurs" 371 | elif team_to_check == "Wolverhampton Wanderers": 372 | team_to_check = "Wolves" 373 | elif team_to_check == "Nottingham Forest": 374 | team_to_check = "Nott'm Forest" 375 | 376 | for i in range(len(all_fpl_players)): 377 | name_to_check = name_to_convert.split() 378 | fpl_name = all_fpl_players[i]['web_name'].split() 379 | fpl_surname = fpl_name[-1].split('.') 380 | fpl_first_name = fpl_name[0] 381 | fpl_official_surname = all_fpl_players[i]['second_name'] 382 | 383 | if unidecode.unidecode(fpl_surname[-1].lower()) == unidecode.unidecode(name_to_check[-1].lower()): 384 | if all_fpl_teams[int(all_fpl_players[i]["team"]-1)]["name"].lower() == team_to_check.lower(): 385 | player_photo = all_fpl_players[i]['photo'].split('.') 386 | player_photo_string = player_photo[0] 387 | fpl_team_id = all_fpl_players[i]['team'] 388 | 389 | elif unidecode.unidecode(fpl_first_name.lower()) == unidecode.unidecode(name_to_check[-1].lower()): 390 | if fpl_teams[int(fpl_players_list[i]["team"]-1)]["name"].lower() == team_to_check.lower(): 391 | player_photo = all_fpl_players[i]['photo'].split('.') 392 | player_photo_string = player_photo[0] 393 | fpl_team_id = all_fpl_players[i]['team'] 394 | 395 | elif unidecode.unidecode(fpl_first_name.lower()) == unidecode.unidecode(name_to_check[0].lower()): 396 | if fpl_teams[int(fpl_players_list[i]["team"]-1)]["name"].lower() == team_to_check.lower(): 397 | player_photo = all_fpl_players[i]['photo'].split('.') 398 | player_photo_string = player_photo[0] 399 | fpl_team_id = all_fpl_players[i]['team'] 400 | 401 | elif unidecode.unidecode(fpl_official_surname.lower()) == unidecode.unidecode(name_to_check[-1].lower()): 402 | if fpl_teams[int(all_fpl_players[i]["team"]-1)]["name"].lower() == team_to_check.lower(): 403 | player_photo = all_fpl_players[i]['photo'].split('.') 404 | player_photo_string = player_photo[0] 405 | fpl_team_id = all_fpl_players[i]['team'] 406 | 407 | elif len(name_to_check) > 1: 408 | if unidecode.unidecode(fpl_first_name.lower()) == unidecode.unidecode(name_to_check[1].lower()): 409 | if fpl_teams[int(fpl_players_list[i]["team"]-1)]["name"].lower() == team_to_check.lower(): 410 | player_photo = all_fpl_players[i]['photo'].split('.') 411 | player_photo_string = player_photo[0] 412 | fpl_team_id = all_fpl_players[i]['team'] 413 | 414 | if all_fpl_players[i]["web_name"].lower() == "smith rowe" and name_to_check == "emile smith-rowe": 415 | player_photo = all_fpl_players[i]['photo'].split('.') 416 | player_photo_string = player_photo[0] 417 | fpl_team_id = all_fpl_players[i]['team'] 418 | 419 | #special name cases as their names are presented with/without special characters on the two sites 420 | if all_fpl_players[i]["web_name"].lower() == "smith rowe" and unidecode.unidecode(name_to_convert.lower()) == "emile smith-rowe": 421 | player_photo = all_fpl_players[i]['photo'].split('.') 422 | player_photo_string = player_photo[0] 423 | fpl_team_id = all_fpl_players[i]['team'] 424 | 425 | if all_fpl_players[i]["web_name"].lower() == "aït-nouri" and unidecode.unidecode(name_to_convert.lower()) == "rayan ait nouri": 426 | player_photo = all_fpl_players[i]['photo'].split('.') 427 | player_photo_string = player_photo[0] 428 | fpl_team_id = all_fpl_players[i]['team'] 429 | 430 | if all_fpl_players[i]["web_name"].lower() == "de cordova-reid" and unidecode.unidecode(name_to_convert.lower()) == "bobby reid": 431 | player_photo = all_fpl_players[i]['photo'].split('.') 432 | player_photo_string = player_photo[0] 433 | fpl_team_id = all_fpl_players[i]['team'] 434 | 435 | if all_fpl_players[i]["web_name"].lower() == "bella-kotchap" and unidecode.unidecode(name_to_convert.lower()) == "armel bella kotchap": 436 | player_photo = all_fpl_players[i]['photo'].split('.') 437 | player_photo_string = player_photo[0] 438 | fpl_team_id = all_fpl_players[i]['team'] 439 | 440 | if all_fpl_players[i]["web_name"].lower() == "o'brien" and name_to_convert.lower() == "lewis o'brien": 441 | player_photo = all_fpl_players[i]['photo'].split('.') 442 | player_photo_string = player_photo[0] 443 | fpl_team_id = all_fpl_players[i]['team'] 444 | 445 | #konsa (villa) uses middle name - this could be improved in future iterations 446 | if all_fpl_players[i]["web_name"].lower() == "konsa" and unidecode.unidecode(name_to_convert.lower()) == "ezri konsa ngoyo": 447 | player_photo = all_fpl_players[i]['photo'].split('.') 448 | player_photo_string = player_photo[0] 449 | fpl_team_id = all_fpl_players[i]['team'] 450 | #also uses middle name 451 | if all_fpl_players[i]["web_name"].lower() == "bech" and unidecode.unidecode(name_to_convert.lower()) == "mads bech sorensen": 452 | player_photo = all_fpl_players[i]['photo'].split('.') 453 | player_photo_string = player_photo[0] 454 | fpl_team_id = all_fpl_players[i]['team'] 455 | #also uses middle name 456 | if all_fpl_players[i]["web_name"].lower() == "sambi" and unidecode.unidecode(name_to_convert.lower()) == "albert sambi lokonga": 457 | player_photo = all_fpl_players[i]['photo'].split('.') 458 | player_photo_string = player_photo[0] 459 | fpl_team_id = all_fpl_players[i]['team'] 460 | 461 | #need to also return team id - for prev 6 & next 6 GW's 462 | return ([player_photo_string, fpl_team_id]) 463 | 464 | #FDR Calcs################################################################################################################### 465 | 466 | async def fpl_fixtures(gw): 467 | async with aiohttp.ClientSession() as session: 468 | fpl = FPL(session) 469 | fix = await fpl.get_fixtures_by_gameweek(gw,return_json=True) 470 | return fix 471 | 472 | async def all_teams(): 473 | async with aiohttp.ClientSession() as session: 474 | fpl = FPL(session) 475 | fix = await fpl.get_teams(return_json=True) 476 | return fix 477 | 478 | def fdr_calculator(counter, team_id): 479 | all_t = asyncio.run(all_teams()) 480 | fixtures = [] 481 | for i in range(6): 482 | gw_fixtures = asyncio.run(fpl_fixtures(counter)) 483 | fixt = [] 484 | for j in range(len(gw_fixtures)): 485 | if gw_fixtures[j]['team_a'] == team_id : 486 | fixt.append(gw_fixtures[j]['team_h']) 487 | fixt.append(gw_fixtures[j]['team_a_difficulty']) 488 | for k in range(len(all_t)): 489 | if all_t[k]['id'] == gw_fixtures[j]['team_h']: 490 | fixt.append(all_t[k]['short_name']) 491 | fixt.append("(A)") 492 | elif gw_fixtures[j]['team_h'] == team_id: 493 | fixt.append(gw_fixtures[j]['team_a']) 494 | fixt.append(gw_fixtures[j]['team_h_difficulty']) 495 | for k in range(len(all_t)): 496 | if all_t[k]['id'] == gw_fixtures[j]['team_a']: 497 | fixt.append(all_t[k]['short_name']) 498 | fixt.append("(H)") 499 | fixtures.append(fixt) 500 | counter += 1 501 | return fixtures 502 | 503 | #PROGRAM STARTS############################################################################################ 504 | 505 | for i in range(len(single_players_understat)): 506 | returned_player_details = asyncio.run(retrieve_player_shots(single_players_understat[i])) 507 | if returned_player_details is not None: 508 | name = returned_player_details[0] 509 | team = returned_player_details[1] 510 | shots = returned_player_details[2] 511 | else: 512 | name = 'Jamie Vardy' 513 | team = 'Leicester' 514 | shots = [] 515 | 516 | #create a function to get FPL related data 517 | fpl_player_data = find_fpl_data(fpl_players_list, fpl_teams, name, team) 518 | player_photo = 'https://resources.premierleague.com/premierleague/photos/players/110x140/p'+str(fpl_player_data[0])+'.png' 519 | fpl_team = fpl_player_data[1] 520 | 521 | #fixtuers lists 522 | prev = fdr_calculator(prev_count, fpl_team) 523 | next_f = fdr_calculator(next_count, fpl_team) 524 | 525 | teams_text = [] 526 | colours = [] 527 | 528 | for j in range(len(prev)): 529 | t_txt = '' 530 | if len(prev[j]) > 4: 531 | t_txt = prev[j][2]+prev[j][3]+'\n'+prev[j][6]+prev[j][7] 532 | colours.append('#E7E7E7') 533 | elif len(prev[j]) > 0: 534 | t_txt = prev[j][2]+'\n'+prev[j][3] 535 | if prev[j][1] == 5: 536 | colours.append('#80072D') 537 | elif prev[j][1] == 4: 538 | colours.append('#FF1751') 539 | elif prev[j][1] == 3: 540 | colours.append('#E7E7E7') 541 | elif prev[j][1] == 2: 542 | colours.append('#02FC79') 543 | elif prev[j][1] == 1: 544 | colours.append('#375523') 545 | else: 546 | colours.append('#E7E7E7') 547 | teams_text.append(t_txt) 548 | 549 | upcoming_teams_text = [] 550 | upcoming_colours = [] 551 | 552 | for j in range(len(next_f)): 553 | t_txt = '' 554 | if len(next_f[j]) > 4: 555 | t_txt = next_f[j][2]+next_f[j][3]+'\n'+next_f[j][6]+next_f[j][7] 556 | upcoming_colours.append('#E7E7E7') 557 | elif len(next_f[j]) > 0: 558 | t_txt = next_f[j][2]+'\n'+next_f[j][3] 559 | if next_f[j][1] == 5: 560 | upcoming_colours.append('#80072D') 561 | elif next_f[j][1] == 4: 562 | upcoming_colours.append('#FF1751') 563 | elif next_f[j][1] == 3: 564 | upcoming_colours.append('#E7E7E7') 565 | elif next_f[j][1] == 2: 566 | upcoming_colours.append('#02FC79') 567 | elif next_f[j][1] == 1: 568 | upcoming_colours.append('#375523') 569 | else: 570 | upcoming_colours.append('#E7E7E7') 571 | upcoming_teams_text.append(t_txt) 572 | 573 | 574 | team_mates = asyncio.run(retrieve_team_mates(team)) 575 | assists = asyncio.run(retrieve_assisted_shots(team_mates,name)) 576 | 577 | data = previous_6_gws(shots,assists) 578 | 579 | ranks = ranking(single_players_understat[i]) 580 | 581 | player_shots_df = pd.DataFrame.from_dict(data[2]) 582 | assissted_shots_df = pd.DataFrame.from_dict(data[3]) 583 | prev_player_shots_df = pd.DataFrame.from_dict(data[0]) 584 | prev_assissted_shots_df = pd.DataFrame.from_dict(data[1]) 585 | player_goals_df = pd.DataFrame.from_dict(data[4]) 586 | assisted_goals_df = pd.DataFrame.from_dict(data[5]) 587 | prev_player_goals_df = pd.DataFrame.from_dict(data[6]) 588 | prev_assisted_goals_df = pd.DataFrame.from_dict(data[7]) 589 | 590 | if len(player_shots_df) > 0: 591 | player_shots_df['xG'] = pd.to_numeric(player_shots_df['xG']) 592 | 593 | if len(assissted_shots_df) > 0: 594 | assissted_shots_df['xG'] = pd.to_numeric(assissted_shots_df['xG']) 595 | 596 | if len(prev_player_shots_df) > 0: 597 | prev_player_shots_df['xG'] = pd.to_numeric(prev_player_shots_df['xG']) 598 | 599 | if len(prev_assissted_shots_df) > 0: 600 | prev_assissted_shots_df['xG'] = pd.to_numeric(prev_assissted_shots_df['xG']) 601 | 602 | if len(player_goals_df) > 0: 603 | player_goals_df['xG'] = pd.to_numeric(player_goals_df['xG']) 604 | 605 | if len(assisted_goals_df) > 0: 606 | assisted_goals_df['xG'] = pd.to_numeric(assisted_goals_df['xG']) 607 | 608 | if len(prev_player_goals_df) > 0: 609 | prev_player_goals_df['xG'] = pd.to_numeric(prev_player_goals_df['xG']) 610 | 611 | if len(prev_assisted_goals_df) > 0: 612 | prev_assisted_goals_df['xG'] = pd.to_numeric(prev_assisted_goals_df['xG']) 613 | 614 | 615 | pitch = VerticalPitch(pad_bottom=0.5, 616 | half=True, 617 | goal_type='box', 618 | goal_alpha=0.8, 619 | pitch_color = "#1A1A1A", 620 | pitch_type='opta') 621 | fig, ax = pitch.draw(figsize=(24, 20)) 622 | 623 | ax.set_ylim(40, 102) 624 | 625 | if len(player_goals_df) > 0: 626 | make_a_plot(player_goals_df,'None','///','#4EFF83','X','Player Goals - Whole Season',ax) 627 | 628 | if len(assisted_goals_df) > 0: 629 | make_a_plot(assisted_goals_df,'None','///','#05F1FF','X','Player Assists - Whole Season',ax) 630 | 631 | if len(player_shots_df) > 0: 632 | make_a_plot(player_shots_df,'None','///','#EBFF01','o','Player Shots - Whole Season',ax) 633 | 634 | if len(assissted_shots_df) > 0: 635 | make_a_plot(assissted_shots_df,'None','///','#FF055B','o','Player Assisted Shots - Whole Season',ax) 636 | 637 | if len(prev_assissted_shots_df) > 0: 638 | make_a_plot_6_weeks(prev_assissted_shots_df,'#FF055B','#37003C','o','Player Assisted Shots - Prev 6 GW',ax) 639 | 640 | if len(prev_player_shots_df) > 0: 641 | make_a_plot_6_weeks(prev_player_shots_df,'#EBFF01','#FF055B','o','Player Shots - Prev 6 GW',ax) 642 | 643 | if len(prev_assisted_goals_df) > 0: 644 | make_a_plot_6_weeks(prev_assisted_goals_df,'#05F1FF','#37003C','X','Player Assisted Goals - Prev 6 GW',ax) 645 | 646 | if len(prev_player_goals_df) > 0: 647 | make_a_plot_6_weeks(prev_player_goals_df,'#4EFF83','#37003C','X','Player Goals - Prev 6 GW',ax) 648 | 649 | txt = ax.text(x=100, y=108, s=name, 650 | size=150, 651 | fontproperties=fm_rubik.prop, color=pitch.line_color, 652 | va='center', ha='left') 653 | 654 | ax_text(100, 103.5, ' ', 655 | fontsize=40, 656 | va='center', 657 | ha='left', 658 | color = pitch.line_color, 659 | weight='bold', 660 | fontproperties=fm_rubik.prop, 661 | highlight_textprops=[{"color": '#4EFF83'},{"color": '#05F1FF'},{"color": '#EBFF01'},{"color": '#FF055B'}]) 662 | 663 | ax.add_patch(patches.Rectangle((94, 101), 6,1, facecolor = '#1A1A1A' , edgecolor = '#4EFF83', hatch = '///')) 664 | ax_text(93.5, 101.5, ' = Data from games not witihin the last 6 GW\'s', fontsize = 25, color = pitch.line_color, va = 'center', ha = 'left', fontproperties=fm_rubik.prop,) 665 | 666 | 667 | #stats table 668 | s_txt = ax.text(x = 66, y = 60, s="Shots" ,size = 40, color=pitch.line_color, va='center', ha='left', fontproperties=fm_rubik.prop) 669 | s_txt = ax.text(x = 66, y = 57, s="Rank" ,size = 40, color=pitch.line_color, va='center', ha='left', fontproperties=fm_rubik.prop) 670 | s_txt = ax.text(x = 66, y = 54, s="Assists" ,size = 40, color=pitch.line_color, va='center', ha='left', fontproperties=fm_rubik.prop) 671 | s_txt = ax.text(x = 66, y = 51, s="Rank" ,size = 40, color=pitch.line_color, va='center', ha='left', fontproperties=fm_rubik.prop) 672 | 673 | ax.text(x=54,y=63.5, s="Season\nCount",size = 40, color=pitch.line_color,va='center', ha='center', fontproperties=fm_rubik.prop) 674 | ax.text(x=42,y=63.5, s="Last 6GW\nCount",size = 40, color=pitch.line_color,va='center', ha='center', fontproperties=fm_rubik.prop) 675 | ax.text(x=30,y=63.5, s="Season xG\nPer Shot",size = 40, color=pitch.line_color,va='center', ha='center', fontproperties=fm_rubik.prop) 676 | ax.text(x=18,y=63.5, s="Last 6GW xG\nPer Shot",size = 40, color=pitch.line_color,va='center', ha='center', fontproperties=fm_rubik.prop) 677 | 678 | s_txt = ax.text(x = 54, y = 60, s=str(ranks[0]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 679 | s_txt = ax.text(x = 42, y = 60, s=str(ranks[1]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 680 | s_txt = ax.text(x = 30, y = 60, s=str(ranks[2]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 681 | s_txt = ax.text(x = 18, y = 60, s=str(ranks[3]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 682 | 683 | s_txt = ax.text(x = 54, y = 57, s=str(ranks[8]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 684 | s_txt = ax.text(x = 42, y = 57, s=str(ranks[9]) ,size = 40, color=ranks[16], va='center', ha='center', fontproperties=fm_rubik.prop) 685 | s_txt = ax.text(x = 30, y = 57, s=str(ranks[10]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 686 | s_txt = ax.text(x = 18, y = 57, s=str(ranks[11]) ,size = 40, color=ranks[17], va='center', ha='center', fontproperties=fm_rubik.prop) 687 | 688 | s_txt = ax.text(x = 54, y = 54, s=str(ranks[4]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 689 | s_txt = ax.text(x = 42, y = 54, s=str(ranks[5]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 690 | s_txt = ax.text(x = 30, y = 54, s=str(ranks[6]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 691 | s_txt = ax.text(x = 18, y = 54, s=str(ranks[7]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 692 | 693 | s_txt = ax.text(x = 54, y = 51, s=str(ranks[12]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 694 | s_txt = ax.text(x = 42, y = 51, s=str(ranks[13]) ,size = 40, color=ranks[18], va='center', ha='center', fontproperties=fm_rubik.prop) 695 | s_txt = ax.text(x = 30, y = 51, s=str(ranks[14]) ,size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 696 | s_txt = ax.text(x = 18, y = 51, s=str(ranks[15]) ,size = 40, color=ranks[19], va='center', ha='center', fontproperties=fm_rubik.prop) 697 | 698 | ax.plot([66, 11], [61.5, 61.5], lw='1', c=pitch.line_color) 699 | ax.plot([66, 11], [58.5, 58.5], lw='1', c=pitch.line_color) 700 | ax.plot([66, 11], [55.5, 55.5], lw='1', c=pitch.line_color) 701 | ax.plot([66, 11], [52.5, 52.5], lw='1', c=pitch.line_color) 702 | 703 | data = [[95,90,85,80,75,70],[47,45,47,45,47,45]] 704 | next_data = [[30,25,20,15,10,5],[45,47,45,47,45,47]] 705 | 706 | for i in range(len(teams_text)): 707 | ax.plot(data[0][i],data[1][i],marker="H", color=colours[i], ms=85) 708 | 709 | texts = [ax.text(data[0][i], data[1][i], teams_text[i], color = "#1A1A1A", size = 28, va='center', ha='center', fontproperties=fm_rubik.prop) for i in range(len(teams_text))] 710 | 711 | for i in range(len(upcoming_teams_text)): 712 | ax.plot(next_data[0][i],next_data[1][i],marker="H", color=upcoming_colours[i], ms=85) 713 | 714 | texts = [ax.text(next_data[0][i], next_data[1][i], upcoming_teams_text[i], color = "#1A1A1A", size = 28, va='center', ha='center', fontproperties=fm_rubik.prop) for i in range(len(teams_text))] 715 | 716 | ax.text(x=82.5, y=42, s="Previous 6 Fixtures", size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 717 | ax.text(x=17.5, y=42, s="Upcoming 6 Fixtures", size = 40, color=pitch.line_color, va='center', ha='center', fontproperties=fm_rubik.prop) 718 | 719 | fig.set_facecolor("#1A1A1A")#FPLOTD 720 | #fig.set_facecolor("#131722") 721 | 722 | 723 | fig.text(0.545,0.85,'Made by Andrew Brown | @casualfantasypl | Data from Understat', size=25, color = "#ADAEB1", fontproperties=fm_rubik.prop) 724 | #plt.show() 725 | 726 | #left, bottom, width, height .... 0.2, 0.425 727 | ax_player = fig.add_axes([0.13,0.095,0.2,0.425]) 728 | ax_player.axis('off') 729 | im = plt.imread(player_photo) 730 | ax_player.imshow(im) 731 | 732 | plt.savefig('D:\\$ Personal\\FPL\\13). Player Shots & Assist Maps\\' + name + ' ' + today + ' Shots & Assists.png', bbox_inches = 'tight') 733 | 734 | 735 | 736 | 737 | ############################################################################################################################ 738 | 739 | --------------------------------------------------------------------------------