Assegna più nuove variabili su LHS in una singola riga

Voglio assegnare più variabili in una singola riga in R. È ansible fare qualcosa di simile?

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' 

In genere voglio assegnare circa 5-6 variabili in una singola riga, invece di avere più linee. C’è un’alternativa?

C’è una grande risposta sul blog Lottando attraverso i problemi

Questo è preso da lì, con modifiche molto minori.

UTILIZZANDO LE SEGUENTI TRE FUNZIONI (Più una per consentire elenchi di dimensioni diverse)

 # 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) } 

Quindi per eseguire:

Raggruppa il lato sinistro usando la nuova funzione g() Il lato destro dovrebbe essere un vettore o un elenco Usa l'operatore binario appena creato %=%

 # 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 

Esempio utilizzando elenchi di dimensioni diverse:

Lato sinistro più lungo

 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" 

Lato destro più lungo

 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" 

Prendi in considerazione l’utilizzo della funzionalità inclusa nella base R.

Ad esempio, crea un dataframe a 1 riga (ad esempio V ) e inizializza le variabili in esso. Ora puoi assegnare a più variabili contemporaneamente V[,c("a", "b")] <- values[c(2, 4)] , chiama ciascuno per nome ( V$a ), o usa molti di allo stesso tempo ( values[c(5, 6)] <- V[,c("a", "b")] ).

Se ti senti pigro e non vuoi andare in giro a chiamare variabili dal dataframe, puoi attach(V) (anche se personalmente non lo faccio mai).

 # 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 

ecco la mia idea Probabilmente la syntax è abbastanza semplice:

 `%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à come questo:

 > 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 

questo non è ben testato però.

Come altri hanno spiegato, sembra che non ci sia nulla di costruito dentro … ma potresti progettare una funzione di vassign come segue:

 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 

Una cosa da considerare è come gestire i casi in cui, ad esempio, si specificano 3 variabili e 5 valori o viceversa. Qui ripeto semplicemente (o tronca) i valori per essere della stessa lunghezza delle variabili. Forse un avvertimento sarebbe prudente. Ma consente quanto segue:

 vassign(aa,bb,cc,dd, values=0) cc # 0 

Un’opzione potenzialmente pericolosa (in quanto l’ assign è rischiosa) sarebbe quella di assign Vectorize :

 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) ab 0 4 > b [1] 4 > a [1] 0 

O suppongo che potresti autorizzarlo manualmente con la tua funzione usando mapply che forse usa un valore predefinito per l’argomento di envir . Ad esempio, Vectorize restituirà una funzione con le stesse proprietà di ambiente di assign , che in questo caso è namespace:base , oppure puoi semplicemente impostare envir = parent.env(environment(assignVec)) .

Ho messo insieme un pacchetto Z per risolvere questo problema. zeallot include un operatore ( %<-% ) per il disimballaggio, l'assegnazione multipla e destrutturante. Il LHS dell'espressione di assegnazione viene creato utilizzando le chiamate a c() . L'RHS dell'espressione di assegnazione può essere qualsiasi espressione che restituisce o è un vettore, un elenco, un elenco annidato, un frame di dati, una stringa di caratteri, un object data o oggetti personalizzati (supponendo che esista un'implementazione di destructure ).

Ecco la domanda iniziale rielaborata usando zeallot (ultima versione, 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 

Per ulteriori esempi e informazioni è ansible controllare la vignetta del pacchetto.

Se il tuo unico requisito è avere una singola riga di codice, allora che ne dici:

 > a<-values[2]; b<-values[4] 

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 ) 

Ho avuto un problema simile di recente e qui ho provato usando purrr::walk2

 purrr::walk2(letters,1:26,assign,envir =parent.frame()) 

Ho paura che la soluzione elegante che stai cercando (come c(a, b) = c(2, 4) ) sfortunatamente non esiste. Ma non arrenderti, non ne sono sicuro! La soluzione più vicina a cui riesco a pensare è questa:

 attach(data.frame(a = 2, b = 4)) 

oppure se sei infastidito dagli avvertimenti, distriggersli:

 attach(data.frame(a = 2, b = 4), warn = F) 

Ma suppongo che tu non sia soddisfatto di questa soluzione, non sarei neanche …

 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 

Un’altra versione con ricorsione:

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

esempio:

 > 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 

La mia versione:

 let <- function(x, value) { mapply( assign, as.character(substitute(x)[-1]), value, MoreArgs = list(envir = parent.frame())) invisible() } 

esempio:

 > let(c(x, y), 1:2 + 3) > x [1] 4 > y [1] 
 list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv) 

Ha servito il mio scopo, vale a dire, assegnando cinque 2 alle prime cinque lettere.