Bagian 5 sampai 7 membedah komoditas satu per satu: pergerakannya, statistiknya, arah trennya. Namun keputusan Bu Warni tidak pernah menyangkut satu bahan saja — setiap rupiah untuk cabai berkompetisi dengan rupiah untuk beras dan ayam, jadi kelima komoditas perlu berdiri dalam satu arena perbandingan. Bagian ini menjawab pertanyaan Q4 dari Bagian 1: apa implikasi pola harga tiap komoditas bagi belanja mingguan dan menu warung. Kita mulai dari papan peringkat gejolak, lanjut ke pembagian kelompok stabil dan bergejolak, lalu menutup dengan tabel perbandingan lengkap yang menjadi dasar rekomendasi laporan akhir.

Papan Peringkat Gejolak #

Peringkat membutuhkan satu penggaris yang sama untuk semua peserta. Simpangan baku tidak memenuhi syarat itu — cabai merah berdagang di kisaran 50 ribu sehingga std 14.53 terasa wajar, sementara beras di 13 ribu membuat std 0.16 tampak sepele; keduanya tidak bisa diadu langsung. Koefisien variasi dari Bagian 6 menyelesaikan persoalan ini karena cv membandingkan sebaran terhadap rata-rata masing-masing komoditas. Materi lengkapnya ada pada chapter Ukuran Penyebaran ; di sini kita langsung memakainya untuk mengurutkan lima komoditas:

python
import pandas as pd

# Resep pembersihan Bagian 4: 41 baris mentah menjadi 40 baris bersih.
df = pd.read_csv("../data/harga_komoditas.csv")
df = df.drop_duplicates(subset=["minggu", "komoditas"], keep="first").copy()
df["komoditas"] = df["komoditas"].str.strip().str.lower()
df["harga_rata_ribu"] = pd.to_numeric(df["harga_rata_ribu"], errors="coerce")
df = df.sort_values(["komoditas", "minggu"])
df["harga_rata_ribu"] = df.groupby("komoditas")["harga_rata_ribu"].transform(
    lambda s: s.interpolate()
)

stat = df.groupby("komoditas")["harga_rata_ribu"].agg(["mean", "std"])
stat["cv"] = stat["std"] / stat["mean"] * 100

print("Papan peringkat gejolak harga 8 pekan:")
for nomor, (komoditas, baris) in enumerate(
    stat.sort_values("cv", ascending=False).iterrows(), start=1
):
    print(
        f"{nomor}. {komoditas:<12} cv {baris['cv']:5.2f}%"
        f"  mean {baris['mean']:5.2f}  std {baris['std']:5.2f}"
    )

Output yang Dihasilkan:

text
Papan peringkat gejolak harga 8 pekan:
1. cabai merah  cv 28.91%  mean 50.25  std 14.53
2. bawang merah cv  6.10%  mean 32.94  std  2.01
3. ayam broiler cv  2.29%  mean 34.00  std  0.78
4. beras        cv  1.21%  mean 13.25  std  0.16
5. gula pasir   cv  0.68%  mean 17.65  std  0.12

Tiga hal perlu dibaca dari papan peringkat tersebut:

  1. Jurang antara peringkat pertama dan kedua sangat lebar: cv cabai merah 28.91% hampir lima kali cv bawang merah 6.10%. Cabai merah berdiri sendirian di kelas atas; tidak ada komoditas lain yang mendekat.
  2. Empat komoditas sisanya berhimpit di bawah 7% — dari bawang merah 6.10% sampai gula pasir 0.68%. Untuk keempatnya, harga bergerak dalam koridor yang relatif sempit.
  3. Peringkat menegaskan temuan Bagian 5: empat dari lima perubahan pekan-ke-pekan terbesar memang milik cabai merah. Ukuran gejolak dan daftar lonjakan saling menguatkan.

Blok di atas memakai groupby() berpasangan agg() untuk menghitung mean dan std sekaligus per komoditas; rincian pemakaiannya ada di dokumentasi pandas untuk groupby .

Membagi Kelompok Stabil dan Bergejolak #

Peringkat menjawab pertanyaan “yang paling”, tetapi keputusan warung bekerja dengan kelompok: bahan mana yang cukup direncanakan sekali seminggu, dan bahan mana yang harus diawasi. Kita pakai ambang sederhana — cv 5% ke atas masuk kelompok bergejolak, di bawahnya stabil. Pada data ini ambang mana pun di antara cv ayam broiler (2.29%) dan cv bawang merah (6.10%) menghasilkan pembagian yang sama, karena tidak ada komoditas yang berada di antara keduanya:

python
import pandas as pd

# Resep pembersihan Bagian 4: 41 baris mentah menjadi 40 baris bersih.
df = pd.read_csv("../data/harga_komoditas.csv")
df = df.drop_duplicates(subset=["minggu", "komoditas"], keep="first").copy()
df["komoditas"] = df["komoditas"].str.strip().str.lower()
df["harga_rata_ribu"] = pd.to_numeric(df["harga_rata_ribu"], errors="coerce")
df = df.sort_values(["komoditas", "minggu"])
df["harga_rata_ribu"] = df.groupby("komoditas")["harga_rata_ribu"].transform(
    lambda s: s.interpolate()
)

stat = df.groupby("komoditas")["harga_rata_ribu"].agg(["mean", "std"])
stat["cv"] = stat["std"] / stat["mean"] * 100

kelompok = {"stabil": [], "bergejolak": []}
for komoditas, baris in stat.iterrows():
    if baris["cv"] >= 5:
        kelompok["bergejolak"].append(komoditas)
    else:
        kelompok["stabil"].append(komoditas)

for nama, anggota in kelompok.items():
    rincian = ", ".join(f"{k} (cv {stat.loc[k, 'cv']:.2f}%)" for k in anggota)
    print(f"Kelompok {nama:10}: {rincian}")

Output yang Dihasilkan:

text
Kelompok stabil    : ayam broiler (cv 2.29%), beras (cv 1.21%), gula pasir (cv 0.68%)
Kelompok bergejolak: bawang merah (cv 6.10%), cabai merah (cv 28.91%)

Kelompok stabil beranggotakan ayam broiler, beras, dan gula pasir — ketiganya berayun di bawah 2.5% dari rata-ratanya sendiri. Kelompok bergejolak beranggotakan bawang merah dan cabai merah, tetapi keduanya tidak sederajat: bawang merah tergolong bergejolak sedang (6.10%, persis sebutan temuan T4 pada laporan akhir), sedangkan cabai merah bergejolak berat (28.91%). Pembagian ini nanti terbaca langsung pada grafik batang Bagian 9 dan menjadi kerangka tabel rekomendasi Bagian 10.

Melengkapi Peringkat dengan Arah Tren #

Gejolak hanyalah separuh cerita. cv mengukur besarnya ayunan tetapi buta terhadap arah, dan perbandingan yang lengkap menyandingkan keduanya: tren ujung-ke-ujung M1 menuju M8 dari Bagian 7, ditambah hitungan berapa pekan harga benar-benar naik. Kolom pekan_naik pada blok berikut dihitung dengan pivot.diff(): selisih tiap pekan terhadap pekan sebelumnya per komoditas, lalu dihitung jumlah selisih yang positif dari tujuh perubahan yang ada:

python
import pandas as pd

# Resep pembersihan Bagian 4: 41 baris mentah menjadi 40 baris bersih.
df = pd.read_csv("../data/harga_komoditas.csv")
df = df.drop_duplicates(subset=["minggu", "komoditas"], keep="first").copy()
df["komoditas"] = df["komoditas"].str.strip().str.lower()
df["harga_rata_ribu"] = pd.to_numeric(df["harga_rata_ribu"], errors="coerce")
df = df.sort_values(["komoditas", "minggu"])
df["harga_rata_ribu"] = df.groupby("komoditas")["harga_rata_ribu"].transform(
    lambda s: s.interpolate()
)
pivot = df.pivot(index="minggu", columns="komoditas", values="harga_rata_ribu")

stat = df.groupby("komoditas")["harga_rata_ribu"].agg(["mean", "std"])
stat["cv"] = stat["std"] / stat["mean"] * 100

tren = pd.DataFrame({"awal_m1": pivot.iloc[0], "akhir_m8": pivot.iloc[-1]})
tren["pct"] = (tren["akhir_m8"] - tren["awal_m1"]) / tren["awal_m1"] * 100

pekan_naik = pivot.diff().dropna().gt(0).sum()

banding = stat.join(tren)[["mean", "cv", "awal_m1", "akhir_m8", "pct"]]
banding["pekan_naik"] = pekan_naik
print(banding.sort_values("cv", ascending=False).round(2).to_string())

Output yang Dihasilkan:

text
               mean     cv  awal_m1  akhir_m8    pct  pekan_naik
komoditas                                                       
cabai merah   50.25  28.91     38.0      40.0   5.26           4
bawang merah  32.94   6.10     30.0      35.0  16.67           3
ayam broiler  34.00   2.29     33.0      35.2   6.67           6
beras         13.25   1.21     13.0      13.5   3.85           4
gula pasir    17.65   0.68     17.5      17.8   1.71           4

Tabel ini memperlihatkan bahwa dua peringkat — gejolak dan tren — tidak searah:

  1. Bawang merah juara tren ujung-ke-ujung (+16.67%) padahal gejolaknya hanya sedang. Dua titik ujung M1 dan M8 memang menipu, sebagaimana sudah kita bahas pada Bagian 7; enam pekan di antaranya naik-turun tanpa arah.
  2. Cabai merah justru tampak jinak (+5.26%) karena harga M8 sudah pulih ke 40.0. Ujung yang rata menyembunyikan puncak 78.0 di tengah periode.
  3. Hanya ayam broiler yang memadukan gejolak rendah dengan arah konsisten: cv 2.29% sekaligus 6 dari 7 pekan naik. Inilah jawaban Q3 — komoditas yang menanjak perlahan tetapi konsisten.

Implikasi untuk Warung Bu Warni #

Tabel perbandingan berikut merangkum seluruh ukuran sekaligus menerjemahkannya menjadi perlakuan belanja dan menu:

KomoditascvTren M1-M8Ciri pergerakanPerlakuan untuk warung
cabai merah28.91%+5.26%, puncak 78.0 pada M5Naik tajam dua pekan, pulih bertahapAwasi tiap pekan; siapkan menu cadangan tanpa cabai segar
bawang merah6.10%+16.67%Bergelombang tanpa arahBeli rutin sesuai kebutuhan pekan
ayam broiler2.29%+6.67%, 6 dari 7 pekan naikMenanjak landai tanpa jedaTinjau harga jual paket ayam secara berkala
beras1.21%+3.85%Nyaris datarAman dirinci dalam anggaran mingguan
gula pasir0.68%+1.71%Paling tenangAman dirinci dalam anggaran mingguan

Menu berbasis cabai adalah sumber risiko terbesar warung. Saat harga menembus 78.0 pada M5, biaya cabai untuk sambal membengkak hampir dua kali lipat dalam dua pekan (42.0 pada M3 menjadi 78.0), sementara harga jual paket nasi tidak mungkin diubah secepat itu. Karena itu laporan akhir menyiapkan dua pintu: saat cabai murah (kisaran 38-44) menambah stok sambal, dan saat cabai mahal (di atas 60) menggeser menu andalan ke olahan tanpa cabai segar.

Beras dan gula pasir adalah kebalikannya: gejolak di bawah 1.5% membuat keduanya aman diandalkan untuk perencanaan, sehingga cukup dirinci dalam anggaran mingguan tanpa perlu menimbun — menimbun justru mengikat uang warung tanpa keuntungan berarti. Ayam broiler menghadirkan jenis risiko ketiga: ayunan mingguannya kecil, tetapi arahnya menaik terus — pergeseran 33.0 menuju 35.2 selama delapan pekan tidak pernah berbalik, sehingga margin paket nasi ayam tergerus perlahan bila harga jual tidak pernah ditinjau.

Ringkasan Bagian 8 #

  • Koefisien variasi memberi peringkat gejolak yang adil lintas tingkat harga: cabai merah 28.91% jauh di atas bawang merah 6.10%, ayam broiler 2.29%, beras 1.21%, dan gula pasir 0.68%.
  • Ambang cv 5% membagi lima komoditas menjadi kelompok stabil (ayam broiler, beras, gula pasir) dan bergejolak (bawang merah sedang, cabai merah berat).
  • Peringkat gejolak dan peringkat tren tidak searah: bawang merah paling cepat naik ujung-ke-ujung (+16.67%), cabai merah tampak jinak (+5.26%) karena puncaknya disembunyikan dua titik ujung, dan hanya ayam broiler yang stabil sekaligus konsisten menanjak.
  • Implikasinya bagi warung: menu berbasis cabai paling berisiko terhadap margin, beras dan gula pasir aman diandalkan dalam anggaran mingguan, dan harga jual paket ayam perlu ditinjau berkala.

Angka-angka kini berbaris rapi, tetapi Bu Warni — seperti kebanyakan pembaca laporan — akan lebih cepat menangkap pola dari gambar daripada dari tabel angka. Pada Bagian 9: Visualisasi Hasil kita menggambar grafik garis pergerakan harga dan grafik batang peringkat gejolak yang akan menjadi wajah laporan akhir.