Dasar deret waktu

Membaca tren dan musiman pada deret waktu serta menghitung jendela bergulir dengan pandas.

Daftar isi

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.

python Menyiapkan catatan peminjaman harian
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())
text Keluaran
     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          41

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

python Meringkas peminjaman harian menjadi mingguan dan bulanan
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))
text Keluaran
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: float64

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

python Menghitung rata-rata bergulir tujuh hari
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))
text Keluaran
            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.142857

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

python Menggambar peminjaman harian beserta rata-rata bergulirnya
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")
text Keluaran
Gambar tersimpan sebagai tren-peminjaman.png

Peminjaman harian dan rata-rata bergulir 7 hari pada grafik garis
Peminjaman harian dan rata-rata bergulir 7 hari pada grafik garis

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_dates dan set_index agar resample dapat dipanggil.
  • resample mengubah frekuensi deret waktu, dengan konstanta W menunjuk akhir pekan dan ME akhir bulan, serta fungsi ringkasan seperti mean atau sum.
  • rolling(7).mean() menghitung rata-rata jendela bergulir dan menghasilkan NaN selama jendelanya belum penuh, dengan min_periods sebagai penurun syaratnya.
  • Grafik garis memisahkan musiman mingguan pada garis harian dari tren naik pada garis rata-rata bergulir.

Referensi#