Example #1
0
# plotting to show that events can move from their pitch markings
# without relative scaling to the pitch markings

# first setup the figure
FIGWIDTH = 16
FIGHEIGHT = 6
fig = plt.figure(figsize=(FIGWIDTH, FIGHEIGHT))

opta_pitch = VerticalPitch(pitch_type='opta',
                           line_color='black',
                           half=True,
                           line_zorder=3)
ax_opta = fig.add_axes(
    (0.05, 0.05, 0.4, 0.4 * FIGWIDTH / FIGHEIGHT / opta_pitch.ax_aspect))
opta_pitch.draw(ax=ax_opta)

custom_pitch = VerticalPitch(pitch_type='custom',
                             line_color='black',
                             half=True,
                             pitch_length=105,
                             pitch_width=68,
                             line_zorder=3)
ax_custom = fig.add_axes(
    (0.55, 0.05, 0.4, 0.4 * FIGWIDTH / FIGHEIGHT / custom_pitch.ax_aspect))
custom_pitch.draw(ax=ax_custom)

# draw the original event points
fm = FontManager()  # a mplsoccer fontmanager with the default Robotto font
opta_pitch.scatter(x,
                   y,
Example #2
0
SHOT_ID = '8bb8bbc2-68a6-4c01-93de-53a194e7a1cf'
df_freeze_frame = df_freeze[df_freeze.id == SHOT_ID].copy()
df_shot_event = df_event[df_event.id == SHOT_ID].dropna(axis=1,
                                                        how='all').copy()

##############################################################################
# Location dataset

df = pd.concat([df_shot_event[['x', 'y']], df_freeze_frame[['x', 'y']]])

x = df.x.values
y = df.y.values
teams = np.concatenate([[True], df_freeze_frame.player_teammate.values])

##############################################################################
# Plotting

# draw plot
pitch = VerticalPitch(half=True)
fig, ax = pitch.draw(figsize=(8, 6.2))

# Plot Voronoi
team1, team2 = pitch.voronoi(x, y, teams)
t1 = pitch.polygon(team1, ax=ax, fc='#c34c45', ec='white', lw=3, alpha=0.4)
t2 = pitch.polygon(team2, ax=ax, fc='#6f63c5', ec='white', lw=3, alpha=0.4)

# Plot players
sc1 = pitch.scatter(x[teams], y[teams], ax=ax, c='#c34c45', s=150)
sc2 = pitch.scatter(x[~teams], y[~teams], ax=ax, c='#6f63c5', s=150)
Example #3
0
# ---------------
# I like the look of the voltage colormap so let's plot in against a light and
# dark background
#
# You can reverse any of the colormaps, by putting
# _r at the end, for example cmr.arctic_r (this also applies to matplotlib cmaps).
#
# Reversing the colormaps is sometimes helpful so the high value colors do not bleed into the
# background. I prefer dark to light colormaps on dark background, and light to dark
# colormaps on light backgrounds. I have shown this below using the same colormap in reverse.

# dark
pitch_dark = VerticalPitch(line_color='#cfcfcf',
                           line_zorder=2,
                           pitch_color='#122c3d')
fig, ax = pitch_dark.draw()
kdeplot_dark = pitch_dark.kdeplot(df.x,
                                  df.y,
                                  ax=ax,
                                  cmap=cmr.voltage,
                                  shade=True,
                                  levels=100)

# light
pitch_light = VerticalPitch(line_zorder=2)
fig, ax = pitch_light.draw()
kdeplot_light = pitch_light.kdeplot(df.x,
                                    df.y,
                                    ax=ax,
                                    cmap=cmr.voltage_r,
                                    shade=True,
Example #4
0
# Shot map Barcelona
# ------------------
# First let's plot Barcelona's shots with the scatter marker size varying
# by the expected goals amount. The maximum of 1 (100% expected chance of scoring)
# has been given size 1000 (points**2).
# By multiplying the expected goals amount by 900 and adding 100
# we essentially get a size that varies between 100 and 1000.
# For choosing color schemes, I really like this website
# `iWantHue <https://medialab.github.io/iwanthue/>`_.

pitch = VerticalPitch(
    pad_bottom=0.5,  # pitch extends slightly below halfway line
    half=True,  # half of a pitch
    goal_type='box',
    goal_alpha=0.8)  # control the goal transparency
fig, ax = pitch.draw(figsize=(12, 10))
sc = pitch.scatter(
    df_shots_barca.x,
    df_shots_barca.y,
    # size varies between 100 and 1000 (points squared)
    s=(df_shots_barca.shot_statsbomb_xg * 900) + 100,
    c='#b94b75',  # color for scatter in hex format
    edgecolors='#383838',  # give the markers a charcoal border
    # for other markers types see: https://matplotlib.org/api/markers_api.html
    marker='h',
    ax=ax)
txt = ax.text(
    x=40,
    y=80,
    s='Barcelona shots\nversus Sevilla',
    size=30,
Example #5
0
# fontmanager for google font (robotto)
robotto_regular = FontManager()

path_eff = [path_effects.Stroke(linewidth=3, foreground='black'),
            path_effects.Normal()]

##############################################################################
# Plot positional heatmap
# -----------------------

# setup pitch
pitch = VerticalPitch(pitch_type='statsbomb', line_zorder=2,
                      pitch_color='#22312b', line_color='white')
# draw
fig, ax = pitch.draw(figsize=(4.125, 6))
bin_statistic = pitch.bin_statistic_positional(df.x, df.y, statistic='count',
                                               positional='full', normalize=True)
pitch.heatmap_positional(bin_statistic, ax=ax, cmap='coolwarm', edgecolors='#22312b')
pitch.scatter(df.x, df.y, c='white', s=2, ax=ax)
labels = pitch.label_heatmap(bin_statistic, color='#f4edf0', fontsize=18,
                             ax=ax, ha='center', va='center',
                             str_format='{:.0%}', path_effects=path_eff)

##############################################################################
# Plot the chart again with a title
# ---------------------------------
# We will use mplsoccer's grid function to plot a pitch with a title and endnote axes.
pitch = VerticalPitch(pitch_type='statsbomb', line_zorder=2, pitch_color='#1e4259')
fig, axs = pitch.grid(endnote_height=0.03, endnote_space=0,
                      title_height=0.08, title_space=0,
Example #6
0
df_before_false9 = df_before_false9.loc[df_before_false9.player_id == 5503,
                                        ['x', 'y']]

##############################################################################
# Create a custom colormap.
# Note see the `custom colormaps
# <https://mplsoccer.readthedocs.io/en/latest/gallery/pitch_plots/plot_cmap.html>`_
# example for more ideas.
flamingo_cmap = LinearSegmentedColormap.from_list("Flamingo - 10 colors",
                                                  ['#e3aca7', '#c03a1d'],
                                                  N=10)

##############################################################################
# Plot Messi's first game as a false-9.
pitch = VerticalPitch(line_color='#000009', line_zorder=2, pitch_color='white')
fig, ax = pitch.draw(figsize=(4.4, 6.4))
hexmap = pitch.hexbin(df_false9.x,
                      df_false9.y,
                      ax=ax,
                      edgecolors='#f4f4f4',
                      gridsize=(8, 8),
                      cmap=flamingo_cmap)

##############################################################################
# Load a custom font.
URL = 'https://github.com/googlefonts/roboto/blob/main/src/hinted/Roboto-Regular.ttf?raw=true'
URL2 = 'https://github.com/google/fonts/blob/main/apache/roboto/static/Roboto-Bold.ttf?raw=true'
robotto_regular = FontManager(URL)
robboto_bold = FontManager(URL2)

##############################################################################
Example #7
0
##############################################################################
# Add the last name to the dataframes

df_statsbomb['last_name'] = df_statsbomb.player_name.str.split(' ').str[-1]
df_wyscout['last_name'] = df_wyscout.player_name.str.split(' ').str[-1]

##############################################################################
# Plot the standardized data
# --------------------------

pitch = VerticalPitch(pitch_type='statsbomb',
                      half=True,
                      pad_left=-10,
                      pad_right=-10,
                      pad_bottom=-20)
fig, ax = pitch.draw(figsize=(16, 9))
fm = FontManager()  # a mplsoccer fontmanager with the default Robotto font

# subset portugals shots for both data providers
mask_portugal_sb = df_statsbomb.team_name == 'Portugal'
mask_shot_sb = df_statsbomb.type_name == 'Shot'
mask_portugal_wyscout = df_wyscout.team_name == 'Portugal'
mask_shot_wyscout = df_wyscout.event_type == 'SHOT'
df_wyscout_portugal = df_wyscout[mask_shot_wyscout
                                 & mask_portugal_wyscout].copy()
df_statsbomb_portugal = df_statsbomb[mask_shot_sb & mask_portugal_sb].copy()

# plotting the shots as a scatter plot with a legend
pitch.scatter(df_wyscout_portugal.coordinates_x,
              df_wyscout_portugal.coordinates_y,
              ax=ax,
Example #8
0
##############################################################################
# View the pass dataframe.

df_pass

##############################################################################
# Plotting

# Setup the pitch
pitch = VerticalPitch(pitch_type='statsbomb',
                      pitch_color='#22312b',
                      line_color='#c7d5cc',
                      half=True,
                      pad_top=2)
fig, ax = pitch.draw(figsize=(16, 11), tight_layout=True)

# Plot the completed passes
pitch.lines(df_pass.x,
            df_pass.y,
            df_pass.end_x,
            df_pass.end_y,
            lw=10,
            transparent=True,
            comet=True,
            cmap='jet',
            label='pass leading to shot',
            ax=ax)

# Plot the goals
pitch.scatter(df_pass[mask_goal].end_x,
match_files = ['19789.json', '19794.json', '19805.json']
kwargs = {'related_event_df': False, 'shot_freeze_frame_df': False,
          'tactics_lineup_df': False, 'warn': False}
df = pd.concat([read_event(f'{EVENT_SLUG}/{file}', **kwargs)['event'] for file in match_files])
# filter chelsea pressure events
mask_chelsea_pressure = (df.team_name == 'Chelsea FCW') & (df.type_name == 'Pressure')
df = df.loc[mask_chelsea_pressure, ['x', 'y']]

##############################################################################
# Plot the heatmaps

# setup pitch
pitch = VerticalPitch(pitch_type='statsbomb', line_zorder=2,
                      pitch_color='#22312b', line_color='white')
# draw
fig, ax = pitch.draw(figsize=(16, 9), ncols=3, nrows=1)
positions = ['full', 'horizontal', 'vertical']
for i, pos in enumerate(positions):
    bin_statistic = pitch.bin_statistic_positional(df.x, df.y, statistic='count', positional=pos)
    pitch.heatmap_positional(bin_statistic, ax=ax[i], cmap='coolwarm', edgecolors='#22312b')
    pitch.scatter(df.x, df.y, c='white', s=2, ax=ax[i])
    total = np.array([bs['statistic'].sum() for bs in bin_statistic]).sum()
    # replace raw counts with percentages and add percentage
    # sign (note immutable named tuple so used _replace)
    for bs in bin_statistic:
        bs['statistic'] = (pd.DataFrame(bs['statistic'] / total)
                           .applymap(lambda x: '{:.0%}'.format(x))
                           .values)
    pitch.label_heatmap(bin_statistic, color='white', fontsize=18,
                        ax=ax[i], ha='center', va='bottom')
TITLE_STR = 'Location of pressure events - 3 home games for Chelsea FC Women'
left2 = (TOP_SPACE * 2) + TOP_WIDTH
bottom2 = (1 - height2) / 2 - top_offset
ax2 = fig.add_axes((left2, bottom2, TOP_WIDTH, height2))
pitch2.draw(ax=ax2)

# top right
left3 = (TOP_SPACE * 3) + (TOP_WIDTH * 2)
bottom3 = (1 - height3) / 2 - top_offset
ax3 = fig.add_axes((left3, bottom3, TOP_WIDTH, height3))
pitch3.draw(ax=ax3)

# bottom left
LEFT4 = BOTTOM_SPACE
bottom4 = (1 - height4) / 2 - bottom_offset
ax4 = fig.add_axes((LEFT4, bottom4, BOTTOM_WIDTH, height4))
pitch4.draw(ax=ax4)

# bottom middle
left5 = (BOTTOM_SPACE * 2) + BOTTOM_WIDTH
bottom5 = (1 - height5) / 2 - bottom_offset
ax5 = fig.add_axes((left5, bottom5, BOTTOM_WIDTH, height5))
pitch5.draw(ax=ax5)

# bottom right
left6 = (BOTTOM_SPACE * 3) + (BOTTOM_WIDTH * 2)
bottom6 = (1 - height6) / 2 - bottom_offset
ax6 = fig.add_axes((left6, bottom6, BOTTOM_WIDTH, height6))
pitch6.draw(ax=ax6)

# draw titles