Fungsi pd.concat menumpuk beberapa DataFrame menjadi satu tabel searah sumbu tertentu, baik menambah baris maupun menyandingkan kolom. Topik ini membahas penumpukan baris pada dua potongan log sensor dari hari berbeda, jebakan label index yang menjadi ganda setelah penumpukan, perbaikannya melalui ignore_index=True, penyandingan kolom dengan axis=1, serta pembatasan hasil dengan join="inner". Setelah topik ini selesai, penumpukan tabel dapat dilakukan dengan label index yang tetap konsisten dan kolom sisipan yang tidak diinginkan dapat dikenali sejak awal.
Menumpuk dua potongan log#
pd.concat menerima daftar DataFrame dan menempelkannya secara berurutan pada sumbu yang ditentukan parameter axis. Nilai default axis=0 berarti penumpukan baris, sehingga tabel kedua ditempel tepat di bawah tabel terakhir di dalam daftarnya. Penyelarasan kolom dilakukan berdasarkan nama kolom, bukan berdasarkan posisi, sehingga urutan kolom yang berbeda antartabel tidak menjadi masalah.
Contoh sepanjang topik ini memakai generator log sensor yang sama dengan topik-topik lain di jalur ini. Log hari pertama dibangkitkan dengan seed 42 untuk tanggal 5 Januari 2026, sedangkan log hari kedua dibangkitkan dengan seed 43 untuk tanggal 6 Januari 2026, sehingga kedua potongan berasal dari hari berbeda dengan lima kolom yang sama persis. Jalankan blok berikut, kemudian amati bentuk dan dua baris pertama setiap log.
import numpy as np
import pandas as pd
def buat_log(seed, awal):
rng = np.random.default_rng(seed)
n = 96
df = pd.DataFrame({
"waktu": pd.date_range(awal, periods=n, freq="15min"),
"ruang": rng.choice(["Lab A", "Lab B", "Ruang Arsip"], n, p=[0.4, 0.35, 0.25]),
"suhu_c": np.round(rng.normal(24, 1.5, n), 1),
"lembap_pct": np.round(rng.normal(60, 5, n), 1),
"status_sensor": rng.choice(["normal", "kalibrasi", "rusak"], n, p=[0.85, 0.10, 0.05]),
})
return df
log_hari1 = buat_log(42, "2026-01-05 07:00")
log_hari2 = buat_log(43, "2026-01-06 07:00")
log_hari1.to_csv("log_hari1.csv", index=False) # disimpan agar blok berikutnya cukup membaca berkas ini
log_hari2.to_csv("log_hari2.csv", index=False)
print(log_hari1.shape, log_hari2.shape)
print(log_hari1.head(2))
print(log_hari2.head(2))(96, 5) (96, 5)
waktu ruang suhu_c lembap_pct status_sensor
0 2026-01-05 07:00:00 Ruang Arsip 21.5 68.6 normal
1 2026-01-05 07:15:00 Lab B 21.8 59.1 normal
waktu ruang suhu_c lembap_pct status_sensor
0 2026-01-06 07:00:00 Lab B 22.4 56.1 kalibrasi
1 2026-01-06 07:15:00 Lab A 24.7 67.2 normalKedua log masing-masing berisi 96 baris dengan lima kolom identik, sehingga penumpukan keduanya wajib menghasilkan 192 baris yang tetap berlima kolom. Penumpukan baris tidak mengurutkan data berdasarkan waktu, karena urutan baris hasil mengikuti urutan daftar yang diberikan kepada pd.concat. Kedua sifat ini menjadi dasar sebelum jebakan label index dibahas.
Jebakan index ganda#
pd.concat tidak pernah menomori ulang index hasil penumpukan, sehingga label dari setiap potongan dipertahankan apa adanya. Dua log berukuran 96 baris masing-masing memuat label 0 sampai 95, sehingga tabel gabungan berisi 192 baris dengan dua kelompok label yang sama persis. Jalankan blok berikut untuk mengamati akibatnya pada head, tail, dan seleksi loc.
import pandas as pd
log_hari1 = pd.read_csv("log_hari1.csv", parse_dates=["waktu"])
log_hari2 = pd.read_csv("log_hari2.csv", parse_dates=["waktu"])
log_gabung = pd.concat([log_hari1, log_hari2])
print(log_gabung.shape)
print(log_gabung.head(2))
print(log_gabung.tail(2))
print(len(log_gabung.loc[0]))(192, 5)
waktu ruang suhu_c lembap_pct status_sensor
0 2026-01-05 07:00:00 Ruang Arsip 21.5 68.6 normal
1 2026-01-05 07:15:00 Lab B 21.8 59.1 normal
waktu ruang suhu_c lembap_pct status_sensor
94 2026-01-07 06:30:00 Lab A 25.1 70.4 normal
95 2026-01-07 06:45:00 Ruang Arsip 22.2 63.4 normal
2Baris tail bertanggal 7 Januari memuat label 94 dan 95, label yang sama dengan baris log hari pertama bertanggal 6 Januari. Seleksi log_gabung.loc[0] mengembalikan dua baris sekaligus, sebagaimana ditunjukkan angka 2 pada baris terakhir keluaran. Seleksi seperti ini sah menurut pandas, tetapi ambigu bagi pembaca kode dan berbahaya ketika diikuti operasi yang mengasumsikan keunikan label.
Parameter ignore_index=True memerintahkan pd.concat membangun index baru berupa bilangan 0 sampai 191 dan mengabaikan label asli kedua potongan. Jalankan blok berikut, kemudian bandingkan ujung indexnya dengan hasil sebelumnya.
import pandas as pd
log_hari1 = pd.read_csv("log_hari1.csv", parse_dates=["waktu"])
log_hari2 = pd.read_csv("log_hari2.csv", parse_dates=["waktu"])
log_rapi = pd.concat([log_hari1, log_hari2], ignore_index=True)
print(log_rapi.shape)
print(log_rapi.index[:2].tolist(), log_rapi.index[-2:].tolist())
print(log_rapi.tail(1))(192, 5)
[0, 1] [190, 191]
waktu ruang suhu_c lembap_pct status_sensor
191 2026-01-07 06:45:00 Ruang Arsip 22.2 63.4 normalUjung index kini berupa 190 dan 191 tanpa label ganda, sehingga seleksi berbasis label kembali tidak ambigu. Konsekuensinya, informasi asal baris hilang karena label asli dibuang, sehingga tambahkan kolom penanda hari sebelum menumpuk apabila asal potongan perlu dipertahankan. Pola ignore_index=True dipakai setiap kali label asli tidak dibutuhkan analisis berikutnya.
Menempatkan kolom berdampingan dengan axis=1#
axis=1 mengubah arah penumpukan dari baris menjadi kolom, sehingga dua tabel ditempatkan berdampingan dan dicocokkan berdasarkan label index. Kasus yang cocok untuk pola ini adalah pengukuran suhu dan kelembapan yang dicatat pada dua tabel terpisah tetapi berbagi label jam pencatatan yang sama. Blok berikut membuat dua tabel mini berenam baris, lalu menempatkannya berdampingan.
import pandas as pd
suhu = pd.DataFrame({
"suhu_c": [23.4, 24.1, 22.8, 25.0, 23.9, 24.6],
}, index=["07:00", "07:15", "07:30", "07:45", "08:00", "08:15"])
lembap = pd.DataFrame({
"lembap_pct": [58.2, 61.3, 63.8, 57.5, 66.1, 60.4],
}, index=["07:00", "07:15", "07:30", "07:45", "08:00", "08:15"])
sanding = pd.concat([suhu, lembap], axis=1)
print(sanding) suhu_c lembap_pct
07:00 23.4 58.2
07:15 24.1 61.3
07:30 22.8 63.8
07:45 25.0 57.5
08:00 23.9 66.1
08:15 24.6 60.4Hasilnya adalah tabel berenam baris dengan dua kolom, karena setiap label jam menemukan pasangannya pada kedua tabel. Apabila label kedua tabel tidak sama persis, pd.concat secara default tetap mempertahankan semua label dan mengisi bagian yang tidak berpasangan dengan NaN. Parameter join="inner" membatasi hasil hanya pada label yang dimiliki kedua tabel, sebagaimana diuji blok berikut.
import pandas as pd
suhu = pd.DataFrame({
"suhu_c": [23.4, 24.1, 22.8, 25.0, 23.9, 24.6],
}, index=["07:00", "07:15", "07:30", "07:45", "08:00", "08:15"])
lembap = pd.DataFrame({
"lembap_pct": [58.2, 61.3, 63.8, 57.5, 66.1, 60.4],
}, index=["07:00", "07:15", "07:30", "07:45", "08:00", "08:15"])
lembap_sebagian = lembap.iloc[2:]
sanding_uji = pd.concat([suhu, lembap_sebagian], axis=1, join="inner")
print(sanding_uji) suhu_c lembap_pct
07:30 22.8 63.8
07:45 25.0 57.5
08:00 23.9 66.1
08:15 24.6 60.4Dua label pertama hilang karena hanya dimiliki tabel suhu, sehingga hasilnya menyusut menjadi empat baris. Pilihan antara outer dan inner pada axis=1 menentukan apakah label yang tidak berpasangan dipertahankan sebagai baris kosong atau dibuang. Perilaku serupa berlaku pada penumpukan baris axis=0, yaitu kolom yang hanya dimiliki salah satu tabel tetap muncul dan diisi NaN.
Kesalahan umum#
Kesalahan yang paling sering pada pd.concat adalah menumpuk tabel yang kolomnya tidak sama persis. pd.concat tidak menolak perbedaan itu dan tidak mencetak peringatan, melainkan membentuk kolom gabungan lalu mengisi bagian yang tidak dimiliki salah satu tabel dengan NaN. Blok berikut menumpuk log hari pertama yang kolom lembap_pct-nya sudah dibuang bersama log hari kedua yang utuh.
import pandas as pd
log_hari1 = pd.read_csv("log_hari1.csv", parse_dates=["waktu"])
log_hari2 = pd.read_csv("log_hari2.csv", parse_dates=["waktu"])
log_tanpa_lembap = log_hari1.drop(columns=["lembap_pct"])
log_bedakolom = pd.concat([log_tanpa_lembap, log_hari2])
print(log_bedakolom.shape)
print(log_bedakolom.head(2))
print(log_bedakolom["lembap_pct"].isna().sum())(192, 5)
waktu ruang suhu_c status_sensor lembap_pct
0 2026-01-05 07:00:00 Ruang Arsip 21.5 normal NaN
1 2026-01-05 07:15:00 Lab B 21.8 normal NaN
96Hasil tetap berukuran 192 baris dan lima kolom, tetapi seluruh 96 baris pertama kini memiliki lembap_pct berisi NaN, sebagaimana dihitung baris terakhir keluaran. Kolom seperti itu biasanya bukan kehendak, melainkan efek samping perbedaan kolom yang tidak disadari. Daftar berikut merangkum kesalahan umum beserta perbaikannya.
- Menumpuk tabel yang kolomnya berbeda tanpa disadari. Bandingkan
df.columnskedua tabel sebelum menumpuk, atau terima kolomNaNbila penggabungan itu memang dimaksudkan. - Melupakan
ignore_index=Truesehingga label ganda menular ke seleksilocdan analisis berikutnya. Isi parameter ini setiap kali label asli tidak dibutuhkan. - Mengira
pd.concatmengurutkan data berdasarkan waktu. Urutan baris mengikuti urutan daftar yang diberikan, sehingga log dari beberapa berkas perlu diurutkan ulang dengansort_valuessetelah ditumpuk.
Ringkasan#
pd.concatmenumpuk baris secara default dan menyandingkan kolom denganaxis=1, dengan pencocokan berdasarkan nama kolom atau label index.- Penumpukan tidak menomori ulang index, sehingga label ganda muncul dan
ignore_index=Truediisi setiap kali label asli tidak dibutuhkan. - Kolom atau label yang hanya dimiliki salah satu tabel tetap muncul diisi
NaN, danjoin="inner"membatasi hasil pada yang sama di kedua tabel.
Referensi#
- pandas.concat — pandas documentation
— Dokumentasi resmi parameter
concat, termasukaxis,join, danignore_index. - Merging — pandas user guide
— Panduan resmi penggabungan objek pandas yang mencakup
concat,merge,join, dancombine_first.