Les objets de R, données et fonctions, sont nommés. Comme R est modulaire, avec la possibilité de lui ajouter un nombre indéterminé de packages, il est très probable que des conflits de nom apparaissent. Pour les régler, R dispose d’un système rigoureux de précédence des noms : le code s’exécute dans un environnement défini, héritant d’environnements parents. Organisation
R démarre dans un environnement vide. Chaque package chargé crée un environnement fils pour former une pile des environnements, dont chaque nouvel élément est appelé “fils” du précédent, qui est son “parent”.
La console se trouve dans l’environnement global, fils du dernier package chargé.
search()
## [1] ".GlobalEnv" "package:R6" "package:entropart" ## [4] "package:forcats" "package:stringr" "package:dplyr" ## [7] "package:purrr" "package:readr" "package:tidyr" ## [10] "package:tibble" "package:ggplot2" "package:tidyverse"
## [13] "package:kableExtra" "package:stats" "package:graphics" ## [16] "package:grDevices" "package:utils" "package:datasets" ## [19] "package:methods" "Autoloads" "package:base"
Le code d’une fonction appelée de la console s’exécute dans un environnement fils de l’environnement global :
# Environnement actuel
environment()
## <environment: R_GlobalEnv>
# La fonction f affiche son environnement
f <- function() environment()
# Affichage de l'environnement de la fonction
f()
## <environment: 0x7fb5a3224650>
# Environnement parent de celui de la fonction
parent.env(f())
## <environment: R_GlobalEnv> Recherche
La recherche des objets commence dans l’environnement local. S’il n’est pas trouvé, il est cherché dans l’environnement parent, puis dans le parent du parent, jusqu’à l’épuisement des environnements qui génère une erreur indiquant que l’objet n’a pas été trouvé.
Exemple :
# Variable q définie dans l'environnement global
q <- "GlobalEnv"
# Fonction définissant q dans son environnement
qLocalFonction <- function() { q <- "Fonction"
return(q) }
# La variable locale est retournée
qLocalFonction()
## [1] "Fonction"
# Fonction (mal écrite) utilisant une variable qu'elle # ne définit pas
qGlobalEnv <- function() { return(q)
}
# La variable de l'environnement global est retournée
qGlobalEnv()
# Suppression de cette variable
rm(q)
# La fonction base::q est retournée
qGlobalEnv()
## function (save = "default", status = 0, runLast = TRUE) ## .Internal(quit(save, status, runLast))
## <bytecode: 0x7fb5a622dcf0> ## <environment: namespace:base>
La variable q est définie dans l’environnement global. La fonction qLocalFonction définit sa propre variable q. L’appel de la fonction retourne la valeur locale de la fonction parce qu’elle se trouve dans l’environnement de la fonction.
La fonction qGlobalEnv retourne la variable q qu’elle ne définit pas localement. Elle la recherche donc dans son environnement parent et trouve la variable définie dans l’environnement global. En supprimant la variable de l’environnement global par rm(q), la fonction qGlobalEnv() parcourt la pile des environnements jusqu’à trouver un objet nommé q dans le package base, qui est la fonction permettant de quitter R. Elle aurait pu trouver un autre objet si un package contenant un objet qavait été chargé.
Pour éviter ce comportement erratique, une fonction ne doit jamais appeler un objet non défini dans son propre environnement.
Espaces de nom des packages
Il est temps de définir précisément ce que les packages rendent visible. Les packages contiennent des objets (fonctions et données) qu’ils exportent ou non. Ils sont habituellement appelés par la fonction library() qui effectue deux opérations :
• elle charge le package en mémoire, ce qui permet d’accéder à tous ses objets avec la syntaxe package::objet pour les objets exportés et package:::objet pour ceux qui ne le sont pas ;
• elle attache ensuite le package, ce qui place son environnement en haut de la pile.
Il est possible de détacher un package avec la fonction unloadNamespace() pour le retirer de la pile des environnements. Exemple :
# entropart chargé et attaché
library("entropart")
# Est-il attaché ?
isNamespaceLoaded("entropart")
# Pile des environnements
search()
## [1] ".GlobalEnv" "package:R6" "package:entropart" ## [4] "package:forcats" "package:stringr" "package:dplyr" ## [7] "package:purrr" "package:readr" "package:tidyr" ## [10] "package:tibble" "package:ggplot2" "package:tidyverse" ## [13] "package:kableExtra" "package:stats" "package:graphics" ## [16] "package:grDevices" "package:utils" "package:datasets" ## [19] "package:methods" "Autoloads" "package:base"
# Diversity(), une fonction exportée par entropart est # trouvée
Diversity(1, CheckArguments = FALSE)
## None
## 1
# Détacher et décharger entropart
unloadNamespace("entropart")
# Est-il attaché ?
isNamespaceLoaded("entropart")
## [1] FALSE
# Pile des environnements, sans entropart
search()
## [1] ".GlobalEnv" "package:R6" "package:forcats" ## [4] "package:stringr" "package:dplyr" "package:purrr" ## [7] "package:readr" "package:tidyr" "package:tibble" ## [10] "package:ggplot2" "package:tidyverse" "package:kableExtra" ## [13] "package:stats" "package:graphics" "package:grDevices" ## [16] "package:utils" "package:datasets" "package:methods" ## [19] "Autoloads" "package:base"
# Diversity() est introuvable
tryCatch(Diversity(1), error = function(e) print(e))
## <simpleError in Diversity(1): could not find function "Diversity">
# mais peut être appelée avec son nom complet. L'appel # exécute implicitement
entropart::Diversity(1, CheckArguments = FALSE)
## None
## 1
L’appel de entropart::Diversity() charge le package (c’est-à-dire, exécute implicitement loadNamespace("entropart")) mais ne l’attache pas.
En pratique, il faut limiter le nombre de package attachés pour limiter le risque d’appeler une fonction non désirée
homonyme de la fonction recherchée. Dans les cas critiques, il faut utiliser le nom complet de la fonction : package::fonction().
Un problème fréquent concerne la filter() de dplyr homonyme de celle de stats. Le package stats est habituellement chargé avant dplyr, un package du tidyverse. stats::filter() doit donc être appelée explicitement.
Cependant, le package dplyr ou tidyverse (qui attache tous les packages du tidyverse) peut être chargé systématiquement en créant un fichier .RProfile à la racine du projet contenant la commande :
library("tidyverse")
Dans ce cas, dplyr est chargé avant stats et c’est sa fonction qui est inaccessible.