Tek bir satırda LHS'ye birden çok yeni değişken atayın


90

R'de tek bir satıra birden çok değişken atamak istiyorum. Böyle bir şey yapmak mümkün mü?

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'

Genellikle, birden çok satıra sahip olmak yerine, tek bir satıra yaklaşık 5-6 değişken atamak istiyorum. Bir alternatif var mı?


PHP'deki gibi bir şey mi demek istiyorsun list($a, $b) = array(1, 2)? İyi olur! +1.
TMS

@Tomas T - Sanırım vassignaşağıdaki önerim yaklaşıyor ... :)
Tommy

Not: Bu R biti için noktalı virgül gerekmez
Yineleyici

1
Bunu uygun bir ortamda denerseniz, bu kadar kolay olacaktır X <- list();X[c('a','b')] <- values[c(2,4)]. Tamam, onları çalışma alanına atamıyorsun, ama bir listede güzelce bir arada tut. Bu şekilde yapmayı tercih ederim.
Joris Meys

7
python'u seviyorum, sadece a, b = 1,2. aşağıdaki tüm cevaplar 100 kat daha zor
appleLover

Yanıtlar:


40

Problemlerle Mücadele Blogunda harika bir cevap var

Bu, çok küçük değişikliklerle oradan alınır.

AŞAĞIDAKİ ÜÇ İŞLEVLERİN KULLANILMASI (Artı bir, farklı boyutlarda listelere izin vermek için)

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


Ardından yürütmek için:

Yeni işlevi kullanarak sol tarafı gruplayın g() Sağ taraf bir vektör veya liste olmalıdır Yeni oluşturulan ikili operatörü kullanın%=%

# 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


Farklı boyutlardaki listeleri kullanan örnek:

Daha Uzun Sol Taraf

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"

Daha Uzun Sağ Taraf

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 tabanında bulunan işlevselliği kullanmayı düşünün.

Örneğin, 1 satırlık bir veri çerçevesi oluşturun (örneğin V) ve içindeki değişkenlerinizi başlatın. Artık aynı anda birden fazla değişken atayabilir V[,c("a", "b")] <- values[c(2, 4)], her birini adıyla ( V$a) çağırabilir veya çoğunu aynı anda kullanabilirsiniz ( values[c(5, 6)] <- V[,c("a", "b")]).

Tembelleşirseniz ve veri çerçevesinden değişkenleri çağırmak istemezseniz, bunu yapabilirsiniz attach(V)(kişisel olarak bunu hiç yapmasam da).

# 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

5
Yapabilseydim +10. İnsanların neden bu kadar açık durumlarda listeleri kullanmayı reddettiğini, bunun yerine çalışma alanını tonlarca anlamsız değişkenle doldurduğunu merak ediyorum. (veri çerçevesi özel bir liste türü olduğu için listeleri kullanıyorsunuz. Ben sadece daha genel bir tane kullanırım.)
Joris Meys

ancak aynı sütunda farklı türde öğeleriniz olamaz ve veri çerçevenizi veya veri çerçevenizin içindeki listeleri depolayamazsınız
skan

1
Aslında listeleri bir veri çerçevesinde saklayabilirsiniz - google "liste sütunu".

Bu kötü bir yaklaşım değil, bazı kolaylıkları var, ancak birçok kullanıcının bu şekilde atanan değişkenleri her kullanmaya veya bunlara erişmeye çalıştıklarında neden data.frame sözdizimi ile uğraşmak istemediğini tahmin etmek o kadar da zor değil.
Brandon

34

Bu sorunun üstesinden gelmek için bir R paketi zeallot oluşturdum. zeallot, atamayı %<-%paketten çıkarmak , çoğaltmak ve yok etmek için bir operatör ( ) içerir . Atama ifadesinin LHS'si, çağrıları kullanılarak oluşturulur c(). Atama ifadesinin RHS'si, dönen veya bir vektör, liste, iç içe geçmiş liste, veri çerçevesi, karakter dizesi, tarih nesnesi veya özel nesneler (bir destructureuygulama olduğu varsayılarak ) olan herhangi bir ifade olabilir .

İşte zeallot (en son sürüm, 0.0.5) kullanılarak elden geçirilen ilk soru.

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

Daha fazla örnek ve bilgi için paket vinyetini inceleyebilirsiniz .


bu tam olarak bulmayı umduğum şeydi, OP'nin istediği python benzeri sözdizimini etkinleştiren bir şey, bir R paketinde uygulandı
jafelds

1
Her değişken adına bir matris atamaya ne dersiniz?
StatsSorceress

14

işte benim fikrim. Muhtemelen sözdizimi oldukça basittir:

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

şöyle verir:

> 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

bu yine de iyi test edilmemiştir.


3
Koshke, bana çok hoş görünüyor :-) Ama operatör önceliği konusunda biraz endişeliyim:% bir şey% operatörü oldukça yüksek, bu nedenle örneğin c(c, d) %tin% c(1, 2) + 3(=> c = 1, d = 1, sayısal ( 0)) şaşırtıcı kabul edilebilir.
cbeleites SX

10

Potansiyel olarak tehlikeli (kullanmanın assignriskli olduğu kadar ) bir seçenek 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

Veya argüman mapplyiçin mantıklı bir varsayılan kullanan kendi fonksiyonunuzla kendiniz manuel olarak vektörleştirebileceğinizi varsayalım envir. Örneğin Vectorize, aynı ortam özelliklerine sahip bir işlevi döndürecektir assign, bu durumda namespace:base, ya da sizin ayarlayabileceğiniz gibi envir = parent.env(environment(assignVec)).


9

Diğerlerinin açıkladığı gibi, yerleşik bir şey yok gibi görünüyor. ... ancak vassignaşağıdaki gibi bir işlevi tasarlayabilirsiniz :

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

Dikkate alınması gereken bir şey de, örneğin 3 değişken ve 5 değer belirlediğiniz veya tam tersini yaptığınız durumların nasıl ele alınacağıdır. Burada, değişkenlerle aynı uzunlukta olması için değerleri tekrarlıyorum (veya kesiyorum). Belki bir uyarı akıllıca olur. Ancak aşağıdakilere izin verir:

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

Bunu beğendim, ancak bir işlevin içinden çağrıldığı bazı durumlarda kırılabileceğinden endişeleniyorum (bunun basit bir testi işe yarasa da, benim hafif bir sürprizim var). ...()Bana kara büyü gibi görünen şey açıklayabilir misin ...?
Ben Bolker

1
@Ben Bolker - Evet, ...()aşırı kara büyü ;-). Öyle olur ki, "işlev çağrısı" ...()değiştirildiğinde, bu bir çift listeye geçebilir as.characterve işte, argümanları dizeler olarak alırsınız ...
Tommy

1
@Ben Bolker - Ve kullandığı için bir fonksiyon içinden çağrıldığında bile doğru çalışması gerekir envir=parent.frame()- Ve isterseniz örneğin belirtebilirsiniz envir=globalenv().
Tommy

Daha soğuk olanı bile bunu değiştirme işlevi olarak alacaktır: `vassign<-` <- function (..., envir = parent.frame (), value)vb. Ancak, atanacak ilk nesnenin halihazırda var olması gerekecek gibi görünüyor. Herhangi bir fikir?
cbeleites SX

@cbeleites - Evet, bu daha iyi olurdu ama ilk argümanın var olması gerektiği sınırlamasını aşabileceğinizi sanmıyorum - bu yüzden buna bir değiştirme işlevi deniyor :) ... ama aksini öğrenirseniz bana bildirin !
Tommy

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

Amacıma hizmet etti, yani ilk beş harfe beş tane 2 atamak.



5

Son zamanlarda benzer bir sorun yaşadım ve işte denemem purrr::walk2

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

3

Tek gereksiniminiz tek bir kod satırına sahip olmaksa, peki ya:

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

2
kısa ve öz bir ifade arıyordu ama sanırım hiçbiri yok
user236215

@ User236215 ile aynı teknedeyim. sağ taraf bir vektörü döndüren karmaşık bir ifade olduğunda, kodun tekrarlanması çok yanlış görünüyor ...
hava saldırısı

1

Maalesef aradığınız (gibi c(a, b) = c(2, 4)) şık bir çözüm yok. Ama pes etme, emin değilim! Aklıma gelen en yakın çözüm şudur:

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

veya uyarılardan rahatsızsanız, bunları kapatın:

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

Ama sanırım bu çözümden memnun değilsiniz, ben de olmazdım ...


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

Özyinelemeli başka bir versiyon:

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

misal:

> 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

Benim versiyonum:

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

misal:

> let(c(x, y), 1:2 + 3)
> x
[1] 4
> y
[1] 

0

Burada verilen cevapların bir kısmını + biraz tuzu birleştirirsek, bu çözüme ne dersiniz?

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

Bunu R bölümünü buraya eklemek için kullandım: http://rosettacode.org/wiki/Sort_three_variables#R

Uyarı: Yalnızca küresel değişkenleri (gibi <<-) atamak için çalışır . Daha iyi, daha genel bir çözüm varsa, lütfen. yorumlarda bana söyle.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.