Exemple #1
0
##############################################################################
# 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',
    pitch_color='#22312b',
    line_color='#c7d5cc',
)
fig, ax = pitch.draw(figsize=(16, 11),
                     constrained_layout=True,
                     tight_layout=False)
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,
Exemple #2
0
import cmasher as cmr
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from highlight_text import ax_text

from mplsoccer import Pitch, VerticalPitch, add_image, FontManager
from mplsoccer.statsbomb import read_event, read_lineup, EVENT_SLUG, LINEUP_SLUG

##############################################################################
# Single Pitch
# ------------
# You can also use grid to plot a single pitch with an endnote and title axis.
# The defaults setup a pitch with a 2.5%
# (of the figure height/ width) border around the sides.
pitch = Pitch()
fig, axs = pitch.grid()

##############################################################################
# Multiple Pitches side by side
# -----------------------------
# Next up let's plot 3 pitches side-by-side.
pitch = VerticalPitch()
fig, axs = pitch.grid(figheight=15, ncols=3)

##############################################################################
# Grid of Pitches
# ---------------
# Here's how to plot a grid of pitches
pitch = Pitch(linewidth=4)
fig, axs = pitch.grid(nrows=3, ncols=4,  # number of rows/ columns
============
Pitch Basics
============

First we import the Pitch classes and matplotlib
"""
import matplotlib.pyplot as plt

from mplsoccer import Pitch, VerticalPitch

##############################################################################
# Draw a pitch on a new axis
# --------------------------
# Let's plot on a new axis first.

pitch = Pitch()
# specifying figure size (width, height)
fig, ax = pitch.draw(figsize=(8, 4))

##############################################################################
# Draw on an existing axis
# ------------------------
# mplsoccer also plays nicely with other matplotlib figures. To draw a pitch on an
# existing matplotlib axis specify an ``ax`` in the ``draw`` method.

fig, axs = plt.subplots(nrows=1, ncols=2)
pitch = Pitch()
pie = axs[0].pie(x=[5, 15])
pitch.draw(ax=axs[1])

##############################################################################
# Show the away data
df_away.head()

##############################################################################
# Show the home data
df_home.head()

##############################################################################
# Show the ball data
df_ball.head()

##############################################################################
# Plot the animation

# First set up the figure, the axis
pitch = Pitch(pitch_type='metricasports', goal_type='line', pitch_width=68, pitch_length=105)
fig, ax = pitch.draw(figsize=(16, 10.4))

# then setup the pitch plot markers we want to animate
marker_kwargs = {'marker': 'o', 'markeredgecolor': 'black', 'linestyle': 'None'}
ball, = ax.plot([], [], ms=6, markerfacecolor='w', zorder=3, **marker_kwargs)
away, = ax.plot([], [], ms=10, markerfacecolor='#b94b75', **marker_kwargs)  # red/maroon
home, = ax.plot([], [], ms=10, markerfacecolor='#7f63b8', **marker_kwargs)  # purple


# animation function
def animate(i):
    """ Function to animate the data. Each frame it sets the data for the players and the ball."""
    # set the ball data with the x and y positions for the ith frame
    ball.set_data(df_ball.iloc[i, 3], df_ball.iloc[i, 4])
    # get the frame id for the ith frame
Exemple #5
0
    'connectionstyle': 'angle3,angleA=0,angleB=-90',
    'color': FONTCOLOR
}
font_kwargs = {
    'fontsize': 14,
    'ha': 'center',
    'va': 'bottom',
    'fontweight': 'bold',
    'fontstyle': 'italic',
    'c': FONTCOLOR
}

for idx, pt in enumerate(pitch_types):
    if pt in ['tracab', 'metricasports', 'custom', 'skillcorner']:
        pitch = Pitch(pitch_type=pt,
                      pitch_length=105,
                      pitch_width=68,
                      **pitch_kwargs)
    else:
        pitch = Pitch(pitch_type=pt, **pitch_kwargs)
    pitch.draw(axes[idx])
    xmin, xmax, ymin, ymax = pitch.extent
    if pitch.dim.aspect != 1:
        TEXT = 'data coordinates \n are square (1:1) \n scale up to a real-pitch size'
        axes[idx].annotate(TEXT,
                           xy=(xmin, ymin),
                           xytext=(0 + (xmax - xmin) / 2, ymin),
                           **font_kwargs)
    axes[idx].xaxis.set_ticks([xmin, xmax])
    axes[idx].yaxis.set_ticks([ymin, ymax])
    axes[idx].tick_params(labelsize=15)
    if pt == 'skillcorner':
Exemple #6
0
                                             coord['team'], player_size,
                                             player_opacity)
                    st.image(draw.compose_image(sensitivity))
                with o_col3:
                    draw = PitchDraw(image, original=False)
                    for pid, coord in dfCoords.iterrows():
                        draw.draw_circle(coord[['x', 'y']].values,
                                         coord['team'], 2, player_opacity)
                        draw.draw_text(coord[['x', 'y']] + 0.5, f"{pid}",
                                       coord['team'])
                    st.image(draw.compose_image(sensitivity))

                st.markdown(get_table_download_link(
                    dfCoords[['team', 'x', 'y']]),
                            unsafe_allow_html=True)

                #mplsoccer based output viz
                _, mpl_output, _ = st.beta_columns(
                    (1, 2,
                     1))  #temporary, as fig size doesn't seem to be working
                from mplsoccer import Pitch
                pitch = Pitch(pitch_type='statsbomb', pitch_color='#22312b')
                fig, ax = pitch.draw()
                pitch.scatter(dfCoords.x,
                              dfCoords.y,
                              c=dfCoords.team,
                              ax=ax,
                              s=120,
                              edgecolors='white')
                with mpl_output:
                    st.pyplot(fig)
Exemple #7
0
##############################################################################
# Rotated markers
# ---------------
# I also included a method for rotating markers in mplsoccer.
#
# 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 ↑
#
# We are going to plot pass data as an arrowhead marker with the
# arrow facing in the direction of the pass
# The marker size is going to relate to the pass distance,
# so larger markers mean the pass was longer.
pitch = Pitch()
fig, ax = pitch.draw(figsize=(14, 12))
angle, distance = pitch.calculate_angle_and_distance(df_pass_barca.x,
                                                     df_pass_barca.y,
                                                     df_pass_barca.end_x,
                                                     df_pass_barca.end_y,
                                                     standardized=False,
                                                     degrees=True)
sc = pitch.scatter(
    df_pass_barca.x,
    df_pass_barca.y,
    rotation_degrees=angle,
    c='#b94b75',  # color for scatter in hex format
    edgecolors='#383838',
    alpha=0.9,
    s=(distance / distance.max()) * 900,
Exemple #8
0
##############################################################################
# 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()

##############################################################################
# Setup the pitch and number of bins
pitch = Pitch(pitch_type='statsbomb',
              line_zorder=2,
              line_color='#c7d5cc',
              pitch_color='#22312b')
bins = (6, 4)

##############################################################################
# Plotting using a single color and length
fig, ax = pitch.draw(figsize=(16, 11),
                     constrained_layout=True,
                     tight_layout=False)
fig.set_facecolor('#22312b')
# plot the heatmap - darker colors = more passes originating from that square
bs_heatmap = pitch.bin_statistic(df_pass.x,
                                 df_pass.y,
                                 statistic='count',
                                 bins=bins)
hm = pitch.heatmap(bs_heatmap, ax=ax, cmap='Blues')
Exemple #9
0
# Get the StatsBomb logo and Fonts

LOGO_URL = 'https://raw.githubusercontent.com/statsbomb/open-data/master/img/statsbomb-logo.jpg'
sb_logo = Image.open(urlopen(LOGO_URL))

# a FontManager object for using a google font (default Robotto)
fm = FontManager()
# path effects
path_eff = [path_effects.Stroke(linewidth=3, foreground='black'),
            path_effects.Normal()]

##############################################################################
# Plot the percentages

# setup a mplsoccer pitch
pitch = Pitch(line_zorder=2, line_color='black', pad_top=20)

# mplsoccer calculates the binned statistics usually from raw locations, such as pressure events
# for this example we will create a binned statistic dividing
# the pitch into thirds for one point (0, 0)
# we will fill this in a loop later with each team's statistics from the dataframe
bin_statistic = pitch.bin_statistic([0], [0], statistic='count', bins=(3, 1))

GRID_HEIGHT = 0.8
CBAR_WIDTH = 0.03
fig, axs = pitch.grid(nrows=4, ncols=5, figheight=20,
                      # leaves some space on the right hand side for the colorbar
                      grid_width=0.88, left=0.025,
                      endnote_height=0.06, endnote_space=0,
                      # Turn off the endnote/title axis. I usually do this after
                      # I am happy with the chart layout and text placement
Exemple #10
0
df_total = pd.DataFrame(df[pressure_cols].sum())
df_total.columns = ['total']
df_total = df_total.T
df_total = df_total.divide(df_total.sum(axis=1), axis=0) * 100

##############################################################################
# Calculate the percentages for each team and sort so that the teams which press higher are last
df[pressure_cols] = df[pressure_cols].divide(df[pressure_cols].sum(axis=1),
                                             axis=0) * 100.
df.sort_values(['Att 3rd', 'Def 3rd'], ascending=[True, False], inplace=True)

##############################################################################
# Plot the percentages

# setup a mplsoccer pitch
pitch = Pitch(line_zorder=2, line_color='black')

# mplsoccer calculates the binned statistics usually from raw locations, such as pressure events
# for this example we will create a binned statistic dividing
# the pitch into thirds for one point (0, 0)
# we will fill this in a loop later with each team's statistics from the dataframe
bin_statistic = pitch.bin_statistic([0], [0], statistic='count', bins=(3, 1))

# Plot
fig, axes = pitch.draw(figsize=(16, 9),
                       ncols=5,
                       nrows=4,
                       tight_layout=False,
                       constrained_layout=True)
axes = axes.ravel()
teams = df['Squad'].values
Exemple #11
0
kwargs = {
    'related_event_df': False,
    'shot_freeze_frame_df': False,
    'tactics_lineup_df': False,
    'warn': 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 messi's actions (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']]

##############################################################################
# plotting
pitch = Pitch(pitch_type='statsbomb',
              pitch_color='#22312b',
              stripe=False,
              line_zorder=2)
fig, ax = pitch.draw(
    figsize=(16, 9),
    nrows=1,
    ncols=2,
)
pitch.hexbin(df_before_false9.x, df_before_false9.y, ax=ax[0], cmap='Blues')
pitch.hexbin(df_false9.x, df_false9.y, ax=ax[1], cmap='Blues')
TITLE_STR1 = 'Messi in the game directly before \n playing in the false 9 role'
TITLE_STR2 = 'The first Game Messi \nplayed in the false 9 role'
title1 = ax[0].set_title(TITLE_STR1, fontsize=25, pad=20)
title2 = ax[1].set_title(TITLE_STR2, fontsize=25, pad=20)
    '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 = Pitch(pitch_type='statsbomb', line_zorder=2, line_color='white')
# draw
fig, ax = pitch.draw(figsize=(16, 9))
bin_statistic = pitch.bin_statistic(df.x,
                                    df.y,
                                    statistic='count',
                                    bins=(25, 25))
bin_statistic['statistic'] = gaussian_filter(bin_statistic['statistic'], 1)
pcm = pitch.heatmap(bin_statistic, ax=ax, cmap='hot', edgecolors='#22312b')
cbar = fig.colorbar(pcm, ax=ax)
TITLE_STR = 'Location of pressure events - 3 home games for Chelsea FC Women'
title = fig.suptitle(TITLE_STR, x=0.4, y=0.98, fontsize=23)
fig = plt.figure(figsize=FIGSIZE)

fm_rubik = FontManager(('https://github.com/google/fonts/blob/main/ofl/rubikmonoone/'
                        'RubikMonoOne-Regular.ttf?raw=true'))

# layout specifications
PAD = 1
pitch_spec = {'pad_left': PAD, 'pad_right': PAD,
              'pad_bottom': PAD, 'pad_top': PAD, 'pitch_color': 'None'}
pitch_width, pitch_length = 80, 105
pitch_width3, pitch_length3 = 60, 105
pitch_length4, pitch_width4 = 120, 68
pitch_length6, pitch_width6 = 85, 68

# define pitches (top left, top middle, top right, bottom left, bottom middle, bottom right)
pitch1 = Pitch(pitch_type='custom', pitch_width=pitch_width, pitch_length=pitch_length,
               line_color='#b94e45', **pitch_spec)
pitch2 = Pitch(pitch_type='statsbomb', **pitch_spec)
pitch3 = Pitch(pitch_type='custom', pitch_width=pitch_width3, pitch_length=pitch_length3,
               line_color='#56ae6c', **pitch_spec)
pitch4 = VerticalPitch(pitch_type='custom', pitch_length=pitch_length4, pitch_width=pitch_width4,
                       line_color='#bc7d39', **pitch_spec)
pitch5 = VerticalPitch(pitch_type='statsbomb', **pitch_spec)
pitch6 = VerticalPitch(pitch_type='custom', pitch_length=pitch_length6, pitch_width=pitch_width6,
                       line_color='#677ad1', **pitch_spec)

TITLE_HEIGHT = 0.1  # title axes are 10% of the figure height

#  width of pitch axes as percent of the figure width
TOP_WIDTH = 0.27
BOTTOM_WIDTH = 0.18
Exemple #14
0
# Load an image of Messi
# ######################

# load the image
IMAGE_URL = 'https://upload.wikimedia.org/wikipedia/commons/b/b8/Messi_vs_Nigeria_2018.jpg'
image = Image.open(urlopen(IMAGE_URL))

##############################################################################
# Plotting an image over a pitch
# ##############################
#
# To plot images you use ``Axes.imshow()`` in matplotlib.
# We are going to draw a pitch and then overlay ontop an image of Messi on a new axis.

# draw the pitch
pitch = Pitch(line_zorder=2)
fig, ax = pitch.draw(figsize=(16, 9), tight_layout=False)

# add an image
ax_image = add_image(image, fig, left=0.55, bottom=0.2, width=0.2,
                     alpha=0.9, interpolation='hanning')

##############################################################################
# Photo from: https://en.wikipedia.org/wiki/Lionel_Messi#/media/File:Messi_vs_Nigeria_2018.jpg;
# License: https://creativecommons.org/licenses/by-sa/3.0/;
# Creator: Кирилл Венедиктов

##############################################################################
# More control over the images and axis
# #####################################
#
Exemple #15
0
"""
===========
Quick start
===========
"""
from mplsoccer import Pitch
pitch = Pitch(pitch_color='grass', line_color='white', stripe=True)
fig, ax = pitch.draw()
Exemple #16
0
# Now creating a DataFrame and Storing the DataFrame in a dictionary with match id as Key
df = json_normalize(data, sep='_').assign(match_id=filename[-9:-5])

# In[13]:

# A dataframe of only Shots
shots = df[df['type_name'] == 'Shot'].set_index('id')
shots.head()

# In[15]:

# fig,ax = createPitch(pitchLengthX,pitchWidthY,'yards','gray')
pitch = Pitch(
    pitch_type='custom',  # example plotting a tracab pitch
    pitch_length=pitchLengthX,
    pitch_width=pitchWidthY,
    axis=True,
    label=True)  # showing axis labels is optional
fig, ax = pitch.draw(figsize=(16, 11),
                     constrained_layout=False,
                     tight_layout=True)

# Now lets plot the shots
for i, shot in shots.iterrows():
    x = shot['location'][0]
    y = shot['location'][1]
    goal = shot['shot_outcome_name'] == 'Goal'
    team_name = shot['team_name']

    circleSize = 2
    #circleSize=np.sqrt(shot['shot_statsbomb_xg'])*12
Exemple #17
0
# the pass comet lines when using capstyle='round'
ALPHA_PITCH_LINE = 0.3
ALPHA_PASS_LINE = 0.15

# The colors are borrowed from mplcyberpunk. Try some of the following alternatives
# '#08F7FE' (teal/cyan), '#FE53BB' (pink), '#F5D300' (yellow),
# '#00ff41' (matrix green), 'r' (red), '#9467bd' (viloet)
BACKGROUND_COLOR = '#212946'
PASS_COLOR = '#FE53BB'
LINE_COLOR = '#08F7FE'

# plot as initial pitch and the lines with alpha=1
# I have used grid to get a title and endnote axis automatically, but you could you pitch.draw()
pitch = Pitch(line_color=LINE_COLOR,
              pitch_color=BACKGROUND_COLOR,
              linewidth=LINEWIDTH,
              line_alpha=1,
              goal_alpha=1,
              goal_type='box')
fig, ax = pitch.grid(grid_height=0.9,
                     title_height=0.06,
                     axis=False,
                     endnote_height=0.04,
                     title_space=0,
                     endnote_space=0)
fig.set_facecolor(BACKGROUND_COLOR)
pitch.lines(
    df_pass.x,
    df_pass.y,
    df_pass.end_x,
    df_pass.end_y,
    capstyle='butt',  # cut-off the line at the end-location.
Exemple #18
0
##############################################################################
# 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()

##############################################################################
# Plotting Messi's first game as a False-9

pitch = Pitch(pitch_type='statsbomb', pitch_color='grass', stripe=True)
fig, ax = pitch.draw(figsize=(16, 11))

# 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',
Exemple #19
0
# I created a function to calculate the maximum dimensions you can get away with while
# having a set figure size. Let's use this to create the largest pitch possible
# with a 16:9 figure aspect ratio.

FIGWIDTH = 16
FIGHEIGHT = 9
NROWS = 1
NCOLS = 1
# here we want the maximum side in proportion to the figure dimensions
# (height in this case) to take up all of the image
MAX_GRID = 1

# pitch with minimal padding (2 each side)
pitch = Pitch(pad_top=2,
              pad_bottom=2,
              pad_left=2,
              pad_right=2,
              pitch_color='#22312b')

# calculate the maximum grid_height/ width
GRID_WIDTH, GRID_HEIGHT = pitch.calculate_grid_dimensions(figwidth=FIGWIDTH,
                                                          figheight=FIGHEIGHT,
                                                          nrows=NROWS,
                                                          ncols=NCOLS,
                                                          max_grid=MAX_GRID,
                                                          space=0)

# plot using the mplsoccer grid function
fig, ax = pitch.grid(figheight=FIGHEIGHT,
                     grid_width=GRID_WIDTH,
                     grid_height=GRID_HEIGHT,