Je souhaite affecter plusieurs variables sur une seule ligne dans R. Est-il possible de faire quelque chose comme ça?
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'
En général, je souhaite attribuer environ 5 à 6 variables sur une seule ligne, au lieu d'avoir plusieurs lignes. Y a-t-il une alternative?
list($a, $b) = array(1, 2)
? Ce serait bien! +1.vassign
suggestion ci-dessous se rapproche ... :)X <- list();X[c('a','b')] <- values[c(2,4)]
. OK, vous ne les attribuez pas dans l'espace de travail, mais conservez-les bien ensemble dans une liste. Je préfère le faire de cette façon.Réponses:
Il y a une excellente réponse sur le blog Lutte contre les problèmes
Ceci est pris à partir de là, avec des modifications très mineures.
UTILISATION DES TROIS FONCTIONS SUIVANTES (Plus une pour permettre des listes de tailles différentes)
# 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) }
Puis pour exécuter:
Regroupez le côté gauche à l'aide de la nouvelle fonction
g()
Le côté droit doit être un vecteur ou une liste Utilisez l'opérateur binaire nouvellement créé%=%
# 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
Exemple utilisant des listes de différentes tailles:
Côté gauche plus long
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"
Côté droit plus long
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"
la source
J'ai créé un zeallot de package R pour résoudre ce problème. zeallot inclut un opérateur (
%<-%
) pour les affectations de déballage, de multiple et de déstructuration. La LHS de l'expression d'affectation est créée à l'aide d'appels àc()
. Le RHS de l'expression d'affectation peut être toute expression qui renvoie ou est un vecteur, une liste, une liste imbriquée, un bloc de données, une chaîne de caractères, un objet de date ou des objets personnalisés (en supposant qu'il existe unedestructure
implémentation).Voici la question initiale retravaillée avec zeallot (dernière version, 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
Pour plus d'exemples et d'informations, vous pouvez consulter la vignette du package .
la source
Pensez à utiliser les fonctionnalités incluses dans la base R.
Par exemple, créez un dataframe à 1 ligne (disons
V
) et initialisez vos variables dedans. Vous pouvez désormais affecter plusieurs variables à la foisV[,c("a", "b")] <- values[c(2, 4)]
, les appeler par leur nom (V$a
) ou en utiliser plusieurs en même temps (values[c(5, 6)] <- V[,c("a", "b")]
).Si vous devenez paresseux et que vous ne voulez pas appeler des variables à partir du dataframe, vous pouvez
attach(V)
(bien que personnellement je ne le fasse jamais).# 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
la source
voici mon idée. La syntaxe est probablement assez 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)
donne comme ça:
> 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
ce n'est pas bien testé cependant.
la source
c(c, d) %tin% c(1, 2) + 3
(=> c = 1, d = 1, renvoie numérique ( 0)) peut être considéré comme surprenant.Une
assign
option potentiellement dangereuse (dans la mesure où l'utilisation est risquée) serait deVectorize
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 je suppose que vous pouvez le vectoriser vous-même manuellement avec votre propre fonction en utilisant
mapply
peut-être une valeur par défaut raisonnable pour l'envir
argument. Par exemple,Vectorize
retournera une fonction avec les mêmes propriétés d'environnement queassign
, ce qui dans ce cas estnamespace:base
, ou vous pouvez simplement définirenvir = parent.env(environment(assignVec))
.la source
Comme d'autres l'ont expliqué, il ne semble y avoir rien de intégré. ... mais vous pouvez concevoir une
vassign
fonction comme suit: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
Une chose à considérer cependant est comment gérer les cas où vous spécifiez par exemple 3 variables et 5 valeurs ou l'inverse. Ici, je répète (ou tronque) simplement les valeurs pour qu'elles soient de la même longueur que les variables. Peut-être qu'un avertissement serait prudent. Mais cela permet ce qui suit:
vassign(aa,bb,cc,dd, values=0) cc # 0
la source
...()
, ce qui ressemble à de la magie noire pour moi ...?...()
c'est de la magie noire extrême ;-). Il se trouve que lorsque "l'appel de fonction"...()
est remplacé, il devient une liste de paires qui peut être transmiseas.character
et le tour est joué, vous avez les arguments sous forme de chaînes ...envir=parent.frame()
- Et vous pouvez spécifier par exempleenvir=globalenv()
si vous le souhaitez.`vassign<-` <- function (..., envir = parent.frame (), value)
et ainsi de suite. Cependant, il semble que le premier objet à attribuer devrait déjà exister. Des idées?list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)
A servi mon objectif, c'est-à-dire attribuer cinq 2 en cinq premières lettres.
la source
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 )
la source
J'ai eu un problème similaire récemment et voici mon essai d'utilisation
purrr::walk2
purrr::walk2(letters,1:26,assign,envir =parent.frame())
la source
Si votre seule exigence est d'avoir une seule ligne de code, que diriez-vous:
> a<-values[2]; b<-values[4]
la source
J'ai peur que la solution élégante que vous recherchez (comme
c(a, b) = c(2, 4)
) n'existe malheureusement pas. Mais n'abandonnez pas, je ne suis pas sûr! La solution la plus proche à laquelle je puisse penser est celle-ci:attach(data.frame(a = 2, b = 4))
ou si les avertissements vous dérangent, désactivez-les:
attach(data.frame(a = 2, b = 4), warn = F)
Mais je suppose que vous n'êtes pas satisfait de cette solution, je ne le serais pas non plus ...
la source
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
la source
Une autre version avec récursivité:
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(...) }
exemple:
> 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
Ma version:
let <- function(x, value) { mapply( assign, as.character(substitute(x)[-1]), value, MoreArgs = list(envir = parent.frame())) invisible() }
exemple:
> let(c(x, y), 1:2 + 3) > x [1] 4 > y [1]
la source
En combinant certaines des réponses données ici + un peu de sel, que diriez-vous de cette solution:
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
J'ai utilisé ceci pour ajouter la section R ici: http://rosettacode.org/wiki/Sort_three_variables#R
Attention: cela ne fonctionne que pour assigner des variables globales (comme
<<-
). S'il y a une meilleure solution plus générale, pls. Dites-moi dans les commentaires.la source