Skip to contents

Les graphiques de prévision

Le but est d’homogénéiser le plus possible les graphiques de prévisions. Les consignes s’appliquent surtout aux graphiques en séries temporelles, avec une fréquence de données trimestrielle. Les consignes sont quasi obligatoires (i.e. obligatoires sauf très bonne raison de ne pas les suivre).

  1. On suit d’abord les consignes qui valent pour les graphiques de séries temporelles

    1. theme_ofce(),

    2. format <date>, et on appelle sa colonne d’index temporel time ; les dates sont au format quotidien ("2024-01-31") par exemple. Par convention, on note les données trimestrielles "yyyy-[01; 04; 07; 10]-01" et les années au premier janvier (mais voir plus bas pour une nuance importante).

    3. scale_ofce_date(),

    4. On utilise les options pour le geom_point_interactive(linewidth=0.5, shape=21, stroke=0.25, col="white", hover_nearest=TRUE)

    5. Chaque .qmd doit commencer par un chunk r qui est formaté comme suit – cette instruction charge les packages nécessaires, définit des options par défaut et ajoute quelques fonctions utiles :

```{r}
#| include: false
ofce::init_qmd()
```
  1. On évite les graphiques spaghetti, double échelle. On privilégie les facets et si possible on répète en gris clair les données des autres facets pour permettre la comparaison.

  2. On place la légende, dans la mesure du possible, dans le graphique. Dans la mesure du possible, on met la légende sous forme de labels.

  3. On indique, lorsque c’est pertinent, la période de prévision par un grisé et en utilisant la fonction annotate_prevision(posy = y), où y est la coordonnée à laquelle on veut que le texte “prévisions” s’affiche (si size=0 il n’y aura pas de texte). On n’utilise pas d’autres moyens pour indiquer les prévisions (par exemple des pointillés).

  4. On rend le graphique interactif, en utilisant girafy et :

    1. avec un geom_point_interactive(aes(tooltip=tooltip, data_id = time)) – et donc pas d’autres éléments interactifs sauf si nécessaire (notamment les barres),

    2. en construisant un tooltip le plus signifiant possible,

    3. en mettant à disposition les données avec un margin_download(),

  5. On utilise ofce::ofce_caption() afin de normaliser l’aspect et la présentation des notes de graphique. ofce_caption() permet de spécifier la source, les notes, les indications de lecture, le champ, le lien vers le code, un texte comme “prévisions OFCE 2025” systématique, etc… (reportez vous à l’aide de cette fonction qui est dans le package ofce).

  6. On met le code du graphique (le ggplot, etc…) dans le .qmd qui contient le texte. Cela permet d’éditer les textes, d’un côté, et de normaliser les présentations, de l’autre. La construction du tooltip est aussi à ce niveau pour pouvoir l’éditer. Il est nécessaire que le girafy() soit dans le qmd, sinon, l’objet interactif n’aura pas le bon format suivant les support (site, présentation, etc…).

  7. On utilise ofce::source_data() pour la construction des données, ce n’est pas obligatoire, mais très efficace et très recommandé pour le travail collaboratif (comme celui de la prévision). Voir la vignette correspondante. On met le code de construction dans un fichier .r, joint dont le nom est simple, signifiant, en minuscule et qui est enregistré au même niveau que le .qmd ou en dessous. On utilise source_data pour éviter de bloquer le fonctionnement de la compilation des qmd et faciliter la portabilité des codes de données – c’est important.

Chacun de ces éléments est détaillés et avec des exemples de codes ci-dessous.

Un exemple de graphique

L’exemple de graphique est celui qui apparaît sur toutes les fiches et la partie France en début de texte. Les données sont stockées dans un fichier excel, ce qui est une pratique qui devra disparaître, mais bon… Le code original se trouve dans le répertoire d’une prévision (fiches/data_pays.R). Il est ici un peu simplifié, parce que data_pays.R produit une fonction qui prend comme paramètre le pays qu’on veut afficher. C’est donc le même code pour tous les pays.

Il y a d’autres exemples dans le cahier de graphiques et les codes sont copiables directement.

Chargement des données et tooltip

code
gt <- function(x, na.rm = FALSE) (100*(x/lag(x,1)-1))
ga <- function(x, na.rm = FALSE) (100*(x/lag(x,4)-1))
data_pays <- fs::path_package("ofce", "extdata/data_pays.xlsx")
data_gpib <- read_excel(data_pays, sheet = "pib", col_names = TRUE) |>
  mutate(date = as.Date(date)+ days(45)) %>%
  mutate(across(c(usa, euz, deu, fra , ita, esp, gbr, jpn), ~gt(.x))) |>
  pivot_longer(cols=-date, names_to= "pays", values_to = "value")  |>
  mutate(
    variable = "Croissance du PIB",
    long = pays_long[toupper(pays)],
    tooltip = glue("<b>{long}</b><br>{date_trim(date)}<br>croissance trimestrielle du PIB : {round(value,1)}%"))

data_tcho <- read_excel(data_pays, sheet = "tcho", col_names = TRUE) |>
  mutate(date = as.Date(date)+ days(45)) |>
  pivot_longer(cols=-date, names_to="pays", values_to = "value") |>
  mutate(
    variable = "Chômage",
    long = pays_long[toupper(pays)],
    tooltip = glue("<b>{long}</b><br>{date_trim(date)}<br>Taux de chômage : {round(value,1)}% de la population active"))

data_prix <- read_excel(data_pays, sheet = "prix", col_names = TRUE) |>
  mutate(date = as.Date(date) + days(45)) %>%
  mutate_at(c("usa", "euz","deu","fra","ita","esp","gbr","jpn"), list(~ga(.))) |>
  pivot_longer(cols=-date, names_to="pays", values_to = "value") |>
  mutate(
    variable = "Inflation (prix à la consommation)",
    long = pays_long[toupper(pays)],
    tooltip = glue("<b>{long}</b><br>{date_trim(date)}<br>Glissement annuel des prix : {round(value,1)}%"))

data_solde <- read_excel(data_pays, sheet = "solde", col_names = TRUE) |>
  mutate(date = as.Date(date) -days(15))|>
  pivot_longer(cols=-c("date"), names_to="pays", values_to = "value") %>%
  mutate(value=as.numeric(value)) |>
  mutate(
    date = date,
    variable = "Solde public (APU)",
    long = pays_long[toupper(pays)],
    tooltip = glue("<b>{long}</b><br>{year(date)} (fin d'année)<br>Solde des administrations publiques : {round(value,1)}%"))

data <- bind_rows(data_gpib, data_tcho, data_prix, data_solde) |>
  filter(date > "2020-12-31") |>
  mutate(
    pays = toupper(pays),
    variable = factor(
    variable,
    c("Croissance du PIB", "Chômage", "Inflation (prix à la consommation)", "Solde public (APU)" )))

Les données, dans data, sont en format long, c’est à dire en ligne pour les dates, les pays et les variables. Rien de particulier à ce stade, si ce n’est la construction du tooltip, pour chaque ligne de data. On choisit un pays qui sera le principal, on fait un facet sur les variables (pib, chômage, inflation, déficit), et on ajoute les autres pays dans le fond, en gris clair pour la comparaison. Ce chunk peut tout à fait être dans un script r (data.r)1 et on utilise alors source_data pour l’exécuter avec data <- source_data("data.r").

Graphique proprement dit

Le graphique est construit ensuite par un ggplot et la mise en oeuvre des recommandations ci-dessus :

code
pays <- "FRA"
pays2 <- "EUZ"
# on définit les données, le pays en évidence, `pays2` moins mis en évidence et `autres` pas mis en évidence du tout
autres <- data |> distinct(pays) |> pull() |> setdiff(c(pays, pays2))

gg <- ggplot(data) +
  # l'`aes` commun à plusieurs geom, une bonne pratique qui rend le code plus simple
  aes(x=date, y=value, group = pays, color = variable, fill = variable) +
  # la bande grise qui indique les prévisions (spécifique à chque exercice de prévision)
  annotate_prevision(size=0) +
  # le `facet`, avec une échelle libre sur les y -- jamais de spaghettis
  facet_wrap(vars(variable), scales = "free_y") +
  # dans ce geom_line on filtre les pays en fond de graphique, qui sont en gris clair et en trait fin ce qui peut se faire aussi avec le pkg `{highlitght}`
  geom_line(
    data =  ~.x |> filter(pays %in% autres),
    size = 2, color = "gray85", linewidth = 0.25) +
  geom_line(data = ~.x |> filter(pays==pays2),
            size = 2, color = "gray35", linewidth = 0.25) +
  # le premier geom_point_interactive, pour les pays du fond, en gris clair
  geom_point_interactive(
    data =  ~.x |> filter(pays %in% autres),
    aes(tooltip = tooltip, data_id = date),
    size = 0.75, stroke = 0.25, shape = 21,
    col = "white", fill = "gray85",
    hover_nearest = TRUE) +
  # le second geom_point_interactive() pour le pays moins en évidence en gris foncé
  geom_point_interactive(
    data = ~.x |> filter(pays==pays2),
    aes(tooltip = tooltip, data_id = date),
    size = 0.75, stroke = 0.25, shape = 21,
    col = "white", fill = "gray35",
    hover_nearest = TRUE) +
  # les lignes colorées (en fonction des variables, voir l'`aes`) pour le pays principal (notez `!!pays` qui signifie utiliser la variable globale, pas celle du data.frame)
  geom_line(
    data = ~ .x |> filter(pays==!!pays)) +
  # les points pour le pays principal, de `shape 21` et colorés par `fill` avec un cercle blanc
  geom_point_interactive(
    data = ~ .x |> filter(pays==!!pays),
    aes(tooltip = tooltip, data_id = date),
    stroke = 0.5, shape = 21, col = "white", hover_nearest = TRUE) +
  # l'échelle des dates qui utilise la fonction du package `{ofce}`, avec des ticks mineurs pour les trimestres
  scale_ofce_date(
    date_breaks = "1 year",
    date_minor_breaks = "3 months",
    expand = expansion(mult=c(0.05, 0.0))) +
  # les labels des axes, on garde ça simple et on indique principalement l'unité
  labs(y="En %", x=NULL) +
  # le theme ofce (l'ordre des éléments n'est pas critique) !!!
  theme_ofce() +
  # les couleurs : procéder ainsi permet la normalisation des couleurs a posteriori
  PrettyCols::scale_color_pretty_d("Summer") +
  PrettyCols::scale_fill_pretty_d("Summer") +
  # l'aspect des labels, avec le bon nombre de chiffres après la virgule et l'utilisation de `scales::pretty_breaks()` pour des labels choisis astucieusement
  scale_y_continuous(labels = label_number(decimal.mark = ",", accuracy = 1, suffix = "%"),
                     breaks = scales::breaks_pretty()) +
  # on ne met pas les légendes de couleur ou de remplissage, ce n'est pas utile dans ce graphique, on essaye de ne pas les mettre en dessous de façon générale si elles sont nécessaire
  guides(color = "none", fill = "none") +
  # les notes et sources, standardisées, avec en particulier les derniers points connus.
  ofce_caption(
    source = "INSEE, Eurostat, instituts nationaux",
    note = "La zone euro est en gris foncé",
    dpt = "2024-04-01", dptf = "quarter")

Des couleurs par pays

La fonction ofce::scale_color_pays() applique une échelle de couleur (et de remplissage) qui suit le choix de la charte graphique de l’ofce. Pour que cela fonctionne, il faut que dans l’aes qui définit la couleur (aes(color=pays)) ou le remplissage (aes(fill=pays)) soit un code iso à 3 lettres. Si le code est un code eurostat il suffit de le préciser (format="eurostat" en paramètre à scale_color_pays()).

Cela donne comme code :

```{r}
ggplot(mes_donnees) +
  aes(x=time, y= pib, color = pays, fill = pays) +
  ...+
  scale_color_pays()
```

Interactivité

Pour finir, on enveloppe le graphique d’un girafy, qui fait fonctionner et normalise l’interactivité (dont les tooltips). On peut demander aussi à ce stade d’inclure l’instruction graph2prev(gg) qui effectue un enregistrement du graphique pour usages ultérieurs (par exemple dans une présentation).

code
girafy(gg)

Et voilà !

Des usages plus avancés

Composition de graphiques : patchwork

Pour empiler des graphiques on peut utiliser patchwork – c’est mieux que les autres solutions comme cowplot. La syntaxe est très simple, pour empiler l’un sur l’autre le graphique g1 et g2 il suffit d’écrire g <- g1/g2 et l’un à côté de l’autre g <- g1 + g2. L’objet g ressemble beaucoup à un graphique ggplot, mais pas complètement. Il peut cependant être utilisé dans {giraph} comme un ggplot. L’interactivité est alors synchrone pour tous les graphiques du patchwork.

Parfois on peut préférer patchwork à un facet, parce que patchwork autorise le mélange de graphique très différents (des line sur un panel, des bar sur un autre).

patchwork réunit les légendes communes, et les notes communes, aligne les axes et permet de faire des décorations communes. La syntaxe pour ces opérations avancées est sur l’aide de patchwork avec quelques exemples dans le cahier de graphiques.

Aligner des graphiques dans des chunks différents

On peut aligner les axes de graphiques dans des chunks différents avec patchwork. C’est assez simple et il y a un exemple sur les graphiques “immobiliers” du cahier de graphiques. On définit un graphique de référence et on en prend les “mesures” avec dim <- get_dim(g0). Les graphiques que l’on veut aligner seront produits par set_dim(g1, dim). Le graphique de référence doit être celui dont les labels de l’axe des y sont les plus larges, ce qui peut demander un peu d’organisation dans l’écriture des chunks – rien de bien compliqué. Ceci permet de produire des pages plus soignées.

Récupérer les métadonnées de source_data()

En utilisant l’option metadata=TRUE dans source_data() on peut récupérer des informations sur, par exemple, la date de téléchargement. C’est illustré sur quelques graphiques du cahier de graphique.

En code cela donne le chunk ci dessous. Les données sont accessibles par $data et la date de téléchargement par $date. Cela permet de construire la note (noter que glue::glue() est appliqué aux textes passés à ofce_caption()).

```{r}
transactions <- source_data("immo/data_transaction.r", metadata=TRUE)

trsc <- ggplot(transactions$data) +
  aes(x=date, y=t*1000) +
  geom_line(alpha = 0.5, col = bluish) +
  geom_point_interactive(aes(tooltip = tooltip, data_id = date),
                         shape = 21, size = 1, stroke = 0.2, col = "white",
                         fill = bluish,
                         hover_nearest = TRUE, show.legend = FALSE)+
  theme_ofce() +
  scale_y_log10(labels = scales::number_format(scale = 1/1000, suffix="k")) +
  scalex +
  ofce_caption(
    source = "IGEDD d'après DGFiP (MEDOC) et bases notariale",
    dpt = transactions$data$date,
    note = "Transactions cumulées sur 12 mois, dans l'ancien, maisons et appartements, échelle log, données téléchargées le {date_jour(transactions$date)}",
    sub= "Nombre de transactions")

```

La taille d’un graphique

Pour définir la taille d’un graphique, on ne spécifie jamais la largeur ou la hauteur du graphique2 . Celle-ci est définie en fonction du support d’affichage et donc n’est pas connue à l’avance. En la fixant dans le .qmd on empêche la mise à l’échelle conditionnelle au support de sortie et cela produit des résultats embettant.

On utilise donc le ratio hauteur/largeur qui s’appelle asp (pour aspect ratio). Il est fixé à 0.61 (l’inverse du nombre d’or) par défaut. Lorsqu’on a une note de graphique très longue, on peut l’augmenter (malheureusement l’asp inclue le graphique et le texte des notes), à 0.7 ou 0.8.

Si on empile deux graphiques (un facet par exemple), un asp de 1 est possible. C’est également un bon ratio pour les cartes (1 donne un format carré).

Pour un graphique pleine page A4, l’asp est celui d’une page A4 soit 1.41. Au delà de cet asp, cela n’a pas beaucoup de sens et en tout cas cela posera des problèmes à l’impression sur papier.

Pour un graphique destiné à une présentation, le ratio doit être celui de l’écran de projection soit 9/16=0,56, ce qui n’est pas très loin de la valeur par défaut (coincidence ? je ne crois pas)

Le code le plus simple pour définir l’asp est celui-là (pour une étrange raison, il faut mettre le 0 et bine sûr utiliser le point pour les décimales, pas d’opérations possibles comme 9/16) :

```{r}
#| label: fig-fig1
#| fig-cap: Le titre de la figure
#| fig-asp: 0.7
...
# le code du graphique
...
```