# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# ### Inspecting the data

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

ratings=pd.read_csv("Beer Data.csv")

ratings.head()

ratings.isnull().sum()

ratings.describe()

ratings.info()

ratings.shape

# Checking for duplicates
ratings[ratings.duplicated(['beer_beerid','review_profilename'],keep='first')]

# Dropping the duplicates based on the first 2 columns
ratings.drop_duplicates(['beer_beerid','review_profilename'],keep='first',inplace=True)

#Dropping the null valued rows
ratings=ratings[~ratings['review_profilename'].isnull()]

# ### Data preparation

# #### Choose only those beers that have at least N number of reviews.

ratings['beer_beerid'].value_counts()

# Highest number of reviews are for '2093' = 977

# Summarising the beer count
ratings['beer_beerid'].value_counts().describe()

counts=ratings['beer_beerid'].value_counts()

type(counts)

# Keeping only those beers that have minimum count of 50 reviews
counts=counts[counts>50]

plt.title('Values of N for different beers')
sns.countplot(x=counts.values)

ratings=ratings[ratings['beer_beerid'].isin(counts[counts > 50].index)]

# Similarly filtering users with atleast 30 reviews
counts_user=ratings['review_profilename'].value_counts()
ratings=ratings[ratings['review_profilename'].isin(counts_user[counts_user > 30].index)]

ratings.shape

# Visualising the distribution
plt.figure(figsize=(20,15))
plt.title('Values of N for different beers')
sns.countplot(x=ratings['beer_beerid'].value_counts())

# This dataset has substancial amount of ratings for every beer from substantial number of users

# ### Data Exploration
# #### What are the unique values of ratings?

pd.Series(ratings['review_overall'].unique()).sort_values()

sns.boxplot(ratings['review_overall'])
plt.title('Range of revies and distribution')
plt.show()

sns.countplot(x='review_overall',data=ratings)

ratings['review_overall'].describe()

# Inspecting average ratings for each beer
ratings.groupby('beer_beerid').review_overall.mean().sort_values(ascending=False)

# # Inspecting average rating by each user
ratings.groupby('review_profilename').review_overall.mean().sort_values(ascending=False)

#The average rating by all users
ratings.groupby('review_profilename').review_overall.mean().values.mean()

# Average user ratings are 3.8

# The average number of ratings given to the beers
counts=ratings['beer_beerid'].value_counts()
counts.values.mean()

# On average each beer has received approx 104 reviews

# The average number of ratings given by the users
counts=ratings['review_profilename'].value_counts()
counts.values.mean()

# On average each user has submitted 86 reviews

# ### Recommendation Models

# Divide your data into training and testing dataset.
from sklearn.model_selection import train_test_split
train, test = train_test_split(ratings, test_size=0.30, random_state=31)

print(train.shape)
print(test.shape)

# pivot ratings into features
df_beer_features = train.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
).fillna(0)

df_beer_features.head()

dummy_train=train.copy()
dummy_test=test.copy()

dummy_train['review_overall'] = dummy_train['review_overall'].apply(lambda x: 0 if x>=1 else 1)
dummy_test['review_overall'] = dummy_test['review_overall'].apply(lambda x: 1 if x>=1 else 0)

# +
# The beers not rated by user is marked as 1 for prediction. 
dummy_train = dummy_train.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
).fillna(1)

# The beer not rated by user is marked as 0 for evaluation. 
dummy_test = dummy_test.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
).fillna(0)
# -

dummy_train.head()

dummy_test.head()

# ### User Similarity Matrix

# +
## Using Cosine Similarity
from sklearn.metrics.pairwise import pairwise_distances

# User Similarity Matrix
user_correlation = 1 - pairwise_distances(df_beer_features, metric='cosine')
user_correlation[np.isnan(user_correlation)] = 0
print(user_correlation)
# -

user_correlation.shape

# ### Adjusted cosine

## Using adjusted Cosine 
beer_features = train.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
)

beer_features.head()

# Normalising the rating of the movie for each user aroung 0 mean
mean = np.nanmean(beer_features, axis=1)
df_subtracted = (beer_features.T-mean).T

df_subtracted.head()

#### Finding cosine similarity
from sklearn.metrics.pairwise import pairwise_distances
# User Similarity Matrix
user_correlation = 1 - pairwise_distances(df_subtracted.fillna(0), metric='cosine')
user_correlation[np.isnan(user_correlation)] = 0
print(user_correlation)

# Visualizing the first 10
plt.figure(figsize=(15,10))
sns.heatmap(user_correlation[0:10,0:10],annot=True)

# ### Prediction

# Prediction
user_correlation[user_correlation<0]=0
user_correlation

plt.figure(figsize=(15,10))
sns.heatmap(user_correlation[0:10,0:10],annot=True)

# Rating predicted by the user (for movies rated as well as not rated) is the weighted 
#sum of correlation with the movie rating (as present in the rating dataset). 
user_predicted_ratings = np.dot(user_correlation, beer_features.fillna(0))
user_predicted_ratings

# Nullifying the existing ratings
user_final_rating = np.multiply(user_predicted_ratings,dummy_train)
user_final_rating.head()

# ### Top 5 recommendations for user 1

user_final_rating.iloc[1].sort_values(ascending=False)[0:5]

# ### # Item Based Similarity

beer_features = train.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
).T

beer_features.head()

# Normalizing
mean = np.nanmean(beer_features, axis=1)
df_subtracted = (beer_features.T-mean).T

# +
from sklearn.metrics.pairwise import pairwise_distances

# User Similarity Matrix
item_correlation = 1 - pairwise_distances(df_subtracted.fillna(0), metric='cosine')
item_correlation[np.isnan(item_correlation)] = 0
print(item_correlation)
# -

# first ten correlation pairs
plt.figure(figsize=(15,10))
sns.heatmap(item_correlation[0:10,0:10],annot=True)

# Taking out negative correlations
item_correlation[item_correlation<0]=0
item_correlation

# ### Predictions

item_predicted_ratings = np.dot((beer_features.fillna(0).T),item_correlation)
item_predicted_ratings

### Filtering the rating only for the movies not rated by the user for recommendation
item_final_rating = np.multiply(item_predicted_ratings,dummy_train)
item_final_rating.head()

# Top 5 prediction for the user 1
item_final_rating.iloc[1].sort_values(ascending=False)[0:5]

# ### Evaluation

# +
test_beer_features = test.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
)
mean = np.nanmean(test_beer_features, axis=1)
test_df_subtracted = (test_beer_features.T-mean).T

# User Similarity Matrix
test_user_correlation = 1 - pairwise_distances(test_df_subtracted.fillna(0), metric='cosine')
test_user_correlation[np.isnan(test_user_correlation)] = 0
print(test_user_correlation)
# -

test_user_correlation[test_user_correlation<0]=0
test_user_predicted_ratings = np.dot(test_user_correlation, test_beer_features.fillna(0))
test_user_predicted_ratings

# +
### Doing prediction for the movies rated by the user
# -

test_user_final_rating = np.multiply(test_user_predicted_ratings,dummy_test)

test_user_final_rating.head()

# +
from sklearn.preprocessing import MinMaxScaler
from numpy import *

X  = test_user_final_rating.copy() 
X = X[X>0].fillna(0)

scaler = MinMaxScaler(feature_range=(1, 5))
print(scaler.fit(X))
y = (scaler.transform(X))

print(y)
# -

test_ = test.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
)

total_non_nan = np.count_nonzero(~np.isnan(y))

rmse = (sum(sum((test_ - y )**2))/total_non_nan)**0.5
print(rmse)

# ### Evaluating item based filtering

# +
test_beer_features = test.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
).T
mean = np.nanmean(test_beer_features, axis=1)
test_df_subtracted = (test_beer_features.T-mean).T

# item Similarity Matrix
test_item_correlation = 1 - pairwise_distances(test_df_subtracted.fillna(0), metric='cosine')
test_item_correlation[np.isnan(test_item_correlation)] = 0
test_item_correlation[test_item_correlation<0]=0
print(test_item_correlation)
# -

test_item_predicted_ratings = (np.dot(test_item_correlation, test_beer_features.fillna(0))).T
test_item_final_rating = np.multiply(test_item_predicted_ratings,dummy_test)
test_item_final_rating.head()

test_ = test.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
)

# +
from sklearn.preprocessing import MinMaxScaler
from numpy import *

X  = test_item_final_rating.copy() 
X = X[X>0].fillna(0)

scaler = MinMaxScaler(feature_range=(1, 5))
print(scaler.fit(X))
y = (scaler.transform(X))

test_ = test.pivot(
    index='review_profilename',
    columns='beer_beerid',
    values='review_overall'
)


# Finding total non-NaN value
total_non_nan = np.count_nonzero(~np.isnan(y))
# -

# RMSE
rmse = (sum(sum((test_ - y )**2))/total_non_nan)**0.5
print(rmse)

# ### Using user based similarity performs better

# User Based Model Should be deployed

# #### Give the names of the top 5 beers that you would recommend to the users 'cokes', 'genog' and 'giblet' using both the models.

# Item based
item_final_rating.loc['cokes'].sort_values(ascending=False)[0:5]

item_final_rating.loc['genog'].sort_values(ascending=False)[0:5]

item_final_rating.loc['giblet'].sort_values(ascending=False)[0:5]

# User based
user_final_rating.loc['cokes'].sort_values(ascending=False)[0:5]

user_final_rating.loc['giblet'].sort_values(ascending=False)[0:5]

user_final_rating.loc['genog'].sort_values(ascending=False)[0:5]





StarcoderdataJupyterScriptsDedupFiltered