resample() pada chapter sebelumnya bekerja sempurna selama tanggal berada di index, tetapi data nyata sering menyimpan tanggal sebagai kolom biasa di samping kolom lain seperti nama toko atau cabang. Menjadikan tanggal index lalu kehilangan kemampuan mengelompokkan berdasarkan toko adalah pilihan yang tidak perlu. pandas menyediakan pd.Grouper untuk situasi ini: objek yang menggambarkan “kelompokkan kolom ini per periode” dan bisa diletakkan di mana saja di dalam groupby(). Chapter ini membahas kegagalan resample() pada kolom biasa, pd.Grouper dengan key, kombinasi Grouper dengan kelompok kategori, serta persamaan hasilnya dengan jalur set_index().

resample() Menuntut Index Tanggal #

Method resample() hanya valid pada DatetimeIndex, TimedeltaIndex, atau PeriodIndex. Saat df.resample("ME") dipanggil pada DataFrame yang indexnya masih RangeIndex, pandas menolaknya dengan TypeError, bukan mengubah index secara diam-diam. Penolakan ini justru berguna: kita tahu persis bahwa jalur resample() bukan jalurnya, tanpa ada efek samping pada data. Contoh berikut membangun DataFrame penjualan dua toko dengan tanggal sebagai kolom biasa, lalu menunjukkan kegagalan tersebut.

python
import pandas as pd

# Kolom tanggal BUKAN index: dua toko, Januari-Maret 2026
baris = []
for toko in ["Gambir", "Cihampelas"]:
    for hari in pd.date_range("2026-01-01", "2026-03-31", freq="D"):
        baris.append(
            {
                "toko": toko,
                "tanggal": hari,
                "omzet": 100_000 + len(baris) * 1_500,
            }
        )
df = pd.DataFrame(baris)
print(df.head(3))
print("\nbentuk data:", df.shape)

# resample() menuntut index tanggal; kolom tanggal biasa tidak bisa
try:
    df.resample("ME").sum()
except TypeError as e:
    print("\nresample() gagal pada kolom biasa:", e)

# pd.Grouper mengelompokkan berdasarkan kolom tanpa mengubah index
bulanan = df.groupby(pd.Grouper(key="tanggal", freq="ME"))["omzet"].sum()
print("\nomzet bulanan seluruh toko:")
print(bulanan)

# Kombinasi toko dan periode bulanan dalam satu groupby
per_toko = (
    df.groupby(["toko", pd.Grouper(key="tanggal", freq="ME")])["omzet"]
    .sum()
)
print("\nomzet bulanan per toko:")
print(per_toko)

# Hasil yang sama bila kolom tanggal dijadikan index terlebih dahulu
per_index = (
    df.set_index("tanggal")
    .groupby(["toko", pd.Grouper(freq="ME")])["omzet"]
    .sum()
)
print("\nhasil Grouper kolom == Grouper index:", per_toko.equals(per_index))

# Frekuensi lain cukup diganti lewat parameter freq
mingguan = df.groupby(pd.Grouper(key="tanggal", freq="W"))["omzet"].sum()
print("\nomzet mingguan (4 kelompok pertama):")
print(mingguan.head(4))

Output yang Dihasilkan:

text
     toko    tanggal   omzet
0  Gambir 2026-01-01  100000
1  Gambir 2026-01-02  101500
2  Gambir 2026-01-03  103000

bentuk data: (180, 3)

resample() gagal pada kolom biasa: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex, but got an instance of 'RangeIndex'

omzet bulanan seluruh toko:
tanggal
2026-01-31    11780000
2026-02-28    13118000
2026-03-31    17267000
Freq: ME, Name: omzet, dtype: int64

omzet bulanan per toko:
toko        tanggal   
Cihampelas  2026-01-31     7982500
            2026-02-28     8449000
            2026-03-31    10726000
Gambir      2026-01-31     3797500
            2026-02-28     4669000
            2026-03-31     6541000
Name: omzet, dtype: int64

hasil Grouper kolom == Grouper index: True

omzet mingguan (4 kelompok pertama):
tanggal
2026-01-04    1358000
2026-01-11    2492000
2026-01-18    2639000
2026-01-25    2786000
Freq: W-SUN, Name: omzet, dtype: int64

Mari kita telaah baris-baris kuncinya:

  1. Baris 4-15: DataFrame 180 baris dibangun dari dua toko yang masing-masing mencatat omzet harian Januari sampai Maret 2026; tanggal adalah kolom biasa, sementara index tetap RangeIndex.
  2. Baris 18-21: panggilan df.resample("ME") dibungkus try/except TypeError, dan pesan galatnya memuat batasan yang sama: hanya index waktu yang boleh di-resample.
  3. Baris 24-25: pd.Grouper(key="tanggal", freq="ME") diteruskan ke groupby() sebagai satu “kunci kelompok”; key="tanggal" menunjuk kolom datetime, dan freq="ME" memotongnya per akhir bulan.
  4. Baris 29-34: Grouper digabung dengan kelompok kategori "toko" dalam satu groupby(), sehingga hasilnya MultiIndex (toko, tanggal) berisi omzet bulanan per toko.
  5. Baris 37-43: jalur pembanding set_index("tanggal") lalu Grouper(freq="ME") tanpa key menghasilkan nilai yang identik, dibuktikan lewat equals() yang mengembalikan True.

Grouper sebagai Kunci Kelompok #

Cara membaca groupby(pd.Grouper(key="tanggal", freq="ME")) adalah: bagi kolom tanggal ke dalam kelompok berjarak satu bulan (sampai akhir bulan), lalu perlakukan setiap kelompok seperti kategori biasa. Karena itu Grouper bisa bercampur dengan kunci lain — ["toko", pd.Grouper(...)] menghasilkan kelompok per toko per bulan sekaligus, sesuatu yang tidak bisa dilakukan resample() tanpa langkah persiapan tambahan. Index pada DataFrame sumber tidak berubah sama sekali; Grouper bekerja saat proses groupby() berjalan, bukan saat data dibaca. Frekuensi yang dipakai sama persis dengan bahasa alias pada resample(): "ME" untuk akhir bulan, "W" untuk mingguan, "D" untuk harian.

Grouper Kolom vs Grouper Index #

Ketika tanggal sudah menjadi index, pd.Grouper(freq="ME") tanpa key otomatis menunjuk index, dan hasilnya identik dengan resample("ME"). Contoh membandingkan kedua jalur dengan equals() dan hasilnya True, yang berarti Grouper bukan pengganti yang berbeda rasa melainkan satu mekanisme yang sama. Aturan praktisnya sederhana: bila tanggal sudah menjadi index dan tidak ada kelompok kategori lain, resample() lebih ringkas; bila tanggal masih kolom atau kelompok kategori ikut dibutuhkan, pd.Grouper(key=...) di dalam groupby() adalah jalurnya. Kedua pilihan menghasilkan kelompok periode dengan batas waktu yang sama persis, sehingga hasil analisis tidak bergantung pada jalur yang dipilih.

Frekuensi mingguan pada contoh menghasilkan 14 kelompok karena 90 hari Januari-Maret 2026 terpotong per pekan; batas pekan mengikuti aturan freq="W" yang berakhir hari Minggu. Proyek penutup course, Analisis Deret Waktu Penjualan, memakai persis pola Grouper ini untuk mengagregasi transaksi harian ke periode bulanan. Chapter berikutnya melanjutkan ke jendela bergerak: rolling() yang menghitung rata-rata dan agregasi lain di atas jendela N baris terakhir.

Dokumentasi resmi yang relevan: pandas.Grouper untuk parameter key, freq, label, dan closed, User Guide: Time series yang menempatkan Grouper berdampingan dengan resample, dan User Guide: Group by untuk aturan campuran kunci kategori dan Grouper.