Vous disposez d'une série chronologique de valeurs de puissance normalisées (437 796 mesures jusqu'au 31 octobre 2023) et souhaitez prédire son évolution pour la semaine suivante en utilisant un modèle de Régression Linéaire Multiple (RLM) en Python. Même si vous considérez la RLM comme un modèle potentiellement "simple" ou "mauvais" pour une tâche complexe de séries temporelles, il est possible d'obtenir les prédictions les plus logiques et correctes possibles *dans le cadre de ce modèle* en suivant une approche structurée. Ce guide vous montrera comment transformer vos données univariées, créer des caractéristiques pertinentes et utiliser LinearRegression
ou SGDRegressor
de Scikit-learn pour atteindre votre objectif.
LinearRegression
(Moindres Carrés Ordinaires) est simple et efficace pour des données modérées. SGDRegressor
(Descente de Gradient Stochastique) est plus adapté aux très grands jeux de données mais nécessite un ajustement des hyperparamètres.La Régression Linéaire Multiple cherche à modéliser une variable dépendante (votre consommation électrique future) comme une combinaison linéaire de plusieurs variables indépendantes (les caractéristiques que vous allez créer). Le défi avec une série temporelle *univariée* est qu'à première vue, vous n'avez qu'une seule variable. La solution réside dans la création de nouvelles variables à partir de votre unique série de données.
C'est l'étape la plus critique. Vous devez extraire des informations prédictives du passé et de la structure temporelle de vos données. Voici les types de caractéristiques les plus courants :
power(t-1)
, power(t-24)
, power(t-168)
(pour une granularité horaire, 168 = 24*7) permet au modèle de capturer cette auto-dépendance. Le choix des décalages pertinents est crucial et peut nécessiter une analyse exploratoire (fonctions d'autocorrélation).Visualisation typique d'une série temporelle, illustrant potentiellement tendances et saisonnalités.
Scikit-learn propose deux implémentations principales pour la RLM :
Modèle | Algorithme | Avantages | Inconvénients | Quand l'utiliser ? |
---|---|---|---|---|
LinearRegression | Moindres Carrés Ordinaires (OLS) | Simple à utiliser, pas d'hyperparamètres à régler (sauf normalisation), solution exacte. | Peut être lent/gourmand en mémoire pour de très très grands jeux de données (calcule l'inverse d'une grande matrice). Sensible aux outliers. | Taille de données modérée (vos ~440k points devraient être gérables), première approche, besoin d'une solution simple et interprétable. |
SGDRegressor | Descente de Gradient Stochastique (SGD) | Efficace pour les très grands jeux de données (traite les données par lots), permet la régularisation (L1, L2, ElasticNet) pour éviter le surajustement, peut être mis à jour avec de nouvelles données (apprentissage en ligne). | Nécessite un réglage des hyperparamètres (taux d'apprentissage, terme de régularisation, nombre d'itérations). Sensible à la mise à l'échelle des caractéristiques (StandardScaler est quasi obligatoire). Les résultats peuvent varier légèrement (nature stochastique). | Très grands jeux de données, besoin de régularisation, apprentissage en ligne, prêt à investir du temps dans le réglage des hyperparamètres. |
Pour votre cas, avec environ 440 000 points, LinearRegression
est probablement un bon point de départ et pourrait être suffisamment performant. Si vous rencontrez des problèmes de mémoire ou si vous souhaitez explorer la régularisation, SGDRegressor
est une alternative viable. Commencer par LinearRegression
est souvent plus direct.
Voici un exemple de code Python illustrant le processus complet : chargement des données, création des caractéristiques, entraînement du modèle LinearRegression
, et prédiction pour la semaine suivante.
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression # Ou SGDRegressor
# from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import StandardScaler
import warnings
# Ignorer les avertissements futurs pour la propreté de la sortie (optionnel)
warnings.simplefilter(action='ignore', category=FutureWarning)
# --- 1. Chargement et Préparation Initiale ---
# Supposons que vos données sont dans un fichier CSV 'power_data.csv'
# avec les colonnes 'timestamp' et 'normalized_power'
# Assurez-vous que 'timestamp' est parsé comme une date
try:
# Adaptez le chemin et le format de votre fichier
df = pd.read_csv('power_data.csv', parse_dates=['timestamp'], index_col='timestamp')
# Assurez-vous que les données sont triées par date
df.sort_index(inplace=True)
# Renommer la colonne pour la clarté
df.rename(columns={'normalized_power': 'power'}, inplace=True)
# Vérifier la fréquence (ex: 10 minutes) - Adaptez si nécessaire
# Si la fréquence n'est pas fixe, il faudra peut-être ré-échantillonner
inferred_freq = pd.infer_freq(df.index)
if inferred_freq is None:
print("Attention: Fréquence non détectée. Assurez-vous que les données sont régulières.")
# Option: Ré-échantillonner à une fréquence fixe, ex: '10T' pour 10 minutes
# df = df.resample('10T').mean().interpolate() # Ou une autre méthode d'agrégation/interpolation
# Pour cet exemple, nous supposons une fréquence fixe après chargement
else:
print(f"Fréquence détectée: {inferred_freq}")
except FileNotFoundError:
print("Fichier 'power_data.csv' non trouvé. Création de données d'exemple.")
# Création de données d'exemple si le fichier n'existe pas
date_rng = pd.date_range(start='2022-01-01', end='2023-10-31 23:59:59', freq='10min') # Exemple de fréquence
df = pd.DataFrame(date_rng, columns=['timestamp'])
df['power'] = np.abs(np.random.randn(len(date_rng)) * 50 + 100 + np.sin(np.arange(len(date_rng)) * 0.1) * 20) # Données simulées avec bruit et saisonnalité simple
df['timestamp'] = pd.to_datetime(df['timestamp'])
df = df.set_index('timestamp')
inferred_freq = '10T' # Fréquence des données d'exemple
print(f"Données chargées : {df.shape[0]} lignes.")
print(df.head())
# Déterminer la fréquence en objet Timedelta pour les calculs de décalage
freq_timedelta = pd.to_timedelta(inferred_freq or '10min') # Utilise '10min' par défaut si non inféré
# --- 2. Feature Engineering ---
def create_features(df, power_col='power', lags=[1, 2, 3], seasonal_lags=[24*6, 24*6*7]):
"""Crée des caractéristiques temporelles et de décalage."""
df_feat = df.copy()
# Lags simples
for lag in lags:
df_feat[f'power_lag_{lag}'] = df_feat[power_col].shift(lag)
# Lags saisonniers (basés sur la fréquence, ex: 24*6 pour 10min = 1 jour)
for lag in seasonal_lags:
df_feat[f'power_seasonal_lag_{lag}'] = df_feat[power_col].shift(lag)
# Caractéristiques temporelles
df_feat['hour'] = df_feat.index.hour
df_feat['dayofweek'] = df_feat.index.dayofweek # Lundi=0, Dimanche=6
df_feat['dayofyear'] = df_feat.index.dayofyear
df_feat['month'] = df_feat.index.month
df_feat['weekofyear'] = df_feat.index.isocalendar().week.astype(int)
df_feat['time_trend'] = np.arange(len(df_feat)) # Tendance linéaire simple
# Supprimer les lignes avec NaN introduits par les décalages
df_feat.dropna(inplace=True)
return df_feat
# Définir les décalages (lags) à créer
# Exemple : 1, 2, 3 pas précédents + lag journalier + lag hebdomadaire
# Adaptez ces valeurs en fonction de votre fréquence et de l'analyse de vos données !
steps_per_hour = pd.Timedelta('1h') / freq_timedelta
steps_per_day = int(24 * steps_per_hour)
steps_per_week = int(7 * steps_per_day)
lags_to_create = [1, 2, 3]
seasonal_lags_to_create = [steps_per_day, steps_per_week] # Lag journalier et hebdomadaire
df_features = create_features(df, lags=lags_to_create, seasonal_lags=seasonal_lags_to_create)
# Séparer X (caractéristiques) et y (cible)
X = df_features.drop('power', axis=1)
y = df_features['power']
# --- 3. Mise à l'échelle des caractéristiques (Important pour SGD, recommandé pour LinearRegression) ---
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Convertir X_scaled en DataFrame pour garder les noms de colonnes et l'index
X_scaled_df = pd.DataFrame(X_scaled, index=X.index, columns=X.columns)
# --- 4. Entraînement du Modèle ---
# Utiliser toutes les données jusqu'au 31 Oct 2023 pour l'entraînement final
# Pas de division train/test ici car on veut le modèle final pour prédire le futur.
# Pour une évaluation robuste, vous devriez normalement faire une division train/test temporelle.
# Option 1: LinearRegression
model = LinearRegression()
model.fit(X_scaled_df, y)
print("\nModèle LinearRegression entraîné.")
# Option 2: SGDRegressor (décommentez pour l'utiliser)
# model = SGDRegressor(max_iter=1000, tol=1e-3, random_state=42, eta0=0.01, penalty='l2', alpha=0.0001)
# model.fit(X_scaled_df, y)
# print("\nModèle SGDRegressor entraîné.")
# --- 5. Préparation pour la Prédiction Future ---
# Déterminer la période de prédiction : la semaine après le 31 Oct 2023
last_timestamp = df.index[-1]
prediction_start_date = last_timestamp + freq_timedelta
prediction_end_date = prediction_start_date + pd.Timedelta(weeks=1) - freq_timedelta # 1 semaine complète
# Créer l'index de temps pour la période de prédiction
future_index = pd.date_range(start=prediction_start_date, end=prediction_end_date, freq=inferred_freq)
print(f"\nPériode de prédiction : {prediction_start_date} à {prediction_end_date}")
# Créer un DataFrame pour les caractéristiques futures
# Nous avons besoin des dernières données d'entraînement pour calculer les premiers lags futurs
max_lag = max(lags_to_create + seasonal_lags_to_create)
last_known_data = df.iloc[-max_lag:] # Prendre suffisamment de données passées
# DataFrame temporaire pour construire les caractéristiques futures pas à pas
future_df_builder = pd.DataFrame(index=future_index)
future_df_builder['power'] = np.nan # La colonne 'power' sera prédite
# Combiner les dernières données connues avec le squelette futur
combined_df = pd.concat([last_known_data, future_df_builder])
# --- 6. Prédiction Itérative ---
# La RLM nécessite toutes les caractéristiques pour prédire. Comme les futurs lags dépendent
# des prédictions précédentes, nous devons prédire pas à pas.
predictions = []
current_data_for_features = combined_df.copy() # Travailler sur une copie
for current_timestamp in future_index:
# 1. Créer les caractéristiques pour l'instant 'current_timestamp'
# Utilise les données jusqu'à l'instant précédent (y compris les prédictions précédentes)
temp_features_df = create_features(current_data_for_features.loc[:current_timestamp],
lags=lags_to_create,
seasonal_lags=seasonal_lags_to_create)
# Extraire la dernière ligne de caractéristiques (celle pour current_timestamp)
# Vérifier si la ligne existe (peut être manquante au tout début si pas assez d'historique)
if current_timestamp not in temp_features_df.index:
print(f"Attention: Impossible de créer les features pour {current_timestamp}, historique insuffisant?")
# Stratégie de secours: utiliser la dernière prédiction ou une valeur par défaut?
# Pour cet exemple, on saute ou met NaN. Une gestion plus robuste est nécessaire.
predicted_value = np.nan
else:
current_X = temp_features_df.drop('power', axis=1).loc[[current_timestamp]]
# 2. Mettre à l'échelle les caractéristiques avec le scaler entraîné
current_X_scaled = scaler.transform(current_X) # Utiliser transform(), pas fit_transform()!
# 3. Prédire la valeur
predicted_value = model.predict(current_X_scaled)[0]
# Stocker la prédiction
predictions.append(predicted_value)
# Mettre à jour la colonne 'power' dans notre DataFrame de construction
# pour que cette prédiction soit utilisée pour calculer les lags futurs
current_data_for_features.loc[current_timestamp, 'power'] = predicted_value
# Créer le DataFrame final des prédictions
predictions_df = pd.DataFrame({'predicted_power': predictions}, index=future_index)
print("\nPrédictions pour la semaine suivante :")
print(predictions_df.head())
print("...")
print(predictions_df.tail())
# --- 7. Visualisation (Optionnelle mais recommandée) ---
import matplotlib.pyplot as plt
plt.figure(figsize=(15, 7))
# Afficher les dernières données d'entraînement
plt.plot(df.index[-steps_per_week*2:], df['power'].iloc[-steps_per_week*2:], label='Données historiques récentes')
# Afficher les prédictions
plt.plot(predictions_df.index, predictions_df['predicted_power'], label='Prédictions RLM', color='red')
plt.title('Prédiction de la Consommation Électrique Normalisée')
plt.xlabel('Date')
plt.ylabel('Puissance Normalisée')
plt.legend()
plt.grid(True)
plt.show()
Explication du Code Détaillée
- Chargement : Charge les données depuis un CSV (ou crée des données d'exemple), s'assure que l'index est temporel et trié, et tente d'inférer la fréquence.
- Feature Engineering (`create_features`) : Une fonction crée les colonnes de lags (simples et saisonniers basés sur la fréquence détectée) et les caractéristiques temporelles (heure, jour, mois, etc.). Les lignes avec des `NaN` dus aux décalages initiaux sont supprimées pour l'entraînement.
- Mise à l'échelle : `StandardScaler` est utilisé pour normaliser les caractéristiques (moyenne=0, écart-type=1). C'est crucial pour `SGDRegressor` et souvent bénéfique pour `LinearRegression`. Le `scaler` est "entraîné" (`fit_transform`) sur les données d'entraînement.
- Entraînement : Le modèle choisi (`LinearRegression` ou `SGDRegressor`) est entraîné sur l'ensemble des caractéristiques mises à l'échelle (`X_scaled_df`) et la variable cible (`y`).
- Préparation Future : Définit l'index temporel pour la semaine de prédiction. Crée un DataFrame temporaire (`combined_df`) qui inclut les dernières données d'entraînement (nécessaires pour calculer les premiers lags futurs) et le squelette de la période future.
- Prédiction Itérative : C'est la partie délicate. Comme les caractéristiques futures (en particulier les lags de `power`) dépendent des valeurs futures inconnues de `power`, on prédit pas à pas :
- Pour chaque instant de la semaine future :
- On utilise la fonction `create_features` sur les données connues *jusqu'à cet instant* (y compris les prédictions déjà faites pour les instants précédents).
- On extrait la ligne de caractéristiques pour l'instant courant.
- On met à l'échelle ces caractéristiques avec le `scaler` déjà entraîné (`transform`).
- On fait la prédiction pour cet instant.
- On stocke cette prédiction et on l'ajoute au DataFrame (`current_data_for_features`) pour qu'elle soit utilisée dans le calcul des caractéristiques des instants suivants.
- Résultats et Visualisation : Les prédictions sont stockées dans un DataFrame et affichées. Une visualisation compare les prédictions aux dernières données historiques.
Visualisation du Processus et Comparaison des Modèles
Cartographie du Flux de Travail
Cette carte mentale résume les étapes clés du processus de prédiction de séries temporelles avec la RLM que nous avons décrit :
mindmap
root["Prédiction de Séries Temporelles avec RLM"]
id1["1. Données"]
id1_1["Chargement (CSV, DB...)"]
id1_2["Nettoyage (NaNs, outliers?)"]
id1_3["Index Temporel (DateTimeIndex)"]
id1_4["Tri Chronologique"]
id1_5["Vérification Fréquence"]
id2["2. Feature Engineering (Crucial)"]
id2_1["Lags (Décalages)"]
id2_1_1["Simples (t-1, t-2...)"]
id2_1_2["Saisonniers (t-jour, t-semaine...)"]
id2_2["Caractéristiques Temporelles"]
id2_2_1["Heure, Jour Semaine/Année"]
id2_2_2["Mois, Trimestre"]
id2_2_3["Indicateurs (Weekend, Férié)"]
id2_3["Tendance (Trend)"]
id2_3_1["Compteur Linéaire"]
id2_3_2["Termes Polynomiaux"]
id2_4["Suppression NaN initiaux"]
id3["3. Préparation Modèle"]
id3_1["Séparation X / y"]
id3_2["Mise à l'Échelle (Scaler)"]
id3_2_1["StandardScaler (recommandé)"]
id3_2_2["Fit sur Train, Transform sur Train/Test/Futur"]
id3_3["Division Train / Test (Optionnel pour évaluation)"]
id4["4. Choix & Entraînement Modèle"]
id4_1["LinearRegression (OLS)"]
id4_1_1["Simple, pas d'hyperparamètres"]
id4_2["SGDRegressor (Gradient Descent)"]
id4_2_1["Grands datasets, Régularisation"]
id4_2_2["Réglage Hyperparamètres (taux, alpha...)"]
id4_3["Entraînement: model.fit(X_train_scaled, y_train)"]
id5["5. Prédiction Future (Itérative)"]
id5_1["Définir Période Future"]
id5_2["Boucle sur chaque pas de temps futur"]
id5_2_1["Créer Features pour l'instant t"]
id5_2_2["(Utilise prédictions t-1, t-2...)"]
id5_2_3["Scaler les Features (transform)"]
id5_2_4["Prédire valeur pour t: model.predict()"]
id5_2_5["Stocker prédiction & mettre à jour historique"]
id6["6. Évaluation & Analyse"]
id6_1["Métriques (RMSE, MAE, MAPE) sur Test Set"]
id6_2["Visualisation (Historique vs Prédictions)"]
id6_3["Analyse des Résidus (Autocorrélation?)"]
Cette structure met en évidence la dépendance de chaque étape par rapport à la précédente, soulignant l'importance du feature engineering et de la boucle de prédiction itérative pour générer des prévisions futures.
Comparaison Qualitative : LinearRegression vs. SGDRegressor
Le graphique radar suivant offre une comparaison qualitative des deux modèles sur des aspects clés pour la prévision de séries temporelles, basée sur leurs caractéristiques générales. Les scores sont sur une échelle indicative où plus élevé est généralement mieux (sauf pour le coût computationnel).
Ce radar illustre les compromis : `LinearRegression` brille par sa simplicité et son interprétabilité, tandis que `SGDRegressor` offre une meilleure scalabilité et des options de régularisation, au prix d'un réglage plus complexe.
Approfondir : Vidéo Explicative
Comprendre la Saisonnalité en Régression Multiple
La vidéo suivante (en anglais) explique comment intégrer la saisonnalité dans un modèle de régression multiple pour la prévision de séries temporelles. Bien que l'outil utilisé puisse différer, les concepts de création de variables indicatrices (dummy variables) pour les mois ou les jours sont directement applicables à notre approche avec Python et Scikit-learn. Comprendre comment modéliser explicitement ces cycles est essentiel pour améliorer la logique des prédictions de votre modèle RLM.
Cette vidéo montre comment, en ajoutant des variables qui capturent les motifs saisonniers (par exemple, une variable pour chaque mois, ou des termes sinusoïdaux/cosinusoïdaux), un modèle de régression linéaire peut apprendre à reproduire ces fluctuations périodiques, rendant les prédictions futures plus plausibles et "logiques", même si le modèle sous-jacent reste linéaire.
Limitations et "Logique" des Prédictions
Gérer les Attentes d'un Modèle RLM
Vous avez mentionné que le modèle pourrait être "mauvais". Il est important de comprendre pourquoi et comment obtenir les résultats "les plus logiques" malgré cela :
- Hypothèses de la RLM : La RLM classique suppose l'indépendance et l'homoscédasticité (variance constante) des erreurs (résidus). Dans les séries temporelles, les erreurs sont souvent autocorrélées (l'erreur d'aujourd'hui dépend de celle d'hier) et la variance peut changer (hétéroscédasticité). La violation de ces hypothèses n'empêche pas la prédiction mais peut affecter la fiabilité des intervalles de confiance et des tests statistiques sur les coefficients.
- Linéarité : La RLM capture uniquement les relations linéaires entre les caractéristiques créées et la cible. Si la dynamique réelle de votre consommation électrique est fortement non linéaire, la RLM aura du mal à la modéliser parfaitement.
- Stationnarité : Idéalement, la relation entre vos variables devrait être stable dans le temps. Si votre série présente une tendance forte ou une saisonnalité qui évolue, un modèle RLM simple pourrait ne pas s'adapter correctement sur le long terme. Le feature engineering (variables de tendance, saisonnalité) aide à atténuer ce problème.
- Qualité du Feature Engineering : Les prédictions "logiques" dépendent ENORMEMENT de la pertinence des caractéristiques que vous créez. Si vous omettez un lag important ou un cycle saisonnier clé, le modèle ne pourra pas les apprendre et les prédire. C'est là que réside la plus grande partie du travail pour améliorer un modèle RLM sur des séries temporelles.
- Horizon de Prédiction : Les modèles basés sur des lags récents fonctionnent généralement mieux à court terme. Prédire une semaine entière est possible, mais l'incertitude et l'erreur potentielle augmentent avec l'horizon de prédiction, car les erreurs de prédiction se propagent dans le calcul des lags futurs.
Pour obtenir les prédictions "les plus logiques" :
- Investissez dans le Feature Engineering : Analysez vos données (graphiques, autocorrélation) pour identifier les lags et cycles pertinents.
- Validez Visuellement : Comparez les prédictions aux données historiques. Suivent-elles la tendance générale et les motifs saisonniers ?
- Soyez Conscient des Limites : Ne vous attendez pas à une précision parfaite, surtout si la série est très volatile ou subit des changements brusques non capturés par les caractéristiques. L'objectif est une prédiction *directionnellement correcte* et *saisonnièrement cohérente* basée sur les informations fournies au modèle.
FAQ : Questions Fréquentes
Comment choisir les bons lags (décalages) ? +
Dois-je rendre ma série stationnaire avant d'utiliser la RLM ? +
Comment gérer l'autocorrélation des résidus ? +
SGDRegressor demande beaucoup de réglages, par où commencer ? +
Recommandations pour Aller Plus Loin
Explorer d'autres approches et affiner votre modèle
-
Comment évaluer la performance d'un modèle de prévision de série temporelle en Python ?
-
Quelles sont les alternatives à la Régression Linéaire Multiple pour prédire la consommation électrique ?
-
Comment utiliser l'analyse d'autocorrélation (ACF/PACF) pour choisir les lags dans un modèle de série temporelle ?
-
Implémenter la validation croisée temporelle (Time Series Cross-Validation) en Scikit-learn
Références
Sources et Lectures Complémentaires
cienciadedatos.net
Skforecast: time series forecasting with Python, Machine ... - cienciadedatos.net
mathworks.com
Time Series Regression I: Linear Models - MathWorks
stats.stackexchange.com
Multiple linear regression vs. Time Series - Cross Validated
cienciadedatos.net
Forecasting energy demand with machine learning - cienciadedatos.net
kaggle.com
Linear Regression With Time Series - Kaggle
digitalocean.com
How To Use the predict() Function in R Programming | DigitalOcean
search.r-project.org
R: Predict x from y for a linear calibration
stats.oarc.ucla.edu
Robust Regression | R Data Analysis Examples
quantumcomputinginc.com
Univariate Time Series Forecasting: NARMA10
geeksforgeeks.org
Step-by-Step Guide to Modeling Time Series Data Using Linear ...
stackoverflow.com
R plot confidence interval lines with a robust linear regression ...
danieldsjoberg.com
tbl_regression() tutorial • gtsummary - Daniel D. Sjoberg
ujangriswanto08.medium.com
Hands-On Guide to Robust Regression for Students Using R
tandfonline.com
Transforming statistical models into machine learning approach
stat.ethz.ch
R: Predict method for Linear Model Fits
cran.r-project.org
Tidy, Type-Safe 'prediction()' Methods
api.rpubs.com
Basics of Logistic Regression - RPubs
stats.stackexchange.com
Time series linear regression vs Linear regression - Cross Validated
statology.org
How to Use the predict() Function with lm() in R
aionlinecourse.com
Time Series Forecasting Using Multiple Linear Regression Model
stat.ethz.ch
HTTP redirect
stackoverflow.com
Advice on how to predict future time series data - Stack Overflow
developers.arcgis.com
Forecasting power consumption in Tetouan city using Deep ...
afit-r.github.io
Logistic Regression - AFIT Data Science Lab R Programming Guide ·
stackoverflow.com
How to plot my multivariable regression time series model in R?
stats.stackexchange.com
What is/are the "mechanical" difference between multiple linear ...
mathworks.com
Time Series Regression Models
blog.bajonczak.com
Generating a KI Model to predict my Power consumption at home
stackoverflow.com
r - How to predict x values from a linear model (lm) - Stack Overflow