Chapter-chapter pada section Data Waktu dan Deret Waktu sudah membedah tiap jendela satu per satu: rolling() untuk statistik bergerak, expanding() untuk statistik kumulatif, dan ewm() untuk bobot eksponensial. Chapter ini merangkainya kembali sebagai satu sistem pemilihan: ketiganya disandingkan pada data yang sama agar perbedaan karakternya terlihat jelas, lalu melengkapi perangkat itu dengan kemampuan yang belum dibahas sebelumnya, yaitu memasang fungsi kustom di dalam jendela lewat apply() beserta parameter raw. Data contohnya sepuluh hari penjualan harian dalam ribu rupiah.

Tiga Keluarga Window pada Data yang Sama #

Script berikut membangun satu Series penjualan, lalu menghitung rata-rata dengan ketiga jenis window dan menyandingkannya dalam satu DataFrame.

python
import pandas as pd

penjualan = pd.Series(
    [120, 135, 150, 90, 180, 200, 210, 130, 145, 160],
    index=pd.date_range("2026-03-02", periods=10, freq="D", name="tanggal"),
    name="penjualan",
)

# tiga keluarga window pada data yang sama, disandingkan
ringkas = pd.DataFrame({
    "rolling3": penjualan.rolling(window=3).mean(),
    "expanding": penjualan.expanding().mean(),
    "ewm_span3": penjualan.ewm(span=3).mean(),
})
print(ringkas.round(1))
print()

# fungsi kustom di dalam jendela: rentang max-min per jendela
def rentang(arr):
    return arr.max() - arr.min()

print(penjualan.rolling(window=3).apply(rentang, raw=True))
print()

# raw=True mengirim ndarray, raw=False mengirim Series
tipe_argumen = []

def catat_tipe(arr):
    tipe_argumen.append(type(arr).__name__)
    return arr.max() - arr.min()

penjualan.rolling(window=3).apply(catat_tipe, raw=True)
print("raw=True  :", tipe_argumen[0])
tipe_argumen.clear()
penjualan.rolling(window=3).apply(catat_tipe, raw=False)
print("raw=False :", tipe_argumen[0])
print()

# hasil raw=True dan raw=False identik; bedanya hanya performa
a = penjualan.rolling(window=3).apply(rentang, raw=True)
b = penjualan.rolling(window=3).apply(rentang, raw=False)
print("hasil identik:", a.equals(b))
print()

# min_periods=1: fungsi kustom mulai sejak jendela belum penuh
print(penjualan.rolling(window=3, min_periods=1).apply(rentang, raw=True))

# groupby().rolling(): jendela dihitung per kelompok, bukan per kolom
df = pd.DataFrame(
    {
        "toko": ["Gambir", "Gambir", "Gambir", "Cihampelas", "Cihampelas", "Cihampelas"],
        "omzet": [100, 120, 140, 200, 190, 210],
    }
)
per_toko = df.groupby("toko").rolling(window=2, on="omzet")["omzet"].mean()
print()
print(per_toko.round(1))

Output yang Dihasilkan:

text
            rolling3  expanding  ewm_span3
tanggal                                   
2026-03-02       NaN      120.0      120.0
2026-03-03       NaN      127.5      130.0
2026-03-04     135.0      135.0      141.4
2026-03-05     125.0      123.8      114.0
2026-03-06     140.0      135.0      148.1
2026-03-07     156.7      145.8      174.4
2026-03-08     196.7      155.0      192.4
2026-03-09     180.0      151.9      161.1
2026-03-10     161.7      151.1      153.0
2026-03-11     145.0      152.0      156.5

tanggal
2026-03-02      NaN
2026-03-03      NaN
2026-03-04     30.0
2026-03-05     60.0
2026-03-06     90.0
2026-03-07    110.0
2026-03-08     30.0
2026-03-09     80.0
2026-03-10     80.0
2026-03-11     30.0
Freq: D, Name: penjualan, dtype: float64

raw=True  : ndarray
raw=False : Series

hasil identik: True

tanggal
2026-03-02      0.0
2026-03-03     15.0
2026-03-04     30.0
2026-03-05     60.0
2026-03-06     90.0
2026-03-07    110.0
2026-03-08     30.0
2026-03-09     80.0
2026-03-10     80.0
2026-03-11     30.0
Freq: D, Name: penjualan, dtype: float64

toko        omzet
Cihampelas  200        NaN
            190      195.0
            210      200.0
Gambir      100        NaN
            120      110.0
            140      130.0
Name: omzet, dtype: float64

Mari kita bedah bagian pentingnya:

  1. Baris 10-14 (penyandingan): ketiga hasil menjadi kolom satu DataFrame; karena semua index bertanggal, penyandingan berjalan otomatis tanpa reindex.
  2. Kolom rolling3: dua baris pertama NaN karena jendela 3 hari belum penuh — perilaku min_periods default yang sama dengan ukuran jendela.
  3. Kolom expanding: terisi sejak baris pertama karena jendelanya tumbuh dari satu observasi; nilai baris terakhir 152.0 adalah rata-rata seluruh data.
  4. Kolom ewm_span3: bereaksi paling cepat; pada baris 2026-03-05 yang turun ke 90, nilainya langsung jatuh ke 114.0, jauh di bawah rolling3 yang masih 125.0.

Memilih Window yang Tepat #

Tabel berikut merangkum kapan masing-masing window paling pantas dipakai.

Aspekrolling()expanding()ewm()
Ukuran jendelatetap, argumen windowtumbuh sampai seluruh datatidak berujung, bobot menurun
Bobot observasisama rata di dalam jendelasama rataterbesar pada data terbaru
Baris awalNaN sampai min_periods terpenuhiterisi sejak min_periods (default 1)terisi sejak baris pertama
Kasus khasmoving average, volatilitas bergerakrekor, rata-rata sejak awaltren yang harus responsif

Aturan praktisnya: pilih rolling() bila definisi statistik memang mengikat periode tertentu (misalnya “rata-rata 7 hari”), expanding() bila pertanyaannya “sejauh ini”, dan ewm() bila data terbaru lebih bermakna daripada data lama. Parameter span dan alpha pada ewm() serta mode adjust sudah dibahas pada chapter Exponentially Weighted Window; di sini cukup span=3 yang berarti alpha = 2/(span+1) = 0.5.

Fungsi Kustom di Dalam Jendela #

Statistik bawaan seperti mean() dan std() tidak mencakup semua kebutuhan; apply() menerima fungsi apa pun dan menjalankannya per jendela. Baris 19-22 mendefinisikan rentang (selisih maksimum–minimum) dan menerapkannya dengan raw=True. Parameter raw menentukan tipe argumen yang diterima fungsi: raw=True mengirim array NumPy mentah, raw=False mengirim Series lengkap dengan index. Blok catat_tipe membuktikannya lewat output ndarray dan Series, sedangkan blok terakhir menegaskan bahwa keduanya menghasilkan angka yang identik. Pilih raw=True bila fungsi hanya berhitung dengan nilai; pilih raw=False bila label index dibutuhkan, misalnya untuk mencatat tanggal akhir jendela.

Mengatur Awal Perhitungan dengan min_periods #

Blok terakhir memakai min_periods=1 pada apply(): fungsi kustom mulai dipanggil sejak jendela pertama walau isinya baru satu nilai, sehingga dua baris awal berisi 0.0 dan 15.0 alih-alih NaN. Nilai default-nya sama dengan ukuran jendela, jadi setiap kali Anda melihat kepala hasil rolling yang kosong, itu pilihan perhitungan — bukan kecacatan data.

Jendela juga bisa berjalan per kelompok: df.groupby("toko").rolling(window=2) menghitung rata-rata bergerak dalam batas masing-masing toko, sehingga jendela tidak menyeret angka dari toko lain. Pola groupby().rolling() ini adalah penyambung langsung antara section Groupby dan section ini.

Dengan ketiga window dan fungsi kustomnya dalam genggaman, perangkat analisis kita sudah lengkap untuk pertanyaan berbasis waktu. Chapter berikutnya beralih ke persoalan skala: menulis ekspresi seleksi dan aritmetika yang ringkas dengan query() dan eval(), dua fasilitas yang berkilau ketika DataFrame membesar.

Referensi resmi untuk chapter ini: user guide Windowing operations , pandas.DataFrame.rolling , dan pandas.DataFrame.ewm .