web-dev-qa-db-fra.com

Comment utilisez-vous "<< -" (affectation de portée) dans R?

Je viens de terminer la lecture de cadrage dans l'intro R , et je suis très curieux de connaître le <<- affectation.

Le manuel a montré un exemple (très intéressant) pour <<-, que j'ai l'impression d'avoir compris. Ce qui me manque encore, c'est le contexte dans lequel cela peut être utile.

Je voudrais donc vous lire des exemples (ou des liens vers des exemples) sur l'utilisation de <<- peut être intéressant/utile. Quels pourraient être les dangers de son utilisation (il semble facile de le perdre) et tous les conseils que vous pourriez avoir envie de partager.

118
Tal Galili

<<- est très utile en conjonction avec les fermetures pour maintenir l'état. Voici une section d'un de mes récents articles:

Une fermeture est une fonction écrite par une autre fonction. Les fermetures sont appelées ainsi parce qu'elles entourent l'environnement de la fonction parent et peuvent accéder à toutes les variables et paramètres de cette fonction. C'est utile car cela nous permet d'avoir deux niveaux de paramètres. Un niveau de paramètres (le parent) contrôle le fonctionnement de la fonction. L'autre niveau (l'enfant) fait le travail. L'exemple suivant montre comment utiliser cette idée pour générer une famille de fonctions de puissance. La fonction parent (power) crée des fonctions enfants (square et cube) qui font réellement le travail.

power <- function(exponent) {
  function(x) x ^ exponent
}

square <- power(2)
square(2) # -> [1] 4
square(4) # -> [1] 16

cube <- power(3)
cube(2) # -> [1] 8
cube(4) # -> [1] 64

La possibilité de gérer des variables à deux niveaux permet également de maintenir l'état à travers les invocations de fonctions en permettant à une fonction de modifier des variables dans l'environnement de son parent. La clé de la gestion des variables à différents niveaux est l'opérateur d'affectation à double flèche <<-. Contrairement à l'affectation habituelle à une seule flèche (<-) qui fonctionne toujours au niveau actuel, l'opérateur à double flèche peut modifier les variables dans les niveaux parents.

Cela permet de maintenir un compteur qui enregistre combien de fois une fonction a été appelée, comme le montre l'exemple suivant. Chaque fois new_counter est exécuté, il crée un environnement, initialise le compteur i dans cet environnement, puis crée une nouvelle fonction.

new_counter <- function() {
  i <- 0
  function() {
    # do something useful, then ...
    i <<- i + 1
    i
  }
}

La nouvelle fonction est une fermeture et son environnement est l'environnement englobant. Lorsque les fermetures counter_one et counter_two sont exécutés, chacun modifie le compteur dans son environnement englobant puis renvoie le nombre actuel.

counter_one <- new_counter()
counter_two <- new_counter()

counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
167
hadley

Il est utile de penser à <<- Comme équivalent à assign (si vous définissez le paramètre inherits dans cette fonction sur TRUE). L'avantage de assign est qu'il vous permet de spécifier plus de paramètres (par exemple l'environnement), donc je préfère utiliser assign plutôt que <<- Dans la plupart des cas.

L'utilisation de <<- Et assign(x, value, inherits=TRUE) signifie que "les environnements englobants de l'environnement fourni sont recherchés jusqu'à ce que la variable 'x' soit rencontrée." En d'autres termes, il continuera à parcourir les environnements dans l'ordre jusqu'à ce qu'il trouve une variable avec ce nom, et il l'affectera à cela. Cela peut être dans le cadre d'une fonction ou dans l'environnement global.

Pour comprendre ce que font ces fonctions, vous devez également comprendre les environnements R (par exemple en utilisant search).

J'utilise régulièrement ces fonctions lorsque je lance une grande simulation et je souhaite enregistrer des résultats intermédiaires. Cela vous permet de créer l'objet en dehors de la portée de la fonction donnée ou de la boucle apply. C'est très utile, surtout si vous avez des inquiétudes concernant une boucle volumineuse se terminant de façon inattendue (par exemple, une déconnexion de la base de données), auquel cas vous pourriez tout perdre dans le processus. Cela équivaudrait à écrire vos résultats dans une base de données ou un fichier pendant un long processus, sauf qu'il stocke plutôt les résultats dans l'environnement R.

Mon principal avertissement avec ceci: soyez prudent car vous travaillez maintenant avec des variables globales, en particulier lorsque vous utilisez <<-. Cela signifie que vous pouvez vous retrouver dans des situations où une fonction utilise une valeur d'objet de l'environnement, alors que vous vous attendiez à ce qu'elle en utilise une qui a été fournie en tant que paramètre. C'est l'une des principales choses que la programmation fonctionnelle essaie d'éviter (voir effets secondaires ). J'évite ce problème en attribuant mes valeurs à des noms de variables uniques (en utilisant coller avec un ensemble ou des paramètres uniques) qui ne sont jamais utilisés dans la fonction, mais juste utilisés pour la mise en cache et au cas où je devrais récupérer plus tard (ou faire quelques méta -analyse des résultats intermédiaires).

33
Shane

Un endroit où j'ai utilisé <<- était dans des interfaces graphiques simples utilisant tcl/tk. Certains des premiers exemples l'ont - car vous devez faire une distinction entre les variables locales et globales pour l'état complet. Voir par exemple

 library(tcltk)
 demo(tkdensity)

qui utilise <<-. Sinon, je suis d'accord avec Marek :) - une recherche Google peut aider.

7
Dirk Eddelbuettel
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")
4
lcgong

À ce sujet, je voudrais souligner que le <<- L'opérateur se comportera étrangement lorsqu'il sera appliqué (incorrectement) dans une boucle for (il peut y avoir d'autres cas aussi). Étant donné le code suivant:

fortest <- function() {
    mySum <- 0
    for (i in c(1, 2, 3)) {
        mySum <<- mySum + i
    }
    mySum
}

vous pourriez vous attendre à ce que la fonction renvoie la somme attendue, 6, mais à la place, elle renvoie 0, avec une variable globale mySum en cours de création et affectée de la valeur 3. Je ne peux pas expliquer complètement ce qui se passe ici, mais certainement le corps d'une boucle for est pas un nouveau niveau de portée. Au lieu de cela, il semble que R regarde en dehors de la fonction fortest, ne trouve pas de variable mySum à affecter, donc en crée une et affecte la valeur 1, la première fois dans la boucle. Lors des itérations suivantes, le RHS de l'affectation doit faire référence à la variable interne (inchangée) mySum tandis que le LHS fait référence à la variable globale. Par conséquent, chaque itération remplace la valeur de la variable globale par la valeur de cette itération de i, d'où la valeur 3 à la sortie de la fonction.

J'espère que cela aide quelqu'un - cela m'a dérouté pendant quelques heures aujourd'hui! (BTW, remplacez simplement <<- avec <- et la fonction fonctionne comme prévu).

2
Matthew Wise

L'opérateur <<- Peut également être utile pour Classes de référence lors de l'écriture des méthodes de référence . Par exemple:

myRFclass <- setRefClass(Class = "RF",
                         fields = list(A = "numeric",
                                       B = "numeric",
                                       C = function() A + B))
myRFclass$methods(show = function() cat("A =", A, "B =", B, "C =",C))
myRFclass$methods(changeA = function() A <<- A*B) # note the <<-
obj1 <- myRFclass(A = 2, B = 3)
obj1
# A = 2 B = 3 C = 5
obj1$changeA()
obj1
# A = 6 B = 3 C = 9
2
Carlos Cinelli