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

Posted

技术标签:

【中文标题】在一行中在 LHS 上分配多个新变量【英文标题】:Assign multiple new variables on LHS in a single line 【发布时间】:2011-11-23 02:28:57 【问题描述】:

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

【问题讨论】:

你的意思是像 php list($a, $b) = array(1, 2)?那样就好了! +1。 @Tomas T - 我认为我下面的vassign 建议很接近... :) 注意:这部分 R 不需要分号。 如果您想在适当的环境中尝试此操作,就像X <- list();X[c('a','b')] <- values[c(2,4)] 一样简单。好的,您不要在工作区中分配它们,而是将它们很好地放在一个列表中。我宁愿那样做。 我喜欢python,只是a,b = 1,2。下面的所有答案都难 100 倍 【参考方案1】:

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

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

【讨论】:

正在寻找一个简洁的陈述,但我想没有 我和@user236215 在同一条船上。当右侧是返回向量的复杂表达式时,重复代码似乎非常错误......【参考方案2】:

一个潜在的危险(尽管使用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))

【讨论】:

【参考方案3】:

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

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

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

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

但我想你对这个解决方案不满意,我也不会......

【讨论】:

【参考方案4】:

正如其他人解释的那样,似乎没有内置任何东西。...但是您可以设计一个 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

【讨论】:

我喜欢这个,但我担心它可能会在某些情况下从函数中调用它(尽管对此的简单测试有效,令我有点惊讶)。你能解释一下...(),这对我来说就像黑魔法......? @Ben Bolker - 是的,...() 是极端的黑魔法 ;-)。碰巧当“函数调用”...() 被替换时,它变成了一个可以传递给as.character 的对列表,瞧,你得到了字符串形式的参数...... @Ben Bolker - 即使从函数中调用它也应该可以正常工作,因为它使用envir=parent.frame() - 你可以指定例如envir=globalenv() 如果你愿意的话。 更酷的也会有这个作为替换功能:`vassign&lt;-` &lt;- function (..., envir = parent.frame (), value) 等等。但是,似乎要分配的第一个对象需要已经存在。有什么想法吗? @cbeleites - 是的,那会更酷,但我认为你不能解决第一个参数必须存在的限制 - 这就是为什么它被称为替换函数:) ...但是如果您发现其他情况,请告诉我!【参考方案5】:

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

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

虽然这没有经过很好的测试。

【讨论】:

Koshke,对我来说看起来很不错 :-) 但我有点担心运算符优先级:%something% 运算符非常高,所以例如的行为c(c, d) %tin% c(1, 2) + 3 (=> c = 1, d = 1, 返回数字 (0)) 可能被认为令人惊讶。【参考方案6】:

Struggling Through Problems Blog上有一个很好的答案

这是取自那里,做了非常小的修改。

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

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

【讨论】:

【参考方案7】:

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

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

【讨论】:

如果可以的话,+10。我想知道为什么人们在如此明显的情况下拒绝使用列表,而是在工作空间中乱扔大量无意义的变量。 (您确实使用了列表,因为 data.frame 是一种特殊的列表。我只会使用更通用的列表。) 但您不能在同一列中包含不同类型的元素,也不能在数据框中存储数据框或列表 其实你可以将列表存储在一个数据框中——google“list column”。 这不是一个糟糕的方法,它有一些便利,但也不难想象为什么许多用户不想在每次尝试使用时都处理 data.frame 语法或访问以这种方式分配的变量。【参考方案8】:
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

【讨论】:

【参考方案9】:

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
        )

【讨论】:

【参考方案10】:
list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)

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

【讨论】:

【参考方案11】:

另一个带有递归的版本:

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] 

【讨论】:

【参考方案12】:

我整理了一个 R 包zeallot 来解决这个问题。 zeallot 包含一个运算符 (%&lt;-%),用于解包、多重和解构赋值。赋值表达式的 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。

【讨论】:

这正是我希望找到的,它支持 OP 要求的类似 python 的语法,在 R 包中实现 给每个变量名分配一个矩阵怎么样?【参考方案13】:

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

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

【讨论】:

【参考方案14】:

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

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

警告:它仅适用于分配全局变量(如&lt;&lt;-)。如果有更好,更通用的解决方案,请。在 cmets 中告诉我。

【讨论】:

以上是关于在一行中在 LHS 上分配多个新变量的主要内容,如果未能解决你的问题,请参考以下文章

为啥在同一行上分配变量后shell内置冒号命令“:”会导致分配空字符串值?

RHS 和 LHS

优化分配的模板技巧

将当前时间分配给powershell变量

Colab 提供 OOM 用于在 tensorflow 中在 GPU 上分配 4.29 GB 张量

将分类值分配给 r 中的多个变量