Подтвердить что ты не робот

Назначить несколько новых переменных на LHS в одной строке

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

4b9b3361

Ответ 1

Существует отличный ответ на "Борьба через блог проблем"

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

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

# 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"

Ответ 2

Рассмотрите возможность использования функциональных возможностей, включенных в базу 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

Ответ 3

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

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

это не очень хорошо протестировано.

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

Ответ 8

Я собрал пакет R zeallot, чтобы решить эту проблему. 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

Для получения дополнительных примеров и информации можно проверить пакет vignette.

Ответ 9

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

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

Ответ 10

Я боюсь, что элегантного решения, которое вы ищете (например, c(a, b) = c(2, 4)), к сожалению, не существует. Но не сдавайтесь, я не уверен! Ближайшее решение, о котором я могу думать, это следующее:

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

или если вас беспокоят предупреждения, отключите их:

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

Но я полагаю, вы не удовлетворены этим решением, я бы тоже не был...

Ответ 11

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

Ответ 12

list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)

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

Ответ 13

Другая версия с рекурсией:

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] 

Ответ 14

Вы хотите сказать, что хотите повторно назначить элементы в векторе? Если это так, то это просто:

values[c(2,4)] <- c(a, b)