Perbedaan antara braket [] dan braket ganda [[]] untuk mengakses elemen daftar atau bingkai data

521

R menyediakan dua metode berbeda untuk mengakses elemen daftar atau data.frame: []dan [[]].

Apa perbedaan antara keduanya dan situasi mana yang harus saya gunakan satu sama lain?

Sharpie
sumber

Jawaban:

327

Definisi Bahasa R berguna untuk menjawab jenis pertanyaan ini:

R memiliki tiga operator pengindeksan dasar, dengan sintaksis ditampilkan oleh contoh-contoh berikut

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

Untuk vektor dan matriks [[formulir jarang digunakan, meskipun mereka memiliki beberapa perbedaan semantik kecil dari [formulir (misalnya menjatuhkan nama atau atribut nama dimnames, dan bahwa pencocokan sebagian digunakan untuk indeks karakter). Saat mengindeks struktur multi-dimensi dengan indeks tunggal, x[[i]]atau x[i]akan mengembalikan ielemen sekuensial x.

Untuk daftar, biasanya digunakan [[untuk memilih elemen tunggal, sedangkan [mengembalikan daftar elemen yang dipilih.

The [[Bentuk memungkinkan hanya satu elemen yang akan dipilih menggunakan integer atau karakter indeks, sedangkan [memungkinkan pengindeksan oleh vektor. Perhatikan bahwa untuk suatu daftar, indeks dapat berupa vektor dan setiap elemen vektor diterapkan secara bergiliran ke daftar, komponen yang dipilih, komponen yang dipilih dari komponen itu, dan seterusnya. Hasilnya masih satu elemen.

ars
sumber
6
Apa alasan di balik penggunaan [[vs [untuk indeks dengan angka tunggal vs vektor? Mengapa tidak menggunakan saja [untuk keduanya? Saya kira Anda dapat menggunakan [[untuk mendapatkan kembali satu entri, dan [dengan satu indeks mengembalikan daftar panjang 1 ... tetapi mengapa tidak hanya membuat [mengembalikan entri tunggal dengan satu indeks alih-alih daftar? Mengapa Anda ingin daftar panjang-1 dikembalikan?
kata
4
@words Selanjutnya, saat pemrograman Anda dapat memiliki vektor dengan panjang tidak ditentukan yang ingin Anda gunakan untuk pengindeksan. Memiliki [selalu kembali daftar berarti bahwa Anda mendapatkan kelas output yang sama untuk x[v]terlepas dari panjang v. Misalnya, satu mungkin ingin lapplyover subset dari daftar: lapply(x[v], fun). Jika [akan menjatuhkan daftar untuk vektor panjang satu, ini akan mengembalikan kesalahan setiap kali vmemiliki panjang satu.
Axeman
1
Saya pikir ini menjelaskan lebih jelas, adv-r.had.co.nz/Subsetting.html
The Red Pea
171

Perbedaan signifikan antara kedua metode adalah kelas objek yang mereka kembalikan saat digunakan untuk ekstraksi dan apakah mereka dapat menerima rentang nilai, atau hanya nilai tunggal selama penugasan.

Pertimbangkan kasus ekstraksi data pada daftar berikut:

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

Katakanlah kami ingin mengekstrak nilai yang disimpan oleh bool dari foo dan menggunakannya di dalam if()pernyataan. Ini akan menggambarkan perbedaan antara nilai-nilai pengembalian []dan [[]]ketika mereka digunakan untuk ekstraksi data. The []kembali metode objek dari daftar kelas (atau data.frame jika foo adalah data.frame a) sedangkan [[]]metode kembali objek yang kelasnya ditentukan oleh jenis nilai-nilai mereka.

Jadi, menggunakan []metode menghasilkan sebagai berikut:

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

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

Ini karena []metode mengembalikan daftar dan daftar bukan objek yang valid untuk diteruskan langsung ke if()pernyataan. Dalam hal ini kita perlu menggunakan [[]]karena akan mengembalikan objek "telanjang" yang disimpan dalam 'bool' yang akan memiliki kelas yang sesuai:

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

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

Perbedaan kedua adalah bahwa []operator dapat digunakan untuk mengakses berbagai slot dalam daftar atau kolom dalam bingkai data sementara [[]]operator dibatasi untuk mengakses slot atau kolom tunggal . Pertimbangkan kasus nilai tugas menggunakan daftar kedua, bar():

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

Katakanlah kita ingin menimpa dua slot foo terakhir dengan data yang terkandung dalam bilah. Jika kami mencoba menggunakan [[]]operator, inilah yang terjadi:

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

Ini karena [[]]terbatas untuk mengakses elemen tunggal. Kita perlu menggunakan []:

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

$str
[1] "R"

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

$bool
[1] -0.6291121

Perhatikan bahwa sementara tugas berhasil, slot di foo menyimpan nama asli mereka.

Sharpie
sumber
111

Kurung ganda mengakses elemen daftar , sementara braket tunggal memberi Anda kembali daftar dengan elemen tunggal.

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

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

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

Dari Hadley Wickham:

Dari Hadley Wickham

Modifikasi (tampak jelek) saya untuk ditampilkan menggunakan tidyverse / purrr:

masukkan deskripsi gambar di sini

jzadra
sumber
2
Keren! Anda mendapatkan beberapa picosecond Grace Hopper !
Steve Pitchers
@StevePitchers ya?
jzadra
Grace Hopper di Letterman, mendemonstrasikan nano detik dailymotion.com/video/x35dsz7 .
Steve Pitchers
48

[]ekstrak daftar, [[]]ekstrak elemen dalam daftar

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"
Brad Gilbert
sumber
18

Hanya menambahkan di sini yang [[juga dilengkapi untuk pengindeksan rekursif .

Ini diisyaratkan dalam jawaban oleh @JijoMatthew tetapi tidak dieksplorasi.

Seperti disebutkan dalam ?"[[", sintaksis like x[[y]], where length(y) > 1, diartikan sebagai:

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

Perhatikan bahwa ini tidak mengubah apa yang seharusnya menjadi takeaway utama Anda pada perbedaan antara [dan [[- yaitu, bahwa yang pertama digunakan untuk subset , dan yang terakhir digunakan untuk mengekstraksi elemen daftar tunggal.

Sebagai contoh,

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

Untuk mendapatkan nilai 3, kita bisa melakukan:

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

Kembali ke jawaban JijoMatthew di atas, ingat r:

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

Secara khusus, ini menjelaskan kesalahan yang cenderung kita dapatkan ketika salah menggunakan [[, yaitu:

r[[1:3]]

Kesalahan dalam r[[1:3]]: pengindeksan rekursif gagal di level 2

Karena kode ini benar-benar mencoba untuk mengevaluasi r[[1]][[2]][[3]], dan bersarangnya rstop pada level satu, upaya untuk mengekstraksi melalui pengindeksan rekursif gagal pada [[2]], yaitu, pada level 2.

Kesalahan dalam r[[c("foo", "far")]]: subskrip di luar batas

Di sini, R sedang mencari r[["foo"]][["far"]], yang tidak ada, jadi kami mendapatkan subscript dari kesalahan batas.

Mungkin akan sedikit lebih membantu / konsisten jika kedua kesalahan ini memberikan pesan yang sama.

MichaelChirico
sumber
Halo, Micheal, bisakah kita menggunakan [[]] untuk pengindeksan ganda ??
Therii
14

Keduanya merupakan cara subset. Braket tunggal akan mengembalikan subset dari daftar, yang dengan sendirinya akan menjadi daftar. yaitu: Ini mungkin atau mungkin tidak mengandung lebih dari satu elemen. Di sisi lain braket ganda akan mengembalikan hanya satu elemen dari daftar.

Braket -Single akan memberi kita daftar. Kami juga dapat menggunakan braket tunggal jika kami ingin mengembalikan beberapa elemen dari daftar. pertimbangkan daftar berikut: -

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

Sekarang tolong perhatikan cara mengembalikan daftar ketika saya mencoba untuk menampilkannya. Saya ketik r dan tekan enter

>r

#the result is:-

[[1]]

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

$foo

[1] 1

$far

[1] 2

Sekarang kita akan melihat keajaiban braket tunggal: -

>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

yang persis sama dengan ketika kami mencoba untuk menampilkan nilai r pada layar, yang berarti penggunaan braket tunggal telah mengembalikan daftar, di mana pada indeks 1 kami memiliki vektor 10 elemen, maka kami memiliki dua elemen lagi dengan nama foo dan jauh. Kami juga dapat memilih untuk memberikan indeks tunggal atau nama elemen sebagai input ke braket tunggal. misalnya:

> r[1]

[[1]]

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

Dalam contoh ini kami memberikan satu indeks "1" dan sebagai gantinya mendapat daftar dengan satu elemen (yang merupakan array dari 10 angka)

> r[2]

$foo

[1] 1

Pada contoh di atas kami memberikan satu indeks "2" dan sebagai gantinya mendapat daftar dengan satu elemen

> r["foo"];

$foo

[1] 1

Dalam contoh ini kami memberikan nama satu elemen dan sebagai balasannya daftar dikembalikan dengan satu elemen.

Anda juga dapat melewati vektor nama elemen seperti: -

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

> r[x];

$foo

[1] 1

$far
[1] 2

Dalam contoh ini kami melewati vektor dengan dua nama elemen "foo" dan "jauh"

Sebagai gantinya kami mendapat daftar dengan dua elemen.

Singkatnya braket tunggal akan selalu mengembalikan Anda daftar lain dengan jumlah elemen yang sama dengan jumlah elemen atau jumlah indeks yang Anda lewati ke dalam braket tunggal.

Sebaliknya, braket ganda akan selalu mengembalikan hanya satu elemen. Sebelum pindah ke bracket ganda, perlu diingat. 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.

Saya akan memberikan beberapa contoh. Harap catat kata-kata dalam huruf tebal dan kembali lagi setelah Anda selesai dengan contoh-contoh di bawah ini:

Ganda braket akan kembali Anda nilai aktual di indeks. (Ini akan tidak kembali daftar)

  > r[[1]]

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


  >r[["foo"]]

    [1] 1

untuk kurung ganda jika kita mencoba melihat lebih dari satu elemen dengan melewatkan vektor, itu akan menghasilkan kesalahan hanya karena itu tidak dibangun untuk memenuhi kebutuhan itu, tetapi hanya untuk mengembalikan satu elemen.

Pertimbangkan yang berikut ini

> 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
Jijo Mathew
sumber
1
Turun karena "melewati vektor ... akan menghasilkan kesalahan hanya karena tidak dibangun untuk memenuhi kebutuhan itu" tidak benar; lihat jawaban baru saya.
MichaelChirico
1
Turun karena membuat klaim kuat seperti "SAAT BRACKET GANDA TIDAK AKAN PERNAH MENGEMBALIKAN A LIST". Itu tidak benar - jika kita memiliki objek yang merupakan daftar daftar, tanda kurung ganda akan mengembalikan daftar lain.
dabsingh
Fakta yang []mengembalikan kelas daftar meskipun hanya satu digit sangat tidak intuitif. Mereka seharusnya membuat sintaks lain seperti ([])untuk daftar dan [[]]untuk mengakses elemen aktual baik-baik saja. Saya lebih suka menganggapnya [[]]sebagai nilai mentah seperti dalam bahasa lain.
TokyoToo
13

Untuk membantu pemula menavigasi kabut manual, mungkin bermanfaat untuk melihat [[ ... ]]notasi sebagai fungsi runtuh - dengan kata lain, itu adalah saat Anda hanya ingin 'mendapatkan data' dari vektor, daftar, atau bingkai data yang dinamai. Adalah baik untuk melakukan ini jika Anda ingin menggunakan data dari objek-objek ini untuk perhitungan. Contoh-contoh sederhana ini akan menggambarkan.

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

Jadi dari contoh ketiga:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2
Redfoot
sumber
1
Sebagai seorang pemula, saya merasa terbantu dalam 3 penugasan ke x (menggunakan "<-") untuk mengganti x = 1 dengan w = 1 untuk menghindari kebingungan dengan x yang merupakan target "<-"
user36800
Meski sangat sederhana, saya sangat suka penjelasan ini. Demonstrasi sederhana lainnya: iris[[1]]mengembalikan vektor, sedangkan iris[1]mengembalikan data.frame
stevec
11

Menjadi terminologis, [[operator mengekstrak elemen dari daftar sedangkan [operator mengambil bagian dari daftar.

submartingale
sumber
7

Untuk case use konkret lain, gunakan tanda kurung ganda ketika Anda ingin memilih bingkai data yang dibuat oleh split()fungsi. Jika Anda tidak tahu, split()kelompokkan daftar / bingkai data menjadi himpunan bagian berdasarkan bidang kunci. Ini berguna jika ketika Anda ingin beroperasi pada banyak grup, plot, dll.

> 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"
Peter
sumber
-1

Silakan lihat penjelasan di bawah ini.

Saya telah menggunakan bingkai data Built-in di R, yang disebut 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 ... 
           ............

Baris atas tabel disebut header yang berisi nama kolom. Setiap garis horizontal sesudahnya menunjukkan baris data, yang dimulai dengan nama baris, dan kemudian diikuti oleh data aktual. Setiap anggota data dari suatu baris disebut sel.

operator "[]" braket persegi tunggal

Untuk mengambil data dalam sel, kita akan memasukkan koordinat baris dan kolomnya di operator braket persegi "[]". Dua koordinat dipisahkan oleh koma. Dengan kata lain, koordinat dimulai dengan posisi baris, kemudian diikuti oleh koma, dan berakhir dengan posisi kolom. Urutan itu penting.

Misalnya 1: - Ini adalah nilai sel dari baris pertama, kolom kedua mtcars.

> mtcars[1, 2] 
[1] 6

Misalnya 2: - Selanjutnya, kita dapat menggunakan nama baris dan kolom alih-alih koordinat numerik.

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

Braket persegi ganda "[[]]" operator

Kami mereferensikan kolom bingkai data dengan operator braket kuadrat ganda "[[]]".

Misalnya 1: - Untuk mengambil vektor kolom kesembilan dari mtcars kumpulan data bawaan, kami menulis mtcars [[9]].

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

Misal 2: - Kita dapat mengambil vektor kolom yang sama dengan namanya.

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

Prasan Karunarathna
sumber
-1

Sebagai tambahan:

Setelah LINK dari JAWABAN sini.

Berikut adalah sedikit contoh yang membahas hal berikut:

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])
Andre Elrico
sumber