Mengapa X [Y] gabungan dari data.tables tidak mengizinkan gabungan luar penuh, atau gabungan kiri?

123

Ini adalah sedikit pertanyaan filosofis tentang sintaks gabungan data.table. Saya menemukan semakin banyak kegunaan untuk data.tables, tetapi masih belajar ...

Format X[Y]gabungan untuk data.tables sangat ringkas, praktis dan efisien, tetapi sejauh yang saya tahu, ini hanya mendukung gabungan dalam dan gabungan luar kanan. Untuk mendapatkan gabungan luar kiri atau penuh, saya perlu menggunakan merge:

  • X[Y, nomatch = NA] - semua baris di Y - gabungan luar kanan (default)
  • X[Y, nomatch = 0] - hanya baris dengan kecocokan di X dan Y - gabungan dalam
  • merge(X, Y, all = TRUE) - semua baris dari X dan Y - gabungan luar penuh
  • merge(X, Y, all.x = TRUE) - semua baris di X - gabungan luar kiri

Menurut saya, akan berguna jika X[Y]format gabungan mendukung semua 4 jenis gabungan. Apakah ada alasan mengapa hanya dua jenis gabungan yang didukung?

Bagi saya, nilai parameter nomatch = 0dan nomatch = NAtidak terlalu intuitif untuk tindakan yang dilakukan. Hal ini lebih mudah bagi saya untuk memahami dan mengingat mergesintaks: all = TRUE, all.x = TRUEdan all.y = TRUE. Karena X[Y]operasi mergelebih menyerupai daripada match, mengapa tidak menggunakan mergesintaks untuk gabungan daripada parameter matchfungsi nomatch?

Berikut adalah contoh kode dari 4 jenis gabungan:

# sample X and Y data.tables
library(data.table)
X <- data.table(t = 1:4, a = (1:4)^2)
setkey(X, t)
X
#    t  a
# 1: 1  1
# 2: 2  4
# 3: 3  9
# 4: 4 16

Y <- data.table(t = 3:6, b = (3:6)^2)
setkey(Y, t)
Y
#    t  b
# 1: 3  9
# 2: 4 16
# 3: 5 25
# 4: 6 36

# all rows from Y - right outer join
X[Y]  # default
#  t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

X[Y, nomatch = NA]  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

merge(X, Y, by = "t", all.y = TRUE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

identical(X[Y], merge(X, Y, by = "t", all.y = TRUE))
# [1] TRUE

# only rows in both X and Y - inner join
X[Y, nomatch = 0]  
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t")  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t", all = FALSE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

identical( X[Y, nomatch = 0], merge(X, Y, by = "t", all = FALSE) )
# [1] TRUE

# all rows from X - left outer join
merge(X, Y, by = "t", all.x = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16

# all rows from both X and Y - full outer join
merge(X, Y, by = "t", all = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16
# 5: 5 NA 25
# 6: 6 NA 36

Pembaruan: data.table v1.9.6 memperkenalkan on=sintaks, yang memungkinkan ad hoc bergabung pada bidang selain kunci utama. jawaban jangorecki atas pertanyaan Bagaimana cara menggabungkan (menggabungkan) bingkai data (dalam, luar, kiri, kanan)? menyediakan beberapa contoh tipe gabungan tambahan yang dapat ditangani oleh data.table.

Douglas Clark
sumber
4
Sudahkah Anda membaca FAQ 1.12 ? Anda selalu dapat menghubungi Y[X]jika Anda ingin kiri luar bergabung dari X[Y]dan rbind(Y[X],X[Y])jika Anda ingin penuh luar bergabung
mnel
Lihat jawaban saya untuk pendekatan data.table yang lebih banyak untuk join luar penuh
mnel
@mnel, saya berasumsi bahwa unique()pendekatan Anda di bawah ini untuk penggabungan penuh lebih disukai rbind(Y[X],X[Y]), karena rbind akan melibatkan penyalinan tabel. Apakah itu benar?
Douglas Clark
sejauh pengetahuan saya, ya. Saya belum menguji apakah tiga panggilan unik yang lebih kecil lebih cepat dari satu panggilan besar (misalnya unique(c(unique(X[,t]), unique(Y[,t]))- ini harus lebih hemat memori karena hanya menggabungkan dua daftar yang akan kurang dari atau sama dengan jumlah baris di X dan Y .
mnel
2
Pertanyaan Anda merupakan deskripsi yang bagus; Saya menemukan jawaban atas pertanyaan saya dalam pertanyaan Anda. Terima kasih
irriss

Jawaban:

71

Mengutip dari data.table FAQ 1.11 Apa perbedaan antara X[Y]dan merge(X, Y)?

X[Y] adalah gabungan, mencari baris X menggunakan Y (atau kunci Y jika ada) sebagai indeks.

Y[X] adalah gabungan, mencari baris Y menggunakan X (atau kunci X jika ada)

merge(X,Y)melakukan kedua cara pada saat yang bersamaan. Jumlah baris X[Y]dan Y[X]biasanya berbeda, sedangkan jumlah baris yang dikembalikan oleh merge(X,Y)dan merge(Y,X)adalah sama.

TAPI itu meleset dari poin utamanya. Sebagian besar tugas memerlukan sesuatu untuk diselesaikan pada data setelah bergabung atau menggabungkan. Mengapa menggabungkan semua kolom data, hanya untuk menggunakan sebagian kecil setelahnya? Anda mungkin menyarankan merge(X[,ColsNeeded1],Y[,ColsNeeded2]), tetapi itu mengharuskan pemrogram untuk menentukan kolom mana yang diperlukan. X[Y,j] di data.table melakukan semua itu dalam satu langkah untuk Anda. Saat Anda menulis X[Y,sum(foo*bar)], data.table secara otomatis memeriksa jekspresi untuk melihat kolom mana yang digunakannya. Ini hanya akan membuat subset kolom tersebut saja; yang lainnya diabaikan. Memori hanya dibuat untuk kolom yang jdigunakan, dan Ykolom menikmati aturan daur ulang R standar dalam konteks setiap grup. Misalkan foois in X, dan bar is in Y(bersama dengan 20 kolom lainnya in Y). TidakX[Y,sum(foo*bar)] lebih cepat memprogram dan lebih cepat menjalankan daripada menggabungkan semua yang sia-sia diikuti oleh subset?


Jika Anda ingin gabungan luar kiri dari X[Y]

le <- Y[X]
mallx <- merge(X, Y, all.x = T)
# the column order is different so change to be the same as `merge`
setcolorder(le, names(mallx))
identical(le, mallx)
# [1] TRUE

Jika Anda ingin full outer join

# the unique values for the keys over both data sets
unique_keys <- unique(c(X[,t], Y[,t]))
Y[X[J(unique_keys)]]
##   t  b  a
## 1: 1 NA  1
## 2: 2 NA  4
## 3: 3  9  9
## 4: 4 16 16
## 5: 5 25 NA
## 6: 6 36 NA

# The following will give the same with the column order X,Y
X[Y[J(unique_keys)]]
mnel
sumber
5
Terima kasih @mnel. FAQ 1.12 tidak menyebutkan gabungan luar penuh atau kiri. Saran gabungan luar lengkap Anda dengan unique () sangat membantu. Itu seharusnya ada di FAQ. Saya tahu Matthew Dowle "merancangnya untuk digunakan sendiri, dan dia menginginkannya seperti itu." (FAQ 1.9), tapi saya pikir X[Y,all=T]bisa menjadi cara yang elegan untuk menentukan gabungan luar penuh dalam sintaks data.table X [Y]. Atau X[Y,all.x=T]untuk bergabung kiri. Saya bertanya-tanya mengapa tidak dirancang seperti itu. Hanya pemikiran saja.
Douglas Clark
1
@DouglasClark Telah menambahkan jawaban, dan mengajukan 2302: Tambahkan sintaks gabungan gabungan mnel ke FAQ (dengan pengaturan waktu) . Saran bagus!
Matt Dowle
1
@mnel Terima kasih atas solusinya ... membuat hari saya ... :)
Ankit
@mnel Adakah cara agar kami dapat menghubungkan NA dengan 0 saat tampil X[Y[J(unique_keys)]]?
Ankit
11
Apa yang membuat saya terkesan tentang dokumentasi data.table adalah bahwa itu bisa sangat bertele-tele, namun tetap sangat samar ...
NiuBiBang
24

Jawaban @ mnel tepat, jadi terima jawaban itu. Ini hanya tindak lanjut, terlalu lama untuk komentar.

Seperti yang dikatakan mnel, gabungan luar kiri / kanan diperoleh dengan menukar Ydan X: Y[X]-vs- X[Y]. Jadi 3 dari 4 jenis gabungan didukung dalam sintaks itu, bukan 2, iiuc.

Menambahkan yang keempat sepertinya ide yang bagus. Katakanlah kita menambahkan full=TRUEatau both=TRUEatau merge=TRUE(tidak yakin nama argumen terbaik?) Maka hal itu tidak terpikir oleh saya sebelumnya yang X[Y,j,merge=TRUE]akan berguna untuk alasan setelah TAPI di FAQ 1.12. Permintaan fitur baru sekarang ditambahkan dan ditautkan kembali ke sini, terima kasih:

FR # 2301: Tambahkan merge = TRUE argumen untuk X [Y] dan Y [X] bergabung seperti merge ().

Versi terbaru telah dipercepat merge.data.table(dengan mengambil salinan dangkal secara internal untuk mengatur kunci lebih efisien, misalnya). Jadi kami mencoba untuk membawa merge()dan X[Y]lebih dekat, dan memberikan semua pilihan kepada pengguna untuk fleksibilitas penuh. Ada pro dan kontra dari keduanya. Permintaan fitur luar biasa lainnya adalah:

FR # 2033: Tambahkan by.x dan by.y ke merge.data.table

Jika ada yang lain, mohon biarkan mereka datang.

Dengan bagian pertanyaan ini:

mengapa tidak menggunakan sintaks gabungan untuk bergabung daripada parameter nomatch fungsi pencocokan?

Jika Anda lebih suka merge()sintaks dan 3 argumen all, all.xdan all.ykemudian hanya menggunakan bahwa alih-alih X[Y]. Pikir itu harus mencakup semua kasus. Atau maksud Anda mengapa argumen itu nomatchmasuk [.data.table? Jika demikian, itu hanya cara yang tampak alami diberikan FAQ 2.14: "Bisakah Anda menjelaskan lebih lanjut mengapa data.table terinspirasi oleh sintaks A [B] dalam basis?". Tapi juga, saat ini nomatchhanya membutuhkan dua nilai 0dan NA. Itu bisa diperpanjang sehingga nilai negatif berarti sesuatu, atau 12 berarti menggunakan nilai baris ke-12 untuk mengisi NAs, misalnya, atau nomatchdi masa depan bisa menjadi vektor atau bahkan dirinya sendiri a data.table.

Hm. Bagaimana by-without-by berinteraksi dengan merge = TRUE? Mungkin kita harus membawa ini ke bantuan dataTable .

Matt Dowle
sumber
Terima kasih @Mat. Jawaban @ mnel sangat bagus, tetapi pertanyaan saya bukanlah bagaimana melakukan penggabungan penuh atau kiri, tetapi "Apakah ada alasan hanya dua jenis penggabungan yang didukung?" Jadi sekarang ini sedikit lebih filosofis ;-) Sebenarnya saya tidak lebih suka menggabungkan sintaks, tetapi tampaknya ada tradisi R untuk membangun hal-hal yang sudah dikenal orang. Saya telah menulis join="all", join="all.x", join="all.y" and join="x.and.y"di tepi catatan saya. Tidak yakin apakah itu lebih baik.
Douglas Clark
@DouglasClark Mungkin joinseperti itu, ide yang bagus. Saya memposting ke bantuan data jadi mari kita lihat. Mungkin berikan data.tablewaktu untuk beradaptasi juga. Misalnya, apakah Anda sudah sampai -tanpa-oleh , dan bergabung dengan lingkup yang diwariskan ?
Matt Dowle
Seperti yang ditunjukkan dalam komentar saya di atas, saya sarankan menambahkan joinkata kunci untuk, ketika saya adalah DataTable: X[Y,j,join=string]. Nilai string yang mungkin untuk bergabung disarankan menjadi: 1) "all.y" dan "right" -
Douglas Clark
1
Hai Matt, pustaka data.table luar biasa; Terima kasih untuk itu; meskipun menurut saya perilaku bergabung (menjadi gabungan luar kanan secara default) harus dijelaskan secara jelas dalam dokumentasi utama; Aku butuh waktu 3 hari untuk memikirkannya.
Timothée HENRY
1
@tucson Hanya untuk menautkan di sini, sekarang diajukan sebagai masalah # 709 .
Matt Dowle
17

Ini "jawaban" adalah proposal untuk diskusi: Seperti yang ditunjukkan dalam komentar saya, saya sarankan menambahkan joinparameter untuk [.data.table () untuk memungkinkan jenis tambahan bergabung, yaitu: X[Y,j,join=string]. Selain 4 jenis gabungan biasa, saya juga menyarankan untuk mendukung 3 jenis gabungan eksklusif , dan gabungan silang .

Nilai joinstring (dan alias) untuk berbagai jenis gabungan diusulkan menjadi:

  1. "all.y"dan "right"- gabung kanan, default data.table sekarang (nomatch = NA) - semua baris Y dengan NAs di mana tidak ada kecocokan X;
  2. "both"dan "inner" - inner join (nomatch = 0) - hanya baris dimana X dan Y cocok;

  3. "all.x"dan "left" - gabungan kiri - semua baris dari X, NAs di mana tidak ada Y yang cocok:

  4. "outer"dan "full" - gabungan luar penuh - semua baris dari X dan Y, NAs jika tidak ada kecocokan

  5. "only.x"dan "not.y"- non-join atau anti-join mengembalikan baris X jika tidak ada kecocokan Y.

  6. "only.y" dan "not.x"- non-join atau anti-join mengembalikan baris Y jika tidak ada kecocokan X.
  7. "not.both" - eksklusif bergabung mengembalikan baris X dan Y di mana tidak ada kecocokan dengan tabel lain, yaitu eksklusif-atau (XOR)
  8. "cross"- Gabungan silang atau perkalian Kartesius dengan setiap baris X cocok dengan setiap baris Y

Nilai default join="all.y"yang sesuai dengan default saat ini.

Nilai string "all", "all.x" dan "all.y" sesuai dengan merge()parameter. String "kanan", "kiri", "dalam", dan "luar" mungkin lebih cocok untuk pengguna SQL.

String "keduanya" dan "not.both" adalah saran terbaik saya saat ini - tetapi seseorang mungkin memiliki saran string yang lebih baik untuk gabungan dalam dan gabungan eksklusif. (Saya tidak yakin apakah "eksklusif" adalah terminologi yang tepat, perbaiki saya jika ada istilah yang tepat untuk gabungan "XOR".)

Penggunaan dari join="not.y"adalah alternatif untuk X[-Y,j]atau X[!Y,j]non-join sintaks dan mungkin lebih jelas (bagi saya), meskipun saya tidak yakin apakah mereka sama (fitur baru dalam data.table versi 1.8.3).

Gabungan silang terkadang berguna, tetapi mungkin tidak sesuai dengan paradigma data.table.

Douglas Clark
sumber
1
Silakan kirim ini ke bantuan data untuk diskusi.
Matt Dowle
3
+1 Tapi, silakan kirim ke bantuan-data , atau ajukan permintaan fitur . Saya tidak keberatan menambahkan jointetapi kecuali itu masuk ke pelacak, itu akan dilupakan.
Matt Dowle
1
Saya melihat Anda belum masuk ke SO untuk sementara waktu. Jadi saya telah mengajukan ini di FR # 2301
Matt Dowle
@MattDowle, +1 untuk fitur ini. (Mencoba melakukannya melalui FR # 2301 tetapi saya mendapatkan pesan izin ditolak).
adilapapaya
@adilapapaya Kami pindah dari RForge ke GitHub. Silakan beri +1 di sini: github.com/Rdatatable/data.table/issues/614 . Arun memindahkan masalah tersebut agar tidak hilang.
Matt Dowle