在一行中为 LHS 分配多个新变量

发布于 2024-12-06 12:52:30 字数 231 浏览 1 评论 0 原文

我想在 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 个变量,而不是多行。还有其他选择吗?

I want to assign multiple variables in a single line in R. Is it possible to do something like this?

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'

Typically I want to assign about 5-6 variables in a single line, instead of having multiple lines. Is there an alternative?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

泪之魂 2024-12-13 12:52:30

我整理了一个 R 包 zeallot 来解决这个问题。 zeallot 包含一个用于解包、多重和解构赋值的运算符 (%<-%)。赋值表达式的 LHS 是通过调用 c() 构建的。分配表达式的RHS可以是任何返回或返回或为向量,列表,嵌套列表,数据框架,字符串,日期对象或自定义对象的表达式(假设有destruct 实现)。

这是使用 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

I put together an R package zeallot to tackle this very problem. zeallot includes an operator (%<-%) for unpacking, multiple, and destructuring assignment. The LHS of the assignment expression is built using calls to c(). The RHS of the assignment expression may be any expression which returns or is a vector, list, nested list, data frame, character string, date object, or custom objects (assuming there is a destructure implementation).

Here is the initial question reworked using zeallot (latest 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

For more examples and information one can check out the package vignette.

九局 2024-12-13 12:52:30

关于挣扎有一个很好的答案通过问题博客

这是从那里获取的,做了很小的修改。

使用以下三个功能
(加一以允许不同大小的列表)

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

There is a great answer on the Struggling Through Problems Blog

This is taken from there, with very minor modifications.

USING THE FOLLOWING THREE FUNCTIONS
(Plus one for allowing for lists of different sizes)

# 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)
}


Then to execute:

Group the left hand side using the new function g()
The right hand side should be a vector or a list
Use the newly-created binary operator %=%

# 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


Example using lists of different sizes:

Longer Left Hand Side

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"

Longer Right Hand Side

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"
呆° 2024-12-13 12:52:30

考虑使用基础 R 中包含的功能。

例如,创建一个 1 行数据框(例如 V)并初始化其中的变量。现在您可以一次分配给多个变量V[,c("a", "b")] <-values[c(2, 4)],按名称调用每个变量 (< code>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

Consider using functionality included in base R.

For instance, create a 1 row dataframe (say V) and initialize your variables in it. Now you can assign to multiple variables at once V[,c("a", "b")] <- values[c(2, 4)], call each one by name (V$a), or use many of them at the same time (values[c(5, 6)] <- V[,c("a", "b")]).

If you get lazy and don't want to go around calling variables from the dataframe, you could attach(V) (though I personally don't ever do it).

# 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
玩心态 2024-12-13 12:52:30

这是我的想法。语法可能非常简单:

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

但这还没有经过充分测试。

here is my idea. Probably the syntax is quite 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)

gives like this:

> 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

this is not well tested though.

一梦浮鱼 2024-12-13 12:52:30

一个潜在的危险(与使用分配一样有风险)选项是<代码>矢量化<代码>分配

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

或者我想你可以自己手动对其进行矢量化使用您自己的函数使用maply,它可能为envir参数使用合理的默认值。例如,Vectorize 将返回一个与 assign 具有相同环境属性的函数,在本例中为 namespace:base,或者您可以只设置envir =parent.env(environment(assignVec))

A potentially dangerous (in as much as using assign is risky) option would be to 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

Or I suppose you could vectorize it yourself manually with your own function using mapply that maybe uses a sensible default for the envir argument. For instance, Vectorize will return a function with the same environment properties of assign, which in this case is namespace:base, or you could just set envir = parent.env(environment(assignVec)).

惜醉颜 2024-12-13 12:52:30
list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)

达到了我的目的,即将五个 2 分配给前五个字母。

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

Served my purpose, i.e., assigning five 2s into first five letters.

遗心遗梦遗幸福 2024-12-13 12:52:30

正如其他人所解释的,似乎没有任何内置内容。...但是您可以设计一个 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 others explained, there doesn't seem to be anything built in. ...but you could design a vassign function as follows:

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

One thing to consider though is how to handle the cases where you e.g. specify 3 variables and 5 values or the other way around. Here I simply repeat (or truncate) the values to be of the same length as the variables. Maybe a warning would be prudent. But it allows the following:

vassign(aa,bb,cc,dd, values=0)
cc # 0
攀登最高峰 2024-12-13 12:52:30

最近遇到了类似的问题,这是我使用 purrr::walk2 的尝试

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

Had a similar problem recently and here was my try using purrr::walk2

purrr::walk2(letters,1:26,assign,envir =parent.frame()) 
淑女气质 2024-12-13 12:52:30

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
        )

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
        )
懵少女 2024-12-13 12:52:30

如果您唯一的要求是一行代码,那么:

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

If your only requirement is to have a single line of code, then how about:

> a<-values[2]; b<-values[4]
小…楫夜泊 2024-12-13 12:52:30

对于命名列表,使用

list2env(mylist, environment())

For example:

mylist <- list(foo = 1, bar = 2)
list2env(mylist, environment())

会将 foo = 1, bar = 2 添加到当前环境,并覆盖具有这些名称的任何对象。这相当于

mylist <- list(foo = 1, bar = 2)
foo <- mylist$foo
bar <- mylist$bar

This 也适用于函数:

f <- function(mylist) {
  list2env(mylist, environment())
  foo * bar
}
mylist <- list(foo = 1, bar = 2) 
f(mylist)

但是,最好对要包含在当前环境中的元素进行命名,以免覆盖另一个对象...所以最好这样写

list2env(mylist[c("foo", "bar")], environment())

最后,如果您想要不同的名称对于新导入的对象,写:

list2env(`names<-`(mylist[c"foo", "bar"]), c("foo2", "bar2")), environment())

相当于

foo2 <- mylist$foo
bar2 <- mylist$bar

For a named list, use

list2env(mylist, environment())

For instance:

mylist <- list(foo = 1, bar = 2)
list2env(mylist, environment())

will add foo = 1, bar = 2 to the current environement, and override any object with those names. This is equivalent to

mylist <- list(foo = 1, bar = 2)
foo <- mylist$foo
bar <- mylist$bar

This works in a function, too:

f <- function(mylist) {
  list2env(mylist, environment())
  foo * bar
}
mylist <- list(foo = 1, bar = 2) 
f(mylist)

However, it is good practice to name the elements you want to include in the current environment, lest you override another object... and so write preferrably

list2env(mylist[c("foo", "bar")], environment())

Finally, if you want different names for the new imported objects, write:

list2env(`names<-`(mylist[c"foo", "bar"]), c("foo2", "bar2")), environment())

which is equivalent to

foo2 <- mylist$foo
bar2 <- mylist$bar
清风挽心 2024-12-13 12:52:30

不幸的是,恐怕您正在寻找的优雅解决方案(例如 c(a, b) = c(2, 4))并不存在。 但不要放弃,我不确定!我能想到的最接近的解决方案是这个:

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

或者如果您对警告感到烦恼,请将其关闭:

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

但我想您不是对这个解决方案感到满意,我也不会......

I'm afraid that elegent solution you are looking for (like c(a, b) = c(2, 4)) unfortunatelly does not exist. But don't give up, I'm not sure! The nearest solution I can think of is this one:

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

or if you are bothered with warnings, switch them off:

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

But I suppose you're not satisfied with this solution, I wouldn't be either...

挽容 2024-12-13 12:52:30
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
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
难如初 2024-12-13 12:52:30

结合这里给出的一些答案+一点点盐,这个解决方案怎么样:

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_third_variables#R

警告:它仅适用于分配全局变量(例如<代码><<-)。如果有更好、更通用的解决方案,请。在评论中告诉我。

Combining some of the answers given here + a little bit of salt, how about this 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

I used this to add the R section here: http://rosettacode.org/wiki/Sort_three_variables#R

Caveat: It only works for assigning global variables (like <<-). If there is a better, more general solution, pls. tell me in the comments.

假装不在乎 2024-12-13 12:52:30

另一个带有递归的版本:

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] 

Another version with recursion:

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(...)
}

example:

> 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

My version:

let <- function(x, value) {
    mapply(
        assign,
        as.character(substitute(x)[-1]),
        value,
        MoreArgs = list(envir = parent.frame()))
    invisible()
}

example:

> let(c(x, y), 1:2 + 3)
> x
[1] 4
> y
[1] 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文