Endnu en leg med xG-data fra den danske Superliga. Det er samme datasæt/tilgang som i Xg-plots med Python og Superliga.dk part 3 og Xg-plots med Python og Superliga.dk (part2), men her brugt til at liste alle chancer i en kamp på pænest mulige måde.
import os
import requests
import json
import pandas as pd
import matplotlib.pyplot as plt
from PIL import Image
# List of teams and their IDs with short names
teams = [
{"teamId": 8071, "teamName": "AGF"},
{"teamId": 8470, "teamName": "AaB"},
{"teamId": 8595, "teamName": "BIF"}, # Short name for Brøndby IF
{"teamId": 8391, "teamName": "FCK"}, # Short name for F.C. København
{"teamId": 8113, "teamName": "FCM"}, # Short name for FC Midtjylland
{"teamId": 10202, "teamName": "FCN"}, # Short name for FC Nordsjælland
{"teamId": 9907, "teamName": "LBK"}, # Short name for Lyngby Boldklub
{"teamId": 8410, "teamName": "RFC"}, # Short name for Randers FC
{"teamId": 8415, "teamName": "SIF"}, # Short name for Silkeborg IF
{"teamId": 8487, "teamName": "SJE"}, # Short name for Sønderjyske Fodbold
{"teamId": 8231, "teamName": "VB"}, # Short name for Vejle Boldklub
{"teamId": 9939, "teamName": "VFF"}, # Short name for Viborg FF
{"teamId": 9814, "teamName": "ACH"}, # Short name for AC Horsens
{"teamId": 8453, "teamName": "B93"}, # Short name for B.93
{"teamId": 8285, "teamName": "EfB"}, # Short name for Esbjerg fB
{"teamId": 9821, "teamName": "Roskilde"}, # Short name for FC Roskilde
{"teamId": 8454, "teamName": "FCF"}, # Short name for FC Fredericia
{"teamId": 8289, "teamName": "HBK"}, # Short name for HB Køge
{"teamId": 9940, "teamName": "HIL"}, # Short name for Hillerød Fodbold
{"teamId": 9950, "teamName": "HIK"}, # Short name for Hobro IK
{"teamId": 10240, "teamName": "Hvidovre"}, # Short name for Hvidovre IF
{"teamId": 6308, "teamName": "Kolding"}, # Short name for Kolding IF
{"teamId": 8414, "teamName": "OB"},
{"teamId": 8615, "teamName": "VEN"}, # Short name for Vendsyssel FF
]
# Function to get the short name of the team based on teamId
def get_team_short_name(team_id):
for team in teams:
if team["teamId"] == team_id:
return team["teamName"]
return "Unknown"
# Function to parse rounds input and handle ranges
def parse_rounds(rounds_input):
rounds = []
for part in rounds_input.split(','):
if '-' in part:
start, end = part.split('-')
rounds.extend(range(int(start), int(end) + 1))
else:
rounds.append(int(part))
return [str(round) for round in rounds] # Convert rounds to strings for comparison
# Prompt user to input the rounds they want to fetch event IDs for
rounds_input = input("Enter the rounds you want to fetch event IDs for (comma separated, e.g., 1,2,5 or 1-5): ")
selected_rounds = parse_rounds(rounds_input)
# Prompt user to input the team ID if they want to filter by a specific team (leave blank if not)
team_id = input("Enter the team ID to filter by (leave blank if not applicable): ").strip()
# Step 1: Fetch events
events_url = 'https://api.superliga.dk/events-v2?appName=dk.releaze.livecenter.spdk&access_token=5b6ab6f5eb84c60031bbbd24&env=production&locale=da&seasonId=20962'
response = requests.get(events_url)
events_data = response.json()
# Step 2: Filter finished events from selected rounds and optional team ID
finished_events = [
event for event in events_data['events']
if event['statusType'] == 'finished' and event['round'] in selected_rounds
and (not team_id or event['homeId'] == int(team_id) or event['awayId'] == int(team_id))
]
# Print the list of available events with short team names
print("Available Events:")
for event in finished_events:
home_short_name = get_team_short_name(event['homeId'])
away_short_name = get_team_short_name(event['awayId'])
print(f"{event['eventId']} {home_short_name} vs {away_short_name}")
# Prompt user to select an event ID to generate the xG table
selected_event_id = input("Enter the event ID you want to generate the xG table for: ").strip()
def generate_xg_table(event_id):
try:
details_url = f'https://api.superliga.dk/opta-stats/event/{event_id}/detail-expected-goals?appName=superligadk&access_token=5b6ab6f5eb84c60031bbbd24&env=production&locale=da'
response = requests.get(details_url)
data = response.json()
home_team_id = data.get('homeId')
away_team_id = data.get('awayId')
home_team_name = data.get('homeName')
away_team_name = data.get('awayName')
home_score = data['score']['home']
away_score = data['score']['away']
# Paths to team logos
logo_directory = 'TeamPngs' # Update with the path to your logos directory
home_team_logo_path = os.path.join(logo_directory, f'{home_team_id}.png')
away_team_logo_path = os.path.join(logo_directory, f'{away_team_id}.png')
# Load team logos
home_team_logo = Image.open(home_team_logo_path)
away_team_logo = Image.open(away_team_logo_path)
# Extract home and away expected goals data
home_data = pd.DataFrame(data['expectedGoalsData']['home'])
away_data = pd.DataFrame(data['expectedGoalsData']['away'])
# Format shot data for table
home_shots = home_data[['min', 'sec', 'firstName', 'lastName', 'expectedGoalsValue', 'type']].copy()
home_shots['team'] = home_team_name
away_shots = away_data[['min', 'sec', 'firstName', 'lastName', 'expectedGoalsValue', 'type']].copy()
away_shots['team'] = away_team_name
# Combine home and away shots
shots = pd.concat([home_shots, away_shots])
# Sort by minute and second
shots = shots.sort_values(by=['min', 'sec'])
# Format xG values to 4 decimal places
shots['expectedGoalsValue'] = shots['expectedGoalsValue'].map(lambda x: f"{x:.4f}")
# Create the table plot
fig, ax = plt.subplots(figsize=(12, 8))
ax.axis('tight')
ax.axis('off')
table_data = shots[['team', 'min', 'sec', 'firstName', 'lastName', 'expectedGoalsValue', 'type']].values
column_labels = ['Team', 'Min', 'Sec', 'First Name', 'Last Name', 'xG Value', 'Type']
table = ax.table(cellText=table_data, colLabels=column_labels, cellLoc='center', loc='center')
# Set table styles
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1.2, 1.2)
# Adjust column widths
table.auto_set_column_width([0, 1, 2, 3, 4, 5, 6])
# Make goal scorers' names bold
for row_index, row in shots.iterrows():
if row['type'] == 'goal':
cell = table[(row_index + 1, 3)] # First name cell
cell.set_text_props(fontweight='bold')
cell = table[(row_index + 1, 4)] # Last name cell
cell.set_text_props(fontweight='bold')
# Add title with team logos
fig.suptitle(f" xG Table for {home_team_name} vs {away_team_name} ", fontsize=16, fontweight='bold', y=0.98)
# Add final score
fig.text(0.5, 0.92, f"Final Score: {home_team_name} {home_score} - {away_team_name} {away_score}", ha='center', fontsize=14, fontweight='bold')
# Reduce space between title and table
plt.subplots_adjust(top=0.99)
# Add team logos to the title
ax_logo_home = fig.add_axes([0.05, 0.88, 0.1, 0.1], anchor='NE', zorder=1)
ax_logo_home.imshow(home_team_logo)
ax_logo_home.axis('off')
ax_logo_away = fig.add_axes([0.85, 0.88, 0.1, 0.1], anchor='NE', zorder=1)
ax_logo_away.imshow(away_team_logo)
ax_logo_away.axis('off')
# Save the plot as PNG
output_directory = 'xg_tables' # Specify your directory here
os.makedirs(output_directory, exist_ok=True) # Create directory if it doesn't exist
filename = f'{home_team_name}_vs_{away_team_name}_xg_table.png'
filepath = os.path.join(output_directory, filename)
plt.savefig(filepath, bbox_inches='tight')
plt.show()
# Trim the image to the content
with Image.open(filepath) as img:
img = img.crop(img.getbbox())
img.save(filepath)
except Exception as e:
print(f"An error occurred while processing event ID {event_id}: {e}")
# Generate the xG table for the selected event
generate_xg_table(selected_event_id)