コード例 #1
0
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")
コード例 #2
0
                   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)
コード例 #3
0
ファイル: plot_kde.py プロジェクト: oruburos/mplsoccer
              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,
コード例 #4
0
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')
コード例 #5
0
"""
====================
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)
コード例 #6
0
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')
コード例 #8
0
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)
コード例 #9
0
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)
コード例 #10
0
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