# -*- coding: utf-8 -*-
"""Collaborative filtering movielens dataset.ipynb

Automatically generated by Colaboratory.

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

# Commented out IPython magic to ensure Python compatibility.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# %matplotlib inline

import seaborn as sns

"""## Data link
kaggle: https://www.kaggle.com/shubhammehta21/movie-lens-small-latest-dataset
"""

rating_data = pd.read_csv("drive/My Drive/colab notebooks/ratings.csv")
movie_data = pd.read_csv("drive/My Drive/colab notebooks/movies.csv")

rating_data

movie_data

rating_data.info()

rating_data['rating'].unique()

rating_data['rating'].value_counts()

plt.figure(figsize = (10,8))
rating_data['rating'].value_counts().plot(kind = 'barh')
plt.title("Distribution Count of Ratings", fontsize = 14, color = 'blue')
plt.xticks(rotation = 45, fontsize = 12)
plt.yticks(fontsize = 12)
plt.show()

plt.figure(figsize = (10,8))
rating_data['rating'].value_counts().plot(kind = 'pie')
plt.title("Distribution Count of Ratings", fontsize = 14, color = 'blue')
plt.xticks(rotation = 45, fontsize = 12)
plt.yticks(fontsize = 12)
plt.show()

rating_data = rating_data.drop("timestamp", axis=1)

rating_data

# find out all users
len(rating_data['userId'].unique())

len(rating_data['movieId'].unique())

#check missing values
rating_data.isna().sum().sum()

"""## Users vs Movies 
- users = rows
- movies = columns.
- 610 users and 9724 columns. 
 - so a matrix of size 610*9724.

 # User-based similarity
"""

user_movies_data = rating_data.pivot(index = 'userId',
                                     columns = 'movieId',
                                     values = 'rating').reset_index(drop = True)

user_movies_data

user_movies_data.fillna(0, inplace = True)

user_movies_data

user_movies_data.iloc[0:5, 0:15]

"""# Recommendation Engine
- Association Rule (Bread and Butter example on e-commerce)
 - using apriori algorithm (support, lift and confidence)
- Collaborative Filtering (Rating based)
 - Euclidean Distance
 - Cosine Similarity
- Matrix factorization

- Hybrid technique

## Cosine Similarity
"""

from sklearn.metrics import pairwise_distances
from scipy.spatial.distance import cosine, correlation

user_similarity = 1 - pairwise_distances(user_movies_data.values, metric = 'cosine')
#store the results in a dataframe
user_similarity_data = pd.DataFrame(user_similarity, index = rating_data.userId.unique(),
                                    columns = rating_data.userId.unique())

user_similarity_data

user_similarity_data.iloc[0:5, 0:5]

user_similarity_data.shape

np.fill_diagonal(user_similarity, 0)

user_similarity_data.iloc[0:5, 0:5]

"""## Narrative
- Filled diagonal with "0" from "1" so we must avoid noise in data by means of self selected pairs.

## Filter similar users
"""

user_similarity_data.idxmax(axis=1)[0:10]

user_similarity_data.iloc[4:5, 465:475]

"""#narrative
- user 5 is similar to user 470

## movie_data part
"""

movie_data[0:5]

movie_data.drop("genres", axis=1, inplace = True)

movie_data[0:5]

"""## finding common movies of similar users"""

def get_user_similar_movies(user_1, user_2):
  # inner join b/w two movies watched between 2 users will give the common movies
  common_movies = rating_data[rating_data.userId == user_1].merge(
      rating_data[rating_data.userId == user_2],
      on = 'movieId',
      how = 'inner'
  )

  #return
  return common_movies.merge(movie_data, on = 'movieId')

get_common_movies = get_user_similar_movies(2, 366)

get_common_movies

get_common_movies[(get_common_movies.rating_x >= 4.0) &
                  ((get_common_movies.rating_y >= 4.0))]

"""## When should we do "User-based similarity"?
- Only good for past analysis and recommendation.
- Once a new user comes, it will not make any sense to find pairwise distance with the previous users as the new user would not have rated any movies.

## Item-based similarity
"""

movies_similarity = rating_data.pivot(index = 'movieId',
                                      columns = 'userId',
                                      values = 'rating').reset_index(drop = True)

movies_similarity

#fill the NaN with 0
movies_similarity.fillna(0, inplace =True)

movies_similarity

# find the correlation between the movies
movies_similarity_corr = 1 - pairwise_distances(movies_similarity.values, metric = 'correlation')

movies_similarity_corr

#store in a dataframe
movies_similarity_corr_df = pd.DataFrame(movies_similarity_corr, index = rating_data.movieId.unique(),
                                      columns = rating_data.movieId.unique())

movies_similarity_corr_df

np.fill_diagonal(movies_similarity_corr, 0)

movies_similarity_corr_df.iloc[0:5, 0:5]

movies_similarity_corr_df.shape

"""# to find most similar movies"""

def find_similar_movies(movie_id, top_suggestion_number = 4):
  #index of the movie record in dataframe
  movie_index = movie_data[movie_data.movieId == movie_id].index[0]
  movie_data['similarity'] = movies_similarity_corr_df.iloc[movie_index]
  top_suggestion_number = movie_data.sort_values(['similarity'], ascending= False) [0:top_suggestion_number]

  return top_suggestion_number

movie_data[movie_data.movieId == 2]

find_similar_movies(2)

