xG-tabel via Python og Superliga.dk

Author:

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)