concat: Menumpuk Data

Menumpuk baris dan kolom dengan concat, jebakan index, dan parameter ignore_index.

Daftar isi

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.

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.

python Contoh: membangkitkan dua potongan 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))
text Keluaran
(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        normal

Kedua 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.

python Contoh: concat tanpa ignore_index
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]))
text Keluaran
(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
2

Baris 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.

python Contoh: concat dengan ignore_index
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))
text Keluaran
(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        normal

Ujung 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.

python Contoh: menempatkan dua tabel berdampingan dengan axis=1
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)
text Keluaran
       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.4

Hasilnya 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.

python Contoh: join inner pada axis=1
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)
text Keluaran
       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.4

Dua 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.

python Contoh: menumpuk tabel dengan kolom berbeda
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())
text Keluaran
(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
96

Hasil 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.columns kedua tabel sebelum menumpuk, atau terima kolom NaN bila penggabungan itu memang dimaksudkan.
  • Melupakan ignore_index=True sehingga label ganda menular ke seleksi loc dan analisis berikutnya. Isi parameter ini setiap kali label asli tidak dibutuhkan.
  • Mengira pd.concat mengurutkan data berdasarkan waktu. Urutan baris mengikuti urutan daftar yang diberikan, sehingga log dari beberapa berkas perlu diurutkan ulang dengan sort_values setelah ditumpuk.

Ringkasan#

  • pd.concat menumpuk baris secara default dan menyandingkan kolom dengan axis=1, dengan pencocokan berdasarkan nama kolom atau label index.
  • Penumpukan tidak menomori ulang index, sehingga label ganda muncul dan ignore_index=True diisi setiap kali label asli tidak dibutuhkan.
  • Kolom atau label yang hanya dimiliki salah satu tabel tetap muncul diisi NaN, dan join="inner" membatasi hasil pada yang sama di kedua tabel.

Referensi#