fix report.gj
[talweg.git] / reports / report.gj
CommitLineData
8eafefbc 1-----
b6233fa6
BA
2# Package R "talweg"
3
4Le package $-$ Time-series sAmpLes forecasted With ExoGenous variables $-$ contient le
5code permettant de (re)lancer les expériences numériques décrites dans cette partie et la
6suivante. Les fonctions principales sont respectivement
7
8 * **getData()** pour construire un objet R contenant les données à partir de fichiers
9CSV (extraits de bases de données). Le format choisi en R est une classe R6 (du package
10du même nom) exposant en particulier les méthodes *getSerie(i)* et *getExo(i)* qui
11renvoient respectivement la $i^{eme}$ série de 24h et les variables exogènes (mesurées)
12correspondantes. Voir ?Data pour plus d'information, une fois le package chargé.
13 * **computeForecast()** pour calculer des prédictions sur une certaine plage temporelle
14contenue dans *data <- getData(...)*
15 * **computeError()** pour évaluer les erreurs commises par différentes méthodes.
16
17Le package contient en outre diverses fonctions graphiques *plotXXX()*, utilisées dans la
18partie suivante.
19-----r
20# Chargement de la librairie (après compilation, "R CMD INSTALL .")
21library(talweg)
22
23# Acquisition des données (depuis les fichiers CSV)
24ts_data <- read.csv(system.file("extdata","pm10_mesures_H_loc.csv",
25 package="talweg"))
26exo_data <- read.csv(system.file("extdata","meteo_extra_noNAs.csv",
27 package="talweg"))
28data <- getData(ts_data, exo_data, input_tz="GMT",
29 date_format="%d/%m/%Y %H:%M", working_tz="GMT", predict_at=7, limit=120)
30# Plus de détails à la section 1 ci-après.
31
32# Prédiction de 10 courbes (jours 102 à 111)
33pred <- computeForecast(data, 101:110, "Persistence", "Zero", memory=50,
34 horizon=12, ncores=1)
35# Plus de détails à la section 2 ci-après.
36
37# Calcul des erreurs (sur un horizon arbitraire <= horizon de prédiction)
38err <- computeError(data, pred, horizon=6)
39# Plus de détails à la section 3 ci-après.
40
41# Puis voir ?plotError et les autres plot dans le paragraphe 'seealso'
42-----
8eafefbc 43${"##"} getData()
b6233fa6
BA
44
45Les arguments de cette fonction sont, dans l'ordre :
46
47 1. **ts_data** : séries temporelles (fichier CSV avec entête ou data.frame) ; la
48première colonne contient les heures, la seconde les valeurs.
49 2. **exo_data** : variables exogènes (fichier CSV avec entête ou data.frame) ; la
50première colonne contient les jours, les $m$ suivantes les variables mesurées pour ce
51jour, et les $m$ dernières les variables prédites pour ce même jour. Dans notre cas $m=4$
52: pression, température, gradient de température, vitesse du vent.
53 3. **input_tz** : zone horaire pour ts_data (défaut : "GMT").
54 4. **date_format** : format des heures dans ts_data (défaut : "%d/%m/%Y %H:%M", format
55du fichier transmis par Michel).
56 5. **working_tz** : zone horaire dans laquelle on souhaite travailler avec les données
57(défaut : "GMT").
58 6. **predict_at** : heure à laquelle s'effectue la prévision $-$ et donc dernière heure
59d'un bloc de 24h, relativement à working_tz. data`$`getSerie(3) renvoit ainsi les 24
60valeurs de 8h à 7h pour le $3^{eme}$ bloc de 24h présent dans le jeu de données.
61-----r
62print(data)
63#?Data
64-----
8eafefbc 65${"##"} computeForecast()
b6233fa6
BA
66
67Les arguments de cette fonction sont, dans l'ordre :
68
69 1. **data** : le jeu de données renvoyé par getData()
70 2. **indices** : l'ensemble de jours dont on veut prévoir les "lendemains" (prochains
71blocs de 24h) ; peut être donnée sous forme d'un vecteur de dates ou d'entiers
72(correspondants aux numéros des jours).
73 3. **forecaster** : le nom du prédicteur principal à utiliser ; voir ?computeForecast
74 4. **pjump** : le nom du prédicteur de saut d'une série à l'autre ; voir
75?computeForecast
76 5. **memory** : le nombre de jours à prendre en compte dans le passé pour chaque
77prévision (par défaut : Inf, c'est-à-dire tout l'historique pris en compte).
78 6. **horizon** : le nombre d'heures à prédire ; par défaut "data`$`getStdHorizon()",
79c'est-à-dire le nombre d'heures restantes à partir de l'instant de prévision + 1 jusqu'à
80minuit (17 pour predict_at=7 par exemple).
81 7. **ncores** : le nombre de processus parallèles (utiliser 1 pour une exécution
82séquentielle)
83-----r
84print(pred)
85#?computeForecast
63ff1ecb 86-----
8eafefbc 87${"##"} computeError()
63ff1ecb 88
b6233fa6 89Les arguments de cette fonction sont, dans l'ordre :
aa059de7 90
b6233fa6
BA
91 1. **data** : le jeu de données renvoyé par getData()
92 2. **pred** : les prédictions renvoyées par computeForecast()
93 3. **horizon** : le nombre d'heures à considérer pour le calcul de l'erreur ; doit être
94inférieur ou égal à l'horizon utilisé pour la prédiction (même valeur par défaut :
95"data`$`getStdHorizon()")
96-----r
97summary(err)
98summary(err$abs)
99summary(err$MAPE)
100-----
8eafefbc 101${"##"} Graphiques
b6233fa6
BA
102
103Voir ?plotError : les autres fonctions graphiques sont dans la section 'seealso' :
63ff1ecb 104
b6233fa6
BA
105 ‘plotCurves’, ‘plotPredReal’, ‘plotSimils’, ‘plotFbox’,
106 ‘computeFilaments’, ‘plotFilamentsBox’, ‘plotRelVar’
107
108?plotXXX, etc.
8eafefbc 109## $\clearpage$ How to do that?
b6233fa6
BA
110-----
111# Expérimentations
63ff1ecb 112
b6233fa6
BA
113Cette partie montre les résultats obtenus via des variantes de l'algorithme décrit à la
114section 2, en utilisant le package présenté à la section 3. Cet algorithme est
115systématiquement comparé à deux approches naïves :
63ff1ecb 116
b6233fa6
BA
117 * la moyenne des lendemains des jours "similaires" dans tout le passé, c'est-à-dire
118prédiction = moyenne de tous les mardis passé si le jour courant est un lundi par
119exemple.
120 * la persistence, reproduisant le jour courant ou allant chercher le lendemain de la
121dernière journée "similaire" (même principe que ci-dessus ; argument "same\_day").
122
123Concernant l'algorithme principal à voisins, trois variantes sont étudiées dans cette
124partie :
125
126 * avec simtype="mix" et raccordement "Neighbors" dans le cas "non local", i.e. on va
127chercher des voisins n'importe où du moment qu'ils correspondent au premier élément d'un
128couple de deux jours consécutifs sans valeurs manquantes.
129 * avec simtype="endo" + raccordement "Neighbors" puis simtype="none" + raccordement
130"Zero" (sans ajustement) dans le cas "local" : voisins de même niveau de pollution et
131même saison.
132
133Pour chaque période retenue $-$ chauffage, épandage, semaine non polluée $-$ les erreurs
134de prédiction sont d'abord affichées, puis quelques graphes de courbes réalisées/prévues
135(sur le jour "en moyenne le plus facile" à gauche, et "en moyenne le plus difficile" à
136droite). Ensuite plusieurs types de graphes apportant des précisions sur la nature et la
137difficulté du problème viennent compléter ces premières courbes. Concernant les graphes
138de filaments, la moitié gauche du graphe correspond aux jours similaires au jour courant,
139tandis que la moitié droite affiche les lendemains : ce sont donc les voisinages tels
140qu'utilisés dans l'algorithme.
63ff1ecb 141<%
b6233fa6 142list_titles = ['Pollution par chauffage','Pollution par épandage','Semaine non polluée']
63ff1ecb
BA
143list_indices = ['indices_ch', 'indices_ep', 'indices_np']
144%>
63ff1ecb 145-----r
63ff1ecb
BA
146library(talweg)
147
d09b09b0
BA
148P = ${P} #instant de prévision
149H = ${H} #horizon (en heures)
150
b6233fa6
BA
151ts_data = read.csv(system.file("extdata","pm10_mesures_H_loc_report.csv",
152 package="talweg"))
153exo_data = read.csv(system.file("extdata","meteo_extra_noNAs.csv",
154 package="talweg"))
155# NOTE: 'GMT' because DST gaps are filled and multiple values merged in
156# above dataset. Prediction from P+1 to P+H included.
157data = getData(ts_data, exo_data, input_tz = "GMT", working_tz="GMT",
158 predict_at=P)
63ff1ecb
BA
159
160indices_ch = seq(as.Date("2015-01-18"),as.Date("2015-01-24"),"days")
161indices_ep = seq(as.Date("2015-03-15"),as.Date("2015-03-21"),"days")
162indices_np = seq(as.Date("2015-04-26"),as.Date("2015-05-02"),"days")
ff5df8e3 163% for i in range(3):
63ff1ecb 164-----
8eafefbc
BA
165##<h2 style="color:blue;font-size:2em">${list_titles[i]}</h2>
166${"##"} ${list_titles[i]}
63ff1ecb 167-----r
445e7bbc 168p1 = computeForecast(data, ${list_indices[i]}, "Neighbors", "Neighbors", horizon=H,
aa059de7 169 simtype="mix", local=FALSE)
445e7bbc 170p2 = computeForecast(data, ${list_indices[i]}, "Neighbors", "Neighbors", horizon=H,
aa059de7 171 simtype="endo", local=TRUE)
445e7bbc
BA
172p3 = computeForecast(data, ${list_indices[i]}, "Neighbors", "Zero", horizon=H,
173 simtype="none", local=TRUE)
174p4 = computeForecast(data, ${list_indices[i]}, "Average", "Zero", horizon=H)
175p5 = computeForecast(data, ${list_indices[i]}, "Persistence", "Zero", horizon=H,
aa059de7 176 same_day=${'TRUE' if loop.index < 2 else 'FALSE'})
63ff1ecb 177-----r
1f811218
BA
178e1 = computeError(data, p1, H)
179e2 = computeError(data, p2, H)
a3f39d31
BA
180e3 = computeError(data, p3, H)
181e4 = computeError(data, p4, H)
182e5 = computeError(data, p5, H)
63ff1ecb 183options(repr.plot.width=9, repr.plot.height=7)
1f811218 184plotError(list(e1, e5, e4, e2, e3), cols=c(1,2,colors()[258],4,6))
63ff1ecb 185
b6233fa6
BA
186# noir: Neighbors non-local (p1), bleu: Neighbors local endo (p2),
187# mauve: Neighbors local none (p3), vert: moyenne (p4),
188# rouge: persistence (p5)
63ff1ecb 189
63afc6d9 190sum_p123 = e1$abs$indices + e2$abs$indices + e3$abs$indices
b6233fa6
BA
191i_np = which.min(sum_p123) #indice de (veille de) jour "facile"
192i_p = which.max(sum_p123) #indice de (veille de) jour "difficile"
193-----
8eafefbc 194% if i == 1:
b6233fa6
BA
195L'erreur absolue dépasse 20 sur 1 à 2 jours suivant les modèles (graphe en haut à
196droite). C'est au-delà de ce que l'on aimerait voir (disons +/- 5 environ). Sur cet
197exemple le modèle à voisins "contraint" (local=TRUE) utilisant des pondérations basées
198sur les similarités de forme (simtype="endo") obtient en moyenne les meilleurs résultats,
199avec un MAPE restant en général inférieur à 30% de 8h à 19h (7+1 à 7+12 : graphe en bas à
200gauche).
8eafefbc 201% elif i == 2:
b6233fa6
BA
202Il est difficile dans ce cas de déterminer une méthode meilleure que les autres : elles
203donnent toutes de plutôt mauvais résultats, avec une erreur absolue moyennée sur la
204journée dépassant presque toujours 15 (graphe en haut à droite).
8eafefbc 205% else:
b6233fa6
BA
206Dans ce cas plus favorable les intensité des erreurs absolues ont clairement diminué :
207elles restent souvent en dessous de 5. En revanche le MAPE moyen reste au-delà de 20%, et
208même souvent plus de 30%. Comme dans le cas de l'épandage on constate une croissance
209globale de la courbe journalière d'erreur absolue moyenne (en haut à gauche) ; ceci peut
210être dû au fait que l'on ajuste le niveau du jour à prédire en le recollant sur la
211dernière valeur observée.
212% endif
63ff1ecb
BA
213-----r
214options(repr.plot.width=9, repr.plot.height=4)
215par(mfrow=c(1,2))
216
445e7bbc
BA
217plotPredReal(data, p1, i_np); title(paste("PredReal p1 day",i_np))
218plotPredReal(data, p1, i_p); title(paste("PredReal p1 day",i_p))
63ff1ecb 219
445e7bbc
BA
220plotPredReal(data, p2, i_np); title(paste("PredReal p2 day",i_np))
221plotPredReal(data, p2, i_p); title(paste("PredReal p2 day",i_p))
63ff1ecb 222
445e7bbc
BA
223plotPredReal(data, p3, i_np); title(paste("PredReal p3 day",i_np))
224plotPredReal(data, p3, i_p); title(paste("PredReal p3 day",i_p))
63ff1ecb 225
b6233fa6
BA
226# Bleu : prévue ; noir : réalisée
227-----
8eafefbc 228% if i == 1:
b6233fa6
BA
229Le jour "facile à prévoir", à gauche, se décompose en deux modes : un léger vers 10h
230(7+3), puis un beaucoup plus marqué vers 19h (7+12). Ces deux modes sont retrouvés par
231les trois variantes de l'algorithme à voisins, bien que l'amplitude soit mal prédite.
232Concernant le jour "difficile à prévoir" il y a deux pics en tout début et toute fin de
233journée (à 9h et 23h), qui ne sont pas du tout anticipés par le programme ; la grande
234amplitude de ces pics explique alors l'intensité de l'erreur observée.
8eafefbc 235% elif i == 2:
b6233fa6
BA
236Dans le cas d'un jour "facile" à prédire $-$ à gauche $-$ la forme est plus ou moins
237retrouvée, mais le niveau moyen est trop bas (courbe en bleu). Concernant le jour
238"difficile" à droite, non seulement la forme n'est pas anticipée mais surtout le niveau
239prédit est très inférieur au niveau de pollution observé. Comme on le voit ci-dessous
240cela découle d'un manque de voisins au comportement similaire.
8eafefbc 241% else:
b6233fa6
BA
242La forme est raisonnablement retrouvée pour les méthodes "locales", l'autre version
243lissant trop les prédictions. Le biais reste cependant important, surtout en fin de
244journée sur le jour "difficile".
245% endif
63ff1ecb
BA
246-----r
247par(mfrow=c(1,2))
b6233fa6
BA
248f_np1 = computeFilaments(data, p1, i_np, plot=TRUE)
249 title(paste("Filaments p1 day",i_np))
250f_p1 = computeFilaments(data, p1, i_p, plot=TRUE)
251 title(paste("Filaments p1 day",i_p))
63ff1ecb 252
b6233fa6
BA
253f_np2 = computeFilaments(data, p2, i_np, plot=TRUE)
254 title(paste("Filaments p2 day",i_np))
255f_p2 = computeFilaments(data, p2, i_p, plot=TRUE)
256 title(paste("Filaments p2 day",i_p))
257-----
8eafefbc 258% if i == 1:
b6233fa6
BA
259Les voisins du jour courant (période de 24h allant de 8h à 7h le lendemain) sont affichés
260avec un trait d'autant plus sombre qu'ils sont proches. On constate dans le cas non
261contraint (en haut) une grande variabilité des lendemains, très nette sur le graphe en
262haut à droite. Ceci indique une faible corrélation entre la forme d'une courbe sur une
263période de 24h et la forme sur les 24h suivantes ; **cette observation est la source des
264difficultés rencontrées par l'algorithme sur ce jeu de données.**
8eafefbc 265% elif i == 2:
b6233fa6
BA
266Les observations sont les mêmes qu'au paragraphe précédent : trop de variabilité des
267lendemains (et même des voisins du jour courant).
8eafefbc 268% else:
b6233fa6
BA
269Les graphes de filaments ont encore la même allure, avec une assez grande variabilité
270observée. Cette observation est cependant trompeuse, comme l'indique plus bas le graphe
271de variabilité relative.
272% endif
63ff1ecb
BA
273-----r
274par(mfrow=c(1,2))
445e7bbc
BA
275plotFilamentsBox(data, f_np1); title(paste("FilBox p1 day",i_np))
276plotFilamentsBox(data, f_p1); title(paste("FilBox p1 day",i_p))
63ff1ecb 277
b6233fa6
BA
278# En pointillés la courbe du jour courant + lendemain (à prédire)
279-----
8eafefbc 280% if i == 1:
b6233fa6
BA
281Sur cette boxplot fonctionnelle (voir la fonction fboxplot() du package R "rainbow") l'on
282constate essentiellement deux choses : le lendemain d'un voisin "normal" peut se révéler
283être une courbe atypique, fort éloignée de ce que l'on souhaite prédire (courbes bleue et
284rouge à gauche) ; et, dans le cas d'une courbe à prédire atypique (à droite) la plupart
285des voisins sont trop éloignés de la forme à prédire et forcent ainsi un aplatissement de
286la prédiction.
8eafefbc 287% elif i == 2:
b6233fa6
BA
288On constate la présence d'un voisin au lendemain complètement atypique avec un pic en
289début de journée (courbe en vert à gauche), et d'un autre phénomène semblable avec la
290courbe rouge sur le graphe de droite. Ajouté au fait que le lendemain à prévoir est
291lui-même un jour "hors norme", cela montre l'impossibilité de bien prévoir une courbe en
292utilisant l'algorithme à voisins.
8eafefbc 293% else:
b6233fa6
BA
294On peut réappliquer les mêmes remarques qu'auparavant sur les boxplots fonctionnels :
295lendemains de voisins atypiques, courbe à prévoir elle-même légèrement "hors norme".
296% endif
63ff1ecb
BA
297-----r
298par(mfrow=c(1,2))
445e7bbc
BA
299plotRelVar(data, f_np1); title(paste("StdDev p1 day",i_np))
300plotRelVar(data, f_p1); title(paste("StdDev p1 day",i_p))
63ff1ecb 301
445e7bbc
BA
302plotRelVar(data, f_np2); title(paste("StdDev p2 day",i_np))
303plotRelVar(data, f_p2); title(paste("StdDev p2 day",i_p))
63ff1ecb 304
b6233fa6
BA
305# Variabilité globale en rouge ; sur les voisins (+ lendemains) en noir
306-----
8eafefbc 307% if i == 1:
b6233fa6
BA
308Ces graphes viennent confirmer l'impression visuelle après observation des filaments. En
309effet, la variabilité globale en rouge (écart-type heure par heure sur l'ensemble des
310couples "aujourd'hui/lendemain"du passé) devrait rester nettement au-dessus de la
311variabilité locale, calculée respectivement sur un voisinage d'une soixantaine de jours
312(pour p1) et d'une dizaine de jours (pour p2). Or on constate que ce n'est pas du tout le
313cas sur la période "lendemain", sauf en partie pour p2 le jour 4 $-$ mais ce n'est pas
314suffisant.
8eafefbc 315% elif i == 2:
b6233fa6
BA
316Comme précédemment les variabilités locales et globales sont confondues dans les parties
317droites des graphes $-$ sauf pour la version "locale" sur le jour "facile"; mais cette
318bonne propriété n'est pas suffisante si l'on ne trouve pas les bons poids à appliquer.
8eafefbc 319% else:
b6233fa6
BA
320Cette fois la situation idéale est observée : la variabilité globale est nettement
321au-dessus de la variabilité locale. Bien que cela ne suffise pas à obtenir de bonnes
322prédictions de forme, on constate au moins l'amélioration dans la prédiction du niveau.
323% endif
63ff1ecb
BA
324-----r
325par(mfrow=c(1,2))
445e7bbc
BA
326plotSimils(p1, i_np); title(paste("Weights p1 day",i_np))
327plotSimils(p1, i_p); title(paste("Weights p1 day",i_p))
63ff1ecb 328
445e7bbc
BA
329plotSimils(p2, i_np); title(paste("Weights p2 day",i_np))
330plotSimils(p2, i_p); title(paste("Weights p2 day",i_p))
b6233fa6 331-----
8eafefbc 332% if i == 1:
b6233fa6
BA
333Les poids se concentrent près de 0 dans le cas "non local" (p1), et se répartissent assez
334uniformément dans [ 0, 0.2 ] dans le cas "local" (p2). C'est ce que l'on souhaite
335observer pour éviter d'effectuer une simple moyenne.
8eafefbc 336% elif i == 2:
b6233fa6
BA
337En comparaison avec le pragraphe précédent on retrouve le même (bon) comportement des
338poids pour la version "non locale". En revanche la fenêtre optimisée est trop grande sur
339le jour "facile" pour la méthode "locale" (voir affichage ci-dessous) : il en résulte des
340poids tous semblables autour de 0.084, l'algorithme effectue donc une moyenne simple $-$
341expliquant pourquoi les courbes mauve et bleue sont très proches sur le graphe d'erreurs.
8eafefbc 342% else:
b6233fa6 343Concernant les poids en revanche, deux cas a priori mauvais se cumulent :
63ff1ecb 344
b6233fa6
BA
345 * les poids dans le cas "non local" ne sont pas assez concentrés autour de 0, menant à
346un lissage trop fort $-$ comme observé sur les graphes des courbes réalisées/prévues ;
347 * les poids dans le cas "local" sont trop semblables (à cause de la trop grande fenêtre
348optimisée par validation croisée, cf. ci-dessous), résultant encore en une moyenne simple
349$-$ mais sur moins de jours, plus proches du jour courant.
350% endif
63ff1ecb 351-----r
b6233fa6
BA
352# Fenêtres sélectionnées dans ]0,7] :
353# "non-local" 2 premières lignes, "local" ensuite
445e7bbc
BA
354p1$getParams(i_np)$window
355p1$getParams(i_p)$window
63ff1ecb 356
445e7bbc
BA
357p2$getParams(i_np)$window
358p2$getParams(i_p)$window
63ff1ecb 359% endfor
b6233fa6 360-----
8eafefbc 361${"##"} Bilan
b6233fa6
BA
362
363Nos algorithmes à voisins ne sont pas adaptés à ce jeu de données où la forme varie
364considérablement d'un jour à l'autre. Plus généralement cette décorrélation de forme rend
365ardue la tâche de prévision pour toute autre méthode $-$ du moins, nous ne savons pas
366comment procéder pour parvenir à une bonne précision.
367
368Toutefois, un espoir reste permis par exemple en aggréger les courbes spatialement (sur
369plusieurs stations situées dans la même agglomération ou dans une même zone).