In [7]:
dfd = DF_Dashboard(name='')

dashboard_title = '# Player Actions - 2003/04 PL'
dashboard_desc = 'Select a player to see their shot map, key passes, and pressure events for the 2003/04 Premier League season. Shot Map: All shots taken, with the size of the circle relative to the StatsBomb xG value. Key Pass Map: All passes made that resulted in a shot. Pressure Events: Shows all of the start locations that the player pressed an opponent from. Use the "Pressing Breakdown" dropdown to select the aggregation method. NOTE: 32 of 38 matches available'

dashboard = pn.Column(pn.Row(pn.Column(dashboard_title,align='center',sizing_mode='stretch_width'),pn.Spacer(width=100),pn.Spacer(width=130),pn.pane.PNG("stats-bomb-logo.png",align='center',width=200,height_policy='fit'),height_policy='min'),
                      pn.Row(pn.Column(dfd.param,align='start'),pn.Column(dashboard_desc,align='end',sizing_mode='stretch_both')),
                      pn.Row(pn.Column(pn.Row(dfd.shot_map,background='#22312b'),pn.Row(dfd.pass_map,background='#22312b')),pn.Column(dfd.pressure_map),background='#22312b'))

dashboard.embed()
                                                                                                                       
In [1]:
import pandas as pd
from mplsoccer.pitch import Pitch
import matplotlib.pyplot as plt
import panel as pn
pn.extension()
import param
import ast
from matplotlib import rcParams
rcParams['text.color'] = '#c7d5cc'
rcParams["figure.facecolor"] = "#22312b"

pd.options.display.max_columns = None
%matplotlib inline
In [2]:
matches = pd.read_excel("20200607 - Matches.xlsx")
lineups = pd.read_excel("20200607 - Lineups.xlsx")
In [3]:
team = "Arsenal"
team_player_ids = lineups[lineups["team_name"] == team]["player_id"].unique()

player_names = []

for i in team_player_ids:
    nn = lineups[lineups["player_id"] == i]["player_nickname"].values[0]
    n = lineups[lineups["player_id"] == i]["player_name"].values[0]
    if lineups[lineups["player_id"] == i]["player_nickname"].isna().values[0]:
        if len(lineups[lineups["player_name"] == n]) >= 5:
            player_names.append(n)
    else:
        if len(lineups[lineups["player_name"] == n]) >= 5:
            player_names.append(nn)
In [4]:
shots = pd.read_excel("20200607 - Shots - Arsenal 2003-04 (Parsed).xlsx")

passes = pd.read_excel("20200607 - Passes - Arsenal 2003-04 (Parsed).xlsx")
passes["assisted_shot_id"] = passes["assisted_shot_id"].astype(str)
key_passes = passes[passes["assisted_shot_id"] != 'nan']

events = pd.read_excel("20200607 - Events.xlsx")
events["type"] = [ast.literal_eval(events["type"][i])["name"] for i in events.index]
events["team"] = [ast.literal_eval(events["team"][i])["name"] for i in events.index]
events["player"].fillna("{'name':'N/A'}",inplace=True)
events["player"] = [ast.literal_eval(events["player"][i])["name"] for i in events.index]
In [5]:
pressure_types = {"Pressing Grid":(6,5), "Pressing Height":(8,1), "Pressing Width":(1,6)}
pressure_keys = [i for i in pressure_types]
In [6]:
class DF_Dashboard(param.Parameterized):
    
    Pressing_Breakdown = param.ObjectSelector(default=pressure_keys[0], objects = pressure_keys)
    Player = param.ObjectSelector(default='Thierry Henry', objects = player_names)
    
    def get_shot_data(self):
        player_name = self.Player
        if len(lineups[lineups["player_nickname"] == player_name]) != 0:
            nickname = player_name
            player = lineups[lineups["player_nickname"] == player_name]["player_name"].values[0]
        else:
            nickname = lineups[lineups["player_name"] == player_name]["player_nickname"].values[0]
            player = player_name

        mask1 = shots["player"] == player
        mask2 = shots["outcome"] == "Goal"
        mask3 = shots["type"] != "Penalty"
        mask4 = shots["outcome"] != "Goal"

        player_non_pen_shots = shots[mask1 & mask3]
        player_non_pen_goals = shots[mask1 & mask2 & mask3]
        player_non_pen_missed = shots[mask1 & mask3 & mask4]

        return player_non_pen_shots,player_non_pen_goals,player_non_pen_missed,player_name
    
    def get_pass_data(self):
        player_name = self.Player
        if len(lineups[lineups["player_nickname"] == player_name]) != 0:
            nickname = player_name
            player = lineups[lineups["player_nickname"] == player_name]["player_name"].values[0]
        else:
            nickname = lineups[lineups["player_name"] == player_name]["player_nickname"].values[0]
            player = player_name

        mask2 = key_passes["player"] == player
        mask4 = key_passes["shot_assist"] == True
        mask5 = key_passes["goal_assist"] == True

        player_season_shot = key_passes[mask2 & mask4]
        player_season_goal = key_passes[mask2 & mask5]
        
        return player_season_shot,player_season_goal,player_name
    
    def get_pressure_data(self):
        
        player_name = self.Player
        if len(lineups[lineups["player_nickname"] == player_name]) != 0:
            nickname = player_name
            player = lineups[lineups["player_nickname"] == player_name]["player_name"].values[0]
        else:
            nickname = lineups[lineups["player_name"] == player_name]["player_nickname"].values[0]
            player = player_name
        
        mask1 = events["player"] == player
        mask2 = events["type"] == 'Pressure'
        
        p_pressure_events = events[mask1 & mask2]
        p_start_location_x = pd.Series([float((p_pressure_events["location"][i]).strip("[]").split(",")[0]) for i in p_pressure_events["location"].index])
        p_start_location_y = pd.Series([float((p_pressure_events["location"][i]).strip("[]").split(",")[1]) for i in p_pressure_events["location"].index])
        
        return p_start_location_x, p_start_location_y, player_name
    
    def get_pressure_bins(self):
        press_type = self.Pressing_Breakdown
        pitch_split = pressure_types[press_type]
        
        return pitch_split
    
    def shot_map(inputs):
        player_non_pen_shots,player_non_pen_goals,player_non_pen_missed,p_name = inputs.get_shot_data()
        
        plt.style.use('ggplot')
        pitch = Pitch(pitch_type='statsbomb', orientation='vertical',view='half',
                      pitch_color='#22312b', line_color='#c7d5cc',
                      constrained_layout=False, tight_layout=True)
        fig, ax = pitch.draw()
        
        ax.set_title('Shot Map: {} goals, {} xG'.format(len(player_non_pen_goals),player_non_pen_shots["statsbomb_xg"].sum().round(2)), fontsize=18, pad=12,color="#22312b")
        sc1 = pitch.scatter(player_non_pen_missed["start_location_x"], player_non_pen_missed["start_location_y"], c='#5499C7', alpha = 0.75,
                           s=[xg*200 for xg in player_non_pen_missed["statsbomb_xg"]], ax=ax,label='Other')
        sc2 = pitch.scatter(player_non_pen_goals["start_location_x"], player_non_pen_goals["start_location_y"], c='yellow', edgecolors='black', alpha = 0.75,
                           s=[xg*200 for xg in player_non_pen_goals["statsbomb_xg"]], ax=ax,label='Goal')
        ax.legend(facecolor='#22312b', edgecolor='None', fontsize=12, loc='lower left')
        plt.close()
        
        return ax.figure
    
    def pass_map(inputs):
        player_season_shot,player_season_goal,p_name = inputs.get_pass_data()
        
        plt.style.use('ggplot')
        pitch = Pitch(pitch_type='statsbomb', orientation='horizontal',
                      pitch_color='#22312b', line_color='#c7d5cc',
                      constrained_layout=False, tight_layout=True)
        fig, ax = pitch.draw()
        
        ax.set_title('Key Pass Map', fontsize=18, pad=12,color="#22312b")
        pitch.arrows(player_season_shot["start_location_x"], player_season_shot["start_location_y"],
                     player_season_shot["end_location_x"], player_season_shot["end_location_y"], width=2,
                     headwidth=6, headlength=6, color='#5499C7', ax=ax, label='[{}] assisted shot'.format(len(player_season_shot)))
        pitch.arrows(player_season_goal["start_location_x"], player_season_goal["start_location_y"],
                     player_season_goal["end_location_x"], player_season_goal["end_location_y"], width=2,
                     headwidth=6, headlength=6, color='yellow', ax=ax, label='[{}] assisted goal'.format(len(player_season_goal)))
        ax.legend(facecolor='#22312b', handlelength=2, edgecolor='None', fontsize=12, loc='lower left')
        plt.close()
        
        return ax.figure
    
    def pressure_map(inputs):
        p_location_x, p_location_y,p_name = inputs.get_pressure_data()
        grid = inputs.get_pressure_bins()
        
        pitch = Pitch(pitch_type='statsbomb',
                      pitch_color='#22312b', line_color='#c7d5cc', orientation='vertical', line_zorder=2,figsize=(6,8.15))
        fig, ax = pitch.draw()
        
        bin_statistic = pitch.bin_statistic(p_location_x, p_location_y, statistic='count', bins=grid)
        
        pitch.heatmap(bin_statistic, ax=ax, cmap='coolwarm', edgecolors='#22312b')
        pitch.scatter(p_location_x, p_location_y, c='white', s=2, ax=ax)
        bin_statistic['statistic'] = (pd.DataFrame((bin_statistic['statistic'] / bin_statistic['statistic'].sum())).applymap(lambda x: '{:.0%}'.format(x)).values)
        pitch.label_heatmap(bin_statistic, color='white', fontsize=16, weight = 'bold', ax=ax, ha='center', va='bottom')
        ax.set_title('Pressure Start Locations', fontsize=18, pad=12,color="#22312b")
        plt.close()
        
        return ax.figure