# import required libraries
import os
import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib_inline
from matplotlib.font_manager import FontProperties
from matplotlib import image
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
Allsvenskan 2021
Object-oriented plotting with matplotlib
Package Prerequisites
Data Preparation
# export required data frame
= pd.read_csv("data/refined/merged_match_results.csv")
df
# find for each team xG For and xG against
= pd.concat([df['home_np_xg'], df['away_np_xg']])
all_xg_for = pd.concat([df['away_np_xg'], df['home_np_xg']]) all_xg_against
Plot Extras
# define a dictionary of colors for each team
= {'Malmö FF':['skyblue', 'white'],
team_color 'AIK':['darkblue', 'yellow'],
'Djurgården':['skyblue', 'darkblue'],
'IF Elfsborg':['yellow', 'black'],
'Hammarby':['white', 'darkgreen'],
'Kalmar FF':['red', '#EBCD57'],
'IFK Norrköping FK':['white', 'blue'],
'IFK Göteborg':['blue', '#EBCD57'],
'Mjällby AIF':['#FCDF51', 'black'],
'Varbergs BoIS FC':['#53B663', 'black'],
'IK Sirius FK':['blue', 'black'],
'BK Häcken':['black', '#FFF275'],
'Degerfors IF':['white', 'red'],
'Halmstad':['#6B91EB', 'black'],
'Örebro':['white', 'black'],
'Östersund':['red', 'black']}
# create a list of teams, according to their ranking, to iterate over
= ['Malmö FF', 'AIK', 'Djurgården', 'IF Elfsborg', 'Hammarby',
team_ranks 'Kalmar FF', 'IFK Norrköping FK', 'IFK Göteborg', 'Mjällby AIF',
'Varbergs BoIS FC', 'IK Sirius FK', 'BK Häcken', 'Degerfors IF',
'Halmstad', 'Örebro', 'Östersund']
Design
# list of club logo paths to iterate over
= os.listdir('images/club_logos/')
files
= plt.subplots(nrows = 4, ncols = 4, figsize = (18, 18),
fig, ax ={'hspace': 0.44})
gridspec_kw
# figure face color equal to axis face color
"#FFF7EE")
fig.set_facecolor(
# Font Properties:
# (x, y) axis labels
= FontProperties(family = 'Maven Pro', size = 28, weight = 'medium')
label_fps # each plot title
= FontProperties(family = 'Maven Pro', size = 20, weight = 'medium')
ax_fps # figure title
= FontProperties(family = 'Maven Pro', size = 45, weight = 'medium')
fig_fps # data source:
= FontProperties(family = 'Maven Pro', size = 16)
ds_fps
= 0
ind for i in range(4):
for j in range(4):
# grid settings
='#62625F', ls = '-.', lw = 0.25, zorder = 0)
ax[i, j].grid(color
# face color of axis
"#FFF7EE")
ax[i, j].set_facecolor(
# plot dashed line
= np.array([0, 7])
x = np.array([0, 7])
y = 'darkgray', ls = '--', lw = 1, zorder = 3)
ax[i, j].plot(x, y, c
# data filtering steps:
# 1.take a team from a list and filter data frame 'team_df'
= df[(df['home'] == team_ranks[ind]) | (df['away'] == team_ranks[ind])]
team_df
# 2. filter non-penalty xG for
'home'] == team_ranks[ind]]['home_np_xg']
team_df[team_df['away'] == team_ranks[ind]]['away_np_xg']
team_df[team_df[= pd.concat([team_df[team_df['home'] == team_ranks[ind]]['home_np_xg'],
xg_for 'away'] == team_ranks[ind]]['away_np_xg']])
team_df[team_df[
# 3. filter non-penalty xG against
'home'] == team_ranks[ind]]['away_np_xg']
team_df[team_df['away'] == team_ranks[ind]]['home_np_xg']
team_df[team_df[= pd.concat([team_df[team_df['home'] == team_ranks[ind]]['away_np_xg'],
xg_against 'away'] == team_ranks[ind]]['home_np_xg']])
team_df[team_df[
= 'gray', alpha = 0.3, s = 50, edgecolor = 'none', zorder = 3)
ax[i, j].scatter(all_xg_against, all_xg_for, c = 100, c = team_color[team_ranks[ind]][0],
ax[i, j].scatter(xg_against, xg_for, s = team_color[team_ranks[ind]][1], zorder = 3)
edgecolor
# set limit for x and y axis
0, 7)
ax[i, j].set_xlim(0, 7)
ax[i, j].set_ylim(0, 8, 1))
ax[i, j].set_xticks(np.arange(0, 8, 1))
ax[i, j].set_xticks(np.arange('equal')
ax[i, j].set_aspect(
# remove tick lines from both axis
= 0)
ax[i, j].tick_params(length
# remove '0' on y-axis
0].label1.set_visible(False)
ax[i, j].yaxis.get_major_ticks()[
# remove top and right spines
'top', 'right']].set_visible(False)
ax[i, j].spines[[
# spines color
= '#62625F'
spines_color 'bottom'].set_color(spines_color)
ax[i, j].spines['top'].set_color(spines_color)
ax[i, j].spines['right'].set_color(spines_color)
ax[i, j].spines['left'].set_color(spines_color)
ax[i, j].spines[
# font dictionary
= {'family': 'monospace', 'weight': 'bold', 'size': 14}
font3
# title for each plot
= 'left', x = 0.225, y = 1.08, fontproperties = ax_fps)
ax[i, j].set_title(team_ranks[ind], loc
# remove topmost and rightmost gridlines:
# retrieve all x and y gridlines
= ax[i, j].get_ygridlines()
y_gridlines = ax[i, j].get_xgridlines()
x_gridlines # select topmost and rightmost and remove them
= y_gridlines[-1]
y_last False)
y_last.set_visible(= x_gridlines[-1]
x_last False)
x_last.set_visible(
# club logo settings:
= image.imread("images/club_logos/" + files[ind])
club_logo # add into image box
= OffsetImage(club_logo, zoom = 0.04)
club_logo # assign axis to image box
= ax[i, j]
club_logo.image.axes # set coordinates
= AnnotationBbox(club_logo, xy = (0.065, 1.16), xycoords = 'axes fraction', frameon = False)
ab # add to axis
ax[i, j].add_artist(ab)
# update index for a list of teams
= ind + 1
ind
# league logo settings:
= image.imread("images/other_logos/allsvenskan_logo.png")
league_img = fig.add_axes([0.88, 0.88, 0.1, 0.1])
league_logo
league_logo.set_axis_off()= "equal")
league_logo.imshow(league_img, aspect
# figure title and labels
'xG Against', fontproperties = label_fps, y = 0.04)
fig.supxlabel('xG For', fontproperties = label_fps, x = 0.1)
fig.supylabel('ALLSVENSKAN | 2021 SEASON', x = 0.485, y = 0.94, fontproperties = fig_fps)
fig.suptitle(
# annotations and logo for data source
0.01, 0.015, r"data source:", fontproperties = ds_fps)
fig.text(= image.imread("images/other_logos/plm.png")
ds_img = fig.add_axes([0.08, 0.001, 0.05, 0.05])
ds_logo
ds_logo.set_axis_off()= "equal")
ds_logo.imshow(ds_img, aspect
# author annotations:
0.85, 0.015, r"author: Farid Musayev", fontproperties = ds_fps)
fig.text(
# create a space for a title and left/right axis
= 0.085, top = 0.83)
fig.subplots_adjust(bottom
plt.show()
# save figure
'xg_subplots.png', dpi = 350, transparent = False) fig.savefig(
To view this plot in a high resolution, please follow this link.