Vous êtes sur la page 1sur 51

RAPPORT DE PROJET

MODELISATION STATISTIQUE

Par :
AHBACH Hamza et AIT ZAABOUL Khawla

Département : Génie Industriel

Option : Mangement Industriel


(MGI)

Une plongée dans le temps du trafic routier de la


ville de Sydney : Modèles Prédictifs pour une
Prévision future

Travail encadré par


Pr ASMAA BENGHABRIT

Année universitaire : 2023/2024


Introduction et contexte :

À l'ère de la mobilité urbaine et de la croissance constante du nombre de


véhicules, la gestion efficace du trafic routier est devenue une priorité
incontournable. Notre projet s'attaque à ce défi en se plongeant dans l'analyse
approfondie des données de trafic routier sur une période de quatre mois. L'objectif
principal est de comprendre les tendances, les fluctuations saisonnières et les
schémas sous-jacents qui influent sur la circulation routière.

À travers une approche basée sur l'exploration de séries temporelles et la


modélisation prédictive, nous cherchons à dégager des insights significatifs pour
chaque junction. Notre démarche repose sur la création de caractéristiques
pertinentes, l'analyse minutieuse des données et l'application de techniques
avancées de modélisation. Nous nous efforçons de développer des modèles
prédictifs capables de fournir des prévisions précises sans recourir à des
informations spécifiques sur le modèle utilisé.

Ce projet s'inscrit dans une vision plus large de l'utilisation de science des donnés
pour optimiser la gestion du trafic routier, contribuant ainsi à la création de villes
intelligentes et à une mobilité urbaine plus fluide. À travers cette exploration, nous
détaillerons les différentes étapes de notre analyse, soulignant les enjeux
spécifiques et envisageant des solutions innovantes pour une gestion du trafic plus
intelligente.
Présentation du Dataset

Le jeu de données "train.xlsx" constitue le socle de notre exploration et de notre


modélisation. Composé de données de trafic routier recueillies sur une période de
temps étendue, ce fichier Excel regroupe des informations essentielles pour chaque
junction Le dataset contient les enregstriement pour chaque junction durant les 20
mois depuis 2015-11-01 jusqu’au 2017-06-30 et durant chaque heure
Voici un aperçu des principales caractéristiques du jeu de données :

• DateTime : Indique la date et l'heure de la mesure de trafic.


• ID : Cette colonne représente un identifiant unique associé à chaque enregistrement.
• Junction : Identifie la junction routière spécifique à laquelle les données sont
associées.
• Vehicles : Représente le nombre de véhicules enregistré à la junction au moment de
la mesure.
Ce jeu de données offre une riche source d'informations pour comprendre les
schémas temporels, les variations saisonnières et les tendances de trafic à travers
différentes zones de junction. Son exploration approfondie et son analyse rigoureuse
sont cruciales pour élaborer des modèles prédictifs robustes et contribuer à
l'amélioration de la gestion du trafic.
Projet modelisation statistique_AHBACH_AITZAABOUL

December 3, 2023

Realisé par
AHBACH Hamza – AIT ZAABOUL Khawla
Encadré par
Pr ASMAA BENGHABRIT

1 Exploration des Données (Data Exploration):

L’étape d’exploration des données revêt une importance capitale dans notre démarche analytique.
Notre objectif est de comprendre en profondeur la structure, les tendances et les particularités du
jeu de données “train.xlsx”.
Cette exploration approfondie constitue le socle sur lequel nous construisons nos modèles prédictifs.
Importons les biblotheques necessaires

[ ]: # importons les biblotheques necessaires

import numpy as np
import pandas as pd
import plotly.express as px
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
import time
from scipy.signal import find_peaks
from scipy.stats import gamma
import statsmodels.api as sm
import warnings
from datetime import datetime
import statsmodels.api as sm
import scipy.stats as stats

import os

1
$$
importons le dataset

[ ]: data = pd.read_csv("/train_ML_IOT.csv")
data

[ ]: DateTime Junction Vehicles ID


0 2015-11-01 00:00:00 1 15 20151101001
1 2015-11-01 01:00:00 1 13 20151101011
2 2015-11-01 02:00:00 1 10 20151101021
3 2015-11-01 03:00:00 1 7 20151101031
4 2015-11-01 04:00:00 1 9 20151101041
... ... ... ... ...
48115 2017-06-30 19:00:00 4 11 20170630194
48116 2017-06-30 20:00:00 4 30 20170630204
48117 2017-06-30 21:00:00 4 16 20170630214
48118 2017-06-30 22:00:00 4 22 20170630224
48119 2017-06-30 23:00:00 4 12 20170630234

[48120 rows x 4 columns]

• Le dataset contient les enregstriement pour chaque junction durant les 20 mois depuis 2015-
11-01 jusqu’au 2017-06-30 et durant chaque heure
informations sur le dataset

[ ]: data.describe(), data.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 48120 entries, 0 to 48119
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 DateTime 48120 non-null object
1 Junction 48120 non-null int64
2 Vehicles 48120 non-null int64
3 ID 48120 non-null int64
dtypes: int64(3), object(1)
memory usage: 1.5+ MB

[ ]: ( Junction Vehicles ID
count 48120.000000 48120.000000 4.812000e+04
mean 2.180549 22.791334 2.016330e+10
std 0.966955 20.750063 5.944854e+06
min 1.000000 1.000000 2.015110e+10
25% 1.000000 9.000000 2.016042e+10
50% 2.000000 15.000000 2.016093e+10
75% 3.000000 29.000000 2.017023e+10
max 4.000000 180.000000 2.017063e+10,

2
None)

Nous procédons à une organisation du dataset

[ ]: data['DateTime'] = pd.to_datetime(data['DateTime'])
data['Weekday'] = [datetime.weekday(date) for date in data.DateTime]
data['Year'] = [date.year for date in data.DateTime]
data['Month'] = [date.month for date in data.DateTime]
data['Day'] = [date.day for date in data.DateTime]
data['Hour'] = [date.hour for date in data.DateTime]
data['Week'] = [date.week for date in data.DateTime]
data['Quarter'] = [date.quarter for date in data.DateTime]
data["IsWeekend"] = data["Weekday"] >= 5

data.head(10)

[ ]: DateTime Junction Vehicles ID Weekday Year Month \


0 2015-11-01 00:00:00 1 15 20151101001 6 2015 11
1 2015-11-01 01:00:00 1 13 20151101011 6 2015 11
2 2015-11-01 02:00:00 1 10 20151101021 6 2015 11
3 2015-11-01 03:00:00 1 7 20151101031 6 2015 11
4 2015-11-01 04:00:00 1 9 20151101041 6 2015 11
5 2015-11-01 05:00:00 1 6 20151101051 6 2015 11
6 2015-11-01 06:00:00 1 9 20151101061 6 2015 11
7 2015-11-01 07:00:00 1 8 20151101071 6 2015 11
8 2015-11-01 08:00:00 1 11 20151101081 6 2015 11
9 2015-11-01 09:00:00 1 12 20151101091 6 2015 11

Day Hour Week Quarter IsWeekend


0 1 0 44 4 True
1 1 1 44 4 True
2 1 2 44 4 True
3 1 3 44 4 True
4 1 4 44 4 True
5 1 5 44 4 True
6 1 6 44 4 True
7 1 7 44 4 True
8 1 8 44 4 True
9 1 9 44 4 True

Les valeurs manquantes


nous allons dans cette étape vérifier si il existe des valeurs manquantes dans le dataset, pour qu’on
les enlève si ils existent :

[ ]: def fill_missing_values(data, fill_value, fill_types, columns, dataframe_name):

print("Missing Values BEFORE REMOVAL in ",dataframe_name," data")


display((data.isnull().sum()).sum())

3
if (data.isnull().sum()).sum() != 0 :

for column in columns :

# Fill Missing Values with Specific Value :


if "Value_Fill" in fill_types :
data[ column ] = data[ column ].fillna(fill_value)

# Fill Missing Values with Forward Fill (Previous Row Value as␣
Current Row in Table) :
,→

if "Forward_Fill" in fill_types :
data[ column ] = data[ column ].ffill(axis = 0)

# Fill Missing Values with Backward Fill (Next Row Value as Current␣
,→Row in Table) :

if "Backward_Fill" in fill_types :
data[ column ] = data[ column ].bfill(axis = 0)

print("Missing Values AFTER REMOVAL in ",dataframe_name," data")


display(data.isnull().sum())

return data

fill_types = [ "Forward_Fill"]
fill_value = 0

train = fill_missing_values(data, fill_value, fill_types, data.columns,"train")

Missing Values BEFORE REMOVAL in train data


0

[ ]: # verification du nombre des valeurs manquantes par variable

data.isna().sum()

[ ]: DateTime 0
Junction 0
Vehicles 0
ID 0
Weekday 0
Year 0
Month 0
Day 0
Hour 0
Week 0

4
Quarter 0
IsWeekend 0
dtype: int64

Il existe pas des valeurs manquantes dans notre dataset


Separation des quatres junctions

[ ]: junction_1 = data[data["Junction"]==1]
junction_2 = data[data["Junction"]==2]
junction_3 = data[data["Junction"]==3]
junction_4 = data[data["Junction"]==4]

junctions = [junction_1, junction_2, junction_3, junction_4]

statistique descriptive

[ ]: i = 1

for junction in junctions:

print("Junction",i,"\n")
print("Std: ",junction['Vehicles'].std())
print("Skewness: ",junction['Vehicles'].skew())
print("Kurtosis: ",junction['Vehicles'].kurt(),"\n\n")
print("Mean: ",junction['Vehicles'].mean())
print("Median: ",junction['Vehicles'].median())
print("Min: ",junction['Vehicles'].min())
print("Max: ",junction['Vehicles'].max() , end='\n')
print("___", end='\n')

i+=1

Junction 1

Std: 23.0083451392927
Skewness: 0.8012975778113662
Kurtosis: 0.19188209363777453

Mean: 45.05290570175438
Median: 40.0
Min: 5
Max: 156
___

5
Junction 2

Std: 7.401306635266316
Skewness: 1.2873364898588882
Kurtosis: 1.783596555414265

Mean: 14.253220942982455
Median: 13.0
Min: 1
Max: 48
___
Junction 3

Std: 10.436005075710996
Skewness: 3.490206183724324
Kurtosis: 27.36437504960055

Mean: 13.694010416666666
Median: 11.0
Min: 1
Max: 180
___
Junction 4

Std: 3.5214545458330613
Skewness: 1.3326967458513121
Kurtosis: 4.737325243604408

Mean: 7.251611418047882
Median: 7.0
Min: 1
Max: 36
___
Junction 1:
Moyenne (Mean): Environ 45.05 véhicules par heure.—- Médiane (Median): 40 véhicules par
heure.—- Écart-type (Std): Environ 23.01, indiquant une certaine variabilité.— Skewness: 0.80,
ce qui suggère une légère asymétrie positive.— Kurtosis: 0.19, indiquant une légère pointe.—-
Junction 2:
Moyenne (Mean): Environ 14.25 véhicules par heure.—- Médiane (Median): 13 véhicules par
heure.—– Écart-type (Std): Environ 7.40, montrant une variabilité modérée.—– Skewness: 1.29, in-
diquant une asymétrie positive plus prononcée.—- Kurtosis: 1.78, montrant une pointe plus pronon-
cée.

6
Junction 3
Moyenne (Mean): Environ 13.69 véhicules par heure.—- Médiane (Median): 11 véhicules par
heure.—- Écart-type (Std): Environ 10.44, indiquant une variabilité plus importante.—– Skew-
ness: 3.49, montrant une asymétrie positive très prononcée.—– Kurtosis: 27.36, indiquant une
pointe extrêmement prononcée.
Junction 4:
Moyenne (Mean): Environ 7.25 véhicules par heure.——- Médiane (Median): 7 véhicules par
heure.—— Écart-type (Std): Environ 3.52, montrant une faible variabilité.—— Skewness: 1.33,
indiquant une asymétrie positive prononcée.——- Kurtosis: 4.74, montrant une pointe modérée.
Tendances des junctions
Évolution du trafic au fil du temps
Nous visualisons ici comment le trafic évolue dans chacun de nos junctions au fil du temps. Chaque
graphique représente le nombre de véhicules observés, avec une courbe pour les données réelles
et une autre pour la moyenne mobile sur une fenêtre de 30 jours. Cela nous permet de voir les
tendances générales du trafic en atténuant les variations à court terme. Ces graphiques nous aident
à comprendre comment le trafic fluctue dans nos carrefours au fil des années et des mois.

[ ]: sns.set(rc={'figure.figsize':(18, 10)})

[ ]: for index,junction in enumerate(junctions):


plt.title('Junction %d' %(index+1))
plt.plot(junction.DateTime, junction.Vehicles)
plt.plot(junction.DateTime, junction.Vehicles.rolling(720, min_periods=1).
,→mean(), label = "Window Mean")

plt.xlabel("Year/Month")
plt.ylabel("Vehicles")
plt.legend()
plt.show()

7
8
• Une série temporelle est considérée comme stationnaire lorsqu’elle ne présente pas de ten-
dance claire. Cependant, dans notre analyse exploratoire des données, nous avons identifié
une saisonnalité hebdomadaire ainsi qu’une tendance à la hausse au fil des années. Cette
observation est confirmée par le graphique ci-dessus, soulignant spécifiquement une tendance
à la hausse pour les carrefours 1 et 2. Pour mieux visualiser la saisonnalité hebdomadaire,

9
il serait bénéfique de limiter la période d’observation. Cela nous permettra d’analyser plus
précisément les variations sur une base hebdomadaire.
Exemple decomposition pour la junction 1

[ ]: #Extraction des données de la série temporelle :


#On extrait la colonne des véhicules du carrefour 1 (junction_1) pour créer␣
,→notre série temporelle.

time_series_data = junction_1.Vehicles

#Décomposition saisonnière additive :

result = sm.tsa.seasonal_decompose(time_series_data, model='additive', period=24)

#On utilise la fonction seasonal_decompose de statsmodels.tsa pour décomposer la␣


,→série temporelle en composantes additive, telles que la tendance, la␣

,→saisonnalité et le résidu.

#Le paramètre model='additive' spécifie que la décomposition doit être additive,␣


,→et le paramètre period=24 indique une saisonnalité de 24 heures (un jour).

result.plot()

#On affiche les composantes de la décomposition (tendance, saisonnalité, résidu)␣


,→dans un seul graphique.

[ ]:

10
• Cette décomposition permet d’analyser visuellement la tendance et la saisonnalité présentes
dans la série temporelle de la jonction 1.
• La période saisonnière de 24 heures suggère que la série temporelle présente une saisonnalité
quotidienne.
• La composante de tendance montre que la série temporelle présente une tendance globale à la
hausse
• La visualisation de ces composantes peut aider à mieux comprendre la structure temporelle
des données et à prendre des décisions éclairées pour la modélisation ultérieure.

[ ]:

Analyse de la tendance, saisonalité et le résidu pour chaque junction :


[ ]: from statsmodels.tsa.seasonal import seasonal_decompose

junctions_tuple = data.groupby('Junction')

for junction_id, junction_data in junctions_tuple: # Trend analysis and noise␣


,→examination for each junction

# Sort the data


junction_data['DateTime'] = pd.to_datetime(junction_data['DateTime'])
junction_data = junction_data.sort_values(by='DateTime')

x = np.arange(len(junction_data))
x = sm.add_constant(x)

11
model = sm.OLS(junction_data['Vehicles'], x).fit()
trend_slope = model.params[1]
trend_intercept = model.params[0]

detrended_data = junction_data['Vehicles'] - (trend_slope * x[:, 1] +␣


,→trend_intercept)

plt.figure(figsize=(15, 4))
plt.subplot(1, 2, 1)
plt.plot(junction_data['DateTime'], junction_data['Vehicles'], label='Data')
plt.plot(junction_data['DateTime'], trend_slope * x[:, 1] + trend_intercept,␣
,→label='Trend', color='red')

plt.xlabel('Date')
plt.ylabel('Vehicle Count')
plt.title(f'Junction {junction_id} - Trend Analysis')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(junction_data['DateTime'], detrended_data, label='Noise',␣
,→color='green')

plt.xlabel('Date')
plt.ylabel('Noise')
plt.title(f'Junction {junction_id} - Noise Examination')
plt.legend()

seasonal_period = 24*90
decomposition = seasonal_decompose(junction_data['Vehicles'],␣
,→period=seasonal_period)

seasonal_component = decomposition.seasonal

# plt.figure(figsize=(15, 4))
# plt.plot(junction_data['DateTime'], seasonal_component, label='Seasonal␣
,→Component', color='blue')

# plt.xlabel('Date')
# plt.ylabel('Seasonal Component')
# plt.title(f'Junction {junction_id} - Seasonal Component Analysis')
# plt.legend()

plt.tight_layout()
plt.show()

seasonal_mean = np.mean(seasonal_component)
seasonal_std = np.std(seasonal_component)
print(f'Junction {junction_id} - Seasonal Mean: {seasonal_mean}')
print(f'Junction {junction_id} - Seasonal Standard Deviation:␣
,→{seasonal_std}')

12
mean_noise = np.mean(detrended_data)
std_noise = np.std(detrended_data)
print(f'Junction {junction_id} - Trend Slope: {trend_slope}')
print(f'Junction {junction_id} - Trend Intercept: {trend_intercept}')
print(f'Junction {junction_id} - Mean Noise: {mean_noise}')
print(f'Junction {junction_id} - Standard Deviation of Noise: {std_noise}')

Junction 1 - Seasonal Mean: -0.011485126264292956


Junction 1 - Seasonal Standard Deviation: 11.768711286309003
Junction 1 - Trend Slope: 0.0038097546911437423
Junction 1 - Trend Intercept: 17.25884035251518
Junction 1 - Mean Noise: 3.470128667845928e-14
Junction 1 - Standard Deviation of Noise: 16.486631390087474

Junction 2 - Seasonal Mean: 0.005418854279004389


Junction 2 - Seasonal Standard Deviation: 2.940924401289598
Junction 2 - Trend Slope: 0.0011630210418028587
Junction 2 - Trend Intercept: 5.768400932509694
Junction 2 - Mean Noise: 6.544472566211449e-15
Junction 2 - Standard Deviation of Noise: 5.547513523900226

13
Junction 3 - Seasonal Mean: -0.10288614422958331
Junction 3 - Seasonal Standard Deviation: 6.525518378313682
Junction 3 - Trend Slope: 0.0007725622288921175
Junction 3 - Trend Intercept: 8.057782675784228
Junction 3 - Mean Noise: -4.549966641270817e-15
Junction 3 - Standard Deviation of Noise: 9.915254195083062

Junction 4 - Seasonal Mean: -0.00527349019554905


Junction 4 - Seasonal Standard Deviation: 3.047149307750716
Junction 4 - Trend Slope: -1.5700269074005267e-05
Junction 4 - Trend Intercept: 7.285704552342071
Junction 4 - Mean Noise: 1.3896802677296986e-14
Junction 4 - Standard Deviation of Noise: 3.5209941540547938
$$
$$
Pour la junction 2 par exemple :
• Trend Slope (Pente de la tendance) - 0.0012 :
La pente de la tendance indique la variation à long terme de la série temporelle.
Une pente positive suggère une augmentation du nombre de véhicules au fil du temps, tandis qu’une
pente négative suggère une diminution.
La valeur de 0.0012 pour Junction 2 suggère une tendance à la hausse, bien que cette augmentation
soit lente.

14
• Trend Intercept (Interception de la tendance) - 5.7684 :
L’interception de la tendance représente la valeur initiale de la série temporelle.
Pour Junction 2, cette valeur de départ est d’environ 5.7684, indiquant le point de départ de la série
temporelle.
• Mean Noise (Moyenne du bruit) - Proche de 0 :
La moyenne du bruit représente la moyenne statistique des fluctuations imprévisibles dans les don-
nées.
Une moyenne proche de zéro indique que le bruit est centré autour de la moyenne globale des
données.
• Standard Deviation of Noise (Écart type du bruit) - 5.5475 :
L’écart type mesure la variabilité des données.
La valeur d’environ 5.5475 indique que le bruit présente une certaine variabilité au-dessus et en
dessous de la moyenne.
$$
Nous traçons la distribution du nombre de véhicules à chaque intersection sous la forme
d’un histogramme (graphique de distribution de fréquence).

[ ]: fig, axs = plt.subplots(2,2)


for i,j in enumerate(junctions):
print("Junction #", i+1)
weekday = j[j["Weekday"] < 5].Vehicles.mean()
weekend = j[j["Weekday"] >= 5].Vehicles.mean()

labels = ['Weekday', 'Weekend']


sizes = [weekday, weekend]

axs[int(i>=2),i%2].pie(sizes, labels=labels, autopct='%1.0f%%')


axs[int(i>=2),i%2].axis('equal')
axs[int(i>=2),i%2].title.set_text("Junction %d" %(i+1))
#plt.show()

print("weekday vehicle mean",j[j["Weekday"] < 5].Vehicles.mean())


print("weekend vehicle mean", j[j["Weekday"] >= 5].Vehicles.mean())

print()

Junction # 1
weekday vehicle mean 49.76685823754789
weekend vehicle mean 33.19990366088632

Junction # 2
weekday vehicle mean 15.834291187739463
weekend vehicle mean 10.277697495183045

15
Junction # 3
weekday vehicle mean 13.793199233716475
weekend vehicle mean 13.44460500963391

Junction # 4
weekday vehicle mean 7.648397435897436
weekend vehicle mean 6.240196078431373

La comparaison des nombres de véhicules en semaine et le week-end à chaque intersection


montre une tendance : généralement, il y a plus de véhicules en semaine que le week-end.
Néanmoins, la différence entre les deux est moins marquée à la jonction 3.
Analyse de la corrélation

[ ]: for i,j in enumerate(junctions):


junction_map = j[["Weekday","Hour","Vehicles"]]
junction_map = junction_map.pivot_table(index='Weekday', columns='Hour',␣
,→aggfunc=sum)

Cols = range(24)
Index = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]
junction_map = pd.DataFrame(data=junction_map.to_numpy(),index=Index,␣
,→columns=Cols)

ax = sns.heatmap(junction_map,cmap="Reds")
ax.set(xlabel='Hour', ylabel='Weekday')
ax.set_title('Hour-Weekday Relation of Junction %d'%(i+1))

16
ax.invert_yaxis()
plt.show()

17
18
Pour la Jonction 1 et 2,de 10h à 23h, les colonnes présentent une intensité de couleur très marquée
tout au long de la semaine, à l’exception du week-end. Cette forte concentration de rouge suggère
une activité significative de circulation pendant ces heures spécifiques en semaine. La diminution
notable du rouge pendant le week-end pourrait indiquer une réduction des déplacements ou une
dynamique différente de trafic pendant cette période.
Pour la Jonction 3 ,un pic significatif de trafic est observé le samedi entre 19h et 23h, représenté
par des colonnes très rouges. En comparaison, les autres jours montrent une activité modérée, avec
une diminution notable entre 1h et 9h du matin.
la Jonction 4 montre une activité routière constante et élevée tout au long de la journée, avec une
diminution marquée tôt le matin entre 3 heures et 8 heures du matin.

[ ]:

Nous calculons l’autocorrélation du nombre de véhicules à chaque intersection, puis


nous traçons le graphique d’autocorrélation.

[ ]: corr = data.corr()
f, ax = plt.subplots(figsize=(16, 7))
sns.heatmap(corr, annot=True, fmt='.2f', vmin=-1, vmax=1, square=True,␣
,→linewidths=1)

f.suptitle('Correlation entre les attributs', fontsize=20)


plt.show()

<ipython-input-26-75d069209dab>:1: FutureWarning:

The default value of numeric_only in DataFrame.corr is deprecated. In a future


version, it will default to False. Select only valid columns or specify the
value of numeric_only to silence this warning.

19
Cette visualisation offre un aperçu visuel des corrélations entre les variables du notre dataset.
Des valeurs proches de 1 indiquent une forte corrélation positive, telles que la corrélation de 0,97
entre “month” et “quarter”, suggérant une relation linéaire positive presque parfaite entre ces deux
variables. À l’inverse, des valeurs proches de -1, comme la corrélation de -0,61 entre “junction”
et “vehicles”, signalent une relation inverse. Cela peut signifier, par exemple, que le nombre de
véhicules a tendance à diminuer lorsque la jonction change. Des corrélations proches de 0 indiquent
une faible corrélation entre les variables.

2 Analyse de la variance
[ ]: !pip install scikit-posthocs

[ ]: from scipy.stats import kstest

# Test de normalité avec le test de Kolmogorov-Smirnov

20
stat, p_value = kstest(data['Vehicles'], 'norm')

# Affichage des résultats


print(f'Statistique de test : {stat}, P-valeur : {p_value}')

# Interprétation des résultats


alpha = 0.05
if p_value > alpha:
print("La distribution est normale (l'hypothèse nulle n'est pas rejetée)")
else:
print("La distribution n'est pas normale (l'hypothèse nulle est rejetée)")

Statistique de test : 0.9862436181778462, P-valeur : 0.0


La distribution n'est pas normale (l'hypothèse nulle est rejetée)
Test de normalité :

[ ]: from scipy.stats import kstest

# Test de normalité avec le test de Kolmogorov-Smirnov


stat, p_value = kstest(data['Vehicles'], 'norm')

# Affichage des résultats


print(f'Statistique de test : {stat}, P-valeur : {p_value}')

# Interprétation des résultats


alpha = 0.05
if p_value > alpha:
print("La distribution est normale (l'hypothèse nulle n'est pas rejetée)")
else:
print("La distribution n'est pas normale (l'hypothèse nulle est rejetée)")

Statistique de test : 0.9862436181778462, P-valeur : 0.0


La distribution n'est pas normale (l'hypothèse nulle est rejetée)
Test d’homogénité

[ ]: from scipy.stats import bartlett

# Test d'homogénéité de la variance


stat, p_value = bartlett(data['Vehicles'][data['Junction'] == 1],
data['Vehicles'][data['Junction'] == 2],
data['Vehicles'][data['Junction'] == 3],
data['Vehicles'][data['Junction'] == 4])

# Affichage des résultats


print(f'Statistique de test : {stat}, P-valeur : {p_value}')

# Interprétation des résultats

21
alpha = 0.05
if p_value > alpha:
print("Homogénéité des variances (l'hypothèse nulle n'est pas rejetée)")
else:
print("Non homogène des variances (l'hypothèse nulle est rejetée)")

Statistique de test : 28278.146763716646, P-valeur : 0.0


Non homogène des variances (l'hypothèse nulle est rejetée)
Étant donné que les conditions de normalité et d’homogénéité ne sont pas vérifiées, la solution
préconisée est le recours au test de Kruskal-Wallis. Ce dernier est fréquemment employé comme
alternative à l’ANOVA lorsque les hypothèses de normalité et d’homogénéité ne sont pas validés

[ ]: from scipy.stats import kruskal


from scikit_posthocs import posthoc_dunn

stat, p_value = kruskal(*[group['Vehicles'] for name, group in data.


,→groupby('Junction')])

print(f"Statistique de test : {stat}, P-value : {p_value}")

# Interprétation du test Kruskal-Wallis


if p_value < 0.05:
print("Il y a des différences significatives entre les groupes.")

# Test de Dunn pour les comparaisons post-hoc


results_dunn = posthoc_dunn(data, val_col='Vehicles', group_col='Junction')

# Affichage des résultats du test de Dunn


print(results_dunn)
else:
print("Il n'y a pas de différences significatives entre les groupes.")

Statistique de test : 25943.746706695038, P-value : 0.0


Il y a des différences significatives entre les groupes.
1 2 3 4
1 1.0 0.000000e+00 0.000000e+00 0.0
2 0.0 1.000000e+00 5.948263e-36 0.0
3 0.0 5.948263e-36 1.000000e+00 0.0
4 0.0 0.000000e+00 0.000000e+00 1.0
La statistique de test élevée (25943.75) associée à une p-value très proche de zéro (0.0) suggère que
le test de Kruskal-Wallis a détecté des différences significatives entre les groupes. Concrètement,
cela signifie que le nombre de véhicules par jonction varie de manière significative entre au moins
deux des jonctions.
Les résultats de la matrice de comparaison multiple de Dunn montrent que les valeurs hors diagonale
sont proches de zéro, indiquant une différence significative entre les groupes correspondants. Les
valeurs diagonales sont proches de 1, ce qui est attendu dans une matrice de ce type.

22
[ ]: import seaborn as sns
import matplotlib.pyplot as plt

# Supposons que 'data' est votre dataframe


plt.figure(figsize=(12, 8))
sns.boxplot(data=data, x='Junction', y='Vehicles')
plt.title('Distribution des véhicules par jonction')
plt.xlabel('Jonction')
plt.ylabel('Nombre de véhicules')
plt.show()

On observe que les boîtes des jonctions 2 et 4 ont une dispersion similaire avec des médianes proches
et des quartiles similaires, cela suggère en effet une similitude dans la distribution du nombre de
véhicules entre ces deux jonctions. le nombre de véhicules dans la jonction 1 est significativement
plus élevé que dans les autres jonctions, cela pourrait indiquer que la jonction 1 est plus fréquentée

3 Modélisation Prédictive :
A ce stade nous allons entrainer et tester notre modele de prediction, pour juger sa
validité pour des previsions futures

23
[ ]: #importons les biblothques necessaires

import xgboost as xgb


from sklearn.metrics import mean_squared_error, r2_score
color_pal = sns.color_palette()
plt.style.use('fivethirtyeight')

[ ]: df = pd.read_csv("/train_ML_IOT.csv")
df = df[df['Junction'] == 1]
df = df.drop(['Junction', 'ID'], axis=1)
df = df.set_index('DateTime')
df.index = pd.to_datetime(df.index)

– Suppression des colonnes inutiles (Junction, ID).


– Conversion de la colonne de date en index.

[ ]: df.plot(style='.',
figsize=(15, 5),
color=color_pal[0],
title='Traffic for Junction 1')
plt.show()

Analyse et suppression des valeurs aberrantes

[ ]: df['Vehicles'].plot(kind='hist', bins=500)

[ ]: <Axes: ylabel='Frequency'>

24
On observe une distribution asymétrique dans l’histogramme, suggérant que les données sur le
nombre de véhicules ne suivent pas une distribution parfaitement équilibrée. De plus, la présence
de barres isolées et d’occurrences très faibles au-delà de 130 indique la présence potentielle de valeurs
aberrantes.
On représente graphiquement les points de données où le nombre de véhicules est supérieur à 130

[ ]: df.query('Vehicles > 130')['Vehicles'] \


.plot(style='.',
figsize=(15, 5),
color=color_pal[5],
title='Outliers')

[ ]: <Axes: title={'center': 'Outliers'}, xlabel='DateTime'>

25
Nous identifions ici des points de données extrêmes , caractérisés par des valeurs nettement dif-
férentes de la tendance globale des données. Ces points, que l’on qualifie de valeurs aberrantes, se
distinguent par leur écart significatif par rapport à la tendance générale observée dans l’ensemble
des données.
On filtre les données pour exclure les lignes où le nombre de véhicules est égal ou supérieur à 130,
considérant ainsi les valeurs au-dessus de ce seuil comme des points de données aberrants.

[ ]: df = df.query('Vehicles < 130').copy()


df

[ ]: Vehicles
DateTime
2015-11-01 00:00:00 15
2015-11-01 01:00:00 13
2015-11-01 02:00:00 10
2015-11-01 03:00:00 7
2015-11-01 04:00:00 9
... ...
2017-06-30 19:00:00 105
2017-06-30 20:00:00 96
2017-06-30 21:00:00 90
2017-06-30 22:00:00 84
2017-06-30 23:00:00 78

[14580 rows x 1 columns]

[ ]:

Division des données en ensembles d’entraînement et de test :

Division des données avant et après le 31 mai 2017, et l’affichage de la division dans un graphique.

[ ]: train = df.loc[df.index < '2017-5-31']


test = df.loc[df.index >= '2017-5-31']

fig, ax = plt.subplots(figsize=(15, 5))


train.plot(ax=ax, label='Training Set', title='Data Train/Test Split')
test.plot(ax=ax, label='Test Set')
ax.axvline('2017-5-31', color='black', ls='--')
ax.legend(['Training Set', 'Test Set'])
plt.show()

26
[ ]:

Validation croisée temporelle (Time Series Cross Validation) : Nous utilisons TimeSeriesS-
plit pour diviser les données en plis temporels, et nous affichons ces plis.
Diviser un ensemble de données en seulement des ensembles d’entraînement et de test
peut entraîner le surajustement du modèle, car certaines données ne sont pas utilisées
dans l’entraînement. Dans ce cas, la validation croisée (CV) divise les données en
plusieurs sous-ensembles (plis) et utilise chaque sous-ensemble séquentiellement comme
ensemble de test. Ainsi, chaque élément de données participe à l’entraînement et aux
tests au moins une fois.

[ ]: from sklearn.model_selection import TimeSeriesSplit

tss = TimeSeriesSplit(n_splits=5, test_size=24*30*1, gap=24)


df = df.sort_index()

fig, axs = plt.subplots(5, 1, figsize=(15, 15), sharex=True)

fold = 0
for train_idx, val_idx in tss.split(df):
train = df.iloc[train_idx]
test = df.iloc[val_idx]
train['Vehicles'].plot(ax=axs[fold],
label='Training Set',
title=f'Data Train/Test Split Fold {fold}')
test['Vehicles'].plot(ax=axs[fold],
label='Test Set')
axs[fold].axvline(test.index.min(), color='black', ls='--')
fold += 1
plt.show()

27
test

[ ]: Vehicles
DateTime
2017-05-31 21:00:00 89
2017-05-31 22:00:00 85
2017-05-31 23:00:00 83
2017-06-01 00:00:00 80
2017-06-01 01:00:00 71
... ...
2017-06-30 19:00:00 105
2017-06-30 20:00:00 96
2017-06-30 21:00:00 90

28
2017-06-30 22:00:00 84
2017-06-30 23:00:00 78

[720 rows x 1 columns]

[ ]:

Création de caractéristiques :
L’augmentation des fonctionnalités en utilisant des dérivées de l’index temporel, comme l’heure, le
jour de la semaine, le trimestre, le mois, l’année, etc.
Nous ajoutons de nouvelles caractéristiques dérivées de l’index temporel pour enrichir nos données
et les utiliser dans la suite du travail.

[ ]: def create_features(df):
"""
Create time series features based on time series index.
"""
df = df.copy()
df['hour'] = df.index.hour
df['dayofweek'] = df.index.dayofweek
df['quarter'] = df.index.quarter
df['month'] = df.index.month
df['year'] = df.index.year
df['dayofyear'] = df.index.dayofyear
df['dayofmonth'] = df.index.day
df['weekofyear'] = df.index.isocalendar().week
return df

df = create_features(df)
df

[ ]: Vehicles hour dayofweek quarter month year \


DateTime
2015-11-01 00:00:00 15 0 6 4 11 2015
2015-11-01 01:00:00 13 1 6 4 11 2015
2015-11-01 02:00:00 10 2 6 4 11 2015
2015-11-01 03:00:00 7 3 6 4 11 2015
2015-11-01 04:00:00 9 4 6 4 11 2015
... ... ... ... ... ... ...
2017-06-30 19:00:00 105 19 4 2 6 2017
2017-06-30 20:00:00 96 20 4 2 6 2017
2017-06-30 21:00:00 90 21 4 2 6 2017
2017-06-30 22:00:00 84 22 4 2 6 2017
2017-06-30 23:00:00 78 23 4 2 6 2017

dayofyear dayofmonth weekofyear


DateTime

29
2015-11-01 00:00:00 305 1 44
2015-11-01 01:00:00 305 1 44
2015-11-01 02:00:00 305 1 44
2015-11-01 03:00:00 305 1 44
2015-11-01 04:00:00 305 1 44
... ... ... ...
2017-06-30 19:00:00 181 30 26
2017-06-30 20:00:00 181 30 26
2017-06-30 21:00:00 181 30 26
2017-06-30 22:00:00 181 30 26
2017-06-30 23:00:00 181 30 26

[14580 rows x 9 columns]

[ ]: def add_lags(df):
target_map = df['Vehicles'].to_dict() # target = Vehicles
df['lag1'] = (df.index - pd.Timedelta('24 hours')).map(target_map)
print(df['lag1'])
df['lag2'] = (df.index - pd.Timedelta('48 hours')).map(target_map)
print(df['lag2'])
df['lag3'] = (df.index - pd.Timedelta('72 hours')).map(target_map)
print(df['lag3'])
return df
target_map = df['Vehicles'].to_dict() # target = Vehicles
df = add_lags(df)

DateTime
2015-11-01 00:00:00 NaN
2015-11-01 01:00:00 NaN
2015-11-01 02:00:00 NaN
2015-11-01 03:00:00 NaN
2015-11-01 04:00:00 NaN
...
2017-06-30 19:00:00 113.0
2017-06-30 20:00:00 110.0
2017-06-30 21:00:00 101.0
2017-06-30 22:00:00 96.0
2017-06-30 23:00:00 95.0
Name: lag1, Length: 14580, dtype: float64
DateTime
2015-11-01 00:00:00 NaN
2015-11-01 01:00:00 NaN
2015-11-01 02:00:00 NaN
2015-11-01 03:00:00 NaN
2015-11-01 04:00:00 NaN
...
2017-06-30 19:00:00 124.0
2017-06-30 20:00:00 112.0

30
2017-06-30 21:00:00 102.0
2017-06-30 22:00:00 96.0
2017-06-30 23:00:00 96.0
Name: lag2, Length: 14580, dtype: float64
DateTime
2015-11-01 00:00:00 NaN
2015-11-01 01:00:00 NaN
2015-11-01 02:00:00 NaN
2015-11-01 03:00:00 NaN
2015-11-01 04:00:00 NaN
...
2017-06-30 19:00:00 124.0
2017-06-30 20:00:00 121.0
2017-06-30 21:00:00 111.0
2017-06-30 22:00:00 98.0
2017-06-30 23:00:00 98.0
Name: lag3, Length: 14580, dtype: float64
Prédiction avec le modele XGBOOST :

[ ]: tss = TimeSeriesSplit(n_splits=5, test_size=24*30*1, gap=24)


df = df.sort_index()

fold = 0
preds = []
scores = []
scores_r2 = []
for train_idx, val_idx in tss.split(df):
train = df.iloc[train_idx]
test = df.iloc[val_idx]

train = create_features(train)
test = create_features(test)

FEATURES = ['dayofyear', 'hour', 'dayofweek', 'quarter', 'month','year',


'lag1','lag2','lag3']
TARGET = 'Vehicles'

X_train = train[FEATURES]
y_train = train[TARGET]

X_test = test[FEATURES]
y_test = test[TARGET]

reg = xgb.XGBRegressor(base_score=0.5, booster='gbtree',


n_estimators=1000,
early_stopping_rounds=50,

31
objective='reg:linear',
max_depth=3,
learning_rate=0.01)
reg.fit(X_train, y_train,
eval_set=[(X_train, y_train), (X_test, y_test)],
verbose=100)

y_pred = reg.predict(X_test)
preds.append(y_pred)
score = np.sqrt(mean_squared_error(y_test, y_pred))
score_r2 = r2_score(y_test, y_pred)
scores_r2.append(score_r2)
scores.append(score)

[0] validation_0-rmse:41.17813 validation_1-rmse:64.43353


[100] validation_0-rmse:16.73642 validation_1-rmse:29.89627
/usr/local/lib/python3.10/dist-packages/xgboost/core.py:160: UserWarning:
[23:26:25] WARNING: /workspace/src/objective/regression_obj.cu:209: reg:linear
is now deprecated in favor of reg:squarederror.
warnings.warn(smsg, UserWarning)
[200] validation_0-rmse:8.64672 validation_1-rmse:17.91418
[300] validation_0-rmse:6.37026 validation_1-rmse:12.62657
[400] validation_0-rmse:5.78139 validation_1-rmse:10.36744
[500] validation_0-rmse:5.51833 validation_1-rmse:9.42855
[600] validation_0-rmse:5.37321 validation_1-rmse:9.06784
[700] validation_0-rmse:5.27924 validation_1-rmse:8.88717
[800] validation_0-rmse:5.19827 validation_1-rmse:8.78335
[900] validation_0-rmse:5.10939 validation_1-rmse:8.61761
[999] validation_0-rmse:5.03971 validation_1-rmse:8.44660
[0] validation_0-rmse:43.00402 validation_1-rmse:64.49702
[100] validation_0-rmse:17.47720 validation_1-rmse:28.23096
/usr/local/lib/python3.10/dist-packages/xgboost/core.py:160: UserWarning:
[23:26:26] WARNING: /workspace/src/objective/regression_obj.cu:209: reg:linear
is now deprecated in favor of reg:squarederror.
warnings.warn(smsg, UserWarning)
[200] validation_0-rmse:8.98370 validation_1-rmse:14.59623
[300] validation_0-rmse:6.55469 validation_1-rmse:9.26452
[400] validation_0-rmse:5.90481 validation_1-rmse:7.77447
[500] validation_0-rmse:5.63825 validation_1-rmse:7.16436
[600] validation_0-rmse:5.52072 validation_1-rmse:6.97043
[700] validation_0-rmse:5.43446 validation_1-rmse:6.85201
[800] validation_0-rmse:5.33015 validation_1-rmse:6.73539
[900] validation_0-rmse:5.24026 validation_1-rmse:6.65030
[999] validation_0-rmse:5.16310 validation_1-rmse:6.59840
[0] validation_0-rmse:44.57756 validation_1-rmse:65.15572
[100] validation_0-rmse:18.06145 validation_1-rmse:28.20552

32
/usr/local/lib/python3.10/dist-packages/xgboost/core.py:160: UserWarning:
[23:26:28] WARNING: /workspace/src/objective/regression_obj.cu:209: reg:linear
is now deprecated in favor of reg:squarederror.
warnings.warn(smsg, UserWarning)
[200] validation_0-rmse:9.20359 validation_1-rmse:14.63685
[300] validation_0-rmse:6.65322 validation_1-rmse:9.42352
[400] validation_0-rmse:5.99378 validation_1-rmse:8.12358
[500] validation_0-rmse:5.74498 validation_1-rmse:7.67068
[600] validation_0-rmse:5.62773 validation_1-rmse:7.49401
[700] validation_0-rmse:5.53594 validation_1-rmse:7.39162
[800] validation_0-rmse:5.43512 validation_1-rmse:7.32979
[900] validation_0-rmse:5.34247 validation_1-rmse:7.25516
[999] validation_0-rmse:5.26145 validation_1-rmse:7.20366
[0] validation_0-rmse:45.94239 validation_1-rmse:72.50179
[100] validation_0-rmse:18.56933 validation_1-rmse:34.43908
/usr/local/lib/python3.10/dist-packages/xgboost/core.py:160: UserWarning:
[23:26:34] WARNING: /workspace/src/objective/regression_obj.cu:209: reg:linear
is now deprecated in favor of reg:squarederror.
warnings.warn(smsg, UserWarning)
[200] validation_0-rmse:9.39631 validation_1-rmse:20.44075
[300] validation_0-rmse:6.73683 validation_1-rmse:14.85433
[400] validation_0-rmse:6.08245 validation_1-rmse:12.82178
[500] validation_0-rmse:5.84305 validation_1-rmse:11.95146
[600] validation_0-rmse:5.72337 validation_1-rmse:11.48660
[700] validation_0-rmse:5.63102 validation_1-rmse:11.20053
[800] validation_0-rmse:5.53206 validation_1-rmse:11.18978
[900] validation_0-rmse:5.43868 validation_1-rmse:11.15002
[990] validation_0-rmse:5.36324 validation_1-rmse:11.15003
[0] validation_0-rmse:47.61980 validation_1-rmse:76.87245
[100] validation_0-rmse:19.29965 validation_1-rmse:36.65566
/usr/local/lib/python3.10/dist-packages/xgboost/core.py:160: UserWarning:
[23:26:36] WARNING: /workspace/src/objective/regression_obj.cu:209: reg:linear
is now deprecated in favor of reg:squarederror.
warnings.warn(smsg, UserWarning)
[200] validation_0-rmse:9.79066 validation_1-rmse:21.45403
[300] validation_0-rmse:7.05655 validation_1-rmse:14.20530
[400] validation_0-rmse:6.32166 validation_1-rmse:11.21340
[500] validation_0-rmse:6.04414 validation_1-rmse:9.49877
[600] validation_0-rmse:5.90016 validation_1-rmse:8.89502
[700] validation_0-rmse:5.79814 validation_1-rmse:8.58045
[800] validation_0-rmse:5.70837 validation_1-rmse:8.37385
[900] validation_0-rmse:5.62641 validation_1-rmse:8.22018
[999] validation_0-rmse:5.55078 validation_1-rmse:8.10271

33
[ ]: test['prediction'] = y_pred
y_test_ = pd.DataFrame(y_test)
y_test_ = y_test_.merge(test[['prediction']], how='left', left_index=True,␣
,→right_index=True)

ax = y_test_[['Vehicles']].plot(figsize=(15, 5))
y_test_['prediction'].plot(ax=ax, style='.')
plt.legend(['Truth Data', 'Predictions'])
ax.set_title('Raw Dat and Prediction')
plt.show()

[ ]: y_test_['prediction'] = y_pred
ax = y_test_[['Vehicles', 'prediction']].plot(figsize=(15, 5))
plt.legend(['Truth Data', 'Predictions'])
ax.set_title('Raw Data and Prediction')
plt.show()

[ ]: print(f'Score across folds {np.mean(scores):0.4f}', end='\n')


print(f'Fold scores:{scores}', end='\n')

34
print(f'R2 Score across folds {scores_r2}', end='\n')
print(f'R2 Score across folds {np.mean(scores_r2):0.4f}', end='\n')

Score across folds 8.2930


Fold scores:[8.445002410307508, 6.59763653222706, 7.20365522520615,
11.116090793287341, 8.102711036625315]
R2 Score across folds [0.8088785267367985, 0.8838339727436686,
0.8759912249785613, 0.8012358522492912, 0.909290422835531]
R2 Score across folds 0.8558
Le score moyen de la performance du modèle XGBoost à travers les différentes validations croisées
basées sur le temps est de 8.2930, avec des scores individuels pour chaque pli comme suit : [8.4450,
6.5976, 7.2037, 11.1161, 8.1027]. Ces scores représentent la racine carrée de l’erreur quadratique
moyenne (RMSE), une mesure de la précision des prédictions du modèle. Un score plus bas indique
une meilleure performance.
En ce qui concerne le coefficient de détermination (R2), qui mesure la proportion de la variance de
la variable dépendante expliquée par le modèle, les scores individuels pour chaque pli sont [0.8089,
0.8838, 0.8760, 0.8012, 0.9093]. Le R2 moyen à travers les plis est de 0.8558. Un R2 proche
de 1 indique une bonne adéquation du modèle aux données.
En conséquence, ce modèle XGBoost peut être retenu pour effectuer des prévisions
des mois prochains, pour cette junction
Pour les autres junctions on a trouvé relativement les mêmes résultats
voici les résultats pour la junction 2 Qui sont Similaire à la junction 1
d’ou la validité du modele aussi pour cette junction

35
KHAWLA_REGRESSION

December 3, 2023

La deuxième méthode: Random Forest Regression et Decision Tree Regression


La deuxième méthode que nous avons utilisée comprend à la fois la régression par forêt aléatoire
(Random Forest Regression) et la régression par arbre de décision (Decision Tree Regression).
Nous nous concentrons exclusivement sur les jonctions 1 et 2 en utilisant différentes tailles de
fenêtre(windows sizes), à savoir 12, 24, 48, 72 et 168 heures. Les tailles de fenêtre représentent la
période sur laquelle le modèle effectue ses prédictions. Nous avons généré des graphiques distincts
pour chaque régression afin d’observer comment les modèles se comportent avec ces différentes
fenêtres de temps.

[ ]: import numpy as np
import pandas as pd

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
for filename in filenames:
print(os.path.join(dirname, filename))

[ ]: import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import missingno as msno
import seaborn as sns
from datetime import datetime
from scipy.stats import skew
from scipy.stats import kurtosis
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score

[ ]: train = pd.read_csv("/train_ML_IOT.csv")

[ ]: j1=train[train["Junction"]==1].Vehicles.to_numpy()
j2=train[train["Junction"]==2].Vehicles.to_numpy()
js = [j1, j2]

[ ]: def create_dataset(dataset, look_back=1):


X, Y = [], []

1
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
X.append(a)
Y.append(dataset[i + look_back, 0])
return np.array(X), np.array(Y)

[ ]: def data_prep(j, look_back):


train_size = int(len(j) * 0.85)
test_size = len(j) - train_size
j_train, j_test = j[0:train_size,:], j[train_size:len(j),:]

X_train, Y_train = create_dataset(j_train, look_back)


X_test, Y_test = create_dataset(j_test, look_back)
X_train = np.reshape(X_train, (X_train.shape[0], 1, X_train.shape[1]))
X_test = np.reshape(X_test, (X_test.shape[0], 1, X_test.shape[1]))

rows_train = X_train.shape[0]
cols_train = X_train.shape[2]
rows_test = X_test.shape[0]
cols_test = X_test.shape[2]

X_train = X_train.reshape(rows_train, cols_train)


X_test = X_test.reshape(rows_test, cols_test)

return X_train, Y_train, X_test, Y_test

[ ]: from sklearn.metrics import mean_squared_error


from sklearn.metrics import mean_absolute_error

[ ]: def feature_extraction(X):
length = X.shape[0]
feature_min = np.min(X, axis=1).reshape(length,1)
feature_max = np.max(X, axis=1).reshape(length,1)
feature_median = np.median(X, axis=1).reshape(length,1)
feature_mean = np.mean(X, axis=1).reshape(length,1)
feature_var = np.var(X, axis=1).reshape(length,1)
feature_energy = np.sum(X**2, axis=1).reshape(length,1)
feature_skew = skew(X, axis=1).reshape(length,1)
feature_kurt = kurtosis(X, axis=1).reshape(length,1)

out = np.concatenate((feature_min, feature_max,feature_median,


feature_mean, feature_var, feature_energy,
feature_skew, feature_kurt),axis=1)
return out

[ ]: from sklearn.ensemble import RandomForestRegressor


from sklearn.tree import DecisionTreeRegressor

2
[ ]: look_back = [12,24,48,72,168]

[ ]: mae_mat_forest = np.zeros((len(js),len(look_back)))
mape_mat_forest = np.zeros((len(js),len(look_back)))
r2_mat_forest = np.zeros((len(js),len(look_back)))

mae_mat_tree = np.zeros((len(js),len(look_back)))
mape_mat_tree = np.zeros((len(js),len(look_back)))
r2_mat_tree = np.zeros((len(js),len(look_back)))

[ ]: for i,j in enumerate(js):


scaler = StandardScaler()
j = j.reshape(-1,1)
j = scaler.fit_transform(j)
for k,m in enumerate(look_back):
X_train, Y_train, X_test, Y_test = data_prep(j, m)
X_train_feat = feature_extraction(X_train)
X_test_feat = feature_extraction(X_test)

regressor = RandomForestRegressor(n_estimators = 100, random_state = 0)


regressor.fit(X_train_feat, Y_train)

Y_pred = regressor.predict(X_test_feat)
Y_pred = scaler.inverse_transform(Y_pred.reshape(-1,1)).reshape(-1)
Y_test = scaler.inverse_transform(Y_test.reshape(-1,1)).reshape(-1)

sns.set(rc={'figure.figsize':(16, 4)})
plt.title('Random Forest Regressor for Junction ' + str(i+1) + " |␣
↪Window Size = " + str(m))

mae_mat_forest[i][k] = mean_absolute_error(Y_test, Y_pred)


mape_mat_forest[i][k] = np.mean(np.abs((Y_test - Y_pred)/Y_test))*100
r2_mat_forest[i][k] = r2_score(Y_test, Y_pred)

plt.plot(Y_test[0:200], label = "actual")


plt.plot(Y_pred[0:200], label = "prediction")
plt.legend()
plt.show()

tree_regressor = DecisionTreeRegressor(max_depth=5)
tree_regressor.fit(X_train_feat, Y_train)
Y_pred = tree_regressor.predict(X_test_feat)
Y_pred = scaler.inverse_transform(Y_pred.reshape(-1,1)).reshape(-1)

mae_mat_tree[i][k] = mean_absolute_error(Y_test, Y_pred)

3
mape_mat_tree[i][k] = np.mean(np.abs((Y_test - Y_pred)/Y_test))*100
r2_mat_tree[i][k] = r2_score(Y_test, Y_pred)

sns.set(rc={'figure.figsize':(16, 4)})
plt.title('Decision Tree Regressor for Junction ' + str(i+1) + " |␣
↪Window Size = " + str(m))

plt.plot(Y_test[0:200], label="actual")
plt.plot(Y_pred[2:202], label="prediction")
plt.legend()
plt.show()

4
5
6
7
8
On calcule R2 et l’erreur absolue moyenne (MAE) pour la régression par forêt aléatoire (Random
Forest Regression)

[ ]: fig, axs = plt.subplots(3, 1,figsize=(10,10))


axs[0].axis("off")
axs[0].title.set_text('Mean Absolute Error (Random Forest)')
the_table = axs[0].table(cellText=mae_mat_forest, loc='center right',
rowLabels=[" 1 "," 2 "],colLabels=look_back)
#the_table.scale(2, 2)

axs[1].axis("off")
axs[1].title.set_text('Mean Absolute Percentage Error (Random Forest)')
the_table = axs[1].table(cellText=mape_mat_forest, loc='center right',
rowLabels=[" 1 "," 2 "],colLabels=look_back)
#the_table.scale(2, 2)

axs[2].axis("off")
axs[2].title.set_text('R2 score (Random Forest)')
the_table = axs[2].table(cellText=r2_mat_forest, loc='center right',
rowLabels=[" 1 "," 2 "],colLabels=look_back)
#the_table.scale(2, 2)

plt.show()

9
Pour la jonction 1, le Mean Absolute Error (MAE) augmente légèrement à mesure que la fenêtre
temporelle augmente, passant de 17 à 22. Cela suggère que pour cette jonction, des fenêtres
temporelles plus courtes peuvent être plus appropriées, mais la performance reste relativement
stable.
Pour la jonction 2, le MAE reste assez constant autour de 6-7, indépendamment de la taille de la
fenêtre temporelle. Cela peut indiquer que cette jonction est moins sensible à la variation de la
fenêtre temporelle et que le modèle maintient une performance cohérente.
Le coefficient de détermination R2 pour les jonctions 1 et 2 est proche de zéro, cela suggère que
le modèle a du mal à expliquer les données. Les prédictions du modèle ne concordent pas de
manière significative avec les observations réelles, indiquant une performance limitée du modèle
pour représenter la variabilité des données.
On calcule R2 et l’erreur absolue moyenne (MAE) pour la régression par arbre de décision (Decision

10
Tree Regression)

[ ]: fig, axs = plt.subplots(3, 1,figsize=(10,10))


axs[0].axis("off")
axs[0].title.set_text('Mean Absolute Error (Decision Tree)')
the_table = axs[0].table(cellText=mae_mat_tree, loc='center right',
rowLabels=[" 1 "," 2 "],colLabels=look_back)
#the_table.scale(2, 2)

axs[1].axis("off")
axs[1].title.set_text('Mean Absolute Percentage Error (Decision Tree)')
the_table = axs[1].table(cellText=mape_mat_tree, loc='center right',
rowLabels=[" 1 "," 2 "],colLabels=look_back)
#the_table.scale(2, 2)

axs[2].axis("off")
axs[2].title.set_text('R2 score (Decision Tree)')
the_table = axs[2].table(cellText=r2_mat_tree, loc='center right',
rowLabels=[" 1 "," 2 "],colLabels=look_back)
#the_table.scale(2, 2)

plt.show()

11
De même pour ce cas, le coefficient de détermination R2 est très proche de zéro pour la régression
par arbre de décision suggère que ce modèle spécifique éprouve des difficultés à expliquer les données.
Les prédictions générées par l’arbre de décision ne s’alignent pas de manière significative avec les
observations réelles, ce qui indique une faible performance prédictive.
Conclusion:
En conclusion, après avoir comparé trois modèles - XGBoost, la régression par arbre de décision et
la régression par forêt aléatoire , il est observé que XGBoost se distingue comme le modèle le plus
performant pour faire les prévisions.

12

Vous aimerez peut-être aussi