# 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,
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)
# --------------- # 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,
# 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,
# 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,
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) ##############################################################################
############################################################################## # 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,
############################################################################## # 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