Skip to contents

# source_data() : plus rapide, plus sûr

``

## Mettre en cache le résultat des calculs avec `source_data()` (aka `sourcoise::sourcoise()`)

`source_data()` est un alias vers la fonction du package `sourcoise::sourcoise()` qui permet de mettre en cache les résultats d’un script `r` et de ne pas avoir à ré-exécuter le code à chaque fois.

Les bénéfices sont nombreux :

1. un gain de temps lorsque l’exécution du code est longue (accès à une API, téléchargement de grosses données, traitements importants). La lecture d’un fichier excel peut aussi être assez longue. Le temps d’accès aux données en cache dépend de leur taille, mais même pour des données volumineuses (et il n’y a pas de raisons qu’elles le soient tant que ça), l’ordre de grandeur est la milliseconde, grâce aux optimisations.

2. le cache est transférable par github. Il se trouve dans un dossier (caché), mais enregistré dans le dossier de projet et commité par github. Le cache produit sur un poste est donc accessible par `pull` sur les autres postes.

3. si le code source déclenche une erreur, on peut passer outre : En cas de package non installé, données absentes (par exemple un chemin absolu dans le code), ou une API qui bloque (comme celle de l’OCDE) alors `source_data()` essaye de prendre la dernière exécution résussie. Bien que cela puisse être problématique, c’est-à-dire une erreur non signalée, cela a l’énorme avantage de ne pas bloquer le processus et de permettre de traiter l’erreur en parallèle.

4. `source_data()` cherche de façon astucieuse le fichier source dans le projet et exécute le code dans un environnement local, en changeant le répertoire de travail pour être celui où se trouve le code source. Cela permet d’appeler dans le code source (le script `r` `mon_script.r` passé en paramètre à `source_data("mon_script.r")`, des scripts `r`, des fichiers de données `.csv` ou `.xlsx` qui sont enregistré dans le même répertoire que le fichier `mon_script.r`. On peut donc réutiliser le code sans se soucier de modifier les chemins qui sont relatifs au dossier où se trouve `mon_script.r`.

5. cela fournit un embryon de reproductibilité en désignant le script qui fabrique les données.

## Comment l’utiliser ?

Première chose mettre son code de données dans un script et l’enregistrer là où est le qmd, dans cet exemple `souverains.qmd` et un dossier remplis de csv, scripts R, etc :

    ├── souverains
    │   ├── ratings.R
    │   ├── ratings.rds
    │   ├── ratings_legende.rds
    │   ├── ratings_taux.rds
    │   ├── taux_souverains_historiques.R
    │   └── taux_souverains_historiques.rds
    └── souverains.qmd

Dans `souverains.qmd` on trouve le chunk suivant. `source_data` est capable de retrouver le dossier dans le même dossier que le `qmd` (ce n’est pas automatique, dans un projet commme la prévision, les chemins sont relatifs à la racine du projet, ici le qmd et ses fichiers peuvent être mis n’importe où grâce à cette heuristique). Le source est ensuite exécuté en changeant le dossier de travail à celui qui le contient (dans cet exemple, `souverains`) ce qui permet d’utiliser des chemins relatifs et donc de pouvoir transporter le code n’importe où.

    ```{r}
    ofce::init_qmd(echo=TRUE)

    taux_souverains_historiques <- source_data("souverains/taux_souverains_historiques.R", lapse = "day")

    ratings_taux <- source_data("souverains/ratings", lapse = "day")
    ```

Le script doit se terminer par un `return` qui renvoie les données calculées ou téléchargées. Ce sont ces données qui sont mises en cache.

    ...
    télécharge et modifie, calcule, etc
    ...
    return(taux_souverains_historiques)

`source_data()` dispose d’un cache (caché dans un dossier `.data`). Il repère le fichier source et détecte les changements faits au code source et invalide le cache. Si le cache est valide, les données sont renvoyées, sinon, le script est exécuté et les nouvelles données mises en cache.

Il existe d’autres moyens d’invalider le cache : il peut avoir une durée de vie maximale, comme avec l’argument `lapse="day"`. Cette option dit que si le cache est plus vieux que 24h, il est renouvelé par une ré-exécution du script. Ce paramètre peut prendre plusieurs valeurs et des formes comme `2 hours` ou `3 weeks`. D’autres déclencheurs temporels seront ajoutés pour introduire des calendriers (comme 45 jours après la fin du trimestre). Attention, cependant, `source_cache` n’est pas capable d’aller vérifier que les données téléchargées ont un point de plus et donc de ne pas valider le cache si la donnée n’a pas encore été publiée.

Il est aussi possible de déclencher l’invalidation du cache si un fichier a été modifié. Il suffit de fournir une liste de fichiers (dont les chemins sont relatifs au script) qui seront suivis. Ces fichiers peuvent être des `.csv` ou des fichiers `.xlsx` (ou encore tout autre type de fichier) et donc sont utiles pour déclencher l’exécution du script quand on a fait une modification manuelle ou par un autre programme de ces fichiers. On peut en mettre autant qu’on veut.

On peut également forcer le déclenchement du script. Cela se fait par une option `force_exec=TRUE`. Cependant, il vaut mieux ne pas spécifier cette option, il existe d’autres moyens pour opérer un rafraîchissement du cache.

Il est possible de bloquer l’exécution du code par une option (`prevent_exec`) qui peut être définie comme une option globale (par `options(ofce.source_data.prevent_exec=TRUE)`). Dans ce cas, aucun script ne sera exécuté, ce qui peut servir lorsqu’on veut faire un rendu du site sans prendre le risque d’une erreur d’API ou d’un blocage.

## Usage avancé : passer des paramètres

Il est possible de passer des paramètres avec `source_data()`, bien que ce soit plus pratique d’écrire une fonction. Les paramètres sont passés sous forme d’une liste (`list(param1="1")` par exemple) et sont disponibles dans le script (dans la variable `args`, donc pour avoir le paramètre `param1` il faut écrire `args$param1` dans le script. Changer les paramètres invalide le cache.

Notez que le script est toujours exécuté en “local” ce qui veut dire que toute variable créée ou tout package ouvert à l’intérieur du script n’est pas renvoyé (comme dans une fonction, en fait).

## Usage avancé : récupérer les métadonnées

En utlisant l’option `metadata=TRUE` dans `source_data()` on peut récupérer des informations sur, par exmple, la date de téléchargement. C’est illustré sur quelques graphiques du cachier 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()`).

    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")

## Quelques opérations sur le cache

Le package ofce fournit des outils pour s’occuper des caches. Le premier est `source_data_status()`. il scanne le répertoire et fournit la liste de tous les caches enregistrés et suivis. Il indique si les caches sont valides ou non et les principaux paramètres utilisés pour chaque script.

`source_data_refresh()` rafraîchit (en le forçant) tous les caches. On peut passer à `source_data-refresh()` un `tibble` comme celui renvoyé par `souce_data_status()` mais filtré pour ne rafraîchir que la liste voulue (attention passer toutes les colonnes). Cela sert lorsqu’on a un processus plus complexe d’invalidation du cache (en fonction d’un calendrier, en interrogeant une API, etc…) et qu’on déclenche en fonction de cette logique l’exécution des caches.

On peut également à partir de `source_data_status()` accéder aux données en cache. Elles sont enregistrées en `.qs` et donc se chargent avec un `qs::qread()`.

On peut également nettoyer complètement le cache (ce qui provoquera sa ré exécution) avec `clear_source_cache()`. `prune_source_cache()` fait la même chose mais garde les dernières versions du cache (il nettoie l’historique donc).