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.vassign
sugerencia 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] 103
Ejemplo 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 unadestructure
implementació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] 4
Para 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$e
fuente
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] 2
Sin 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
assign
opción potencialmente peligrosa (en la medida en que su uso es riesgoso) seríaVectorize
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
O supongo que podría vectorizarlo usted mismo manualmente con su propia función usando
mapply
que tal vez use un valor predeterminado razonable para elenvir
argumento. Por ejemplo,Vectorize
devolverá 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
vassign
funció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 # 13
Sin 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 # 0
fuente
...()
, 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.character
y 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::walk2
purrr::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] 4
fuente
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] 1
Mi 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] 4
Usé 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