CHAPITRE 2: Modélisation prédictive
Dans le précédent article, on a pu voir que la clause MODEL d’Oracle permettait de faire des calculs un peu comme on les ferait avec un tableur en utilisant le résultat de certaines cellules pour en calculer d’autres.
La modélisation permet aussi de créer des lignes comlpètes de toute pièce en s’appuiant sur les précédentes. Ces types de modélisations sont généralement utilisés pour se projeter dans l’avenir et donc intéressant pour les modèles décisionnels
Je vais construire ici un modèle fictif, pour montrer la méthode, qui va donc se baser sur un historique et extrapoler un avenir.
Le modèle à utiliser sera celui d’un ingénieur commercial qui voudrait prédire l’évolution de ses tarifs. Rien que du très classique en fin de compte. Les règles de calcul vont être lesquelles ?
- Les tarifs sont révisés annuellement
- Les tarifs sont révisés à date de renouvellement de contrat
- La révision des tarifs aura pour objectif d’être égale à l’évolution annuelle de l’indice syntec 2 mois avant la date anniversaire du contrat.
- Il y a deux indices syntec, on se base sur celui qui correspond à la date d’origine du contrat. L’ancienne valeur a été diffusée jusqu’à la date du 1er juillet 2022 et qu’à partir de là il se calcul en multipliant le nouvel indice par une constante égale à 0,97975.
Je vais donc
- Extrapoler l’indice syntec jusqu’en juin 2025
- Extrapoler l’ancienne indice par rapport au nouveau à partir d’aout 2022
- Calculer les variations annuelles entre juillet 2023 et juin 2025
je prendrai comme hypothèse, qu’au pire des cas, l’indice n’évoluera pas plus qu’il n’a évolué l’une de ces deux dernières années.
Je crée la table de référence contenant les données connues à date de sortie de l’article (donc la variation des index anciens et nouveaux entre décembre 2019 et juin 2023). Je la nomme SYNTEC
create table syntec as select to_date('01/12/2019','DD/MM/YYYY') mois,274.7 ancien, 274.7 nouveau from dual select to_date('01/01/2020','DD/MM/YYYY') mois,274.9 ancien, 274.7 nouveau from dual UNION ALL select to_date('01/02/2020','DD/MM/YYYY') mois,274.7 ancien, 274.4 nouveau from dual UNION ALL select to_date('01/03/2020','DD/MM/YYYY') mois,275.3 ancien, 275.3 nouveau from dual UNION ALL select to_date('01/04/2020','DD/MM/YYYY') mois,275.1 ancien, 275.1 nouveau from dual UNION ALL select to_date('01/05/2020','DD/MM/YYYY') mois,275.2 ancien, 275.6 nouveau from dual UNION ALL select to_date('01/06/2020','DD/MM/YYYY') mois,274.9 ancien, 275.6 nouveau from dual UNION ALL select to_date('01/07/2020','DD/MM/YYYY') mois,274.9 ancien, 275.6 nouveau from dual UNION ALL select to_date('01/08/2020','DD/MM/YYYY') mois,274.4 ancien, 275.2 nouveau from dual UNION ALL select to_date('01/09/2020','DD/MM/YYYY') mois,275.2 ancien, 275.2 nouveau from dual UNION ALL select to_date('01/10/2020','DD/MM/YYYY') mois,274.4 ancien, 274.8 nouveau from dual UNION ALL select to_date('01/11/2020','DD/MM/YYYY') mois,274.8 ancien, 275.1 nouveau from dual UNION ALL select to_date('01/12/2020','DD/MM/YYYY') mois,275 ancien, 275.9 nouveau from dual UNION ALL select to_date('01/01/2021','DD/MM/YYYY') mois,275 ancien, 275.9 nouveau from dual UNION ALL select to_date('01/02/2021','DD/MM/YYYY') mois,275.1 ancien, 276.6 nouveau from dual UNION ALL select to_date('01/03/2021','DD/MM/YYYY') mois,274.6 ancien, 275.9 nouveau from dual UNION ALL select to_date('01/04/2021','DD/MM/YYYY') mois,275.1 ancien, 276.9 nouveau from dual UNION ALL select to_date('01/05/2021','DD/MM/YYYY') mois,275.3 ancien, 277.1 nouveau from dual UNION ALL select to_date('01/06/2021','DD/MM/YYYY') mois,275.7 ancien, 278.1 nouveau from dual UNION ALL select to_date('01/07/2021','DD/MM/YYYY') mois,275.8 ancien, 279.3 nouveau from dual UNION ALL select to_date('01/08/2021','DD/MM/YYYY') mois,276.5 ancien, 279.8 nouveau from dual UNION ALL select to_date('01/09/2021','DD/MM/YYYY') mois,276.5 ancien, 280 nouveau from dual UNION ALL select to_date('01/10/2021','DD/MM/YYYY') mois,276.7 ancien, 280 nouveau from dual UNION ALL select to_date('01/11/2021','DD/MM/YYYY') mois,276.9 ancien, 280.3 nouveau from dual UNION ALL select to_date('01/12/2021','DD/MM/YYYY') mois,277.3 ancien, 280.7 nouveau from dual UNION ALL select to_date('01/01/2022','DD/MM/YYYY') mois,277.5 ancien, 281.8 nouveau from dual UNION ALL select to_date('01/02/2022','DD/MM/YYYY') mois,278.2 ancien, 282.8 nouveau from dual UNION ALL select to_date('01/03/2022','DD/MM/YYYY') mois,279.9 ancien, 284.6 nouveau from dual UNION ALL select to_date('01/04/2022','DD/MM/YYYY') mois,279.8 ancien, 283.8 nouveau from dual UNION ALL select to_date('01/05/2022','DD/MM/YYYY') mois,280 ancien, 284.6 nouveau from dual UNION ALL select to_date('01/06/2022','DD/MM/YYYY') mois,280 ancien, 284.7 nouveau from dual UNION ALL select to_date('01/07/2022','DD/MM/YYYY') mois,280.4 ancien, 285.8 nouveau from dual UNION ALL select to_date('01/08/2022','DD/MM/YYYY') mois,null ancien, 286.4 nouveau from dual UNION ALL select to_date('01/09/2022','DD/MM/YYYY') mois,null ancien, 287.2 nouveau from dual UNION ALL select to_date('01/10/2022','DD/MM/YYYY') mois,null ancien, 289.4 nouveau from dual UNION ALL select to_date('01/11/2022','DD/MM/YYYY') mois,null ancien, 289.9 nouveau from dual UNION ALL select to_date('01/12/2022','DD/MM/YYYY') mois,null ancien, 292.6 nouveau from dual UNION ALL select to_date('01/01/2023','DD/MM/YYYY') mois,null ancien, 293.9 nouveau from dual UNION ALL select to_date('01/02/2023','DD/MM/YYYY') mois,null ancien, 298.5 nouveau from dual UNION ALL select to_date('01/03/2023','DD/MM/YYYY') mois,null ancien, 300.7 nouveau from dual UNION ALL select to_date('01/04/2023','DD/MM/YYYY') mois,null ancien, 301.9 nouveau from dual UNION ALL select to_date('01/05/2023','DD/MM/YYYY') mois,null ancien, 302.7 nouveau from dual UNION ALL select to_date('01/06/2023','DD/MM/YYYY') mois,null ancien, 304 nouveau from dual /
Je vais m’intenant interroger la table SYNTEC avec une contrainte particulière: return uodated rows et aucune règle.
select * from SYNTEC model return updated rows dimension by (MOIS) measures ( ancien, nouveau) rules() ;
La requête ne retourne aucune ligne, ce qui est normal puisqu’aucune règle n’a été appliquée et qu’en conséquence, aucune ligne, aucune cellule n’a été calculée.
Calcul de la progression des valeurs de l’indice au delà des dates pour lesquelles on l’a.
A compter du 1er juillet 2023 et jusqu’au 1er juin 2025, je vais calculer un ratio qui donnera la pire (plus importante) évolution mensuelle de l’indice syntec ces deux dernières années à la même période.
La colonne de dimension, MOIS, est de type date, les indices dans les règles devront eux aussi être de trype date et exister dans la colonne. S’ils n’existent pas ils seront générés.
ratio [ for mois from to_date('01/07/2023','DD/MM/YYYY') to to_date('01/06/2025','DD/MM/YYYY') increment interval '1' MONTH ] = greatest( nouveau[add_months(cv(),-12)] / nouveau[add_months(cv(),-13)] , nouveau[add_months(cv(),-24)] / nouveau[add_months(cv(),-25)] )
Il n’y a donc pas besoin de générateur de type select level from dual connect by level … Ce sont les indices imposés pour la dimension qui génèrent les dates futures souhaitées.
Résultat :
MOIS ANCIEN NOUVEAU RATIO -------- ---------- ---------- ---------- 01/08/22 280,6 286,4 01/09/22 281,4 287,2 01/10/22 283,5 289,4 01/11/22 284 289,9 01/12/22 286,7 292,6 01/01/23 287,9 293,9 01/02/23 292,5 298,5 01/03/23 294,6 300,7 01/04/23 295,8 301,9 01/05/23 296,6 302,7 01/06/23 297,8 304 01/07/23 299,1 305,3 1,00431499 01/08/23 299,7 305,9 1,00209937 01/09/23 300,6 306,8 1,0027933 01/10/23 302,9 309,2 1,00766017 01/11/23 303,4 309,7 1,00172771 01/12/23 306,3 312,6 1,00931356 01/01/24 307,6 314 1,00444293 01/02/24 312,4 318,9 1,01565158 01/03/24 314,8 321,3 1,00737018 01/04/24 316,1 322,6 1,00399069 01/05/24 316,9 323,5 1,00281889 01/06/24 318,3 324,9 1,00429468 01/07/24 01/08/24 01/09/24 01/10/24 01/11/24 01/12/24 01/01/25 01/02/25 01/03/25 01/04/25 01/05/25 01/06/25
La plage de date souhaitée a bien été générée. Cependant si Oracle a bien calculé les valeurs pour 2023 et début 2024, il ne l’a pas fait juillet pour 2024 et les dates suivantes. La raison est qu’il passe les règles dans l’ordre où elles sont écrites une fois et une seule sur la table cournate. On a besoin du résultat de la règle nouveau sur 2024 pour calculer les dates manquantes sur 2025. Et on aura pas besoin de plus. J’aimerais bien qu’il fasse deux fois le passage des règles pour calculer toutes les cases qui nous intéressent.
La clause iterate
La réponse à la problématique évoquée au paragraphe précédent réside dans la clause iterate qui indique à oracle combien de fois de suite il doit appliquer les règles séquentiellement et dans l’ordre où ells sont écrites.
select * from SYNTEC model return updated rows dimension by (MOIS) measures ( ancien , nouveau , cast(null as integer) as ratio ) rules iterate(2) ( ratio [ for mois from to_date('01/07/2023','DD/MM/YYYY') to to_date('01/06/2025','DD/MM/YYYY') increment interval '1' MONTH ] = greatest( nouveau[add_months(cv(),-12)] / nouveau[add_months(cv(),-13)] , nouveau[add_months(cv(),-24)] / nouveau[add_months(cv(),-25)] ) , nouveau[ mois > to_date('30/06/2023','DD/MM/YYYY') ] = round( nouveau[add_months(cv(),-1)] * ratio[cv()], 1 ) , ancien[ mois > to_date('31/07/2022','DD/MM/YYYY') ] = round( nouveau[cv()] * 0.97975, 1 ) ) ;
Résultat
MOIS ANCIEN NOUVEAU RATIO -------- ---------- ---------- ---------- 01/08/22 280,6 286,4 01/09/22 281,4 287,2 01/10/22 283,5 289,4 01/11/22 284 289,9 01/12/22 286,7 292,6 01/01/23 287,9 293,9 01/02/23 292,5 298,5 01/03/23 294,6 300,7 01/04/23 295,8 301,9 01/05/23 296,6 302,7 01/06/23 297,8 304 01/07/23 299,1 305,3 1,00431499 01/08/23 299,7 305,9 1,00209937 01/09/23 300,6 306,8 1,0027933 01/10/23 302,9 309,2 1,00766017 01/11/23 303,4 309,7 1,00172771 01/12/23 306,3 312,6 1,00931356 01/01/24 307,6 314 1,00444293 01/02/24 312,4 318,9 1,01565158 01/03/24 314,8 321,3 1,00737018 01/04/24 316,1 322,6 1,00399069 01/05/24 316,9 323,5 1,00281889 01/06/24 318,3 324,9 1,00429468 01/07/24 319,7 326,3 1,00427632 01/08/24 320,4 327 1,00209937 01/09/24 321,4 328 1,00294214 01/10/24 323,9 330,6 1,00782269 01/11/24 324,5 331,2 1,00172771 01/12/24 327,5 334,3 1,0093639 01/01/25 329 335,8 1,00447857 01/02/25 334,2 341,1 1,01565158 01/03/25 336,7 343,7 1,00752587 01/04/25 338,1 345,1 1,00404606 01/05/25 339,1 346,1 1,00278983 01/06/25 340,6 347,6 1,00432767
On a bien toutes nos valeurs d’indices syntec prospectives. Le reste n’est que calcul du type de ceusx qui ont été faits au chapitre précédent, rien de nouveau coté clause MODEL.
Requête définitive
La requête définitive est donc la suivante:
select mois , ancien , txaug_a , nouveau , txaug_n from syntec model return updated rows dimension by (MOIS) measures ( ancien , nouveau , cast(null as integer) as ratio , cast(null as integer) as txaug_a , cast(null as integer) as txaug_n ) rules iterate(2) ( ratio [ for mois from to_date('01/07/2022','DD/MM/YYYY') to to_date('01/06/2025','DD/MM/YYYY') increment interval '1' MONTH ] = greatest( nouveau[add_months(cv(),-12)] / nouveau[add_months(cv(),-13)] , nouveau[add_months(cv(),-24)] / nouveau[add_months(cv(),-25)] ) , nouveau[ mois > to_date('30/06/2023','DD/MM/YYYY') ] = round( nouveau[add_months(cv(),-1)] * ratio[cv()], 1 ) , ancien[ mois > to_date('30/06/2022','DD/MM/YYYY') ] = round( nouveau[cv()] * 0.97975, 1 ) , txaug_a[ mois > to_date('30/06/2022','DD/MM/YYYY') ] = round( ancien[add_months(cv(),-2)] / ancien[add_months(cv(),-14)], 4) , txaug_n[ mois > to_date('30/06/2022','DD/MM/YYYY') ] = round( nouveau[add_months(cv(),-2)] / nouveau[add_months(cv(),-14)], 4) ) ;
Son résultat est le suivant:
MOIS ANCIEN TXAUG_A NOUVEAU TXAUG_N -------- ---------- ---------- ---------- ---------- 01/07/22 280 1,0171 285,8 1,0271 01/08/22 280,6 1,0156 286,4 1,0237 01/09/22 281,4 1,0152 287,2 1,0233 01/10/22 283,5 1,0148 289,4 1,0236 01/11/22 284 1,0177 289,9 1,0257 01/12/22 286,7 1,0246 292,6 1,0336 01/01/23 287,9 1,0256 293,9 1,0342 01/02/23 292,5 1,0339 298,5 1,0424 01/03/23 294,6 1,0375 300,7 1,0429 01/04/23 295,8 1,0514 301,9 1,0555 01/05/23 296,6 1,0525 302,7 1,0566 01/06/23 297,8 1,0572 304 1,0638 01/07/23 299,1 1,0593 305,3 1,0636 01/08/23 299,7 1,0636 305,9 1,0678 01/09/23 300,6 1,0682 306,8 1,0682 01/10/23 302,9 1,0681 309,2 1,0681 01/11/23 303,4 1,0682 309,7 1,0682 01/12/23 306,3 1,0684 312,6 1,0684 01/01/24 307,6 1,0683 314 1,0683 01/02/24 312,4 1,0684 318,9 1,0684 01/03/24 314,8 1,0684 321,3 1,0684 01/04/24 316,1 1,068 322,6 1,0683 01/05/24 316,9 1,0686 323,5 1,0685 01/06/24 318,3 1,0686 324,9 1,0686 01/07/24 319,7 1,0684 326,3 1,0687 01/08/24 320,4 1,0688 327 1,0688 01/09/24 321,4 1,0689 328 1,0688 01/10/24 323,9 1,0691 330,6 1,069 01/11/24 324,5 1,0692 331,2 1,0691 01/12/24 327,5 1,0693 334,3 1,0692 01/01/25 329 1,0695 335,8 1,0694 01/02/25 334,2 1,0692 341,1 1,0694 01/03/25 336,7 1,0696 343,7 1,0694 01/04/25 338,1 1,0698 345,1 1,0696 01/05/25 339,1 1,0696 346,1 1,0697 01/06/25 340,6 1,0696 347,6 1,0697