Saya menggunakan data.table dan ada banyak fungsi yang mengharuskan saya untuk menyetel kunci (misalnya X[Y]
). Karena itu, saya ingin memahami apa yang dilakukan kunci untuk mengatur kunci dengan benar dalam tabel data saya.
Satu sumber yang saya baca adalah ?setkey
.
setkey()
mengurutkandata.table
dan menandainya sebagai diurutkan. Kolom yang diurutkan adalah kuncinya. Kuncinya dapat berupa kolom apa pun dalam urutan apa pun. Kolom selalu diurutkan dalam urutan menaik. Tabel diubah dengan referensi. Tidak ada salinan yang dibuat sama sekali, selain memori kerja sementara sebesar satu kolom.
Kesimpulan saya di sini adalah bahwa kunci akan "menyortir" data.table, menghasilkan efek yang sangat mirip dengan order()
. Namun, itu tidak menjelaskan tujuan memiliki kunci.
FAQ data.table 3.2 dan 3.3 menjelaskan:
3.2 Saya tidak memiliki kunci di meja besar, tetapi pengelompokan masih sangat cepat. Mengapa demikian?
data.table menggunakan penyortiran radix. Ini secara signifikan lebih cepat daripada algoritma pengurutan lainnya. Radix secara khusus hanya untuk bilangan bulat, lihat
?base::sort.list(x,method="radix")
. Ini juga salah satu alasan mengapasetkey()
cepat. Jika tidak ada kunci yang disetel, atau kami mengelompokkan dalam urutan yang berbeda dari kunci tersebut, kami menyebutnya secara ad hoc oleh.3.3 Mengapa pengelompokan berdasarkan kolom dalam kunci lebih cepat daripada secara ad hoc?
Karena setiap grup berdekatan dalam RAM, sehingga meminimalkan pengambilan halaman, dan memori dapat disalin secara massal (
memcpy
dalam C) daripada mengulang dalam C.
Dari sini, saya rasa bahwa pengaturan kunci memungkinkan R untuk menggunakan "penyortiran radix" di atas algoritma lain, dan itulah mengapa ini lebih cepat.
Panduan mulai cepat 10 menit juga memiliki panduan tentang kunci.
- Kunci
Mari kita mulai dengan mempertimbangkan data.frame, khususnya nama rown (atau dalam bahasa Inggris, nama baris). Artinya, banyak nama yang menjadi satu baris. Beberapa nama yang termasuk dalam satu baris? Bukan itu yang biasa kita lakukan dalam data.frame. Kita tahu bahwa setiap baris paling banyak memiliki satu nama. Seseorang memiliki setidaknya dua nama, nama pertama dan nama kedua. Itu berguna untuk mengatur direktori telepon, misalnya, yang diurutkan berdasarkan nama belakang, lalu nama pertama. Namun, setiap baris dalam data.frame hanya dapat memiliki satu nama.
Kunci terdiri dari satu atau lebih kolom nama baris, yang mungkin berupa bilangan bulat, faktor, karakter atau kelas lain, bukan hanya karakter. Selanjutnya, baris tersebut diurutkan berdasarkan kunci. Oleh karena itu, data.table dapat memiliki paling banyak satu kunci, karena tidak dapat diurutkan dengan lebih dari satu cara.
Keunikan tidak diberlakukan, yaitu nilai kunci duplikat diperbolehkan. Karena baris diurutkan berdasarkan kunci, setiap duplikat dalam kunci akan muncul secara berurutan
Direktori telepon sangat membantu dalam memahami apa itu kunci, tetapi tampaknya kunci tidak berbeda jika dibandingkan dengan memiliki kolom faktor. Lebih lanjut, tidak dijelaskan mengapa sebuah key dibutuhkan (terutama untuk menggunakan fungsi tertentu) dan bagaimana memilih kolom yang akan dijadikan key. Juga, tampaknya dalam data.table dengan waktu sebagai kolom, mengatur kolom lain sebagai kunci mungkin akan mengacaukan kolom waktu juga, yang membuatnya semakin membingungkan karena saya tidak tahu apakah saya diizinkan mengatur kolom lain sebagai kunci. Bisakah seseorang mencerahkan saya?
sumber
Jawaban:
Pembaruan kecil: Harap lihat juga vinyet HTML baru . Masalah ini menyoroti sketsa lain yang kami rencanakan.
Saya telah memperbarui jawaban ini lagi (Feb 2016) sehubungan dengan
on=
fitur baru yang memungkinkan ad-hoc bergabung juga. Lihat riwayat untuk jawaban sebelumnya (usang).Apa sebenarnya yang
setkey(DT, a, b)
dilakukannya?Itu melakukan dua hal:
DT
dengan kolom yang disediakan ( a , b ) dengan referensi , selalu dalam urutan yang meningkat .sorted
keDT
.Pengubahan urutannya cepat (karena penyortiran radix internal data.table ) dan efisien memori (hanya satu kolom tambahan tipe ganda dialokasikan).
Kapan
setkey()
dibutuhkan?Untuk operasi pengelompokan,
setkey()
tidak pernah menjadi persyaratan mutlak. Artinya, kita bisa melakukan cold-by atau adhoc-by .Namun, sebelum
v1.9.6
, bergabung dari bentukx[i]
yang diperlukankey
untuk diatur padax
. Denganon=
argumen baru dari v1.9.6 + , ini tidak berlaku lagi, dan oleh karena itu pengaturan kunci juga bukan persyaratan mutlak di sini.Perhatikan bahwa
on=
argumen dapat ditentukan secara eksplisit bahkan untukkeyed
gabungan juga.Jadi apa alasan untuk menerapkan
on=
argumen?Ada beberapa alasan.
Ini memungkinkan untuk secara jelas membedakan operasi sebagai operasi yang melibatkan dua data.tables . Hanya melakukan
X[Y]
tidak membedakan ini juga, meskipun dapat diperjelas dengan menamai variabel dengan tepat.Ini juga memungkinkan untuk memahami kolom di mana gabungan / subset dilakukan segera dengan melihat baris kode itu (dan tidak harus menelusuri kembali ke
setkey()
baris yang sesuai ).Dalam operasi di mana kolom ditambahkan atau diperbarui dengan referensi ,
on=
operasi jauh lebih berkinerja karena tidak memerlukan seluruh data.table untuk diurutkan ulang hanya untuk menambah / memperbarui kolom. Sebagai contoh,Dalam kasus kedua, kami tidak perlu menyusun ulang. Ini bukan menghitung urutan yang memakan waktu, tetapi secara fisik menyusun ulang data.table dalam RAM, dan dengan menghindarinya, kami mempertahankan urutan asli, dan juga berfungsi.
Bahkan sebaliknya, kecuali Anda melakukan join berulang-ulang, seharusnya tidak ada perbedaan performa yang terlihat antara gabungan keyed dan ad-hoc .
Hal ini mengarah pada pertanyaan, keuntungan apa lagi yang dimiliki keying data.table ?
Apakah ada keuntungan memasukkan data.table?
Keying sebuah data.table fisik menata ulang itu berdasarkan pada kolom (s) di RAM. Menghitung urutan biasanya bukan bagian yang memakan waktu, melainkan penyusunan ulang itu sendiri. Namun, setelah data diurutkan dalam RAM, baris yang termasuk dalam grup yang sama semuanya bersebelahan dalam RAM, dan oleh karena itu sangat efisien dalam cache. Urutan inilah yang mempercepat operasi pada data.tables yang dikunci.
Oleh karena itu, penting untuk mengetahui apakah waktu yang dihabiskan untuk menyusun ulang seluruh data.table sepadan dengan waktu untuk melakukan gabungan / agregasi yang efisien-cache. Biasanya, kecuali ada operasi pengelompokan / penggabungan berulang yang dilakukan pada keyed data.table yang sama , seharusnya tidak ada perbedaan yang mencolok.
Pertanyaan: Menurut Anda, seperti apa kinerja dibandingkan dengan gabungan kunci , jika Anda menggunakan
setorder()
untuk menyusun ulang data.table dan menggunakanon=
? Jika Anda telah mengikuti sejauh ini, Anda seharusnya dapat menemukannya :-).sumber
DT[J(1e4:1e5)]
benar - benar setara denganDF[DF$x > 1e4 & DF$x < 1e5, ]
? Bisakah Anda menunjukkan kepada saya apaJ
artinya? Selain itu, penelusuran tidak akansample(1e4, 1e7, TRUE)
menghasilkan baris apa pun karena tidak menyertakan angka di atas 1e4.>=
dan<=
- diperbaiki.J
(dan.
) adalah aliaslist
(yaitu, setara). Secara internal, wheni
is list, itu diubah menjadi data.table yang mengikuti pencarian biner yang digunakan untuk menghitung indeks baris. Diperbaiki1e4
untuk1e5
menghindari kebingungan. Terima kasih sudah bercak. Perhatikan bahwa kita sekarang bisa langsung menggunakanon=
argumen untuk melakukan subset biner daripada menyetel kunci. Baca lebih lanjut dari sketsa HTML baru . Dan perhatikan halaman itu untuk sketsa untuk gabungan.Kunci pada dasarnya adalah indeks ke dalam kumpulan data, yang memungkinkan operasi pengurutan, filter, dan penggabungan yang sangat cepat dan efisien. Ini mungkin alasan terbaik untuk menggunakan tabel data daripada bingkai data (sintaks untuk menggunakan tabel data juga jauh lebih ramah pengguna, tetapi itu tidak ada hubungannya dengan kunci).
Jika Anda tidak memahami indeks, pertimbangkan ini: nama buku telepon "diindeks". Jadi jika saya ingin mencari nomor telepon seseorang, itu sangat mudah. Tetapi bagaimana jika saya ingin mencari berdasarkan nomor telepon (misalnya, mencari siapa yang memiliki nomor telepon tertentu)? Kecuali saya dapat "mengindeks ulang" buku telepon dengan nomor telepon, ini akan memakan waktu yang sangat lama.
Pertimbangkan contoh berikut: misalkan saya memiliki tabel, ZIP, dari semua kode pos di AS (> 33.000) bersama dengan informasi terkait (kota, negara bagian, populasi, pendapatan median, dll.). Jika saya ingin mencari informasi untuk kode pos tertentu, pencarian (filter) sekitar 1000 kali lebih cepat jika saya
setkey(ZIP,zipcode)
pertama kali.Manfaat lain berkaitan dengan bergabung. Misalkan seseorang memiliki daftar orang dan kode posnya dalam tabel data (sebut saja "PPL"), dan saya ingin menambahkan informasi dari tabel ZIP (misalnya kota, negara bagian, dan sebagainya). Kode berikut akan melakukannya:
Ini adalah "gabung" dalam arti bahwa saya bergabung dengan informasi dari 2 tabel berdasarkan bidang umum (kode pos). Gabungan seperti ini pada tabel yang sangat besar sangat lambat dengan bingkai data, dan sangat cepat dengan tabel data. Dalam contoh kehidupan nyata saya harus melakukan lebih dari 20.000 penggabungan seperti ini pada tabel lengkap kode pos. Dengan tabel data, skrip membutuhkan waktu sekitar 20 menit. untuk berlari. Saya bahkan tidak mencobanya dengan bingkai data karena akan memakan waktu lebih dari 2 minggu.
IMHO Anda tidak hanya harus membaca tetapi mempelajari materi FAQ dan Intro. Lebih mudah untuk memahami jika Anda memiliki masalah sebenarnya untuk menerapkan ini.
[Tanggapan untuk komentar @ Frank]
Perihal: pengurutan vs. pengindeksan - Berdasarkan jawaban atas pertanyaan ini , tampaknya
setkey(...)
sebenarnya mengatur ulang kolom dalam tabel (misalnya, pengurutan fisik), dan tidak membuat indeks dalam pengertian database. Ini memiliki beberapa implikasi praktis: untuk satu hal jika Anda menetapkan kunci dalam tabel dengansetkey(...)
dan kemudian mengubah salah satu nilai di kolom kunci, data.table hanya mendeklarasikan tabel tidak lagi diurutkan (dengan mematikansorted
atribut); itu tidak secara dinamis mengindeks ulang untuk mempertahankan urutan yang benar (seperti yang akan terjadi dalam database). Juga, "menghapus kunci" menggunakansetky(DT,NULL)
tidak tidak mengembalikan meja untuk itu asli, agar tidak dipisahkan.Re: filter vs. join - perbedaan praktisnya adalah filtering mengekstrak subset dari satu dataset, sedangkan join menggabungkan data dari dua dataset berdasarkan field umum. Ada banyak jenis gabungan (dalam, luar, kiri). Contoh di atas adalah gabungan dalam (hanya rekaman dengan kunci yang sama untuk kedua tabel yang dikembalikan), dan ini memiliki banyak kesamaan dengan pemfilteran.
sumber
setkey
benar - benar menyusun ulang baris secara permanen. Jika hanya untuk keperluan tampilan, lalu bagaimana cara mencetak sepuluh baris pertama sesuai dengan urutan "benar" (yang akan saya lihat sebelum setkey)? Saya cukup yakinsetkey(DT,NULL)
tidak melakukan ini ... (lanjutan)X[Y,...]
, Anda perlu "memfilter" baris X menggunakan kunci. Memang, hal-hal lain terjadi setelah itu (kolom Y tersedia, dan ada implisit oleh-tanpa-oleh), tetapi saya masih tidak melihatnya sebagai manfaat yang berbeda secara konseptual. Saya kira jawaban Anda diletakkan dalam istilah operasi yang mungkin ingin Anda lakukan, di mana perbedaannya mungkin berguna.setkey(DT,NULL)
menghapus kunci tetapi tidak mempengaruhi urutan. Mengajukan pertanyaan tentang ini di sini . Ayo lihat.