# -*- coding: utf-8 -*-
"""Market Basket Analysis.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1ZZhHNM1SPbjL7o3FZ7XJPihM5yhNnQz0
"""

import pandas as pd

from google.colab import drive
drive.mount('/content/drive')

path = '/content/drive/My Drive/Colab Notebooks/Datasets/basket_analysis.csv'

df = pd.read_csv(path)

df = pd.DataFrame(df)

df.head()

from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, fpgrowth
from mlxtend.frequent_patterns import association_rules

"""Preprocessing the data"""

# removing column 0 as it's unnecessary
df = df.drop(columns=['Unnamed: 0'])
transactions = df.apply(lambda row: df.columns[row.values], axis=1).tolist()

df.head()

"""Applying apriori algorithm"""

te = TransactionEncoder()
te_ary = te.fit(transactions).transform(transactions)
df_encoded = pd.DataFrame(te_ary, columns=te.columns_)
frequent_itemsets_apriori = apriori(df_encoded, min_support=0.1, use_colnames=True)

"""Generating association rules for apriori"""

association_rules_apriori = association_rules(frequent_itemsets_apriori, metric='confidence', min_threshold=0.5)

print("Association Rules (Apriori):")
print(association_rules_apriori)

"""FP growth algorithm"""

frequent_itemsets_fpgrowth = fpgrowth(df_encoded, min_support=0.1, use_colnames=True)

"""Association rules for FP growth"""

association_rules_fpgrowth = association_rules(frequent_itemsets_fpgrowth, metric='confidence', min_threshold=0.5)

print("Association Rules (FP-Growth):")
print(association_rules_fpgrowth)

"""Comparing performance and results"""

apriori_freq_itemsets_count = frequent_itemsets_apriori.shape[0]
fpgrowth_freq_itemsets_count = frequent_itemsets_fpgrowth.shape[0]

"""Comparing no. of associations"""

apriori_association_rules_count = association_rules_apriori.shape[0]
fpgrowth_association_rules_count = association_rules_fpgrowth.shape[0]

"""Compare average antecedent and consequent lengths"""

apriori_avg_antecedent_len = association_rules_apriori.antecedents.apply(lambda x: len(x)).mean()
apriori_avg_consequent_len = association_rules_apriori.consequents.apply(lambda x: len(x)).mean()
fpgrowth_avg_antecedent_len = association_rules_fpgrowth.antecedents.apply(lambda x: len(x)).mean()
fpgrowth_avg_consequent_len = association_rules_fpgrowth.consequents.apply(lambda x: len(x)).mean()

"""Comparison Results"""

print("Comparison Results:")
print("-" * 50)
print("Frequent Itemsets Count:")
print("Apriori: ", apriori_freq_itemsets_count)
print("FP-Growth: ", fpgrowth_freq_itemsets_count)
print("-" * 50)
print("Number of Association Rules:")
print("Apriori: ", apriori_association_rules_count)
print("FP-Growth: ", fpgrowth_association_rules_count)
print("-" * 50)
print("Average Antecedent Length:")
print("Apriori: ", apriori_avg_antecedent_len)
print("FP-Growth: ", fpgrowth_avg_antecedent_len)
print("-" * 50)
print("Average Consequent Length:")
print("Apriori: ", apriori_avg_consequent_len)
print("FP-Growth: ", fpgrowth_avg_consequent_len)
print("-" * 50)

comparison_df = pd.DataFrame({
    'Algorithm': ['Apriori', 'FP-Growth'],
    'Frequent Itemsets Count': [apriori_freq_itemsets_count, fpgrowth_freq_itemsets_count],
    'Association Rules Count': [apriori_association_rules_count, fpgrowth_association_rules_count],
    'Average Antecedent Length': [apriori_avg_antecedent_len, fpgrowth_avg_antecedent_len],
    'Average Consequent Length': [apriori_avg_consequent_len, fpgrowth_avg_consequent_len]
})

import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(10, 6))
sns.barplot(data=comparison_df, x='Algorithm', y='Frequent Itemsets Count')
plt.title('Comparison of Frequent Itemsets Count')
plt.show()

"""### Conclusions based on the above dataset:

1. **Frequent itemsets count**: The FP-Growth algorithm outperforms the Apriori algorithm in terms of generating frequent itemsets. It produces a smaller number of frequent itemsets, indicating better efficiency and scalability.

2. **No. of Association rules**: The Apriori algorithm generates a higher number of association rules compared to the FP-Growth algorithm. This suggests that the Apriori algorithm explores more potential associations between items in the dataset.

3. **Avg. antecedent and consequent length**: The Apriori algorithm tends to produce association rules with longer antecedents and consequents compared to the FP-Growth algorithm.
---
* If efficiency and scalability are crucial, and a smaller number of frequent itemsets is sufficient for analysis, the FP-Growth algorithm is a better choice.
* If generating a larger number of association rules and exploring more detailed patterns is a priority, the Apriori algorithm can be preferred.
* The choice of algorithm should be based on the specific requirements and constraints of the analysis, such as dataset size, computational resources, and the desired level of granularity in the association rules.

**Result Interpretation:**


---


**Apriori Algorithm Results**:
* Bread --> Yogurt: Customers who purchase Bread are also likely to purchase Yogurt with a confidence of 50.26%.
* Ice cream --> Butter: Customers who purchase Ice cream are also likely to purchase Butter with a confidence of 50.49%.
* Dill --> Chocolate: Customers who purchase Dill are also likely to purchase Chocolate with a confidence of 50%.
* Milk --> Chocolate: Customers who purchase Milk are also likely to purchase Chocolate with a confidence of 52.1%.
* Chocolate --> Milk: Customers who purchase Chocolate are also likely to purchase Milk with a confidence of 50.12%.



---

**FP-Growth Algorithm Results**:
* Ice cream, Butter --> Chocolate: Customers who purchase Ice cream and Butter are also likely to purchase Chocolate with a confidence of 52.66%.
* Ice cream, Chocolate --> Butter: Customers who purchase Ice cream and Chocolate are also likely to purchase Butter with a confidence of 53.96%.
* Butter, Chocolate --> Ice cream: Customers who purchase Butter and Chocolate are also likely to purchase Ice cream with a confidence of 53.96%.
* Ice cream, Butter --> Sugar: Customers who purchase Ice cream and Butter are also likely to purchase Sugar with a confidence of 51.21%.
* Dill, Unicorn --> Chocolate: Customers who purchase Dill and Unicorn are also likely to purchase Chocolate with a confidence of 60.12%.

### Neural Collaborative Filtering algorithm
"""

import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Embedding, Concatenate, Flatten, Dense, Dropout
from tensorflow.keras.optimizers import Adam

def prepare_data_for_ncf(df):
    num_items = len(df.columns) - 1
    interactions = []
    for _, row in df.iterrows():
        user_items = np.where(row.iloc[1:].values)[0].tolist()
        interactions.append(user_items)
    return interactions

# Convert interactions to one-hot encoded vectors
def one_hot_encode(interactions, num_items):
    encoded_interactions = np.zeros((len(interactions), num_items))
    for i, user_items in enumerate(interactions):
        for item_index in user_items:
            encoded_interactions[i, item_index] = 1
    return encoded_interactions

# Prepare the data
interactions = prepare_data_for_ncf(df)
num_items = len(df.columns) - 1
encoded_interactions = one_hot_encode(interactions, num_items)

# Create train and test splits
split_ratio = 0.8
split_index = int(split_ratio * len(encoded_interactions))
X_train, X_test = encoded_interactions[:split_index], encoded_interactions[split_index:]

# Build the NCF model
def build_ncf_model(num_users, num_items, embedding_dim=50, dense_dim=64):
    user_input = Input(shape=(num_items,), name='user_input')
    item_input = Input(shape=(num_items,), name='item_input')

    user_embedding = Embedding(input_dim=num_users, output_dim=embedding_dim, name='user_embedding')(user_input)
    item_embedding = Embedding(input_dim=num_items, output_dim=embedding_dim, name='item_embedding')(item_input)

    user_flatten = Flatten()(user_embedding)
    item_flatten = Flatten()(item_embedding)

    concat = Concatenate()([user_flatten, item_flatten])
    dropout = Dropout(0.2)(concat)
    dense1 = Dense(dense_dim, activation='relu')(dropout)
    dense2 = Dense(dense_dim, activation='relu')(dense1)
    output = Dense(num_items, activation='softmax')(dense2)

    model = Model(inputs=[user_input, item_input], outputs=output)
    return model

# Create and compile the model
model = build_ncf_model(len(df), num_items)
model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.001), metrics=['accuracy'])

# Train the model
num_epochs = 10
batch_size = 32
history = model.fit([X_train, X_train], X_train, epochs=num_epochs, batch_size=batch_size, validation_split=0.2)

# Make recommendations for users
def make_recommendations(user_index, model, num_items):
    user_vector = np.zeros((1, num_items))  # Reshape the user vector to (1, num_items)
    user_vector[0, user_index] = 1
    predictions = model.predict([user_vector, user_vector])  # Pass the user vector as a list
    recommended_items_indices = predictions.argsort()[0][::-1][:5]
    recommended_items = df.columns[1:][recommended_items_indices].tolist()
    return recommended_items

# Test the model
user_index_to_test = 0
recommendations = make_recommendations(user_index_to_test, model, num_items)
print(f"Top 5 recommendations for user {user_index_to_test}: {recommendations}")

"""### Highlights and interpretation based on above results

* User 0 Recommendations: For User 0, the model has provided the top 5 recommended items based on their previous interactions with items. The recommendations are ['Kidney Beans', 'Cheese', 'Sugar', 'Milk', 'Unicorn'].

* Interpreting the Recommendations: The model's recommendations are based on the user's interactions with items. The items that the user has previously interacted with (e.g., purchased, viewed, etc.) are used to make predictions for items the user might be interested in. The model then ranks items based on the predicted likelihood of the user interacting with them.

* Item Names: The item names 'Kidney Beans', 'Cheese', 'Sugar', 'Milk', and 'Unicorn' correspond to the items represented by the indices in the DataFrame. It is essential to have a mapping between item indices and item names to interpret the results correctly.

* Confidence Level: The model's predictions are based on a binary classification (market basket analysis) where the output is a probability score between 0 and 1. In your case, the threshold used to determine whether an item is recommended or not seems to be set to 0.5. Items with a prediction probability greater than 0.5 are considered recommended.
"""