Projet 9 - Prédire la demande d'électricité

Paramètres

In [1]:
import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    fxn()
    # importations de modules "local"
    import sys
    sys.path.append('functions/')
    import base_notebook 
    import css_base_dark
    import fonctions_perso

1. Analyses des données


In [2]:
# import des librairies et des paramètres 
from base_notebook import *
from css_regression import *
from css_base_dark import *

# creation dossier grahs
path_graph = "data/exports/graphs/"
os.makedirs(path_graph, exist_ok=True)

import matplotlib as mpl
mpl.rcParams['figure.dpi'] = 100

# repère de début de traitement pour calculer le temps total d'exécution du notebook
start_time_m4 = time.time()
In [3]:
# Import des données de production et de consommation d'électricité
df = pd.read_csv("data/inputs/eCO2mix_RTE_energie_M.csv", index_col=0, parse_dates=True, sep='\t',  engine='python', encoding='ANSI')
In [4]:
# affichage aléatoires de 3 observations
df.sample(3)
# affichage de la taille du df
df.shape
# affichage des colonnes
df.columns
# affichage de l'index 
df.index
Out[4]:
Qualité Territoire Production totale Production nucléaire Production thermique totale Production thermique charbon Production thermique fioul Production thermique gaz Production hydraulique Production éolien ... Consommation totale Solde exportateur Echanges export Echanges import Echanges avec le Royaume-Uni Echanges avec l'Espagne Echanges avec l'Italie Echanges avec la Suisse Echanges avec l'Allemagne et la Belgique Unnamed: 22
Mois
2017-07-01 Données définitives PACA 1504 NaN 607 65.0 15 526 589 9 ... 3210 -1709.0 NaN NaN NaN NaN NaN NaN NaN NaN
2019-12-01 Données définitives Ile-de-France 424 NaN 288 0.0 16 272 3 29 ... 7196 -6772.0 NaN NaN NaN NaN NaN NaN NaN NaN
2019-09-01 Données définitives Nouvelle-Aquitaine 4276 3563.0 13 NaN 0 13 91 138 ... 2891 1385.0 NaN NaN NaN NaN NaN NaN NaN NaN

3 rows × 22 columns

Out[4]:
(1312, 22)
Out[4]:
Index(['Qualité', 'Territoire', 'Production totale', 'Production nucléaire',
       'Production thermique totale', 'Production thermique charbon',
       'Production thermique fioul', 'Production thermique gaz',
       'Production hydraulique', 'Production éolien', 'Production solaire',
       'Production bioénergies', 'Consommation totale', 'Solde exportateur',
       'Echanges export', 'Echanges import', 'Echanges avec le Royaume-Uni',
       'Echanges avec l'Espagne', 'Echanges avec l'Italie',
       'Echanges avec la Suisse', 'Echanges avec l'Allemagne et la Belgique',
       'Unnamed: 22'],
      dtype='object')
Out[4]:
DatetimeIndex(['2012-01-01', '2012-02-01', '2012-03-01', '2012-04-01',
               '2012-05-01', '2012-06-01', '2012-07-01', '2012-08-01',
               '2012-09-01', '2012-10-01',
               ...
               '2021-04-01', '2021-04-01', '2021-04-01', '2021-04-01',
               '2021-04-01', '2021-04-01', '2021-04-01', '2021-04-01',
               '2021-04-01', '2021-04-01'],
              dtype='datetime64[ns]', name='Mois', length=1312, freq=None)
  • Analyse du daraframe

    • on va s'intéresser à la demande en electricité au niveau national :
      • on limite les données au Territoire France
      • on limite les colonnes à la seule colonne "Consommation Totale" (que l'on renomme en 'conso' - l'unité est le GWh)
    • on renomme l'index en 'date'
In [5]:
df_fr = df.loc[df['Territoire'] == 'France',  ['Consommation totale']]
df_fr.columns = ['conso']
df_fr=df_fr.rename_axis('date')
df_fr
Out[5]:
conso
date
2012-01-01 51086
2012-02-01 54476
2012-03-01 43156
2012-04-01 40176
2012-05-01 35257
... ...
2020-12-01 47565
2021-01-01 53020
2021-02-01 43040
2021-03-01 43856
2021-04-01 37553

112 rows × 1 columns

In [6]:
# vérification de présence de valeurs nulles
df_fr.isna().sum()
Out[6]:
conso    0
dtype: int64
In [7]:
# Visualisons la série temporelle de la consommation d'électricité en France
plt.figure(figsize=(12,4))
plt.plot(df_fr['conso'])
plt.title("Consommation totale d'électricité en France (en GWh)", y=1.01)
plt.ylabel("Consommation (en GWh)")
plt.tight_layout()
plt.savefig(path_graph+"01-serie_temporelle_conso_France_brute.png", dpi=200)
plt.show();

Ainsi, on constate que :

  • la tendance semble stable au cours du temps
  • la saisonnalité est annuelle
In [8]:
# calculons le minimum et le maximum de la consommation pour chaque année (en indiquant également le mois)

# années de la periode
# on limite les données aux années complètes (on ne compte pas 2021)
years_list = np.arange(2012, 2021, 1).tolist()

# création d'un df df_fr_year avec pour index les années
df_fr_year=pd.DataFrame(
    index=years_list,
    columns=["avg", "min", "min_m", "max", "max_m"])

# création d'un df groupby par année
df_fr2=df_fr.copy()
df_fr2=df_fr2[df_fr2.index < "2021-01.-01"]
df_fr2['year']=pd.DatetimeIndex(df_fr2.index).year
df_fr2["month"] = pd.DatetimeIndex(df_fr2.index).month
df_fr3=df_fr2.groupby(['year'])

# initialisation des listes avec valeurs et numéro du mois
avg_list_value=[]
sum_list_value=[]
min_list_value=[]
min_list_month=[]
max_list_value=[]
max_list_month=[]

# on boucle sur chaque année et on récupère les données dans les listes
for name, group in df_fr3:
    avg_list_value.append(group['conso'].mean())
    sum_list_value.append(group['conso'].sum())
    min_list_value.append(group['conso'].min())
    min_list_month.append(group.loc[group['conso'] == group['conso'].min(), "month"].values[0])
    max_list_value.append(group['conso'].max())
    max_list_month.append(group.loc[group['conso'] == group['conso'].max(), "month"].values[0])

# on remplit df_fr_year avec les listes précédentes
df_fr_year['avg'] = avg_list_value
df_fr_year['avg'] = df_fr_year['avg'].apply(lambda x: round(x))
df_fr_year['conso_annee'] = sum_list_value
df_fr_year['conso_annee'] = df_fr_year['conso_annee'].apply(lambda x: "{:,}".format(x).replace(',', ' '))
df_fr_year['min'] = min_list_value
df_fr_year['min_m'] = min_list_month
df_fr_year['max'] = max_list_value
df_fr_year['max_m'] = max_list_month
df_fr_year
Out[8]:
avg min min_m max max_m conso_annee
2012 40793 32247 8 54476 2 489 517
2013 41237 31591 8 53619 1 494 842
2014 38762 31004 8 49359 1 465 150
2015 39671 31603 8 52536 1 476 050
2016 40268 32132 8 50670 12 483 219
2017 40167 32110 8 57406 1 482 008
2018 39869 32451 8 50202 2 478 431
2019 39450 31564 8 54117 1 473 401
2020 37417 30622 5 49676 1 449 005
  • Représentation graphique de la saisonnalité
In [9]:
# prepa des données
years = df_fr_year.index
df_fr['year']=pd.DatetimeIndex(df_fr.index).year
df_fr["month"] = pd.DatetimeIndex(df_fr.index).month
months_chiffre = np.arange(0,12,1).tolist()
months=["Janv", "Fev", "Mars", "Avril", "Mai", "Juin", "Juil", "Aout", "Sept", "Oct", "Nov", "Dec"]
df_fr["month"]=df_fr["month"].apply(lambda x: months[x-1])
df_fr.dtypes        
# prepa des couleurs
np.random.seed(100)
mycolors = np.random.choice(list(mpl.colors.XKCD_COLORS.keys()), len(years), replace=False)

# Dessiner le graphique
plt.figure(figsize=(12,6), dpi= 100)
for i, y in enumerate(years) :
    if i > 0 : 
        plt.plot('month', 'conso', data=df_fr.loc[df_fr.year==y, :], color=mycolors[i], label=y)
        plt.text(df_fr.loc[df_fr.year==y, :].shape[0]-.9, df_fr.loc[df_fr.year==y, 'conso'][-1 :].values[0], y, fontsize=8, color=mycolors[i])

#Décoration
plt.gca().set(xlim=(-0.3, 12), ylim=(25000, 60000))
plt.ylabel("Consommation (en GWh)")
plt.yticks(fontsize=11, alpha=.7)

plt.title("Saisonnalité de la consommation brute d'électricité en France")
plt.tight_layout()
plt.savefig(path_graph+"02-saisonnalite_consommation_electricite_brute.png", dpi=200)
plt.show();
  • On constate que la consommation brute d'électricité suit approximativement le même schéma chaque année : baisse jusqu'en juillet-août, puis augmentation jusqu'à la fin de l'année
    -> d'où une saisonnalité de 12 mois

  • De plus, on constate que les courbes annuelles se superposent : les niveaux de consommation sont équivalents chaque année
    -> d'où une tendance stable dans le temps (on remarque l'année particulière de 2020 du fait de la crise sanitaire et qui se manifeste par une consommation moindre d'électricité)
In [10]:
with plt.style.context("seaborn-ticks"):
    fig, axes = plt.subplots(1, 2, figsize=(15,5))
    config_moy={"marker":"o",
               "markerfacecolor":"white", 
               "markeredgecolor":"black",
               "markeredgewidth":"0.5",
               "markersize":"6"}
    config_moy_little={"marker":"o",
               "markerfacecolor":"white", 
               "markeredgecolor":"black",
               "markeredgewidth":"0.5",
               "markersize":"4"}
    q_colors = sns.color_palette("Set1", 12, .7)
    _=sns.boxplot(x='year', y='conso', data=df_fr[df_fr['year'] != 2021], showmeans=True, meanprops=config_moy, palette=(q_colors[i] for i,j in enumerate(df_fr['year'][:-1])), ax=axes[0]);
    qualitative_colors = sns.color_palette("Set3", 18);
    _=sns.boxplot(x='month', y='conso', data=df_fr.loc[~df_fr.year.isin([2012, 2020]), :], palette=(qualitative_colors[i] for i,j in enumerate(df_fr['month'])), meanprops=config_moy_little, showmeans=True);
    _=axes[0].set_title('BoxPlot par année\n(la Tendance)', color="darkred", fontsize=14);
    _=axes[0].set_ylabel("Consommation brute (en GWh)");
    _=axes[0].set_xlabel("");
    _=axes[1].set_title('BoxPlot par mois\n(La saisonnalité)', color="darkred", fontsize=14);
    _=axes[1].set_ylabel("Consommation brute (en GWh)");
    _=axes[1].set_xlabel("");
    plt.tight_layout(w_pad=3)
    plt.savefig(path_graph+"03-boxplot_annee_mois_conso_brute.png", dpi=200)
    plt.show();
  • Boxplot par année

    • cf l'évolution de la consommation mensuelle annualisée
    • Confirmation du constat précédent : l'évolution de la consommation d'électricité est comparable d'une année sur l'autre : les niveaux sont relativement constants.
    • On constate que c'est en 2017 que le mois avec la consommation la plus importante sur la période étudiée a eu lieu.
    • A l'inverse, c'est 2020 qui connait le mois durant lequel la consommation a été la plus faible (et les moyennes mensuelles les plus faibles) : cf. l'effet covid

  • Boxplot par mois

    • on peut classer les mois en 2 catégories :
      • de novembre à mars : niveaux et dipersion élevés de la consommation
      • d'avril à octobre : faibles niveaux et dispersion de la consommation

        => rend compte de l'importance des faibles températures sur le niveau et la dispersion de la consommation d'électricité
Haut de page    

2. Correction de l'effet température sur la consommation


  • Intégration des données météo
    Pour corriger les données de l'effet température, on va intégrer les Degrés Jour Unifié :

    • Le degré jour est une valeur représentative de l’écart entre la température d’une journée donnée et une température de référence, ici 18 °C
    • On utilisera le DJU de chauffe, c'est-à-dire celui correspondant à la situation où la température moyenne de la journée est inférieure à la température de référence
      --> donc si la température extérieure, pour une journée donnée, est de 9 °C, alors dju = 18 - 9 = 9 (si température supérieure à 18°, DJU de chauffe = 0)
      ---> puis les données sont agrégées par mois

  • Remarques

    • Etant donné le nombre élevé de stations différentes (1 ou 2 par département), nous avons décidé de nous limiter à une seule station, celle de Paris
    • Concernant la méthode de calcul, nous avons retenu la méthode simplifiée "Météo"
In [11]:
# import des dju chauffe
dju = pd.read_csv("data/inputs/dju_paris_meteo_chauffe.csv", index_col=[0], sep=";", decimal=",")
dju
Out[11]:
JAN FÉV MAR AVR MAI JUN JUI AOÛ SEP OCT NOV DÉC Total
2020 339.0 249.6 268.6 81.4 65.7 20.6 0.0 0.0 0.0 0.0 0.0 0.0 1024.9
2019 404.9 268.3 233.1 168.5 117.9 24.4 0.0 1.7 26.7 133.7 282.6 327.3 1989.0
2018 303.4 432.6 314.3 119.7 55.9 8.1 0.0 3.3 34.3 122.4 282.5 325.9 2002.2
2017 467.9 278.4 206.1 182.6 75.0 9.4 1.0 6.8 62.6 99.4 282.6 369.0 2040.6
2016 364.4 321.6 321.1 212.1 88.1 27.5 5.7 3.2 11.7 176.0 285.6 390.8 2207.3
2015 392.0 365.7 275.5 141.1 91.5 15.8 6.9 6.1 71.9 176.9 195.0 248.1 1986.2
2014 324.4 281.9 223.9 135.5 100.2 19.1 8.3 19.3 16.0 92.3 222.6 368.2 1811.5
2013 429.2 402.2 376.6 209.5 158.4 43.6 0.6 5.0 41.5 105.0 303.9 349.5 2424.8
2012 336.0 435.9 201.9 230.3 83.3 35.0 12.4 2.4 58.0 154.6 296.2 345.9 2191.5
2011 392.0 304.8 243.1 77.6 43.4 31.4 15.0 11.9 23.2 127.6 226.6 312.7 1809.0
2010 499.2 371.4 294.5 165.3 140.9 22.6 0.0 11.1 52.3 172.2 310.0 512.0 2551.1
2009 486.8 365.7 293.2 135.1 82.2 39.8 3.1 0.9 26.9 149.6 224.7 411.8 2219.7
In [12]:
# nos données de consommation commencent le 1/1/2012 -> on ne garde que les dju depuis cette date
dju=dju[dju.index>2011]

# suppression de la colonne "total"
dju=dju.drop(columns="Total")

# on ordonnes les données par année (ordre croissant)
dju=dju.sort_index()

dju
Out[12]:
JAN FÉV MAR AVR MAI JUN JUI AOÛ SEP OCT NOV DÉC
2012 336.0 435.9 201.9 230.3 83.3 35.0 12.4 2.4 58.0 154.6 296.2 345.9
2013 429.2 402.2 376.6 209.5 158.4 43.6 0.6 5.0 41.5 105.0 303.9 349.5
2014 324.4 281.9 223.9 135.5 100.2 19.1 8.3 19.3 16.0 92.3 222.6 368.2
2015 392.0 365.7 275.5 141.1 91.5 15.8 6.9 6.1 71.9 176.9 195.0 248.1
2016 364.4 321.6 321.1 212.1 88.1 27.5 5.7 3.2 11.7 176.0 285.6 390.8
2017 467.9 278.4 206.1 182.6 75.0 9.4 1.0 6.8 62.6 99.4 282.6 369.0
2018 303.4 432.6 314.3 119.7 55.9 8.1 0.0 3.3 34.3 122.4 282.5 325.9
2019 404.9 268.3 233.1 168.5 117.9 24.4 0.0 1.7 26.7 133.7 282.6 327.3
2020 339.0 249.6 268.6 81.4 65.7 20.6 0.0 0.0 0.0 0.0 0.0 0.0
  • On va intégrer les dju dans le df de la consommation

  • On va au préalable limiter les données de df_fr aux périodes pour lesquelles on dispose des valeurs de dju :
    --> jusqu'en mai 2020 inclus

In [13]:
# limitation des données de df_fr
df_fr = df_fr[df_fr.index < '2020-06-01']
# initialisation liste qui contiendra les valeurs de dju
li=[]
# pour chaque index
for i in dju.index:
    # on ajoute les valeurs des colonnes
    li.extend([dju.loc[i,col] for col in dju.columns])
# on ne garde que les 5 premiers mois de l'années 2020  -> on ne garde donc pas les 7 dernières valeurs de la liste
li=li[:-7]
# on verifie que les tailles de df_fr et de li sont identiques
if len(li) == df_fr.shape[0]:
    df_fr['dju']=li
    df_fr
Out[13]:
conso year month dju
date
2012-01-01 51086 2012 Janv 336.0
2012-02-01 54476 2012 Fev 435.9
2012-03-01 43156 2012 Mars 201.9
2012-04-01 40176 2012 Avril 230.3
2012-05-01 35257 2012 Mai 83.3
... ... ... ... ...
2020-01-01 49676 2020 Janv 339.0
2020-02-01 43358 2020 Fev 249.6
2020-03-01 41486 2020 Mars 268.6
2020-04-01 30658 2020 Avril 81.4
2020-05-01 30622 2020 Mai 65.7

101 rows × 4 columns

In [14]:
df_fr.dtypes
Out[14]:
conso      int64
year       int64
month     object
dju      float64
dtype: object
In [15]:
plt.rc('grid', alpha=0.3, linestyle='--')
plt.rc('figure', figsize=(10, 6), dpi=100, titlesize=14)
plt.rc('axes', titlecolor="darkred", titlesize=14, labelcolor="gray", labelsize=12)
plt.rc('xtick', labelsize=11)
plt.rc('ytick', labelsize=11)
In [16]:
fig=plt.figure(figsize=(12,4))
plt.rc('grid', alpha=0.3, linestyle='--')
ax = fig.add_subplot(211)
ax.plot(df_fr['conso'], label="conso", color="teal")
plt.ylabel("Consommation (en GWh)", color="gray", fontsize=10)
plt.legend(fontsize=9)
ax = fig.add_subplot(212)
ax.plot(df_fr['dju'], label="DJU", color="red")
plt.ylabel("DJU (en °C)", fontsize=10)
plt.suptitle('Comparaison graphique "consommation électrique" et graphique "DJU"', y=0.93, color="darkred")
plt.legend(fontsize=9)
plt.tight_layout()
plt.savefig(path_graph+"04-serie_temporelle_conso_France_brute_et_dju_2_graphs.png", dpi=200)
plt.show();
In [17]:
fig, ax1 = plt.subplots(figsize=(15,5))
color = 'teal'
ax1.plot(df_fr["conso"], label="Consommation (en GWh)", color=color)
plt.ylabel("Conso (en GWh)", color=color, labelpad=10)
plt.legend(loc="upper left")

ax2 = plt.gca().twinx()
color2='red'
ax2.plot(df_fr["dju"], label="DJU (en d °C)", color=color2)
plt.ylabel("DJU (en °C)", color=color2, labelpad=10)
plt.legend(loc="upper right")
plt.suptitle('Superposition graphique "consommation électrique" et graphique "DJU"', y=0.95)
plt.tight_layout()
plt.savefig(path_graph+"05-serie_temporelle_conso_France_brute_et_dju_1_graph.png", dpi=200)
plt.show();
In [18]:
#export des data
df_fr.to_csv("data/exports/df_fr.csv", header=True, index=True)
df_fr_year.to_csv("data/exports/df_fr_year.csv", header=True, index=True)
  • Remarque :
    • on constate la présence, chaque année, de pics de consommation en juillet, qui ne semblent pas être pris en compte par les DJU.
      -> or nous n'avons intégré que les DJU de chauffe : on peut donc penser que ces pics correspondent à l'utilisation des climatisations.
      (mais nous nous limiterons aux seuls DJU de chauffe pour notre analyse)
In [19]:
from fonction_regression_lineaire import *
# modèle de régression linéaire
reg = ols('conso~dju', data=df_fr).fit()
In [20]:
# verification des hypothèses d'application du modèle linéaire
res_hyp = hypotheses_regression_lineaire('conso', ['dju'], df_fr, 'conso_dju_chauffe')
Hypothèses du modèle de régression linéaire
Hypothèse 1 : Linéarité
Affichage aléatoire de 5 observations :
valeurs_reelles valeurs_predites residus
date
2018-05-01 33994 34151.409664 -157.409664
2012-03-01 43156 41310.990564 1845.009436
2016-12-01 50670 50574.311330 95.688670
2017-01-01 57406 54355.158504 3050.841496
2016-06-01 32852 32758.724064 93.275936