Catatan yang diukur berulang sepanjang waktu, misalnya peminjaman harian sebuah perpustakaan, menyimpan pola yang tidak terlihat pada tabel biasa, yaitu tren sebagai arah perubahan jangka panjang dan musiman sebagai pola yang berulang pada periode tetap. Topik ini membahas penyusunan data harian menjadi deret waktu, peringkasan dengan resample, penghalusan dengan jendela bergulir, serta pembacaan grafiknya. Setelah topik ini selesai, pertanyaan apakah peminjaman meningkat dari bulan ke bulan dan pada hari apa perpustakaan paling ramai dapat dijawab dari satu berkas CSV.
Data peminjaman harian#
Deret waktu (time series) adalah kumpulan nilai yang setiap satunya terikat pada satu penanda waktu, biasanya berupa tanggal atau jam. Urutan penandanya penting, karena kebanyakan analisis deret waktu membaca perubahan antartitik, bukan sekadar sebarannya. Data contoh topik ini berupa catatan peminjaman harian perpustakaan sekolah selama delapan minggu, dari 1 Juli sampai 25 Agustus 2024. Datanya dibangun dari pola mingguan yang disengaja, yaitu hari sekolah yang ramai, Sabtu yang sedang, dan Minggu yang nyaris kosong, ditambah kenaikan bertahap serta variasi harian acak. Argumen 42 pada pemanggilan default_rng adalah seed-nya, sehingga keluaran yang sama muncul setiap kali blok ini dijalankan.
import numpy as np
import pandas as pd
tanggal = pd.date_range("2024-07-01", periods=56, freq="D")
rng = np.random.default_rng(42)
pola = np.array([28, 34, 31, 35, 42, 24, 2]) # urutan Senin sampai Minggu
tren = np.linspace(0, 10, 56)
jumlah = np.clip(np.tile(pola, 8) + tren + rng.integers(-4, 5, 56), 0, None)
df = pd.DataFrame({"tanggal": tanggal, "peminjaman": jumlah.astype(int)})
df.to_csv("peminjaman-harian.csv", index=False)
print(df.head()) tanggal peminjaman
0 2024-07-01 24
1 2024-07-02 36
2 2024-07-03 32
3 2024-07-04 34
4 2024-07-05 41Berkas peminjaman-harian.csv kini berisi 56 baris dengan dua kolom, dan berkas itu menjadi bahan seluruh blok berikutnya. Lima baris teratasnya bergerak di kisaran dua puluhan dan tiga puluhan, sehingga polanya belum terbaca. Pembacaan pola baru bermakna setelah datanya diberi index waktu dan diringkas pada bagian berikutnya.
Index waktu dan resample#
Deret waktu dikenali pandas apabila kolom tanggalnya menjadi index DataFrame. Pemuatan CSV dengan parse_dates mengubah kolom tanggal menjadi tipe datetime, dan set_index menjadikannya index baris. Setelah index terbentuk, metode resample dapat dipanggil. Resample mengubah frekuensi deret waktu, misalnya dari harian menjadi mingguan, dengan menyatukan banyak nilai ke dalam satu nilai ringkasan seperti mean atau sum.
import pandas as pd
df = pd.read_csv("peminjaman-harian.csv", parse_dates=["tanggal"])
df = df.set_index("tanggal")
mingguan = df["peminjaman"].resample("W").mean()
print(mingguan.round(1))
print()
bulanan = df["peminjaman"].resample("ME").mean()
print(bulanan.round(1))tanggal
2024-07-07 27.7
2024-07-14 29.9
2024-07-21 31.3
2024-07-28 32.7
2024-08-04 32.7
2024-08-11 34.4
2024-08-18 36.7
2024-08-25 36.6
Freq: W-SUN, Name: peminjaman, dtype: float64
tanggal
2024-07-31 30.9
2024-08-31 35.0
Freq: ME, Name: peminjaman, dtype: float64Label mingguan menunjuk hari Minggu sebagai akhir setiap pekan, sehingga label 2024-07-07 memuat rata-rata peminjaman dari 1 sampai 7 Juli. Delapan minggu data menghasilkan delapan angka mingguan yang naik dari sekitar 28 menuju 37, dan dua angka bulanan yang naik dari 31 menuju 35. Angka yang makin besar dari kiri ke kanan inilah bentuk paling sederhana dari sebuah tren. Frekuensi lain cukup diperoleh dengan mengganti konstantanya, misalnya resample("ME") yang mengelompokkan per bulan pada akhir bulannya.
Jendela bergulir dengan rolling#
Jendela bergulir (rolling window) menghitung ukuran ringkasan atas sekelompok baris yang posisinya bergeser satu per satu, misalnya rata-rata dari tujuh hari terakhir untuk setiap tanggal. Jendela tujuh hari dipilih karena menutup satu siklus mingguan penuh, sehingga naik-turunnya hari sekolah dan Minggu saling meniadakan menjadi garis yang lebih halus. Metode rolling(7).mean() menambahkan perhitungan itu sebagai satu kolom pada DataFrame.
import pandas as pd
df = pd.read_csv("peminjaman-harian.csv", parse_dates=["tanggal"])
df = df.set_index("tanggal")
df["rata_bergulir"] = df["peminjaman"].rolling(7).mean()
print(df.head(9)) peminjaman rata_bergulir
tanggal
2024-07-01 24 NaN
2024-07-02 36 NaN
2024-07-03 32 NaN
2024-07-04 34 NaN
2024-07-05 41 NaN
2024-07-06 27 NaN
2024-07-07 0 27.714286
2024-07-08 31 28.714286
2024-07-09 32 28.142857Enam baris pertama pada kolom rata_bergulir berisi NaN, yaitu nilai kosong, karena jendelanya belum memuat tujuh hari penuh. Nilai pertama yang terisi muncul tepat pada 2024-07-07, saat jendelanya telah memuat satu pekan lengkap. Perilaku ini perlu diantisipasi, sebab perhitungan lanjutan yang menyentuh NaN akan menghasilkan NaN juga, dan grafik yang digambar dari kolom ini otomatis dimulai dari hari ketujuh. Argumen min_periods pada rolling dapat menurunkan syaratnya, misalnya rolling(7, min_periods=3) yang mulai menghitung setelah tiga hari tersedia.
Membaca tren dan musiman dari grafik#
Angka pada tabel resample dan kolom rolling bermakna lebih cepat apabila digambar. Grafik garis berikut menampilkan dua deret sekaligus, yaitu peminjaman harian sebagai garis kelabu tipis dan rata-rata bergulir tujuh hari sebagai garis biru. Penyimpanan gambar dilakukan dengan plt.savefig karena lingkungannya tidak memiliki layar.
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd
df = pd.read_csv("peminjaman-harian.csv", parse_dates=["tanggal"])
df = df.set_index("tanggal")
df["rata_bergulir"] = df["peminjaman"].rolling(7).mean()
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(df.index, df["peminjaman"], color="#BBBBBB", label="Harian")
ax.plot(df.index, df["rata_bergulir"], color="#4C72B0", label="Rata-rata bergulir 7 hari")
ax.set_xlabel("Tanggal")
ax.set_ylabel("Jumlah peminjaman")
ax.set_title("Peminjaman harian dan rata-rata bergulir 7 hari")
ax.legend()
plt.savefig("tren-peminjaman.png", dpi=120)
print("Gambar tersimpan sebagai tren-peminjaman.png")Gambar tersimpan sebagai tren-peminjaman.png
Garis kelabu bergerigi tajam dan pola giginya berulang setiap pekan, yaitu tinggi pada hari sekolah, turun pada Sabtu, dan mendekati nol pada Minggu. Pola berulang pada periode tetap seperti inilah yang disebut musiman, dan periodenya tidak harus satu tahun karena di sini periodenya satu pekan. Garis biru memotong gigi tersebut dan menampilkan gerak naik yang landai dari sekitar 28 menuju 37 peminjaman per hari, dan itulah trennya. Bentuk pola pada grafik juga menunjukkan gunanya jendela bergulir, yaitu menyaring gerak harian agar arah jangka panjangnya terbaca. Dua pembacaan ini menjawab dua pertanyaan yang berbeda, yaitu kapan perpustakaan ramai dan apakah minat bacanya meningkat.
Kesalahan umum#
Kesalahan pertama adalah memanggil resample sebelum index waktunya terbentuk. Metode itu menuntut index bertipe datetime, dan pemuatan CSV tanpa parse_dates menjadikan kolom tanggal sebagai kolom biasa, sehingga panggilannya berakhir dengan error TypeError bertuliskan Only valid with DatetimeIndex. Perbaikannya, muat ulang data dengan parse_dates=["tanggal"], lalu jalankan set_index sebelum resample.
Kesalahan kedua adalah menggunakan konstanta "M" untuk resample bulanan pada pandas 2.2. Konstanta itu sudah usang, ditandai dengan pesan FutureWarning, dan akan dihapus pada versi berikutnya. Konstanta penggantinya adalah "ME", yaitu month end, dan perbedaannya terlihat pada label keluaran yang menunjuk akhir bulan.
Kesalahan ketiga adalah membaca naik-turun harian sebagai tren. Angka yang mendekati nol setiap Minggu bukan tanda minat menurun, melainkan efek musiman mingguan yang kembali pada Senin. Kesimpulan tren harus diambil dari garis rata-rata bergulir atau dari angka resample, bukan dari dua titik harian yang berdekatan.
Ringkasan#
- Kolom tanggal diubah menjadi index dengan
parse_datesdanset_indexagarresampledapat dipanggil. resamplemengubah frekuensi deret waktu, dengan konstantaWmenunjuk akhir pekan danMEakhir bulan, serta fungsi ringkasan sepertimeanatausum.rolling(7).mean()menghitung rata-rata jendela bergulir dan menghasilkan NaN selama jendelanya belum penuh, denganmin_periodssebagai penurun syaratnya.- Grafik garis memisahkan musiman mingguan pada garis harian dari tren naik pada garis rata-rata bergulir.
Referensi#
- pandas documentation — Pandas — Dokumentasi resmi pandas yang menjadi rujukan seluruh metodenya.
- pandas user guide — Pandas — Panduan resmi pandas dengan bagian time series untuk index waktu, resample, dan window.
- pandas.DataFrame.rolling — Pandas
— Parameter
rolling, termasukwindowdanmin_periods, beserta perilaku jendelanya.