{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8d4bd7e7",
   "metadata": {},
   "source": [
    "# Lab Minggu 4 — Apriori & Frequent Itemsets\n",
    "\n",
    "**Tujuan lab:**\n",
    "1. Menghitung support & confidence secara manual dengan Python.\n",
    "2. Menelusuri algoritma Apriori langkah demi langkah pada contoh Han & Kamber.\n",
    "3. Menjalankan Apriori dengan `mlxtend` pada data transaksi ritel.\n",
    "4. Melihat efek nilai *min_support* terhadap jumlah itemset dan waktu komputasi."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b28575db",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "sys.path.insert(0, \"..\")\n",
    "\n",
    "from itertools import combinations\n",
    "import time\n",
    "\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from common.datasets import transaksi_contoh, transaksi_ritel"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb0ea8bb",
   "metadata": {},
   "source": [
    "## 1. Support & confidence manual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "01f81d49",
   "metadata": {},
   "outputs": [],
   "source": [
    "trx = [[\"roti\", \"susu\"], [\"roti\", \"mentega\", \"telur\"], [\"susu\", \"mentega\", \"kopi\"],\n",
    "       [\"roti\", \"susu\", \"mentega\"], [\"roti\", \"susu\", \"kopi\"]]\n",
    "\n",
    "def support(transaksi, itemset):\n",
    "    s = set(itemset)\n",
    "    return sum(s <= set(t) for t in transaksi) / len(transaksi)\n",
    "\n",
    "print(\"s(roti)        =\", support(trx, {\"roti\"}))\n",
    "print(\"s(roti, susu)  =\", support(trx, {\"roti\", \"susu\"}))\n",
    "print(\"conf(roti=>susu) =\", support(trx, {\"roti\", \"susu\"}) / support(trx, {\"roti\"}))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a73f11c5",
   "metadata": {},
   "source": [
    "## 2. Apriori langkah demi langkah (Han & Kamber, min_sup count = 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f497e08",
   "metadata": {},
   "outputs": [],
   "source": [
    "T = [set(t) for t in transaksi_contoh()]\n",
    "for i, t in enumerate(T, 1):\n",
    "    print(f\"T{i}00: {sorted(t)}\")\n",
    "MIN = 2\n",
    "\n",
    "def hitung(kandidat):\n",
    "    return {c: sum(c <= t for t in T) for c in kandidat}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18500656",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Iterasi 1\n",
    "C1 = {frozenset([i]) for t in T for i in t}\n",
    "cnt = hitung(C1)\n",
    "L1 = {c for c, v in cnt.items() if v >= MIN}\n",
    "pd.Series({\",\".join(sorted(c)): v for c, v in cnt.items()}).sort_index()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84a6e7c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Iterasi 2: semua pasangan dari L1\n",
    "items = sorted(i for c in L1 for i in c)\n",
    "C2 = {frozenset(p) for p in combinations(items, 2)}\n",
    "cnt = hitung(C2)\n",
    "L2 = {c for c, v in cnt.items() if v >= MIN}\n",
    "tabel = pd.DataFrame([(\",\".join(sorted(c)), v, v >= MIN) for c, v in cnt.items()],\n",
    "                     columns=[\"itemset\", \"count\", \"frequent\"]).sort_values(\"itemset\")\n",
    "tabel"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c424123",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Iterasi 3: JOIN lalu PRUNE\n",
    "def join(L_prev, k):\n",
    "    srt = sorted(sorted(x) for x in L_prev)\n",
    "    hasil = set()\n",
    "    for i in range(len(srt)):\n",
    "        for j in range(i + 1, len(srt)):\n",
    "            if srt[i][:k - 2] == srt[j][:k - 2]:\n",
    "                hasil.add(frozenset(srt[i]) | frozenset(srt[j]))\n",
    "    return hasil\n",
    "\n",
    "C3_join = join(L2, 3)\n",
    "print(\"Hasil JOIN :\", [sorted(c) for c in C3_join])\n",
    "C3 = {c for c in C3_join if all(frozenset(s) in L2 for s in combinations(c, 2))}\n",
    "print(\"Hasil PRUNE:\", [sorted(c) for c in C3])\n",
    "cnt = hitung(C3)\n",
    "L3 = {c for c, v in cnt.items() if v >= MIN}\n",
    "print(\"L3:\", {\",\".join(sorted(c)): v for c, v in cnt.items() if v >= MIN})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d6a55610",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Iterasi 4\n",
    "C4 = {c for c in join(L3, 4) if all(frozenset(s) in L3 for s in combinations(c, 3))}\n",
    "print(\"C4:\", C4, \"-> kosong, algoritma berhenti\")\n",
    "print(\"Total frequent itemset:\", len(L1) + len(L2) + len(L3))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0ec40ac",
   "metadata": {},
   "source": [
    "## 3. Data ritel dengan mlxtend"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65baf090",
   "metadata": {},
   "outputs": [],
   "source": [
    "from mlxtend.preprocessing import TransactionEncoder\n",
    "from mlxtend.frequent_patterns import apriori\n",
    "\n",
    "ritel = transaksi_ritel()\n",
    "print(len(ritel), \"transaksi; contoh:\", ritel[:3])\n",
    "\n",
    "te = TransactionEncoder()\n",
    "onehot = pd.DataFrame(te.fit(ritel).transform(ritel), columns=te.columns_)\n",
    "onehot.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0514869",
   "metadata": {},
   "outputs": [],
   "source": [
    "onehot.mean().sort_values(ascending=False).plot(kind=\"bar\", figsize=(10, 3), title=\"Support 1-itemset\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0602669",
   "metadata": {},
   "outputs": [],
   "source": [
    "fi = apriori(onehot, min_support=0.05, use_colnames=True)\n",
    "fi[\"panjang\"] = fi[\"itemsets\"].apply(len)\n",
    "fi.sort_values(\"support\", ascending=False).query(\"panjang >= 2\").head(15)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95833f41",
   "metadata": {},
   "source": [
    "Pola yang \"ditanam\" di generator data (roti–mentega, kopi–gula, sereal–susu, mie_instan–telur)\n",
    "muncul di atas."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86cb820b",
   "metadata": {},
   "source": [
    "## 4. Efek min_support"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94cf8aec",
   "metadata": {},
   "outputs": [],
   "source": [
    "hasil = []\n",
    "for ms in [0.2, 0.15, 0.1, 0.07, 0.05, 0.03, 0.02, 0.01]:\n",
    "    t0 = time.perf_counter()\n",
    "    f = apriori(onehot, min_support=ms, use_colnames=True)\n",
    "    hasil.append({\"min_support\": ms, \"jumlah_itemset\": len(f),\n",
    "                  \"waktu_ms\": (time.perf_counter() - t0) * 1000})\n",
    "efek = pd.DataFrame(hasil)\n",
    "efek"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1059e88",
   "metadata": {},
   "outputs": [],
   "source": [
    "efek.plot(x=\"min_support\", y=\"jumlah_itemset\", marker=\"o\", logy=True,\n",
    "          title=\"Semakin kecil min_support, itemset meledak\")\n",
    "plt.gca().invert_xaxis()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4525a703",
   "metadata": {},
   "source": [
    "## 5. Maximal itemsets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a11ad1d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "fs = set(apriori(onehot, min_support=0.05, use_colnames=True)[\"itemsets\"])\n",
    "maximal = [x for x in fs if not any(x < y for y in fs)]\n",
    "print(len(fs), \"frequent ->\", len(maximal), \"maximal\")\n",
    "sorted(sorted(x) for x in maximal if len(x) > 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ea6cadb",
   "metadata": {},
   "source": [
    "## ✍️ Coba sendiri\n",
    "\n",
    "1. Hitung confidence `{sereal} => {susu}` dan `{susu} => {sereal}` pada data ritel. Mengapa berbeda?\n",
    "2. Tampilkan semua 3-itemset dengan min_support 0.02. Adakah pola `{roti, susu, telur}`?\n",
    "3. Implementasikan fungsi `apriori_saya(transaksi, min_support)` sendiri (lihat `exercise.py`)\n",
    "   lalu bandingkan hasilnya dengan mlxtend.\n",
    "4. Rekomendasi bisnis: berdasarkan pasangan produk teratas, usulkan 2 strategi penempatan/promo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6287d6b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0d0c63a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b25d0b7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3028b961",
   "metadata": {},
   "source": [
    "**Rekomendasi no. 4:**\n",
    "\n",
    "- ..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69ea0e87",
   "metadata": {},
   "source": [
    "## Selanjutnya\n",
    "Kerjakan kuis dan `exercise.py`, lalu jalankan `python check.py 4` dari folder utama."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
