superponer - ¿Cómo asignar desde una función que devuelve más de un valor?
superponer graficas en r (12)
¿Qué hay de usar asignar?
functionReturningTwoValues <- function(a, b) {
assign(a, 1, pos=1)
assign(b, 2, pos=1)
}
Puede pasar los nombres de la variable que desea que se pase por referencia.
> functionReturningTwoValues(''a'', ''b'')
> a
[1] 1
> b
[1] 2
Si necesita acceder a los valores existentes, el converso de assign
es get
.
Aún intentando ingresar a la lógica R ... ¿cuál es la "mejor" forma de descomprimir (en LHS) los resultados de una función que devuelve múltiples valores?
No puedo hacer esto al parecer:
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
¿Debo realmente hacer lo siguiente?
R> r <- functionReturningTwoValues()
R> a <- r[1]; b <- r[2]
o el programador de R escribiría algo más como esto:
R> functionReturningTwoValues <- function() {return(list(first=1, second=2))}
R> r <- functionReturningTwoValues()
R> r$first
[1] 1
R> r$second
[1] 2
--- editado para responder a las preguntas de Shane ---
Realmente no necesito dar nombres a las partes de valor de resultado. Estoy aplicando una función agregada al primer componente y otra al segundo componente ( min
Y max
. Si fuera la misma función para ambos componentes, no necesitaría dividirlos).
De alguna manera, me topé con este inteligente truco en Internet ... No estoy seguro de que sea desagradable o hermoso, pero te permite crear un operador "mágico" que te permite desempaquetar múltiples valores de retorno en su propia variable. La función :=
se define aquí , y se incluye a continuación para la posteridad:
'':='' <- 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 eso en la mano, puedes hacer lo que buscas:
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
No sé cómo me siento al respecto. Quizás le resulte útil en su espacio de trabajo interactivo. Usarla para construir bibliotecas (re) utilizables (para consumo masivo) puede no ser la mejor idea, pero supongo que eso depende de usted.
... sabes lo que dicen sobre responsabilidad y poder ...
Las listas parecen perfectas para este propósito. Por ejemplo dentro de la función tendrías
x = desired_return_value_1 # (vector, matrix, etc)
y = desired_return_value_2 # (vector, matrix, etc)
returnlist = list(x,y...)
} # end of function
programa principal
x = returnlist[[1]]
y = returnlist[[2]]
No hay una respuesta correcta a esta pregunta. Realmente depende de lo que estés haciendo con los datos. En el ejemplo simple anterior, sugeriría fuertemente:
- Mantenga las cosas lo más simples posible.
- Siempre que sea posible, es una buena práctica mantener sus funciones vectorizadas. Eso proporciona la mayor cantidad de flexibilidad y velocidad a largo plazo.
¿Es importante que los valores 1 y 2 de arriba tengan nombres? En otras palabras, ¿por qué es importante en este ejemplo que 1 y 2 se llamen a y b, en lugar de solo r [1] yr [2]? Una cosa importante que se debe entender en este contexto es que ayb son también vectores de longitud 1. Por lo tanto, no está cambiando nada en el proceso de hacer esa asignación, aparte de tener 2 vectores nuevos que no necesitan subíndices para ser referenciado:
> 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
También puede asignar los nombres al vector original si prefiere hacer referencia a la letra que al índice:
> names(r) <- c("a","b")
> names(r)
[1] "a" "b"
> r["a"]
a
1
[Editar] Dado que aplicará min y max a cada vector por separado, sugeriría usar una matriz (si a y b tendrán la misma longitud y el mismo tipo de datos) o marco de datos (si a y b serán la misma longitud, pero pueden ser de diferentes tipos de datos) o bien use una lista como en su último ejemplo (si pueden ser de diferentes longitudes y tipos de datos).
> 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
Para obtener múltiples salidas de una función y mantenerlas en el formato deseado, puede guardar las salidas en su disco duro (en el directorio de trabajo) desde dentro de la función y luego cargarlas desde fuera de la función:
myfun <- function(x) {
df1 <- ...
df2 <- ...
save(df1, file = "myfile1")
save(df2, file = "myfile2")
}
load("myfile1")
load("myfile2")
Por lo general, envuelvo la salida en una lista, que es muy flexible (puede tener cualquier combinación de números, cadenas, vectores, matrices, matrices, listas, objetos en la salida)
asi como:
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
Sí a la segunda y tercera pregunta: eso es lo que debe hacer, ya que no puede tener múltiples ''valores'' a la izquierda de una tarea.
Si desea devolver la salida de su función al entorno global, puede usar list2env
, como en este ejemplo:
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)
Esta función creará tres objetos en su entorno 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
[A] Si cada uno de foo y bar es un solo número, entonces no hay nada de malo en c (foo, bar); y también puedes nombrar los componentes: c (Foo = foo, Bar = bar). Así que podría acceder a los componentes del resultado ''res'' como res [1], res [2]; o, en el caso mencionado, como res ["Foo"], res ["BAR"].
[B] Si foo y bar son vectores del mismo tipo y longitud, tampoco hay nada de malo en devolver cbind (foo, bar) o rbind (foo, bar); igualmente nombrables. En el caso ''cbind'', accedería a foo y bar como res [, 1], res [, 2] o como res [, "Foo"], res [, "Bar"]. También puede preferir devolver un marco de datos en lugar de una matriz:
data.frame(Foo=foo,Bar=bar)
Y accede a ellos como res $ Foo, res $ Bar. Esto también funcionaría bien si foo y bar fueran de la misma longitud pero no del mismo tipo (por ejemplo, foo es un vector de números, bar un vector de cadenas de caracteres).
[C] Si foo y bar son lo suficientemente diferentes como para que no se combinen convenientemente como se indicó anteriormente, entonces definitivamente deberías devolver una lista.
Por ejemplo, su función podría ajustarse a un modelo lineal y también calcular valores predichos, por lo que podría tener
LM<-lm(....) ; foo<-summary(LM); bar<-LM$fit
y luego return list(Foo=foo,Bar=bar)
y luego accedería al resumen como res $ Foo, los valores pronosticados como res $ Bar
fuente: http://r.789695.n4.nabble.com/How-to-return-multiple-values-in-a-function-td858528.html
Junté un paquete R de zeallot para abordar este problema. zeallot incluye un operador de asignación múltiple o desempaquetado, %<-%
. El LHS del operador es cualquier número de variables para asignar, construido usando llamadas a c()
. El RHS del operador es un vector, lista, cuadro de datos, objeto de fecha o cualquier objeto personalizado con un método de destructure
implementado (consulte ?zeallot::destructure
).
Aquí hay un puñado de ejemplos basados en la publicación 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, ..)
Echa un vistazo a la vignette del paquete para obtener más información y ejemplos.
(1) lista [...] <- Había publicado esto hace más de una década en r-help . Desde entonces se ha agregado al paquete gsubfn. No requiere un operador especial, pero sí requiere que el lado izquierdo se escriba usando una list[...]
como esta:
library(gsubfn) # need 0.7-0 or later
list[a, b] <- functionReturningTwoValues()
Si solo necesitas el primer o el segundo componente, también funcionan todos:
list[a] <- functionReturningTwoValues()
list[a, ] <- functionReturningTwoValues()
list[, b] <- functionReturningTwoValues()
(Por supuesto, si solo necesita un valor, la functionReturningTwoValues()[[1]]
o functionReturningTwoValues()[[2]]
sería suficiente.)
Vea el hilo c-help citado para más ejemplos.
(2) con Si la intención es simplemente combinar los múltiples valores posteriormente y los valores de retorno se nombran, entonces una alternativa simple es usar with
:
myfun <- function() list(a = 1, b = 2)
list[a, b] <- myfun()
a + b
# same
with(myfun(), a + b)
(3) adjuntar Otra alternativa es adjuntar:
attach(myfun())
a + b
AÑADIDO: with
y attach
functionReturningTwoValues <- function() {
results <- list()
results$first <- 1
results$second <-2
return(results)
}
a <- functionReturningTwoValues()
Creo que esto funciona.