Pergunta

Ainda tentando entrar na lógica R ... qual é a "melhor" maneira de descompactar (em LHS) os resultados de uma função que retorna vários valores?

Eu não posso fazer isso, aparentemente:

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

deve Eu realmente fazer o seguinte?

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

ou seria o R write programador algo mais parecido com isto:

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 perguntas de Shane ---

Eu realmente não precisa dar nomes às partes valor do resultado. Estou aplicando uma função agregada ao primeiro componente e um outro para o segundo componente (min e max. Se era a mesma função para ambos os componentes eu não precisaria de dividi-los).

Foi útil?

Solução

(1) list [...] <- eu tinha postado isso mais de uma década atrás, em r-ajuda . Desde então, tem sido adicionado ao pacote gsubfn. Ele não requer um operador de especial, mas exige que o lado esquerdo ser escrito usando list[...] assim:

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

Se você precisar apenas do primeiro ou segundo componente todos estes trabalhos demasiado:

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

(Claro, se você só precisava de um valor, então functionReturningTwoValues()[[1]] ou functionReturningTwoValues()[[2]] seria suficiente.)

Veja o fio r-ajuda citada para mais exemplos.

(2) com Se a intenção é apenas para combinar os vários valores, posteriormente, e os valores de retorno são nomeados em seguida, uma alternativa simples é usar with:

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

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

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

(3) anexar Outra alternativa é anexar:

attach(myfun())
a + b

ADICIONADO: with e attach

Outras dicas

De alguma maneira eu tropeçou em este hack esperto na internet ... Eu não tenho certeza se é desagradável ou bonito, mas permite criar um operador de "mágica" que lhe permite vários valores de retorno descompactar em sua própria variável. A função := é definido aqui , e incluiu a seguir para posteridade:

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

Com isso em mãos, você pode fazer o que você está depois:

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

Eu não sei como me sinto sobre isso. Talvez você pode achar que é útil em sua área de trabalho interativa. Usá-lo para construir (re) bibliotecas utilizáveis ??(para consumo de massa) pode não ser a melhor idéia, mas eu acho que é até você.

... você sabe o que eles dizem sobre a responsabilidade e poder ...

Normalmente eu embrulhar a saída em uma lista, que é muito flexível (você pode ter qualquer combinação de números, cordas, vetores, matrizes, matrizes, listas, objetos int ele produção)

tão parecido com:

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()

Eu acho que isso funciona.

Eu coloquei um pacote de R zeallot para resolver este problema. zeallot inclui uma atribuição múltipla ou operador de atribuição de desempacotamento, %<-%. Os LHS do operador é qualquer número de variáveis ??para atribuir, construídas usando chamadas para c(). O RHS do operador é um vetor, lista, quadro de dados, objeto de data, ou qualquer objeto personalizado com um método destructure implementado (ver ?zeallot::destructure).

Aqui está um punhado de exemplos baseados no 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, ..)

Confira o pacote vinheta para mais informações e exemplos.

Não há resposta certa para esta pergunta. Eu realmente depende do que você está fazendo com os dados. No exemplo simples acima, gostaria de sugerir fortemente:

  1. Mantenha as coisas o mais simples possível.
  2. Sempre que possível, é uma boa prática para manter suas funções vetorizado. Que oferece a maior quantidade de flexibilidade e velocidade no longo prazo.

É importante que os valores 1 e 2 têm nomes? Em outras palavras, por que é importante neste exemplo que 1 e 2 ser chamado a e b, em vez de apenas r [1] e R [2]? Uma coisa importante a entender neste contexto é que a e b são também ambos os vetores de tamanho 1. Então, você não está realmente mudando alguma coisa no processo de fazer essa atribuição, para além de ter 2 novos vetores que não precisa subscritos a 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

Você também pode atribuir os nomes ao vetor original, se você preferiria fazer referência à carta que o índice:

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

[Editar] Dado que vai aplicar mínimo e máximo para cada um dos vectores separadamente, eu sugiro quer utilizando uma matriz (se a e b vai ter o mesmo comprimento e o mesmo tipo de dados) ou quadro de dados (se a e b será o mesmo comprimento, mas pode haver diferentes tipos de dados) ou então usar uma lista como em seu último exemplo (se eles podem ser de diferentes comprimentos e tipos de dados).

> 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

Listas parece perfeito para esta finalidade. Por exemplo dentro da função que você teria

x = desired_return_value_1 # (vector, matrix, etc)

y = desired_return_value_2 # (vector, matrix, etc)

returnlist = list(x,y...)

}  # end of function

principal do programa

x = returnlist[[1]]

y = returnlist[[2]]

Sim para a sua segunda e terceira questões -. Isso é o que você precisa fazer como você não pode ter vários 'lvalues' no lado esquerdo de uma atribuição

Como sobre o uso de atribuir?

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

Você pode passar os nomes da variável que deseja ser passados ??por referência.

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

Se você precisar acessar os valores existentes, o inverso do assign é get.

[A] Se cada um de foo e bar é um único número, então não há nada de errado com c (foo, bar); e você também pode nomear a componentes: c (Foo = foo, bar = bar). Assim, você poderá acessar os componentes do resultado 'res' como res [1], res [2]; ou, no caso nomeado, como res [ "Foo"], res [ "bar"].

[B] Se foo e bar são vetores do mesmo tipo e comprimento, em seguida, novamente não há nada errado com o retorno cbind (foo, bar) ou rbind (foo, bar); Da mesma forma nameable. No caso 'cbind', você poderia acessar foo e bar, res [, 1], res [, 2] ou como res [ "Foo"], res [, "Bar"]. Você também pode preferir retornar uma trama de dados, em vez de uma matriz:

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

e acessá-los como res $ foo, res $ Bar. Isso também funciona bem se foo e barra eram do mesmo comprimento, mas não do mesmo tipo (por exemplo foo é um vector de números, barrar um vector de cadeias de caracteres).

[C] Se foo e bar são suficientemente diferentes para não combinam convenientemente como acima, então você shuld voltar definitivamente uma lista.

Por exemplo, sua função pode ajustar um modelo linear e Também calcular valores previstos, para que pudesse ter

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

e então você return list(Foo=foo,Bar=bar) e, em seguida, acessar o resumo como res $ Foo, os valores previstos como res $ Bar

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

Se você quiser retornar a saída de sua função para o Meio Ambiente Global, você pode usar list2env, como neste exemplo:

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 função irá criar três objetos em seu Global Environment:

> my_obj1
  [1] 1 2 3

> my_obj2
  [1] 5 4 3

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

Para obter múltiplas saídas de uma função e mantê-los no formato desejado, você pode salvar as saídas para o disco rígido (no diretório de trabalho) de dentro da função e, em seguida, carregá-los de fora da função:

myfun <- function(x) {
                      df1 <- ...
                      df2 <- ...
                      save(df1, file = "myfile1")
                      save(df2, file = "myfile2")
}
load("myfile1")
load("myfile2")
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top