Quero atribuir várias variáveis em uma única linha em R. É possível fazer algo assim?
values # initialize some vector of values
(a, b) = values[c(2,4)] # assign a and b to values at 2 and 4 indices of 'values'
Normalmente, desejo atribuir cerca de 5-6 variáveis em uma única linha, em vez de ter várias linhas. Existe uma alternativa?
list($a, $b) = array(1, 2)
? Isso seria legal! +1.vassign
sugestão abaixo chega perto ... :)X <- list();X[c('a','b')] <- values[c(2,4)]
. OK, você não os atribui na área de trabalho, mas os mantém juntos em uma lista. Eu prefiro fazer assim.Respostas:
Há uma ótima resposta no blog Lutando com os Problemas
Isso é obtido a partir daí, com modificações muito pequenas.
USANDO AS SEGUINTES TRÊS FUNÇÕES (Mais uma para permitir listas de tamanhos diferentes)
# Generic form '%=%' = function(l, r, ...) UseMethod('%=%') # Binary Operator '%=%.lbunch' = function(l, r, ...) { Envir = as.environment(-1) if (length(r) > length(l)) warning("RHS has more args than LHS. Only first", length(l), "used.") if (length(l) > length(r)) { warning("LHS has more args than RHS. RHS will be repeated.") r <- extendToMatch(r, l) } for (II in 1:length(l)) { do.call('<-', list(l[[II]], r[[II]]), envir=Envir) } } # Used if LHS is larger than RHS extendToMatch <- function(source, destin) { s <- length(source) d <- length(destin) # Assume that destin is a length when it is a single number and source is not if(d==1 && s>1 && !is.null(as.numeric(destin))) d <- destin dif <- d - s if (dif > 0) { source <- rep(source, ceiling(d/s))[1:d] } return (source) } # Grouping the left hand side g = function(...) { List = as.list(substitute(list(...)))[-1L] class(List) = 'lbunch' return(List) }
Então, para executar:
Agrupe o lado esquerdo usando a nova função
g()
O lado direito deve ser um vetor ou uma lista Use o operador binário recém-criado%=%
# Example Call; Note the use of g() AND `%=%` # Right-hand side can be a list or vector g(a, b, c) %=% list("hello", 123, list("apples, oranges")) g(d, e, f) %=% 101:103 # Results: > a [1] "hello" > b [1] 123 > c [[1]] [1] "apples, oranges" > d [1] 101 > e [1] 102 > f [1] 103
Exemplo de uso de listas de tamanhos diferentes:
Lado Esquerdo Mais Longo
g(x, y, z) %=% list("first", "second") # Warning message: # In `%=%.lbunch`(g(x, y, z), list("first", "second")) : # LHS has more args than RHS. RHS will be repeated. > x [1] "first" > y [1] "second" > z [1] "first"
Lado Direito Mais Longo
g(j, k) %=% list("first", "second", "third") # Warning message: # In `%=%.lbunch`(g(j, k), list("first", "second", "third")) : # RHS has more args than LHS. Only first2used. > j [1] "first" > k [1] "second"
fonte
Considere usar a funcionalidade incluída na base R.
Por exemplo, crie um dataframe de 1 linha (digamos
V
) e inicialize suas variáveis nele. Agora você pode atribuir a várias variáveis de uma vezV[,c("a", "b")] <- values[c(2, 4)]
, chamar cada uma por nome (V$a
) ou usar muitas delas ao mesmo tempo (values[c(5, 6)] <- V[,c("a", "b")]
).Se você ficar preguiçoso e não quiser sair chamando variáveis do dataframe, você pode
attach(V)
(embora eu pessoalmente nunca faça isso).# Initialize values values <- 1:100 # V for variables V <- data.frame(a=NA, b=NA, c=NA, d=NA, e=NA) # Assign elements from a vector V[, c("a", "b", "e")] = values[c(2,4, 8)] # Also other class V[, "d"] <- "R" # Use your variables V$a V$b V$c # OOps, NA V$d V$e
fonte
Eu montei um zeallot de pacote R para resolver esse problema. zeallot inclui um operador (
%<-%
) para descompactação, atribuição múltipla e desestruturação. O LHS da expressão de atribuição é construído usando chamadas parac()
. O RHS da expressão de atribuição pode ser qualquer expressão que retorna ou é um vetor, lista, lista aninhada, quadro de dados, cadeia de caracteres, objeto de data ou objetos personalizados (assumindo que haja umadestructure
implementação).Aqui está a pergunta inicial retrabalhada usando zeallot (versão mais recente, 0.0.5).
library(zeallot) values <- c(1, 2, 3, 4) # initialize a vector of values c(a, b) %<-% values[c(2, 4)] # assign `a` and `b` a #[1] 2 b #[1] 4
Para mais exemplos e informações, pode-se verificar a vinheta do pacote .
fonte
aqui está minha ideia. Provavelmente, a sintaxe é bastante simples:
`%tin%` <- function(x, y) { mapply(assign, as.character(substitute(x)[-1]), y, MoreArgs = list(envir = parent.frame())) invisible() } c(a, b) %tin% c(1, 2)
dá assim:
> a Error: object 'a' not found > b Error: object 'b' not found > c(a, b) %tin% c(1, 2) > a [1] 1 > b [1] 2
isso não foi bem testado.
fonte
c(c, d) %tin% c(1, 2) + 3
(=> c = 1, d = 1, retorna numérico ( 0)) pode ser considerado surpreendente.Uma opção potencialmente perigosa (na medida em que usar
assign
é arriscado) seriaVectorize
assign
:assignVec <- Vectorize("assign",c("x","value")) #.GlobalEnv is probably not what one wants in general; see below. assignVec(c('a','b'),c(0,4),envir = .GlobalEnv) a b 0 4 > b [1] 4 > a [1] 0
Ou suponho que você mesmo possa vetorizá-lo manualmente com sua própria função usando
mapply
isso talvez use um padrão razoável para oenvir
argumento. Por exemplo,Vectorize
retornará uma função com as mesmas propriedades de ambiente deassign
, que neste caso énamespace:base
, ou você poderia apenas definirenvir = parent.env(environment(assignVec))
.fonte
Como outros explicaram, não parece haver nada integrado. ... mas você pode projetar uma
vassign
função da seguinte maneira:vassign <- function(..., values, envir=parent.frame()) { vars <- as.character(substitute(...())) values <- rep(values, length.out=length(vars)) for(i in seq_along(vars)) { assign(vars[[i]], values[[i]], envir) } } # Then test it vals <- 11:14 vassign(aa,bb,cc,dd, values=vals) cc # 13
Porém, uma coisa a se considerar é como lidar com os casos em que você, por exemplo, especifica 3 variáveis e 5 valores ou o contrário. Aqui, simplesmente repito (ou trunco) os valores para que tenham o mesmo comprimento que as variáveis. Talvez um aviso seja prudente. Mas permite o seguinte:
vassign(aa,bb,cc,dd, values=0) cc # 0
fonte
...()
, o que parece magia negra para mim ...?...()
é magia negra extrema ;-). Acontece que quando a "chamada de função"...()
é substituída, ela se torna um pairlist que pode ser passado paraas.character
e voila, você tem os argumentos como strings ...envir=parent.frame()
- E você pode especificar, por exemplo,envir=globalenv()
se quiser.`vassign<-` <- function (..., envir = parent.frame (), value)
e assim por diante. No entanto, parece que o primeiro objeto a ser atribuído já precisaria existir. Alguma ideia?list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)
Serviu ao meu propósito, ou seja, atribuir cinco 2s nas primeiras cinco letras.
fonte
https://stat.ethz.ch/R-manual/R-devel/library/base/html/list2env.html :
list2env( list( a=1, b=2:4, c=rpois(10,10), d=gl(3,4,LETTERS[9:11]) ), envir=.GlobalEnv )
fonte
Tive um problema semelhante recentemente e aqui estava minha tentativa de usar
purrr::walk2
purrr::walk2(letters,1:26,assign,envir =parent.frame())
fonte
Se seu único requisito é ter uma única linha de código, que tal:
> a<-values[2]; b<-values[4]
fonte
Infelizmente, essa solução elegante que você está procurando (como
c(a, b) = c(2, 4)
) não existe. Mas não desista, não tenho certeza! A solução mais próxima que consigo pensar é esta:attach(data.frame(a = 2, b = 4))
ou se você se incomodar com avisos, desligue-os:
attach(data.frame(a = 2, b = 4), warn = F)
Mas suponho que você não esteja satisfeito com esta solução, eu também não ...
fonte
R> values = c(1,2,3,4) R> a <- values[2]; b <- values[3]; c <- values[4] R> a [1] 2 R> b [1] 3 R> c [1] 4
fonte
Outra versão com recursão:
let <- function(..., env = parent.frame()) { f <- function(x, ..., i = 1) { if(is.null(substitute(...))){ if(length(x) == 1) x <- rep(x, i - 1); stopifnot(length(x) == i - 1) return(x); } val <- f(..., i = i + 1); assign(deparse(substitute(x)), val[[i]], env = env); return(val) } f(...) }
exemplo:
> let(a, b, 4:10) [1] 4 5 6 7 8 9 10 > a [1] 4 > b [1] 5 > let(c, d, e, f, c(4, 3, 2, 1)) [1] 4 3 2 1 > c [1] 4 > f [1] 1
Minha versão:
let <- function(x, value) { mapply( assign, as.character(substitute(x)[-1]), value, MoreArgs = list(envir = parent.frame())) invisible() }
exemplo:
> let(c(x, y), 1:2 + 3) > x [1] 4 > y [1]
fonte
Combinando algumas das respostas dadas aqui + um pouco de sal, que tal esta solução:
assignVec <- Vectorize("assign", c("x", "value")) `%<<-%` <- function(x, value) invisible(assignVec(x, value, envir = .GlobalEnv)) c("a", "b") %<<-% c(2, 4) a ## [1] 2 b ## [1] 4
Usei isso para adicionar a seção R aqui: http://rosettacode.org/wiki/Sort_three_variables#R
Advertência: só funciona para atribuir variáveis globais (como
<<-
). Se houver uma solução melhor e mais geral, pls. diga-me nos comentários.fonte