Graphique des sous-séries saisonnières

Le chiffre d'affaires industriel de chaque mois de l'année au fil des ans, avec la moyenne mensuelle comme référence.

Graphique des sous-séries saisonnières

Un graphique des sous-séries saisonnières (ou month plot) découpe une série mensuelle en douze mini-séries chronologiques, une par mois de l'année, chacune couvrant toutes les années. La barre horizontale est la moyenne de ce mois : la forme de la saison se lit donc d'une barre à l'autre, tandis que la ligne au sein de chaque panneau montre si ce mois dérive ou oscille d'une année à l'autre.

Les données sont l'indice mensuel Eurostat du chiffre d'affaires de la fabrication d'équipements électriques dans la zone euro (2010 = 100), non désaisonnalisé, pour les 21 années complètes 1995–2015 (statsmodels.datasets.elec_equip, fourni avec statsmodels). Le chiffre d'affaires chute chaque mois d'août, lorsque les usines ferment pour l'été, et remonte à chaque fin de trimestre. Le sous-titre rapporte une ANOVA à un facteur du chiffre d'affaires selon le mois (scipy.stats.f_oneway) avec l'η², la part de la variation totale expliquée par la saison — modeste ici, car la tendance de long terme au sein de chaque panneau explique une grande partie du reste. La récession de 2009 est mise en évidence.

Le code et le texte à l'intérieur des figures sont en anglais.

seasonal-subseries.py · 53 lignes
Télécharger GitHub
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
from statsmodels.datasets import elec_equip

# Data: monthly turnover index 1995–2015, reshaped to one row per year, one column per month
turnover = elec_equip.load().data.iloc[:, 0][:"2015"]
years = turnover.index.year.unique().to_numpy()
months = turnover.index[:12].strftime("%b")
values = turnover.to_numpy().reshape(len(years), 12)

# Seasonal effect: one-way ANOVA of turnover on month, with eta squared
F, p = stats.f_oneway(*values.T)
eta2 = ((values.mean(0) - values.mean()) ** 2).sum() * len(years) / ((values - values.mean()) ** 2).sum()

fig, ax = plt.subplots(figsize=(10, 5.2))
width = 0.8
x_in = (years - years.min()) / (years.max() - years.min()) * width - width / 2
highlight = {2009: list(months)}

# One mini series per month, plus its mean
for i, m in enumerate(months):
    v = values[:, i]
    if i % 2 == 0:
        ax.axvspan(i - 0.5, i + 0.5, color="#F2F2F7", lw=0, zorder=0)
    ax.plot(i + x_in, v, color="#007AFF", lw=0.9, alpha=0.7)
    ax.hlines(v.mean(), i - width / 2, i + width / 2, color="#FF9500", lw=2.5, zorder=3)
    for yr, ms in highlight.items():
        if m in ms:
            j = np.where(years == yr)[0][0]
            ax.scatter(i + x_in[j], v[j], s=26, color="#FF3B30", edgecolor="white", lw=0.8, zorder=4)

aug09 = (7 + x_in[years == 2009][0], values[years == 2009, 7][0])
ax.annotate("2009 recession", aug09, xytext=(30, -25), textcoords="offset points",
            ha="left", va="center", fontsize=9.5,
            color="#FF3B30", fontweight="semibold",
            arrowprops=dict(arrowstyle="-", color="#FF3B30", lw=0.8))
ax.set_xticks(range(12), months)
ax.set_xlim(-0.5, 11.5)
ax.grid(axis="y")
ax.set_ylabel("Turnover index (2010 = 100)")
ax.plot([], [], color="#FF9500", lw=2.5, label="Monthly mean")
ax.plot([], [], color="#007AFF", lw=1, label=f"{years.min()}–{years.max()}, left to right")
ax.legend(loc="upper left", ncols=2)
ax.set_ylim(top=values.max() + 15)
lo_m = turnover.index[values.mean(0).argmin()].strftime("%B")   # first year runs Jan–Dec
hi_m = turnover.index[values.mean(0).argmax()].strftime("%B")
p_txt = "p < .001" if p < 0.001 else f"p = {p:.3f}"
ax.set_title(f"Euro-area electrical equipment turnover bottoms out in {lo_m} and peaks in {hi_m}", pad=26)
ax.text(0, 1.02, f"Turnover index, monthly · one-way ANOVA F(11, {values.size - 12}) = {F:.1f}, "
        f"{p_txt}, η² = {eta2:.2f} · n = {len(years)} years",
        transform=ax.transAxes, fontsize=9.5, color="#6E6E73")
plt.show()

Les figures utilisent le style PyStatlas (facultatif) : ajoutez plt.style.use("pystatlas.mplstyle") pour obtenir exactement le même rendu.

Source des données

  • Euro-area electrical-equipment turnover, monthly, 1995–2016. Eurostat. Euro area (18, fixed composition) total turnover index, manufacture of electrical equipment (NACE Rev. 2), working-day adjusted, not seasonally adjusted. https://ec.europa.eu/eurostat
    Distributed with statsmodels (statsmodels.datasets.elec_equip); public domain.

Les exemples illustrent des méthodes statistiques. Leurs titres décrivent ce que chaque figure montre dans ces données précises — souvent un échantillon restreint, daté ou partiel —, et non la position de l'auteur sur la question de fond. Pour l'interprétation, le contexte et la qualité des données, reportez-vous aux sources originales citées ici.

Comment citer

PyStatlas est créé et maintenu par Thierry Warin. S'il vous a été utile dans vos travaux, merci de le citer.

Warin, T. (2026). Seasonal subseries plot. In PyStatlas: A Python Gallery of Statistical Graphics for the Social Sciences. https://warint.github.io/pystatlas/time-series/seasonal-subseries/

@misc{warin2026statlas,
  author       = {Warin, Thierry},
  title        = {PyStatlas: A Python Gallery of Statistical Graphics for the Social Sciences},
  year         = {2026},
  howpublished = {\url{https://warint.github.io/pystatlas/}},
  note         = {Source code: https://github.com/warint/pystatlas}
}

Plus dans Séries chronologiques