Diberikan dua frame data:
df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1)))
df1
# CustomerId Product
# 1 Toaster
# 2 Toaster
# 3 Toaster
# 4 Radio
# 5 Radio
# 6 Radio
df2
# CustomerId State
# 2 Alabama
# 4 Alabama
# 6 Ohio
Bagaimana saya bisa melakukan gaya database, yaitu, gaya sql, bergabung ? Yaitu, bagaimana saya mendapatkan:
- Sebuah bergabung dalam dari
df1
dandf2
:
Kembali hanya baris di mana tabel kiri telah sesuai dengan kunci di tabel kanan. - Sebuah luar bergabung dari
df1
dandf2
:
Pengembalian semua baris dari kedua tabel, bergabung catatan dari kiri yang memiliki pencocokan kunci di tabel kanan. - Sebuah meninggalkan luar bergabung (atau hanya kiri bergabung) dari
df1
dandf2
Kembali semua baris dari tabel kiri, dan setiap baris dengan pencocokan kunci dari tabel kanan. - Sebuah luar benar bergabung dari
df1
dandf2
Kembali semua baris dari tabel kanan, dan setiap baris dengan pencocokan kunci dari tabel kiri.
Kredit tambahan:
Bagaimana saya bisa melakukan pernyataan pilih gaya SQL?
Jawaban:
Dengan menggunakan
merge
fungsi dan parameter opsionalnya:Gabungan dalam:
merge(df1, df2)
akan berfungsi untuk contoh-contoh ini karena R secara otomatis menggabungkan frame dengan nama variabel yang umum, tetapi Anda kemungkinan besar ingin menentukanmerge(df1, df2, by = "CustomerId")
untuk memastikan bahwa Anda hanya mencocokkan pada bidang yang Anda inginkan. Anda juga dapat menggunakanby.x
danby.y
parameter jika variabel yang cocok memiliki nama yang berbeda di bingkai data yang berbeda.Gabung luar:
merge(x = df1, y = df2, by = "CustomerId", all = TRUE)
Kiri luar:
merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)
Luar kanan:
merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)
Silang bergabung:
merge(x = df1, y = df2, by = NULL)
Sama seperti dengan gabungan dalam, Anda mungkin ingin secara eksplisit meneruskan "CustomerId" ke R sebagai variabel yang cocok.Saya pikir itu hampir selalu terbaik untuk secara eksplisit menyatakan pengidentifikasi yang ingin Anda gabungkan; lebih aman jika frame data input berubah secara tak terduga dan lebih mudah dibaca nanti.Anda dapat menggabungkan beberapa kolom dengan memberikan
by
vektor, misby = c("CustomerId", "OrderId")
. , .Jika nama kolom untuk digabungkan tidak sama, Anda dapat menentukan, misalnya, di
by.x = "CustomerId_in_df1", by.y = "CustomerId_in_df2"
manaCustomerId_in_df1
nama kolom dalam bingkai data pertama danCustomerId_in_df2
adalah nama kolom dalam bingkai data kedua. (Ini juga bisa menjadi vektor jika Anda perlu menggabungkan beberapa kolom.)sumber
data.table
paket - itu adalah set baru gabungan sintaksis, tetapi secara radikal lebih cepat dari apa pun yang kita bicarakan di sini.merge(x=df1,y=df2, by.x=c("x_col1","x_col2"), by.y=c("y_col1","y_col2"))
data.table
sekarang, fungsi yang sama lebih cepat.Saya akan merekomendasikan memeriksa paket sqldf Gabor Grothendieck , yang memungkinkan Anda untuk mengekspresikan operasi ini dalam SQL.
Saya menemukan sintaks SQL menjadi lebih sederhana dan lebih alami daripada yang setara dengan R (tapi ini mungkin hanya mencerminkan bias RDBMS saya).
Lihat GitHub sqldf Gabor untuk informasi lebih lanjut tentang bergabung.
sumber
Ada pendekatan data.table untuk gabungan internal, yang sangat efisien waktu dan memori (dan diperlukan untuk beberapa kerangka data lebih besar):
merge
juga berfungsi pada data.tables (karena generik dan panggilanmerge.data.table
)data.table didokumentasikan di stackoverflow:
Bagaimana melakukan operasi gabungan data.table
Penerjemahan SQL bergabung pada kunci asing untuk R data.table sintaks
alternatif Efisien untuk menggabungkan untuk lebih besar data.frames R
Bagaimana melakukan kiri luar dasar bergabung dengan data.table dalam R?
Namun opsi lain adalah
join
fungsi yang ditemukan dalam paket plyrPilihan untuk
type
:inner
,left
,right
,full
.Dari
?join
: Berbedamerge
, [join
] mempertahankan urutan x tidak peduli apa pun tipe join yang digunakan.sumber
plyr::join
. Microbenchmarking menunjukkan, bahwa kinerjanya sekitar 3 kali lebih cepat daripadamerge
.data.table
jauh lebih cepat dari keduanya. Ada juga dukungan besar di SO, saya tidak melihat banyak penulis paket menjawab pertanyaan di sini seseringdata.table
penulis atau kontributor.data.table
sintaksis untuk menggabungkan daftar bingkai data ?nomatch = 0L
dalam hal ini.Anda juga dapat bergabung menggunakan paket dplyr Hadley Wickham yang mengagumkan .
Mutating joins: tambahkan kolom ke df1 menggunakan kecocokan di df2
Penyaringan bergabung: menyaring baris di df1, jangan memodifikasi kolom
sumber
CustomerId
ke angka? Saya tidak melihat disebutkan dalam dokumentasi (untuk keduanyaplyr
dandplyr
) tentang jenis pembatasan ini. Apakah kode Anda berfungsi secara salah, jika kolom gabungancharacter
bertipe (terutama tertarikplyr
)? Apakah saya melewatkan sesuatu?Ada beberapa contoh bagus untuk melakukan hal ini di R Wiki . Saya akan mencuri pasangan di sini:
Metode Penggabungan
Karena kunci Anda dinamai sama, cara singkat untuk melakukan gabungan dalam adalah menggabungkan ():
gabungan bagian dalam penuh (semua catatan dari kedua tabel) dapat dibuat dengan kata kunci "semua":
gabungan luar kiri dari df1 dan df2:
gabungan luar kanan dari df1 dan df2:
Anda dapat membalik mereka, menampar mereka dan menggosok mereka ke bawah untuk mendapatkan dua bergabung luar lainnya yang Anda tanyakan :)
Metode Subskrip
Gabung luar kiri dengan df1 di kiri menggunakan metode subskrip adalah:
Kombinasi lain dari gabungan luar dapat dibuat dengan mungling contoh bergabung luar kiri. (Ya, saya tahu itu setara dengan mengatakan "Saya akan meninggalkannya sebagai latihan untuk pembaca ...")
sumber
Baru di 2014:
Terutama jika Anda juga tertarik pada manipulasi data secara umum (termasuk menyortir, memfilter, mengatur ulang, meringkas dll.), Anda harus memperhatikan
dplyr
, yang dilengkapi dengan berbagai fungsi yang semuanya dirancang untuk memfasilitasi pekerjaan Anda secara khusus dengan bingkai data dan beberapa tipe database lain. Bahkan menawarkan antarmuka SQL yang cukup rumit, dan bahkan fungsi untuk mengubah (sebagian besar) kode SQL langsung menjadi R.Keempat fungsi yang terkait dengan bergabung dalam paket dplyr adalah (mengutip):
inner_join(x, y, by = NULL, copy = FALSE, ...)
: kembalikan semua baris dari x di mana ada nilai yang cocok di y, dan semua kolom dari x dan yleft_join(x, y, by = NULL, copy = FALSE, ...)
: kembalikan semua baris dari x, dan semua kolom dari x dan ysemi_join(x, y, by = NULL, copy = FALSE, ...)
: kembalikan semua baris dari x di mana ada nilai yang cocok di y, menjaga kolom saja dari x.anti_join(x, y, by = NULL, copy = FALSE, ...)
: kembalikan semua baris dari x di mana tidak ada nilai yang cocok di y, menjaga hanya kolom dari xSemuanya ada di sini dengan sangat rinci.
Memilih kolom dapat dilakukan oleh
select(df,"column")
. Jika itu tidak cukup untuk Anda, maka adasql()
fungsinya, di mana Anda dapat memasukkan kode SQL apa adanya, dan itu akan melakukan operasi yang Anda tentukan seperti yang Anda tulis di R selama ini (untuk informasi lebih lanjut, silakan lihat ke sketsa dplyr / database ). Misalnya, jika diterapkan dengan benar,sql("SELECT * FROM hflights")
akan memilih semua kolom dari tabel dplyr "hflights" (a "tbl").sumber
Perbarui metode data.tabel untuk menggabungkan kumpulan data. Lihat contoh di bawah ini untuk setiap jenis bergabung. Ada dua metode, satu dari
[.data.table
ketika melewati data.table kedua sebagai argumen pertama untuk subset, cara lain adalah dengan menggunakanmerge
fungsi yang dikirim ke metode data.table cepat.Di bawah ini adalah tes benchmark base R, sqldf, dplyr dan data.table.
Benchmark menguji set data yang tidak dikunci / tidak terindeks. Benchmark dilakukan pada dataset 50M-1 rows, terdapat 50M-2 nilai umum pada kolom join sehingga setiap skenario (inner, kiri, kanan, penuh) dapat diuji dan bergabung masih belum sepele untuk dilakukan. Ini adalah tipe join yang menekankan algoritma join. Timing adalah sebagai dari
sqldf:0.4.11
,dplyr:0.7.8
,data.table:1.12.0
.Waspadai ada beberapa jenis gabungan yang dapat Anda lakukan menggunakan
data.table
:- perbarui saat bergabung - jika Anda ingin mencari nilai dari tabel lain ke tabel utama Anda
- agregat saat bergabung - jika Anda ingin agregat pada kunci yang Anda bergabung, Anda tidak memiliki untuk mewujudkan semua bergabung hasil
- tumpang tindih bergabung - jika Anda ingin menggabungkan dengan rentang
- bergulir bergabung - jika Anda ingin bergabung untuk bisa cocok dengan nilai-nilai dari preceeding / berikut baris dengan menggulung mereka maju atau mundur
- non-equi bergabung - jika Anda kondisi bergabung tidak setara
Kode untuk direproduksi:
sumber
on =
juga?on
argmerge.data.table
adasort = TRUE
argumen default , yang menambahkan kunci selama penggabungan dan membiarkannya ada di hasilnya. Ini adalah sesuatu yang harus diwaspadai, terutama jika Anda mencoba menghindari pengaturan tombol.data.table
, apa maksud Anda? Bisakah Anda lebih spesifik?dplyr sejak 0,4 menerapkan semua bergabung termasuk
outer_join
, tetapi perlu dicatat bahwa untuk beberapa rilis pertama sebelum 0,4 digunakan untuk tidak menawarkanouter_join
, dan sebagai akibatnya ada banyak kode pengguna solusi peretasan yang sangat buruk melayang-layang di sekitar untuk sementara waktu sesudahnya (Anda masih dapat menemukan kode seperti itu di SO, Kaggle menjawab, github dari periode itu. Karenanya jawaban ini masih memiliki tujuan yang bermanfaat.)Sorotan rilis terkait-bergabung :
v0.5 (6/2016)
v0.4.0 (1/2015)
v0.3 (10/2014)
v0.2 (5/2014)
v0.1.3 (4/2014)
Solusi per komentar Hadley dalam masalah itu:
sumber
dplyr
sintaks, perubahan darilazyeval
kerlang
backend memecahkan banyak kode untuk saya, yang mendorong saya untuk belajar lebih banyakdata.table
, dan sekarang saya lebih banyak menggunakandata.table
.)plyr
/dplyr
/data.table
/ tidyverse sangat tergantung pada tahun mana kami memulai, dan bagaimana (embrionik) keadaan paket saat itu, yang bertentangan dengan sekarang ...Dalam menggabungkan dua frame data dengan ~ 1 juta baris masing-masing, satu dengan 2 kolom dan yang lainnya dengan ~ 20, saya secara mengejutkan ternyata
merge(..., all.x = TRUE, all.y = TRUE)
lebih cepatdplyr::full_join()
. Ini dengan dplyr v0.4Penggabungan membutuhkan ~ 17 detik, full_join membutuhkan ~ 65 detik.
Beberapa makanan untuk, karena saya biasanya default untuk dplyr untuk tugas manipulasi.
sumber
Untuk kasus gabungan kiri dengan
0..*:0..1
kardinalitas atau gabungan kanan dengan0..1:0..*
kardinalitas, dimungkinkan untuk menetapkan kolom unilateral dari joiner (0..1
tabel) langsung ke joinee (0..*
tabel), dan dengan demikian menghindari penciptaan tabel data yang sama sekali baru. Ini membutuhkan pencocokan kolom kunci dari joinee ke dalam joiner dan pengindeksan + memesan baris joiner yang sesuai untuk penugasan.Jika kuncinya adalah satu kolom, maka kita dapat menggunakan satu panggilan
match()
untuk melakukan pencocokan. Inilah yang akan saya bahas dalam jawaban ini.Berikut ini adalah contoh berdasarkan OP, kecuali saya telah menambahkan baris tambahan
df2
dengan id dari 7 untuk menguji kasus kunci yang tidak cocok di dalam joiner. Ini secara efektifdf1
dibiarkan bergabungdf2
:Pada contoh di atas saya membuat asumsi bahwa kolom kunci adalah kolom pertama dari kedua tabel input. Saya berpendapat bahwa, secara umum, ini bukan asumsi yang tidak masuk akal, karena, jika Anda memiliki data.frame dengan kolom kunci, akan aneh jika tidak diatur sebagai kolom pertama dari data.frame dari permulaan. Dan Anda selalu dapat menyusun ulang kolom untuk membuatnya jadi. Konsekuensi yang menguntungkan dari asumsi ini adalah bahwa nama kolom kunci tidak harus dikodekan secara keras, meskipun saya kira itu hanya mengganti satu asumsi dengan yang lain. Concision adalah keuntungan lain dari pengindeksan bilangan bulat, serta kecepatan. Dalam tolok ukur di bawah ini saya akan mengubah implementasi untuk menggunakan pengindeksan nama string agar sesuai dengan implementasi yang bersaing.
Saya pikir ini adalah solusi yang sangat tepat jika Anda memiliki beberapa tabel yang ingin Anda gabung dengan satu meja besar. Membangun kembali seluruh tabel secara berulang untuk setiap penggabungan akan menjadi tidak perlu dan tidak efisien.
Di sisi lain, jika Anda memerlukan joinee untuk tetap tidak berubah melalui operasi ini untuk alasan apa pun, maka solusi ini tidak dapat digunakan, karena ia memodifikasi joinee secara langsung. Meskipun dalam hal ini Anda bisa membuat salinan dan melakukan tugas di tempat pada salinan.
Sebagai catatan, saya secara singkat melihat kemungkinan solusi yang cocok untuk kunci multicolumn. Sayangnya, satu-satunya solusi yang cocok yang saya temukan adalah:
match(interaction(df1$a,df1$b),interaction(df2$a,df2$b))
, atau ide yang sama denganpaste()
.outer(df1$a,df2$a,`==`) & outer(df1$b,df2$b,`==`)
.merge()
dan fungsi gabungan berbasis paket yang setara, yang selalu mengalokasikan tabel baru untuk mengembalikan hasil yang digabungkan, dan karenanya tidak cocok untuk solusi berbasis penugasan di tempat.Misalnya, lihat Mencocokkan beberapa kolom pada bingkai data yang berbeda dan mendapatkan kolom lainnya sebagai hasilnya , cocokkan dua kolom dengan dua kolom lainnya , Cocokkan pada beberapa kolom , dan dupe dari pertanyaan ini di mana saya awalnya menemukan solusi di tempat, Gabungkan dua frame data dengan nomor yang berbeda dari baris dalam R .
Benchmarking
Saya memutuskan untuk melakukan tolok ukur sendiri untuk melihat bagaimana pendekatan penugasan di tempat dibandingkan dengan solusi lain yang telah ditawarkan dalam pertanyaan ini.
Kode pengujian:
Inilah patokan contoh berdasarkan OP yang saya tunjukkan sebelumnya:
Di sini saya membandingkan data input acak, mencoba skala yang berbeda dan pola tumpang tindih kunci yang berbeda antara dua tabel input. Penghitungan ini masih terbatas pada kasus kunci integer satu kolom. Selain itu, untuk memastikan bahwa solusi di tempat akan bekerja untuk gabungan kiri dan kanan dari tabel yang sama, semua data uji acak menggunakan
0..1:0..1
kardinalitas. Ini diimplementasikan dengan pengambilan sampel tanpa mengganti kolom kunci dari data.frame pertama saat membuat kolom kunci dari data.frame kedua.Saya menulis beberapa kode untuk membuat plot log-log dari hasil di atas. Saya membuat plot terpisah untuk setiap persentase yang tumpang tindih. Agak berantakan, tapi saya suka semua tipe solusi dan tipe join diwakili dalam plot yang sama.
Saya menggunakan interpolasi spline untuk menunjukkan kurva halus untuk setiap kombinasi solusi / tipe gabungan, yang digambar dengan simbol pch individu. Jenis gabungan ditangkap oleh simbol pch, menggunakan titik untuk kurung sudut dalam, kiri dan kanan untuk kiri dan kanan, dan berlian untuk penuh. Jenis solusi ditangkap oleh warna seperti yang ditunjukkan dalam legenda.
Inilah patokan skala besar kedua yang lebih berat, berkenaan dengan jumlah dan jenis kolom utama, serta kardinalitas. Untuk tolok ukur ini saya menggunakan tiga kolom utama: satu karakter, satu integer, dan satu logis, tanpa batasan kardinalitas (yaitu,
0..*:0..*
). (Secara umum tidak disarankan untuk mendefinisikan kolom kunci dengan nilai ganda atau kompleks karena komplikasi perbandingan floating-point, dan pada dasarnya tidak ada yang pernah menggunakan jenis mentah, apalagi untuk kolom kunci, jadi saya belum memasukkan jenis-jenis itu dalam kunci Juga, demi informasi, saya awalnya mencoba menggunakan empat kolom kunci dengan memasukkan kolom kunci POSIXct, tetapi tipe POSIXct tidak cocok dengansqldf.indexed
solusi untuk beberapa alasan, mungkin karena anomali perbandingan floating-point, jadi saya dihapus.)Plot yang dihasilkan, menggunakan kode plot yang sama seperti yang diberikan di atas:
sumber
merge
fungsi kita dapat memilih variabel tabel kiri atau tabel kanan, dengan cara yang sama seperti yang kita semua kenal dengan pernyataan pilih dalam SQL (EX: Select a. * ... atau Select b. From .....)Kita harus menambahkan kode tambahan yang akan di subset dari tabel yang baru bergabung.
SQL: -
select a.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df1)]
Cara yang sama
SQL: -
select b.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df2)]
sumber
Untuk gabung bagian dalam pada semua kolom, Anda juga dapat menggunakan
fintersect
dari data.tabel -paket atauintersect
dari dplyr -paket sebagai alternatifmerge
tanpa menentukanby
-kolom. ini akan memberikan baris yang sama antara dua kerangka data:Contoh data:
sumber
Perbarui bergabung. Satu gabungan gaya SQL penting lainnya adalah " pembaruan bergabung " di mana kolom dalam satu tabel diperbarui (atau dibuat) menggunakan tabel lain.
Mengubah tabel contoh OP ...
Misalkan kita ingin menambahkan keadaan pelanggan dari
cust
ke tabel pembeliansales
,, mengabaikan kolom tahun. Dengan basis R, kami dapat mengidentifikasi baris yang cocok dan kemudian menyalin nilai lebih dari:Seperti yang bisa dilihat di sini,
match
pilih baris pertama yang cocok dari tabel pelanggan.Perbarui bergabung dengan banyak kolom.Pendekatan di atas bekerja dengan baik ketika kita bergabung hanya pada satu kolom dan puas dengan pertandingan pertama. Misalkan kita ingin tahun pengukuran di tabel pelanggan agar sesuai dengan tahun penjualan.
Sebagai jawaban @ bgoldst menyebutkan,
match
denganinteraction
mungkin menjadi opsi untuk kasus ini. Lebih mudahnya, seseorang dapat menggunakan data.tabel:Bergulir pembaruan bergabung. Sebagai alternatif, kami mungkin ingin mengambil status terakhir tempat pelanggan ditemukan:
Tiga contoh di atas semuanya berfokus pada membuat / menambahkan kolom baru. Lihat FAQ R terkait untuk contoh memperbarui / memodifikasi kolom yang ada.
sumber