Domanda

Sto ancora cercando di entrare nella logica R ... qual è il " best " modo per decomprimere (su LHS) i risultati da una funzione che restituisce più valori?

Apparentemente non posso farlo:

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

devo davvero fare quanto segue?

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

o il programmatore R scriverebbe qualcosa di più simile a questo:

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

--- modificato per rispondere alle domande di Shane ---

Non ho davvero bisogno di dare nomi alle parti del valore del risultato. Sto applicando una funzione aggregata al primo componente e un'altra al secondo componente (min e max. Se fosse la stessa funzione per entrambi i componenti non avrei bisogno di dividerli).

È stato utile?

Soluzione

(1) elenco [...] < - l'ho pubblicato più di un decennio fa su r-help . Da allora è stato aggiunto al pacchetto gsubfn. Non richiede un operatore speciale ma richiede che la parte sinistra sia scritta usando list[...] in questo modo:

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

Se hai bisogno solo del primo o del secondo componente, funzionano anche tutti questi:

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

(Naturalmente, se fosse necessario solo un valore, functionReturningTwoValues()[[1]] o functionReturningTwoValues()[[2]] sarebbe sufficiente.)

Vedi il thread c-help citato per altri esempi.

(2) con Se l'intento è semplicemente quello di combinare i valori multipli successivamente e i valori di ritorno sono nominati, una semplice alternativa è usare with:

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

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

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

(3) allegare Un'altra alternativa è allegare:

attach(myfun())
a + b

AGGIUNTO: attach e <=>

Altri suggerimenti

In qualche modo mi sono imbattuto in questo trucco intelligente su Internet ... Non sono sicuro che sia cattivo o bello, ma ti consente di creare un " magico " operatore che consente di decomprimere più valori di ritorno nella propria variabile. La := funzione è definita qui e inclusa sotto per i posteri:

':=' <- 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)) 
}

Con quello in mano, puoi fare quello che cerchi:

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

Non so come mi sento al riguardo. Forse potresti trovarlo utile nel tuo spazio di lavoro interattivo. Usarlo per costruire librerie (ri) utilizzabili (per il consumo di massa) potrebbe non essere la migliore idea, ma immagino dipenda da te.

... sai cosa dicono di responsabilità e potere ...

Di solito avvolgo l'output in un elenco, che è molto flessibile (puoi avere qualsiasi combinazione di numeri, stringhe, vettori, matrici, matrici, elenchi, oggetti nell'output)

così come:

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
functionReturningTwoValues <- function() { 
  results <- list()
  results$first <- 1
  results$second <-2
  return(results) 
}
a <- functionReturningTwoValues()

Penso che funzioni.

Ho messo insieme un pacchetto R zeallot per affrontare questo problema. zeallot include un operatore di assegnazione di incarichi multipli o di disimballaggio, %<-%. L'LHS dell'operatore è un numero qualsiasi di variabili da assegnare, costruite usando le chiamate a c(). L'RHS dell'operatore è un vettore, un elenco, un frame di dati, un oggetto data o qualsiasi oggetto personalizzato con un metodo destructure implementato (vedere ?zeallot::destructure).

Ecco alcuni esempi basati sul post originale

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, ..)

Scopri il pacchetto vignette per maggiori informazioni ed esempi.

Non c'è una risposta giusta a questa domanda. Dipendo davvero da cosa stai facendo con i dati. Nel semplice esempio sopra, suggerirei vivamente:

  1. Rendi le cose il più semplice possibile.
  2. Ove possibile, è consigliabile mantenere vettorizzate le funzioni. Ciò offre la massima flessibilità e velocità nel lungo periodo.

È importante che i valori 1 e 2 sopra abbiano nomi? In altre parole, perché è importante in questo esempio che 1 e 2 siano denominati aeb, anziché solo r [1] e r [2]? Una cosa importante da capire in questo contesto è che aeb sono anche entrambi vettori di lunghezza 1. Quindi non stai davvero cambiando nulla nel processo di assegnazione di quel compito, oltre ad avere 2 nuovi vettori a cui non è necessario fare riferimento a pedici:

> 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

Puoi anche assegnare i nomi al vettore originale se preferisci fare riferimento alla lettera piuttosto che all'indice:

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

[Modifica] Dato che applicherai min e max su ciascun vettore separatamente, suggerirei di usare una matrice (se aeb avranno la stessa lunghezza e lo stesso tipo di dati) o frame di dati (se aeb avrà la stessa lunghezza ma possono essere di tipi di dati diversi) oppure utilizzare un elenco come nel tuo ultimo esempio (se possono avere lunghezze e tipi di dati diversi).

> 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

Le liste sembrano perfette per questo scopo. Ad esempio all'interno della funzione che avresti

x = desired_return_value_1 # (vector, matrix, etc)

y = desired_return_value_2 # (vector, matrix, etc)

returnlist = list(x,y...)

}  # end of function

programma principale

x = returnlist[[1]]

y = returnlist[[2]]

Sì alla tua seconda e terza domanda: ecco cosa devi fare perché non puoi avere più "valori" a sinistra di un compito.

Che ne dici di usare assegnare?

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

Puoi passare i nomi della variabile che vuoi passare per riferimento.

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

Se è necessario accedere ai valori esistenti, il contrario di assign è get.

[A] Se ciascuno di foo e bar è un singolo numero, allora non c'è niente di sbagliato in c (foo, bar); e puoi anche nominare i componenti: c (Foo = foo, Bar = bar). Quindi puoi accedere ai componenti del risultato 'res' come res [1], res [2]; oppure, nel caso indicato, come res [" Foo "], res [" BAR "].

[B] Se foo e bar sono vettori dello stesso tipo e lunghezza, di nuovo non c'è niente di sbagliato nel restituire cbind (foo, bar) o rbind (foo, bar); allo stesso modo nominabile. Nel caso 'cbind', si accederà a foo e bar come res [, 1], res [, 2] o come res [, & Quot; Foo & Quot;], res [, & Quot ; Bar quot &;]. Potresti anche preferire restituire un frame di dati piuttosto che una matrice:

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

e accedi a loro come res $ Foo, res $ Bar. Ciò funzionerebbe anche se foo e bar erano della stessa lunghezza ma non dello stesso tipo (ad esempio foo è un vettore di numeri, esclude un vettore di stringhe di caratteri).

[C] Se foo e bar sono sufficientemente diversi da non combinarsi convenientemente come sopra, allora si dovrebbe sicuramente restituire un elenco.

Ad esempio, la tua funzione potrebbe adattarsi a un modello lineare e calcola anche i valori previsti, quindi potresti aver

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

e quindi return list(Foo=foo,Bar=bar) e quindi accedi al riepilogo come res $ Foo, i valori previsti come res $ Bar

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

Se vuoi riportare l'output della tua funzione nell'ambiente globale, puoi usare list2env, come in questo esempio:

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)

Questa funzione creerà tre oggetti nel tuo ambiente globale:

> my_obj1
  [1] 1 2 3

> my_obj2
  [1] 5 4 3

> myDF
    a b
  1 1 5
  2 2 4
  3 3 3

Per ottenere più output da una funzione e mantenerli nel formato desiderato, è possibile salvare gli output sul disco rigido (nella directory di lavoro) dall'interno della funzione e caricarli dall'esterno della funzione:

myfun <- function(x) {
                      df1 <- ...
                      df2 <- ...
                      save(df1, file = "myfile1")
                      save(df2, file = "myfile2")
}
load("myfile1")
load("myfile2")
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top