Назначить несколько новых переменных в LHS в одной строке - PullRequest
76 голосов
/ 22 сентября 2011

Я хочу назначить несколько переменных в одной строке в 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 переменных в одной строкеиметь несколько строк.Есть ли альтернатива?

Ответы [ 14 ]

30 голосов
/ 13 ноября 2012

В блоге Борьба с проблемами есть отличный ответ

Это взято оттуда, с очень незначительными изменениями.

ИСПОЛЬЗОВАНИЕ СЛЕДУЮЩИХ ТРИ ФУНКЦИЙ (Плюс один для учета списков разных размеров)

# 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"
28 голосов
/ 25 февраля 2013

Рассмотрите возможность использования функциональности, включенной в базу R.

Например, создайте 1-рядный фрейм данных (скажем, 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
23 голосов
/ 26 июля 2017

Я собрал пакет R фанат , чтобы решить эту проблему.Zeallot включает в себя оператор (%<-%) для распаковки, множественного и деструктурирующего присваивания.LHS выражения присваивания строится с использованием вызовов c().RHS выражения присваивания может быть любым выражением, которое возвращает или является вектором, списком, вложенным списком, фреймом данных, строкой символов, объектом даты или пользовательскими объектами (при условии реализации 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

Дополнительные примеры и информацию можно найти в пакете виньетка .

13 голосов
/ 23 сентября 2011

вот моя идея.Вероятно, синтаксис довольно прост:

`%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

хотя это не очень хорошо проверено.

10 голосов
/ 22 сентября 2011

Потенциально опасный (поскольку использование 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)).

8 голосов
/ 23 сентября 2011

Как объяснили другие, кажется, что ничего встроенного в ... нет, но вы могли бы спроектировать функцию 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
4 голосов
/ 28 марта 2018

Недавно у меня была похожая проблема, и вот моя попытка использования purrr::walk2

purrr::walk2(letters,1:26,assign,envir =parent.frame()) 
4 голосов
/ 17 сентября 2015

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
        )
4 голосов
/ 22 сентября 2011

Если ваше единственное требование - иметь одну строку кода, то как насчет:

> a<-values[2]; b<-values[4]
3 голосов
/ 17 апреля 2016
list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)

Служил моей цели, т. Е. Назначая пять 2-х на первые пять букв.

...