Je veux 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 veux affecter environ 5-6 variables sur une seule ligne, au lieu d'avoir plusieurs lignes. Y a-t-il une alternative?
Il y a une excellente réponse sur le Blog de 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)
}
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
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"
Par exemple, créez un cadre de données à 1 ligne (disons V
) et initialisez vos variables dedans. Vous pouvez maintenant affecter plusieurs variables à la fois V[,c("a", "b")] <- values[c(2, 4)]
, appeler chacune par son nom (V$a
), Ou utiliser plusieurs d'entre elles 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 de la trame de données, 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
J'ai mis en place un package R zeallot pour résoudre ce problème même. zeallot inclut un opérateur (%<-%
) pour le déballage, l'affectation multiple et la déstructuration. Le LHS de l'expression d'affectation est construit à 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 un destructure
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, on peut consulter le package vignette .
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.
Une option potentiellement dangereuse (dans la mesure où l'utilisation de assign
est risquée) serait de Vectorize
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
qui utilise peut-être une valeur par défaut raisonnable pour l'argument envir
. Par exemple, Vectorize
renverra une fonction avec les mêmes propriétés d'environnement que assign
, qui dans ce cas est namespace:base
, Ou vous pouvez simplement définir envir = parent.env(environment(assignVec))
.
Comme d'autres l'ont expliqué, il ne semble rien y avoir de construit. ... mais vous pouvez concevoir une fonction vassign
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 de savoir comment gérer les cas où vous par exemple spécifiez 3 variables et 5 valeurs ou l'inverse. Ici, je répète (ou tronque) simplement les valeurs 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
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
)
list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)
A servi mon but, c'est-à-dire assigner cinq 2 aux cinq premières lettres.
J'ai eu un problème similaire récemment et voici mon essai en utilisant purrr::walk2
purrr::walk2(letters,1:26,assign,envir =parent.frame())
Si votre seule exigence est d'avoir une seule ligne de code, que diriez-vous:
> a<-values[2]; b<-values[4]
J'ai bien 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 peux penser est celle-ci:
attach(data.frame(a = 2, b = 4))
ou si vous êtes gêné par des avertissements, 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 ...
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
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]
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é cela pour ajouter la section R ici: http://rosettacode.org/wiki/Sort_three_variables#R
Avertissement: cela ne fonctionne que pour l'attribution de variables globales (comme <<-
). S'il existe une meilleure solution, plus générale, veuillez. Dites-moi dans les commentaires.