Tabel per produk, kategori, dan kota pada Bagian 3 menjawab pertanyaan kinerja, tetapi manajemen Toko Sembako Berkah biasanya lebih dulu bertanya tentang waktu: bagaimana perkembangan penjualan bulan demi bulan. Pada bagian terakhir proyek ini kita merangkum 234 transaksi menjadi tiga angka bulanan dengan resample(), menyusun matriks kategori per bulan dengan pivot_table(), mengekspor kedua ringkasan ke berkas CSV, lalu mengubah seluruh angka proyek menjadi narasi singkat yang siap dibacakan. Setelah itu proyek Analisis Penjualan tuntas dan kita siap melangkah ke proyek berikutnya.

Ringkasan Bulanan dengan resample #

Agregasi per bulan membutuhkan kolom tanggal bertipe datetime — hasil kerja Bagian 2 yang kini terbayar. Kolom tanggal dijadikan index dengan set_index(), lalu resample("ME") membagi data per akhir bulan sebelum fungsi agg() menghitung omzet dan jumlah transaksi tiap kelompok. Pola ini persis materi Resampling Data pada section Data Waktu dan Deret Waktu.

python
"""Membuat ringkasan bulanan, pivot_table, dan berkas ekspor penjualan."""
from pathlib import Path
import tempfile

import pandas as pd

from buat_dataset import bersihkan, buat_dataset


if __name__ == "__main__":
    with tempfile.TemporaryDirectory() as folder_hasil:
        folder = Path(folder_hasil)

        berkas = folder / "penjualan.csv"
        buat_dataset().to_csv(berkas, index=False)
        df = bersihkan(pd.read_csv(berkas))

        df["omzet"] = df["jumlah"] * df["harga"]
        df["bulan"] = df["tanggal"].dt.strftime("%Y-%m")

        bulanan = (
            df.set_index("tanggal")
            .resample("ME")
            .agg(omzet=("omzet", "sum"), transaksi=("id_transaksi", "count"))
        )
        bulanan.index = bulanan.index.strftime("%Y-%m")
        print("== Ringkasan bulanan (resample) ==")
        print(bulanan.to_string())

        pivot = pd.pivot_table(
            df, index="kategori", columns="bulan", values="omzet",
            aggfunc="sum", margins=True, margins_name="Total",
        )
        print("\n== Omzet kategori per bulan (pivot_table + margins) ==")
        print(pivot.to_string())

        berkas_bulanan = folder / "ringkasan_bulanan.csv"
        berkas_kategori = folder / "ringkasan_kategori.csv"
        bulanan.to_csv(berkas_bulanan, index_label="bulan")
        pivot.to_csv(berkas_kategori)
        baca_bulanan = pd.read_csv(berkas_bulanan)
        baca_kategori = pd.read_csv(berkas_kategori, index_col=0)
        print("\n== Ekspor ringkasan ke CSV ==")
        print(f"ringkasan_bulanan.csv  : {len(baca_bulanan)} baris, kolom {list(baca_bulanan.columns)}")
        print(
            f"ringkasan_kategori.csv : {baca_kategori.shape[0]} baris x "
            f"{baca_kategori.shape[1]} kolom"
        )

        kategori_terbesar = pivot.drop(index="Total")["Total"].idxmax()
        per_kota = df.groupby("kota", as_index=False)["omzet"].sum()
        kota_terbesar = per_kota.loc[per_kota["omzet"].idxmax(), "kota"]
        perubahan = (
            (bulanan["omzet"].iloc[-1] - bulanan["omzet"].iloc[0])
            / bulanan["omzet"].iloc[0] * 100
        ).round(1)
        print("\n== Temuan utama ==")
        print(f"total omzet 3 bulan : {int(bulanan['omzet'].sum())}")
        print(
            f"bulan terbaik       : {bulanan['omzet'].idxmax()} "
            f"(omzet {int(bulanan['omzet'].max())})"
        )
        print(
            f"kategori terbesar   : {kategori_terbesar} "
            f"(omzet {int(pivot.loc[kategori_terbesar, 'Total'])})"
        )
        print(
            f"kota terbesar       : {kota_terbesar} "
            f"(omzet {int(per_kota['omzet'].max())})"
        )
        print(f"tren Januari-Maret  : {perubahan}%")

Output yang Dihasilkan:

text
== Ringkasan bulanan (resample) ==
           omzet  transaksi
tanggal                    
2025-01  9878500         92
2025-02  7363500         63
2025-03  6425500         79

== Omzet kategori per bulan (pivot_table + margins) ==
bulan        2025-01  2025-02  2025-03     Total
kategori                                        
Bahan Pokok  5190500  4509000  3512000  13211500
Kebersihan    629000   536500   351500   1517000
Minuman      3604500  1986500  2104500   7695500
Perawatan     454500   331500   457500   1243500
Total        9878500  7363500  6425500  23667500

== Ekspor ringkasan ke CSV ==
ringkasan_bulanan.csv  : 3 baris, kolom ['bulan', 'omzet', 'transaksi']
ringkasan_kategori.csv : 5 baris x 4 kolom

== Temuan utama ==
total omzet 3 bulan : 23667500
bulan terbaik       : 2025-01 (omzet 9878500)
kategori terbesar   : Bahan Pokok (omzet 13211500)
kota terbesar       : Surabaya (omzet 6649500)
tren Januari-Maret  : -35.0%

Tiga hal di blok kode perlu penjelasan:

  1. resample("ME"): alias ME berarti month end. Ini penanda wajib pada pandas 3.0 — alias lama "M" sudah dihapus dan menimbulkan ValueError — sehingga tabel bulanan berakhir pada 31 Januari, 28 Februari, dan 31 Maret.
  2. .agg(omzet=..., transaksi=...): named aggregation yang sama seperti pada Bagian 3, kini diterapkan pada objek resampler; hasilnya DataFrame dengan index bertipe datetime.
  3. bulanan.index.strftime("%Y-%m"): index datetime diganti label 2025-01 dan seterusnya agar ringkasan terbaca seperti laporan, bukan tanggal teknis akhir bulan.

Tabel bulanannya ringkas: Januari 9878500 dengan 92 transaksi, Februari 7363500 dengan 63 transaksi, Maret 6425500 dengan 79 transaksi.

Matriks Kategori per Bulan dengan pivot_table #

Satu angka per bulan belum cukup bila kita ingin tahu kategori mana yang menggerakkan naik-turunnya. pivot_table() menjawabnya: index="kategori" sebagai baris, columns="bulan" (kolom bantu %Y-%m yang dibuat di awal skrip), values="omzet", dan aggfunc="sum". Argumen margins=True dengan margins_name="Total" menambahkan baris dan kolom total, sehingga pembaca langsung melihat bahwa Bahan Pokok menyumbang 13211500 dari total 23667500 tanpa menghitung manual.

Matriks ini juga menunjukkan arah per kategori: omzet Bahan Pokok turun dari 5190500 pada Januari ke 3512000 pada Maret, sementara Perawatan justru berakhir lebih tinggi (457500) dari awalnya (454500).

Mengekspor dan Memeriksa Ringkasan #

Ringkasan baru berguna bila bisa keluar dari Python. bulanan.to_csv() menulis ringkasan_bulanan.csv dengan index_label="bulan" agar kolom index punya nama, dan pivot.to_csv() menulis ringkasan_kategori.csv. Kedua berkas langsung dibaca ulang dengan read_csv() untuk memastikan isinya utuh: tabel bulanan kembali dengan 3 baris dan kolom bulan, omzet, transaksi, sedangkan matriks kategori kembali berbentuk 5 baris x 4 kolom. Semua berkas ditulis di dalam TemporaryDirectory, pola yang sudah Anda pakai sejak Bagian 1; pada pekerjaan nyata, ganti path dengan folder laporan Anda.

Menyusun Narasi Temuan #

Bagian akhir skrip mencetak temuan utama yang disarikan dari seluruh proyek: total omzet tiga bulan 23667500, bulan terbaik Januari 2025 dengan omzet 9878500, kategori terbesar Bahan Pokok, kota terbesar Surabaya dengan 6649500, dan tren Januari-Maret yang turun 35.0 persen. Angka-angka ini dirangkai menjadi kalimat siap lapor: omzet dipimpin Bahan Pokok dengan margin terendah (23,2 persen) sesuai temuan Bagian 3, kategori bernilai kecil seperti Perawatan membawa margin tertinggi (29,2 persen), dan penurunan 35 persen dari Januari ke Maret adalah sinyal yang layak ditindaklanjuti dengan promosi kategori bermargin tinggi.

Proyek Analisis Penjualan di sini tuntas: satu berkas CSV mentah dengan lima masalah data berubah menjadi dataset bersih 234 baris, tabel kinerja produk dan kota, serta ringkasan bulanan yang terekspor. Skill yang Anda pakai — memuat CSV, pembersihan bertahap, groupby(), merge(), resample(), pivot_table() — adalah bekal yang sama untuk proyek berikutnya, Proyek: Analisis Harga Komoditas. Di sana kita mulai dari awal lagi dengan dataset harga komoditas harian: Bagian 1 mengimpor data harga, membersihkannya di Bagian 2, menghitung perubahan harga di Bagian 3, lalu membandingkan antar komoditas di Bagian 4.

Sumber bacaan resmi: pandas.DataFrame.resample , pandas.pivot_table , dan pandas.DataFrame.to_csv .