Modifier la valeur d'un membre d'une liste externe

Postez ici vos questions, réponses, commentaires ou suggestions - Les sujets seront ultérieurement répartis dans les archives par les modérateurs

Modérateur : Groupe des modérateurs

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Modifier la valeur d'un membre d'une liste externe

Messagepar François Bonnot » 13 Oct 2017, 07:40

Bonjour,
La fonction f1 suivante double la valeur de son argument dans l'environnement global :

Code : Tout sélectionner

f1 <- function(x) {
    nom <- deparse(substitute(x))
    assign(nom,x*2,envir = .GlobalEnv)
}
z <- 3
f1(z)
z ## 6

Comment écrire une fonction f2 qui double le membre x (nom fixé) d'une liste passée en argument ?
Par exemple après exécution du code suivant, li$x devrait avoir la valeur 6:

Code : Tout sélectionner

li <- list(x=3)
f2(li)

J'ai essayé ceci, qui ne fonctionne pas:

Code : Tout sélectionner

f2 <- function(z) {
    env <- list2env(z)
    assign("x",x*2,envir = env)
}
François

Dominique Soudant
Messages : 758
Enregistré le : 23 Avr 2008, 11:12
Contact :

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar Dominique Soudant » 13 Oct 2017, 08:35

Hello,

À l'arrache :

Code : Tout sélectionner

ListeGlobale <- list(x=3)
ListeGlobale
f2 <- function(ListName,Item){
  Liste <- get(ListName)
  Liste[[Item]] <- Liste[[Item]]*2
  assign(ListName,Liste,envir=.GlobalEnv)
}

f2("ListeGlobale","x")
ListeGlobale
Je suis peut être un peu HS ?
@+

EDIT 00 : V2
Je ne sais pas pourquoi je me suis pris les pieds dans le tapis des deparse/subsiture, bref:

Code : Tout sélectionner

ListeGlobale <- list(x=3)
ListeGlobale
f2 <- function(List,Item){
  ListName <- deparse(substitute(List))
  Liste <- get(ListName)
  Liste[[Item]] <- Liste[[Item]]*2
  assign(ListName,Liste,envir=.GlobalEnv)
}

f2(ListeGlobale,"x")
ListeGlobale


EDIT 01
Mais ce n'est pas sain comme programmation, pb de portée des variables, lisibilité, j'ai appris "pas d'affectation globale dans une fonction". Donc encore plus simple : tu passes une liste et le nom de l'item à doubler, tu renvois la liste modifiée que tu affectes à la liste initiale.

Code : Tout sélectionner

ListeGlobale <- list(x=3)
ListeGlobale

DoubleItemListe <- function(Liste,Item){
  Liste[[Item]] <- Liste[[Item]]*2
  return(Liste)
}

ListeGlobale <- DoubleItemListe(ListeGlobale,"x")
ListeGlobale

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar François Bonnot » 13 Oct 2017, 09:44

Bonjour,
Merci pour ces réponses... mais elles ne répondent pas à la question.

1) La syntaxe doit être : f2(ListeGlobale)
pas f2("ListeGlobale") , ni f2(ListeGlobale,"x")

2) J'utilise évidemment fréquemment la syntaxe li <- f2(li)
L'objet de ma question est justement : comment faire pour ne pas l'utiliser

J'ai simplifié la question par souci de clarté, mais les motivations réelles sont multiples:
- mes listes sont volumineuses et les recopier en retour de la fonction peut être consommateur de temps et de mémoire
- la fonction peut avoir plusieurs listes en argument, le retour est alors problématique (retourner une liste de liste...)
- appliquer la fonction successivement à de nombreuses listes peut être source d'erreur s'il faut répéter le nom à la fois en argument et en valeur retournée

EDIT : Dominique, en fait la fonction dans EDIT 00 V2 donne une solution plus générique que celle que j'attendais, donc j'obtiens la solutuion si je simplifie la fonction en fixant le nom du membre à modifier. Il reste que la totalité de la liste est rercopiée et pas seulement le membre modifié, mais le résultat est correct.
Merci pour la réponse.
François

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar Logez Maxime » 13 Oct 2017, 16:42

Bonjour,

une autre possibilité :

Code : Tout sélectionner

fun2 <- function(li) {
  x <- deparse(substitute(li))
  eval(substitute(ted$x <- ted$x*2, list(ted = as.name(x))), envir = globalenv())
}
li <- list(x = 3)

fun2(li)
li
$x
[1] 6
On doit pouvoir remplacer globalenv() par parent.frame().
Cordialement,
Maxime

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar François Bonnot » 16 Oct 2017, 07:16

Bonjour,
Merci à tous les deux pour vos réponses.
J'ai testé la vitesse d'exécution des 3 solutions (fonctions légèrement modifiées sans en changer le principe), en chargeant la liste li avec le volumineux vecteur v pour forcer une copie en mémoire:

Code : Tout sélectionner

f1 <- function(List){
  ListName <- deparse(substitute(List))
  Liste <- get(ListName)
  Liste$x <- Liste$x+1
  assign(ListName,Liste,envir=.GlobalEnv)
}

f2 <- function(li) {
    li$x <- li$x+1
    li
}

f3 <- function(li) {
  x <- deparse(substitute(li))
  eval(substitute(ted$x <- ted$x+1, list(ted = as.name(x))), envir = globalenv())
}

li <- list(x = 0,v=1:100000000)

system.time(for (i in 1:50) f1(li))
li$x

system.time(for (i in 1:50) li <- f2(li))
li$x

system.time(for (i in 1:50) f3(li))
li$x

Conclusion: la fonction f3 (alias fun2), qui n'accède qu'à x et ne recopie pas v, est beaucoup plus rapide.
François

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar Logez Maxime » 16 Oct 2017, 07:48

Bonjour,

Pour ce qui est mesure du temps de calculs, j'utilise le package microbenchmark et sa fonction éponyme :

Code : Tout sélectionner

 microbenchmark(f1(li), li <- f2(li), f3(li), times = 1e3)
Unit: nanoseconds
         expr   min    lq      mean median    uq   max neval
       f1(li)  9062  9817 11194.798  10195 11328 52861  1000
 li <- f2(li)     0   378   623.939    379   756 17747  1000
       f3(li) 10573 11705 13401.332  12083 13216 58524  1000
D'après ces résultats c'est li <- f2(li) qui gagne ...

Cordialement,
Maxime

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar François Bonnot » 16 Oct 2017, 08:03

D'après ces résultats c'est li <- f2(li) qui gagne ...

C'est vraiment bizarre... chez moi sans utiliser ni system.time ni microbenchmark f3 est manifestement plus rapide.
Je me demande si ça ne vient pas de la version de R (celle que j'utilise actuellement est la 3.0.2).
François

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar Logez Maxime » 16 Oct 2017, 08:27

Je me demande si ça ne vient pas de la version de R (celle que j'utilise actuellement est la 3.0.2).
C'est fort probable parce qu'ils ont fait depuis pas mal de modifications pour améliorer le temps de calcul. Un exemple, la compilation just in time qui est par défaut avec les versions les plus récentes. J'avoue que je n'ai pas lu la rnews pour savoir ce qu'ils avaient modifié entre temps.

Cordialement,
Maxime

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar François Bonnot » 17 Oct 2017, 06:22

Bonjour,
La différence vient effectivement de la version de R.
Voici ce que j'obtiens avec system.time() (sur 2 ordinateurs différents mais de puissances équivalentes)

Code : Tout sélectionner

50 itéraiions, version 3.0.2
               user  system elapsed
f1(li))        4.31    3.79    9.62
li <- f2(li))  4.17    3.56    7.74
f3(li))        0.06    0.06    0.12

50000 itérations, version 3.2
               user  system elapsed
f1(li))        1.09    0.00    1.09
li <- f2(li))  0.16    0.00    0.16
f3(li))        1.20    0.00    1.20

C'est assez incroyable... non seulement les rapports des temps entre fonctions sont profondément modifiés, mais si on tient compte du nombre d'itérations f2 est 26000 fois plus rapide avec la version 3.2. J'ai du mal à comprendre comment f2 peut être si rapide... peut-être un mécanisme qui détecte que la recopie de v est inutile.
François

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar Logez Maxime » 17 Oct 2017, 07:19

Je ne sais pas au juste, mais j'ai pu trouvé ça quand on est passé à la 3.1.0 :
Avoid duplicating the right hand side values in complex assignments when possible. This reduces copying of replacement values in expressions such as Z$a <- a0 and ans[[i]] <- tmp: some package code has relied on there being copies.

Also, a number of other changes to reduce copying of objects; all contributed by or based on suggestions by Michael Lawrence.

Cordialement,
Maxime

jean lobry
Messages : 733
Enregistré le : 17 Jan 2008, 20:00
Contact :

Re: Modifier la valeur d'un membre d'une liste externe

Messagepar jean lobry » 17 Oct 2017, 18:10

Bonjour,

j'ai aussi constaté dans mon utilisation de R de tous les jours des améliorations spectaculaires des performances depuis que la compilation à la volée est activée par défaut (depuis 6 mois je dirais au pif, je suis loin d'être proactif pour être à jour). C'est assez impressionnant je dois dire.

Bien amicalement,

jean lobry


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Google [Bot] et 1 invité