In [1]:
import pandas as pd

import matplotlib.pyplot as plt
import matplotlib.patheffects as pe
from highlight_text import fig_text
In [2]:
df = pd.read_clipboard(sep=",")
df.head(10)
Out[2]:
Date Time Round Day Venue Result GF GA Opponent xG xGA Poss Attendance Captain Formation Referee Match Report Notes
0 2020-08-22 21:00 (00:30) Matchweek 1 Sat Home D 1 1 Rennes 0.5 1.4 52 3975.0 José Fonte 4-2-2-2 Clément Turpin Match Report NaN
1 2020-08-30 13:00 (16:30) Matchweek 2 Sun Away W 1 0 Reims 1.1 0.2 49 NaN José Fonte 4-4-2 Stéphanie Frappart Match Report NaN
2 2020-09-13 13:00 (16:30) Matchweek 3 Sun Home W 1 0 Metz 1.5 0.7 62 NaN José Fonte 4-4-2 Jérémie Pignard Match Report NaN
3 2020-09-20 21:00 (00:30) Matchweek 4 Sun Away D 1 1 Marseille 1.7 0.7 44 NaN José Fonte 4-4-2 Hakim Ben El Hadj Salem Match Report NaN
4 2020-09-25 21:00 (00:30) Matchweek 5 Fri Home W 2 0 Nantes 2.0 0.9 54 NaN José Fonte 4-4-2 Willy Delajod Match Report NaN
5 2020-10-04 15:00 (18:30) Matchweek 6 Sun Away W 3 0 Strasbourg 1.9 1.7 53 NaN José Fonte 4-4-1-1 Mikael Lesage Match Report NaN
6 2020-10-18 21:00 (00:30) Matchweek 7 Sun Home W 4 0 Lens 2.6 0.4 57 NaN José Fonte 4-4-2 François Letexier Match Report NaN
7 2020-10-25 17:00 (21:30) Matchweek 8 Sun Away D 1 1 Nice 0.5 1.0 47 NaN José Fonte 4-4-2 Franck Schneider Match Report NaN
8 2020-11-01 21:00 (01:30) Matchweek 9 Sun Home D 1 1 Lyon 0.5 0.8 60 NaN José Fonte 4-4-2 Mikael Lesage Match Report NaN
9 2020-11-08 13:00 (17:30) Matchweek 10 Sun Away L 2 3 Brest 2.4 1.2 63 NaN José Fonte 4-4-2 Florent Batta Match Report NaN
In [3]:
TEAM_NAME = "Lille"

window = 5
gd_color = "dodgerblue"
xgd_color = "gold"

df["GD"] = df["GF"] - df["GA"]
df["xGD"] = df["xG"] - df["xGA"]

gd_rolling = df["GD"].rolling(window).mean().values[window:]
xgd_rolling = df["xGD"].rolling(window).mean().values[window:]
In [5]:
with plt.style.context("dark_background"):
    plt.rcParams['font.family'] = 'Palatino Linotype' ##set global font
    fig, ax = plt.subplots(figsize=(12, 8))

    ax.plot(gd_rolling, color=gd_color,  linestyle="-.", marker="o",  mfc=gd_color, mec="white", markersize=8, mew=0.4, zorder=10)  ##goal-difference
    ax.plot(xgd_rolling, color=xgd_color,  linestyle="-.", marker = "o", mfc=xgd_color, mec="white", markersize=8, mew=0.4, zorder=10) ##expected goals difference
    
    ax.fill_between(x=range(len(gd_rolling)), y1=gd_rolling, y2=xgd_rolling, where = gd_rolling>xgd_rolling, 
                    alpha=0.2, color=gd_color, interpolate=True, zorder=5) ##shade the areas in between
    ax.fill_between(x=range(len(gd_rolling)), y1=gd_rolling, y2=xgd_rolling, where = gd_rolling<=xgd_rolling, 
                    alpha=0.2, color=xgd_color, interpolate=True, zorder=5)
    
    ax.grid(linestyle="dashed", lw=0.7, alpha=0.1, zorder=1) ## a faint grid
    for spine in ["top", "right"]:
        ax.spines[spine].set_visible(False)  
    ax.set_position([0.08, 0.08, 0.82, 0.78]) ## make space for the title on top of the axes
    
    ## labels, titles and subtitles
    ax.set(xlabel=f"{window} match rolling mean", xlim=(-1, len(df)-window))     
    ax.xaxis.label.set(fontsize=12, fontweight='bold')    

    fig.text(x=0.08, y=0.92, s="Lille | Performance Trend", 
            ha='left', fontsize=24, fontweight='bold', 
            path_effects=[pe.Stroke(linewidth=3, foreground='0.15'),
                       pe.Normal()])   
    
    fig_text(x=0.08, y=0.90, ha='left',
             fontsize=18, fontweight='bold',
             s='Ligue 1 | 2020-21 | <Goal Difference> vs <Expected Goal Difference>',
             path_effects=[pe.Stroke(linewidth=3, foreground='0.15'),
                       pe.Normal()],
             highlight_textprops=[{"color": gd_color},
                                  {"color": xgd_color}])
    
fig.savefig("xg-trend-line-chart", dpi=180) ##save image