Sự khác biệt giữa ngoặc vuông [] và ngoặc kép [[]] để truy cập các phần tử của danh sách hoặc khung dữ liệu


521

R cung cấp hai phương thức khác nhau để truy cập các phần tử của danh sách hoặc data.frame: [][[]].

Sự khác biệt giữa hai và trong tình huống nào tôi nên sử dụng so với tình huống khác?

Câu trả lời:


327

Định nghĩa ngôn ngữ R có ích để trả lời các loại câu hỏi sau:

R có ba toán tử lập chỉ mục cơ bản, với cú pháp được hiển thị bằng các ví dụ sau

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

Đối với vectơ và ma trận, các [[biểu mẫu hiếm khi được sử dụng, mặc dù chúng có một số khác biệt nhỏ về ngữ nghĩa so với [biểu mẫu (ví dụ: nó bỏ bất kỳ tên hoặc thuộc tính dimnames nào và kết hợp một phần được sử dụng cho các chỉ mục ký tự). Khi lập chỉ mục các cấu trúc đa chiều với một chỉ mục duy nhất, x[[i]]hoặc x[i]sẽ trả về iphần tử tuần tự thứ của x.

Đối với danh sách, người ta thường sử dụng [[để chọn bất kỳ phần tử đơn lẻ nào, trong khi [trả về danh sách các phần tử được chọn.

Biểu [[mẫu chỉ cho phép một phần tử duy nhất được chọn bằng các chỉ số nguyên hoặc ký tự, trong khi [cho phép lập chỉ mục theo vectơ. Lưu ý rằng đối với một danh sách, chỉ mục có thể là một vectơ và mỗi phần tử của vectơ được áp dụng lần lượt cho danh sách, thành phần được chọn, thành phần được chọn của thành phần đó, v.v. Kết quả vẫn là một yếu tố duy nhất.


6
Lý do đằng sau việc sử dụng [[vs [để lập chỉ mục với một số duy nhất so với vectơ là gì? Tại sao không chỉ sử dụng [cho cả hai? Tôi đoán bạn có thể sử dụng [[để lấy lại một mục nhập và [với một chỉ mục trả về danh sách có độ dài 1 ... nhưng tại sao không tạo [trả lại một mục nhập với một chỉ mục thay vì danh sách? Tại sao bạn có thể muốn một danh sách dài 1 được trả về?
lời giới thiệu

4
@wordsforthewise, khi lập trình bạn có thể có một vectơ có độ dài không xác định mà bạn muốn sử dụng để lập chỉ mục. Luôn [luôn trả về một danh sách có nghĩa là bạn nhận được cùng một lớp đầu ra cho x[v]bất kể độ dài của v. Ví dụ: người ta có thể muốn lapplyvượt qua một tập hợp con của danh sách : lapply(x[v], fun). Nếu [sẽ bỏ danh sách các vectơ có độ dài một, điều này sẽ trả về lỗi bất cứ khi nào vcó độ dài một.
Axeman

1
Tôi nghĩ rằng điều này giải thích rõ ràng hơn, adv-r.had.co.nz/Subetting.html
Hạt đậu đỏ

171

Sự khác biệt đáng kể giữa hai phương thức là lớp của các đối tượng mà chúng trả về khi được sử dụng để trích xuất và liệu chúng có thể chấp nhận một phạm vi giá trị hay chỉ một giá trị trong khi gán.

Xem xét trường hợp trích xuất dữ liệu trong danh sách sau:

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

Giả sử chúng tôi muốn trích xuất giá trị được lưu trữ bởi bool từ foo và sử dụng nó trong một if()câu lệnh. Điều này sẽ minh họa sự khác biệt giữa các giá trị trả về [][[]]khi chúng được sử dụng để trích xuất dữ liệu. Các []phương pháp lợi nhuận đối tượng trong danh sách lớp (hoặc data.frame nếu foo là một data.frame) trong khi [[]]trở về phương pháp đối tượng mà lớp được xác định bởi các loại giá trị của họ.

Vì vậy, sử dụng []phương pháp cho kết quả như sau:

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

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

Điều này là do []phương thức trả về một danh sách và một danh sách không phải là đối tượng hợp lệ để chuyển trực tiếp vào một if()câu lệnh. Trong trường hợp này, chúng ta cần sử dụng [[]]vì nó sẽ trả về đối tượng "trần" được lưu trữ trong 'bool' sẽ có lớp thích hợp:

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

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

Sự khác biệt thứ hai là []toán tử có thể được sử dụng để truy cập vào một loạt các vị trí trong danh sách hoặc cột trong khung dữ liệu trong khi [[]]toán tử bị giới hạn truy cập vào một vị trí hoặc cột duy nhất . Xem xét trường hợp gán giá trị bằng danh sách thứ hai , bar():

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

Giả sử chúng tôi muốn ghi đè hai vị trí cuối cùng của foo bằng dữ liệu chứa trong thanh. Nếu chúng ta cố gắng sử dụng [[]]toán tử, đây là những gì xảy ra:

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

Điều này là do [[]]bị giới hạn truy cập vào một yếu tố duy nhất. Chúng ta cần sử dụng []:

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

$str
[1] "R"

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

$bool
[1] -0.6291121

Lưu ý rằng trong khi chuyển nhượng thành công, các vị trí trong foo vẫn giữ nguyên tên gốc của chúng.


111

Dấu ngoặc kép truy cập một phần tử danh sách , trong khi một dấu ngoặc đơn cung cấp cho bạn một danh sách với một phần tử duy nhất.

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

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

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


48

[]trích xuất một danh sách, [[]]trích xuất các yếu tố trong danh sách

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

Chỉ cần thêm vào đây [[cũng được trang bị cho lập chỉ mục đệ quy .

Điều này đã được gợi ý trong câu trả lời của @JijoMatthew nhưng không được khám phá.

Như đã lưu ý ?"[[", cú pháp như x[[y]], ở đâu length(y) > 1, được hiểu là:

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

Lưu ý rằng điều này không thay đổi gì nên takeaway chính của bạn về sự khác biệt giữa [[[- cụ thể là cựu được sử dụng cho Subsetting , và sau này được sử dụng để chiết xuất các yếu tố danh sách duy nhất.

Ví dụ,

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

Để có được giá trị 3, chúng ta có thể làm:

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

Quay trở lại câu trả lời của @ JijoMatthew ở trên, hãy nhớ lại r:

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

Cụ thể, điều này giải thích các lỗi chúng ta có xu hướng mắc phải khi sử dụng sai [[, cụ thể là:

r[[1:3]]

Lỗi trong r[[1:3]]: lập chỉ mục đệ quy thất bại ở cấp 2

Vì mã này thực sự đã cố gắng đánh giá r[[1]][[2]][[3]]và việc lồng các rđiểm dừng ở cấp độ một, nên nỗ lực trích xuất thông qua lập chỉ mục đệ quy đã thất bại [[2]]ở cấp độ 2, tức là ở cấp độ 2.

Lỗi trong r[[c("foo", "far")]]: đăng ký ngoài giới hạn

Ở đây, R đã tìm kiếm r[["foo"]][["far"]], cái không tồn tại, vì vậy chúng tôi nhận được chỉ số lỗi trong giới hạn.

Có lẽ sẽ hữu ích / nhất quán hơn một chút nếu cả hai lỗi này đều đưa ra cùng một thông điệp.


Xin chào Micheal, chúng ta có thể sử dụng [[]] cho nhiều chỉ mục không ??
Therii

14

Cả hai đều là cách để tập hợp lại. Dấu ngoặc đơn sẽ trả về một tập hợp con của danh sách, mà chính nó sẽ là một danh sách. tức là: Nó có thể có hoặc không chứa nhiều hơn một yếu tố. Mặt khác, một dấu ngoặc kép sẽ chỉ trả về một phần tử duy nhất từ ​​danh sách.

-Single ngoặc sẽ cung cấp cho chúng tôi một danh sách. Chúng tôi cũng có thể sử dụng dấu ngoặc đơn nếu chúng tôi muốn trả về nhiều phần tử từ danh sách. xem xét danh sách sau: -

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

Bây giờ xin lưu ý cách danh sách được trả về khi tôi cố gắng hiển thị nó. Tôi gõ r và nhấn enter

>r

#the result is:-

[[1]]

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

$foo

[1] 1

$far

[1] 2

Bây giờ chúng ta sẽ thấy sự kỳ diệu của dấu ngoặc đơn: -

>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

giống hệt như khi chúng ta cố gắng hiển thị giá trị của r trên màn hình, điều đó có nghĩa là việc sử dụng dấu ngoặc đơn đã trả về một danh sách, trong đó tại chỉ mục 1 chúng ta có một vectơ gồm 10 phần tử, sau đó chúng ta có thêm hai phần tử có tên foo và xa Chúng tôi cũng có thể chọn cung cấp một chỉ mục hoặc tên thành phần làm đầu vào cho dấu ngoặc đơn. ví dụ:

> r[1]

[[1]]

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

Trong ví dụ này, chúng tôi đã đưa ra một chỉ số "1" và đổi lại có một danh sách với một phần tử (là một mảng gồm 10 số)

> r[2]

$foo

[1] 1

Trong ví dụ trên, chúng tôi đã đưa ra một chỉ số "2" và đổi lại có một danh sách với một yếu tố

> r["foo"];

$foo

[1] 1

Trong ví dụ này, chúng tôi đã chuyển tên của một phần tử và đổi lại, một danh sách được trả về với một phần tử.

Bạn cũng có thể truyền một vectơ tên các thành phần như: -

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

> r[x];

$foo

[1] 1

$far
[1] 2

Trong ví dụ này, chúng tôi đã truyền một vectơ có hai tên phần tử là "foo" và "far"

Đổi lại chúng tôi có một danh sách với hai yếu tố.

Trong một dấu ngoặc đơn ngắn sẽ luôn trả về cho bạn một danh sách khác có số phần tử bằng với số phần tử hoặc số chỉ mục bạn chuyển vào dấu ngoặc đơn.

Ngược lại, dấu ngoặc kép sẽ luôn chỉ trả về một phần tử. Trước khi chuyển sang khung đôi, một lưu ý cần lưu ý. 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.

Tôi sẽ trang web một vài ví dụ. Vui lòng ghi lại các từ in đậm và quay lại từ đó sau khi bạn hoàn thành các ví dụ dưới đây:

Dấu ngoặc kép sẽ trả về cho bạn giá trị thực tại chỉ mục. (Nó sẽ KHÔNG trả về danh sách)

  > r[[1]]

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


  >r[["foo"]]

    [1] 1

đối với dấu ngoặc kép nếu chúng ta cố gắng xem nhiều hơn một phần tử bằng cách truyền một vectơ, nó sẽ dẫn đến lỗi chỉ vì nó không được xây dựng để phục vụ nhu cầu đó, mà chỉ trả về một phần tử.

Hãy xem xét những điều sau đây

> 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
Bị từ chối vì "vượt qua một vectơ ... sẽ dẫn đến một lỗi chỉ vì nó không được xây dựng để phục vụ cho nhu cầu đó" là không chính xác; xem câu trả lời mới của tôi
MichaelChirico

1
Bị từ chối vì nó đưa ra những tuyên bố mạnh mẽ như "WHILE MỘT NHÂN ĐÔI NHÂN ĐÔI SVER KHÔNG BAO GIỜ QUAY LẠI MỘT DANH SÁCH". Điều đó không đúng - nếu chúng ta có một đối tượng là danh sách các danh sách, dấu ngoặc kép sẽ trả về một danh sách khác.
dabsingh

Thực tế []trả về một lớp danh sách ngay cả khi nó là một chữ số là rất không trực quan. Họ nên tạo một cú pháp khác như ([])cho danh sách và [[]]để truy cập phần tử thực tế là ổn. Tôi thích nghĩ về [[]]giá trị thô như trong các ngôn ngữ khác.
TokyoToo

13

Để giúp người mới điều hướng qua sương mù thủ công, có thể hữu ích khi xem [[ ... ]]ký hiệu là hàm thu gọn - nói cách khác, đó là khi bạn chỉ muốn 'lấy dữ liệu' từ một vectơ, danh sách hoặc khung dữ liệu được đặt tên. Thật tốt khi làm điều này nếu bạn muốn sử dụng dữ liệu từ các đối tượng này để tính toán. Những ví dụ đơn giản sẽ minh họa.

(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]]

Vì vậy, từ ví dụ thứ ba:

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

1
Là một người mới, tôi thấy hữu ích trong 3 bài tập cho x (sử dụng "<-") để thay thế x = 1 bằng w = 1 để tránh nhầm lẫn với x đó là mục tiêu của "<-"
user36800

Mặc dù rất đơn giản, tôi thực sự thích lời giải thích này. Một cuộc biểu tình đơn giản: iris[[1]]trả về một vector, trong khi iris[1]lợi nhuận một data.frame
stevec

11

Là thuật ngữ, [[toán tử trích xuất phần tử từ danh sách trong khi [toán tử lấy tập hợp con của danh sách.


7

Đối với trường hợp sử dụng cụ thể khác, hãy sử dụng dấu ngoặc kép khi bạn muốn chọn khung dữ liệu được tạo bởi split()hàm. Nếu bạn không biết, hãy split()nhóm một danh sách / khung dữ liệu thành các tập hợp con dựa trên một trường chính. Sẽ hữu ích nếu khi bạn muốn hoạt động trên nhiều nhóm, vẽ biểu đồ, v.v.

> 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

Vui lòng tham khảo giải thích chi tiết dưới đây.

Tôi đã sử dụng khung dữ liệu tích hợp trong R, được gọi là 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 ... 
           ............

Dòng trên cùng của bảng được gọi là tiêu đề chứa tên cột. Mỗi dòng ngang sau đó biểu thị một hàng dữ liệu, bắt đầu bằng tên của hàng và sau đó là dữ liệu thực tế. Mỗi thành viên dữ liệu của một hàng được gọi là một ô.

toán tử dấu ngoặc đơn "[]"

Để truy xuất dữ liệu trong một ô, chúng ta sẽ nhập tọa độ hàng và cột của nó trong toán tử "[]" của dấu ngoặc vuông. Hai tọa độ được phân tách bằng dấu phẩy. Nói cách khác, tọa độ bắt đầu bằng vị trí hàng, sau đó là dấu phẩy và kết thúc bằng vị trí cột. Thứ tự là quan trọng.

Ví dụ 1: - Đây là giá trị ô từ hàng đầu tiên, cột thứ hai của mtcars.

> mtcars[1, 2] 
[1] 6

Ví dụ 2: - Hơn nữa, chúng ta có thể sử dụng tên hàng và cột thay vì tọa độ số.

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

Toán tử ngoặc vuông "[[]]"

Chúng tôi tham chiếu một cột khung dữ liệu với toán tử ngoặc vuông "[[]]".

Ví dụ 1: - Để truy xuất vectơ cột thứ chín của mtcars tập hợp dữ liệu tích hợp, chúng ta viết mtcars [[9]].

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

Ví dụ 2: - Chúng ta có thể truy xuất cùng một vectơ cột theo tên của nó.

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


-1

Ngoài ra:

Sau khi LINK của ĐÁP đây.

Dưới đây là một ví dụ nhỏ giải quyết vấn đề sau:

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])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.