web-dev-qa-db-fra.com

Comment assigner une fonction qui retourne plus d'une valeur?

Nous essayons toujours d'entrer dans la logique R ... Quel est le "meilleur" moyen de décompresser (sur LHS) les résultats d'une fonction renvoyant plusieurs valeurs?

Je ne peux pas faire ça apparemment:

R> functionReturningTwoValues <- function() { return(c(1, 2)) }
R> functionReturningTwoValues()
[1] 1 2
R> a, b <- functionReturningTwoValues()
Error: unexpected ',' in "a,"
R> c(a, b) <- functionReturningTwoValues()
Error in c(a, b) <- functionReturningTwoValues() : object 'a' not found

dois-je vraiment faire ce qui suit?

R> r <- functionReturningTwoValues()
R> a <- r[1]; b <- r[2]

ou le programmeur R écrirait-il quelque chose de plus semblable à ceci:

R> functionReturningTwoValues <- function() {return(list(first=1, second=2))}
R> r <- functionReturningTwoValues()
R> r$first
[1] 1
R> r$second
[1] 2

--- édité pour répondre aux questions de Shane ---

Je n'ai pas vraiment besoin de donner des noms aux parties de la valeur du résultat. J'applique une fonction d'agrégat au premier composant et une autre au second (min et max. Si c'était la même fonction pour les deux composants, je n'aurais pas besoin de les séparer).

199
mariotomo

(1) list [...] <- Je l'avais posté il y a plus de dix ans sur r-help . Depuis lors, il a été ajouté au paquet gsubfn. Il ne nécessite pas d'opérateur spécial, mais requiert que le côté gauche soit écrit en utilisant list[...] comme ceci:

library(gsubfn)  # need 0.7-0 or later
list[a, b] <- functionReturningTwoValues()

Si vous avez seulement besoin du premier ou du deuxième composant, ceux-ci fonctionnent également:

list[a] <- functionReturningTwoValues()
list[a, ] <- functionReturningTwoValues()
list[, b] <- functionReturningTwoValues()

(Bien entendu, si vous n'avez besoin que d'une seule valeur, alors functionReturningTwoValues()[[1]] ou functionReturningTwoValues()[[2]] suffiront.)

Voir le fil de discussion r-help cité pour plus d'exemples.

(2) avec Si l'intention est simplement de combiner les valeurs multiples par la suite et que les valeurs de retour sont nommées, une alternative simple consiste à utiliser with:

myfun <- function() list(a = 1, b = 2)

list[a, b] <- myfun()
a + b

# same
with(myfun(), a + b)

(3) attach Une autre alternative est d'attacher:

attach(myfun())
a + b

AJOUTÉ: with et attach

170
G. Grothendieck

Je suis tombé sur cette astuce intelligente sur Internet ... Je ne suis pas sûr que ce soit méchant ou beau, mais cela vous permet de créer un opérateur "magique" qui vous permet de décompresser plusieurs valeurs de retour dans leur propre variable. La fonction :=est définie ici , et incluse ci-dessous pour la postérité:

':=' <- function(lhs, rhs) {
  frame <- parent.frame()
  lhs <- as.list(substitute(lhs))
  if (length(lhs) > 1)
    lhs <- lhs[-1]
  if (length(lhs) == 1) {
    do.call(`=`, list(lhs[[1]], rhs), envir=frame)
    return(invisible(NULL)) 
  }
  if (is.function(rhs) || is(rhs, 'formula'))
    rhs <- list(rhs)
  if (length(lhs) > length(rhs))
    rhs <- c(rhs, rep(list(NULL), length(lhs) - length(rhs)))
  for (i in 1:length(lhs))
    do.call(`=`, list(lhs[[i]], rhs[[i]]), envir=frame)
  return(invisible(NULL)) 
}

Avec cela en main, vous pouvez faire ce que vous recherchez:

functionReturningTwoValues <- function() {
  return(list(1, matrix(0, 2, 2)))
}
c(a, b) := functionReturningTwoValues()
a
#[1] 1
b
#     [,1] [,2]
# [1,]    0    0
# [2,]    0    0

Je ne sais pas ce que je ressens à ce sujet. Peut-être que vous trouverez cela utile dans votre espace de travail interactif. L’utiliser pour construire des bibliothèques (réutilisables) (pour une consommation de masse) n’est peut-être pas la meilleure idée, mais je suppose que cela vous appartient.

... vous savez ce qu'ils disent sur la responsabilité et le pouvoir ...

68
Steve Lianoglou

Habituellement, j'emballe la sortie dans une liste très flexible (vous pouvez avoir n'importe quelle combinaison de nombres, chaînes, vecteurs, matrices, tableaux, listes, objets dans la sortie)

tellement comme:

func2<-function(input) {
   a<-input+1
   b<-input+2
   output<-list(a,b)
   return(output)
}

output<-func2(5)

for (i in output) {
   print(i)
}

[1] 6
[1] 7
43
Federico Giorgi
functionReturningTwoValues <- function() { 
  results <- list()
  results$first <- 1
  results$second <-2
  return(results) 
}
a <- functionReturningTwoValues()

Je pense que cela fonctionne.

14
Jojo

Je mets ensemble un paquet R zeallot pour résoudre ce problème. zeallot inclut un opérateur d’affectation multiple ou de décompression, %<-%. Le LHS de l'opérateur est un nombre quelconque de variables à affecter, construit à l'aide des appels à c(). Le RHS de l'opérateur est un vecteur, une liste, un bloc de données, un objet de date ou tout objet personnalisé avec une méthode implémentée destructure (voir ?zeallot::destructure).

Voici une poignée d'exemples basés sur le post original,

library(zeallot)

functionReturningTwoValues <- function() { 
  return(c(1, 2)) 
}

c(a, b) %<-% functionReturningTwoValues()
a  # 1
b  # 2

functionReturningListOfValues <- function() {
  return(list(1, 2, 3))
}

c(d, e, f) %<-% functionReturningListOfValues()
d  # 1
e  # 2
f  # 3

functionReturningNestedList <- function() {
  return(list(1, list(2, 3)))
}

c(f, c(g, h)) %<-% functionReturningNestedList()
f  # 1
g  # 2
h  # 3

functionReturningTooManyValues <- function() {
  return(as.list(1:20))
}

c(i, j, ...rest) %<-% functionReturningTooManyValues()
i     # 1
j     # 2
rest  # list(3, 4, 5, ..)

Découvrez le package vignette pour plus d'informations et d'exemples.

11
nteetor

Il n'y a pas de bonne réponse à cette question. Je dépend vraiment de ce que vous faites avec les données. Dans l'exemple simple ci-dessus, je suggérerais fortement:

  1. Gardez les choses aussi simples que possible.
  2. Dans la mesure du possible, il est recommandé de garder vos fonctions vectorisées. Cela offre la plus grande flexibilité et rapidité à long terme.

Est-il important que les valeurs 1 et 2 ci-dessus aient un nom? En d'autres termes, pourquoi est-il important dans cet exemple que 1 et 2 soient nommés a et b, plutôt que simplement r [1] et r [2]? Une chose importante à comprendre dans ce contexte est que a et b sont aussi les deux vecteurs de longueur 1. Donc, vous ne changez rien dans le processus d’exécution de cette tâche, mis à part le fait d’avoir 2 nouveaux vecteurs. qui n'a pas besoin d'indices pour être référencé:

> r <- c(1,2)
> a <- r[1]
> b <- r[2]
> class(r)
[1] "numeric"
> class(a)
[1] "numeric"
> a
[1] 1
> a[1]
[1] 1

Vous pouvez également affecter les noms au vecteur d'origine si vous préférez référencer la lettre plutôt que l'index:

> names(r) <- c("a","b")
> names(r)
[1] "a" "b"
> r["a"]
a 
1 

[Edit] Etant donné que vous appliquerez séparément min et max à chaque vecteur, je suggérerais d'utiliser une matrice (si a et b auront la même longueur et le même type de données) ou un bloc de données (si a et b auront la même longueur mais peuvent être de types de données différents) ou utilisez une liste comme dans votre dernier exemple (s’ils peuvent être de longueurs et de types de données différents).

> r <- data.frame(a=1:4, b=5:8)
> r
  a b
1 1 5
2 2 6
3 3 7
4 4 8
> min(r$a)
[1] 1
> max(r$b)
[1] 8
10
Shane

Les listes semblent parfaites à cette fin. Par exemple, dans la fonction que vous auriez

x = desired_return_value_1 # (vector, matrix, etc)

y = desired_return_value_2 # (vector, matrix, etc)

returnlist = list(x,y...)

}  # end of function

programme principal

x = returnlist[[1]]

y = returnlist[[2]]
5
Arnold

Oui à vos deuxième et troisième questions - c’est ce que vous devez faire, car vous ne pouvez pas avoir plusieurs "valeurs" à gauche d’un devoir.

4
Dirk Eddelbuettel

Que diriez-vous d'utiliser assigner?

functionReturningTwoValues <- function(a, b) {
  assign(a, 1, pos=1)
  assign(b, 2, pos=1)
}

Vous pouvez transmettre les noms de la variable que vous souhaitez transmettre par référence.

> functionReturningTwoValues('a', 'b')
> a
[1] 1
> b
[1] 2

Si vous devez accéder aux valeurs existantes, l'inverse de assign est get.

2
Steve Pitchers

[A] Si foo et bar sont des nombres simples, c (foo, bar) ne pose aucun problème; et vous pouvez également nommer les composants: c (Foo = foo, Bar = bar). Vous pouvez donc accéder aux composants du résultat 'res' en tant que res [1], res [2]; ou, dans le cas nommé, en tant que res ["Foo"], res ["BAR"].

[B] Si foo et bar sont des vecteurs du même type et de la même longueur, il n’ya rien de mal à renvoyer cbind (foo, bar) ou rbind (foo, bar); de même nommable. Dans le cas "cbind", vous accéderiez à foo et à bar en tant que res [ 1], res [ 2] ou en tant que res [ "Foo"], res [ "Bar"]. Vous pouvez également préférer renvoyer une image de données plutôt qu'une matrice:

data.frame(Foo=foo,Bar=bar)

et y accéder en tant que res $ Foo, res $ Bar. Cela fonctionnerait également bien si foo et bar étaient de la même longueur mais pas du même type (par exemple, foo est un vecteur de nombres, barré un vecteur de chaînes de caractères).

[C] Si foo et bar sont suffisamment différents pour ne pas être combinés convenablement comme ci-dessus, vous devez alors renvoyer une liste.

Par exemple, votre fonction peut s’adapter à un modèle linéaire et également calculer des valeurs prédites, de sorte que vous pourriez avoir

LM<-lm(....) ; foo<-summary(LM); bar<-LM$fit

puis vous feriez return list(Foo=foo,Bar=bar), puis accédez au résumé sous la forme res $ Foo, les valeurs prédites sous la forme res $ Bar

source: http://r.789695.n4.nabble.com/How-to-return-multiple-values-in-a-function-td858528.html

1
Prakhar Agrawal

Si vous souhaitez renvoyer la sortie de votre fonction dans l'environnement global, vous pouvez utiliser list2env, comme dans cet exemple:

myfun <- function(x) { a <- 1:x
                       b <- 5:x
                       df <- data.frame(a=a, b=b)

                       newList <- list("my_obj1" = a, "my_obj2" = b, "myDF"=df)
                       list2env(newList ,.GlobalEnv)
                       }
    myfun(3)

Cette fonction créera trois objets dans votre environnement global:

> my_obj1
  [1] 1 2 3

> my_obj2
  [1] 5 4 3

> myDF
    a b
  1 1 5
  2 2 4
  3 3 3
1
rafa.pereira

Pour obtenir plusieurs sorties d’une fonction et les conserver au format souhaité, vous pouvez enregistrer les sorties sur votre disque dur (dans le répertoire de travail) à partir de la fonction, puis les charger de l’extérieur:

myfun <- function(x) {
                      df1 <- ...
                      df2 <- ...
                      save(df1, file = "myfile1")
                      save(df2, file = "myfile2")
}
load("myfile1")
load("myfile2")
0
Andrew Eaves