Quels scripts Python pour séries temporelles utiliser ?

Avec pandas et statsmodels, je peux nettoyer, contrôler, décomposer puis prévoir une série temporelle sans usine à gaz. Le vrai sujet n’est pas le script lui-même, mais l’enchaînement fiable des étapes pour éviter des prévisions propres en apparence et fausses en pratique.

Comment préparer une série irrégulière ?

Une série irrégulière se prépare en convertissant la colonne date en index temporel, puis en la rééchantillonnant à une fréquence explicite avant toute analyse. Une série temporelle irrégulière contient des mesures qui n’arrivent pas avec le même intervalle : pas exactement toutes les minutes, toutes les heures, tous les jours ou tous les mois.

Pandas est l’outil standard en Python pour ce travail. La fonction to_datetime() transforme une colonne en vraies dates exploitables. La méthode set_index() place ces dates comme index temporel. La méthode resample() regroupe ensuite les données dans une fréquence choisie. La documentation officielle pandas détaille ces opérations dans les sections “Time series / date functionality” et “Resampling” : pandas.pydata.org/docs/user_guide/timeseries.html.

Le choix de la fréquence change directement le résultat. Une mesure rééchantillonnée à la minute garde beaucoup de détail, mais produit plus de trous. Une fréquence horaire lisse davantage. Une fréquence quotidienne convient souvent au pilotage business. Une fréquence hebdomadaire ou mensuelle sert plutôt aux tendances. Ce choix n’est donc pas technique uniquement : il dépend de la décision à prendre.

L’agrégation doit suivre la logique métier :

  • mean convient à une température, un taux de conversion ou une latence moyenne.
  • sum convient à des ventes, des clics ou une consommation totale.
  • max convient à un pic de charge, une pression maximale ou un trafic serveur.
  • first ou last convient à un stock, un solde ou un état connu à un instant donné.

Les trous temporels méritent autant d’attention que les valeurs. Garder des NaN, c’est-à-dire des valeurs manquantes, évite de masquer une absence de données. Le forward-fill propage la dernière valeur connue, utile si cette valeur reste valide, par exemple un stock. L’interpolation estime une valeur entre deux points, utile pour des mesures continues, mais dangereuse si elle invente un comportement qui n’a pas été observé.

Un script robuste doit accepter CSV et Excel, gérer plusieurs colonnes de valeurs, appliquer une configuration par colonne et produire deux sorties : un fichier nettoyé et un rapport des intervalles manquants.

import pandas as pd

freq = "h"
aggregations = {
    "temperature": "mean",
    "ventes": "sum",
    "stock": "last"
}

df = pd.read_csv("mesures.csv")

df["date"] = pd.to_datetime(df["date"], errors="coerce", utc=True)

df = (
    df.dropna(subset=["date"])
      .set_index("date")
      .sort_index()
)

clean = df.resample(freq).agg(aggregations)

gap_report = clean.isna().reset_index()
gap_report = gap_report[gap_report[list(aggregations)].any(axis=1)]

clean.to_csv("serie_nettoyee.csv")
gap_report.to_csv("rapport_gaps.csv", index=False)
Cas d’usageAgrégation recommandéeRisque à surveiller
Température, taux, latencemeanLisser un pic important
Ventes, clics, volumessumCompter deux fois des données dupliquées
Charge serveur, pression, trafic maximalmaxRater un pic si la fréquence est trop large
Stock, solde, étatfirst ou lastPropager une valeur devenue invalide

Comment repérer les anomalies ?

Les anomalies se repèrent en combinant plusieurs méthodes statistiques simples, puis en annotant les points suspects au lieu de les supprimer automatiquement. Une anomalie est une valeur inhabituelle par rapport au comportement attendu de la série. Elle peut venir d’une erreur de mesure, d’un incident technique, d’un bug de collecte ou d’un vrai signal business, comme un pic de ventes ou une chute brutale de trafic.

Le z-score mesure l’écart d’une observation à la moyenne, exprimé en nombre d’écarts-types. Le seuil ±3 est courant car, sous hypothèse de distribution normale, environ 99,7 % des observations se situent dans les trois écarts-types autour de la moyenne. Cette hypothèse n’est pas toujours vraie, surtout avec des séries asymétriques, saisonnières ou très bruitées.

L’IQR, pour Interquartile Range ou écart interquartile, est souvent plus robuste. Q1 correspond au 25e percentile, Q3 au 75e percentile, et l’IQR vaut Q3 – Q1. La règle de Tukey marque comme suspecte toute valeur inférieure à Q1 – 1,5 × IQR ou supérieure à Q3 + 1,5 × IQR.

Les statistiques roulantes sont utiles quand le niveau de la série change dans le temps. Une fenêtre glissante calcule une moyenne mobile et un écart-type mobile sur les dernières observations. On compare ensuite chaque point à son contexte local, pas à toute l’historique.

import pandas as pd
from scipy.stats import zscore

df = pd.read_csv("serie.csv", parse_dates=["date"])
col = "valeur"

# Calcul du z-score global.
df["zscore"] = zscore(df[col], nan_policy="omit")
df["flag_zscore"] = df["zscore"].abs() > 3

# Calcul de la règle IQR de Tukey.
q1 = df[col].quantile(0.25)
q3 = df[col].quantile(0.75)
iqr = q3 - q1
borne_basse = q1 - 1.5 * iqr
borne_haute = q3 + 1.5 * iqr
df["flag_iqr"] = (df[col] < borne_basse) | (df[col] > borne_haute)

# Calcul rolling sur une fenêtre de 24 points.
window = 24
df["rolling_mean"] = df[col].rolling(window).mean()
df["rolling_std"] = df[col].rolling(window).std()
df["flag_rolling"] = (df[col] - df["rolling_mean"]).abs() > 3 * df["rolling_std"]

# Conservation de la méthode qui détecte chaque point.
df["anomaly_methods"] = df[["flag_zscore", "flag_iqr", "flag_rolling"]].apply(
    lambda row: ",".join(row.index[row]),
    axis=1
)

df.to_csv("serie_annotee.csv", index=False)

La sortie attendue reste simple : un fichier annoté avec un flag par méthode, un rapport résumé par colonne et, si besoin, des graphiques où les anomalies sont mises en évidence. Le point important : garder la trace de la méthode qui a détecté chaque point, car un pic repéré par trois méthodes ne se traite pas comme un point isolé repéré par une seule.

MéthodeRobustesseSimplicitéCas d’usage
Z-scoreMoyenne si la distribution n’est pas normaleTrès simpleSéries stables, bruit proche d’une loi normale
IQRBonne face aux valeurs extrêmesSimpleDonnées asymétriques ou avec outliers ponctuels
Statistiques roulantesBonne si la série évolue dans le tempsMoyenneTendance, saisonnalité, changement de niveau

Comment lire tendance et saisonnalité ?

On lit une série temporelle en séparant ce qui relève de la tendance, de la saisonnalité et du résidu. La tendance correspond au mouvement de fond : une hausse durable du chiffre d’affaires, une baisse progressive du trafic, une consommation qui augmente avec le temps. La saisonnalité désigne les motifs qui se répètent à période fixe : chaque lundi, chaque mois de décembre, chaque heure de pointe. Le résidu, lui, représente ce qui reste une fois la tendance et la saisonnalité retirées : bruit, anomalies, événements ponctuels ou variations non expliquées.

La décomposition classique se fait très simplement avec statsmodels.tsa.seasonal.seasonal_decompose(). La documentation officielle de statsmodels précise que cette méthode applique une décomposition naïve par moyennes mobiles, utile pour une première lecture exploratoire de la série. Elle ne remplace pas un modèle de prévision, mais elle aide à comprendre la structure du signal avant d’aller plus loin.

Le choix entre modèle additif et multiplicatif est important. Un modèle additif suppose que les effets saisonniers gardent une amplitude stable. Par exemple, les ventes gagnent environ 500 unités chaque décembre, quel que soit le niveau global. Un modèle multiplicatif convient quand l’amplitude saisonnière augmente ou diminue avec le niveau de la série. Par exemple, décembre représente toujours +20 %, mais +20 % sur 10 000 n’a pas le même poids que +20 % sur 100 000.

Fréquence des donnéesPériode courante
Données journalières avec cycle hebdomadaire7
Données mensuelles avec cycle annuel12
Données horaires avec cycle quotidien24

Une mauvaise période produit une lecture trompeuse. Avec period=6 sur des données mensuelles annuelles, la saisonnalité détectée ne correspondra pas au vrai cycle métier. Dans mes scripts, je prévois donc un rééchantillonnage si nécessaire, un choix explicite du modèle, une période configurable, un export Excel avec la série originale, la tendance, la saisonnalité et le résidu, puis un graphique multi-panneaux sauvegardé pour garder une trace lisible de l’analyse.

import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt

# Charger les données
df = pd.read_csv("serie_mensuelle.csv", parse_dates=["date"])
df = df.set_index("date").sort_index()

# Rééchantillonner si nécessaire, ici en fréquence mensuelle
serie = df["valeur"].resample("ME").sum()

# Décomposer la série avec un modèle additif et une saisonnalité annuelle
resultat = seasonal_decompose(
    serie,
    model="additive",
    period=12
)

# Exporter les composantes
export = pd.DataFrame({
    "original": serie,
    "tendance": resultat.trend,
    "saisonnalite": resultat.seasonal,
    "residu": resultat.resid
})
export.to_excel("decomposition_serie_temporelle.xlsx")

# Sauvegarder le graphique multi-panneaux
fig = resultat.plot()
fig.set_size_inches(12, 8)
plt.tight_layout()
plt.savefig("decomposition_serie_temporelle.png", dpi=150)
plt.close()
  • Choisir additif si l’écart saisonnier reste à peu près constant dans le temps.
  • Choisir multiplicatif si les variations saisonnières grossissent quand le niveau de la série augmente.
  • Éviter multiplicatif si la série contient des zéros ou des valeurs négatives.
  • Vérifier la période avec la logique métier avant de croire le graphique.

Comment prévoir les prochaines valeurs ?

Les prochaines valeurs se prévoient en entraînant un modèle SARIMA sur l’historique, puis en validant ses erreurs sur une période mise à l’écart. Cette séparation temporelle est indispensable : on entraîne sur le passé, on teste sur une période plus récente, et seulement ensuite on projette le futur.

SARIMA signifie Seasonal AutoRegressive Integrated Moving Average. C’est un modèle statistique qui combine quatre idées simples : la dépendance aux valeurs passées, la différenciation pour stabiliser la série, la moyenne mobile des erreurs passées, et une composante saisonnière. Les paramètres p, d, q décrivent la partie non saisonnière. Les paramètres P, D, Q, s décrivent la saisonnalité, avec s comme longueur du cycle, par exemple 12 pour des données mensuelles avec une saisonnalité annuelle.

En Python, l’implémentation crédible est statsmodels.tsa.statespace.sarimax.SARIMAX. La documentation officielle est disponible ici : https://www.statsmodels.org/stable/generated/statsmodels.tsa.statespace.sarimax.SARIMAX.html.

Une courbe qui “colle bien” ne suffit pas. Il faut une période de validation, des métriques d’erreur et des intervalles de confiance. La MAE, erreur absolue moyenne, donne l’erreur moyenne dans l’unité de la série. La RMSE, racine de l’erreur quadratique moyenne, pénalise davantage les grosses erreurs. La MAPE, erreur absolue moyenne en pourcentage, est lisible, mais devient problématique quand les valeurs réelles sont nulles ou très faibles.

L’AIC, critère d’information d’Akaike, sert à comparer plusieurs modèles. Il cherche un compromis entre qualité d’ajustement et complexité. À performance proche, un modèle plus simple est souvent préférable.

import numpy as np
import pandas as pd
from statsmodels.tsa.statespace.sarimax import SARIMAX

# Série attendue : index temporel, une colonne "y"
df = df.asfreq("MS")
y = df["y"]

test_size = 12
train = y.iloc[:-test_size]
test = y.iloc[-test_size:]

model = SARIMAX(
    train,
    order=(1, 1, 1),
    seasonal_order=(1, 1, 1, 12),
    enforce_stationarity=False,
    enforce_invertibility=False
)

results = model.fit(disp=False)

validation_forecast = results.get_forecast(steps=len(test))
pred = validation_forecast.predicted_mean
conf_int = validation_forecast.conf_int()

mae = np.mean(np.abs(test - pred))
rmse = np.sqrt(np.mean((test - pred) ** 2))

n_periods = 6
future_forecast = results.get_forecast(steps=n_periods)
future_values = future_forecast.predicted_mean
future_intervals = future_forecast.conf_int()

print("AIC:", results.aic)
print("MAE:", mae)
print("RMSE:", rmse)
print(future_values)
print(future_intervals)

Le script doit produire un forecast sur un nombre de périodes configurable, les valeurs prédites, les intervalles de confiance, les métriques calculées sur le jeu de validation et, si utile, un graphique pour contrôler visuellement les écarts.

Point à vérifierPourquoi c’est important
Séparation temporelle train/testÉvite de tester le modèle sur des données déjà vues.
MAE et RMSEMesure l’erreur réelle au lieu de juger seulement une courbe.
MAPE avec prudenceDevient instable si les valeurs réelles sont nulles ou très faibles.
Intervalles de confianceMontre l’incertitude, pas seulement une valeur centrale.
AIC entre modèlesAide à choisir un modèle performant sans complexité inutile.
Saisonnalité cohérenteUn mauvais paramètre s fausse toute la prévision.

Prêt à fiabiliser vos séries temporelles ?

Je retiens une règle simple : une prévision n’a de valeur que si la série a été correctement préparée avant. Le rééchantillonnage fixe le calendrier d’analyse, la détection d’anomalies évite de modéliser des accidents comme des comportements normaux, la décomposition aide à comprendre tendance et saisonnalité, puis SARIMA permet de produire des forecasts mesurables avec intervalles de confiance. Ces scripts Python ne remplacent pas le jugement métier, mais ils donnent une base reproductible, traçable et automatisable. Le bénéfice pour vous : gagner du temps, réduire les erreurs d’analyse et prendre de meilleures décisions sur vos données temporelles.

FAQ

  • Quel est le meilleur outil Python pour manipuler des séries temporelles ?
    pandas reste la base pour charger, nettoyer, rééchantillonner et agréger des séries temporelles. Sa méthode resample() est particulièrement utile dès qu’une série doit être ramenée à une fréquence régulière.
  • Pourquoi rééchantillonner une série temporelle avant l’analyse ?
    Le rééchantillonnage impose une fréquence claire, par exemple heure, jour ou mois. Sans cette étape, les agrégations, les graphiques, la décomposition et les prévisions peuvent être incohérents, surtout si les données arrivent à intervalles irréguliers.
  • Quelle méthode utiliser pour détecter les anomalies ?
    Il vaut mieux combiner plusieurs méthodes. Le z-score est simple si la distribution est proche d’une loi normale, l’IQR résiste mieux aux valeurs extrêmes, et les statistiques roulantes sont utiles quand le niveau de la série évolue dans le temps.
  • À quoi sert la décomposition d’une série temporelle ?
    Elle sépare la série en tendance, saisonnalité et résidu. Cette lecture aide à comprendre si une variation vient d’un mouvement de fond, d’un cycle récurrent ou d’un comportement inexpliqué qui mérite une analyse complémentaire.
  • SARIMA suffit-il pour faire de bonnes prévisions ?
    SARIMA est un modèle solide pour des séries avec saisonnalité, mais il ne suffit pas à lui seul. Il faut préparer les données, réserver une période de validation, mesurer les erreurs avec MAE, RMSE ou MAPE, et vérifier les intervalles de confiance.

 

 

A propos de l’auteur

Je suis Franck Scandolera, responsable de l’agence webAnalyste et de l’organisme Formations Analytics. J’accompagne les entreprises sur le tracking avancé server-side, l’Analytics Engineering, l’automatisation No/Low Code avec n8n, l’intégration de l’IA, le SEO et le GEO. J’ai travaillé pour des acteurs comme Logis Hôtel, Yelloh Village, BazarChic, la Fédération Française de Football ou Texdecor. Si vous voulez fiabiliser vos données, automatiser vos analyses ou industrialiser vos workflows IA, contactez-moi.

Retour en haut
Formations Analytics