Formation-R-perfectionnement

Module Écrire des fonctions avec R

SSP/DEMESIS

20/02/2025

Avant-propos

Ce diaporama de formation a été rédigé dans le but d’être le support visuel des formations dispensées au MASA.

Ces formations s’adressent à des agents qui ont suivi la formation R initialisation.

Champ couvert par cette formation

Ce support couvre Écrire des fonctions avec R , pour en finir avec les copiés-collés.

Ce module se décompose en plusieurs chapitres :

  • 01 - Écrire ses propres fonctions

  • 02 - Itération de fonctions

  • 03 - Annexes

Ce support est orienté pour être utile aux agents du SSM MASA et se concentre sur une utilisation de R via RStudio qui est mise à disposition des agents sur la plateforme interne Cerise basée sur RStudio Workbench.

Chapitre 01 : Écrire ses propres fonctions

  • Quand écrire une fonction ?

  • Comment écrire une fonction ?

  • Comment utiliser une fonction ?

  • Valeurs par défaut

  • Exécution conditionnelle

  • Portée des variables

  • Stocker ses fonctions

  • Documenter ses fonctions

  • Programmer avec le tidyverse

  • Passer des colonnes en paramètres

  • Nommer de nouvelles colonnes

  • Utiliser des chaînes de caractères

  • Récapitulatif

Quand écrire une fonction ?

Quand écrire une fonction ?


R dispose de nombreuses fonctions de base et le chargement de packages permet d’en importer d’autres.


R permet également à l’utilisateur d’écrire ses propres fonctions.

=> C’est l’équivalent des macro dans SAS

Quand écrire une fonction ?


Règle courante : dès qu’on a répété le même code plus de deux fois, ça vaut le coup de l’encapsuler dans une fonction

  • Avantages :

    • Évite les erreurs de copié-collé

    • Facilite la lecture du code si la fonction a un nom adapté

    • Facilite la mise à jour du code et la correction des erreurs (on corrige une seule fois dans la fonction et non à plein d’endroits du code)

    • Permet de réutiliser le même code dans plusieurs scripts

Quand écrire une fonction ?

Plutôt que de recopier le même code pour chaque département, on peut écrire une fonction

Comment écrire une fonction ?

Comment écrire une fonction ?

Une fonction exécute une série d’instructions, à partir d’éventuels objets donnés en entrée.

On crée une fonction en utilisant l’instruction function.

Elle doit être suivie :

  • d’une paire de parenthèses ⇒ pour indiquer les arguments

  • d’une paire d’accolades ⇒ pour indiquer les instructions

ma_fonction <- function(...){ ...}

Comment écrire une fonction ?


Quatre éléments principaux pour créer une fonction :

  • Le nom de la fonction

  • Les arguments éventuels de la fonction

  • Un bloc d’instructions pour constituer le corps de la fonction

  • Le résultat éventuel de la fonction

Comment appeler une fonction ?


Les parenthèses servent à indiquer les paramètres de la fonction (ou arguments)

Ce sont ceux qui seront passés lors de l’appel de la fonction

  • On peut prévoir autant d’arguments que l’on souhaite

  • Les arguments doivent être nommés pour être ensuite utilisés dans la fonction


Exemple : eff_milliers <- function(x, digits)

Comment écrire une fonction ?


Les accolades comprennent une série d’instructions R

c’est le corps de la fonction, le code qui sera exécuté à l’appel de la fonction


  • On utilise dans le corps de la fonction les arguments renseignés dans les parenthèses

  • La fonction renvoie un résultat via l’instruction return()

Comment écrire une fonction ?

L’instruction return() arrête l’exécution de la fonction

⇒ tout ce qui est après le return() ne sera pas exécuté

Comment écrire une fonction ?


return() est obligatoire

  • une fonction peut ne rien renvoyer : certaines fonctions accomplissent une action mais ne renvoient rien (affichage de graphique, export de fichier par exemple)

  • sans précision, la fonction renvoie le dernier objet appelé dans le bloc d’instructions

  • pour un code plus sûr et plus clair, il est recommandé d’utiliser systématiquement la fonction return() afin de préciser l’élément à renvoyer, quitte à ce que ce soit l’élément NULL

Comment écrire une fonction ?

Une fonction est un objet de type function – elle apparaît dans l’environnement global dans la catégorie Functions

R traite les fonctions définies par l’utilisateur de la même façon que les autres.

Attention à ne pas nommer une fonction personnelle de la même façon qu’une fonction existante, pour ne pas l’écraser.

Comment écrire une fonction ?

Identifier et écrire une fonction dans la pratique :

Code initial :

Comment écrire une fonction ?

Identifier et écrire une fonction dans la pratique :

Comment utiliser une fonction ?

Comment utiliser une fonction ?

Une fonction personnelle s’utilise comme n’importe quelle autre fonction de R

Notamment, on récupère la sortie de la fonction en affectant le résultat à un objet

⇒ sinon il n’y aura que l’affichage dans la console

Dans la console :

Valeurs par défaut

Valeurs par défaut

Au moment de la définition d’une fonction, on peut indiquer une valeur par défaut qui sera prise par l’argument si l’utilisateur n’en fournit pas.

Valeurs par défaut

⇒ lorsqu’un paramètre a une valeur par défaut, il devient facultatif

on peut utiliser la fonction sans préciser de valeur pour ce paramètre.


Valeurs par défaut

L’ordre et le nommage des paramètres ont une importance :

  • Si on les nomme, on peut les appeler dans l’ordre que l’on veut

  • Sinon, il faut les appeler dans l’ordre prévu lors de la définition de la fonction

Lors de la définition d’une fonction, il vaut donc mieux placer les arguments avec une valeur par défaut en dernier : de cette façon, il est plus facile de ne pas les nommer.

Exécution conditionnelle

Exécution conditionnelle


Dans une fonction, on liste une série d’instructions que la fonction va effectuer

Pour des actions un peu plus complexes, on peut utiliser des conditions, en utilisant les mots-clés if et else

Par exemple, on va pouvoir exécuter certaines instructions selon la valeur prise par les arguments de la fonction.


Exécution conditionnelle

⇒ if permet de n’exécuter du code que lorsqu’une condition est remplie


⇒ else permet d’exécuter des instructions lorsque la condition donnée au if n’est pas remplie


⇒ on peut enchaîner plusieurs blocs d’instructions if / else if / else


⇒ else s’utilise après if, mais n’est pas obligatoire

Portée des variables

Portée des variables

Dans une fonction, l’environnement est temporaire

⇒ les éléments créés dans la fonction n’existent pas dans l’environnement global

Stocker ses fonctions

Stocker ses fonctions


Pour conserver ses fonctions, on peut les enregistrer dans un script à part.

⇒ on utilise ensuite la commande source() pour faire appel à ce script depuis un autre script :

Pour les experts, il est possible de créer un package pour encapsuler ses fonctions.


Documenter ses fonctions

Documenter ses fonctions

Pour réutiliser ou partager ses fonctions, il est important de les documenter

→ le package {roxygen} aide à générer une documentation efficace dans le corps de la fonction :

cliquer sur -> Code -> Insert Roxygen Skeleton pour faire apparaître un bloc de documentation à remplir


Programmer avec le Tidyverse

Passer des noms de colonnes en paramètres

Les fonctions du {tidyverse} sont compliquées à utiliser dans nos propres fonctions

⇒ lorsque les variables des tableaux de données utilisées dans le corps de la fonction ne sont pas saisies directement mais proviennent d’un paramètre, cela génère des erreurs


Passer des noms de colonnes en paramètres

l’opérateur {{ }} (curly curly) permet de forcer l’évaluation du paramètre

⇒ lorsque le paramètre de la fonction est une variable à laquelle on souhaite accéder, on entoure les utilisations du paramètre par des doubles accolades


Nommer de nouvelles colonnes

Pour créer de nouvelles colonnes à partir d’un argument,

on utilise les deux opérateurs {{ }} et := (walrus operator)

⇒ on peut placer le texte que l’on souhaite comme nouveau nom de colonne

(ex : "{{var_moyenne}}_moy")


Utiliser des chaines de caractères

Lorsque l’argument est passé sous forme de chaînes de caractères, on ne peut pas utiliser l’opérateur {{ }}

⇒ le pronom .data permet d’accéder aux colonnes du tableau à partir de leur nom sous forme de chaîne de caractères


Récapitulatif (source ici)

Chapitre 02 : Itération de fonctions

  • Itération de fonctions : présentation

  • Utilisation de across

  • Utilisation de across : Exemple avec mutate

  • Utilisation de across : Exemple avec summarise

  • Package purrr

  • {purrr} : Itérer sur un vecteur ou une liste

  • {purrr} : Itérer sur deux éléments

  • {purrr} : Généralisation

  • {purrr} : Manipuler des listes

  • {purrr} : Fonctions à effets de bord

Itération de fonctions

Présentation


Intérêt d’une fonction = pouvoir être utilisée plusieurs fois

Utilisation de la fonction across()

  • Permet d’appliquer, dans un environnement dplyr, un même traitement à plusieurs variables d’une même table

Utilisation du package {purrr}

  • Regroupe des outils pour appliquer la même fonction à plusieurs éléments d’une liste ou d’un vecteur

Utilisation de across()


Utilisation de across()

Utilisation de across()

Objectif de across() ⇒ appliquer une même fonction à un ensemble de variables d’une table de données

across() s’utilise à l’intérieur des fonctions dplyr comme mutate() et summarise().

La syntaxe générale est la suivante :

Utilisation de across()

Pour désigner les variables à traiter, on utilise les mêmes « select helpers » que ceux utilisés dans la fonction select par exemple :

Utilisation de across()

Le traitement à effectuer correspond à une fonction ⇒ on peut la renseigner de différentes façons :

  • avec le nom de la fonction (sans parenthèse) = lorsque la fonction n’a qu’un seul paramètre, qui est la variable à traiter

  • avec ~ et .x :

Utilisation de across()

  • avec une fonction anonyme:

  • définie avec (x)

  • définie avec des paramètres

Utilisation de across()

Utilisation de across() : Exemples avec mutate

Utilisation de across() : Exemples avec summarise

Utilisation de across() : Exemples avec summarise

Package {purrr}

Package {purrr}

{purrr} : Itérer sur un vecteur OU une liste

Objectif de map() ⇒ appliquer une même fonction à l’ensemble des éléments d’un vecteur ou d’une liste


map() s’utilise sur un vecteur ou une liste d’objets. La syntaxe générale est la suivante :

{[}purrr} : Itérer sur un vecteur OU une liste

Le traitement correspond à la fonction à appliquer ⇒ on peut la renseigner de différentes manières :

  • 1) Avec le nom de la fonction (sans parenthèse) = lorsque la fonction n’a qu’un seul paramètre, qui est l’élément à traiter

  • 2) Avec une fonction anonyme

    • définie avec un ~ : les éléments successifs de la liste sont alors identifiés avec le mot clé .x

    • définie avec des paramètres

{purrr} : Itérer sur un vecteur OU une liste

{purrr} : Itérer sur un vecteur OU une liste

{purrr} : Itérer sur un vecteur OU une liste

Les écritures suivantes sont équivalentes :

{purrr} : Itérer sur un vecteur OU une liste

Il n’est pas nécessaire de créer une fonction en temps que telle : on peut le faire directement à l’intérieur de map()


{purrr} : Itérer sur un vecteur OU une liste

Possibilité d’ajouter des arguments supplémentaires à la fonction à appliquer

{purrr} : Itérer sur deux éléments

Objectif de map2() = appliquer une même fonction sur chacun des couples d’éléments de deux listes ou vecteurs

la fonction à appliquer peut être renseignée avec une fonction anonyme

  • définie avec un ~ : les éléments successifs de la liste sont alors identifiés avec les mots clé .x et .y

  • définie avec des paramètres

{purrr} : Généralisation

Généralisation avec la fonction pmap() = permet d’appliquer une même fonction sur chacun des p-uplets d’éléments des différentes listes ou vecteurs

{purrr} : Manipuler des listes

Les fonctions map, map2 et pmap renvoient des listes

→ on peut retrouver des data.frame plus faciles à manipuler en utilisant bind_rows(), ou la fonction reduce()

⇒ bind_rows() permet de concaténer une liste de tables

{purrr} : Manipuler des listes

⇒ reduce() applique une fonction de façon récursive à chaque élément d’une liste ou d’un vecteur

{purrr} : Fonctions à effets de bord


Pour les fonctions qui ne renvoient aucun résultat, on utilise la fonction walk()

⇒ même fonctionnement que pour les fonctions map, mais spécifiques pour les fonctions à effets de bord


Exemples : impression dans la console, affichage de graphiques, export de données…

  • Extension à walk2() et pwalk()

{purrr} : Fonctions à effets de bord

{purrr} : Fonctions à effets de bord

Chapitre 03 : Annexes : Pour aller plus loin

  • Passer un nombre indéfini de paramètres

  • Retourner plusieurs objets dans une fonction

  • Utiliser plusieurs return dans une fonction

  • Utilisation de across : règle de renommage

  • {purrr} : Contrôler le format de sortie

  • {purrr} : Nommer les éléments d’une liste

  • {purrr} : modifier le comportement d’une fonction

Annexes: Pour aller plus loin

Passer un nombre indéfini de paramètres

Il est possible de prévoir un nombre indéfini de paramètres lors de l’élaboration d’une fonction avec … .

Cela permet d’accéder aux paramètres d’une “sous-fonction” utilisée dans le corps de la fonction créée.

Passer un nombre indéfini de paramètres

Les … permettent également de prévoir que l’utilisateur puisse passer un nombre indéfini de paramètres

Retourner plusieurs objets dans une fonction

L’instruction return() ne peut renvoyer qu’un seul objet ⇒ pour renvoyer plusieurs éléments dans une fonction, il faut les placer dans une liste

Utiliser plusieurs Return dans une fonction

Il est possible de mettre plusieurs return dans le bloc d’instructions d’une fonction.

=> Dans ce cas, l’exécution s’arrête au premier return rencontré.

Utilisation de across() : Règle de renommage

Une règle de renommage des variables issues de across peut être donnée via l’argument .names.

On lui donne une chaîne de caractère concaténée où :

  • {.col} désigne la variable à traiter

  • {.fn} la fonction de traitement.

Par exemple : .names = “{.col}_{.fn}”

Si aucune règle de renommage n’est précisée, R se débrouille :

  • Dans un mutate, les variables d’origine sont écrasées

  • Dans un summarise avec un seul agrégat à calculer, conserve le nom des variables d’origine

  • Dans un summarise avec plusieurs agrégats, suffixe la fonction d’agrégation au nom des variables d’origine

Utilisation de across() Règle de renommage

{purrr}: Contrôler le format de sortie

Possibilité de contrôler le format de sortie avec les dérivés de la fonction map

{purrr}: Nommer les éléments d’une liste

Retour sur le calcul des évolutions départementales des populations par composition familiale :

Par défaut, les différents éléments de la liste résultats ne sont pas nommés. On ne peut accéder aux différents éléments que par index :

{purrr}: Nommer les éléments d’une liste

La fonction set_names() du package purrr permet de nommer les éléments d’une liste en entrée :


{purrr}: Modififer le comportement d’une fonction


Les fonctions de {purrr} ne renvoient un résultat que s’il n’y a aucune erreur dans l’ensemble… sinon, l’exécution est arrêtée et aucun résultat n’est retourné, même si l’erreur est à la fin !


{purrr}: Modififer le comportement d’une fonction

safely() ⇒ retourne le résultat et le message d’erreur de la fonction

{purrr}: Modififer le comportement d’une fonction

possibly() ⇒ retourne le résultat ou une valeur par défaut en cas d’erreur

Chapitre 04 : Liens utiles

Liens utiles

Ecriture de fonctions

  • Ecriture de fonctions :

https://juba.github.io/tidyverse/14-fonctions.html https://juba.github.io/tidyverse/17-if-boucles.html

  • BONUS ! gestion des erreurs :

https://thinkr.fr/controle-et-gestion-des-erreurs-dans-r/

  • Programmer avec le tidyverse :

https://juba.github.io/tidyverse/19-programmer-tidyverse.html

https://thinkr.fr/comment-creer-des-fonctions-dans-le-tidyverse-avec-la-tidyeval-et-le-stash-stash/

Itérations de fonctions

  • Utilisation de across() :

https://juba.github.io/tidyverse/15-dplyr-avance.html#sec-across

https://www.icem7.fr/r/across-plus-puissant-flexible-quil-ny-parait/

  • Introduction à purrr :

https://dcl-prog.stanford.edu/iteration.html

https://thinkr.fr/code-ronronne-purrr/

https://speakerdeck.com/jennybc/purrr-workshop?slide=91

  • Aides mémoire :

https://rstudio.com/resources/cheatsheets/