กำหนดตัวแปรใหม่หลายตัวบน LHS ในบรรทัดเดียว


90

ฉันต้องการกำหนดตัวแปรหลายตัวในบรรทัดเดียวใน 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.
TMS

@Tomas T - ฉันคิดว่าvassignข้อเสนอแนะของฉันด้านล่างใกล้เข้ามาแล้ว ... :)
Tommy

หมายเหตุ: อัฒภาคไม่จำเป็นสำหรับบิตนี้ของ R
Iterator

1
หากคุณลองสิ่งนี้ภายในสภาพแวดล้อมที่เหมาะสมก็จะง่ายพอX <- list();X[c('a','b')] <- values[c(2,4)]ๆ ตกลงคุณไม่ได้กำหนดไว้ในพื้นที่ทำงาน แต่ให้รวมเข้าด้วยกันอย่างดีในรายการ ฉันอยากทำแบบนั้น
Joris Meys

7
ฉันชอบ python แค่ a, b = 1,2 คำตอบทั้งหมดด้านล่างยากขึ้น 100 เท่า
appleLover

คำตอบ:


40

มีคำตอบที่ยอดเยี่ยมในบล็อกการดิ้นรนผ่านปัญหา

สิ่งนี้นำมาจากที่นั่นโดยมีการปรับเปลี่ยนเล็กน้อยมาก

การใช้ฟังก์ชันสามอย่างต่อไปนี้ (บวกหนึ่งเพื่ออนุญาตรายการที่มีขนาดแตกต่างกัน)

# 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

ตัวอย่างเช่นสร้าง dataframe 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

5
+10 ถ้าฉันทำได้ ฉันสงสัยว่าทำไมผู้คนถึงปฏิเสธที่จะใช้รายการในกรณีที่ชัดเจนเช่นนี้ แต่กลับทิ้งพื้นที่ทำงานด้วยตัวแปรที่ไม่มีความหมายมากมาย (คุณใช้รายการเนื่องจาก data.frame เป็นรายการชนิดพิเศษฉันแค่ใช้รายการทั่วไปมากกว่านี้)
Joris Meys

แต่คุณไม่สามารถมีองค์ประกอบประเภทต่างๆในคอลัมน์เดียวกันและคุณไม่สามารถจัดเก็บดาต้าเฟรมหรือรายการในกรอบข้อมูลของคุณได้
กัน

1
จริงๆแล้วคุณสามารถจัดเก็บรายการในกรอบข้อมูล - "คอลัมน์รายการ" ของ Google

ไม่ใช่แนวทางที่ไม่ดี แต่ก็มีสิ่งอำนวยความสะดวกบางอย่าง แต่ก็ไม่ใช่เรื่องยากที่จะจินตนาการว่าทำไมผู้ใช้จำนวนมากไม่ต้องการจัดการกับ data.frame syntax ทุกครั้งที่พยายามใช้หรือเข้าถึงตัวแปรที่กำหนดด้วยวิธีนี้
Brandon

34

ฉันรวบรวมzeallotแพ็คเกจ 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

สำหรับตัวอย่างเพิ่มเติมและข้อมูลหนึ่งสามารถตรวจสอบแพคเกจบทความ


นี่คือสิ่งที่ฉันหวังว่าจะพบสิ่งที่เปิดใช้งานไวยากรณ์เหมือน python ที่ OP ขอนำไปใช้ในแพ็คเกจ R
jafelds

1
แล้วการกำหนดเมทริกซ์ให้กับชื่อตัวแปรแต่ละตัวล่ะ?
StatsSorceress

14

นี่คือความคิดของฉัน อาจเป็นไวยากรณ์ที่ค่อนข้างง่าย:

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

สิ่งนี้ยังไม่ผ่านการทดสอบอย่างดี


3
Koshke ดูดีมากสำหรับฉัน :-) แต่ฉันค่อนข้างกังวลเกี่ยวกับลำดับความสำคัญของตัวดำเนินการ: ตัวดำเนินการ% บางอย่างค่อนข้างสูงดังนั้นพฤติกรรมของเช่นc(c, d) %tin% c(1, 2) + 3(=> c = 1, d = 1 จะส่งกลับตัวเลข ( 0)) อาจถือว่าน่าแปลกใจ
cbeleites ไม่พอใจกับ SX

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

หรือฉันคิดว่าคุณสามารถกำหนดเวกเตอร์ด้วยตัวเองด้วยฟังก์ชันของคุณเองโดยmapplyใช้ค่าเริ่มต้นที่เหมาะสมสำหรับenvirอาร์กิวเมนต์ ตัวอย่างเช่นVectorizeจะส่งคืนฟังก์ชันที่มีคุณสมบัติสภาพแวดล้อมเดียวกันassignซึ่งในกรณีนี้คือnamespace:baseหรือคุณสามารถตั้งค่าenvir = parent.env(environment(assignVec))ได้


9

ตามที่คนอื่นอธิบายไว้ดูเหมือนจะไม่มีอะไรอยู่ในตัว ... แต่คุณสามารถออกแบบ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

1
@ Ben Bolker - ใช่...()มนต์ดำสุด ๆ ;-) มันเกิดขึ้นเมื่อ "การเรียกใช้ฟังก์ชัน" ...()ถูกแทนที่มันจะกลายเป็น pairlist ที่สามารถส่งผ่านไปยังas.characterและ voila คุณได้อาร์กิวเมนต์เป็นสตริง ...
Tommy

1
@ Ben Bolker - และควรทำงานได้อย่างถูกต้องแม้ว่าจะเรียกจากภายในฟังก์ชันเนื่องจากใช้งานenvir=parent.frame()- และคุณสามารถระบุเช่นenvir=globalenv()หากคุณต้องการ
Tommy

เย็นกว่านี้จะมีฟังก์ชั่นทดแทน: `vassign<-` <- function (..., envir = parent.frame (), value)และอื่น ๆ อย่างไรก็ตามดูเหมือนว่าวัตถุแรกที่ได้รับมอบหมายจะต้องมีอยู่แล้ว ความคิดใด ๆ ?
cbeleites ไม่พอใจกับ SX

@cbeleites - ใช่มันจะดีกว่า แต่ฉันไม่คิดว่าคุณจะสามารถแก้ไขข้อ จำกัด ที่อาร์กิวเมนต์แรกต้องมีอยู่ได้นั่นคือเหตุผลที่เรียกว่าฟังก์ชันแทนที่ :) ... แต่โปรดแจ้งให้เราทราบหากคุณพบว่าเป็นอย่างอื่น !
Tommy

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

ตอบสนองจุดประสงค์ของฉันคือกำหนด 2 2s เป็น 5 ตัวอักษรแรก



5

มีปัญหาที่คล้ายกันเมื่อเร็ว ๆ นี้และนี่คือการลองใช้ purrr::walk2

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

3

หากความต้องการเพียงอย่างเดียวของคุณคือต้องมีโค้ดบรรทัดเดียวให้ทำอย่างไร:

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

2
กำลังมองหาข้อความที่รวบรัด แต่ฉันเดาว่าไม่มีเลย
user236215

ฉันนั่งเรือลำเดียวกับ @ user236215 เมื่อด้านขวามือเป็นนิพจน์ที่ซับซ้อนส่งคืนเวกเตอร์รหัสการทำซ้ำดูเหมือนจะผิดมาก ...
โจมตีทางอากาศ

1

ฉันเกรงว่าจะไม่มีทางออกที่หรูหราที่คุณกำลังมองหา (เหมือนc(a, b) = c(2, 4)) โชคร้าย แต่อย่ายอมแพ้ฉันไม่แน่ใจ! ทางออกที่ใกล้ที่สุดที่ฉันคิดได้คือ:

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

หรือหากคุณไม่สบายใจกับคำเตือนให้ปิด:

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

แต่ฉันคิดว่าคุณไม่พอใจกับวิธีแก้ปัญหานี้ฉันก็ไม่เป็นเช่นกัน ...



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

Caveat: ใช้ได้เฉพาะกับการกำหนดตัวแปรส่วนกลาง (เช่น<<-) หากมีวิธีแก้ปัญหาทั่วไปที่ดีกว่ากรุณา บอกฉันในความคิดเห็น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.