Quiero asignar múltiples variables en una sola línea en R. ¿Es posible hacer algo como esto?
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 quiero asignar alrededor de 5-6 variables en una sola línea, en lugar de tener varias líneas. ¿Existe alguna alternativa?

list($a, $b) = array(1, 2)? ¡Eso estaría bien! +1.vassignsugerencia a continuación se acerca ... :)X <- list();X[c('a','b')] <- values[c(2,4)]. De acuerdo, no los asigna en el espacio de trabajo, pero manténgalos juntos en una lista. Preferiría hacerlo de esa manera.Respuestas:
Hay una gran respuesta en el blog Luchando por los problemas
Esto se toma a partir de ahí, con modificaciones muy pequeñas.
USANDO LAS SIGUIENTES TRES FUNCIONES (Más una para permitir listas de diferentes tamaños)
# 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) }Luego para ejecutar:
Agrupe el lado izquierdo usando la nueva función
g()El lado derecho debe ser un vector o una lista Use el operador binario recién creado%=%# 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] 103Ejemplo usando listas de diferentes tamaños:
Lado izquierdo más largo
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 derecho más largo
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"fuente
Reuní un paquete R zeallot para abordar este mismo problema. zeallot incluye un operador (
%<-%) para desempaquetar, asignaciones múltiples y desestructuraciones. El LHS de la expresión de asignación se crea mediante llamadas ac(). El RHS de la expresión de asignación puede ser cualquier expresión que devuelva o sea un vector, lista, lista anidada, marco de datos, cadena de caracteres, objeto de fecha u objetos personalizados (suponiendo que haya unadestructureimplementación).Aquí está la pregunta inicial reelaborada usando zeallot (última versión, 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] 4Para obtener más ejemplos e información, puede consultar la viñeta del paquete .
fuente
Considere usar la funcionalidad incluida en la base R.
Por ejemplo, cree un marco de datos de 1 fila (digamos
V) e inicialice sus variables en él. Ahora puede asignar múltiples variables a la vezV[,c("a", "b")] <- values[c(2, 4)], llamar a cada una por su nombre (V$a) o usar muchas de ellas al mismo tiempo (values[c(5, 6)] <- V[,c("a", "b")]).Si se vuelve perezoso y no quiere andar llamando variables desde el marco de datos, podría
attach(V)hacerlo (aunque yo personalmente nunca lo hago).# 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$efuente
aquí está mi idea. Probablemente la sintaxis sea bastante simple:
`%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)da así:
> a Error: object 'a' not found > b Error: object 'b' not found > c(a, b) %tin% c(1, 2) > a [1] 1 > b [1] 2Sin embargo, esto no está bien probado.
fuente
c(c, d) %tin% c(1, 2) + 3(=> c = 1, d = 1, devuelve numérico ( 0)) puede considerarse sorprendente.Una
assignopción potencialmente peligrosa (en la medida en que su uso es riesgoso) seríaVectorizeassign: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] 0O supongo que podría vectorizarlo usted mismo manualmente con su propia función usando
mapplyque tal vez use un valor predeterminado razonable para elenvirargumento. Por ejemplo,Vectorizedevolverá una función con las mismas propiedades de entorno deassign, que en este caso esnamespace:base, o simplemente podría establecerenvir = parent.env(environment(assignVec)).fuente
Como explicaron otros, no parece haber nada integrado ... pero puede diseñar una
vassignfunción de la siguiente manera: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 # 13Sin embargo, una cosa a considerar es cómo manejar los casos en los que, por ejemplo, especifica 3 variables y 5 valores o al revés. Aquí simplemente repito (o trunco) los valores para que tengan la misma longitud que las variables. Quizás sería prudente una advertencia. Pero permite lo siguiente:
vassign(aa,bb,cc,dd, values=0) cc # 0fuente
...(), lo que me parece magia negra ...?...()es magia negra extrema ;-). Sucede que cuando...()se sustituye la "llamada de función" , se convierte en una lista de pares que se puede pasaras.charactery listo, tienes los argumentos como cadenas ...envir=parent.frame()- Y puede especificar, por ejemplo,envir=globalenv()si lo desea.`vassign<-` <- function (..., envir = parent.frame (), value)y así sucesivamente. Sin embargo, parece que el primer objeto que se asignará ya debería existir. ¿Algunas ideas?list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)Cumplió mi propósito, es decir, asignar cinco 2 en las primeras cinco letras.
fuente
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 )fuente
Tuve un problema similar recientemente y aquí estaba mi intento de usar
purrr::walk2purrr::walk2(letters,1:26,assign,envir =parent.frame())fuente
Si su único requisito es tener una sola línea de código, entonces qué tal:
> a<-values[2]; b<-values[4]fuente
Me temo que esa solución elegante que está buscando (como
c(a, b) = c(2, 4)) lamentablemente no existe. ¡Pero no te rindas, no estoy seguro! La solución más cercana que puedo pensar es esta:attach(data.frame(a = 2, b = 4))o si le molestan las advertencias, apáguelas:
attach(data.frame(a = 2, b = 4), warn = F)Pero supongo que no estás satisfecho con esta solución, yo tampoco lo estaría ...
fuente
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] 4fuente
Otra versión con recursividad:
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(...) }ejemplo:
> 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] 1Mi version:
let <- function(x, value) { mapply( assign, as.character(substitute(x)[-1]), value, MoreArgs = list(envir = parent.frame())) invisible() }ejemplo:
> let(c(x, y), 1:2 + 3) > x [1] 4 > y [1]fuente
Combinando algunas de las respuestas dadas aquí + un poco de sal, ¿qué tal esta solución?
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] 4Usé esto para agregar la sección R aquí: http://rosettacode.org/wiki/Sort_three_variables#R
Advertencia: solo funciona para asignar variables globales (como
<<-). Si hay una solución mejor y más general, por favor. Dime en los comentarios.fuente