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

df = pd.read_csv("groceries.csv")
print("Shape:", df.shape)
print("First 5 rows:\n", df.head())
print("Data types:\n", df.dtypes)
df.info()
print("Summary:\n", df.describe(include="all"))
print("Missing values:\n", df.isnull().sum())

required = ["Member_number", "Date", "itemDescription"]
if not set(required).issubset(df.columns):
    raise ValueError("groceries.csv must contain: " + ", ".join(required))

df = df.dropna(subset=required).copy()
df["itemDescription"] = df["itemDescription"].astype(str).str.strip()
df = df[df["itemDescription"] != ""]

# One transaction contains the items bought by one member on one date.
transactions = df.groupby(
    ["Member_number", "Date"]
)["itemDescription"].apply(list).tolist()

print("Total Transactions:", len(transactions))
if len(transactions) < 10:
    raise ValueError("Use a dataset containing at least 10 transactions.")

print("\nFirst 5 transactions:")
for transaction in transactions[:5]:
    print(transaction)

encoder = TransactionEncoder()
encoded_data = encoder.fit_transform(transactions)
basket = pd.DataFrame(
    encoded_data,
    columns=encoder.columns_
)
print("\nOne-hot encoded basket:\n", basket.head())

frequent_itemsets = apriori(
    basket,
    min_support=0.01,
    use_colnames=True
)
frequent_itemsets = frequent_itemsets.sort_values(
    by="support",
    ascending=False
)

print("\nFrequent Itemsets:")
print(frequent_itemsets.head(10))

if frequent_itemsets.empty or not frequent_itemsets["itemsets"].map(len).gt(1).any():
    print("\nNo frequent itemsets with two or more items; no rules to generate.")
else:
    rules = association_rules(
        frequent_itemsets,
        num_itemsets=len(basket),
        metric="confidence",
        min_threshold=0.1
    )
    rules = rules.sort_values(
        by="lift",
        ascending=False
    )

    print("\nAssociation Rules:")
    if rules.empty:
        print("No rules meet the minimum confidence threshold.")
    else:
        print(rules[[
            "antecedents", "consequents", "support", "confidence", "lift"
        ]].to_string(index=False))
