Data deret waktu hasil rekap jarang sempurna: toko tutup sementara, mesin kasir gagal merekam, atau unggahan rekap tertinggal beberapa hari. Index datetime yang bolong membuat perhitungan antarperiode menyesatkan — selisih “satu baris” bisa berarti jarak tiga hari kalender, dan rolling(7) menghitung tujuh catatan yang sebenarnya membentang lebih lama. Sebelum menganalisis, kita perlu mendeteksi tanggal yang hilang lalu memilih cara mengisinya. Chapter ini membahas difference() untuk deteksi, reindex dan asfreq untuk merenggangkan kalender, serta interpolate() dan ffill() untuk mengisi nilai yang kosong.
Menemukan Tanggal yang Hilang #
Cara termudah membandingkan index tercatat dengan kalender lengkap yang diinginkan: buat date_range() harian, lalu panggil difference() untuk mendapatkan tanggal yang ada di kalender tetapi tidak ada di index data. Setelah diketahui, reindex(kalender) memasukkan baris-baris baru itu dengan nilai NaN; dtype kolom ikut naik ke float64 karena kini menampung nilai kosong.
import pandas as pd
kalender = pd.date_range("2026-03-01", "2026-03-08", freq="D")
tanggal_tercatat = pd.to_datetime(
["2026-03-01", "2026-03-02", "2026-03-05", "2026-03-06", "2026-03-08"]
)
penjualan = pd.Series(
[398, 320, 326, 328, 412], index=tanggal_tercatat, name="penjualan"
)
print("Data penjualan yang tercatat:")
print(penjualan)
hilang = kalender.difference(penjualan.index)
print("\nTanggal yang hilang:")
print(hilang)
kembali = penjualan.reindex(kalender)
print("\nreindex ke kalender lengkap menambah baris NaN:")
print(kembali)
print("\nasfreq('D', fill_value=0) mengisi langsung dengan nol:")
print(penjualan.asfreq("D", fill_value=0))Output yang Dihasilkan:
Data penjualan yang tercatat:
2026-03-01 398
2026-03-02 320
2026-03-05 326
2026-03-06 328
2026-03-08 412
Name: penjualan, dtype: int64
Tanggal yang hilang:
DatetimeIndex(['2026-03-03', '2026-03-04', '2026-03-07'], dtype='datetime64[us]', freq=None)
reindex ke kalender lengkap menambah baris NaN:
2026-03-01 398.0
2026-03-02 320.0
2026-03-03 NaN
2026-03-04 NaN
2026-03-05 326.0
2026-03-06 328.0
2026-03-07 NaN
2026-03-08 412.0
Freq: D, Name: penjualan, dtype: float64
asfreq('D', fill_value=0) mengisi langsung dengan nol:
2026-03-01 398
2026-03-02 320
2026-03-03 0
2026-03-04 0
2026-03-05 326
2026-03-06 328
2026-03-07 0
2026-03-08 412
Freq: D, Name: penjualan, dtype: int64Mari kita telaah baris-baris kuncinya:
- Baris 3-6: kalender lengkap 1-8 Maret 2026 dibangun dengan
date_range(freq="D"), lalupd.to_datetimemenyusun daftar tanggal yang benar-benar tercatat — hanya lima dari delapan. - Baris 7-9 (
pd.Series): nilai penjualan ditempelkan pada tanggal tercatat sehingga index-nya bolong. - Baris 13 (
difference):kalender.difference(penjualan.index)menghasilkan2026-03-03,2026-03-04, dan2026-03-07sebagai tanggal yang hilang. - Baris 17 (
reindex): index data disesuaikan dengan kalender lengkap; baris baru muncul denganNaN, dan output menunjukkan398.0dst berubah menjadifloat64. - Baris 21 (
asfreq):asfreq("D", fill_value=0)merenggangkan index ke harian sekaligus mengisi baris baru dengan nol tanpa tahap terpisah.
Merenggangkan Kalender dengan asfreq #
asfreq() adalah konversi frekuensi langsung pada index datetime: satu panggilan mengubah index menjadi harian dan menyisakan NaN pada posisi yang tidak punya data. Parameter fill_value mengganti NaN tersebut, dan perhatikan pada output bahwa dtype tetap int64 karena tidak ada nilai kosong yang tersisa. Untuk perenggangan sederhana, asfreq("D") setara dengan reindex ke kalender harian; pilih asfreq bila yang dibutuhkan sekadar perubahan granularitas index, dan reindex bila target index-nya bentuk apa pun yang Anda tentukan sendiri. Opsi pengisian lain juga tersedia, misalnya method="ffill" yang menyalin nilai terakhir secara langsung.
Mengisi Nilai dengan interpolate() dan ffill() #
Baris NaN yang sudah ada sekarang perlu diisi, dan pilihan isinya menentukan makna angka hasil analisis. interpolate() menghitung nilai tengah secara linear antara dua titik yang diketahui, sedangkan ffill() menyalin nilai terakhir yang diketahui ke depan. File berikut memakai ulang data Maret kita, kini lengkap dengan lubangnya:
import pandas as pd
kalender = pd.date_range("2026-03-01", "2026-03-08", freq="D")
penjualan = pd.Series(
[398, 320, None, None, 326, 328, None, 412], index=kalender, name="penjualan"
)
print("interpolate() mengisi NaN secara linear:")
print(penjualan.interpolate())
print("\nffill() menyalin nilai terakhir yang diketahui:")
print(penjualan.ffill())
stempel = pd.to_datetime(
["2026-03-02 09:00", "2026-03-02 10:00", "2026-03-02 12:30", "2026-03-02 13:00"]
)
pengunjung = pd.Series(
[100, None, None, 140], index=stempel, name="pengunjung"
)
print("\nStempel jam tidak beraturan, linear vs time:")
print("linear (posisi baris):")
print(pengunjung.interpolate().round(1))
print("time (jarak waktu):")
print(pengunjung.interpolate(method="time").round(1))Output yang Dihasilkan:
interpolate() mengisi NaN secara linear:
2026-03-01 398.0
2026-03-02 320.0
2026-03-03 322.0
2026-03-04 324.0
2026-03-05 326.0
2026-03-06 328.0
2026-03-07 370.0
2026-03-08 412.0
Freq: D, Name: penjualan, dtype: float64
ffill() menyalin nilai terakhir yang diketahui:
2026-03-01 398.0
2026-03-02 320.0
2026-03-03 320.0
2026-03-04 320.0
2026-03-05 326.0
2026-03-06 328.0
2026-03-07 328.0
2026-03-08 412.0
Freq: D, Name: penjualan, dtype: float64
Stempel jam tidak beraturan, linear vs time:
linear (posisi baris):
2026-03-02 09:00:00 100.0
2026-03-02 10:00:00 113.3
2026-03-02 12:30:00 126.7
2026-03-02 13:00:00 140.0
Name: pengunjung, dtype: float64
time (jarak waktu):
2026-03-02 09:00:00 100.0
2026-03-02 10:00:00 110.0
2026-03-02 12:30:00 135.0
2026-03-02 13:00:00 140.0
Name: pengunjung, dtype: float64Hasil interpolasi mengisi 2026-03-03 dan 2026-03-04 dengan 322.0 dan 324.0, tepat di tengah lintasan 320 menuju 326, lalu 2026-03-07 dengan 370.0. Versi ffill() menghasilkan 320.0, 320.0, dan 328.0 — nilai lama disalin apa adanya. Untuk deret yang bergerak landai, interpolasi biasanya lebih dekat ke kenyataan; untuk besaran yang bertahan lama seperti stok, ffill() lebih jujur.
Linear Berbasis Posisi vs Berbasis Waktu #
Bagian akhir skrip membedah asumsi tersembunyi interpolate() default. Method ini menginterpolasi berdasarkan posisi baris, sehingga 2026-03-02 10:00 dianggap sama jauhnya dari tetangganya — hasilnya 113.3, padahal jarak waktunya hanya satu jam dari 09:00 dan dua setengah jam menuju 12:30. Dengan method="time", pandas menghitung berdasarkan jarak stempel waktu dan menghasilkan 110.0 serta 135.0. Pada kalender harian yang seragam seperti data Maret, kedua method memberi hasil identik; perbedaannya baru terasa saat stempel waktu tidak beraturan, dan saat itu method="time" adalah pilihan yang lebih tepat.
Dokumentasi pandas.DataFrame.asfreq , pandas.DataFrame.reindex , dan pandas.Series.interpolate merinci parameter perenggangan dan pengisian yang dibahas di sini. Kalender lengkap dan nilai terisi membuat seluruh alat pada section ini bekerja sebagaimana mestinya. Chapter berikutnya menyatukan semuanya menjadi satu alur analisis deret waktu yang utuh, dari resampling sampai visualisasi tren.