================================================================================
  Question 57: Apriori Algorithm & Association Rule Mining (Toy & Real Groceries)
================================================================================

FULL QUESTION:
Develop an Association Rule Mining application using the Apriori algorithm:
Part 1: Demonstrate basic Market Basket Analysis on a small dictionary/list of customer transactions.
Part 2: Download or load the real-world Groceries dataset, encode transactions using TransactionEncoder,
        mine frequent itemsets with min_support = 0.01 (1%), and generate association rules filtered by
        confidence and lift thresholds. Display the top association rules sorted by Lift.

SHORT QUESTION:
Market basket analysis using Apriori algorithm on demo transactions and real groceries dataset.

================================================================================
README & SETUP INSTRUCTIONS:
================================================================================
# Question 57: Apriori Association Rule Mining

## Requirements
- Python 3.x
- Libraries: pandas, mlxtend, requests

## How to Run
```bash
python question_57_apriori.py
```

================================================================================
SOURCE CODE FILES:
================================================================================

1. question_57_apriori.py
--------------------------------------------------------------------------------
# ============================================================
# APRIORI ALGORITHM - ASSOCIATION RULE MINING
# Market Basket Analysis (Demo Transactions & Groceries Dataset)
# ============================================================

import warnings
import pandas as pd
import requests
from mlxtend.frequent_patterns import apriori, association_rules
from mlxtend.preprocessing import TransactionEncoder

warnings.filterwarnings("ignore", category=DeprecationWarning)

# ============================================================
# PART 1: TOY BASKET DEMO TRANSACTIONS
# ============================================================
print("--- PART 1: TOY BASKET TRANSACTIONS ---")

toy_transactions = [
    ["Milk", "Bread", "Butter"],
    ["Bread", "Butter"],
    ["Milk", "Bread"],
    ["Milk", "Bread", "Butter", "Eggs"],
    ["Bread", "Eggs"],
    ["Milk", "Butter"],
    ["Bread", "Butter", "Eggs"],
    ["Milk", "Bread", "Butter"],
    ["Milk", "Bread", "Eggs"],
    ["Bread", "Butter"],
]

te_toy = TransactionEncoder()
encoded_toy = te_toy.fit(toy_transactions).transform(toy_transactions)
df_toy = pd.DataFrame(encoded_toy, columns=te_toy.columns_)

print("\nOne-Hot Encoded Toy Dataset:")
print(df_toy)

# Mine frequent itemsets with min_support = 0.3
frequent_toy = apriori(df_toy, min_support=0.3, use_colnames=True)
print("\nFrequent Itemsets (min_support = 0.3):")
print(frequent_toy)

# Generate association rules with confidence >= 0.6
rules_toy = association_rules(frequent_toy, metric="confidence", min_threshold=0.6)
sorted_rules_toy = rules_toy.sort_values(by="lift", ascending=False)[
    ["antecedents", "consequents", "support", "confidence", "lift"]
]

print("\nAssociation Rules Sorted by Lift:")
print(sorted_rules_toy.to_string(index=False))


# ============================================================
# PART 2: GROCERIES BENCHMARK DATASET
# ============================================================
print("\n\n--- PART 2: REAL-WORLD GROCERIES DATASET ---")

url = "https://raw.githubusercontent.com/stedy/Machine-Learning-with-R-datasets/refs/heads/master/groceries.csv"

response = requests.get(url)
response.raise_for_status()

groceries_transactions = [
    line.strip().split(",")
    for line in response.text.splitlines()
    if line.strip()
]

print(f"Total Transactions Loaded: {len(groceries_transactions)}")
print("Sample First 3 Transactions:", groceries_transactions[:3])

# One-hot encode groceries dataset
te_groc = TransactionEncoder()
encoded_groc = te_groc.fit(groceries_transactions).transform(groceries_transactions)
df_groc = pd.DataFrame(encoded_groc, columns=te_groc.columns_)

print(f"Encoded Dimensions: {df_groc.shape[0]} rows x {df_groc.shape[1]} unique items")

# Mine frequent itemsets with min_support = 0.01 (1%)
frequent_groc = apriori(df_groc, min_support=0.01, use_colnames=True)
print(f"\nDiscovered Frequent Itemsets: {frequent_groc.shape[0]}")
print(frequent_groc.head())

# Generate association rules with confidence >= 0.1 (10%)
rules_groc = association_rules(frequent_groc, metric="confidence", min_threshold=0.1)

# Ensure rules have at least one antecedent and one consequent
rules_groc = rules_groc[
    rules_groc["antecedents"].apply(lambda x: len(x) >= 1)
    & rules_groc["consequents"].apply(lambda x: len(x) >= 1)
]

print(f"\nTotal Association Rules Generated: {rules_groc.shape[0]}")

# Top 5 rules by confidence
top_conf_rules = rules_groc.sort_values(by="confidence", ascending=False)[
    ["antecedents", "consequents", "support", "confidence", "lift"]
].head(5)

print("\nTop 5 Rules by Confidence:")
print(top_conf_rules.to_string(index=False))

# Top 5 rules by lift
top_lift_rules = rules_groc.sort_values(by="lift", ascending=False)[
    ["antecedents", "consequents", "support", "confidence", "lift"]
].head(5)

print("\nTop 5 Rules by Lift:")
print(top_lift_rules.to_string(index=False))
--------------------------------------------------------------------------------
