Penggabungan Panda 101

365
  • Bagaimana cara melakukan ( LEFT| RIGHT| FULL) ( INNER| OUTER) bergabung dengan panda?
  • Bagaimana cara menambahkan NaN untuk baris yang hilang setelah digabungkan?
  • Bagaimana cara saya menyingkirkan NaN setelah bergabung?
  • Bisakah saya menggabungkan indeks?
  • Bergabung silang dengan panda?
  • Bagaimana cara menggabungkan beberapa DataFrames?
  • merge? join? concat? update? WHO? Apa? Mengapa?!

... dan banyak lagi. Saya telah melihat pertanyaan berulang ini menanyakan tentang berbagai aspek fungsi gabungan panda. Sebagian besar informasi mengenai penggabungan dan berbagai kasus penggunaannya saat ini terpecah-pecah di antara banyak posting yang tidak memiliki kata-kata dan tidak dapat ditelusuri. Tujuannya di sini adalah untuk mengumpulkan beberapa poin penting bagi keturunan.

QnA ini dimaksudkan untuk menjadi angsuran berikutnya dalam serangkaian panduan pengguna yang bermanfaat tentang idiom panda umum (lihat posting ini tentang pivoting , dan posting ini tentang penggabungan , yang akan saya bahas nanti).

Harap dicatat bahwa posting ini tidak dimaksudkan sebagai pengganti dokumentasi , jadi harap baca juga! Beberapa contoh diambil dari sana.

cs95
sumber

Jawaban:

520

Posting ini bertujuan untuk memberikan pembaca sebuah primer tentang penggabungan rasa SQL dengan panda, cara menggunakannya, dan kapan tidak menggunakannya.

Secara khusus, inilah yang akan dikirim melalui pos ini:

  • Dasar-dasar - jenis gabungan (KIRI, KANAN, OUTER, INNER)

    • bergabung dengan nama kolom yang berbeda
    • menghindari duplikat kolom kunci gabungan dalam output
  • Menggabungkan dengan indeks dalam kondisi yang berbeda
    • secara efektif menggunakan indeks nama Anda
    • menggabungkan kunci sebagai indeks dari satu dan kolom yang lain
  • Penggabungan multiway pada kolom dan indeks (unik dan non-unik)
  • Alternatif penting untuk mergedanjoin

Apa yang tidak akan dikirim melalui pos ini:

  • Diskusi dan pengaturan waktu terkait kinerja (untuk saat ini). Sebagian besar menyebutkan alternatif yang lebih baik, jika perlu.
  • Menangani sufiks, menghapus kolom tambahan, mengganti nama keluaran, dan kasus penggunaan khusus lainnya. Ada pos lain (baca: lebih baik) yang berurusan dengan itu, jadi cari tahu!

Catatan
Kebanyakan contoh default untuk operasi Gabung INNER sambil menunjukkan berbagai fitur, kecuali ditentukan lain.

Selanjutnya, semua DataFrames di sini dapat disalin dan direplikasi sehingga Anda dapat bermain dengannya. Juga, lihat posting ini tentang cara membaca DataFrames dari clipboard Anda.

Terakhir, semua representasi visual dari operasi JOIN telah digambar tangan menggunakan Google Drawings. Inspirasi dari sini .

Cukup Bicara, cukup tunjukkan saya bagaimana menggunakan merge!

Mendirikan

np.random.seed(0)
left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})    
right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)})

left

  key     value
0   A  1.764052
1   B  0.400157
2   C  0.978738
3   D  2.240893

right

  key     value
0   B  1.867558
1   D -0.977278
2   E  0.950088
3   F -0.151357

Demi kesederhanaan, kolom kunci memiliki nama yang sama (untuk saat ini).

Sebuah INNER JOIN diwakili oleh

Perhatikan
ini, bersama dengan angka-angka yang akan datang semua mengikuti konvensi ini:

  • biru menunjukkan baris yang ada di hasil gabungan
  • merah menunjukkan baris yang dikecualikan dari hasil (yaitu, dihapus)
  • hijau menunjukkan nilai yang hilang yang diganti dengan NaN dalam hasilnya

Untuk melakukan INNER JOIN, panggil mergeDataFrame kiri, tentukan DataFrame kanan dan tombol join (paling tidak) sebagai argumen.

left.merge(right, on='key')
# Or, if you want to be explicit
# left.merge(right, on='key', how='inner')

  key   value_x   value_y
0   B  0.400157  1.867558
1   D  2.240893 -0.977278

Ini hanya mengembalikan baris dari leftdan rightyang berbagi kunci umum (dalam contoh ini, "B" dan "D).

Sebuah KIRI Outer JOIN , atau LEFT JOIN diwakili oleh

Ini dapat dilakukan dengan menentukan how='left'.

left.merge(right, on='key', how='left')

  key   value_x   value_y
0   A  1.764052       NaN
1   B  0.400157  1.867558
2   C  0.978738       NaN
3   D  2.240893 -0.977278

Perhatikan penempatan NaN dengan hati-hati di sini. Jika Anda menentukan how='left', maka hanya kunci dari leftyang digunakan, dan data yang hilang dari rightdigantikan oleh NaN.

Dan demikian pula, untuk GABUNGAN LUAR TEPAT , atau GABUNG TEPAT yang ...

... sebutkan how='right':

left.merge(right, on='key', how='right')

  key   value_x   value_y
0   B  0.400157  1.867558
1   D  2.240893 -0.977278
2   E       NaN  0.950088
3   F       NaN -0.151357

Di sini, kunci dari rightdigunakan, dan data yang hilang dari leftdigantikan oleh NaN.

Akhirnya, untuk FULL OUTER JOIN , diberikan oleh

tentukan how='outer'.

left.merge(right, on='key', how='outer')

  key   value_x   value_y
0   A  1.764052       NaN
1   B  0.400157  1.867558
2   C  0.978738       NaN
3   D  2.240893 -0.977278
4   E       NaN  0.950088
5   F       NaN -0.151357

Ini menggunakan kunci dari kedua frame, dan NaN dimasukkan untuk baris yang hilang di keduanya.

Dokumentasi merangkum berbagai gabungan ini dengan baik:

masukkan deskripsi gambar di sini

GABUNGAN Lainnya - KIRI-Tidak Termasuk, KANAN-Tidak, dan FULL-Tidak Termasuk / ANTI BERGABUNG

Jika Anda membutuhkan GABUNGAN KIRI-Kecualikan dan GABUNGAN KECUALI -KECUALI dalam dua langkah.

Untuk KIRI-Tidak Termasuk BERGABUNG, diwakili sebagai

Mulailah dengan melakukan LEFT OUTER JOIN dan kemudian filterkan (tidak termasuk!) Baris yang lefthanya berasal dari ,

(left.merge(right, on='key', how='left', indicator=True)
     .query('_merge == "left_only"')
     .drop('_merge', 1))

  key   value_x  value_y
0   A  1.764052      NaN
2   C  0.978738      NaN

Dimana,

left.merge(right, on='key', how='left', indicator=True)

  key   value_x   value_y     _merge
0   A  1.764052       NaN  left_only
1   B  0.400157  1.867558       both
2   C  0.978738       NaN  left_only
3   D  2.240893 -0.977278       both

Dan juga, untuk BERGABUNG DENGAN KANAN,

(left.merge(right, on='key', how='right', indicator=True)
     .query('_merge == "right_only"')
     .drop('_merge', 1))

  key  value_x   value_y
2   E      NaN  0.950088
3   F      NaN -0.151357

Terakhir, jika Anda diminta untuk melakukan penggabungan yang hanya mempertahankan kunci dari kiri atau kanan, tetapi tidak keduanya (TKI, melakukan ANTI-GABUNG ),

Anda bisa melakukan ini dengan cara yang sama—

(left.merge(right, on='key', how='outer', indicator=True)
     .query('_merge != "both"')
     .drop('_merge', 1))

  key   value_x   value_y
0   A  1.764052       NaN
2   C  0.978738       NaN
4   E       NaN  0.950088
5   F       NaN -0.151357

Nama berbeda untuk kolom kunci

Jika kolom kunci diberi nama yang berbeda — misalnya, leftmemiliki keyLeft, dan rightmemiliki keyRightbukannya key—kemudian Anda harus menentukan left_ondan right_onsebagai argumen alih-alih on:

left2 = left.rename({'key':'keyLeft'}, axis=1)
right2 = right.rename({'key':'keyRight'}, axis=1)

left2

  keyLeft     value
0       A  1.764052
1       B  0.400157
2       C  0.978738
3       D  2.240893

right2

  keyRight     value
0        B  1.867558
1        D -0.977278
2        E  0.950088
3        F -0.151357

left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')

  keyLeft   value_x keyRight   value_y
0       B  0.400157        B  1.867558
1       D  2.240893        D -0.977278

Menghindari kolom kunci duplikat dalam output

Saat menggabungkan keyLeftdari leftdan keyRightdari right, jika Anda hanya menginginkan salah satu keyLeftatau keyRight(tetapi tidak keduanya) dalam output, Anda bisa mulai dengan mengatur indeks sebagai langkah awal.

left3 = left2.set_index('keyLeft')
left3.merge(right2, left_index=True, right_on='keyRight')

    value_x keyRight   value_y
0  0.400157        B  1.867558
1  2.240893        D -0.977278

Bandingkan ini dengan output dari perintah sebelum (ini, output dari left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')), Anda akan melihat keyLeftada yang hilang. Anda bisa mengetahui kolom mana yang akan disimpan berdasarkan indeks frame mana yang ditetapkan sebagai kunci. Ini mungkin penting ketika, katakanlah, melakukan beberapa operasi GABUNG LUAR.

Menggabungkan hanya satu kolom dari salah satu DataFrames

Sebagai contoh, pertimbangkan

right3 = right.assign(newcol=np.arange(len(right)))
right3
  key     value  newcol
0   B  1.867558       0
1   D -0.977278       1
2   E  0.950088       2
3   F -0.151357       3

Jika Anda diharuskan untuk menggabungkan hanya "new_val" (tanpa ada kolom lain), Anda biasanya dapat hanya subset kolom sebelum menggabungkan:

left.merge(right3[['key', 'newcol']], on='key')

  key     value  newcol
0   B  0.400157       0
1   D  2.240893       1

Jika Anda melakukan LEFT OUTER JOIN, solusi yang lebih berkinerja akan melibatkan map:

# left['newcol'] = left['key'].map(right3.set_index('key')['newcol']))
left.assign(newcol=left['key'].map(right3.set_index('key')['newcol']))

  key     value  newcol
0   A  1.764052     NaN
1   B  0.400157     0.0
2   C  0.978738     NaN
3   D  2.240893     1.0

Seperti disebutkan, ini mirip dengan, tetapi lebih cepat dari

left.merge(right3[['key', 'newcol']], on='key', how='left')

  key     value  newcol
0   A  1.764052     NaN
1   B  0.400157     0.0
2   C  0.978738     NaN
3   D  2.240893     1.0

Menggabungkan beberapa kolom

Untuk bergabung di lebih dari satu kolom, tentukan daftar untuk on(atau left_ondan right_on, jika sesuai).

left.merge(right, on=['key1', 'key2'] ...)

Atau, jika namanya berbeda,

left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])

merge*Operasi dan fungsi lain yang bermanfaat

Bagian ini hanya membahas dasar-dasarnya saja, dan dirancang hanya untuk memuaskan selera Anda. Untuk contoh dan kasus, lihat dokumentasi di merge, join, danconcat serta link ke spesifikasi fungsi.


Berbasis indeks * -JOIN (+ indeks-kolom merges)

Mendirikan

np.random.seed([3, 14])
left = pd.DataFrame({'value': np.random.randn(4)}, index=['A', 'B', 'C', 'D'])    
right = pd.DataFrame({'value': np.random.randn(4)}, index=['B', 'D', 'E', 'F'])
left.index.name = right.index.name = 'idxkey'

left
           value
idxkey          
A      -0.602923
B      -0.402655
C       0.302329
D      -0.524349

right

           value
idxkey          
B       0.543843
D       0.013135
E      -0.326498
F       1.385076

Biasanya, gabungan pada indeks akan terlihat seperti ini:

left.merge(right, left_index=True, right_index=True)


         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Dukungan untuk nama indeks

Jika indeks Anda dinamai, maka pengguna v0.23 juga dapat menentukan nama level untuk on(atau left_ondan right_onjika perlu).

left.merge(right, on='idxkey')

         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Menggabungkan pada indeks satu, kolom yang lain

Dimungkinkan (dan cukup sederhana) untuk menggunakan indeks satu, dan kolom lainnya, untuk melakukan penggabungan. Sebagai contoh,

left.merge(right, left_on='key1', right_index=True)

Atau sebaliknya ( right_on=...dan left_index=True).

right2 = right.reset_index().rename({'idxkey' : 'colkey'}, axis=1)
right2

  colkey     value
0      B  0.543843
1      D  0.013135
2      E -0.326498
3      F  1.385076

left.merge(right2, left_index=True, right_on='colkey')

    value_x colkey   value_y
0 -0.402655      B  0.543843
1 -0.524349      D  0.013135

Dalam kasus khusus ini, indeks untuk leftdinamai, sehingga Anda juga dapat menggunakan nama indeks dengan left_on, seperti ini:

left.merge(right2, left_on='idxkey', right_on='colkey')

    value_x colkey   value_y
0 -0.402655      B  0.543843
1 -0.524349      D  0.013135

DataFrame.join
Selain itu, ada opsi ringkas lainnya. Anda dapat menggunakan DataFrame.joindefault untuk bergabung dengan indeks. DataFrame.joinapakah LEFT OUTER JOIN secara default, jadi how='inner'perlu di sini.

left.join(right, how='inner', lsuffix='_x', rsuffix='_y')

         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Perhatikan bahwa saya perlu menentukan lsuffixdan rsuffixargumen karena joinkalau tidak akan keluar:

left.join(right)
ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object')

Karena nama kolomnya sama. Ini tidak akan menjadi masalah jika nama mereka berbeda.

left.rename(columns={'value':'leftvalue'}).join(right, how='inner')

        leftvalue     value
idxkey                     
B       -0.402655  0.543843
D       -0.524349  0.013135

pd.concat
Terakhir, sebagai alternatif untuk gabungan berbasis indeks, Anda dapat menggunakan pd.concat:

pd.concat([left, right], axis=1, sort=False, join='inner')

           value     value
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Hapus join='inner'jika Anda memerlukan FULL OUTER JOIN (default):

pd.concat([left, right], axis=1, sort=False)

      value     value
A -0.602923       NaN
B -0.402655  0.543843
C  0.302329       NaN
D -0.524349  0.013135
E       NaN -0.326498
F       NaN  1.385076

Untuk informasi lebih lanjut, lihat posting kanonik ini pd.concatoleh @piRSquared .


Generalisasi: mergeing beberapa DataFrames

Seringkali, situasi muncul ketika beberapa DataFrame akan digabung bersama. Naif, ini bisa dilakukan dengan chaining mergepanggilan:

df1.merge(df2, ...).merge(df3, ...)

Namun, ini dengan cepat keluar dari tangan banyak DataFrames. Selain itu, mungkin perlu digeneralisasi untuk jumlah DataFrames yang tidak diketahui.

Di sini saya memperkenalkan pd.concatuntuk sambungan multi-arah pada kunci unik , dan DataFrame.joinuntuk sambungan multi-arah pada kunci non-unik . Pertama, pengaturannya.

# Setup.
np.random.seed(0)
A = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4)})    
B = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4)})
C = pd.DataFrame({'key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4)})
dfs = [A, B, C] 

# Note, the "key" column values are unique, so the index is unique.
A2 = A.set_index('key')
B2 = B.set_index('key')
C2 = C.set_index('key')

dfs2 = [A2, B2, C2]

Penggabungan multiway pada kunci unik (atau indeks)

Jika kunci Anda (di sini, kunci tersebut bisa berupa kolom atau indeks) adalah unik, maka Anda dapat menggunakannya pd.concat. Perhatikan bahwa pd.concatbergabung dengan DataFrames pada indeks .

# merge on `key` column, you'll need to set the index before concatenating
pd.concat([
    df.set_index('key') for df in dfs], axis=1, join='inner'
).reset_index()

  key    valueA    valueB  valueC
0   D  2.240893 -0.977278     1.0

# merge on `key` index
pd.concat(dfs2, axis=1, sort=False, join='inner')

       valueA    valueB  valueC
key                            
D    2.240893 -0.977278     1.0

Hilangkan join='inner'untuk GABUNGAN LUAR LENGKAP. Perhatikan bahwa Anda tidak dapat menentukan gabungan LEFT atau RIGHT OUTER (jika Anda memerlukannya, gunakan join, dijelaskan di bawah).

Multiway menggabungkan kunci dengan duplikat

concatcepat, tetapi memiliki kekurangannya. Itu tidak bisa menangani duplikat.

A3 = pd.DataFrame({'key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5)})

pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner')
ValueError: Shape of passed values is (3, 4), indices imply (3, 2)

Dalam situasi ini, kita dapat menggunakan joinkarena dapat menangani kunci non-unik (perhatikan bahwa joinbergabung dengan DataFrames pada indeks mereka; ia memanggil di mergebawah tenda dan melakukan LEFT OUTER JOIN kecuali ditentukan lain).

# join on `key` column, set as the index first
# For inner join. For left join, omit the "how" argument.
A.set_index('key').join(
    [df.set_index('key') for df in (B, C)], how='inner').reset_index()

  key    valueA    valueB  valueC
0   D  2.240893 -0.977278     1.0

# join on `key` index
A3.set_index('key').join([B2, C2], how='inner')

       valueA    valueB  valueC
key                            
D    1.454274 -0.977278     1.0
D    0.761038 -0.977278     1.0
cs95
sumber
49

Tampilan visual tambahan pd.concat([df0, df1], kwargs). Perhatikan bahwa, makna kwarg axis=0atau axis=1tidak seintuitif df.mean()ataudf.apply(func)


di pd.concat ([df0, df1])

eliu
sumber
9
Ini diagram yang bagus. Bolehkah saya bertanya bagaimana Anda memproduksinya?
cs95
6
built-in google doc "masukkan ==> gambar ... ==> baru" (pada 2019-Mei). Tetapi, untuk menjadi jelas: satu-satunya alasan saya menggunakan google doc untuk gambar ini adalah karena catatan saya disimpan di google doc, dan saya ingin gambar yang dapat dimodifikasi dengan cepat di dalam google doc itu sendiri. Sebenarnya sekarang Anda menyebutkannya, alat menggambar google doc cukup rapi.
eliu
Wow, ini luar biasa. Berasal dari dunia SQL, gabungan "vertikal" bukanlah gabungan di kepala saya, karena struktur tabel selalu diperbaiki. Sekarang bahkan berpikir panda harus mengkonsolidasikan concatdan mergedengan parameter arah menjadi horizontalatau vertical.
Ufos
2
@ Ufos Bukankah itu persis apa axis=1dan apa axis=0?
cs95
2
ya, ada sudah sekarang mergedan concatdan sumbu dan apa pun. Namun, seperti yang ditunjukkan @eliu, itu semua hanya konsep gabungan yang sama dengan "kiri" dan "kanan" dan "horisontal" atau "vertikal". Saya, secara pribadi, harus melihat ke dalam dokumentasi setiap kali saya harus mengingat "sumbu" 0mana dan mana 1.
Ufos