Я хочу назначить несколько переменных в одной строке в R. Можно ли сделать что-то подобное?
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'
Обычно я хочу назначить около 5-6 переменных в одной строке вместо нескольких строк. Есть ли альтернатива?
list($a, $b) = array(1, 2)
? Это было бы чудесно! +1.vassign
предложение ниже подходит ... :)X <- list();X[c('a','b')] <- values[c(2,4)]
. Хорошо, вы не назначаете их в рабочей области, а храните их вместе в списке. Я бы предпочел сделать это так.Ответы:
Есть отличный ответ в блоге о борьбе с проблемами
Это взято оттуда с очень небольшими изменениями.
ИСПОЛЬЗОВАНИЕ ТРЕХ СЛЕДУЮЩИХ ФУНКЦИЙ (плюс одна для создания списков разного размера)
# 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) }
Затем выполнить:
Сгруппируйте левую часть с помощью новой функции
g()
. Правая часть должна быть вектором или списком. Используйте вновь созданный бинарный оператор.%=%
# 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
Пример использования списков разного размера:
Более длинная левая сторона
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"
Более длинная правая сторона
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"
источник
Я собрал zeallot пакета R для решения этой самой проблемы. zeallot включает оператор (
%<-%
) для распаковки, множественного и деструктурирования присваивания. LHS выражения присваивания строится с использованием вызововc()
. Правая часть выражения присваивания может быть любым выражением, которое возвращает или является вектором, списком, вложенным списком, фреймом данных, символьной строкой, объектом даты или настраиваемыми объектами (при условии, что существуетdestructure
реализация).Вот начальный вопрос, переработанный с помощью zeallot (последняя версия, 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
Дополнительные примеры и информацию можно найти на виньетке на упаковке .
источник
Рассмотрите возможность использования функций, включенных в базовый R.
Например, создайте фрейм данных с одной строкой (скажем
V
) и инициализируйте в нем свои переменные. Теперь вы можете назначать сразу несколько переменныхV[,c("a", "b")] <- values[c(2, 4)]
, вызывать каждую по имени (V$a
) или использовать многие из них одновременно (values[c(5, 6)] <- V[,c("a", "b")]
).Если вам лень и вы не хотите обходиться с вызовами переменных из фрейма данных, вы можете
attach(V)
(хотя я лично никогда этого не делаю).# 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
источник
вот моя идея. Наверное, синтаксис довольно простой:
`%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)
дает вот так:
> 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
Однако это не очень хорошо проверено.
источник
c(c, d) %tin% c(1, 2) + 3
(=> c = 1, d = 1, возвращает числовое ( 0)) можно считать удивительным.Потенциально опасным (в той мере, в какой использование
assign
рискованно) вариантом будет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
Или я полагаю, вы могли бы векторизовать его вручную с помощью своей собственной функции, используя
mapply
, возможно, разумное значение по умолчанию дляenvir
аргумента. Например,Vectorize
вернет функцию с такими же свойствами средыassign
, как в данном случаеnamespace:base
, или вы можете просто установитьenvir = parent.env(environment(assignVec))
.источник
Как объясняли другие, похоже, что здесь ничего не встроено ... но вы можете создать
vassign
функцию следующим образом: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
Однако следует учитывать, как обрабатывать случаи, когда вы, например, указываете 3 переменные и 5 значений или наоборот. Здесь я просто повторяю (или обрезаю) значения, чтобы они были той же длины, что и переменные. Может быть, предупреждение было бы разумным. Но это позволяет следующее:
vassign(aa,bb,cc,dd, values=0) cc # 0
источник
...()
, что мне кажется черной магией ...?...()
это экстремальная черная магия ;-). Бывает так, что когда "вызов функции"...()
заменяется, он становится паирлистом, который можно передать,as.character
и вуаля, вы получили аргументы в виде строк ...envir=parent.frame()
- И вы можете указать, например,envir=globalenv()
если хотите.`vassign<-` <- function (..., envir = parent.frame (), value)
и так далее. Однако кажется, что первый назначаемый объект должен уже существовать. Любые идеи?list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)
Это послужило моей цели, т. Е. Присвоение пяти двоек первым пяти буквам.
источник
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 )
источник
Недавно была аналогичная проблема, и вот я попытался использовать
purrr::walk2
purrr::walk2(letters,1:26,assign,envir =parent.frame())
источник
Если ваше единственное требование - иметь одну строку кода, как насчет:
> a<-values[2]; b<-values[4]
источник
Боюсь, что того элегантного решения, которое вы ищете (вроде
c(a, b) = c(2, 4)
), к сожалению, не существует. Но не сдавайтесь, я не уверен! Ближайшее решение, которое я могу придумать, таково:attach(data.frame(a = 2, b = 4))
или, если вас беспокоят предупреждения, отключите их:
attach(data.frame(a = 2, b = 4), warn = F)
Но я полагаю, вас не устраивает это решение, я бы тоже не стал ...
источник
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
источник
Другая версия с рекурсией:
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(...) }
пример:
> 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
Моя версия:
let <- function(x, value) { mapply( assign, as.character(substitute(x)[-1]), value, MoreArgs = list(envir = parent.frame())) invisible() }
пример:
> let(c(x, y), 1:2 + 3) > x [1] 4 > y [1]
источник
Комбинируя некоторые из приведенных здесь ответов + немного соли, как насчет этого решения:
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
Я использовал это, чтобы добавить сюда раздел R: http://rosettacode.org/wiki/Sort_three_variables#R
Предостережение: он работает только для назначения глобальных переменных (например,
<<-
). Если есть лучшее, более общее решение, пожалуйста. Ответь мне в комментариях.источник