Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save j-nila/cad01edc47f369547e9ea07b034a42cd to your computer and use it in GitHub Desktop.
Save j-nila/cad01edc47f369547e9ea07b034a42cd to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# KNN Based Collaborative Filtering In Python Using Surprise "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data Exploration "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### About the dataset\n",
"Dataset for collaborative filtering usually contains 3 columns. user id , itemid and rating. We have a related dataset for details of users and items separately so that we can do some analysis on those items and users. items can either br movies , books, products or anything that a user can rate.\n",
"In our case we are going to use a movies dataset and we 'll keep our dataset simple for illustration purpose.\n",
"\n",
"\n",
"Our dataset is for illustration purpose so I have kept it simple and easily comprehendible. In practice you 'll find much more complex dataset. In our case we have 5 users rating 10 movies. There are total 50 ratings possible out of which 30 are available since some of the users have not rated some of the movies.The movies selected are top 10 movies from imdb top 250 movies. User names and ratings are fictitious."
]
},
{
"cell_type": "code",
"execution_count": 440,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"ratings_ds = pd.read_csv('data/sample/movies_ratings.csv')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"lets explore the full dataset. Since number of records is small we can have a look on all of them otherwise you'd want to use head() , describe or other functions from pandas to get a sense of the data."
]
},
{
"cell_type": "code",
"execution_count": 441,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>userName</th>\n",
" <th>userId</th>\n",
" <th>movieName</th>\n",
" <th>movieId</th>\n",
" <th>rating</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>1</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Godfather</td>\n",
" <td>2</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Godfather: Part II</td>\n",
" <td>3</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Dark Knight</td>\n",
" <td>4</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>12 Angry Men</td>\n",
" <td>5</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>Schindler's List</td>\n",
" <td>6</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Lord of the Rings: The Return of the King</td>\n",
" <td>7</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>Pulp Fiction</td>\n",
" <td>8</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Good, the Bad and the Ugly</td>\n",
" <td>9</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>Fight Club</td>\n",
" <td>10</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>Bob</td>\n",
" <td>2</td>\n",
" <td>The Godfather: Part II</td>\n",
" <td>3</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>Bob</td>\n",
" <td>2</td>\n",
" <td>The Dark Knight</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>Bob</td>\n",
" <td>2</td>\n",
" <td>12 Angry Men</td>\n",
" <td>5</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>Bob</td>\n",
" <td>2</td>\n",
" <td>Fight Club</td>\n",
" <td>10</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>Bob</td>\n",
" <td>2</td>\n",
" <td>The Good, the Bad and the Ugly</td>\n",
" <td>9</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>Carl</td>\n",
" <td>3</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16</th>\n",
" <td>Carl</td>\n",
" <td>3</td>\n",
" <td>The Godfather</td>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>17</th>\n",
" <td>Carl</td>\n",
" <td>3</td>\n",
" <td>The Godfather: Part II</td>\n",
" <td>3</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>18</th>\n",
" <td>Carl</td>\n",
" <td>3</td>\n",
" <td>Pulp Fiction</td>\n",
" <td>8</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19</th>\n",
" <td>Carl</td>\n",
" <td>3</td>\n",
" <td>The Good, the Bad and the Ugly</td>\n",
" <td>9</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>20</th>\n",
" <td>Deb</td>\n",
" <td>4</td>\n",
" <td>The Godfather</td>\n",
" <td>2</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21</th>\n",
" <td>Deb</td>\n",
" <td>4</td>\n",
" <td>The Godfather: Part II</td>\n",
" <td>3</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>22</th>\n",
" <td>Deb</td>\n",
" <td>4</td>\n",
" <td>The Dark Knight</td>\n",
" <td>4</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>23</th>\n",
" <td>Deb</td>\n",
" <td>4</td>\n",
" <td>12 Angry Men</td>\n",
" <td>5</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>24</th>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>Schindler's List</td>\n",
" <td>6</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25</th>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>Pulp Fiction</td>\n",
" <td>8</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>26</th>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>The Good, the Bad and the Ugly</td>\n",
" <td>9</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>27</th>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>1</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>28</th>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>The Dark Knight</td>\n",
" <td>4</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>29</th>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>Fight Club</td>\n",
" <td>10</td>\n",
" <td>3</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" userName userId movieName movieId \\\n",
"0 Alice 1 The Shawshank Redemption   1 \n",
"1 Alice 1 The Godfather 2 \n",
"2 Alice 1 The Godfather: Part II 3 \n",
"3 Alice 1 The Dark Knight  4 \n",
"4 Alice 1 12 Angry Men 5 \n",
"5 Alice 1 Schindler's List 6 \n",
"6 Alice 1 The Lord of the Rings: The Return of the King 7 \n",
"7 Alice 1 Pulp Fiction 8 \n",
"8 Alice 1 The Good, the Bad and the Ugly  9 \n",
"9 Alice 1 Fight Club 10 \n",
"10 Bob 2 The Godfather: Part II 3 \n",
"11 Bob 2 The Dark Knight  4 \n",
"12 Bob 2 12 Angry Men 5 \n",
"13 Bob 2 Fight Club 10 \n",
"14 Bob 2 The Good, the Bad and the Ugly  9 \n",
"15 Carl 3 The Shawshank Redemption   1 \n",
"16 Carl 3 The Godfather 2 \n",
"17 Carl 3 The Godfather: Part II 3 \n",
"18 Carl 3 Pulp Fiction 8 \n",
"19 Carl 3 The Good, the Bad and the Ugly  9 \n",
"20 Deb 4 The Godfather 2 \n",
"21 Deb 4 The Godfather: Part II 3 \n",
"22 Deb 4 The Dark Knight  4 \n",
"23 Deb 4 12 Angry Men 5 \n",
"24 Earl 5 Schindler's List 6 \n",
"25 Earl 5 Pulp Fiction 8 \n",
"26 Earl 5 The Good, the Bad and the Ugly  9 \n",
"27 Earl 5 The Shawshank Redemption   1 \n",
"28 Earl 5 The Dark Knight  4 \n",
"29 Earl 5 Fight Club 10 \n",
"\n",
" rating \n",
"0 4 \n",
"1 3 \n",
"2 1 \n",
"3 5 \n",
"4 3 \n",
"5 3 \n",
"6 2 \n",
"7 2 \n",
"8 3 \n",
"9 1 \n",
"10 5 \n",
"11 4 \n",
"12 4 \n",
"13 4 \n",
"14 2 \n",
"15 1 \n",
"16 1 \n",
"17 5 \n",
"18 2 \n",
"19 3 \n",
"20 5 \n",
"21 1 \n",
"22 1 \n",
"23 4 \n",
"24 2 \n",
"25 3 \n",
"26 4 \n",
"27 5 \n",
"28 1 \n",
"29 3 "
]
},
"execution_count": 441,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ratings_ds"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Follwoing are the different number of ratings by each user. As we can see rating 1 is given 7 times and 5 only 5 times."
]
},
{
"cell_type": "code",
"execution_count": 442,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x1a2598f690>"
]
},
"execution_count": 442,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"ratings_ds.groupby('rating').count()['userId'].plot.bar()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Following graph shows how many users have rated a particular movie. As we can see The lord of the rings is rated least amount of time."
]
},
{
"cell_type": "code",
"execution_count": 443,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x1a25cca110>"
]
},
"execution_count": 443,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"ratings_ds.groupby('movieName').count()['userId'].plot.bar()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The below graph shows that the mean rating is highest for the 12 Angry Men and lowest for the Lord of the rings."
]
},
{
"cell_type": "code",
"execution_count": 444,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x1a25c669d0>"
]
},
"execution_count": 444,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"ratings_ds.groupby('movieName').mean()['rating'].plot.bar()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lets check in heatmap what are the different ratings given by each user to the movies."
]
},
{
"cell_type": "code",
"execution_count": 445,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x1a25f44c90>"
]
},
"execution_count": 445,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"sns.heatmap(pd.crosstab(ratings_ds['userName'], ratings_ds['rating']), cmap=\"YlGnBu\", cbar=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As We can see Alice has rated most of the moves as 3 and Bob as four. This is another property for user based rating data. Some of the users rate very casually and some very strictly. We factor in this scenario by subtracting mean rating of a user from all the ratings of that user. "
]
},
{
"cell_type": "code",
"execution_count": 446,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x1a2604c190>"
]
},
"execution_count": 446,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"sns.heatmap(pd.crosstab(ratings_ds['movieName'], ratings_ds['rating']), cmap=\"YlGnBu\", cbar=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"From Above plot we can see that Godfather part 2 has got most number of 5 ratings as well as most number of 1 rating. That means either users have highly liked it they they have highly disliked it. \"12 Angry Men \" mostly got ratings as 3 or 4. It will be interesting to see if these facts are somehow reflected in our modelling"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Modeling "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lets create a recommendation model using surprise package. I am going to use KNNWithMeans model. I'll be tweaking some of the parameters of the model."
]
},
{
"cell_type": "code",
"execution_count": 447,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"from surprise import SVD\n",
"from surprise import Dataset\n",
"from surprise import accuracy\n",
"from surprise.model_selection import train_test_split\n",
"from surprise import KNNBasic, KNNWithMeans, KNNBaseline\n",
"from surprise.model_selection import KFold\n",
"from surprise import Reader\n",
"from surprise import NormalPredictor\n",
"from surprise.model_selection import cross_validate\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"from surprise.model_selection import GridSearchCV"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lets create our dataset based on which we 'll train and test our model. We'll also create a antiset that will be used for recommendations."
]
},
{
"cell_type": "code",
"execution_count": 448,
"metadata": {},
"outputs": [],
"source": [
"reader = Reader(rating_scale=(1, 5))\n",
"# The columns must correspond to user id, item id and ratings (in that order).\n",
"data = Dataset.load_from_df(ratings_ds[['userId', 'movieId', 'rating']], reader)\n",
"anti_set = data.build_full_trainset().build_anti_testset()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An antiset is a set of those user and item pairs for which a rating doesn't exist in original dataset. This is the set for which we are trying to predict ratings. For example in following example userId 2 that is Bob has not rated movieID 1 that is The Shawshank Redemption. \n",
"Surprise creates a set of such combinations by providing a default average rating. We 'll be calculating an estimated rating for this set using our model."
]
},
{
"cell_type": "code",
"execution_count": 449,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"[(2, 1, 2.9),\n",
" (2, 2, 2.9),\n",
" (2, 6, 2.9),\n",
" (2, 7, 2.9),\n",
" (2, 8, 2.9),\n",
" (3, 4, 2.9),\n",
" (3, 5, 2.9),\n",
" (3, 6, 2.9),\n",
" (3, 7, 2.9),\n",
" (3, 10, 2.9),\n",
" (4, 1, 2.9),\n",
" (4, 6, 2.9),\n",
" (4, 7, 2.9),\n",
" (4, 8, 2.9),\n",
" (4, 9, 2.9),\n",
" (4, 10, 2.9),\n",
" (5, 2, 2.9),\n",
" (5, 3, 2.9),\n",
" (5, 5, 2.9),\n",
" (5, 7, 2.9)]"
]
},
"execution_count": 449,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"anti_set"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll create movies and user's dataframe to join them with the dataframes that contain user and movieid's."
]
},
{
"cell_type": "code",
"execution_count": 450,
"metadata": {},
"outputs": [],
"source": [
"movies = ratings_ds[['movieId' , 'movieName']].drop_duplicates(['movieId' , 'movieName'])\n",
"users = ratings_ds[['userId' , 'userName']].drop_duplicates(['userId' , 'userName'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"KNN basic with default parameters"
]
},
{
"cell_type": "code",
"execution_count": 451,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Computing the msd similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 1.7945\n",
"Computing the msd similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.5418\n",
"Computing the msd similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 1.3928\n"
]
}
],
"source": [
"kf = KFold(n_splits=3)\n",
"algo = KNNBasic()\n",
"best_algo = None\n",
"best_rmse = 1000.0\n",
"best_pred = None\n",
"for trainset, testset in kf.split(data):\n",
" # train and test algorithm.\n",
" algo.fit(trainset)\n",
" predictions = algo.test(testset)\n",
" # Compute and print Root Mean Squared Error\n",
" rmse = accuracy.rmse(predictions, verbose=True)\n",
" if rmse < best_rmse:\n",
" best_algo = algo\n",
" best_pred = predictions\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 452,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[Prediction(uid=1, iid=2, r_ui=3.0, est=5, details={'actual_k': 1, 'was_impossible': False}),\n",
" Prediction(uid=4, iid=3, r_ui=1.0, est=1, details={'actual_k': 1, 'was_impossible': False}),\n",
" Prediction(uid=5, iid=9, r_ui=4.0, est=2.9999999999999996, details={'actual_k': 2, 'was_impossible': False}),\n",
" Prediction(uid=2, iid=5, r_ui=4.0, est=3.05, details={'was_impossible': True, 'reason': 'Not enough neighbors.'}),\n",
" Prediction(uid=2, iid=9, r_ui=2.0, est=3.0000000000000004, details={'actual_k': 2, 'was_impossible': False}),\n",
" Prediction(uid=5, iid=6, r_ui=2.0, est=3.0, details={'actual_k': 1, 'was_impossible': False}),\n",
" Prediction(uid=3, iid=2, r_ui=1.0, est=3.05, details={'was_impossible': True, 'reason': 'Not enough neighbors.'}),\n",
" Prediction(uid=1, iid=5, r_ui=3.0, est=4.0, details={'actual_k': 1, 'was_impossible': False}),\n",
" Prediction(uid=2, iid=4, r_ui=4.0, est=1.5161290322580645, details={'actual_k': 2, 'was_impossible': False}),\n",
" Prediction(uid=3, iid=8, r_ui=2.0, est=2.354430379746835, details={'actual_k': 2, 'was_impossible': False})]"
]
},
"execution_count": 452,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"best_pred"
]
},
{
"cell_type": "code",
"execution_count": 453,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.1756\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.0055\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.2148\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.3694\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.2428\n",
"2.0055133068899793\n"
]
}
],
"source": [
"kf = KFold(n_splits=5)\n",
"sim_options = {'name':'cosine'}\n",
"algo = KNNWithMeans(sim_options = sim_options)\n",
"best_algo = None\n",
"best_rmse = 1000.0\n",
"best_pred = None\n",
"for trainset, testset in kf.split(data):\n",
" # train and test algorithm.\n",
" algo.fit(trainset)\n",
" predictions = algo.test(testset)\n",
" # Compute and print Root Mean Squared Error\n",
" rmse = accuracy.rmse(predictions, verbose=True)\n",
" if rmse < best_rmse:\n",
" best_algo = algo\n",
" best_rmse= rmse\n",
" best_pred = predictions\n",
"print(best_rmse)"
]
},
{
"cell_type": "code",
"execution_count": 454,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"[Prediction(uid=1, iid=3, r_ui=1.0, est=3.3846312031704846, details={'actual_k': 3, 'was_impossible': False}),\n",
" Prediction(uid=2, iid=9, r_ui=2.0, est=4.72213595499958, details={'actual_k': 2, 'was_impossible': False}),\n",
" Prediction(uid=1, iid=9, r_ui=3.0, est=3.3075798466104294, details={'actual_k': 2, 'was_impossible': False}),\n",
" Prediction(uid=3, iid=2, r_ui=1.0, est=4.1875, details={'actual_k': 2, 'was_impossible': False}),\n",
" Prediction(uid=5, iid=8, r_ui=3.0, est=2.125, details={'actual_k': 1, 'was_impossible': False}),\n",
" Prediction(uid=3, iid=8, r_ui=2.0, est=2.125, details={'actual_k': 1, 'was_impossible': False})]"
]
},
"execution_count": 454,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"best_pred"
]
},
{
"cell_type": "code",
"execution_count": 455,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Estimating biases using als...\n",
"Computing the msd similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.1314\n",
"Estimating biases using als...\n",
"Computing the msd similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.2056\n",
"Estimating biases using als...\n",
"Computing the msd similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.3116\n"
]
}
],
"source": [
"kf = KFold(n_splits=3)\n",
"algo = KNNBaseline(k=3)\n",
"best_algo = None\n",
"best_rmse = 1000.0\n",
"best_pred = None\n",
"for trainset, testset in kf.split(data):\n",
" # train and test algorithm.\n",
" algo.fit(trainset)\n",
" predictions = algo.test(testset)\n",
" # Compute and print Root Mean Squared Error\n",
" rmse = accuracy.rmse(predictions, verbose=True)\n",
" if rmse < best_rmse:\n",
" best_rmse = rmse\n",
" best_algo = algo\n",
" best_pred = predictions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Changing Similarity options for Algorithm\n",
"\n",
"We can see from the output of above modelling that the similarity measre used was MSD. IF we want to use cosine similarity instead of default MSD based similarity then we can pass sim_options dictionary to in following way.\n",
"Also if We cant to use user_based collaborative filtering instead of item based based collabarative filtering we can set user_based option to True."
]
},
{
"cell_type": "code",
"execution_count": 464,
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.2057\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 0.9548\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.1218\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 2.4499\n",
"Computing the cosine similarity matrix...\n",
"Done computing similarity matrix.\n",
"RMSE: 1.7273\n"
]
}
],
"source": [
"sim_options = { 'name': 'cosine' ,'user_based': False}\n",
"kf = KFold(n_splits=5)\n",
"algo = KNNWithMeans(k =3 , sim_options = sim_options)\n",
"best_algo = None\n",
"best_rmse = 1000.0\n",
"best_pred = None\n",
"for trainset, testset in kf.split(data):\n",
" # train and test algorithm.\n",
" algo.fit(trainset)\n",
" predictions = algo.test(testset)\n",
" # Compute and print Root Mean Squared Error\n",
" rmse = accuracy.rmse(predictions, verbose=True)\n",
" if rmse < best_rmse:\n",
" best_rmse= rmse\n",
" best_algo = algo\n",
" best_pred = predictions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Analysis of Predictions "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lets see how our best prediction has faired on existing set of ratings test data"
]
},
{
"cell_type": "code",
"execution_count": 465,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uid</th>\n",
" <th>iid</th>\n",
" <th>userName</th>\n",
" <th>userId</th>\n",
" <th>movieName</th>\n",
" <th>movieId</th>\n",
" <th>est</th>\n",
" <th>rating</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>5</td>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>12 Angry Men</td>\n",
" <td>5</td>\n",
" <td>3.000000</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>5</td>\n",
" <td>6</td>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>Schindler's List</td>\n",
" <td>6</td>\n",
" <td>2.861111</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2</td>\n",
" <td>3</td>\n",
" <td>Bob</td>\n",
" <td>2</td>\n",
" <td>The Godfather: Part II</td>\n",
" <td>3</td>\n",
" <td>3.171382</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>5</td>\n",
" <td>8</td>\n",
" <td>Earl</td>\n",
" <td>5</td>\n",
" <td>Pulp Fiction</td>\n",
" <td>8</td>\n",
" <td>2.416667</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>1</td>\n",
" <td>3.305556</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>1</td>\n",
" <td>2</td>\n",
" <td>Alice</td>\n",
" <td>1</td>\n",
" <td>The Godfather</td>\n",
" <td>2</td>\n",
" <td>3.750000</td>\n",
" <td>3</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" uid iid userName userId movieName movieId est \\\n",
"0 1 5 Alice 1 12 Angry Men 5 3.000000 \n",
"1 5 6 Earl 5 Schindler's List 6 2.861111 \n",
"2 2 3 Bob 2 The Godfather: Part II 3 3.171382 \n",
"3 5 8 Earl 5 Pulp Fiction 8 2.416667 \n",
"4 1 1 Alice 1 The Shawshank Redemption   1 3.305556 \n",
"5 1 2 Alice 1 The Godfather 2 3.750000 \n",
"\n",
" rating \n",
"0 3 \n",
"1 2 \n",
"2 5 \n",
"3 3 \n",
"4 4 \n",
"5 3 "
]
},
"execution_count": 465,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pred_df = pd.DataFrame(best_pred).merge(ratings_ds , left_on = ['uid', 'iid'], right_on = ['userId', 'movieId'])\n",
"pred_df[['uid', 'iid', 'userName', 'userId', 'movieName', 'movieId', 'est','rating']]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Well not very well for predicting The Dark Knight for Earl but for other movies it is fairly close.\n",
"Now lets try to predict ratings for movies for those users who have not watched them earlier.\n",
"Remember we created an antiset for such pairs. Lets apply our best algo on that set"
]
},
{
"cell_type": "code",
"execution_count": 466,
"metadata": {},
"outputs": [],
"source": [
"anti_pre = best_algo.test(anti_set)\n",
"pred_df = pd.DataFrame(anti_pre).merge(movies , left_on = ['iid'], right_on = ['movieId'])\n",
"pred_df = pd.DataFrame(pred_df).merge(users , left_on = ['uid'], right_on = ['userId'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Following are the predicted rating for the users"
]
},
{
"cell_type": "code",
"execution_count": 467,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uid</th>\n",
" <th>iid</th>\n",
" <th>r_ui</th>\n",
" <th>est</th>\n",
" <th>details</th>\n",
" <th>movieId</th>\n",
" <th>movieName</th>\n",
" <th>userId</th>\n",
" <th>userName</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" <td>2.9</td>\n",
" <td>3.311469</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>1</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>2</td>\n",
" <td>Bob</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2.9</td>\n",
" <td>4.991123</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>2</td>\n",
" <td>The Godfather</td>\n",
" <td>2</td>\n",
" <td>Bob</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2</td>\n",
" <td>6</td>\n",
" <td>2.9</td>\n",
" <td>2.944444</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>6</td>\n",
" <td>Schindler's List</td>\n",
" <td>2</td>\n",
" <td>Bob</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2</td>\n",
" <td>7</td>\n",
" <td>2.9</td>\n",
" <td>3.000000</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>7</td>\n",
" <td>The Lord of the Rings: The Return of the King</td>\n",
" <td>2</td>\n",
" <td>Bob</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2</td>\n",
" <td>8</td>\n",
" <td>2.9</td>\n",
" <td>2.686264</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>8</td>\n",
" <td>Pulp Fiction</td>\n",
" <td>2</td>\n",
" <td>Bob</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>4</td>\n",
" <td>1</td>\n",
" <td>2.9</td>\n",
" <td>3.529850</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>1</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>4</td>\n",
" <td>6</td>\n",
" <td>2.9</td>\n",
" <td>2.277778</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>6</td>\n",
" <td>Schindler's List</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>4</td>\n",
" <td>7</td>\n",
" <td>2.9</td>\n",
" <td>1.777778</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>7</td>\n",
" <td>The Lord of the Rings: The Return of the King</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>4</td>\n",
" <td>8</td>\n",
" <td>2.9</td>\n",
" <td>2.216539</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>8</td>\n",
" <td>Pulp Fiction</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>4</td>\n",
" <td>10</td>\n",
" <td>2.9</td>\n",
" <td>3.083333</td>\n",
" <td>{'was_impossible': True, 'reason': 'User and/o...</td>\n",
" <td>10</td>\n",
" <td>Fight Club</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>4</td>\n",
" <td>9</td>\n",
" <td>2.9</td>\n",
" <td>2.178106</td>\n",
" <td>{'actual_k': 2, 'was_impossible': False}</td>\n",
" <td>9</td>\n",
" <td>The Good, the Bad and the Ugly</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>5</td>\n",
" <td>2</td>\n",
" <td>2.9</td>\n",
" <td>3.611111</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>2</td>\n",
" <td>The Godfather</td>\n",
" <td>5</td>\n",
" <td>Earl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>5</td>\n",
" <td>7</td>\n",
" <td>2.9</td>\n",
" <td>1.611111</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>7</td>\n",
" <td>The Lord of the Rings: The Return of the King</td>\n",
" <td>5</td>\n",
" <td>Earl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>5</td>\n",
" <td>5</td>\n",
" <td>2.9</td>\n",
" <td>4.277778</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>5</td>\n",
" <td>12 Angry Men</td>\n",
" <td>5</td>\n",
" <td>Earl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>5</td>\n",
" <td>3</td>\n",
" <td>2.9</td>\n",
" <td>3.368083</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>3</td>\n",
" <td>The Godfather: Part II</td>\n",
" <td>5</td>\n",
" <td>Earl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>3</td>\n",
" <td>6</td>\n",
" <td>2.9</td>\n",
" <td>2.424315</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>6</td>\n",
" <td>Schindler's List</td>\n",
" <td>3</td>\n",
" <td>Carl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16</th>\n",
" <td>3</td>\n",
" <td>7</td>\n",
" <td>2.9</td>\n",
" <td>1.777778</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>7</td>\n",
" <td>The Lord of the Rings: The Return of the King</td>\n",
" <td>3</td>\n",
" <td>Carl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>17</th>\n",
" <td>3</td>\n",
" <td>4</td>\n",
" <td>2.9</td>\n",
" <td>3.114020</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>4</td>\n",
" <td>The Dark Knight</td>\n",
" <td>3</td>\n",
" <td>Carl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>18</th>\n",
" <td>3</td>\n",
" <td>5</td>\n",
" <td>2.9</td>\n",
" <td>2.777778</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>5</td>\n",
" <td>12 Angry Men</td>\n",
" <td>3</td>\n",
" <td>Carl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19</th>\n",
" <td>3</td>\n",
" <td>10</td>\n",
" <td>2.9</td>\n",
" <td>3.083333</td>\n",
" <td>{'was_impossible': True, 'reason': 'User and/o...</td>\n",
" <td>10</td>\n",
" <td>Fight Club</td>\n",
" <td>3</td>\n",
" <td>Carl</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" uid iid r_ui est \\\n",
"0 2 1 2.9 3.311469 \n",
"1 2 2 2.9 4.991123 \n",
"2 2 6 2.9 2.944444 \n",
"3 2 7 2.9 3.000000 \n",
"4 2 8 2.9 2.686264 \n",
"5 4 1 2.9 3.529850 \n",
"6 4 6 2.9 2.277778 \n",
"7 4 7 2.9 1.777778 \n",
"8 4 8 2.9 2.216539 \n",
"9 4 10 2.9 3.083333 \n",
"10 4 9 2.9 2.178106 \n",
"11 5 2 2.9 3.611111 \n",
"12 5 7 2.9 1.611111 \n",
"13 5 5 2.9 4.277778 \n",
"14 5 3 2.9 3.368083 \n",
"15 3 6 2.9 2.424315 \n",
"16 3 7 2.9 1.777778 \n",
"17 3 4 2.9 3.114020 \n",
"18 3 5 2.9 2.777778 \n",
"19 3 10 2.9 3.083333 \n",
"\n",
" details movieId \\\n",
"0 {'actual_k': 3, 'was_impossible': False} 1 \n",
"1 {'actual_k': 3, 'was_impossible': False} 2 \n",
"2 {'actual_k': 3, 'was_impossible': False} 6 \n",
"3 {'actual_k': 3, 'was_impossible': False} 7 \n",
"4 {'actual_k': 3, 'was_impossible': False} 8 \n",
"5 {'actual_k': 3, 'was_impossible': False} 1 \n",
"6 {'actual_k': 3, 'was_impossible': False} 6 \n",
"7 {'actual_k': 3, 'was_impossible': False} 7 \n",
"8 {'actual_k': 3, 'was_impossible': False} 8 \n",
"9 {'was_impossible': True, 'reason': 'User and/o... 10 \n",
"10 {'actual_k': 2, 'was_impossible': False} 9 \n",
"11 {'actual_k': 3, 'was_impossible': False} 2 \n",
"12 {'actual_k': 3, 'was_impossible': False} 7 \n",
"13 {'actual_k': 3, 'was_impossible': False} 5 \n",
"14 {'actual_k': 3, 'was_impossible': False} 3 \n",
"15 {'actual_k': 3, 'was_impossible': False} 6 \n",
"16 {'actual_k': 3, 'was_impossible': False} 7 \n",
"17 {'actual_k': 3, 'was_impossible': False} 4 \n",
"18 {'actual_k': 3, 'was_impossible': False} 5 \n",
"19 {'was_impossible': True, 'reason': 'User and/o... 10 \n",
"\n",
" movieName userId userName \n",
"0 The Shawshank Redemption   2 Bob \n",
"1 The Godfather 2 Bob \n",
"2 Schindler's List 2 Bob \n",
"3 The Lord of the Rings: The Return of the King 2 Bob \n",
"4 Pulp Fiction 2 Bob \n",
"5 The Shawshank Redemption   4 Deb \n",
"6 Schindler's List 4 Deb \n",
"7 The Lord of the Rings: The Return of the King 4 Deb \n",
"8 Pulp Fiction 4 Deb \n",
"9 Fight Club 4 Deb \n",
"10 The Good, the Bad and the Ugly  4 Deb \n",
"11 The Godfather 5 Earl \n",
"12 The Lord of the Rings: The Return of the King 5 Earl \n",
"13 12 Angry Men 5 Earl \n",
"14 The Godfather: Part II 5 Earl \n",
"15 Schindler's List 3 Carl \n",
"16 The Lord of the Rings: The Return of the King 3 Carl \n",
"17 The Dark Knight  3 Carl \n",
"18 12 Angry Men 3 Carl \n",
"19 Fight Club 3 Carl "
]
},
"execution_count": 467,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pred_df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### finding Recommendations for a user\n",
"\n",
"We can decide based on below that we 'll recommend a movie to the users if the estimated rating is more than 3. based on the above following are going to be the recommendations for the user 2 (Bob)\n"
]
},
{
"cell_type": "code",
"execution_count": 468,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uid</th>\n",
" <th>iid</th>\n",
" <th>r_ui</th>\n",
" <th>est</th>\n",
" <th>details</th>\n",
" <th>movieId</th>\n",
" <th>movieName</th>\n",
" <th>userId</th>\n",
" <th>userName</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" <td>2.9</td>\n",
" <td>3.311469</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>1</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>2</td>\n",
" <td>Bob</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2.9</td>\n",
" <td>4.991123</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>2</td>\n",
" <td>The Godfather</td>\n",
" <td>2</td>\n",
" <td>Bob</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" uid iid r_ui est details \\\n",
"0 2 1 2.9 3.311469 {'actual_k': 3, 'was_impossible': False} \n",
"1 2 2 2.9 4.991123 {'actual_k': 3, 'was_impossible': False} \n",
"\n",
" movieId movieName userId userName \n",
"0 1 The Shawshank Redemption   2 Bob \n",
"1 2 The Godfather 2 Bob "
]
},
"execution_count": 468,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pred_df[(pred_df['est']>3.0)&(pred_df['userId']==2)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Recommendations for Deb"
]
},
{
"cell_type": "code",
"execution_count": 469,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uid</th>\n",
" <th>iid</th>\n",
" <th>r_ui</th>\n",
" <th>est</th>\n",
" <th>details</th>\n",
" <th>movieId</th>\n",
" <th>movieName</th>\n",
" <th>userId</th>\n",
" <th>userName</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>4</td>\n",
" <td>1</td>\n",
" <td>2.9</td>\n",
" <td>3.529850</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>1</td>\n",
" <td>The Shawshank Redemption</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>4</td>\n",
" <td>10</td>\n",
" <td>2.9</td>\n",
" <td>3.083333</td>\n",
" <td>{'was_impossible': True, 'reason': 'User and/o...</td>\n",
" <td>10</td>\n",
" <td>Fight Club</td>\n",
" <td>4</td>\n",
" <td>Deb</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" uid iid r_ui est \\\n",
"5 4 1 2.9 3.529850 \n",
"9 4 10 2.9 3.083333 \n",
"\n",
" details movieId \\\n",
"5 {'actual_k': 3, 'was_impossible': False} 1 \n",
"9 {'was_impossible': True, 'reason': 'User and/o... 10 \n",
"\n",
" movieName userId userName \n",
"5 The Shawshank Redemption   4 Deb \n",
"9 Fight Club 4 Deb "
]
},
"execution_count": 469,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pred_df[(pred_df['est']>3.0)&(pred_df['userId']==4)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Recommendations for Earl"
]
},
{
"cell_type": "code",
"execution_count": 470,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uid</th>\n",
" <th>iid</th>\n",
" <th>r_ui</th>\n",
" <th>est</th>\n",
" <th>details</th>\n",
" <th>movieId</th>\n",
" <th>movieName</th>\n",
" <th>userId</th>\n",
" <th>userName</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>5</td>\n",
" <td>2</td>\n",
" <td>2.9</td>\n",
" <td>3.611111</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>2</td>\n",
" <td>The Godfather</td>\n",
" <td>5</td>\n",
" <td>Earl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>5</td>\n",
" <td>5</td>\n",
" <td>2.9</td>\n",
" <td>4.277778</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>5</td>\n",
" <td>12 Angry Men</td>\n",
" <td>5</td>\n",
" <td>Earl</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>5</td>\n",
" <td>3</td>\n",
" <td>2.9</td>\n",
" <td>3.368083</td>\n",
" <td>{'actual_k': 3, 'was_impossible': False}</td>\n",
" <td>3</td>\n",
" <td>The Godfather: Part II</td>\n",
" <td>5</td>\n",
" <td>Earl</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" uid iid r_ui est details \\\n",
"11 5 2 2.9 3.611111 {'actual_k': 3, 'was_impossible': False} \n",
"13 5 5 2.9 4.277778 {'actual_k': 3, 'was_impossible': False} \n",
"14 5 3 2.9 3.368083 {'actual_k': 3, 'was_impossible': False} \n",
"\n",
" movieId movieName userId userName \n",
"11 2 The Godfather 5 Earl \n",
"13 5 12 Angry Men 5 Earl \n",
"14 3 The Godfather: Part II 5 Earl "
]
},
"execution_count": 470,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pred_df[(pred_df['est']>3.0)&(pred_df['userId']==5)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Finding nearest neighbours of an item \n",
"\n",
"One last thing I want to check is the neighborhood of a particular user. This will show which users are similar to the other users. In following example we we'll try to find the movies that are closest to the movieId 1 (The Shashank Redemption) based on our training set for algo model"
]
},
{
"cell_type": "code",
"execution_count": 471,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>movieId</th>\n",
" <th>movieName</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>The Godfather</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>5</td>\n",
" <td>12 Angry Men</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>6</td>\n",
" <td>Schindler's List</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>7</td>\n",
" <td>The Lord of the Rings: The Return of the King</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" movieId movieName\n",
"1 2 The Godfather\n",
"4 5 12 Angry Men\n",
"5 6 Schindler's List\n",
"6 7 The Lord of the Rings: The Return of the King"
]
},
"execution_count": 471,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tsr_inner_id = best_algo.trainset.to_inner_iid(1)\n",
"tsr_neighbors = best_algo.get_neighbors(tsr_inner_id, k=2)\n",
"movies[movies.movieId.isin([algo.trainset.to_raw_iid(inner_id)\n",
" for inner_id in tsr_neighbors])]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Above are the nearest neighbours for the movieId 1 (The Shashank Redemption) as per our model."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## References \n",
"https://www.youtube.com/watch?v=6BTLobS7AU8\n",
"\n",
"\n",
"https://surprise.readthedocs.io/en/stable/"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment