ความแตกต่างระหว่างวงเล็บ [] และวงเล็บคู่ [[]] สำหรับการเข้าถึงองค์ประกอบของรายการหรือ dataframe


521

R มีสองวิธีที่แตกต่างกันสำหรับการเข้าถึงองค์ประกอบของรายการหรือ data.frame ที่: และ[][[]]

อะไรคือความแตกต่างระหว่างสองสิ่งในสถานการณ์ที่ฉันควรใช้สถานการณ์หนึ่งกับอีกสถานการณ์หนึ่ง

คำตอบ:


327

นิยามภาษา R มีประโยชน์ในการตอบคำถามประเภทนี้:

R มีโอเปอเรเตอร์การทำดัชนีพื้นฐานสามรายการโดยมีไวยากรณ์ที่แสดงโดยตัวอย่างต่อไปนี้

    x[i]
    x[i, j]
    x[[i]]
    x[[i, j]]
    x$a
    x$"a"

สำหรับเวกเตอร์และเมทริกซ์[[นั้นไม่ค่อยมีการใช้แบบฟอร์มแม้ว่าจะมีความแตกต่างทางความหมายเล็กน้อยจาก[แบบฟอร์ม (เช่นมันลดชื่อหรือแอตทริบิวต์ชื่อติ่มซำและใช้การจับคู่บางส่วนสำหรับดัชนีตัวละคร) เมื่อจัดทำดัชนีโครงสร้างหลายมิติที่มีดัชนีเดียวx[[i]]หรือx[i]จะกลับองค์ประกอบตามลำดับวันของ ix

สำหรับรายการหนึ่งโดยทั่วไปใช้[[เพื่อเลือกองค์ประกอบเดียวใด ๆ ในขณะที่[ส่งกลับรายการขององค์ประกอบที่เลือก

[[รูปแบบช่วยให้เพียงองค์ประกอบเดียวที่จะเลือกใช้จำนวนเต็มหรือตัวอักษรดัชนีในขณะที่[ช่วยให้การจัดทำดัชนีโดยเวกเตอร์ โปรดทราบว่าสำหรับรายการดัชนีสามารถเป็นเวกเตอร์และแต่ละองค์ประกอบของเวกเตอร์จะถูกนำไปใช้กับรายการส่วนประกอบที่เลือกองค์ประกอบที่เลือกของส่วนประกอบนั้นและอื่น ๆ ผลลัพธ์ยังคงเป็นองค์ประกอบเดียว


6
อะไรคือเหตุผลเบื้องหลังที่ใช้ [[vs [to index กับตัวเลขจำนวนเดียวกับ vector] ทำไมไม่ใช้ [สำหรับทั้งคู่? ฉันเดาว่าคุณสามารถใช้ [[เพื่อกลับรายการเดียวและ [กับหนึ่งดัชนีส่งคืนรายการความยาว 1 ... แต่ทำไมไม่ทำให้ [คืนค่ารายการเดียวด้วยดัชนีหนึ่งรายการแทนที่จะเป็นรายการ? ทำไมคุณถึงต้องการส่งคืนรายการความยาว 1
คำจากนั้นไป

4
@ คำบอกกล่าวเมื่อเขียนโปรแกรมคุณสามารถมีความยาวที่ไม่ได้กำหนดซึ่งคุณต้องการใช้สำหรับการจัดทำดัชนี มี[เสมอกลับรายการหมายความว่าคุณจะได้รับการส่งออกระดับเดียวกันโดยไม่คำนึงถึงความยาวของx[v] vตัวอย่างเช่นหนึ่งอาจต้องการมากกว่าย่อยของรายการนี้:lapply lapply(x[v], fun)ถ้า[จะดรอปลิสต์ของเวกเตอร์ที่มีความยาวหนึ่งนี่จะส่งคืนข้อผิดพลาดเมื่อใดก็ตามที่vมีความยาวหนึ่งอัน
Axeman

1
ฉันคิดว่านี่จะอธิบายได้ชัดเจนยิ่งขึ้นadv-r.had.co.nz/Subsetting.html
The Red Pea

171

ความแตกต่างอย่างมีนัยสำคัญระหว่างสองวิธีคือคลาสของวัตถุที่ส่งคืนเมื่อใช้สำหรับการแยกและอาจยอมรับช่วงของค่าหรือเพียงค่าเดียวระหว่างการกำหนด

พิจารณากรณีของการดึงข้อมูลในรายการต่อไปนี้:

foo <- list( str='R', vec=c(1,2,3), bool=TRUE )

สมมติว่าเราต้องการดึงค่าที่จัดเก็บโดยบูลจาก foo และใช้มันในif()คำสั่ง สิ่งนี้จะแสดงให้เห็นถึงความแตกต่างระหว่างค่าส่งคืนของ[]และ[[]]เมื่อใช้สำหรับการแยกข้อมูล []วิธีการส่งกลับวัตถุของรายการระดับ (หรือ data.frame ถ้า foo เป็น data.frame) ในขณะที่[[]]วัตถุวิธีการผลตอบแทนที่มีระดับจะถูกกำหนดโดยประเภทของค่าของพวกเขา

ดังนั้นการใช้[]เมธอดจะส่งผลต่อไปนี้:

if( foo[ 'bool' ] ){ print("Hi!") }
Error in if (foo["bool"]) { : argument is not interpretable as logical

class( foo[ 'bool' ] )
[1] "list"

นี่เป็นเพราะ[]วิธีการส่งคืนรายการและรายการนั้นเป็นวัตถุที่ไม่ถูกต้องในการส่งผ่านไปยังif()คำสั่ง ในกรณีนี้เราต้องใช้[[]]เพราะมันจะคืนค่าวัตถุ "เปล่า" ที่เก็บใน 'บูล' ซึ่งจะมีคลาสที่เหมาะสม:

if( foo[[ 'bool' ]] ){ print("Hi!") }
[1] "Hi!"

class( foo[[ 'bool' ]] )
[1] "logical"

ความแตกต่างที่สองคือการที่[]ผู้ประกอบการอาจจะถูกใช้ในการเข้าถึงช่วงของช่องในรายการหรือคอลัมน์ในกรอบข้อมูลในขณะที่[[]]ผู้ประกอบการจะถูก จำกัด การเข้าถึงเดียวสล็อตหรือคอลัมน์ พิจารณากรณีของการกำหนดค่าโดยใช้รายการที่สองbar():

bar <- list( mat=matrix(0,nrow=2,ncol=2), rand=rnorm(1) )

สมมติว่าเราต้องการเขียนทับ foo สองช่องสุดท้ายด้วยข้อมูลที่อยู่ในแถบ หากเราพยายามใช้[[]]โอเปอเรเตอร์นี่คือสิ่งที่เกิดขึ้น:

foo[[ 2:3 ]] <- bar
Error in foo[[2:3]] <- bar : 
more elements supplied than there are to replace

นี่เป็นเพราะ[[]]ถูก จำกัด ให้เข้าถึงองค์ประกอบเดียว เราจำเป็นต้องใช้[]:

foo[ 2:3 ] <- bar
print( foo )

$str
[1] "R"

$vec
     [,1] [,2]
[1,]    0    0
[2,]    0    0

$bool
[1] -0.6291121

โปรดทราบว่าในขณะที่การมอบหมายนั้นสำเร็จสล็อตใน foo ยังคงชื่อเดิมไว้


111

วงเล็บคู่จะเข้าถึงองค์ประกอบรายการในขณะที่วงเล็บเดี่ยวจะให้รายการกลับพร้อมองค์ประกอบเดียว

lst <- list('one','two','three')

a <- lst[1]
class(a)
## returns "list"

a <- lst[[1]]
class(a)
## returns "character"

66

จาก Hadley Wickham:

จาก Hadley Wickham

การแก้ไขของฉัน (ดูเส็งเคร็ง) เพื่อแสดงโดยใช้ tidyverse / purrr:

ป้อนคำอธิบายรูปภาพที่นี่


2
เย็น! คุณมีpicosecondsของ Grace Hopper แล้ว!
Steve Pitchers

@StevePitchers ใช่มั้ย
jzadra

เกรซกระโดดบนเล็แสดงให้เห็นถึงนาโนวินาทีdailymotion.com/video/x35dsz7
Steve Pitchers

48

[]แยกรายการ[[]]แยกองค์ประกอบภายในรายการ

alist <- list(c("a", "b", "c"), c(1,2,3,4), c(8e6, 5.2e9, -9.3e7))

str(alist[[1]])
 chr [1:3] "a" "b" "c"

str(alist[1])
List of 1
 $ : chr [1:3] "a" "b" "c"

str(alist[[1]][1])
 chr "a"

18

เพียงแค่เพิ่มที่นี่[[ยังมีความพร้อมสำหรับการจัดทำดัชนี recursive

นี่เป็นคำใบ้ในคำตอบโดย @JijoMatthew แต่ไม่ได้สำรวจ

ตามที่ระบุไว้ใน?"[["ไวยากรณ์เช่นx[[y]]ที่ซึ่งlength(y) > 1ถูกตีความว่า:

x[[ y[1] ]][[ y[2] ]][[ y[3] ]] ... [[ y[length(y)] ]]

ทราบว่านี้ไม่ได้เปลี่ยนสิ่งที่ควรจะ Takeaway หลักของคุณบนความแตกต่างระหว่าง[และ[[- คือว่าอดีตจะใช้สำหรับการSubsettingและหลังจะใช้สำหรับการแยกองค์ประกอบของรายการเดียว

ตัวอย่างเช่น,

x <- list(list(list(1), 2), list(list(list(3), 4), 5), 6)
x
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [1] 1
#
# [[1]][[2]]
# [1] 2
#
# [[2]]
# [[2]][[1]]
# [[2]][[1]][[1]]
# [[2]][[1]][[1]][[1]]
# [1] 3
#
# [[2]][[1]][[2]]
# [1] 4
#
# [[2]][[2]]
# [1] 5
#
# [[3]]
# [1] 6

ในการรับค่า 3 เราสามารถทำ:

x[[c(2, 1, 1, 1)]]
# [1] 3

กลับไปที่คำตอบของ @jijoMatthew ด้านบนจำได้ว่าr:

r <- list(1:10, foo=1, far=2)

โดยเฉพาะอย่างยิ่งสิ่งนี้จะอธิบายข้อผิดพลาดที่เรามักจะได้รับเมื่อใช้งานผิดพลาด[[คือ:

r[[1:3]]

ข้อผิดพลาดในr[[1:3]]: การทำดัชนีซ้ำซ้ำล้มเหลวที่ระดับ 2

เนื่องจากรหัสนี้พยายามที่จะประเมินผลจริงr[[1]][[2]][[3]]และการซ้อนrจุดหยุดที่ระดับหนึ่งความพยายามในการแยกผ่านการทำดัชนีแบบเรียกซ้ำล้มเหลวที่[[2]]เช่นที่ระดับ 2

ข้อผิดพลาดในr[[c("foo", "far")]]: ตัวห้อยไม่อยู่ในขอบเขต

ที่นี่ R กำลังมองหาr[["foo"]][["far"]]ซึ่งไม่มีอยู่จริงดังนั้นเราจึงได้ข้อผิดพลาดจากตัวห้อย

อาจเป็นประโยชน์ / สอดคล้องกันมากกว่านี้หากข้อผิดพลาดทั้งสองข้อให้ข้อความเดียวกัน


สวัสดี Micheal ท่านสามารถใช้ [[]] เพื่อจัดทำดัชนีหลายรายการได้หรือไม่?
Therii

14

ทั้งสองวิธีเป็นวิธีย่อย วงเล็บเดี่ยวจะส่งคืนชุดย่อยของรายการซึ่งในนั้นจะเป็นรายการ ie: มันอาจจะมีหรือไม่มีองค์ประกอบมากกว่าหนึ่งองค์ประกอบ ในทางกลับกันวงเล็บคู่จะส่งคืนเพียงองค์ประกอบเดียวจากรายการ

- วงเล็บเดียวจะให้รายการเรา นอกจากนี้เรายังสามารถใช้วงเล็บเดี่ยวได้หากต้องการคืนองค์ประกอบหลายรายการจากรายการ พิจารณารายการต่อไปนี้: -

>r<-list(c(1:10),foo=1,far=2);

ตอนนี้โปรดทราบวิธีการส่งคืนรายการเมื่อฉันพยายามที่จะแสดงมัน ฉันพิมพ์ r แล้วกด Enter

>r

#the result is:-

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

$foo

[1] 1

$far

[1] 2

ตอนนี้เราจะเห็นความมหัศจรรย์ของฉากยึดเดี่ยว: -

>r[c(1,2,3)]

#the above command will return a list with all three elements of the actual list r as below

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

$foo

[1] 1


$far

[1] 2

ซึ่งเหมือนกับเมื่อเราพยายามที่จะแสดงค่าของ r บนหน้าจอซึ่งหมายความว่าการใช้วงเล็บเดี่ยวได้ส่งคืนรายการที่ดัชนี 1 เรามีเวกเตอร์ 10 องค์ประกอบจากนั้นเรามีอีกสององค์ประกอบที่มีชื่อ foo และไกล นอกจากนี้เรายังอาจเลือกที่จะให้ดัชนีหรือชื่อองค์ประกอบเดียวเป็นอินพุตในวงเล็บเดี่ยว เช่น:

> r[1]

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

ในตัวอย่างนี้เราให้หนึ่งดัชนี "1" และในทางกลับกันได้รับรายการที่มีองค์ประกอบหนึ่ง (ซึ่งเป็นอาร์เรย์ของตัวเลข 10)

> r[2]

$foo

[1] 1

ในตัวอย่างข้างต้นเราให้หนึ่งดัชนี "2" และในทางกลับกันได้รับรายการที่มีองค์ประกอบหนึ่ง

> r["foo"];

$foo

[1] 1

ในตัวอย่างนี้เราผ่านชื่อขององค์ประกอบหนึ่งและในคืนรายการถูกส่งกลับด้วยองค์ประกอบหนึ่ง

คุณอาจส่งเวกเตอร์ชื่อองค์ประกอบเช่น: -

> x<-c("foo","far")

> r[x];

$foo

[1] 1

$far
[1] 2

ในตัวอย่างนี้เราผ่านเวกเตอร์ที่มีชื่อองค์ประกอบสองชื่อ "foo" และ "ไกล"

ในทางกลับกันเราได้รายการที่มีสององค์ประกอบ

ในวงเล็บเดี่ยวสั้น ๆ จะส่งคืนรายการอื่นที่มีจำนวนองค์ประกอบเท่ากับจำนวนองค์ประกอบหรือจำนวนดัชนีที่คุณส่งผ่านไปยังวงเล็บเหลี่ยมเดียวเสมอ

ในทางตรงกันข้ามวงเล็บคู่จะส่งคืนองค์ประกอบเดียวเท่านั้น ก่อนที่จะย้ายไปที่วงเล็บคู่ควรจดบันทึกไว้ด้วย NOTE:THE MAJOR DIFFERENCE BETWEEN THE TWO IS THAT SINGLE BRACKET RETURNS YOU A LIST WITH AS MANY ELEMENTS AS YOU WISH WHILE A DOUBLE BRACKET WILL NEVER RETURN A LIST. RATHER A DOUBLE BRACKET WILL RETURN ONLY A SINGLE ELEMENT FROM THE LIST.

ฉันจะยกตัวอย่างสถานที่ โปรดจดคำที่เป็นตัวหนาและกลับมาที่คำศัพท์หลังจากคุณทำตามตัวอย่างด้านล่าง:

วงเล็บคู่จะคืนค่าจริงให้คุณที่ดัชนี (จะไม่ส่งคืนรายการ)

  > r[[1]]

     [1]  1  2  3  4  5  6  7  8  9 10


  >r[["foo"]]

    [1] 1

สำหรับวงเล็บสองชั้นหากเราพยายามดูองค์ประกอบมากกว่าหนึ่งองค์ประกอบโดยการส่งเวกเตอร์มันจะส่งผลให้เกิดข้อผิดพลาดเพียงเพราะมันไม่ได้สร้างขึ้นเพื่อตอบสนองความต้องการนั้น แต่เพียงเพื่อส่งคืนองค์ประกอบเดียว

พิจารณาดังต่อไปนี้

> r[[c(1:3)]]
Error in r[[c(1:3)]] : recursive indexing failed at level 2
> r[[c(1,2,3)]]
Error in r[[c(1, 2, 3)]] : recursive indexing failed at level 2
> r[[c("foo","far")]]
Error in r[[c("foo", "far")]] : subscript out of bounds

1
Downvoted เพราะ "ผ่านเวกเตอร์ ... จะส่งผลให้เกิดข้อผิดพลาดเพียงเพราะไม่ได้สร้างขึ้นเพื่อรองรับความต้องการนั้น" ไม่ถูกต้อง เห็นคำตอบใหม่ของฉัน
MichaelChirico

1
ลงเพราะมันทำให้การเรียกร้องที่แข็งแกร่งเช่น "ในขณะที่การยึดซ้อนสองเท่าจะไม่กลับรายการ" มันไม่เป็นความจริง - ถ้าเรามีอ็อบเจกต์ที่เป็นลิสต์ของรายการวงเล็บเหลี่ยมสองอันจะส่งคืนลิสต์อื่น
dabsingh

13

เพื่อช่วยให้มือใหม่นำทางผ่านหมอกแบบแมนนวลมันจะมีประโยชน์ที่จะเห็น[[ ... ]]สัญกรณ์เป็นฟังก์ชั่นการยุบ - กล่าวอีกนัยหนึ่งก็คือเมื่อคุณต้องการ 'รับข้อมูล' จากเวกเตอร์ชื่อรายการหรือกรอบข้อมูล เป็นการดีถ้าคุณต้องการใช้ข้อมูลจากวัตถุเหล่านี้เพื่อการคำนวณ ตัวอย่างง่ายๆเหล่านี้จะอธิบาย

(x <- c(x=1, y=2)); x[1]; x[[1]]
(x <- list(x=1, y=2, z=3)); x[1]; x[[1]]
(x <- data.frame(x=1, y=2, z=3)); x[1]; x[[1]]

ดังนั้นจากตัวอย่างที่สาม:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2

1
ในฐานะมือใหม่ฉันพบว่ามีประโยชน์ใน 3 การมอบหมายให้ x (โดยใช้ "<-") เพื่อแทนที่ x = 1 ด้วย w = 1 เพื่อหลีกเลี่ยงความสับสนกับ x ซึ่งเป็นเป้าหมายของ "<-"
user36800

แม้ว่าจะง่ายมากฉันชอบคำอธิบายนี้จริงๆ อีกสาธิตง่าย: iris[[1]]ส่งกลับเวกเตอร์ในขณะที่iris[1]ผลตอบแทน data.frame
stevec

11

เป็นศัพท์, [[ผู้ประกอบการแยกองค์ประกอบจากรายการในขณะที่[ผู้ประกอบการใช้เวลาส่วนย่อยของรายการ


7

สำหรับกรณีการใช้อื่นที่เป็นรูปธรรมให้ใช้วงเล็บคู่เมื่อคุณต้องการเลือกกรอบข้อมูลที่สร้างขึ้นโดยsplit()ฟังก์ชัน หากคุณไม่ทราบให้split()จัดกลุ่มรายการ / กรอบข้อมูลเป็นชุดย่อยตามฟิลด์คีย์ มันมีประโยชน์ถ้าเมื่อคุณต้องการที่จะทำงานกับหลายกลุ่มพล็อตพวกเขา ฯลฯ

> class(data)
[1] "data.frame"

> dsplit<-split(data, data$id)
> class(dsplit)
[1] "list"

> class(dsplit['ID-1'])
[1] "list"

> class(dsplit[['ID-1']])
[1] "data.frame"

-1

โปรดอ้างอิงคำอธิบายรายละเอียดด้านล่าง

ฉันใช้เฟรมข้อมูลในตัวใน R เรียกว่า mtcars

> mtcars 
               mpg cyl disp  hp drat   wt ... 
Mazda RX4     21.0   6  160 110 3.90 2.62 ... 
Mazda RX4 Wag 21.0   6  160 110 3.90 2.88 ... 
Datsun 710    22.8   4  108  93 3.85 2.32 ... 
           ............

บรรทัดบนสุดของตารางเรียกว่าส่วนหัวซึ่งมีชื่อคอลัมน์ แต่ละบรรทัดในแนวนอนหลังจากนั้นแสดงแถวข้อมูลซึ่งเริ่มต้นด้วยชื่อของแถวจากนั้นตามด้วยข้อมูลจริง สมาชิกข้อมูลแต่ละแถวเรียกว่าเซลล์

เครื่องหมายวงเล็บสี่เหลี่ยมเดี่ยว "[]"

ในการดึงข้อมูลในเซลล์เราจะป้อนพิกัดของแถวและคอลัมน์ในเครื่องหมายวงเล็บเหลี่ยม "[]" พิกัดทั้งสองจะคั่นด้วยเครื่องหมายจุลภาค กล่าวอีกนัยหนึ่งพิกัดเริ่มต้นด้วยตำแหน่งแถวจากนั้นตามด้วยเครื่องหมายจุลภาคและลงท้ายด้วยตำแหน่งคอลัมน์ คำสั่งซื้อเป็นสิ่งสำคัญ

เช่น 1: - นี่คือค่าของเซลล์จากแถวแรกคอลัมน์ที่สองของ mtcars

> mtcars[1, 2] 
[1] 6

เช่น 2: - นอกจากนี้เราสามารถใช้ชื่อแถวและคอลัมน์แทนพิกัดตัวเลข

> mtcars["Mazda RX4", "cyl"] 
[1] 6 

เครื่องหมายวงเล็บเหลี่ยมคู่ [[]] "

เราอ้างอิงคอลัมน์เฟรมข้อมูลด้วยเครื่องหมายวงเล็บคู่ "[[]]"

เช่น 1: - เพื่อดึงเวกเตอร์คอลัมน์ที่เก้าของ mtcars ชุดข้อมูลในตัวเราเขียน mtcars [[9]]

mtcars [[9]] [1] 1 1 1 0 0 0 0 0 0 0 0 0 ...

เช่น 2: - เราสามารถดึงเวกเตอร์คอลัมน์เดียวกันด้วยชื่อ

mtcars [["am"]] [1] 1 1 1 0 0 0 0 0 0 0 0 0 ...


-1

นอกจากนี้:

ติดตามลิงค์ของคำตอบที่นี่

นี่เป็นตัวอย่างเล็กน้อยที่กล่าวถึงประเด็นต่อไปนี้:

x[i, j] vs x[[i, j]]

df1   <- data.frame(a = 1:3)
df1$b <- list(4:5, 6:7, 8:9)

df1[[1,2]]
df1[1,2]

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