import matplotlib.pyplot as plt

# ============================================================
# Match order (Latest to Earliest)
# ============================================================
matches = ["Final/\nBronze", "SF", "QF", "R16", "R32", "GS3", "GS2", "GS1"]
x = list(range(len(matches)))

# ============================================================
# Data
# ============================================================
data = {
    "Spain": {
        "xGF":[2.29,1.63,1.96,1.69,2.32,0.86,2.30,2.10],
        "xGA":[0.22,0.31,0.34,0.63,0.32,0.20,0.14,0.20],
        "Opp":["ARG","FRA","BEL","POR","AUT","URU","KSA","CPV"]
    },

    "Argentina":{
        "xGF":[0.22,1.59,1.94,2.84,2.26,2.13,2.36,1.26],
        "xGA":[2.29,0.53,0.47,0.89,0.47,0.76,0.53,0.32],
        "Opp":["ESP","ENG","SUI","EGY","CPV","JOR","AUT","ALG"]
    },

    "England":{
        "xGF":[2.88,0.53,1.04,1.61,2.16,1.56,1.36,3.20],
        "xGA":[2.88,1.59,0.68,1.88,0.77,0.66,0.17,0.70],
        "Opp":["FRA","ARG","NOR","MEX","COD","PAN","GHA","CRO"]
    },

    "France":{
        "xGF":[2.88,0.31,3.69,1.45,3.24,1.50,2.67,1.79],
        "xGA":[2.88,1.63,0.14,0.13,0.70,1.70,0.63,0.53],
        "Opp":["ENG","ESP","MAR","PAR","SWE","NOR","IRQ","SEN"]
    }
}

# ============================================================
# Plot
# ============================================================
fig, axes = plt.subplots(2, 2, figsize=(15, 10), sharex=True, sharey=True)
axes = axes.flatten()

for ax, (team, values) in zip(axes, data.items()):

    # Plot xGF
    ax.plot(
        x,
        values["xGF"],
        color="royalblue",
        marker="o",
        linewidth=3,
        markersize=7,
        label="xGF",
        zorder=3
    )

    # Plot xGA
    ax.plot(
        x,
        values["xGA"],
        color="crimson",
        marker="s",
        linewidth=3,
        markersize=7,
        label="xGA",
        zorder=3
    )

    # Fill area
    ax.fill_between(
        x,
        values["xGF"],
        values["xGA"],
        color="limegreen",
        alpha=0.12,
        zorder=1
    )

    # --------------------------------------------------------
    # Annotate every point
    # --------------------------------------------------------
    for xi, xgf, xga, opp in zip(
            x,
            values["xGF"],
            values["xGA"],
            values["Opp"]):

        # xGF value (above blue point)
        ax.annotate(
            f"{xgf:.2f}",
            (xi, xgf),
            xytext=(0, 10),
            textcoords="offset points",
            ha="center",
            fontsize=8,
            fontweight="bold",
            color="royalblue",
            bbox=dict(
                facecolor="white",
                alpha=0.80,
                edgecolor="none",
                pad=0.2
            )
        )

        # Opponent code (above red point)
        ax.annotate(
            opp,
            (xi, xga),
            xytext=(0, 10),
            textcoords="offset points",
            ha="center",
            fontsize=8,
            fontweight="bold",
            color="black",
            bbox=dict(
                facecolor="white",
                edgecolor="black",
                linewidth=0.5,
                alpha=0.85,
                pad=0.2
            )
        )

        # xGA value (below red point)
        ax.annotate(
            f"{xga:.2f}",
            (xi, xga),
            xytext=(0, -16),
            textcoords="offset points",
            ha="center",
            fontsize=8,
            fontweight="bold",
            color="crimson",
            bbox=dict(
                facecolor="white",
                alpha=0.80,
                edgecolor="none",
                pad=0.2
            )
        )

    # Formatting
    ax.set_title(team, fontsize=16, fontweight="bold", pad=12)
    ax.set_xticks(x)
    ax.set_xticklabels(matches, fontsize=10)
    ax.set_ylim(0, 4)

    ax.grid(True, linestyle="--", alpha=0.35)

    ax.legend(
        loc="upper right",
        frameon=False,
        fontsize=10
    )

# ============================================================
# Figure formatting
# ============================================================
fig.suptitle(
    "2026 FIFA World Cup Semi-finalists\nExpected Goals For (xGF) vs Expected Goals Against (xGA)",
    fontsize=20,
    fontweight="bold",
    y=0.98
)

fig.supxlabel(
    "Tournament Stage",
    fontsize=14,
    fontweight="bold"
)

fig.supylabel(
    "Expected Goals (xG)",
    fontsize=14,
    fontweight="bold"
)

plt.tight_layout(rect=[0.03, 0.04, 1, 0.94])
plt.show()

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: