par Anissa Saumtally



Ce guide fait partie d’une série de trois guides sur tresthor :



Le package tresthor développé par la DG Trésor est un outil permettant de créer des modèles économiques, de les résoudre pour effectuer des prévisions et de les analyser. Il est mis à disposition du public afin d’éclairer sur les méthodes utilisées pour les prévisions économiques des projets de loi de finances et programmes de stabilités. Il peut être également utilisé à des fins pédagogiques pour mieux appréhender les mécanismes économiques suivant un modèle macroéconomique construit par l’utilisateur.

  • Les dépendances du package :

    • dplyr et Deriv(seront chargés avec le package)
    • purrr, tidyr, stringr, ggplot2
    • scales , splitstackshape, assertthat,stats,Matrix,cointReg
    • Rcpp, RcppArmadillo
  • Le package fonctionne sur R en version 4.0.2 ou ultérieure, et est compatible avec Microsoft Open R.

  • Pour faire fonctionner le solveur en mode Rcpp (entre autres), il peut être nécessaire d’installer Rtools sur Windows, et sur MacOS il faudra XQuartz et soit XCode ou une version de gfortran pour avoir un compilateur C++ compatible.

1 Les Modèles

1.1 Quelques termes

  • Modèle : Un modèle est un système d’équations. Les équations servent à définir le comportement de l’économie. Dans l’exemple d’Opale, il y a deux types d’équations : des équations comptables et des équations comportementales. Les équations comptables servent à assurer l’équilibre comptable du modèle (par exemple : l’équilibre du PIB avec ses composantes calculées par le modèle). Les équations comportementales (en général économétriques) vont quant à elles définir les mécanismes et dynamiques de l’économie (par exemple la consommation des ménages dépend (entre-autres) du taux de chômage et du revenu disponible brut des ménages).

  • Variables endogènes : Il s’agit des variables qui sont résolues par le modèle pour établir la prévision. Elle vont être calculées en fonctions des dynamiques du modèles, des variables exogènes et des coefficients.

  • Variables exogènes : Il s’agit des variables à renseigner par l’utilisateur pour toutes les périodes. Le solveur les prend comme données et ne les modifie pas. Pour la prévision économique, il s’agit des variables que le prévisionniste peut moduler afin de refléter les hypothèses et jugements formulés pour orienter la prévision.

  • Coefficients : Dans le contexte du package, le terme de coefficient correspond aux coefficients économétriques des équations économétriques du modèle. Il est supposé que ceux-ci ne changent pas sur l’ensemble des observations, bien qu’il soit possible en théorie de renseigner manuellement des valeurs différentes selon les périodes.

1.2 L’objet modèle thoR.model

L’objet modèle, de la classe (S4) thoR.model définie dans le package, est central au fonctionnement de tresthor. Il contient les attributs suivants :

  • model_name : le nom du modèle.

  • equation_list : un data.frame contenant toutes les équations avec diverses informations (i.e. : leur index (id), leur noms, leur position dans le système et différentes versions de leurs formules.

  • Les vecteurs contenant le noms des variables par type de variable. Ces vecteurs sont utiles pour extraire les variables d’intérêt d’une base données. Par exemple : mon_data_frame[,mon_modele@exo_list].

    • endo_list : les variables endogènes du modèle.
    • exo_list : les variables exogènes du modèle.
    • coeff_list : les coefficients économétriques du modèle.
  • Le détail de chaque bloc du modèle décomposé par l’algorithme (voir la section concernée)

    • Des éléments logiques qui renseignent sur l’existence de chaque bloc : prologue,heart,epilogue.
    • Les matrices des jacobiennes symboliques de chaque bloc : prologue_jacobian,heart_jacobian,epilogue_jacobian.
    • Les vecteurs contenant les variables endogènes résolues dans chaque bloc : prologue_endo,heart_endo,epilogue_endo.
    • Les fonctions de passage du symbolique au numérique pour les équations et les matrices jacobiennes de chaque bloc : prologue_equations_f, heart_equations_f, epilogue_equations_f, prologue_jacobian_f, heart_jacobian_f, epilogue_jacobian_f.
    • Un indicateur algo pour savoir si l’algorithme de décomposition a été utilisé lors de la création du modèle.
  • Les informations sur l’utilisation de Rcpp : rcpp permet de savoir si les fonctions du modèle ont été traduites en Rcpp lors de la création du modèle et rcpp_source spécifie l’emplacement du fichier Rcpp correspondant au modèle (ce fichier s’appelle [nom_du_modele]_pomme_newton.cpp).

  • var_map est la liste des variables du modèle avec leur type et les équations où elles apparaissent.

    • names(model@var_map) permet de récupérer rapidement un vecteur contenant l’ensemble des variables du modèle.
  • variables_matrix est un data.frame qui liste pour chaque équation les variables endogènes qui apparaissent sans retard dans l’équation (ce sont les endogènes contemporaines qui sont résolues par ces équations).

1.3 L’algorithme de décomposition de modèle

La résolution de modèle passe par un algorithme de Newton-Raphson, ce qui implique l’inversion des matrices jacobiennes et une convergence de toutes les équations. Sur les modèles larges (qui contiennent un grand nombre d’équations), cela peut poser des problèmes car on se retrouve avec des matrices très larges (500x500 pour un modèle à 500 équations) et une solution convergente peut alors être difficile à atteindre. Afin de pouvoir résoudre les modèles larges, un algorithme1 permettant de décomposer ces modèles en trois sous-blocs peut être utilisé. Ces trois blocs sont ensuite résolus séquentiellement :

  • Le prologue contient les équations et les variables endogènes concernées qui peuvent être résolues en premier lieu indépendemment des autres variables (après que les premières variables du prologue sont calculées, si d’autres variables peuvent être résolues directement, elle font également parties du prologue).

  • Le coeur du modèle concerne les variables interdépendantes qui doivent être résolues en même temps. L’algorithme utilisé ne détermine qu’un seul coeur, même s’il peut exister à l’intérieur de celui-ci plusieurs blocs d’équations simultanées indépendants les uns des autres.

  • L’epilogue contient les équations et les variables qui peuvent être résolues simplement une fois que les variables du coeur sont calculées.

D’autres algorithmes décomposant les modèles en davantage de blocs peuvent être envisagés et seront à l’étude pour les améliorations du package à venir. Cependant, si augmenter le nombre de blocs permet de créer des blocs plus petits (et donc résolus plus rapidement), cela augmente le nombre d’itérations dans la résolution du modèle, donc un optimum sera à déterminer en fonction de la taille du modèle.

1.4 Construction du modèle : create_model()

La construction de modèle thoR.model passe par la fonction create_model(). Il y a deux manières de construire les modèles : soit manuellement en renseignant chaque composante du modèle, soit en passant par un input externe au format défini. Pour les modèles les plus larges, il est recommandé de passer par cette méthode. Les arguments de la fonction à renseigner sont :

  • model_name : le nom que l’on veut donner à l’objet créé.
  • model_source : le chemin d’accès au fichier texte si on souhaite créer le modèle par cette méthode.
  • algo : indiquer TRUE si on souhaite utiliser l’algorithme de décomposition de modèle (conseillé pour les modèles larges à plus de 100 équations) (TRUE par défaut)
  • rcpp : indiquer TRUE si on souhaite créer une version du modèle compatible avec le solveur Rcpp pour des résolutions plus rapide sur les modèles larges. (FALSE par défaut)
  • rcpp_path : le dossier où l’on souhaite stocker le fichier rcpp du modèle (par défaut il s’agit du répertoire de travail).
  • endogenous : Si le modèle est renseigné manuellement, il faut indiquer ici les variables endogènes du modèle sous la forme d’un vecteur. L’argument est ignoré si un model_source est renseigné.
  • exogenous : Si le modèle est renseigné manuellement, il faut indiquer ici les variables exogènes du modèle sous la forme d’un vecteur. L’argument est ignoré si un model_source est renseigné.
  • coefficients : Si le modèle est renseigné manuellement, il faut indiquer ici les coefficients du modèle sous la forme d’un vecteur. L’argument est ignoré si un model_source est renseigné.
  • equations : Si le modèle est renseigné manuellement, il faut indiquer ici les équations du modèle sous la forme d’un vecteur. L’argument est ignoré si un model_source est renseigné.
  • env : l’environnement dans lequel l’objet modèle sera créé. (par défaut il s’agit de globalenv())

1.4.1 Création de modèle à partir d’un fichier texte

La méthode la plus simple consiste à créer un fichier texte qui contient l’objet modèle. Pour visualier rapidement le format attendu, on peut utiliser la fonction model_source_example(). L’exemple présenté peut servir de structure de base.

Le fichier texte s’organise ainsi :

  • ligne 1 : contient le mot “endo”
  • ligne 2 : lister les variables endogènes sans guillemets et séparées par une virgule
  • ligne 3 : vide (mettre des commentaires éventuellement, cette ligne n’est pas lue par la fonction)
  • ligne 4 : contient le mot “exo”
  • ligne 5 : lister les variables exogènes sans guillemets et séparées par une virgule
  • ligne 6 : vide (mettre des commentaires éventuellement, cette ligne n’est pas lue par la fonction)
  • ligne 7 : contient le mot “coeff”
  • ligne 8 : lister les coefficients sans guillemets et séparés par une virgule
  • ligne 9 : vide (mettre des commentaires éventuellement, cette ligne n’est pas lue par la fonction)
  • ligne 10 : contient le mot “equation”
  • lignes 11 et au delà : écrire ligne par ligne les équations du modèles selon les règles syntaxiques .

Voici l’exemple de modèle correctement écrit dans un fichier .txt:

endo :
endovar1,endovar2,endovar3,endovar4
######Anything on this line will no be read######
exo :
exovar1,exovar2,exovar3,exovar4,exovar5
######Anything on this line will no be read######
coeff :
cf1,cf2,cf3
######Anything on this line will no be read######
equations:
delta(1,log(endovar1)) = endovar2 + lag(endovar3,1) +exovar1
delta(1,log(endovar2)) = cf3*endovar4 + cf1*lag(endovar2,1) +exovar2
equation_var3 : delta(1,log(endovar3)) = exovar4/exovar5 + cf2 * delta(1,log(lag(endovar3,2)))
equilibrium : endovar4 = endovar1 + endovar2 +endovar3

Pour créer un modèle à partir d’un fichier texte :

## Création rapide
create_model(model_name = "mon_modele",
             model_source = "mon_dossier/modele.txt")

## Creation détaillée
create_model(model_name = "mon_modele",
             model_source = "mon_dossier/modele.txt",
             algo = FALSE, rcpp = TRUE , rcpp_path = "mon_dossier/sousdossier_rcpp")

1.4.2 Création manuelle de modèle

En l’absence de fichier externe, on peut également créer manuellement le modèle :

## Création manuelle
create_model(model_name = "mon_modele_manuel",
             endogenous = c("x","y","z"),
             exogenous = c("a","b","c"),
             coefficients = c("coef_1", "coef_2"),
             equations = c(
               "delta(1,log(x)) = 0.5* delta(1,log(a)) - 0.2 * delta(1,log(z))",
               "mon_equation1 :delta(1,log(y)) = coef_1 + coef_2 * delta(1,log(b)) - 0.3 * lag(x,1)",
               "calcul_de_z:z = lag(c,1) + x + y")  ) 

1.4.3 Indexation et noms de variables

Lors de la création du modèle, les équations sont indexées par un index ayant en préfixe eq_##, et si des noms d’équations ont été spécifiés, ils sont également attribués. Les équations possèdent donc un index générique (id) et un nom. Si une équation n’a pas de nom spécifié, le nom attribué sera l’index.

Pour spécifier un nom : il faut précéder l’expression de l’équation par nom:, ex : conso : delta(1,log(conso))=a+b*log(c). Si des noms sont répétés, la fonction attribue un _2 aux doublons.

Pour visualiser cela, en utilisant l’exemple générique de modèle :

## Création manuelle
create_model(model_name = "model_source_example",
             model_source = model_source_example(TRUE)  ) 
model_source_example@equation_list %>% select(id,name,equation) 

1.5 Règles d’écriture de modèle

L’ensembe des fonctionnalités du package, de la création des modèles et équations à leur résolution, repose sur des lecteurs syntaxiques qui permettent l’analyse du texte et leur transformation. Il convient de suivre quelques règles pour spécifier correctement les équations et les variables.

1.5.1 Respecter la casse

Le langage R est sensible à la casse. Pour éviter tout bug lié au problème de majuscules et miniscules, les variables du modèle ainsi que les équations sont systématiquement transformées en minuscules, et de même pour les fichiers modèles. Il convient donc d’ajuster le nom des variables dans la base de données en appliquant la même transformation de façon à pouvoir mobiliser les données pour l’utilisation du solveur et autres fonctionnalités.

1.5.2 Nommer (correctement) les variables

Les caractères acceptés pour les variables sont : les lettres minuscules, les chiffres et les underscore (ensemble regex : [a-z0-9_]). Le premier caractère doit être une lettre (minuscule).

1.5.3 Les équations

  1. Les équations peuvent contenir les opérateurs suivants : + / * - ^ . Une multiplication doit être signalée par un * :
  • x = a*(b+c) est correct.
  • x = a(b+c) n’est pas correct.
  1. Elles doivent contenir un et un seul signe =.

  2. Dans un thoR.model, pour nommer une équation, il faut écrire le nom de l’équation suivi de : . Cette pratique ne s’applique pas pour la création d’équation thoR.equation car le nom est à renseigner dans la fonction.

  • equation_conso:delta(1,log(conso))=a+b
  1. Les différences s’écrivent au format delta(n,variable) , où n est un entier positif, et variable est la variable avec une transformation au besoin. Des multiples transformations peuvent ne pas être prises en compte.
  • delta(1,log(conso) = a +b fonctionnera.
  • delta(1,log(conso/lag(ipc,1)))= a +b risque de poser problème.
  • delta(1,x) équivaut à écrire x - lag(x,1)
  1. Les variables retardées sont à signaler avec lag(x,n) où x est la variable et n le retard à appliquer, avec ou sans signe -. Les deux seront interprétés de la même manière. Les lag ne doivent contenir que la variable. En cas de composition avec une fonction, le lag s’écrit au plus près de la variable. De même en cas de variable composée, il faut détailler tous les lags, ou alors créer une variable synthétique.
  • log(lag(conso,1)) et non pas lag(log(conso),1).
  • lag(conso,1)/lag(ipc,1) et non pas lag(conso/ipc,1). Le plus simple étant de définir une variable dans une équation conso_reelle = conso/ipc puis d’écrire lag(conso_reelle,1). Cette pratique est d’ailleurs recommandée pour permettre la bonne utilisation des fonctions d’analyse du package du type contributions dynamiques ou estimation rapide de coefficients.
  1. Plusieurs fonctions mathématiques sont supportées. Il s’agit des fonctions à un argument prises en charge par le package Deriv qui permet de calculer les dérivées symboliques. Elles sont visualisables dans le vecteur par thor_functions_supported.
  • expm1, log2, log, log1p, exp, logb, acosh, asinh, sinpi, cospi, asin, sinh, cosh, log10, atanh, tanpi, acos, sqrt, sin, atan, tanh, abs, cos, tan, sign

1.5.4 Identification adéquate du modèle

Le modèle devra être correctement identifié. La fonction create_model() vérifie cela, et signale en cas d’erreur. Un modèle correctement identifié contient :

  • autant d’équations que d’endogènes
  • chaque équation contient au moins une endogène
  • l’ensemble des équations doit permettre de résoudre l’ensemble des endogènes

1.6 Parcourir le modèle

Les différents attributs de l’objet modèle permettent de mieux comprendre la construction de celui-ci. Par exemple, la décomposition en trois blocs permet d’avoir une idée des variables qui sont calculées à la fin et donc davantage affectées par les autres endogènes.

Des fonctions permettent d’accéder directement aux informations du modèle par équation ou par variable.

  • La fonction equation_info_model() prend en argument un vecteur contenant des noms ou index d’équation, ainsi que le modèle en question, et retourne les informations de chacune de ces équations.

  • La fonction var_info_model() prend en argument un vecteur contenant des variables du modèle, ainsi que le modèle en question, et retourne les informations de chacune de ces variables.

equation_info_model(c("equilibrium","eq_1"),model_source_example)
## 
## ************************
## 
##  EQUATION ID: eq_1 | NAME : eq_1 |  PART : heart
## 
##  ENDOGENOUS VARIABLES:
## endovar1, endovar2, endovar3
## 
##  EXOGENOUS VARIABLES:
## exovar1
## 
##  COEFFICIENT VARIABLES:
## 
## 
##  FORMULAS:
##  delta(1,log(endovar1))=endovar2+lag(endovar3,1)+exovar1 
## 
## ************************
## 
##  EQUATION ID: eq_4 | NAME : equilibrium |  PART : heart
## 
##  ENDOGENOUS VARIABLES:
## endovar4, endovar1, endovar2, endovar3
## 
##  EXOGENOUS VARIABLES:
## 
## 
##  COEFFICIENT VARIABLES:
## 
## 
##  FORMULAS:
##  endovar4=endovar1+endovar2+endovar3
var_info_model(c("endovar4","cf3"),model_source_example)
## 
## ************************
##  
##  INFORMATION ON endovar4
## 
##  TYPE:
## endogenous, heart
## 
##  EQUATIONS:
## eq_2,equilibrium 
## 
##  EQUATIONS FORMULAS:
## eq_2 eq_2 : delta(1,log(endovar2))=cf3*endovar4+cf1*lag(endovar2,1)+exovar2
##  
##  eq_4 equilibrium : endovar4=endovar1+endovar2+endovar3
##  
## 
##  
## ************************
##  
##  INFORMATION ON cf3
## 
##  TYPE:
## coefficient
## 
##  EQUATIONS:
## eq_2 
## 
##  EQUATIONS FORMULAS:
## eq_2 eq_2 : delta(1,log(endovar2))=cf3*endovar4+cf1*lag(endovar2,1)+exovar2
##  
## 
## 

1.7 Modifications de modèles

A partir d’un modèle existant, il est possible de modifier ce dernier :

  • En intervertissant des variables endogènes et exogènes : model_endo_exo_switch(). Il faut spécifier autant d’endogènes que d’exogènes et la fonction vérifie que la nouvelle version du modèle est correctement identifiée. Cette fonction est particulièrement utile pour créer des modèles inversés utilisés notamment lorsqu’il faut calculer les variables résiduelles sur le passé en conservant la valeur observée des variables d’intérêt.

  • En ajoutant des équations de type thoR.equation : model_equations_add()

  • En enlevant des équations en spécifiant le nom ou l’index : model_equations_remove(). Il faut alors spécifier quelles sont les endogènes à retirer. Si les endogènes en question apparaissaient dans d’autres équations, elles deviennent des variables exogènes du modèle.

Ces fonctions créent un nouvel objet modèle dont le nom est à spécificier dans l’argument new_model_name. Il est possible de spécifier l’utilisation de Rcpp et de la décomposition du système comme dans create_model().

Exemples d’utilisation :

model_endo_exo_switch(base_model = model_source_example,
                      new_model_name = "mse_version2",
                      new_endo = c("exovar2"),
                      new_exo= c("endovar2"))

model_equations_remove(base_model = model_source_example,
                       new_model_name = "mse_version3",
                       equations_to_remove = c("eq_2") ,
                       endos_to_remove = c('endovar2')) ## Dans ce exemple endovar2 deviendra une variable exogène

##On crée une nouvelle équation
create_equation('new_equation',formula = "new_var = cf_4 *exovar2/endovar3",endogenous = "new_var",coefflist = c('cf_4'))

##On l'ajoute au modèle
model_equations_add(base_model = model_source_example,
                    new_model_name = "mse_version4",
                    thor_equations_add = list(new_equation))

1.8 Sauvegarde de modèle

  • La fonction save_model() permet de sauvegarder un objet thoR.model au format .rds pour réutilisation future. Elle prend en argument l’objet thoR.model ainsi que le chemin d’accès souhaité (par défaut il s’agit du répertoire de travail).

  • La fonction load_model() permet de charger un modèle existant. S’il a été créé avec rcpp=TRUE, alors les fonctions Rcpp spécifiques au modèle seront compilées à cette étape si besoin.

  • La fonction export_model() permet d’exporter un modèle existant au format .txt réutilisable par create_model(). Cette fonction est utile si le modèle a été créé manuellement ou a subit des modifications.

save_model(mse_version3,folder_path = "mes_modeles")

##Deux manières de charger un modèle : nom + dossier
load_model(model = "mse_version3",folder_path = "mes_modeles")
load_model(file = "mes_models/mse_version3.rds")

export_model(mse_version4,filename = "mes_modeles/nouveau_mod.txt")

2 Résolution du modèle

2.1 Les données

Une fois le modèle créé et avant de pouvoir le résoudre, il faut s’assurer d’avoir une base de données compatible. Le solveur établit des vérifications sur la base et signale les problèmes. Voici une checklist pour constituer la base de données :

  • La base doit contenir toutes les variables du modèle. Pour vérifier cela : setdiff(names(model@var_map),names(database)) doit être vide. Une fonction améliorée pour réaliser ce diagnostique est data_model_checks()(voir la section). Il faut vérifier la casse dans les noms des variables de la base ; tresthor ne fonctionnant qu’avec des lettres miniscules.

  • Les exogènes doivent être renseignées sur toute la période de prévision, de même pour les coefficients. Pour vérifier cela, utiliser la fonction na_report_variables_times() qui permet de vérifier si les variables sont manquantes sur certaines périodes (voir la section).

  • Si le modèle fait appel à des variables retardées, il faut suffisamment d’observations sur le passé pour ces variables.

  • Le solveur utilise l’observation n-1 pour initialiser ses calculs. Cela signifie qu’il faut au minimum une observation passée complète (qui ne produit pas de NA ou NaN) et plus en cas de présence de variables retardées dans les équations du modèle.

  • La base de données doit contenir une colonne qui sert d’indicateur temporel (l’argument s’appelle index_time sur l’ensemble du package). Un indicateur temporel peut prendre n’importe quel format du moment que :

    • les observations sont rangées par ordre croissant (si on utilise la classe Date ou un indicateur numérique), alphabétique sinon,
    • toutes les observations sont renseignées pour cette colonne (pas de NA)
    • il n’y a pas de doublons.
    • si l’indicateur temporel est au format Date, la fonction reformat_date() permet de tranformer les dates en chaînes de caractère, avec un format visuellement plus confortable pour les données annuelles, semestrielles, trimestrielles et mensuelles. Quelques exemples ci dessous (voir aussi pour un autre exemple appliqué) :
dates_annuelles <- seq.Date(as.Date("2010-01-01"), by="year", length.out = 5)
print(dates_annuelles)
## [1] "2010-01-01" "2011-01-01" "2012-01-01" "2013-01-01" "2014-01-01"
cat(reformat_date(dates_annuelles))
## 2010 2011 2012 2013 2014
dates_semestrielles <- seq.Date(as.Date("2010-01-01"), by="6 month", length.out = 5)
print(dates_semestrielles)
## [1] "2010-01-01" "2010-07-01" "2011-01-01" "2011-07-01" "2012-01-01"
cat(reformat_date(dates_semestrielles))
## 2010S1 2010S2 2011S1 2011S2 2012S1
dates_trimestrielles <- seq.Date(as.Date("2010-01-01"), by="quarter", length.out = 5)
print(dates_trimestrielles)
## [1] "2010-01-01" "2010-04-01" "2010-07-01" "2010-10-01" "2011-01-01"
cat(reformat_date(dates_trimestrielles))
## 2010Q1 2010Q2 2010Q3 2010Q4 2011Q1
dates_mensuelles <- seq.Date(as.Date("2010-01-01"), by="month", length.out = 5)
print(dates_mensuelles)
## [1] "2010-01-01" "2010-02-01" "2010-03-01" "2010-04-01" "2010-05-01"
# les mois s'afficheront dans la langue de l'installation de R de l'utilisateur
# puisque l'ordre alphabétique ne sera pas conservé, on ne peut pas utiliser ce format pour les données mensuelles comme indicateur temporel
cat(reformat_date(dates_mensuelles)) 
## janv. 2010 févr. 2010 mars 2010 avril 2010 mai 2010

2.1.1 Fonctions de vérification des données

Ces trois fonctions sont mises à disposition de l’utilisateur afin de pouvoir tester si les données sont suffisantes et repérer des problèmes éventuels sur la base.

  • data_model_checks() vérifie que toutes les variables du modèle sont dans la base de données. Elle retourne un élément logique : TRUE si toutes les données sont présentes, FALSE sinon. La fonction indique quel type de variables manque ainsi que le nom de ces variables. La fonction prend en argument une base de données et un objet thoR.model ou thoR.equation.

  • na_report_variables_times() vérifie qu’un set de variables est bien renseigné sur toutes les périodes demandées (sans NA donc). La fonction retourne le set de variables pour lesquelles des NA (observations manquantes) ont été trouvées.

  • times_solver_test_run() permet de tester si le solveur peut fonctionner sur des périodes demandées. Cette fonction ne résout pas de modèle, mais fait juste tourner les fonctions de passage du symbolique au numérique du modèle. Elle permet de tester si les calculs peuvent être effectués avec les données d’une base. Un diagnostique utile fourni par cette fonction est de détecter si certaines variables ont des valeurs en dehors du domaine de définition des fonctions qui les mobilisent (par exemple des 0 alors que la variable est utilisée en logarithme.) .

2.1.2 Ajouts de coefficients

Si les coefficients sont manquants mais estimés ailleurs, ils peuvent être ajoutés avec la fonction add_coeffs(). Cette fonction prend en argument un data.frame contenant les noms et valeurs des coefficients, la base de données cible et retourne une base de données avec les coefficients ajoutés sur toutes les périodes.

Les arguments pos.coeff.name et pos.coeff.value servent à indiquer les colonnes de la base de données des coefficients qui contiennent respectivement les noms et valeurs de ceux-ci. On peut renseigner la position de la colonne dans le data.frame ou bien le nom lui-même.

coeff_df <- data.frame(name  = c("coeff1","coeff2"),
                       value = c(0.23,0.78) )

donnees_completes <- add_coeffs(listcoeff = coeff_df, database = donnees_base , 
                                pos.coeff.name = 1, pos.coeff.value = 2, ##Specifier les colonnes où se trouvent le nom des coefficients et leur valeur.
                                overwrite = TRUE) #Pour remplacer les anciennes valeurs des coefficients 

2.2 Le solveur : thor_solver()

Avec le modèle et les données en place, il ne reste plus qu’à lancer la résolution du modèle.

La fonction thor_solver() prend en argument :

  • model : le thoR.model à utiliser.

  • first_period : la première période de projection. Attention à ne pas donner la période correspondant à la première observation de la base de données.

  • last_period : la dernière période de projection.

  • main_variable : un argument optionnel qui permet de visualiser directement les résultats du solveur sur cette variable. A associer avec l’argument main_variable_fx qui est une fonction à appliquer sur cette variable.

    • Un cas d’usage de cette option avec un modèle macroéconomique où la variable d’intérêt est le PIB mais on s’intéresse surtout à son taux de croissance. On précisera alors main_variable = pib et main_variable_fx = function(x){100*((x/dplyr::lag(x))-1)},
  • database : la base de données complète

  • index_time : la variable qui sert d’indicateur temporel (first_period et last_period doivent en faire partie). Voir la section sur les données pour en savoir plus sur les contraintes sur cette variable. Par défaut, cette variable est “date”.

  • convergence_criteria : le critère de convergence (par défaut il est de 1e-9). Pour des modèles complexes, il est conseillé d’en rester à ce niveau. R et l’algorithme de Newton-Raphson montreront leurs limites à partir de 1e-14 même pour les plus petits modèles.

  • dynamic_convergence : le nombre maximum d’itérations (par défaut 10) dont le solveur dispose pour tenter de converger avant d’augmenter le critère de convergence :

    • Pour chaque bloc et chaque période le solveur tentera de résoudre le système avec le critère de convergence imposé. Si au bout du nombre de périodes spécifié la convergence n’est pas atteinte, alors le critère est multiplié par 10 pour faciliter la résolution. Le critère de convergence revient à sa valeur fixée dans les arguments pour chaque bloc à chaque période où un algorithme de Newton est initialisé.
    • L’option adv_diag permet de publier dans la console le critère de convergence maximal que le solveur a utilisé pendant les résolutions.
  • rcpp : si le solveur doit mobiliser la version Rcpp du modèle (possible que si le modèle a été créé avec l’option rcpp = TRUE). Il compilera alors au besoin les fonctions Rcpp qui ont été créées pour résoudre le système.

Pour résoudre le modèle, le solveur charge le modèle et les fonctions spécifiques créées. Puis, il résout par un algorithme de Newton-Raphson chaque bloc à chaque période, en se basant sur l’observation précédente comme critère d’initialisation. Les périodes sont estimées séquentiellement de manière à prendre en compte les estimations des endogènes sur les périodes antérieures, si celles-ci font partie de l’horizon de projection demandé.

La fonction retourne la base de données avec les endogènes modifiées par leurs valeurs en prévision.

Voici un exemple d’utilisation de la fonction avec les arguments indispensables :

donness_prevision <- thor_solver(model_source_example, 
                                first_period = "2000Q1", last_period = "2020Q4",
                                database = mes_donnees, index_time = "dateQ" )

2.2.1 Améliorer les performances du solveur

Pour les modèles larges comme Opale, la résolution peut prendre du temps en fonction de la puissance de l’ordinateur utilisé. Voici quelques pistes pour améliorer les vitesses de calcul.

  • Pour le plus petits modèles, l’utilisation de la décomposition du modèle peut être contre-productive car cela implique trois fois plus d’itérations, alors que le gain de temps sur le calcul des matrices inverses est minimal. Il est donc conseillé pour les petits modèle de se passer de l’algorithme (algo = FALSE) lors de la création du modèle.

  • Pour les modèles les plus larges, la décomposition est indispensable, elle permet de réduire significativement la taille des matrices jacobiennes.

  • L’utilisation de Rcpp (rcpp= TRUE dans le solveur et la création du modèle) fait gagner un temps considérable à la résolution des modèles larges.

    • Il y a un coût fixe à la compilation des fonctions Rcpp au chargement du modèle, qui peut être largement neutralisé si la projection s’effectue au long terme.
    • à l’inverse si la projection ne doit se faire que sur quelques périodes (3 ou 4), alors il peut être préférable de ne pas utiliser l’option Rcpp.
    • Par ailleurs, les fonctions Rcpp compilées dans une session R restent en mémoire (même si l’environnement global est vidé), donc si plusieurs sont mobilisés et que de ce fait les fonctions Rcpp actives sont remplacées, si la session R n’a pas été redémarrée alors la mobilisation des modèles précédents ne prend pas de temps supplémentaire.
  • Le critère de convergence augmenté peut diminuer le nombre d’itérations. Attention à ne pas trop l’augmenter non plus sinon les résultats n’ont plus trop de sens.

  • L’utilisation de Microsoft R Open (disponible en version 4.0.2) est particulièrement pertinente dans le cas des modèles larges et avec Rcpp. Cette distribution de R fonctionne comme R 4.0 mais elle est optimisée pour le calcul algébrique. Couplée à l’utilisation de Rcpp, nous avons obtenus des gains de performance de facteur 200 sur une même résolution pour Opale (projection sur 150 périodes réalisée en moins de 1 seconde).

2.2.2 Utilisation en dehors de l’analyse économique

Il est possible d’utiliser les solveurs de tresthor pour résoudre des systèmes d’équations ou des équations isolées (linéaires ou non linéaires, dynamiques ou statiques) du moment que ceux-ci répondent au critère d’identification du modèle attendu.

Mise à part la construction du modèle ou de l’équation, il faudra juste déterminer une condition d’initialisation valide (i.e. qui répond aux domaines de définition des fonctions du modèle). Le solveur utilise la base de données pour déterminer la condition d’initialisation pour la résolution : il prend la dernière observation avant la première période de résolution.

Il convient donc de créer une base donnée qui contiendra au moins 2 observations (plus si le système est dynamique et prend des données sur le passé) : la première (obs = 0 dans l’exemple ci-dessous) contiendra des valeurs d’initialisation valides et la deuxième (obs = 1 dans l’exemple ci-dessous) contiendra par la suite les résultats du solveur, ainsi que les valeurs des exogènes et coefficients s’il y en a.

L’exemple ci-dessous permet de résoudre le système suivant : \[ \begin{cases} a + log(b) = 2\\a + c = 4\\log(b) + c = 3 \end{cases} \]

create_model(model_name = "exemple",
             equations = c("a + log(b) = 2",
                           "a + c = 4",
                           "log(b) + c = 3"),
                           endogenous = c("a","b","c") ,algo = FALSE)

donnees <- data.frame(obs = c(0,1), ## obs servira d'indicateur de temps
                        a = c(0,NA),
                        b = c(1,NA), ## b est utilisée en log donc 0 n'est pas une valeur initiale valide 
                        c = c(0,NA) )

solution <- thor_solver(exemple,first_period = 1,last_period = 1,
                          database = donnees , index_time = "obs") %>% 
            filter(obs == 1) %>% select(all_of(exemple@endo_list))

Cela produit la solution suivante :

\[ \begin{cases} a = 1.5\\b = 1.6487\\c = 2.5 \end{cases} \] Le même processus peut être utilisé pour résoudre des équations, par exemple : \[ log(x) + x = 4 + exo_1 \]

create_equation("eq_exemple",
                formula = "log(x) + x = 4 + exo_1",
                endogenous = "x")

donnees <- data.frame(obs = c(0,1), ## obs servira d'indicateur de temps
                        x = c(1,NA),
                    exo_1 = c(2,2)) ## bien renseigner la valeur de l'exogène sur les deux observations, celle de obs = 1 est utilisé pour la résolution

solution <- thor_equation_solver(eq_exemple,first_period = 1,last_period = 1,
                          database = donnees , index_time = "obs") %>% 
            filter(obs == 1) %>% select(paste0(eq_exemple@endogenous,".simul"))

Cela produit la solution suivante :

\[ x = 4.4967 \] Si l’équation à résoudre ne contient aucune variable exogène ni aucun coefficient, et qu’elle est statique (pas de valeurs retardées), c’est-à-dire que la formule ne contient que des chiffres et la variable endogène au temps \(t\), alors on peut utiliser la fonction quick_solve(). Elle prend en argument la formule (mêmes contraintes syntaxiques que pour les autres fonctions), le nom de l’endogène et une valeur initiale valide pour faire démarrer le solveur.

quick_solve("x^2 + log(x) - 3 = 0", "x", init = 1)
## 
##  x = 1.59214293705809

3 L’objet thoR.equation

Le package contient quelques fonctions pour analyser rapidement les résultats de la prévision. Ces analyses passent par la relecture post-prévision des équations comportementales du modèle. L’objet thoR.equation permet de se focaliser sur une équation précise du modèle pour avoir une lecture plus claire des mécanismes et dynamiques économiques sous-jacents au modèle.

3.1 Caractéristiques des équations

N’importe quel type d’équation peut-être créé en objet thoR.equation du moment que l’équation respecte les règles syntaxiques évoquées plus haut. La seule différence est que le nom de l’équation n’est pas à inscire dans la formule comme cela est le cas pour les modèles. L’objet S4 contient les attributs suivants :

  • equation_name : Le nom de l’équation.

  • formula : La formule telle qu’initialement renseignée.

  • new_formula : La formule transformée utilisée par le solveur d’équation thor_equation_solver

  • coefflist : Un vecteur contenant les variables déclarées comme coefficients. Cela est utile si on veut que l’équation soit utilisée pour estimer ces coefficients à l’aide d’outils économétriques. Au moment de la création du modèle, un algorithme tente d’ordonner ces coefficients par ordre d’apparition dans l’équation, pour pouvoir facilement faire le lien avec les fonctions économétriques.

  • endogenous : La variable endogène de l’équation (il n’y en a qu’une).

  • exogenous : Le vecteur des variables exogènes de l’équation.

  • LHS et RHS : Les parties gauche et droite de l’équation transformées pour utilisation avec des fonctions économétriques (les coefficients ont été retirés. Pour une utilisation avec la fonction lm(), il suffirait alors de combiner les deux parties avec un paste(LHS, RHS, sep = ~), en supposant que l’équation est correctement spécifiée pour permettre cela.

  • maxlag : le nombre de retards mobilisés par l’équation. Ce nombre est estimé par analyse syntaxique. Si cette analyse échoue, un nombre par défaut spécifié lors de la création de l’objet est utilisé. Cela est notamment utile pour déterminer l’échantillon de données nécessaires pour couvrir une plage de périodes spécifiée. i.e. Si on veut estimer de 2000Q1 à 2010Q4 et que maxlag=4, alors il faudra procurer des données allant de 1999Q1 à 2010Q4.

  • jacobian : La dérivée symbolique de la formule new_formula par l’endgoène spécifiée.

  • eq_f et jac_f les fonctions d’évaluation numérique de l’équation et de sa dérivée.

  • econometric_safe : Lors de la création de l’objet, un test est effectué pour savoir si la partie droite de la formule, en fonction de sa spécification et de ses paramètres, est uilisable facilement avec la fonction lm(). Elle permet de signaler à l’utilisateur, si la valeur est FALSE, que l’estimation économétrique va demander quelques ajustements au niveau de la formule. Si la valeur est TRUE cela signale une spécification correcte (mais sans garantie…).

3.2 Construction de l’objet équation

Il existe deux méthodes pour créer un objet thoR.equation :

  1. En utilisant create_equation(), c’est-à-dire en la créant manuellement.
    • On renseigne le nom à donner à l’équation, la formule selon les règles, la variable endogène et les coefficients. Les variables exogènes sont déterminées automatiquement.
  2. En faisant appel à une équation d’un thoR.model avec la fonction equation_from_model().
    • On ne renseigne que le nom ou l’index de l’équation, le modèle et la variable endogène. Les coefficients de l’équation seront déterminés par les coefficients du modèle. Optionnellement, on peut donner un autre nom à l’équation que celui du modèle, utile notamment s’il n’y a pas de nom dans le modèle et que l’index est utilisé à la place.

Dans les deux cas on peut spécifier l’environnement (env) où sera créé l’objet et une valeur par défaut de maxlag (nombre de retards maximum de l’équation) si l’algorithme échoue à le déterminer.

Les fonctions de création d’équation analysent ensuite les formules (tests et transformations) et créent l’objet. Le vecteur des coeffients est normalement réordonné pour correspondre à l’ordre d’apparition dans l’équation (dans la mesure du possible en fonction de la complexité de la formule).

## création de l'équation manuelle
create_equation(equation_name = "eq_prix",
                formula = "y = b0 +a1*delta(1,x1) + a2*lag(x2,1) + residu",
                endogenous = "y",coefflist =   c("a1","a2","b0"))
## 
## Equation variables identified :
## a1 a2 b0 residu x1 x2 y
##  Exogenous variables :
## residu x1 x2
##  RHS of equation appears to be ok for econometric estimations with the current coefficient list.
## les coefficients sont réordonnés par ordre d'apparition dans la formule:
eq_prix@coefflist
## [1] "b0" "a1" "a2"

L’exemple de création d’équation à partir d’Opale :

## création de l'équation à partir du modèle
create_model("Opale", 
             model_source = system.file("Opale","opale.txt",package = "tresthor") )

## On récupère l'équation de consommation des ménages d'Opale
equation_from_model(equation_name_or_id = "conso_m",model = Opale,
                    endogenous = "td_p3m_d7_ch",new_name = "conso_opale")

3.3 Les variables résiduelles

Les équations économétriques sont structurantes au modèle économique, ce sont elles qui vont guider les dynamiques du modèle.

Pour pouvoir correctement les intégrer à un modèle, il est primordial d’inclure dans la formule une variable qui représentera les résidus de l’équation. A noter qu’il est possible d’utiliser des variables résiduelles dans des équations qui reflètent juste une relation entre deux variables - sans structure économétrique - pour pouvoir correctement calibrer les données en fonction du modèle.

Il faut faire attention à bien retirer les résidus retardés si la variable endogène est présente dans l’équation avec des retards. La règle générale consiste à toujours retirer le résidu correspondant à chaque instance temporelle de la variable endogène que l’on met dans l’équation.

Les équations économétriques à intégrer dans le modèle devront donc prendre ces formes (en fonctions des cas de figure). On note \(res\) la variable résiduelle et \(y_t\) la variable endogène :

  • Une MCO simple : \(y_t = \beta_0 + \beta_1*x_t + res_t\)

  • Equation avec un terme AR1 : \(y_t = \beta_0 + \beta_1*x_t + \beta_2*(y_{t-1} - res_{t-1}) + res_t\)

  • Modèle à correction d’erreur : \(\Delta_1 y_t = \beta_0 + \beta_1*\Delta_1 x_t - \mu *(y_{t-1}-\alpha_0 - \alpha_1 * x_{t-1} - res_{t-1}) +\Delta_1 res_t\)

  • Equation non-économétrique : \(y_t = \gamma * x_t + res_t\)

Si l’équation est intégrée à un objet thoR.model, il faudra bien penser à déclarer la variable résiduelle en tant qu’exogène (ou endogène selon les cas).

Les résidus jouent un rôle important dans le modèle. Sur le passé, ils permettent d’assurer l’équilibre du modèle. Une relation économétrique n’est jamais parfaite, il y a toujours un écart entre la variable expliquée et les variables explicatives. Avant de lancer la prévision par un modèle il convient donc de recaluler la valeur de ces résidus sur le passé, car on prend les données observées comme telles.

Pour ce faire, on déclare les endogènes des équations économétriques comme variables exogènes et les résidus comme variables endogènes, puis on résout pour la variable résiduelle sur le passé. Les données sont alors correctement calibrées pour la prévision.

Il y a plusieurs moyens de faire cela avec tresthor :

  • soit on crée un modèle inversé en utilisant la fonction model_endo_exo_switch() en passant l’ensemble des variables résiduelles des équations du modèle en variables endogènes et les variables expliquées de ces équations en exogène. On résoud ensuite le modèle sur le passé.

  • soit on utilise pour chaque équation où les résidus apparaissent la fonction thor_equation_solver() sur le passé en créant les équations réciproques où le résidu est endogène

  • soit on utilise la fonction simulate_equation() sur chaque équation (en laissant le résidu exogène) sur le passé, le résidu étant automatiquement recalculé par cette fonction.

3.4 Cas pratique : une équation de consommation

3.4.1 Modélisation MCE

On construit un exemple de MCE pour modéliser le comportement de consommation des ménages qui servira d’illustration pour le reste des fonctions. On note \(conso_t\) la consommation des ménages, \(rdb_t\) le revenu disponible brut des ménages et \(chom_t\) le taux de chômage. On propose le modèle suivant :

\[ \Delta_1(log(conso_t)) = c0 + c1*\Delta_1(log(rdb_t))+c2*\Delta_1(chom_t)+ m*(log(conso_{t-1})-lt0 - lt1*log(rdb_{t-1})) \\ \text{avec } m<0 \] Pour créer cette équation dans tresthor, il faut bien penser à ajouter les résidus dans la formule :

create_equation("conso_ex",
                formula = "delta(1,log(conso)) = c0 + c1 * delta(1,log(rdb)) + c2 * delta(1,chom) +
                            m*(log(lag(conso,-1)) - lag(residu,-1)-lt0 - lt1*log(lag(rdb,1)))+
                            delta(1,residu)"  ,
                coefflist = c("c0","c1","c2","m","lt0","lt1"),endogenous = "conso")
## 
## Equation variables identified :
## c0 c1 c2 chom conso lt0 lt1 m rdb residu
##  Exogenous variables :
## chom rdb residu
##  RHS of equation does not appear to be ok for econometric estimations with the current coefficient list.

RHS of equation does not appear to be ok for econometric estimations with the current coefficient list.

Cette erreur signale que l’équation en l’état ne peut pas être estimée par les MCO.

3.4.2 Les données

Pour constituer la base de données pour travailler avec cette équation, on récupère les trois séries nécessaires (données pour la France sur la consommation des ménages, le revenu disponible brut des ménages et le taux de chômage) de l’Insee via la plateforme Dbnomics en utilisant le package rdbnomics.

## téléchargement des données et constitution de la base
library(rdbnomics)
## Visit <https://db.nomics.world>.
ids <- set_names(c("INSEE/CNT-2014-PIB-EQB-RF/T.CNT-EQUILIBRE_PIB.S14.P3.D-CNT.VALEUR_ABSOLUE.FE.L.EUROS.CVS-CJO",
                   "INSEE/CNT-2014-CSI/T.CNT-SOLDES_COMPTABLES_SECTEURS_INST.S14.SO.B6.VALEUR_ABSOLUE.FE.EUROS.CVS-CJO",
                   "INSEE/CHOMAGE-TRIM-NATIONAL/T.CTTXC.TAUX.FR-D976.0.00-.POURCENT.CVS"),
                 c("conso","rdb","chom"))

donnees_conso <- ids %>% imap(~{rdb(ids=.x) %>%  
                                select(period,value) %>%  
                                filter(period >= as.Date("1990-01-01") & period <= as.Date("2020-10-01")) %>% 
                                rename(date = period, !!.y := value) %>% as.data.frame()}) %>% 
                          reduce(full_join,by="date") %>% 
                          mutate(residu = 0, # on ajoute une colonne pour la variable résiduelle
                                 date = reformat_date(date)) #optionnel, pour transformer le format de date 

3.5 Visualiser les équation

Les fonctions suivantes permettent de transformer les formules en une version plus lisible.

  • formula_with_coeffs() prend en argument une formule (formula soit une chaîne de caractère, soit directement un objet thoR.equation), si la formule n’est pas un objet thoR.equation, il faut indiquer dans un vecteur les coefficients (coefflist), une base de données (database) qui contient les coefficients de l’équation en noms de variable. round permet de préciser le nombre de décimales à afficher. L’exemple ci-dessous utilise une base données avec uniquement les coefficients, cependant dans la pratique, un base utilisable par le solveur (modèle ou équation) peut-être mobilisée ici puisqu’elle contient les coefficients.
## création d'une base données avec des coefficients quelconques
data_coeffs <- data.frame(c0 = 0.5,c1=0.3,c2=-0.01, m = -0.4,lt0=0.01,lt1=0.98765432)

cat(formula_with_coeffs(conso_ex,database = data_coeffs,quiet = TRUE))
## delta(1,log(conso)) = 0.5 + 0.3 * delta(1,log(rdb)) + -0.01 * delta(1,chom) +
##                             -0.4*(log(lag(conso,-1)) - lag(residu,-1)-0.01 - 0.9877*log(lag(rdb,1)))+
##                             delta(1,residu)
## on peut ajouter ces coeffficients à la base de données existante :
donnees_conso_c <- data_coeffs %>% t() %>% as.data.frame() %>% mutate(names = rownames(.)) %>%
                         add_coeffs(donnees_conso,pos.coeff.name = 2 , pos.coeff.value = 1)

# donnees_conso_c <- cbind(donnees_conso, data_coeffs) # fonctionne aussi car les coefficientsne sont pas dans la base et le format de data_coeffs s'y prête.

### et utiliser la base complète pour la fonction :
# cat(formula_with_coeffs(conso_ex,database = data_coeffs,quiet = TRUE))
  • Pour présenter les équations dans des documents (Word, \(\LaTeX\), html, markdown), la fonction formula_latex() génère le code LaTeX pour afficher l’équation. Elle prend en argument un objet thoR.equation ou une formule en chaîne de caractère (dans ce cas il faudra également pr