Pertanyaan bisnis jarang berhenti pada granularitas data: data penjualan tercatat harian, tetapi manajemen bertanya total per bulan; sebaliknya, rencana kebutuhan disusun harian sementara datanya mingguan. DataFrame.resample() menjawab kedua arah tersebut dengan bekerja seperti groupby() yang kelompoknya dibentuk otomatis dari index waktu. Chapter ini mempraktikkan downsampling (harian menjadi mingguan dan bulanan) beserta pemilihan fungsi agregasinya, lalu upsampling (mingguan kembali ke harian) beserta cara mengisi baris-baris baru yang muncul. Seluruh contoh tetap memakai dataset penjualan 90 hari Toko Sembako Berkah.

Downsampling: Harian Menjadi Bulanan #

Baris 8 memanggil df.resample("ME").sum(): alias "ME" berarti Month End, sehingga 90 baris harian dikelompokkan ke tiga bulan dan dijumlahkan. Hasilnya di blok output pertama: Januari 6570, Februari 6720, dan Maret 8330. Angka-angka ini bisa diperiksa manual; Februari 2026 berisi tepat empat minggu penuh dengan total mingguan 1470, ditambah kenaikan bulanan 30 per hari. Label barisnya adalah tanggal akhir bulan (2026-01-31, 2026-02-28, 2026-03-31) karena kelompok dilabeli di ujung periode.

python
import pandas as pd

tanggal = pd.date_range("2026-01-01", "2026-03-31", freq="D", name="tanggal")
penjualan = [150 + t.dayofweek * 20 + (t.month - 1) * 30 for t in tanggal]
df = pd.DataFrame({"penjualan": penjualan}, index=tanggal)

# Downsampling: harian (90 baris) menjadi bulanan (3 baris)
bulanan = df.resample("ME").sum()
print(bulanan)

# Beberapa agregasi sekaligus lewat agg
ringkas = df.resample("ME").agg({"penjualan": ["sum", "mean", "max"]}).round(2)
print()
print(ringkas)

# Downsampling ke mingguan; 4 kelompok pertama
mingguan = df.resample("W").sum()
print()
print(mingguan.head(4))

# Upsampling: mingguan kembali ke harian dengan asfreq (muncul NaN)
print("\nasfreq:")
print(mingguan.resample("D").asfreq().head(4))

# Upsampling dengan pengisian maju (forward fill)
print("ffill:")
print(mingguan.resample("D").ffill().head(4))

Output yang Dihasilkan:

text
            penjualan
tanggal              
2026-01-31       6570
2026-02-28       6720
2026-03-31       8330

           penjualan             
                 sum    mean  max
tanggal                          
2026-01-31      6570  211.94  270
2026-02-28      6720  240.00  300
2026-03-31      8330  268.71  330

            penjualan
tanggal              
2026-01-04        960
2026-01-11       1470
2026-01-18       1470
2026-01-25       1470

asfreq:
            penjualan
tanggal              
2026-01-04      960.0
2026-01-05        NaN
2026-01-06        NaN
2026-01-07        NaN
ffill:
            penjualan
tanggal              
2026-01-04        960
2026-01-05        960
2026-01-06        960
2026-01-07        960

Mari kita telaah bagian-bagian penting skrip di atas:

  1. Baris 8-9: df.resample("ME").sum() menghasilkan tiga baris bulanan; resample mengembalikan objek perantara, dan agregasi seperti .sum() baru menghasilkan DataFrame.
  2. Baris 12-14: agg dengan dictionary meminta tiga statistik sekaligus untuk kolom penjualan.
  3. Baris 17-19: downsampling mingguan dengan alias "W"; empat kelompok pertama ditampilkan.
  4. Baris 22-27: upsampling mingguan ke harian, pertama dengan .asfreq() (muncul NaN), lalu dengan .ffill() (nilai digandakan maju).

Memilih Agregasi yang Tepat #

Blok kedua pada output memperlihatkan agg({"penjualan": ["sum", "mean", "max"]}): total, rata-rata, dan nilai tertinggi tiap bulan dalam satu panggilan. Rata-rata Februari tampil persis 240.00 karena bulan itu berisi siklus mingguan utuh; rata-rata Januari 211.94 sedikit lebih rendah dan Maret 268.71 lebih tinggi karena pergeseran hari awal bulan. Pemilihan agregasi menentukan makna angka: .sum() untuk volume total, .mean() untuk intensitas harian, .max() untuk puncak, dan .last() untuk kondisi akhir periode. Fungsi agregasi apa pun yang berlaku pada groupby() juga diterima di sini.

Memahami Label pada Downsampling Mingguan #

Blok ketiga memakai alias "W" yang kelompoknya ter-anchored di hari Minggu. Perhatikan kelompok pertama hanya berisi 4 hari (1-4 Januari, total 960) karena data dimulai hari Kamis, sementara kelompok berikutnya berisi 7 hari penuh (total 1470). Labelnya juga ada di hari Minggu — akhir kelompok — karena untuk frekuensi seperti W dan ME, pandas secara bawaan memakai closed="right", label="right". Jika kita ingin kelompok dilabeli di awal periode, gunakan df.resample("W", label="left"); kombinasi closed dan label inilah yang mengatur tepi kelompok dan posisi labelnya.

Upsampling: Mingguan Kembali ke Harian #

Arah sebaliknya menambah baris, bukan meringkasnya. Baris 23 memakai mingguan.resample("D").asfreq(): setiap hari antara dua label mingguan menjadi baris baru, tetapi hanya tanggal label yang membawa nilai dan sisanya NaN — pandas menolak mengarang angka. Baris 27 memakai .ffill() (forward fill) yang meneruskan nilai minggu terakhir ke hari-hari berikutnya, sehingga angka 960 terulang sepanjang minggu tersebut. Pilihan lainnya adalah .bfill() yang meneruskan nilai dari belakang; keduanya adalah keputusan analitis yang perlu dijelaskan saat melaporkan data.

Dokumentasi resmi yang relevan: pandas.DataFrame.resample , User Guide: Time series , dan What’s new in pandas 3.0.0 .

Resample memakai string seperti "ME", "W", dan "D" sebagai frekuensi. Chapter berikutnya mengatasi keterbatasan pertama resample(): mengelompokkan data berbasis waktu lewat pd.Grouper saat tanggal masih berupa kolom biasa dan kelompok kategori perlu ikut dibagi.