passes_between['width'] = passes_between.pass_count / passes_between.pass_count.max() * max_line_width average_locs_and_count['marker_size'] = (average_locs_and_count['count'] / average_locs_and_count['count'].max() * max_marker_size) ############################################################################## # Set color to make the lines more transparent when fewer passes are made min_transparency = 0.3 color = np.array(to_rgba('white')) color = np.tile(color, (len(passes_between), 1)) c_transparency = passes_between.pass_count / passes_between.pass_count.max() c_transparency = (c_transparency * (1 - min_transparency)) + min_transparency color[:, 3] = c_transparency ############################################################################## # Plotting pitch = Pitch(pitch_type='statsbomb', orientation='horizontal', pitch_color='#22312b', line_color='#c7d5cc', figsize=(16, 11), constrained_layout=True, tight_layout=False) fig, ax = pitch.draw() pass_lines = pitch.lines(passes_between.x, passes_between.y, passes_between.x_end, passes_between.y_end, lw=passes_between.width, color=color, zorder=1, ax=ax) pass_nodes = pitch.scatter(average_locs_and_count.x, average_locs_and_count.y, s=average_locs_and_count.marker_size, color='red', edgecolors='black', linewidth=1, alpha=1, ax=ax) for index, row in average_locs_and_count.iterrows(): pitch.annotate(row.name, xy=(row.x, row.y), c='white', va='center', ha='center', size=16, weight='bold', ax=ax) title = ax.set_title("{} {} Formation vs {}".format(team, formation, opponent), size=28, y=0.97, color='#c7d5cc') fig.set_facecolor("#22312b")
df_shot_event.y, df_shot_event.shot_end_x, df_shot_event.shot_end_y, comet=True, label='shot', color='#cb5a4c', ax=ax) # plot the angle to the goal pitch.goal_angle(df_shot_event.x, df_shot_event.y, ax=ax, alpha=0.2, zorder=1.1, color='#cb5a4c', goal='right') # plot the jersey numbers for i, label in enumerate(df_freeze_frame.player_jersey_number): pitch.annotate(label, (df_freeze_frame.x[i], df_freeze_frame.y[i]), va='center', ha='center', color='white', fontsize=12, ax=ax) # add a legend and title legend = ax.legend(loc='center left', labelspacing=1, fontsize=15) title = ax.set_title( f'{df_shot_event.player_name.iloc[0]}\n{team1} vs. {team2}', fontsize=30)
stripe=True, constrained_layout=False) fig, ax = pitch.draw() # plotting ax.set_title('The first Game Messi played in the false 9 role', fontsize=30, pad=20) # plot the kernel density estimation pitch.kdeplot(df_false9.x, df_false9.y, ax=ax, cmap='plasma', linewidths=3) # annotate pitch.annotate('6-2 thrashing \nof Real Madrid', (25, 10), color='white', fontsize=25, ha='center', va='center', ax=ax) pitch.annotate('more events', (70, 30), (20, 30), ax=ax, color='white', ha='center', va='center', fontsize=20, arrowprops=dict(facecolor='white', edgecolor='None')) pitch.annotate('fewer events', (51, 20), (20, 20), ax=ax, color='white', ha='center', va='center', fontsize=20,
def createPassNetworks(match_data, events_df, matchId, team, max_line_width, marker_size, edgewidth, dh_arrow_width, marker_color, marker_edge_color, shrink, ax, kit_no_size=20): # getting team id and venue if match_data['home']['name'] == team: teamId = match_data['home']['teamId'] venue = 'home' else: teamId = match_data['away']['teamId'] venue = 'away' # getting opponent if venue == 'home': opponent = match_data['away']['name'] else: opponent = match_data['home']['name'] # getting player dictionary team_players_dict = {} for player in match_data[venue]['players']: team_players_dict[player['playerId']] = player['name'] # getting minute of first substitution for i in events_df.index: if events_df.loc[i, 'type'] == 'SubstitutionOn' and events_df.loc[ i, 'teamId'] == teamId: sub_minute = str(events_df.loc[i, 'minute']) break # getting players dataframe match_players_df = pd.DataFrame() player_names = [] player_ids = [] player_pos = [] player_kit_number = [] for player in match_data[venue]['players']: player_names.append(player['name']) player_ids.append(player['playerId']) player_pos.append(player['position']) player_kit_number.append(player['shirtNo']) match_players_df['playerId'] = player_ids match_players_df['playerName'] = player_names match_players_df['playerPos'] = player_pos match_players_df['playerKitNumber'] = player_kit_number # extracting passes passes_df = events_df.loc[events_df['teamId'] == teamId].reset_index().drop('index', axis=1) passes_df['playerId'] = passes_df['playerId'].astype('float').astype( 'Int64') if 'playerName' in passes_df.columns: passes_df = passes_df.drop(columns='playerName') passes_df.dropna(subset=["playerId"], inplace=True) passes_df.insert( 27, column='playerName', value=[team_players_dict[i] for i in list(passes_df['playerId'])]) if 'passRecipientId' in passes_df.columns: passes_df = passes_df.drop(columns='passRecipientId') passes_df = passes_df.drop(columns='passRecipientName') passes_df.insert(28, column='passRecipientId', value=passes_df['playerId'].shift(-1)) passes_df.insert(29, column='passRecipientName', value=passes_df['playerName'].shift(-1)) passes_df.dropna(subset=["passRecipientName"], inplace=True) passes_df = passes_df.loc[events_df['type'] == 'Pass', :].reset_index( drop=True) passes_df = passes_df.loc[events_df['outcomeType'] == 'Successful', :].reset_index(drop=True) index_names = passes_df.loc[passes_df['playerName'] == passes_df['passRecipientName']].index passes_df.drop(index_names, inplace=True) passes_df = passes_df.merge(match_players_df, on=['playerId', 'playerName'], how='left', validate='m:1') passes_df = passes_df.merge(match_players_df.rename( { 'playerId': 'passRecipientId', 'playerName': 'passRecipientName' }, axis='columns'), on=['passRecipientId', 'passRecipientName'], how='left', validate='m:1', suffixes=['', 'Receipt']) passes_df = passes_df[passes_df['playerPos'] != 'Sub'] # getting team formation formation = match_data[venue]['formations'][0]['formationName'] formation = '-'.join(formation) # getting player average locations location_formation = passes_df[['playerKitNumber', 'x', 'y']] average_locs_and_count = location_formation.groupby('playerKitNumber').agg( { 'x': ['mean'], 'y': ['mean', 'count'] }) average_locs_and_count.columns = ['x', 'y', 'count'] # getting separate dataframe for selected columns passes_formation = passes_df[[ 'id', 'playerKitNumber', 'playerKitNumberReceipt' ]].copy() passes_formation['EPV'] = passes_df['EPV'] # getting dataframe for passes between players passes_between = passes_formation.groupby( ['playerKitNumber', 'playerKitNumberReceipt']).agg({ 'id': 'count', 'EPV': 'sum' }).reset_index() passes_between.rename({'id': 'pass_count'}, axis='columns', inplace=True) passes_between = passes_between.merge(average_locs_and_count, left_on='playerKitNumberReceipt', right_index=True) passes_between = passes_between.merge(average_locs_and_count, left_on='playerKitNumber', right_index=True, suffixes=['', '_end']) # filtering passes pass_filter = int(passes_between['pass_count'].mean()) passes_between = passes_between.loc[ passes_between['pass_count'] > pass_filter] # calculating the line width passes_between[ 'width'] = passes_between.pass_count / passes_between.pass_count.max( ) * max_line_width passes_between = passes_between.reset_index(drop=True) # setting color to make the lines more transparent when fewer passes are made min_transparency = 0.3 color = np.array(to_rgba('white')) color = np.tile(color, (len(passes_between), 1)) c_transparency = passes_between.pass_count / passes_between.pass_count.max( ) c_transparency = (c_transparency * (1 - min_transparency)) + min_transparency color[:, 3] = c_transparency passes_between['alpha'] = color.tolist() # separating paired passes from normal passes passes_between_threshold = 15 filtered_pair_df = [] pair_list = [ comb for comb in combinations(passes_between['playerKitNumber'].unique(), 2) ] for pair in pair_list: df = passes_between[( (passes_between['playerKitNumber'] == pair[0]) & (passes_between['playerKitNumberReceipt'] == pair[1])) | ( (passes_between['playerKitNumber'] == pair[1]) & (passes_between['playerKitNumberReceipt'] == pair[0]))] if df.shape[0] == 2: if (np.array(df.pass_count)[0] >= passes_between_threshold) and ( np.array(df.pass_count)[1] >= passes_between_threshold): filtered_pair_df.append(df) passes_between.drop(df.index, inplace=True) if len(filtered_pair_df) > 0: filtered_pair_df = pd.concat(filtered_pair_df).reset_index(drop=True) passes_between = passes_between.reset_index(drop=True) # plotting pitch = Pitch(pitch_type='opta', pitch_color='#171717', line_color='#5c5c5c', goal_type='box') pitch.draw(ax=ax, constrained_layout=True, tight_layout=True) average_locs_and_count['zorder'] = list(np.linspace(1, 5, 11)) for i in average_locs_and_count.index: pitch.scatter(average_locs_and_count.loc[i, 'x'], average_locs_and_count.loc[i, 'y'], s=marker_size, color=marker_color, edgecolors=marker_edge_color, linewidth=edgewidth, alpha=1, zorder=average_locs_and_count.loc[i, 'zorder'], ax=ax) for i in passes_between.index: x = passes_between.loc[i, 'x'] y = passes_between.loc[i, 'y'] endX = passes_between.loc[i, 'x_end'] endY = passes_between.loc[i, 'y_end'] coordsA = "data" coordsB = "data" con = ConnectionPatch([endX, endY], [x, y], coordsA, coordsB, arrowstyle="simple", shrinkA=shrink, shrinkB=shrink, mutation_scale=passes_between.loc[i, 'width'] * max_line_width, color=passes_between.loc[i, 'alpha']) ax.add_artist(con) if len(filtered_pair_df) > 0: for i in filtered_pair_df.index: x = filtered_pair_df.loc[i, 'x'] y = filtered_pair_df.loc[i, 'y'] endX = filtered_pair_df.loc[i, 'x_end'] endY = filtered_pair_df.loc[i, 'y_end'] coordsA = "data" coordsB = "data" con = ConnectionPatch([endX, endY], [x, y], coordsA, coordsB, arrowstyle="<|-|>", shrinkA=shrink, shrinkB=shrink, mutation_scale=dh_arrow_width, lw=filtered_pair_df.loc[i, 'width'] * max_line_width / 5, color=filtered_pair_df.loc[i, 'alpha']) ax.add_artist(con) for i in average_locs_and_count.index: pitch.annotate(i, xy=(average_locs_and_count.loc[i, 'x'], average_locs_and_count.loc[i, 'y']), family='DejaVu Sans', c='white', va='center', ha='center', zorder=average_locs_and_count.loc[i, 'zorder'], size=kit_no_size, weight='bold', ax=ax) ax.text(50, 104, "{} (Mins 1-{})".format(team, sub_minute).upper(), size=10, fontweight='bold', ha='center', va='center') ax.text(2, 3, '{}'.format(formation), size=9, c='grey')
""" ==================== Plot rotated markers ==================== This example shows how to plot rotated markers. Warning: The rotation angle is in degrees and assumes the original marker is pointing upwards ↑. If it's not you will have to modify the rotation degrees. Rotates the marker in degrees, clockwise. 0 degrees is facing the direction of play (left to right). In a horizontal pitch, 0 degrees is this way →, in a vertical pitch, 0 degrees is this way ↑ """ from mplsoccer.pitch import Pitch import matplotlib.pyplot as plt from mplsoccer.scatterutils import arrowhead_marker import numpy as np plt.style.use('ggplot') pitch = Pitch(figsize=(10, 8), axis=True, label=True) fig, ax = pitch.draw() n = 15 x = np.linspace(0, 120, n) y = np.linspace(0, 80, n) rotate = np.linspace(0, 360, n).round(0).astype(np.int32) pitch.scatter(x, y, rotation_degrees=rotate, marker=arrowhead_marker, edgecolors='black', s=3500, ax=ax) for i in range(n): pitch.annotate(f'{rotate[i]}°', (x[i], y[i]), ha='center', va='center', fontsize=15, color='white', ax=ax)
def createPVFormationMap(match_data, events_df, team, color_palette, markerstyle, markersize, markeredgewidth, labelsize, labelcolor, ax): # getting team id and venue if match_data['home']['name'] == team: teamId = match_data['home']['teamId'] venue = 'home' else: teamId = match_data['away']['teamId'] venue = 'away' # getting opponent if venue == 'home': opponent = match_data['away']['name'] else: opponent = match_data['home']['name'] # getting player dictionary team_players_dict = {} for player in match_data[venue]['players']: team_players_dict[player['playerId']] = player['name'] # getting minute of first substitution for i, row in events_df.iterrows(): if row['type'] == 'SubstitutionOn' and row['teamId'] == teamId: sub_minute = str(row['minute']) break # getting players dataframe match_players_df = pd.DataFrame() player_names = [] player_ids = [] player_pos = [] player_kit_number = [] for player in match_data[venue]['players']: player_names.append(player['name']) player_ids.append(player['playerId']) player_pos.append(player['position']) player_kit_number.append(player['shirtNo']) match_players_df['playerId'] = player_ids match_players_df['playerName'] = player_names match_players_df['playerPos'] = player_pos match_players_df['playerKitNumber'] = player_kit_number # extracting passes passes_df = events_df.loc[events_df['teamId'] == teamId].reset_index().drop('index', axis=1) passes_df['playerId'] = passes_df['playerId'].astype('float').astype( 'Int64') if 'playerName' in passes_df.columns: passes_df = passes_df.drop(columns='playerName') passes_df.dropna(subset=["playerId"], inplace=True) passes_df.insert( 27, column='playerName', value=[team_players_dict[i] for i in list(passes_df['playerId'])]) if 'passRecipientId' in passes_df.columns: passes_df = passes_df.drop(columns='passRecipientId') passes_df = passes_df.drop(columns='passRecipientName') passes_df.insert(28, column='passRecipientId', value=passes_df['playerId'].shift(-1)) passes_df.insert(29, column='passRecipientName', value=passes_df['playerName'].shift(-1)) passes_df.dropna(subset=["passRecipientName"], inplace=True) passes_df = passes_df.loc[events_df['type'] == 'Pass', :].reset_index( drop=True) passes_df = passes_df.loc[events_df['outcomeType'] == 'Successful', :].reset_index(drop=True) index_names = passes_df.loc[passes_df['playerName'] == passes_df['passRecipientName']].index passes_df.drop(index_names, inplace=True) passes_df = passes_df.merge(match_players_df, on=['playerId', 'playerName'], how='left', validate='m:1') passes_df = passes_df.merge(match_players_df.rename( { 'playerId': 'passRecipientId', 'playerName': 'passRecipientName' }, axis='columns'), on=['passRecipientId', 'passRecipientName'], how='left', validate='m:1', suffixes=['', 'Receipt']) # passes_df = passes_df[passes_df['playerPos'] != 'Sub'] # Getting net possesion value for passes netPVPassed = passes_df.groupby(['playerId', 'playerName'])['EPV'].sum().reset_index() netPVReceived = passes_df.groupby(['passRecipientId', 'passRecipientName' ])['EPV'].sum().reset_index() # Getting formation and player ids for first 11 formation = match_data[venue]['formations'][0]['formationName'] formation_positions = match_data[venue]['formations'][0][ 'formationPositions'] playerIds = match_data[venue]['formations'][0]['playerIds'][:11] # Getting all data in a dataframe formation_data = [] for playerId, pos in zip(playerIds, formation_positions): pl_dict = {'playerId': playerId} pl_dict.update(pos) formation_data.append(pl_dict) formation_data = pd.DataFrame(formation_data) formation_data['vertical'] = normalize( formation_data['vertical'], { 'actual': { 'lower': 0, 'upper': 10 }, 'desired': { 'lower': 10, 'upper': 110 } }) formation_data['horizontal'] = normalize(formation_data['horizontal'], { 'actual': { 'lower': 0, 'upper': 10 }, 'desired': { 'lower': 80, 'upper': 0 } }) formation_data = netPVPassed.join(formation_data.set_index('playerId'), on='playerId', how='inner').reset_index(drop=True) formation_data = formation_data.rename(columns={"EPV": "PV"}) # Plotting pitch = Pitch(pitch_type='statsbomb', pitch_color='#171717', line_color='#5c5c5c', goal_type='box') pitch.draw(ax=ax, constrained_layout=True, tight_layout=True) sns.scatterplot(x='vertical', y='horizontal', data=formation_data, hue='PV', s=markersize, marker=markerstyle, legend=False, palette=color_palette, linewidth=markeredgewidth, ax=ax) ax.text(2, 78, '{}'.format('-'.join(formation)), size=20, c='grey') for index, row in formation_data.iterrows(): pitch.annotate(str(round(row.PV * 100, 2)) + '%', xy=(row.vertical, row.horizontal), c=labelcolor, va='center', ha='center', size=labelsize, zorder=2, weight='bold', ax=ax) pitch.annotate(row.playerName, xy=(row.vertical, row.horizontal + 5), c=labelcolor, va='center', ha='center', size=labelsize, zorder=2, weight='bold', ax=ax)
arrows = pitch.lines(1.2 * pass_between.x, .8 * pass_between.y, 1.2 * pass_between.x_end, .8 * pass_between.y_end, ax=ax, lw=pass_between.pass_count * 1, color='White', zorder=1, alpha=0.5) nodes = pitch.scatter(1.2 * average_locations.x, .8 * average_locations.y, s=600, color='#d3d3d3', edgecolors='black', linewidth=2.5, alpha=1, ax=ax) for index, row in average_locations.iterrows(): pitch.annotate(int(row.passer), xy=(row.x * 1.2, row.y * .8), c='Black', va='center', ha='center', size=13, weight='bold', ax=ax) ax.set_title('Barcelona vs Valladolida', color='#dee6ea', fontsize=30) ax.set_xlabel('@ibramallu')
def createPassNetworks(match_data, matches_df, events_df, team, pitch_color, max_lw, marker_size, marker_color, marker_label='kit_no', marker_label_size=20): """ Parameters ---------- match_data : Data containing everything about the match matches_df : DataFrame containing match data. events_df : DataFrame containing event data for that match. team : Name of the required team. pitch_color : color of the pitch and figure. max_lw : maximum line width of network lines. marker_size : size of the circle markers. marker_color : color of the circle markers. Returns ------- Pitch Plot. """ matchId = match_data['matchId'] # getting team id and venue if match_data['home']['name'] == team: teamId = match_data['home']['teamId'] venue = 'home' else: teamId = match_data['away']['teamId'] venue = 'away' # getting opponent if venue == 'home': opponent = match_data['away']['name'] else: opponent = match_data['home']['name'] team_players_dict = {} for player in matches_df[venue][matchId]['players']: team_players_dict[player['playerId']] = player['name'] team_playerskitno_dict = {} for player in matches_df[venue][matchId]['players']: team_playerskitno_dict[player['shirtNo']] = player['name'] passes_df = events_df.loc[[row['displayName'] == 'Pass' for row in list(events_df['type'])]].reset_index(drop=True) passes_df = passes_df.loc[passes_df['teamId'] == teamId].reset_index().drop('index', axis=1) passes_df = passes_df.loc[[row['displayName'] == 'Successful' for row in list(passes_df['outcomeType'])]].reset_index(drop=True) passes_df.insert(27, column='playerName', value=[team_players_dict[i] for i in list(passes_df['playerId'])]) passes_df.insert(28, column='passRecipientId', value=passes_df['playerId'].shift(-1)) passes_df.insert(29, column='passRecipientName', value=passes_df['playerName'].shift(-1)) passes_df.dropna(subset=["passRecipientName"], inplace=True) match_players_df = pd.DataFrame() player_names = [] player_ids = [] player_pos = [] player_kit_number = [] for player in matches_df[venue][matchId]['players']: player_names.append(player['name']) player_ids.append(player['playerId']) player_pos.append(player['position']) player_kit_number.append(player['shirtNo']) match_players_df['playerId'] = player_ids match_players_df['playerName'] = player_names match_players_df['playerPos'] = player_pos match_players_df['playerKitNumber'] = player_kit_number passes_df = passes_df.merge(match_players_df, on=['playerId', 'playerName'], how='left', validate='m:1') passes_df = passes_df.merge(match_players_df.rename({'playerId': 'passRecipientId', 'playerName':'passRecipientName'}, axis='columns'), on=['passRecipientId', 'passRecipientName'], how='left', validate='m:1', suffixes=['', 'Receipt']) passes_df = passes_df[passes_df['playerPos'] != 'Sub'] passes_formation = passes_df[['id', 'playerKitNumber', 'playerKitNumberReceipt']].copy() location_formation = passes_df[['playerKitNumber', 'x', 'y']] average_locs_and_count = location_formation.groupby('playerKitNumber').agg({'x': ['mean'], 'y': ['mean', 'count']}) average_locs_and_count.columns = ['x', 'y', 'count'] passes_formation['kitNo_max'] = passes_formation[['playerKitNumber', 'playerKitNumberReceipt']].max(axis='columns') passes_formation['kitNo_min'] = passes_formation[['playerKitNumber', 'playerKitNumberReceipt']].min(axis='columns') passes_between = passes_formation.groupby(['kitNo_max', 'kitNo_min']).id.count().reset_index() passes_between.rename({'id': 'pass_count'}, axis='columns', inplace=True) # add on the location of each player so we have the start and end positions of the lines passes_between = passes_between.merge(average_locs_and_count, left_on='kitNo_min', right_index=True) passes_between = passes_between.merge(average_locs_and_count, left_on='kitNo_max', right_index=True, suffixes=['', '_end']) ############################################################################## # Calculate the line width and marker sizes relative to the largest counts max_line_width = max_lw passes_between['width'] = passes_between.pass_count / passes_between.pass_count.max() * max_line_width #average_locs_and_count['marker_size'] = (average_locs_and_count['count'] # / average_locs_and_count['count'].max() * max_marker_size) ############################################################################## # Set color to make the lines more transparent when fewer passes are made min_transparency = 0.3 color = np.array(to_rgba('white')) color = np.tile(color, (len(passes_between), 1)) c_transparency = passes_between.pass_count / passes_between.pass_count.max() c_transparency = (c_transparency * (1 - min_transparency)) + min_transparency color[:, 3] = c_transparency # Plotting name on markers if marker_label == 'name': average_locs_and_count.index = average_locs_and_count.index.map(team_playerskitno_dict) ############################################################################## # Plotting pitch = Pitch(pitch_type='statsbomb', orientation='horizontal', pitch_color=pitch_color, line_color='#c7d5cc', figsize=(16, 11), constrained_layout=True, tight_layout=False) fig, ax = pitch.draw() pitch.lines(passes_between.x/100*120, 80-passes_between.y/100*80, passes_between.x_end/100*120, 80-passes_between.y_end/100*80, lw=passes_between.width, color=color, zorder=1, ax=ax) pitch.scatter(average_locs_and_count.x/100*120, 80-average_locs_and_count.y/100*80, s=marker_size, color=marker_color, edgecolors='black', linewidth=1, alpha=1, ax=ax) for index, row in average_locs_and_count.iterrows(): pitch.annotate(row.name, xy=(row.x/100*120, 80-row.y/100*80), c='white', va='center', ha='center', size=marker_label_size, weight='bold', ax=ax) ax.set_title("{} Pass Network vs {}".format(team, opponent), size=15, y=0.97, color='#c7d5cc') fig.set_facecolor(pitch_color)
def createAttPassNetworks(match_data, matches_df, events_df, team, pitch_color, max_lw, marker_size, marker_color, marker_label='kit_no', marker_label_size=20): """ Parameters ---------- match_data : Data containing everything about the match matches_df : DataFrame containing match data. events_df : DataFrame containing event data for that match. team : Name of the required team. pitch_color : color of the pitch and figure. max_lw : maximum line width of network lines. marker_size : size of the circle markers. marker_color : color of the circle markers. Returns ------- Pitch Plot. """ # getting match_id matchId = match_data['matchId'] # getting team id and venue if match_data['home']['name'] == team: teamId = match_data['home']['teamId'] venue = 'home' else: teamId = match_data['away']['teamId'] venue = 'away' # getting opponent if venue == 'home': opponent = match_data['away']['name'] else: opponent = match_data['home']['name'] # getting player dictionary team_players_dict = {} for player in match_data[venue]['players']: team_players_dict[player['playerId']] = player['name'] # getting player dictionary with kit no team_playerskitno_dict = {} for player in matches_df[venue][matchId]['players']: team_playerskitno_dict[player['shirtNo']] = player['name'] # getting minute of first substitution for i,row in events_df.iterrows(): if row['type']['displayName'] == 'SubstitutionOn' and row['teamId'] == teamId: sub_minute = str(row['minute']) break # getting players dataframe match_players_df = pd.DataFrame() player_names = [] player_ids = [] player_pos = [] player_kit_number = [] for player in match_data[venue]['players']: player_names.append(player['name']) player_ids.append(player['playerId']) player_pos.append(player['position']) player_kit_number.append(player['shirtNo']) match_players_df['playerId'] = player_ids match_players_df['playerName'] = player_names match_players_df['playerPos'] = player_pos match_players_df['playerKitNumber'] = player_kit_number # extracting passes passes_df = events_df.loc[events_df['teamId'] == teamId].reset_index().drop('index', axis=1) passes_df.dropna(subset=["playerId"], inplace=True) passes_df.insert(27, column='playerName', value=[team_players_dict[i] for i in list(passes_df['playerId'])]) passes_df.insert(28, column='passRecipientId', value=passes_df['playerId'].shift(-1)) passes_df.insert(29, column='passRecipientName', value=passes_df['playerName'].shift(-1)) passes_df.dropna(subset=["passRecipientName"], inplace=True) passes_df = passes_df.loc[[row['displayName'] == 'Pass' for row in list(passes_df['type'])]].reset_index(drop=True) passes_df = passes_df.loc[[row['displayName'] == 'Successful' for row in list(passes_df['outcomeType'])]].reset_index(drop=True) index_names = passes_df.loc[passes_df['playerName']==passes_df['passRecipientName']].index passes_df.drop(index_names, inplace=True) passes_df = passes_df.merge(match_players_df, on=['playerId', 'playerName'], how='left', validate='m:1') passes_df = passes_df.merge(match_players_df.rename({'playerId': 'passRecipientId', 'playerName':'passRecipientName'}, axis='columns'), on=['passRecipientId', 'passRecipientName'], how='left', validate='m:1', suffixes=['', 'Receipt']) passes_df = passes_df[passes_df['playerPos'] != 'Sub'] # getting team formation formation = match_data[venue]['formations'][0]['formationName'] formation = '-'.join(formation) # getting player average locations location_formation = passes_df[['playerKitNumber', 'x', 'y']] average_locs_and_count = location_formation.groupby('playerKitNumber').agg({'x': ['mean'], 'y': ['mean', 'count']}) average_locs_and_count.columns = ['x', 'y', 'count'] # filtering progressive passes passes_df = passes_df.loc[passes_df['EPV_difference'] > 0] # getting separate dataframe for selected columns passes_formation = passes_df[['id', 'playerKitNumber', 'playerKitNumberReceipt']].copy() passes_formation['EPV'] = passes_df['EPV_difference'] # getting dataframe for passes between players passes_between = passes_formation.groupby(['playerKitNumber', 'playerKitNumberReceipt']).agg({ 'id' : 'count', 'EPV' : 'sum'}).reset_index() passes_between.rename({'id': 'pass_count'}, axis='columns', inplace=True) passes_between = passes_between.merge(average_locs_and_count, left_on='playerKitNumberReceipt', right_index=True) passes_between = passes_between.merge(average_locs_and_count, left_on='playerKitNumber', right_index=True, suffixes=['', '_end']) # filtering passes pass_filter = int(passes_between['pass_count'].mean()) passes_between = passes_between.loc[passes_between['pass_count'] > pass_filter*2] # calculating the line width max_line_width = max_lw passes_between['width'] = passes_between.pass_count / passes_between.pass_count.max() * max_line_width passes_between = passes_between.reset_index(drop=True) # setting color to make the lines more transparent when fewer passes are made min_transparency = 0.3 color = np.array(to_rgba('white')) color = np.tile(color, (len(passes_between), 1)) c_transparency = passes_between.EPV / passes_between.EPV.max() c_transparency = (c_transparency * (1 - min_transparency)) + min_transparency color[:, 3] = c_transparency passes_between['alpha'] = color.tolist() # Plotting name on markers if marker_label == 'name': average_locs_and_count.index = average_locs_and_count.index.map(team_playerskitno_dict) # plotting pitch = Pitch(pitch_type='statsbomb', orientation='horizontal', pitch_color=pitch_color, line_color='#c7d5cc', figsize=(16, 11), constrained_layout=True, tight_layout=False) fig, ax = pitch.draw() average_locs_and_count['zorder'] = list(np.linspace(2,6,11, dtype='int')) pitch.lines(passes_between.x/100*120, 80-passes_between.y/100*80, passes_between.x_end/100*120, 80-passes_between.y_end/100*80, lw=passes_between.width, color=color, zorder=1, ax=ax) for index, row in average_locs_and_count.iterrows(): pitch.scatter(row.x/100*120, 80-row.y/100*80, s=marker_size, color=marker_color, edgecolors='black', linewidth=1, alpha=1, zorder=row.zorder, ax=ax) for index, row in average_locs_and_count.iterrows(): pitch.annotate(row.name, xy=(row.x/100*120, 80-row.y/100*80), family='DejaVu Sans', c='white', va='center', ha='center', zorder=row.zorder, size=marker_label_size, weight='bold', ax=ax) ax.set_title("{} Progressive Pass Network vs {}".format(team, opponent), size=15, y=0.97, color='#c7d5cc') fig.set_facecolor(pitch_color)
def passmap(Df, teamid, teamname, min1, max1): pitch = Pitch(pitch_type='statsbomb', orientation='vertical', pitch_color='#000000', line_color='#a9a9a9', constrained_layout=True, tight_layout=False, linewidth=0.5) fig, ax = pitch.draw() df = Df.copy() df = df[(df.expandedMinute >= min1) & (df.expandedMinute <= max1)] allplayers = df[(df.teamId == teamid) & (df.name.notna())].name.tolist() playersubbedoff = df[(df.type_displayName == 'SubstitutionOff') & (df.teamId == teamid)]['name'].tolist() timeoff = df[(df.type_displayName == 'SubstitutionOff') & (df.teamId == teamid)]['expandedMinute'].tolist() playersubbedon = df[(df.type_displayName == 'SubstitutionOn') & (df.teamId == teamid)]['name'].tolist() timeon = df[(df.type_displayName == 'SubstitutionOn') & (df.teamId == teamid)]['expandedMinute'].tolist() majoritylist = [] minoritylist = [] for i in range(len(timeon)): if ((timeon[i] >= min1) & (timeon[i] <= max1)): player1min = timeon[i] - min1 player2min = max1 - timeon[i] if (player1min >= player2min): majoritylist.append(playersubbedoff[i]) minoritylist.append(playersubbedon[i]) else: majoritylist.append(playersubbedon[i]) minoritylist.append(playersubbedoff[i]) players = list(set(allplayers) - set(minoritylist)) #return players shirtNo = [] for p in players: shirtNo.append(int(df[df.name == p]['shirtNo'].values[0])) passes_df = df.query( "(type_displayName=='Pass')&(name in @players)&(receiver in @players)&\ (outcomeType_displayName == 'Successful')&(teamId==@teamid)") #passes_df.insert(29, column='passRecipientName', value=passes_df['name'].shift(-1)) passes_df.dropna(subset=["receiver"], inplace=True) #passes_df['passer'] = passes_df['playerId'] #passes_df['recipient'] = passes_df['passer'].shift(-1) passes_df = passes_df[passes_df.columns[~passes_df.isnull().all()]] passes_df['playerKitNumber'] = passes_df['shirtNo'].fillna(0).astype( np.int) passes_df['playerKitNumberReceipt'] = passes_df['shirtNo'].shift( -1).fillna(0).astype(np.int) passer_avg = passes_df.groupby('playerKitNumber').agg({ 'x': ['median'], 'y': ['median', 'count'], 'epv_value': ['sum'] }) passer_avg.columns = ['x', 'y', 'count', 'epv'] passer_avg.index = passer_avg.index.astype(int) passes_formation = passes_df[[ 'id', 'playerKitNumber', 'playerKitNumberReceipt' ]].copy() passes_formation['kitNo_max'] = passes_formation[[ 'playerKitNumber', 'playerKitNumberReceipt' ]].max(axis='columns') passes_formation['kitNo_min'] = passes_formation[[ 'playerKitNumber', 'playerKitNumberReceipt' ]].min(axis='columns') passes_between = passes_formation.groupby(['kitNo_max', 'kitNo_min' ]).id.count().reset_index() passes_between.rename({'id': 'pass_count'}, axis='columns', inplace=True) # add on the location of each player so we have the start and end positions of the lines passes_between = passes_between.merge(passer_avg, left_on='kitNo_min', right_index=True) passes_between = passes_between.merge(passer_avg, left_on='kitNo_max', right_index=True, suffixes=['', '_end']) ''' #Between Passer and Recipient passes_between = passes_df.groupby(['passer', 'recipient']).id.count().reset_index() passes_between.rename({'id': 'pass_count'}, axis='columns', inplace=True) passes_between = passes_between.merge(passer_avg, left_on='passer', right_index=True) passes_between = passes_between.merge(passer_avg, left_on='recipient', right_index=True, suffixes=['', '_end']) ''' #Minimum No. of Passes passes_between = passes_between.loc[(passes_between['pass_count'] >= 3)] #Scaling for StatsBomb passes_between['x'] = passes_between['x'] * 1.2 passes_between['y'] = passes_between['y'] * 0.8 passer_avg['x'] = passer_avg['x'] * 1.2 passer_avg['y'] = passer_avg['y'] * 0.8 passes_between['x_end'] = passes_between['x_end'] * 1.2 passes_between['y_end'] = passes_between['y_end'] * 0.8 #Width Variable yo = passes_between.pass_count / passes_between.pass_count.max() b = 1 min_transparency = 0.3 color = np.array(to_rgba('#00bfff')) color = np.tile(color, (len(passes_between), 1)) c_transparency = passes_between.pass_count / passes_between.pass_count.max( ) c_transparency = (c_transparency * (1 - min_transparency)) + min_transparency color[:, 3] = c_transparency a = plt.scatter(passer_avg.y, passer_avg.x, s=100, c="#111111", facecolor='none', lw=1, cmap="winter", alpha=1, zorder=2, vmin=0, vmax=0.6, marker='h') c = plt.scatter(passer_avg.y, passer_avg.x, s=60, c='#FF0000', alpha=1, zorder=3, marker='h') pitch.arrows(passes_between.x, passes_between.y, passes_between.x_end, passes_between.y_end, color=color, ax=ax, zorder=1, width=1.5) cbar = plt.colorbar(a, orientation="horizontal", shrink=0.3, pad=0, ticks=[0, 0.2, 0.4, 0.6]) cbar.set_label('Expected Possession Value (EPV)', color='#a9a9a9', size=6) cbar.outline.set_edgecolor('#a9a9a9') cbar.ax.xaxis.set_tick_params(color='#a9a9a9') cbar.ax.xaxis.set_tick_params(labelcolor='#a9a9a9') cbar.ax.tick_params(labelsize=5) plt.gca().invert_xaxis() for index, row in passer_avg.iterrows(): pitch.annotate(row.name, xy=(row.x, row.y), c='#a9a9a9', va='center', ha='center', size=5, ax=ax) plt.text( 79, 2, "Positions = Median Location of Successful Passes\nArrows = Pass Direction\nTransparency = Frequency of Combination\nMinimum of 3 Passes ", color='#a9a9a9', fontsize=5, alpha=0.5, zorder=1) plt.text(80, 122, "Minutes 45-90", color='#a9a9a9', fontsize=5) plt.text(18, 122, "@nikhilrajesh231", color='#a9a9a9', fontsize=5) ax.set_title("Barcelona PV Pass Network\n5-2 vs Getafe (H)", fontsize=8, color="#a9a9a9", fontweight='bold', y=1.01) fig = plt.savefig('pn2.png', bbox_inches="tight", facecolor="#000000", dpi=600) return fig