Proyek pertama kita menerapkan seluruh alur analisis pada data penjualan sebuah toko ritel fiktif: dari memahami masalah bisnis, mengumpulkan dan memvalidasi data, membersihkan berbagai masalah kualitas, mengeksplorasi, menganalisis performa produk dan waktu, hingga menyusun laporan yang siap dibaca pemangku kepentingan. Kita bekerja untuk satu orang bernama Bu Rina, pemilik kafe Toko Kopi Kita di Bandung, yang selama ini menentukan stok dan jadwal pegawai dari ingatan saja. Sepanjang proyek ini kita menggantikan kebiasaan tersebut dengan keputusan berbasis data.
Dataset yang kita olah adalah hasil ekspor aplikasi kasir selama 12 hari operasional (5-16 Januari 2026) untuk 4 menu: kopi susu, matcha latte, americano, dan teh tawar. Data mentahnya sengaja tidak sempurna — ada baris duplikat, sel yang kosong, nama menu yang tidak konsisten, dan satu nilai pendapatan yang salah — karena itulah kondisi yang paling sering kita temui di lapangan. Seluruh proyek dikerjakan dalam sepuluh bagian berurutan, dan setiap bagian menghasilkan keluaran yang bisa Anda jalankan sendiri.
Alur Sepuluh Bagian #
Kerjakan bagian-bagian ini sesuai urutannya karena setiap tahap dibangun di atas tahap sebelumnya:
| Bagian | Fokus tahap |
|---|---|
| Bagian 1: Memahami Masalah | Konteks bisnis Bu Rina, tujuan proyek, dan batasan data |
| Bagian 2: Menentukan Pertanyaan Analisis | Empat pertanyaan terukur beserta metrik pendukungnya |
| Bagian 3: Mengumpulkan Data | Asal-usul data ekspor kasir dan struktur kolomnya |
| Bagian 4: Memvalidasi Data | Pemeriksaan kualitas: duplikat, sel kosong, nama menu, nilai 0 |
| Bagian 5: Membersihkan Data | Perbaikan satu per satu hingga data bersih 48 baris |
| Bagian 6: Eksplorasi Data | Statistik deskriptif, agregasi per menu, per hari, dan akhir pekan |
| Bagian 7: Analisis Penjualan | Menjawab keempat pertanyaan dengan angka |
| Bagian 8: Visualisasi Hasil | Grafik batang dan grafik garis untuk laporan |
| Bagian 9: Menyusun Temuan | Memetakan temuan, bukti, dan tindak lanjut |
| Bagian 10: Menyusun Laporan | Laporan akhir siap baca dan refleksi proyek |
Materi pendukungnya sudah pernah kita bahas di chapter-chapter sebelumnya: teknik validasi ada pada Section Data Cleaning , alur eksplorasi pada Exploratory Data Analysis , dan prinsip penyusunan ringkasan pada Menyusun Ringkasan Eksekutif .
Dataset Mentah #
Berkas data/penjualan_kotor.csv di bawah ini adalah titik awal seluruh analisis. Kolomnya empat: tanggal (hari penjualan), menu (nama minuman), gelas (jumlah gelas terjual), dan pendapatan_ribu (pendapatan dalam ribuan rupiah). Coba perhatikan sejenak — beberapa baris terlihat janggal, dan justru kejanggalan itulah yang menjadi bahan Bagian 4 dan 5. Tautan unduh tersedia di kepala blok kode berikut:
tanggal,menu,gelas,pendapatan_ribu
2026-01-05,kopi susu,34,748
2026-01-05,matcha latte,21,525
2026-01-05,americano,18,324
2026-01-05,teh tawar,15,150
2026-01-06,kopi susu,31,682
2026-01-06,matcha latte,19,475
2026-01-06,americano,-,288
2026-01-06,teh tawar,12,120
2026-01-07,kopi susu,36,792
2026-01-07,kopi susu,36,792
2026-01-07,matcha latte,22,550
2026-01-07,americano,19,342
2026-01-07,teh tawar,14,140
2026-01-08,Kopi Susu,33,726
2026-01-08,matcha latte,20,500
2026-01-08,americano,17,306
2026-01-08,teh tawar,13,130
2026-01-09,kopi susu,38,836
2026-01-09,matcha latte,24,600
2026-01-09,americano,20,360
2026-01-09,teh tawar,-,160
2026-01-10,kopi susu,52,1144
2026-01-10,matcha latte,35,875
2026-01-10,americano,28,504
2026-01-10,teh tawar,22,220
2026-01-11,kopi susu,55,1210
2026-01-11,matcha latte,37,925
2026-01-11,americano,30,540
2026-01-11,teh tawar,24,240
2026-01-12,kopi susu,40,880
2026-01-12,matcha latte,25,625
2026-01-12,americano,21,378
2026-01-12,teh tawar,17,0
2026-01-13,kopi susu,37,814
2026-01-13,matcha latte,23,575
2026-01-13,americano,19,342
2026-01-13,teh tawar,15,150
2026-01-14,kopi susu,18,396
2026-01-14,matcha latte,11,275
2026-01-14,americano,9,162
2026-01-14,teh tawar,7,70
2026-01-15,kopi susu,41,902
2026-01-15,matcha latte,-,650
2026-01-15,americano,22,396
2026-01-15,teh tawar,18,180
2026-01-16,kopi susu,44,968
2026-01-16,matcha latte,28,700
2026-01-16,americano,23,414
2026-01-16,teh tawar,19,190
2026-01-11,teh tawar,24,240Skrip Pipeline Utuh #
Seluruh alur proyek dirangkum menjadi satu skrip utuh di src/analisis_penjualan.py: memuat data, memvalidasi, membersihkan, meringkas per menu dan per hari, menulis laporan akhir, lalu mencetak ringkasan. Skrip ini deterministik — hasilnya sama setiap kali dijalankan pada data mentah yang sama. Jalankan dari folder proyek dengan perintah python3 src/analisis_penjualan.py:
"""Pipeline analisis penjualan Toko Kopi Kita (periode 5-16 Januari 2026).
Alur lengkap dalam satu skrip yang dapat diulang:
1. Muat data mentah hasil ekspor kasir (data/penjualan_kotor.csv).
2. Validasi kualitas: duplikat, sel kosong "-", nama menu tidak konsisten,
pendapatan 0.
3. Bersihkan seluruh masalah yang ditemukan.
4. Ringkas penjualan per menu dan per hari (termasuk akhir pekan).
5. Susun temuan utama, tulis laporan/laporan-akhir.txt, cetak ringkasan.
Pemakaian (dari folder proyek):
python3 src/analisis_penjualan.py
"""
from datetime import datetime
from pathlib import Path
import pandas as pd
DATA_PATH = "data/penjualan_kotor.csv"
LAPORAN_PATH = "laporan/laporan-akhir.txt"
AKHIR_PEKAN = ("2026-01-10", "2026-01-11") # Sabtu dan Minggu periode ini
NAMA_HARI = {
"Monday": "Senin", "Tuesday": "Selasa", "Wednesday": "Rabu",
"Thursday": "Kamis", "Friday": "Jumat", "Saturday": "Sabtu",
"Sunday": "Minggu",
}
def muat_data(path=DATA_PATH):
"""Muat CSV mentah hasil ekspor aplikasi kasir."""
return pd.read_csv(path)
def validasi(df):
"""Kumpulkan temuan masalah kualitas pada data mentah."""
menu_asli = df["menu"]
return {
"jumlah_baris": len(df),
"duplikat_persis": int(df.duplicated().sum()),
"sel_gelas_strip": int((df["gelas"] == "-").sum()),
"menu_tak_konsisten": sorted(
menu_asli.loc[menu_asli != menu_asli.str.lower()].unique().tolist()
),
"pendapatan_nol": int((df["pendapatan_ribu"] == 0).sum()),
}
def bersihkan(df):
"""Perbaiki masalah hasil validasi lalu kembalikan data bersih dan catatan perbaikan."""
bersih = df.drop_duplicates().reset_index(drop=True)
perbaikan = {"duplikat_dibuang": len(df) - len(bersih)}
# Sel "-" pada kolom gelas berarti pencatatan kosong.
tak_konsisten = bersih.loc[
bersih["menu"] != bersih["menu"].str.lower(), "menu"
].unique().tolist()
bersih["gelas"] = pd.to_numeric(bersih["gelas"].replace("-", pd.NA))
bersih["menu"] = bersih["menu"].str.lower()
perbaikan["menu_diseragamkan"] = [
f"'{nama}' menjadi '{nama.lower()}'" for nama in tak_konsisten
]
# Harga satuan tiap menu diambil dari baris yang lengkap.
lengkap = bersih[bersih["gelas"].notna() & (bersih["pendapatan_ribu"] > 0)]
harga_satuan = (
lengkap["pendapatan_ribu"] / lengkap["gelas"]
).groupby(lengkap["menu"]).median()
# Pendapatan 0 dihitung ulang: gelas dikali harga satuan menu.
nol = bersih["pendapatan_ribu"] == 0
nilai_baru = (bersih.loc[nol, "gelas"] * bersih.loc[nol, "menu"].map(harga_satuan)).astype(int)
perbaikan["pendapatan_diperbaiki"] = [
f"{int(bersih.loc[idx, 'gelas'])} gelas {bersih.loc[idx, 'menu']} "
f"menjadi {rupiah(nilai)}"
for idx, nilai in nilai_baru.items()
]
bersih.loc[nol, "pendapatan_ribu"] = nilai_baru
# Gelas kosong dihitung dari pendapatan dibagi harga satuan menu.
kosong = bersih["gelas"].isna()
perbaikan["gelas_diisi"] = int(kosong.sum())
bersih.loc[kosong, "gelas"] = (
bersih.loc[kosong, "pendapatan_ribu"] / bersih.loc[kosong, "menu"].map(harga_satuan)
)
bersih["gelas"] = bersih["gelas"].astype(int)
return bersih, perbaikan
def ringkas_menu(df):
"""Total gelas, pendapatan, dan kontribusi tiap menu."""
per_menu = (
df.groupby("menu")
.agg(gelas_terjual=("gelas", "sum"), pendapatan_ribu=("pendapatan_ribu", "sum"))
.sort_values("pendapatan_ribu", ascending=False)
)
per_menu["kontribusi_persen"] = (
100 * per_menu["pendapatan_ribu"] / per_menu["pendapatan_ribu"].sum()
).round(1)
return per_menu
def ringkas_harian(df):
"""Total pendapatan tiap tanggal beserta penanda akhir pekan."""
per_hari = df.groupby("tanggal")["pendapatan_ribu"].sum().reset_index()
per_hari["akhir_pekan"] = per_hari["tanggal"].isin(AKHIR_PEKAN)
return per_hari
def hitung_temuan(per_menu, per_hari):
"""Angka kunci yang menjadi isi laporan."""
total_pendapatan = int(per_menu["pendapatan_ribu"].sum())
total_gelas = int(per_menu["gelas_terjual"].sum())
terbaik = per_hari.loc[per_hari["pendapatan_ribu"].idxmax()]
kedua = per_hari.nlargest(2, "pendapatan_ribu").iloc[1]
tersepi = per_hari.loc[per_hari["pendapatan_ribu"].idxmin()]
akhir_pekan = per_hari[per_hari["akhir_pekan"]]
hari_kerja = per_hari[~per_hari["akhir_pekan"]]
return {
"total_pendapatan_ribu": total_pendapatan,
"total_gelas": total_gelas,
"rata_harian_ribu": total_pendapatan / len(per_hari),
"menu_terlaris": per_menu.index[0],
"kontribusi_terlaris": float(per_menu["kontribusi_persen"].iloc[0]),
"gelas_terlaris": int(per_menu["gelas_terjual"].iloc[0]),
"pendapatan_terlaris_ribu": int(per_menu["pendapatan_ribu"].iloc[0]),
"menu_terkecil": per_menu.index[-1],
"kontribusi_terkecil": float(per_menu["kontribusi_persen"].iloc[-1]),
"hari_terbaik": str(terbaik["tanggal"]),
"pendapatan_terbaik_ribu": int(terbaik["pendapatan_ribu"]),
"hari_kedua": str(kedua["tanggal"]),
"pendapatan_kedua_ribu": int(kedua["pendapatan_ribu"]),
"hari_tersepi": str(tersepi["tanggal"]),
"pendapatan_tersepi_ribu": int(tersepi["pendapatan_ribu"]),
"persen_tersepi_dari_rata": 100 * int(tersepi["pendapatan_ribu"]) / (total_pendapatan / len(per_hari)),
"rata_akhir_pekan_ribu": float(akhir_pekan["pendapatan_ribu"].mean()),
"rata_hari_kerja_ribu": float(hari_kerja["pendapatan_ribu"].mean()),
}
def label_tanggal(iso):
"""Ubah '2026-01-11' menjadi 'Minggu 11 Januari' (tanpa jam, tetap deterministik)."""
tanggal = datetime.strptime(iso, "%Y-%m-%d")
return f"{NAMA_HARI[tanggal.strftime('%A')]} {tanggal.day} Januari"
def ribuan_id(nilai):
"""Format bilangan bulat dengan titik ribuan gaya Indonesia."""
return f"{int(nilai):,}".replace(",", ".")
def desimal_id(nilai, digit=2):
"""Format desimal dengan koma gaya Indonesia, mis. '1,57'."""
return f"{nilai:.{digit}f}".replace(".", ",")
def rupiah(ribu):
"""Format angka ribu rupiah menjadi 'Rp 23.649.000'."""
return "Rp " + ribuan_id(round(ribu * 1000))
def persen_id(nilai):
"""Format angka menjadi '42,7%' (gaya Indonesia, koma desimal)."""
return desimal_id(nilai, 1) + "%"
def tulis_laporan(temuan, temuan_validasi, perbaikan, jumlah_bersih):
"""Susun laporan akhir lalu tulis ke laporan/laporan-akhir.txt."""
rasio_pekan = temuan["rata_akhir_pekan_ribu"] / temuan["rata_hari_kerja_ribu"]
naik_persen = 100 * (rasio_pekan - 1)
baris = []
baris.append("=" * 64)
baris.append("LAPORAN ANALISIS PENJUALAN - TOKO KOPI KITA")
baris.append("=" * 64)
baris.append("Periode data : 5-16 Januari 2026 (12 hari, dua pekan penuh)")
baris.append("Pemilik : Bu Rina")
baris.append("Sumber data : ekspor aplikasi kasir (data/penjualan_kotor.csv)")
baris.append("Pengolah : src/analisis_penjualan.py (pandas)")
baris.append("")
baris.append("1. RINGKASAN EKSEKUTIF")
baris.append("-" * 64)
baris.append(
f"Sepanjang 5-16 Januari 2026, Toko Kopi Kita membukukan pendapatan\n"
f"total {rupiah(temuan['total_pendapatan_ribu'])} dari "
f"{ribuan_id(temuan['total_gelas'])} gelas terjual."
)
baris.append(
f"{temuan['menu_terlaris']} menjadi menu penopang dengan kontribusi\n"
f"{persen_id(temuan['kontribusi_terlaris'])} dari total pendapatan."
)
baris.append(
f"Rata-rata pendapatan akhir pekan {rupiah(temuan['rata_akhir_pekan_ribu'])}\n"
f"per hari, {persen_id(naik_persen)} di atas rata-rata hari kerja\n"
f"{rupiah(temuan['rata_hari_kerja_ribu'])}."
)
baris.append(
f"{label_tanggal(temuan['hari_tersepi'])} menjadi hari tersepi\n"
f"({rupiah(temuan['pendapatan_tersepi_ribu'])}, "
f"{persen_id(temuan['persen_tersepi_dari_rata'])} dari rata-rata harian)\n"
f"setelah hujan deras seharian menurut catatan Bu Rina."
)
baris.append("")
baris.append("2. TEMUAN UTAMA")
baris.append("-" * 64)
baris.append(
f"T1. {temuan['menu_terlaris']} adalah menu penopang.\n"
f" {ribuan_id(temuan['gelas_terlaris'])} gelas terjual, pendapatan "
f"{rupiah(temuan['pendapatan_terlaris_ribu'])}\n"
f" ({persen_id(temuan['kontribusi_terlaris'])} dari total). Stok susu dan\n"
f" biji kopi wajib diprioritaskan setiap hari."
)
baris.append(
f"T2. Akhir pekan adalah periode tersibuk.\n"
f" {label_tanggal(temuan['hari_kedua'])} {rupiah(temuan['pendapatan_kedua_ribu'])} dan\n"
f" {label_tanggal(temuan['hari_terbaik'])} {rupiah(temuan['pendapatan_terbaik_ribu'])}\n"
f" (tertinggi sepanjang periode). Rata-rata akhir pekan "
f"{desimal_id(rasio_pekan)} kali\n"
f" rata-rata hari kerja."
)
baris.append(
f"T3. {label_tanggal(temuan['hari_tersepi'])} adalah anomali penurunan.\n"
f" Pendapatan {rupiah(temuan['pendapatan_tersepi_ribu'])}, hanya "
f"{persen_id(temuan['persen_tersepi_dari_rata'])}\n"
f" dari rata-rata harian. Penyebabnya hujan deras seharian, bukan\n"
f" perubahan selera pelanggan."
)
baris.append(
f"T4. {temuan['menu_terkecil']} menyumbang porsi terkecil.\n"
f" Kontribusinya {persen_id(temuan['kontribusi_terkecil'])} dari total\n"
f" pendapatan. Menu ini tetap dipertahankan sebagai pilihan non-kopi\n"
f" yang murah."
)
baris.append("")
baris.append("3. ANGKA KUNCI")
baris.append("-" * 64)
baris.append(f"- Pendapatan total 12 hari : {rupiah(temuan['total_pendapatan_ribu'])}")
baris.append(f"- Rata-rata pendapatan harian: {rupiah(temuan['rata_harian_ribu'])}")
baris.append(f"- Gelas terjual : {ribuan_id(temuan['total_gelas'])} gelas")
baris.append(f"- Hari terbaik : {temuan['hari_terbaik']} ({rupiah(temuan['pendapatan_terbaik_ribu'])})")
baris.append(f"- Hari tersepi : {temuan['hari_tersepi']} ({rupiah(temuan['pendapatan_tersepi_ribu'])})")
baris.append(f"- Rata-rata akhir pekan : {rupiah(temuan['rata_akhir_pekan_ribu'])} per hari")
baris.append(f"- Rata-rata hari kerja : {rupiah(temuan['rata_hari_kerja_ribu'])} per hari")
baris.append("")
baris.append("4. REKOMENDASI")
baris.append("-" * 64)
baris.append(
"R1. Tambah porsi stok susu dan biji kopi jelang akhir pekan;\n"
f" belanja Sabtu-Minggu perlu sekitar {persen_id(naik_persen)} lebih\n"
f" banyak dari hari kerja."
)
baris.append(
"R2. Siapkan strategi hari hujan: promo minuman hangat atau layanan\n"
f" pesan-antar agar hari seperti {temuan['hari_tersepi']} tidak\n"
f" sepenuhnya sepi."
)
baris.append(
f"R3. Pertahankan {temuan['menu_terkecil']} sebagai menu pelengkap tanpa\n"
f" menambah stok besar; pantau kontribusinya lagi setelah tiga bulan."
)
baris.append(
f"R4. Perbaiki kebiasaan input kasir: data mentah memuat\n"
f" {perbaikan['duplikat_dibuang']} baris duplikat, "
f"{perbaikan['gelas_diisi']} sel gelas kosong,\n"
f" {len(perbaikan['menu_diseragamkan'])} nama menu tidak konsisten, dan\n"
f" {temuan_validasi['pendapatan_nol']} pendapatan 0 dari total\n"
f" {temuan_validasi['jumlah_baris']} baris yang diekspor."
)
baris.append("")
baris.append("5. CATATAN KUALITAS DATA")
baris.append("-" * 64)
baris.append(f"- Baris data mentah : {temuan_validasi['jumlah_baris']}")
baris.append(f"- Baris data bersih : {jumlah_bersih}")
baris.append(
f"- Pembersihan : {perbaikan['duplikat_dibuang']} duplikat persis dibuang,\n"
f" {perbaikan['gelas_diisi']} sel gelas '-' diisi ulang dari\n"
f" harga satuan menu, "
f"{' dan '.join(perbaikan['menu_diseragamkan'])},\n"
f" 1 pendapatan 0 dihitung ulang\n"
f" ({perbaikan['pendapatan_diperbaiki'][0]})."
)
baris.append("- Seluruh angka pada laporan ini dapat direproduksi dengan")
baris.append(" menjalankan ulang skrip yang sama pada data mentah yang sama.")
baris.append("=" * 64)
Path(LAPORAN_PATH).parent.mkdir(parents=True, exist_ok=True)
with open(LAPORAN_PATH, "w", encoding="utf-8") as berkas:
berkas.write("\n".join(baris) + "\n")
return "\n".join(baris)
def main():
print("== Pipeline Analisis Penjualan - Toko Kopi Kita ==")
mentah = muat_data()
print(f"Muat data mentah : {DATA_PATH} ({len(mentah)} baris)")
print("\nValidasi data mentah:")
temuan_validasi = validasi(mentah)
print(f" - baris duplikat persis : {temuan_validasi['duplikat_persis']}")
print(f" - sel gelas bernilai '-' : {temuan_validasi['sel_gelas_strip']}")
print(f" - nama menu tak konsisten: {temuan_validasi['menu_tak_konsisten']}")
print(f" - pendapatan tercatat 0 : {temuan_validasi['pendapatan_nol']}")
bersih, perbaikan = bersihkan(mentah)
print(f"\nPembersihan selesai: {len(bersih)} baris bersih, {bersih['menu'].nunique()} menu unik")
per_menu = ringkas_menu(bersih)
print("\nRingkasan per menu:")
print(per_menu.to_string())
per_hari = ringkas_harian(bersih)
temuan = hitung_temuan(per_menu, per_hari)
print("\nRingkasan harian:")
print(f" - total pendapatan 12 hari : {temuan['total_pendapatan_ribu']} ribu")
print(f" - hari terbaik : {temuan['hari_terbaik']} ({temuan['pendapatan_terbaik_ribu']} ribu)")
print(f" - hari tersepi : {temuan['hari_tersepi']} ({temuan['pendapatan_tersepi_ribu']} ribu)")
print(f" - rata-rata akhir pekan : {temuan['rata_akhir_pekan_ribu']:.1f} ribu")
print(f" - rata-rata hari kerja : {temuan['rata_hari_kerja_ribu']:.1f} ribu")
tulis_laporan(temuan, temuan_validasi, perbaikan, len(bersih))
print(f"\nLaporan ditulis ke: {LAPORAN_PATH}")
if __name__ == "__main__":
main()Output yang Dihasilkan:
== Pipeline Analisis Penjualan - Toko Kopi Kita ==
Muat data mentah : data/penjualan_kotor.csv (50 baris)
Validasi data mentah:
- baris duplikat persis : 2
- sel gelas bernilai '-' : 3
- nama menu tak konsisten: ['Kopi Susu']
- pendapatan tercatat 0 : 1
Pembersihan selesai: 48 baris bersih, 4 menu unik
Ringkasan per menu:
gelas_terjual pendapatan_ribu kontribusi_persen
menu
kopi susu 459 10098 42.7
matcha latte 291 7275 30.8
americano 242 4356 18.4
teh tawar 192 1920 8.1
Ringkasan harian:
- total pendapatan 12 hari : 23649 ribu
- hari terbaik : 2026-01-11 (2915 ribu)
- hari tersepi : 2026-01-14 (903 ribu)
- rata-rata akhir pekan : 2829.0 ribu
- rata-rata hari kerja : 1799.1 ribu
Laporan ditulis ke: laporan/laporan-akhir.txtDari ringkasan tersebut terlihat cerita utuhnya: kopi susu menopang 42,7% pendapatan, akhir pekan jauh lebih ramai daripada hari kerja, dan tanggal 14 Januari tercatat sebagai hari tersepi. Bagian-bagian berikutnya membedah dari mana angka-angka itu berasal, langkah demi langkah.
Laporan Akhir #
Skrip di atas juga menulis laporan/laporan-akhir.txt — laporan datar yang siap dibaca Bu Rina tanpa perlu membuka Python. Isinya lima bagian: ringkasan eksekutif, temuan utama, angka kunci, rekomendasi, dan catatan kualitas data. Anda bisa mengunduhnya melalui tautan pada kepala blok berikut:
================================================================
LAPORAN ANALISIS PENJUALAN - TOKO KOPI KITA
================================================================
Periode data : 5-16 Januari 2026 (12 hari, dua pekan penuh)
Pemilik : Bu Rina
Sumber data : ekspor aplikasi kasir (data/penjualan_kotor.csv)
Pengolah : src/analisis_penjualan.py (pandas)
1. RINGKASAN EKSEKUTIF
----------------------------------------------------------------
Sepanjang 5-16 Januari 2026, Toko Kopi Kita membukukan pendapatan
total Rp 23.649.000 dari 1.184 gelas terjual.
kopi susu menjadi menu penopang dengan kontribusi
42,7% dari total pendapatan.
Rata-rata pendapatan akhir pekan Rp 2.829.000
per hari, 57,2% di atas rata-rata hari kerja
Rp 1.799.100.
Rabu 14 Januari menjadi hari tersepi
(Rp 903.000, 45,8% dari rata-rata harian)
setelah hujan deras seharian menurut catatan Bu Rina.
2. TEMUAN UTAMA
----------------------------------------------------------------
T1. kopi susu adalah menu penopang.
459 gelas terjual, pendapatan Rp 10.098.000
(42,7% dari total). Stok susu dan
biji kopi wajib diprioritaskan setiap hari.
T2. Akhir pekan adalah periode tersibuk.
Sabtu 10 Januari Rp 2.743.000 dan
Minggu 11 Januari Rp 2.915.000
(tertinggi sepanjang periode). Rata-rata akhir pekan 1,57 kali
rata-rata hari kerja.
T3. Rabu 14 Januari adalah anomali penurunan.
Pendapatan Rp 903.000, hanya 45,8%
dari rata-rata harian. Penyebabnya hujan deras seharian, bukan
perubahan selera pelanggan.
T4. teh tawar menyumbang porsi terkecil.
Kontribusinya 8,1% dari total
pendapatan. Menu ini tetap dipertahankan sebagai pilihan non-kopi
yang murah.
3. ANGKA KUNCI
----------------------------------------------------------------
- Pendapatan total 12 hari : Rp 23.649.000
- Rata-rata pendapatan harian: Rp 1.970.750
- Gelas terjual : 1.184 gelas
- Hari terbaik : 2026-01-11 (Rp 2.915.000)
- Hari tersepi : 2026-01-14 (Rp 903.000)
- Rata-rata akhir pekan : Rp 2.829.000 per hari
- Rata-rata hari kerja : Rp 1.799.100 per hari
4. REKOMENDASI
----------------------------------------------------------------
R1. Tambah porsi stok susu dan biji kopi jelang akhir pekan;
belanja Sabtu-Minggu perlu sekitar 57,2% lebih
banyak dari hari kerja.
R2. Siapkan strategi hari hujan: promo minuman hangat atau layanan
pesan-antar agar hari seperti 2026-01-14 tidak
sepenuhnya sepi.
R3. Pertahankan teh tawar sebagai menu pelengkap tanpa
menambah stok besar; pantau kontribusinya lagi setelah tiga bulan.
R4. Perbaiki kebiasaan input kasir: data mentah memuat
2 baris duplikat, 3 sel gelas kosong,
1 nama menu tidak konsisten, dan
1 pendapatan 0 dari total
50 baris yang diekspor.
5. CATATAN KUALITAS DATA
----------------------------------------------------------------
- Baris data mentah : 50
- Baris data bersih : 48
- Pembersihan : 2 duplikat persis dibuang,
3 sel gelas '-' diisi ulang dari
harga satuan menu, 'Kopi Susu' menjadi 'kopi susu',
1 pendapatan 0 dihitung ulang
(17 gelas teh tawar menjadi Rp 170.000).
- Seluruh angka pada laporan ini dapat direproduksi dengan
menjalankan ulang skrip yang sama pada data mentah yang sama.
================================================================Cara Mengerjakan #
Unduh ketiga berkas di atas ke satu folder dengan struktur yang sama (data/, src/, laporan/), lalu ikuti sepuluh bagian secara berurutan. Setiap bagian memuat kode yang bisa dijalankan langsung dan merujuk ke berkas yang sama, sehingga kondisi proyek tetap utuh di setiap tahap. Skrip analisis_penjualan.py juga muncul di bagian Berkas Sumber pada tiap halaman bagian untuk memudahkan pengunduhan.
Mari kita mulai dari fondasinya: memahami masalah yang ingin diselesaikan Bu Rina pada Bagian 1: Memahami Masalah .
Pahami konteks bisnis Toko Kopi Kita dan tetapkan tujuan serta batasan proyek sebelum analisis dimulai.
Buka → # Bagian 2: Menentukan Pertanyaan AnalisisUbah keluhan bisnis Bu Rina menjadi lima pertanyaan analisis yang terukur beserta metrik dan keputusannya.
Buka → # Bagian 3: Mengumpulkan DataTelusuri asal-usul data ekspor kasir dan kenali struktur empat kolomnya sebelum diolah.
Buka → # Bagian 4: Memvalidasi DataPeriksa kualitas data mentah: duplikat, sel kosong, nama menu tak konsisten, dan nilai nol.
Buka → # Bagian 5: Membersihkan DataBersihkan data mentah langkah demi langkah hingga menghasilkan 48 baris data yang siap dianalisis.
Buka → # Bagian 6: Eksplorasi DataJelajahi data bersih dengan statistik deskriptif serta agregasi per menu, per hari, dan per kelompok hari.
Buka → # Bagian 7: Analisis PenjualanJawab kelima pertanyaan analisis dari Bagian 2 satu per satu dengan angka dari data bersih 48 baris.
Buka → # Bagian 8: Visualisasi HasilUbah jawaban analisis menjadi grafik batang dan grafik garis yang mudah dibaca pemangku kepentingan.
Buka → # Bagian 9: Menyusun TemuanPetakan setiap temuan ke bukti angka dan tindak lanjutnya sebelum ditulis menjadi laporan.
Buka → # Bagian 10: Menyusun LaporanSusun laporan akhir lima bagian untuk Bu Rina, periksa ulang angkanya, dan tutup proyek dengan refleksi.
Buka →