import pandas as pd from mplsoccer import VerticalPitch from mplsoccer.statsbomb import read_event, EVENT_SLUG # get data match_files = ['19789.json', '19794.json', '19805.json'] kwargs = { 'related_event_df': False, 'shot_freeze_frame_df': False, 'tactics_lineup_df': False, 'warn': False } df = pd.concat([ read_event(f'{EVENT_SLUG}/{file}', **kwargs)['event'] for file in match_files ]) # filter chelsea pressure events mask_chelsea_pressure = (df.team_name == 'Chelsea FCW') & (df.type_name == 'Pressure') df = df.loc[mask_chelsea_pressure, ['x', 'y']] ############################################################################## # Plot the heatmaps # setup pitch pitch = VerticalPitch(pitch_type='statsbomb', line_zorder=2, pitch_color='#22312b', line_color='white')
This example shows how to plot the passes from a team as a pass flow plot. """ from matplotlib import rcParams import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap from mplsoccer import Pitch, FontManager from mplsoccer.statsbomb import read_event, EVENT_SLUG rcParams['text.color'] = '#c7d5cc' # set the default text color # get event dataframe for game 7478, create a dataframe of the passes, # and a boolean mask for the outcome df = read_event(f'{EVENT_SLUG}/7478.json', related_event_df=False, shot_freeze_frame_df=False, tactics_lineup_df=False)['event'] ############################################################################## # Boolean mask for filtering the dataset by team team1, team2 = df.team_name.unique() mask_team1 = (df.type_name == 'Pass') & (df.team_name == team1) ############################################################################## # Filter dataset to only include one teams passes and get boolean mask for the completed passes df_pass = df.loc[mask_team1, ['x', 'y', 'end_x', 'end_y', 'outcome_name']] mask_complete = df_pass.outcome_name.isnull() ##############################################################################
This example shows how to plot the location of events occurring in a match using kernel density estimation (KDE). """ from mplsoccer import Pitch from mplsoccer.statsbomb import read_event, EVENT_SLUG ############################################################################## # load first game that Messi played as a false-9 and the match before as dataframes kwargs = { 'related_event_df': False, 'shot_freeze_frame_df': False, 'tactics_lineup_df': False } df_false9 = read_event(f'{EVENT_SLUG}/69249.json', **kwargs)['event'] df_before_false9 = read_event(f'{EVENT_SLUG}/69251.json', **kwargs)['event'] ############################################################################## # Filter the dataframes to only include Messi's events and the starting positions df_false9 = df_false9.loc[df_false9.player_id == 5503, ['x', 'y']] df_before_false9 = df_before_false9.loc[df_before_false9.player_id == 5503, ['x', 'y']] ############################################################################## # View a dataframe df_false9.head() ##############################################################################
# they are not plotted. note this automatically # sets the endnote/title space to zero # so the grid starts at the bottom/left location endnote_height=0, title_height=0) ############################################################################## # Pass maps # --------- # The following example plots pass maps for each player on a team. # The plot design is copied from # `Brad @DymondFormation <https://twitter.com/DymondFormation>`_. # # First we need to get the StatsBomb events, tactics, and lineup data JSON = '15946.json' kwargs = {'related_event_df': False, 'shot_freeze_frame_df': False, 'tactics_lineup_df': True} event_dict = read_event(f'{EVENT_SLUG}/{JSON}', **kwargs) events = event_dict['event'] tactics = event_dict['tactics_lineup'] # read the lineup (has all the players even if they didn't play) lineup = read_lineup(f'{LINEUP_SLUG}/{JSON}', warn=False) ############################################################################## # Add on the subbed on/ off times to the lineup dataframe # dataframe with player_id and when they were subbed off time_off = events.loc[(events.type_name == 'Substitution'), ['player_id', 'minute']] time_off.rename({'minute': 'off'}, axis='columns', inplace=True) # dataframe with player_id and when they were subbed on time_on = events.loc[(events.type_name == 'Substitution'), ['substitution_replacement_id', 'minute']]
This example shows how to plot passes between players in a set formation. """ import pandas as pd from mplsoccer.pitch import Pitch from matplotlib.colors import to_rgba import numpy as np from mplsoccer.statsbomb import read_event, EVENT_SLUG ############################################################################## # Set team and match info, and get event and tactics dataframes for the defined match_id match_id = 15946 team = 'Barcelona' opponent = 'Alavés (A), 2018/19 La Liga' event_dict = read_event(f'{EVENT_SLUG}/{match_id}.json', warn=False) players = event_dict['tactics_lineup'] events = event_dict['event'] ############################################################################## # Adding on the last tactics id and formation for the team for each event events.loc[events.tactics_formation.notnull(), 'tactics_id'] = events.loc[ events.tactics_formation.notnull(), 'id'] events[['tactics_id', 'tactics_formation']] = events.groupby('team_name')[[ 'tactics_id', 'tactics_formation']].ffill() ############################################################################## # Add the abbreviated player position to the players dataframe formation_dict = {1: 'GK', 2: 'RB', 3: 'RCB', 4: 'CB', 5: 'LCB', 6: 'LB', 7: 'RWB',
# https://github.com/koenvo/wyscout-soccer-match-event-dataset wyscout_id = df_overlap.iloc[IDX].wyscout_json WYSCOUT_URL = ( f'https://raw.githubusercontent.com/koenvo/wyscout-soccer-match-event-dataset/' f'main/processed/files/{wyscout_id}') # Here we get the url of the cooresponding statsbomb match statsbomb_id = df_overlap.iloc[IDX].statsbomb_json STATSBOMB_URL = f'{EVENT_SLUG}/{statsbomb_id}' ############################################################################## # Get the StatsBomb data as a dataframe # ------------------------------------- df_statsbomb = read_event(STATSBOMB_URL, related_event_df=False, shot_freeze_frame_df=False, tactics_lineup_df=False)['event'] ############################################################################## # Get the Wyscout data as a dataframe using Kloppy # ------------------------------------------------ # We first save the file locally and then load it in Kloppy as a dataframe # create the data/wyscout directories to store the file if they don't exist WYSCOUT_PATH = os.path.join('data', 'wyscout') if os.path.exists(WYSCOUT_PATH) is False: os.makedirs(WYSCOUT_PATH) # Save the Wyscout json in the data/wyscout directory WYSCOUT_JSON_PATH = os.path.join(WYSCOUT_PATH, wyscout_id) if os.path.exists(
""" import numpy as np import pandas as pd from matplotlib.colors import to_rgba from mplsoccer import Pitch from mplsoccer.statsbomb import read_event, EVENT_SLUG ############################################################################## # Set team and match info, and get event and tactics dataframes for the defined match_id MATCH_ID = 15946 TEAM = 'Barcelona' OPPONENT = 'Alavés (A), 2018/19 La Liga' event_dict = read_event(f'{EVENT_SLUG}/{MATCH_ID}.json', warn=False) players = event_dict['tactics_lineup'] events = event_dict['event'] ############################################################################## # Adding on the last tactics id and formation for the team for each event events.loc[events.tactics_formation.notnull(), 'tactics_id'] = events.loc[events.tactics_formation.notnull(), 'id'] events[['tactics_id', 'tactics_formation' ]] = events.groupby('team_name')[['tactics_id', 'tactics_formation']].ffill() ############################################################################## # Add the abbreviated player position to the players dataframe
df_lineup.to_parquet(os.path.join(DATA_FOLDER, 'lineup.parquet')) df_lineup.info() ############################################################################## # Event data # ---------- # We will also loop through the first five event files. # However, the ``read_event`` function returns a dictionary of four dataframes: # 'event', 'related_event', 'shot_freeze_frame' and 'tactics_lineup'. # It's possible to alter ``read_event`` to return fewer dataframes (see the API docs). # loop through all the changed links and store as parquet files - small and fast files for file in event_links: save_path = f'{os.path.basename(file)[:-4]}parquet' try: dict_event = sbapi.read_event(file, warn=False) # save to parquet files # using the dictionary key to access the dataframes from the dictionary dict_event['event'].to_parquet( os.path.join(DATA_FOLDER, 'event_raw', save_path)) dict_event['related_event'].to_parquet( os.path.join(DATA_FOLDER, 'related_raw', save_path)) dict_event['shot_freeze_frame'].to_parquet( os.path.join(DATA_FOLDER, 'freeze_raw', save_path)) dict_event['tactics_lineup'].to_parquet( os.path.join(DATA_FOLDER, 'tactic_raw', save_path)) except ValueError: print('Skipping:', file) ############################################################################## # Get a list of match_ids to update
Shot freeze frame ================= This example shows how to plot a shot freeze frame. """ from mplsoccer.pitch import Pitch from mplsoccer.statsbomb import read_event, read_lineup, EVENT_SLUG, LINEUP_SLUG import matplotlib.pyplot as plt plt.style.use('ggplot') # get event and lineup dataframes for game 7478 # event data dict_event = read_event(f'{EVENT_SLUG}/7478.json', related_event_df=False, tactics_lineup_df=False, warn=False) df_event = dict_event['event'] df_freeze = dict_event['shot_freeze_frame'] # lineup data df_lineup = read_lineup(f'{LINEUP_SLUG}/7478.json', warn=False) df_lineup = df_lineup[['player_id', 'player_jersey_number', 'team_name']].copy() ############################################################################## # Subset a shot shot_id = '8bb8bbc2-68a6-4c01-93de-53a194e7a1cf' df_freeze_frame = df_freeze[df_freeze.id == shot_id].copy() df_shot_event = df_event[df_event.id == shot_id].dropna(axis=1,
import matplotlib.pyplot as plt import numpy as np import pandas as pd from matplotlib.colors import LinearSegmentedColormap import cmasher as cmr from mplsoccer import VerticalPitch from mplsoccer.statsbomb import read_event, EVENT_SLUG from mplsoccer.utils import FontManager # get data match_files = ['19789.json', '19794.json', '19805.json'] kwargs = {'related_event_df': False, 'shot_freeze_frame_df': False, 'tactics_lineup_df': False, 'warn': False} df = pd.concat([read_event(f'{EVENT_SLUG}/{file}', **kwargs)['event'] for file in match_files]) # filter chelsea pressure events mask_chelsea_pressure = (df.team_name == 'Chelsea FCW') & (df.type_name == 'Pressure') df = df.loc[mask_chelsea_pressure, ['x', 'y']] ############################################################################## # cmasher colormaps # ----------------------- # Cmasher colormaps are scientific colormaps that have been designed to be # perceptually uniform (i.e. color changes visually look the same as the value changes) # and mostly colorblind friendly. A great choice # to get started and potentially more exciting than the default matplotlib choices. # # Let's first get a dictionary of all the colormaps # # See the docs for more info: https://cmasher.readthedocs.io/.
import pandas as pd import seaborn as sns from matplotlib.cm import get_cmap from mplsoccer import Pitch, VerticalPitch from mplsoccer.statsbomb import read_event, EVENT_SLUG from mplsoccer.utils import FontManager # get data for a Sevilla versus Barcelona match with a high amount of shots kwargs = { 'related_event_df': False, 'shot_freeze_frame_df': False, 'tactics_lineup_df': False, 'warn': False } df = read_event(f'{EVENT_SLUG}/9860.json', **kwargs)['event'] # setup the mplsoccer StatsBomb Pitches # note not much padding around the pitch so the marginal axis are tight to the pitch # if you are using a different goal type you will need to increase the padding to see the goals pitch = Pitch(pad_top=0.05, pad_right=0.05, pad_bottom=0.05, pad_left=0.05, line_zorder=2) vertical_pitch = VerticalPitch(half=True, pad_top=0.05, pad_right=0.05, pad_bottom=0.05, pad_left=0.05, line_zorder=2)
""" import numpy as np import pandas as pd import seaborn as sns from matplotlib.cm import get_cmap import matplotlib.pyplot as plt from mplsoccer import Pitch, VerticalPitch from mplsoccer.statsbomb import read_event, EVENT_SLUG from mplsoccer.utils import FontManager # get data for a Sevilla versus Barcelona match with a high amount of shots kwargs = {'related_event_df': False, 'shot_freeze_frame_df': False, 'tactics_lineup_df': False, 'warn': False} df = read_event(f'{EVENT_SLUG}/9860.json', **kwargs)['event'] # setup the mplsoccer StatsBomb Pitches # note not much padding around the pitch so the marginal axis are tight to the pitch # if you are using a different goal type you will need to increase the padding to see the goals pitch = Pitch(pad_top=0.05, pad_right=0.05, pad_bottom=0.05, pad_left=0.05, line_zorder=2) vertical_pitch = VerticalPitch(half=True, pad_top=0.05, pad_right=0.05, pad_bottom=0.05, pad_left=0.05, line_zorder=2) # setup a mplsoccer FontManager to download google fonts (Roboto-Regular / SigmarOne-Regular) fm = FontManager() fm_rubik = FontManager(('https://github.com/google/fonts/blob/main/ofl/rubikmonoone/' 'RubikMonoOne-Regular.ttf?raw=true')) ############################################################################## # Subset the shots for each team and move Barcelona's shots to the other side of the pitch.