LHSに複数の新しい変数を1行で割り当てます


89

Rの1行に複数の変数を割り当てたいのですが、このようなことはできますか?

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'

通常、複数行ではなく、1行に約5〜6個の変数を割り当てたいと思います。代替手段はありますか?


PHPのような意味list($a, $b) = array(1, 2)ですか?それはいいね!+1。
TMS

@ TomasT-vassign以下の私の提案は近いと思います... :)
Tommy

注:セミコロンはR.のこのビットのために必要されていません
イテレータ

1
適切な環境でこれを試してみると、それはと同じくらい簡単X <- list();X[c('a','b')] <- values[c(2,4)]です。OK、ワークスペースでそれらを割り当てませんが、リストにそれらをうまくまとめてください。私はそれをそのようにしたいです。
Joris Meys 2013

7
私はPythonが好きで、a、b = 1,2だけです。以下のすべての答えは100倍難しい
appleLover 2013

回答:


39

Struggling ThroughProblemsブログに素晴らしい答えがあります

これは、非常に小さな変更を加えて、そこから取得されます。

次の3つの機能の使用(さらに、さまざまなサイズのリストを可能にするための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"

34

私はこの問題に取り組むために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

その他の例と情報については、パッケージビネットを確認してください。


これはまさに私が見つけたいと思っていたものであり、OPが求めていたPythonのような構文を可能にし、Rパッケージに実装されています。
jafeldsは

1
各変数名に行列を割り当てるのはどうですか?
statsSorceress

33

ベース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

4
できれば+10。なぜ人々はそのような明白なケースでリストの使用を拒否し、むしろ無意味な変数のトンでワークスペースを散らかすのだろうかと思います。(data.frameは特別な種類のリストであるため、リストを使用します。より一般的なリストを使用します。)
Joris Meys 2013

しかし、あなたは、同じ列の要素の異なる種類を持っていないことができ、また、あなたのデータフレーム内のデータフレームやリストを保存することができます
SKAN

1
実際には、リストをデータフレーム(グーグルの「リスト列」)に保存できます。

これは悪いアプローチではなく、いくつかの便利さもありますが、多くのユーザーがこの方法で割り当てられた変数を使用またはアクセスしようとするたびにdata.frame構文を処理する必要がない理由を想像するのもそれほど難しくありません。
ブランドン

13

これが私の考えです。おそらく構文は非常に単純です。

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

ただし、これは十分にテストされていません。


2
Koshke、私にはとても素敵に見えます:-)しかし、演算子の優先順位について少し心配しています:%something%演算子はかなり高いので、eg c(c, d) %tin% c(1, 2) + 3(=> c = 1、d = 1の動作は数値(=> c = 1、d = 1)を返します0))意外と思われるかもしれません。
–cbeleitesはSXに不満を持っています2011

10

潜在的に危険な(使用するの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

またはmapplyenvir引数に適切なデフォルトを使用する関数を使用して、手動でベクトル化できると思います。たとえば、はとVectorize同じ環境プロパティを持つ関数を返します。assignこの場合はですnamespace:base。または、を設定することもできますenvir = parent.env(environment(assignVec))


8

他の人が説明したように、何も組み込まれていないようです。...しかし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

ただし、考慮すべきことの1つは、たとえば3つの変数と5つの値を指定する場合、またはその逆の場合の処理​​方法です。ここでは、変数と同じ長さになるように値を繰り返す(または切り捨てる)だけです。たぶん警告は賢明でしょう。しかし、それは以下を可能にします:

vassign(aa,bb,cc,dd, values=0)
cc # 0

私はこれが好きですが、関数内から呼び出された場合に壊れるのではないかと心配しています(これの簡単なテストは機能しましたが、少し驚いたことに)。...()私には黒魔術のように見える、説明してもらえますか...?
ベンボルカー2011

1
@Ben Bolker-はい、...()極端な黒魔術です;-)。「関数呼び出し」...()が置き換えられると、それが渡されるペアリストになり、出来上がりas.character、引数を文字列として取得することがあります...
Tommy

1
@Ben Bolker-を使用しているため、関数内から呼び出された場合でも正しく機能するはずです-必要に応じてenvir=parent.frame()指定できますenvir=globalenv()
トミー

さらにクールなのは、これを交換機能として持つこと`vassign<-` <- function (..., envir = parent.frame (), value)です。ただし、割り当てられる最初のオブジェクトはすでに存在している必要があるようです。何か案は?
–cbeleitesはSXに不満2011

@ cbeleites-はい、それはもっとかっこいいでしょうが、最初の引数が存在しなければならないという制限を回避できるとは思いません-それが置換関数と呼ばれる理由です:) ...しかし、そうでないことがわかったら教えてください!!
トミー

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

私の目的を果たしました。つまり、最初の5文字に5つの2を割り当てました。



4

最近同様の問題が発生しました。これを使用してみました。 purrr::walk2

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

3

唯一の要件が1行のコードである場合は、次のようにします。

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

2
簡潔なステートメントを探していましたが、何もないと思います
user236215 2011

私は@ user236215と同じ船に乗っています。右側がベクトルを返す複雑な式である場合、コードの繰り返しは非常に間違っているように見えます...
空爆2016年

1

c(a, b) = c(2, 4)残念ながら、あなたが探している(のような)緊急の解決策は存在しないのではないかと思います。しかし、あきらめないでください、私にはわかりません!私が考えることができる最も近い解決策はこれです:

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

または、警告が気になる場合は、警告をオフにしてください。

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

しかし、私はあなたがこの解決策に満足していないと思います、私もそうではないでしょう...


1
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

0

再帰を伴う別のバージョン:

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] 

0

ここで与えられた答えのいくつかと少しの塩を組み合わせて、この解決策はどうですか?

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

警告:グローバル変数(など<<-)を割り当てる場合にのみ機能します。より良い、より一般的な解決策がある場合は、pls。コメントで教えてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.