├── movielens ├── __init__.py ├── models │ ├── __init__.py │ ├── random.py │ └── base.py └── data.py ├── .gitignore ├── README.md ├── main.py └── notebook.ipynb /movielens/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /movielens/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project 2 | .idea 3 | .ipynb_checkpoints 4 | venv 5 | 6 | # Python 7 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # recommender-system-anderson 2 | 3 | ## 결과 4 | 5 | | Model | Precision | Recall | F1 | Coverage | Popularity | Seconds | 6 | |:----------|:----------|:--------|:--------|:---------|:-----------|:---------| 7 | | Random | 0.0016 | 0.0004 | 0.0006 | 1.00 | 0.01 | 2.25 | 8 | 9 | ## 실행하는 방법 10 | 11 | ### Random Model 실행하는 방법 12 | 13 | ``` 14 | python main.py random 15 | ``` 16 | -------------------------------------------------------------------------------- /movielens/models/random.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing import List 3 | 4 | from movielens.models.base import BaseRecommendModel 5 | 6 | 7 | class RandomRecommendModel(BaseRecommendModel): 8 | 9 | def predict(self, user_ids: List[int]): 10 | pred_movies = list() 11 | for user_id in user_ids: 12 | _pred_movies = list() 13 | watched_movies = self.user2mv[user_id] 14 | 15 | while len(_pred_movies) < self.n_recommend: 16 | movie_id = random.choice(self.movie_ids) 17 | if movie_id not in watched_movies: 18 | _pred_movies.append(movie_id) 19 | pred_movies.append(_pred_movies) 20 | 21 | return pred_movies 22 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from argparse import ArgumentParser, Namespace 2 | 3 | from movielens.data import load_dataset 4 | from movielens.models.random import RandomRecommendModel 5 | 6 | 7 | def init() -> Namespace: 8 | parser = ArgumentParser() 9 | parser.add_argument('model', type=str, help='random|') 10 | opt = parser.parse_args() 11 | opt.model = opt.model.lower().strip() 12 | 13 | assert opt.model in {'random'}, 'unknown model name' 14 | return opt 15 | 16 | 17 | def get_model(model_name): 18 | model = None 19 | if model_name == 'random': 20 | model = RandomRecommendModel() 21 | 22 | assert model is not None 23 | return model 24 | 25 | 26 | def main(): 27 | # Initialization 28 | opt = init() 29 | 30 | # Data 31 | train_data, test_data, movie_popularity = load_dataset() 32 | train_pct = len(train_data) / (len(train_data) + len(test_data)) * 100 33 | test_pct = len(test_data) / (len(train_data) + len(test_data)) * 100 34 | # print(f'train_data : {train_data.shape} {train_pct:.2f}%') 35 | # print(f'test_data : {test_data.shape} {test_pct:.2f}%') 36 | print(f'train_data : {len(train_data)} user IDs | {train_pct:.1f}%') 37 | print(f'test_data : {len(test_data)} user IDs | {test_pct:.1f}%') 38 | print(f'movies : {len(movie_popularity)} movies') 39 | 40 | # Model 41 | model = get_model(opt.model) 42 | 43 | # Train 44 | print(f'\nStart Testing for {opt.model} model') 45 | model.fit(train_data, movie_popularity) 46 | 47 | # Test 48 | model.test(test_data) 49 | 50 | 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /movielens/models/base.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABC 2 | from datetime import datetime 3 | from typing import Dict, Union, List 4 | 5 | import numpy as np 6 | from sklearn.base import BaseEstimator, ClassifierMixin 7 | 8 | 9 | class BaseRecommendModel(BaseEstimator, ClassifierMixin, ABC): 10 | 11 | def __init__(self, n_recommend=10): 12 | self.n_recommend = n_recommend 13 | self.user2mv: Union[Dict[int, dict], None] = None 14 | self.movie_popularity: Union[dict, None] = None 15 | self.movie_ids = list() 16 | self.n_movies = 0 17 | 18 | @abstractmethod 19 | def predict(self, user_ids: List[int]): 20 | raise NotImplementedError 21 | 22 | def fit(self, user2mv: Dict[int, dict], movie_popularity: dict): 23 | self.user2mv = user2mv 24 | self.movie_popularity = movie_popularity 25 | self.movie_ids = list(movie_popularity.keys()) 26 | self.n_movies = len(movie_popularity) 27 | 28 | def test(self, user2mv: Dict[int, Dict[int, int]], seed: int = 0): 29 | np.random.seed(seed) 30 | start_dt = datetime.now() 31 | users = list(user2mv.keys()) 32 | pred_rcmd_movies = self.predict(users) 33 | all_rcmd_movies = set() 34 | 35 | n_correct = 0 # used for TP + FP 36 | n_test_movies = 0 # used for TP + FN 37 | n_rcmd = 0 38 | popularity_sum = 0 39 | for i, user_id in enumerate(users): 40 | test_movies = set(user2mv[user_id]) # the test user will watch the movie. 41 | rcmd_movies = set(pred_rcmd_movies[i]) # recommended movies (dict) 42 | all_rcmd_movies |= rcmd_movies 43 | 44 | corrected_movies = test_movies & rcmd_movies 45 | n_correct += len(corrected_movies) 46 | n_test_movies += len(test_movies) 47 | n_rcmd += self.n_recommend 48 | popularity_sum += sum([np.log1p(self.movie_popularity[mv_id]) for mv_id in corrected_movies]) 49 | 50 | precision = n_correct / n_rcmd # precision = TP/(TP+FP) 51 | recall = n_correct / n_test_movies # recall = TP/(TP+FN) 52 | f1_score = 2 * precision * recall / (precision + recall) 53 | coverage = len(all_rcmd_movies) / self.n_movies # unique_추천된_영화_갯수/전체_영화_갯수 54 | popularity = popularity_sum / n_rcmd 55 | time = datetime.now() - start_dt 56 | 57 | print(f'precision:{precision:.4f} | recall:{recall:.4f} | f1:{f1_score:.4f} | coverage:{coverage:.2f} | ' 58 | f'popularity:{popularity:.2f} | time:{time}') 59 | -------------------------------------------------------------------------------- /movielens/data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pickle 3 | import tempfile 4 | import zipfile 5 | from collections import defaultdict 6 | from pathlib import Path 7 | from typing import Tuple, Dict 8 | 9 | import kaggle 10 | import numpy as np 11 | import pandas as pd 12 | from sklearn.model_selection import train_test_split 13 | 14 | 15 | def load_dataset(force: bool = False) -> Tuple[Dict[int, dict], Dict[int, dict], Dict[int, int]]: 16 | """ 17 | return data 18 | - train_data : [(userId, movieId, rating), ...] 19 | - test_data : [(userId, movieId, rating), ...] 20 | - movie_popularity dict: {movieId: count, ...} 21 | - train_user2mv Dict[dict]: {userId: {movieId: rating, ...}, ...} 22 | - test_user2mv Dict[dict]: {userId: {movieId: rating, ...}, ...} 23 | """ 24 | 25 | np.random.seed(1) 26 | download_path = Path(tempfile.gettempdir()) / 'movielens' 27 | train_data_path = download_path / 'train-20m-data.npy' 28 | test_data_path = download_path / 'test-20m-data.npy' 29 | popularity_path = download_path / 'popularity.pkl' 30 | train_user2mv_path = download_path / 'train-user2mv.pkl' 31 | test_user2mv_path = download_path / 'test-user2mv.pkl' 32 | 33 | # Load Data 34 | if not force and popularity_path.exists() and train_user2mv_path.exists() and test_user2mv_path.exists(): 35 | print('Load preprocessed data') 36 | # train_data = np.load(open(train_data_path, 'rb')) 37 | # test_data = np.load(open(test_data_path, 'rb')) 38 | movie_popularity = pickle.load(open(popularity_path, 'rb')) 39 | train_user2mv = pickle.load(open(train_user2mv_path, 'rb')) 40 | test_user2mv = pickle.load(open(test_user2mv_path, 'rb')) 41 | return train_user2mv, test_user2mv, movie_popularity 42 | 43 | # Downlaod movielens from Kaggle 44 | print('Download & Preprocess Data') 45 | kaggle.api.dataset_download_files('grouplens/movielens-20m-dataset', download_path) 46 | 47 | # Unzip the dataset 48 | zipfile_path = download_path / [x for x in os.listdir(download_path) if x.endswith('.zip')][0] 49 | with zipfile.ZipFile(zipfile_path, 'r') as zip_ref: 50 | zip_ref.extractall(download_path) 51 | csv_files = [x for x in os.listdir(download_path) if x.endswith('.csv')] 52 | 53 | # Load Movie Dataset 54 | # movies = pd.read_csv(download_path / 'movie.csv', index_col=0) 55 | 56 | # Load Rating Dataset 57 | data = load_raw_data(download_path) 58 | # data = data.merge(movies[['title']], left_on='movieId', right_index=True) 59 | 60 | # Get movie popularity (the number of views by users wrt movies) 61 | movie_popularity = defaultdict(int) 62 | _popularity = data.groupby('movieId').count()['userId'] 63 | _popularity = _popularity.sort_values(ascending=False) 64 | movie_popularity.update(_popularity.to_dict()) 65 | 66 | # Split to train and test 67 | train_data, test_data = train_test_split(data.values, test_size=0.3, random_state=1) 68 | 69 | # to dictionary. {user_id: {movie_id: rating, ...}} 70 | train_user2mv = make_user_based_data(train_data) 71 | test_user2mv = make_user_based_data(test_data) 72 | 73 | # np.save(open(train_data_path, 'wb'), train_data) 74 | # np.save(open(test_data_path, 'wb'), test_data) 75 | pickle.dump(movie_popularity, open(popularity_path, 'wb')) 76 | pickle.dump(train_user2mv, open(train_user2mv_path, 'wb')) 77 | pickle.dump(test_user2mv, open(test_user2mv_path, 'wb')) 78 | return train_user2mv, test_user2mv, movie_popularity 79 | 80 | 81 | def load_raw_data(download_path) -> pd.DataFrame: 82 | return pd.read_csv(download_path / 'rating.csv', usecols=[0, 1, 2]) 83 | 84 | 85 | def make_user_based_data(data): 86 | user2movies = defaultdict(dict) 87 | for i in range(len(data)): 88 | user_id, movie_id, rating = data[i] 89 | user_id, movie_id = int(user_id), int(movie_id) 90 | user2movies[user_id][movie_id] = rating 91 | 92 | return user2movies 93 | -------------------------------------------------------------------------------- /notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import pandas as pd\n", 10 | "import numpy as np\n", 11 | "\n", 12 | "np.set_printoptions(formatter={'float_kind':'{:.1f}'.format})" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "# Introduction\n", 20 | "\n", 21 | "1. 과거의 행동에 기반하여, 유저가 좋아할만한(likelihood) 아이템을 추천하는 것\n", 22 | "2. 추천의 목적은 유저가 아직 보지 못한 아이템을 추천하는 것을 목적으로 한다" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "# Algorithms\n", 30 | "\n", 31 | "## User-Based Collaborative Filtering \n", 32 | "\n", 33 | "\n", 34 | "문제점으로 cold-start problem을 갖고 있다.
\n", 35 | "cold-start problem은 새로운 유저, 또는 새로운 아이템이 추가되었을 경우 제대로 작동을 안하는 단점을 갖고 있다.
\n", 36 | "\n", 37 | "\n", 38 | "## " 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "# Dataset" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 2, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "from movielens.data import load_dataset, load_raw_data\n", 55 | "from pathlib import Path\n", 56 | "import tempfile\n", 57 | "\n", 58 | "download_path = Path(tempfile.gettempdir()) / 'movielens'\n", 59 | "\n", 60 | "data = load_raw_data(download_path)\n", 61 | "train_user2mv, test_user2mv, movie_popularity = load_dataset(force=False)" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 6, 67 | "metadata": {}, 68 | "outputs": [ 69 | { 70 | "data": { 71 | "text/html": [ 72 | "
\n", 73 | "\n", 86 | "\n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | "
userIdmovieIdrating
0123.5
11293.5
21323.5
31473.5
41503.5
............
20000258138493689544.5
20000259138493695264.5
20000260138493696443.0
20000261138493702865.0
20000262138493716192.5
\n", 164 | "

20000263 rows × 3 columns

\n", 165 | "
" 166 | ], 167 | "text/plain": [ 168 | " userId movieId rating\n", 169 | "0 1 2 3.5\n", 170 | "1 1 29 3.5\n", 171 | "2 1 32 3.5\n", 172 | "3 1 47 3.5\n", 173 | "4 1 50 3.5\n", 174 | "... ... ... ...\n", 175 | "20000258 138493 68954 4.5\n", 176 | "20000259 138493 69526 4.5\n", 177 | "20000260 138493 69644 3.0\n", 178 | "20000261 138493 70286 5.0\n", 179 | "20000262 138493 71619 2.5\n", 180 | "\n", 181 | "[20000263 rows x 3 columns]" 182 | ] 183 | }, 184 | "execution_count": 6, 185 | "metadata": {}, 186 | "output_type": "execute_result" 187 | } 188 | ], 189 | "source": [ 190 | "data" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "# Analysis \n", 198 | "\n", 199 | "## Long Tail Plot" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 36, 205 | "metadata": {}, 206 | "outputs": [ 207 | { 208 | "name": "stdout", 209 | "output_type": "stream", 210 | "text": [ 211 | "Most Popular Movies\n" 212 | ] 213 | }, 214 | { 215 | "data": { 216 | "text/plain": [ 217 | "['Priest (1994)',\n", 218 | " 'I Love Trouble (1994)',\n", 219 | " 'Strawberry and Chocolate (Fresa y chocolate) (1993)',\n", 220 | " 'Wild Bunch, The (1969)',\n", 221 | " 'Lassie (1994)',\n", 222 | " 'Ladybird Ladybird (1994)',\n", 223 | " 'Rumble in the Bronx (Hont faan kui) (1995)',\n", 224 | " 'Beauty and the Beast (1991)',\n", 225 | " 'Rocky Horror Picture Show, The (1975)',\n", 226 | " 'Secret Garden, The (1993)']" 227 | ] 228 | }, 229 | "execution_count": 36, 230 | "metadata": {}, 231 | "output_type": "execute_result" 232 | }, 233 | { 234 | "data": { 235 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAEICAYAAAC9E5gJAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3de5xdVX338c8vCTehkERinkhig5JHH7QFYQqh0j4qCgH7MvSp0qCWiGhsQau2PjWRWhTEgnIzLYIRIgkCSUBoUgiEMRDCJbcJuZHrTO4TkswkM5ncbzO//nHWmeyZOfc5Z86ZOd/363Ves/faa++9zp6Z/dt7rbXXNndHRETKW69iF0BERIpPwUBERBQMREREwUBERFAwEBERFAxERIQMgoGZfdjMlkY+e83su2bW38wqzaw6/OwX8puZjTezGjNbbmYXRbY1OuSvNrPRkfSLzWxFWGe8mVlhvq6IiCRi2TxnYGa9gW3ApcAtQIO732VmY4F+7v4DM7sG+DZwTcj3S3e/1Mz6A1VABeDAYuBid280s4XAPwILgJnAeHd/MVVZzj77bB86dGh231ZEpIwtXrx4l7sPSLSsT5bbugJY7+6bzWwk8MmQPgmYA/wAGAlM9liUmW9mfc1sUMhb6e4NAGZWCYwwsznAme4+P6RPBq4FUgaDoUOHUlVVlWXxRUTKl5ltTrYs2zaDUcBTYXqgu28P0zuAgWH6HGBrZJ3akJYqvTZBegdmNsbMqsysqr6+Psuii4hIMhkHAzM7Gfg88HT7ZeEuoODjWrj7BHevcPeKAQMS3umIiEgOsrkzuBp42913hvmdofqH8LMupG8DhkTWGxzSUqUPTpAuIiJdJJtgcD0nqogAZgDxHkGjgemR9BtCr6LhQFOoTpoFXGlm/ULPoyuBWWHZXjMbHnoR3RDZloiIdIGMGpDN7HTgs8A3I8l3AdPM7CZgM3BdSJ9JrCdRDXAQuBHA3RvM7A5gUch3e7wxGbgZeAw4jVjDccrGYxERya+supaWkoqKCldvIhGRzJnZYnevSLRMTyCLiEj5BYNjzS1Mq9pKS0v3vCMSESmEbB866/Z+8/oGfv7SWnC47s+GpF9BRKQMlN2dQcP+owA0HTpW5JKIiJSOsgsGcV74Z+RERLqNsgsGWxsPAjB5XtIhOkREyk7ZBYPNu2PBoLbxUJFLIiJSOsouGIiISEcKBiIiomAgIiIKBiIigoKBiIigYCAiIigYiIgICgYiIoKCgYiIoGAgIiIoGIiICAoGIiKCgoGIiKBgICIiZBgMzKyvmT1jZmvMbLWZXWZm/c2s0syqw89+Ia+Z2XgzqzGz5WZ2UWQ7o0P+ajMbHUm/2MxWhHXGm5nl/6uKiEgymd4Z/BJ4yd0/AlwArAbGArPdfRgwO8wDXA0MC58xwEMAZtYfuA24FLgEuC0eQEKeb0TWG9G5ryUiItlIGwzM7CzgL4FHAdz9qLvvAUYCk0K2ScC1YXokMNlj5gN9zWwQcBVQ6e4N7t4IVAIjwrIz3X2+uzswObItERHpApncGZwL1AO/NbMlZvaImZ0ODHT37SHPDmBgmD4H2BpZvzakpUqvTZDegZmNMbMqM6uqr6/PoOgiIpKJTIJBH+Ai4CF3/zhwgBNVQgCEK/qCv2He3Se4e4W7VwwYMKDQuxMRKRuZBINaoNbdF4T5Z4gFh52hiofwsy4s3wYMiaw/OKSlSh+cIL0g1DYtItJR2mDg7juArWb24ZB0BbAKmAHEewSNBqaH6RnADaFX0XCgKVQnzQKuNLN+oeH4SmBWWLbXzIaHXkQ3RLaVd7GbGBERieqTYb5vA0+Y2cnABuBGYoFkmpndBGwGrgt5ZwLXADXAwZAXd28wszuARSHf7e7eEKZvBh4DTgNeDB8REekiGQUDd18KVCRYdEWCvA7ckmQ7E4GJCdKrgI9lUhYREck/PYEsIiIKBiIiomAgIiIoGIiICAoGIiKCgoGIiKBgICIiKBiIiAgKBiIiQhkGgzU79hW7CCIiJafsgoGIiHSkYCAiIgoGIiKiYCAiIigYiIgICgYiIoKCgYiIoGAgIiIoGLR6umorW3YfLHYxRESKQsEg+P/PLOfaX71Z7GKIiBRFRsHAzDaZ2QozW2pmVSGtv5lVmll1+NkvpJuZjTezGjNbbmYXRbYzOuSvNrPRkfSLw/ZrwrqW7y+aiYYDR4uxWxGRosvmzuBT7n6hu1eE+bHAbHcfBswO8wBXA8PCZwzwEMSCB3AbcClwCXBbPICEPN+IrDci528kIiJZ60w10UhgUpieBFwbSZ/sMfOBvmY2CLgKqHT3BndvBCqBEWHZme4+390dmBzZloiIdIFMg4EDL5vZYjMbE9IGuvv2ML0DGBimzwG2RtatDWmp0msTpHdgZmPMrMrMqurr6zMsuoiIpNMnw3yXu/s2M3sfUGlma6IL3d3NzPNfvLbcfQIwAaCioqLg+xMRKRcZ3Rm4+7bwsw54jlid/85QxUP4WReybwOGRFYfHNJSpQ9OkC4iIl0kbTAws9PN7I/i08CVwDvADCDeI2g0MD1MzwBuCL2KhgNNoTppFnClmfULDcdXArPCsr1mNjz0Irohsq0uEWuqEBEpX5lUEw0Engu9PfsAT7r7S2a2CJhmZjcBm4HrQv6ZwDVADXAQuBHA3RvM7A5gUch3u7s3hOmbgceA04AXw0dERLpI2mDg7huACxKk7wauSJDuwC1JtjURmJggvQr4WAblFRGRAtATyIBqiUSk3CkYRBTnuWcRkeIr62Bw90trONbcUuxiiIgUXVkHg4fmrOfel9cVuxgiIkVX1sEAoKZuH2oyEJFyV/bBIEpNBiJSrhQMREREwWDVu3uLXQQRkaIr+2DwbtPh1ukWh7p9h1PkFhHpmco+GLS3aGNjsYsgItLlFAzQQHUiIgoGoK6lIlL2FAxERETBQEREFAw6cFUaiUgZUjAQEREFAxERUTAQEREUDAC96UxERMFAREQyDwZm1tvMlpjZ82H+XDNbYGY1ZjbVzE4O6aeE+ZqwfGhkG+NC+lozuyqSPiKk1ZjZ2Px9vcxEexDpLkFEylE2dwbfAVZH5u8G7nf384BG4KaQfhPQGNLvD/kws/OBUcBHgRHAr0KA6Q08CFwNnA9cH/KKiEgXySgYmNlg4HPAI2HegE8Dz4Qsk4Brw/TIME9YfkXIPxKY4u5H3H0jUANcEj417r7B3Y8CU0Leoti1/0ixdi0iUjSZ3hk8APwLEH97/HuBPe5+PMzXAueE6XOArQBheVPI35rebp1k6R2Y2RgzqzKzqvr6+gyLnp2f/PeqgmxXRKSUpQ0GZvZXQJ27L+6C8qTk7hPcvcLdKwYMGFDs4oiI9Bh9MsjzCeDzZnYNcCpwJvBLoK+Z9QlX/4OBbSH/NmAIUGtmfYCzgN2R9LjoOsnSu4QajUWk3KW9M3D3ce4+2N2HEmsAfsXdvwy8CnwhZBsNTA/TM8I8YfkrHnthwAxgVOhtdC4wDFgILAKGhd5JJ4d9zMjLtxMRkYxkcmeQzA+AKWb2U2AJ8GhIfxR43MxqgAZiJ3fcfaWZTQNWAceBW9y9GcDMvgXMAnoDE919ZSfKJSIiWcoqGLj7HGBOmN5ArCdQ+zyHgS8mWf9O4M4E6TOBmdmURURE8kdPIKfg7jS3qEFBRHo+BYMUfrdgCx/64Uzq9h4udlFERApKwQC47tfzEqY/93YtAFsbD3ZlcUREupyCAbC8tqnYRRARKSoFAxERUTAQEREFg5TUj0hEyoWCQUas2AUQESkoBQMREVEwEBERBYOUNJqpiJQLBYMMTV20hYNHj6fPKCLSDSkYZODNml384Pcr+OkLq9NnFhHphhQMMnDgSOyOYLfejywiPZSCQQI1dfsBPWcgIuVDwSCBz9z3WtsEPWYgIj2cgoGIiCgYJDN+djXLtu4pdjFERLqEgkES91WuS5jedOgYj8/bhOshBBHpQbJ6B3K5skijwbhnlzNzxQ7Of/9ZXPzH/YpYKhGR/NGdQQYs0oC8e/9RAI4ebylSaURE8i9tMDCzU81soZktM7OVZvaTkH6umS0wsxozm2pmJ4f0U8J8TVg+NLKtcSF9rZldFUkfEdJqzGxs/r+miIikksmdwRHg0+5+AXAhMMLMhgN3A/e7+3lAI3BTyH8T0BjS7w/5MLPzgVHAR4ERwK/MrLeZ9QYeBK4GzgeuD3lFRKSLpA0GHrM/zJ4UPg58GngmpE8Crg3TI8M8YfkVZmYhfYq7H3H3jUANcEn41Lj7Bnc/CkwJeUVEpItk1GYQruCXAnVAJbAe2OPu8ZHbaoFzwvQ5wFaAsLwJeG80vd06ydITlWOMmVWZWVV9fX0mRc8LPXMmIj1dRsHA3Zvd/UJgMLEr+Y8UtFTJyzHB3SvcvWLAgAHFKIKISI+UVW8id98DvApcBvQ1s3jX1MHAtjC9DRgCEJafBeyOprdbJ1m6iIh0kUx6Ew0ws75h+jTgs8BqYkHhCyHbaGB6mJ4R5gnLX/HYE1ozgFGht9G5wDBgIbAIGBZ6J51MrJF5Rj6+nIiIZCaTh84GAZNCr59ewDR3f97MVgFTzOynwBLg0ZD/UeBxM6sBGoid3HH3lWY2DVgFHAducfdmADP7FjAL6A1MdPeVefuGIiKSVtpg4O7LgY8nSN9ArP2gffph4ItJtnUncGeC9JnAzAzKW1SmpmQR6aH0BLKIiCgYiIiIgkHWNFapiPRECgYZsARNBYnSRES6KwUDERFRMBAREQUDERFBbzrLSPz5gpdW7ihySURECkN3BjlS+7GI9CQKBhlQzyER6ekUDERERMEgE1saDha7CCIiBaVgkIHpS98tdhFERApKwUBERBQMREREwSBnWxsPFbsIIiJ5o2CQo+8/vazYRRARyRsFAxERUTAQEREFAxERIYNgYGZDzOxVM1tlZivN7Dshvb+ZVZpZdfjZL6SbmY03sxozW25mF0W2NTrkrzaz0ZH0i81sRVhnvJkGgBAR6UqZ3BkcB/7Z3c8HhgO3mNn5wFhgtrsPA2aHeYCrgWHhMwZ4CGLBA7gNuBS4BLgtHkBCnm9E1hvR+a8mkp2mg8cY8cBcaur2FbsoIl0ubTBw9+3u/naY3gesBs4BRgKTQrZJwLVheiQw2WPmA33NbBBwFVDp7g3u3ghUAiPCsjPdfb67OzA5sq1uobbxILGiS3f26to61uzYx3+8UlPsooh0uazaDMxsKPBxYAEw0N23h0U7gIFh+hxga2S12pCWKr02QXqi/Y8xsyozq6qvr8+m6AWzbOseLr/7VZ5YsKXYRRERyVnGwcDMzgB+D3zX3fdGl4Ur+oJfGrv7BHevcPeKAQMGFHp3GVlfvx+AxZsbi1wSEZHcZRQMzOwkYoHgCXd/NiTvDFU8hJ91IX0bMCSy+uCQlip9cIJ0ERHpIpn0JjLgUWC1u98XWTQDiPcIGg1Mj6TfEHoVDQeaQnXSLOBKM+sXGo6vBGaFZXvNbHjY1w2RbUmJWLBhN4+9ubHYxRCRAsnkHcifAP4OWGFmS0PaD4G7gGlmdhOwGbguLJsJXAPUAAeBGwHcvcHM7gAWhXy3u3tDmL4ZeAw4DXgxfKSE/O2E+QB89RPnFrkkIlIIaYOBu79B8lf+XpEgvwO3JNnWRGBigvQq4GPpyiIiIoWhJ5BFRETBIF/0nIGIdGcKBp2kgTNEpCdQMBAREQUDERFRMBARERQMOu17U3vu6y+PNbdQU7e/2MUQkS6gYCBJ/fvMNXzmvteobTxY7KKISIEpGEhSCzftBqDxwLEil0RECk3BoBPW7DgxeKueMhCR7kzBoBNGPPB6sYvQYzz6xkaGjn0h7cN7c9fV88WH36K5ReFXJJ8UDCQt74L7njtfWAVAunP8P05ZwqJNjew9pKqr9iaGgHqsuaXYRZFuSMFAkrKk4xMWjob1yN39lesAOHSsucglke5IwUBKgoVxPTINBQoZIvmlYCDdioaCSk83V5ILBQNJqytOLvGTfLp96TyXgiKldIKCQZ70xKux+Iisy7c1ddm+MvUPv1vM3zz0VmEKEyzZ0sgra3YWdB8F0QP/FqXwMnntpZS5KQu3dNm+2vdc+tWcGv78Q2dz4ZC+wImL3wUbGyiUOWvrGf6z2ezYexiATXd9rmD7yifdGEhn6M4gTxoOHC12Ebq1eM+l9ndYP39pLdc++GbrfOPBwncpbTp0rDUQdEdd0RVYeh4FgzzZuOtAsYtQMF1SBabL2k4zvWlJOiFtMDCziWZWZ2bvRNL6m1mlmVWHn/1CupnZeDOrMbPlZnZRZJ3RIX+1mY2OpF9sZivCOuNNf9Elo7v9ItydiW9s5MCR4x2WPbO4tsPdW6J8PUFPbL+SwsvkzuAxYES7tLHAbHcfBswO8wBXA8PCZwzwEMSCB3AbcClwCXBbPICEPN+IrNd+X1IG8hF4Zq+u4/bnV3HnzNVt0jftOsD3n17Gt558uzVt7rp6PnrbLOat352HPZcGXUZJZ6QNBu4+F2jfWjcSmBSmJwHXRtIne8x8oK+ZDQKuAirdvcHdG4FKYERYdqa7z/fYo6eTI9vqVnryk7NdeZLpzGGMP3nb1G6oiiPHY8Mz1O87AsDj8zdzw8SFACzeXLiGaJHuJNc2g4Huvj1M7wAGhulzgK2RfLUhLVV6bYL0bqfnhoL8VDssr93D/771Rer2JW6YjQeceOPnvsPHch9jJ0l54/v40X+9kzhDiaip28fy2j05r9+T/xalcDrdgByu6Lvk78/MxphZlZlV1dfXd8Uuy1uaW4JP3TOHcc+uyGhTj7y+kaPNLbxVk7hapv04SH/y45f5+qSqzMoZ30a7gBKXqndNVzVRzVyxneqd+zLK+5n75vL5/3wzfcZ2VEsknZFrMNgZqngIP+tC+jZgSCTf4JCWKn1wgvSE3H2Cu1e4e8WAAQNyLHph9IR/xOeW1DJ9acfDf/Bo4obWjbsO8FSGzyDET8fpzr3Ru5DX1mUX8NMNrJfPgfce+MM6xv5+ecb5b37ibT57/1yGjn2B383fnLdyiORLrsFgBhDvETQamB5JvyH0KhoONIXqpFnAlWbWLzQcXwnMCsv2mtnw0Ivohsi2pIt9b+oyvjNlaYf0TbsL/9rLE1f1uXmjehcNB2JtAu2rtQrRnPPAH6qZsmhr+owJPPL6hjyXRqTz0j6BbGZPAZ8EzjazWmK9gu4CppnZTcBm4LqQfSZwDVADHARuBHD3BjO7A1gU8t3u7vGWu5uJ9Vg6DXgxfLod1dOmFm9gT1Yt05lrdnfnK48uSLE87KNEbt8K/bfSkzszSOGkDQbufn2SRVckyOvALUm2MxGYmCC9CvhYunJI99Z6Qk6br/Mnsmw2UcwAsXBjAx8acDrvPeOUvGxPj+hIZ+gJZMnKc0tqOXI895enJDtfFfJEVmrDM2xvivWouu7X8/jiw/OKXBqRGAWDPIn/g/d035u6jPsrq7NeL9MTci6n7Q5tBEm2kijgFONtbkePn+gyu6EAw5iUVuiT7kLBQLKW7FmBVOIn7KpNjfxmbscG1EzfZ5CLcqlCVyWRdIaCgWQvh5Nr/IT82FubOgwXAeR8JkvUFTbZyX/19r257aSbKZfgJ/mlYCBJJTs/51SVk+laDtubDnVIXrNjLy+u2N4h/bdvbupUtUg+mircncnzNrH3cOGH105F7cfSGQoGZebW51YwdOwLXb7fdFer0fPY3z26sMPyEQ+8zj888XaH9ESbbZ+2bU/H4JJPVZsb+bfpK/lhhk9jZ8PdqW3M7jmPUmswl+5BwaDMPLEg/RPDB44cZ2tD5iegW554u8PgcACHjjZn3VX0gttfpqZuf+YrZLD9bz6+OKsyZOtwGCCv8WDmLziKjqCaytRFW7n87ldZvLkxZb5xz65g1/7k+6/a1EBzi4KEJKdgIB2MmjCfv/j5qxnnf2HFdh6ft6lNWt2+w/yff3uJR17fCKSvWsq1a6nT8dmE+Ozj8zYxLc1TwpmcwIf/bHbK4JjsLW2pPL/8RJVXc4vzT9OWsi7B2EVVIQisTxMg2wwL4nC8uYX19bF1nllcyxcensfDr63PvIBSdhQMpIMV25qA5HXQia7240mLNjXw2rp6ahtjVTMvhHr+tNVEOdZ3tyTcsNPS4vxo+kr+pd34QZvadeX89WsbWLy5MeWb6nbsPczTi2uTLm8dSiPHC+91O/fx7Nvb+PaTSzpuO7dNcs/L67ji3tda3+UAZHfHlcTaHfvY2Y1fCSrJKRhI1lKd87748DxGT1zYWiXRp1drp9E2+e6rXJefsiQpzIKNid9T8Ml75nRIGz+7mk/dM6c1cCXSK8VZ+cQ3zC0aZDJcRnzbLyzfztCxL7BmR+qeUYs2xb7/rv1HcipTMlc9MJdLfza7df7BV2v4xaw1ed2HFIeCgeSF0/bK83hz7OTVu1fiKpTxs9s+uJbrFbB7x+DkTlZXr/E7oVXvJj/B9kp1pg6L5m9o4HgO72CIn+gTPhTX7q7jltDWcM+sWDA9fCz7dpl8+sWstTz4aueqn9ydpxZuYX8PfQ1pd6FgIEm1f2dwXPuqlrhRE04MrRC/M2gNBmn21Zk2g0S+O7Xj6KvJxMua6qR6X+U6Rj6Y+B0D0aeYZ6+pS5gnlVTjNqV6Qnrn3sN85Ecv8dhbmxJsMz8BoqZuH3OzHEo8W1WbGxn37Ar+9bn898aSzKUdqE7K1+YkQ1fHXx8Z5R7rPRTXHE5GbxX4HcOJTnrZnpBb4sEgTb5lWxO/fSwaxzpzEl61fS97kjRoJ9pqvMvpjGXvtkn/1lNLWttscm2Lmbd+N02HjvH3v4v1xNp01+dy21AG4n83qXpDSeHpzqAMuDtv1ezK29Vioh6Kr66ta3N139zStrrknVAVk0wxn5faF6oncj080bK7x07S2TTWRhvBR02Y33bbKRqn48e7/bKFGxuoSxCwn1uyjSvunZOwDA+/tp43a3a1zl//m/mtgaDQ4lVwej6iuHRnUAaefXsb//z0Mn7xhT9tTWs6dIyzTjspp+0levPZpt1tq47ibQYQ64GS6OSUD+75G34h15NRNAg6cPndsW65V310IEeOtzBx9J+l3m9kt2t2tO1eGt/0mzW7+NKlH2i7rHX9zMu9vj5xFd9dL8YagQt5B9B06Bjuzmkn9+aUPr1b07ftid3htOT4ymvJD90ZlIGtoTpha+OJJ3Hve3ltztvbe7hjMHBve4UcfcDpqgfmpt1mrtUZxb6aXPluU+vJDNqe2Get3MmctfXcNmNlym1k8g0S9XRqvTNIuWZ2B3Z9/X42785+JNXP/+cb/GHVzqTLV2/fywU/eZkLb6/ka48tarPsB7+PtRVk+7t0d73IJ48UDMrA9j2xnjXPvn2ir3yiJ4Y741hzS2t1C5xoM8hUrvXF63buZ+qizN7DnE4u55XPjX+D701d1jqf6LmHJ9O8Jzr1CS35yTy+JPGzFul96p45HZ6EvuLe1/i/v5jTIe/42dU0JulQALC8tol/mtax0f7VtXW8Ub2LtZE7njdrdjN07AvU1O1rc9GQ7QPS546byfW/mZ8237HmFhZsKGzbVU+gYFAGquti/4i1kTuD43kemuDg0bYvvHlnW/oRQl9Zs5OV76ZuS8jEj6anvvLOVKZVWemGhmgv3TAQqRanumPqlaTNIFMbdx1o8yR0KvdVruPjd1SmzJOoGDf+dhFfeXQBb63f1WHZZ+6by1d/e2IcqmRB8Y3qXUyYuz7h8vkbTjxPMmvlDr70m/mtw4PE3fvyOv52wnyWJukAsLXhICMemJv3ZzK6GwWDMpXr1WSmMhn64GuPVfG58W90+OctZW9U7+JvHnor6fJcjuqRJN//1pRdLZ3D4Y1zqX6Vy2sTnwCjRk/sODBgOkeONyf9Hc9evZOhY19gd+TkOq0q8RPcr1efCBKJvsc725r4yqML+NnMNWxK0rsNYP+R43zz8cW8tX43X3i47e8nfgymRO7Qdu49zP2V63h55Q7+45Vq1uzYx4ylbXtllRs1IPdwh4428/aWjieEUhq0rH0dcimrqes4flDUPz7VcUiJdL70yIKE6U8s2MKX2zUax7nD7f+9Coh1SU3mJyFPKq/l8BzBhNc2cG/7p8g9dnV/06Qq4MTDfJlKdIGyI/IGQSO2/fbPpBxvbmkTUKN3pc0t3tq9ecqirXz+gvfz5+ed3eYpaolRMOjhfvLfiatQSikYFPpZhHz6cQYn13zakmSAvH2Hj+f0xjmI/e6TVZlkqkMgINZFd2FkGJADR7K740tUdfn1yVWt0/GhRNr3eDrv1heTbvO+yrYdJb70yIKkPaZK5z+iOEomGJjZCOCXQG/gEXe/q8hF6vY21O9nSpJRO1MNzCalo201yonT1cJNDbz/rFNz2uaHfjiz0+VK5veRTgq3ZDhMd9zy2ibcnd8t2ML/OvNUPvnhAQnzffhfX+TI8dT9UHfuPcxTC7ckbBNJNzx7c4u3Pjk/6a1NXPah99L3PSdxyZ2z+eWoCxl54TlA7H+oucU5731nZPL1Sl5JBAMz6w08CHwWqAUWmdkMd+/ay7Ae5ocp6pzX1x/geHMLfXrn1mz06Bsbcy2W5OjccW1P4u82ld7oocnaBjLV/jsmki4QACmrgZINz37H86tYX7+fJxds4YxT+rQZK+mu//cnAHxnylIu+kA/hvR/D58Kdyqf/sj7mPjV2LMk0UCSieYWp5flPhxLPlkp9NM1s8uAH7v7VWF+HIC7/3uydSoqKryqqirZ4qQK+ZavgWeekjA91SFOd/TT/3qSZ8iku2afXsapJ/Xm1JN6aTgAkRwNe98ZVEeeOh+W5m7BiQ2DsiHcoafLH9XvPScz7e8vy6mcZrbY3SsSLSuJOwPgHCBan1ELXNo+k5mNAcYAfOADiRvW0jm5Ty+OZnBlka3B/U7j8vPOTro8feBP0Z88zbrJFjcdOpa06+A3//KDnH5KHw4fa+bwsRYOH2/myQzegiYiHQ0beAa79h+h8eAx/mLY2fzRqelPrb3M2LDrAB8ccDrDBmYeDM48NbeRA9IplTuDLwAj3P3rYf7vgEvd/VvJ1sn1zkBEpFylujMolecMtgFDIvODQ5qIiBtx7WUAAActSURBVHSBUgkGi4BhZnaumZ0MjAJmFLlMIiJloyTaDNz9uJl9C5hFrGvpRHfPzxgDIiKSVkkEAwB3nwkUrgO0iIgkVSrVRCIiUkQKBiIiomAgIiIKBiIiQok8dJYLM6sHNue4+tlAx7dtlL7uWm7ovmVXubtedy17dyj3H7t7whEAu20w6Awzq0r2FF4p667lhu5bdpW763XXsnfXcsepmkhERBQMRESkfIPBhGIXIEfdtdzQfcuucne97lr27lpuoEzbDEREpK1yvTMQEZEIBQMRESmvYGBmI8xsrZnVmNnYIpVhiJm9amarzGylmX0npPc3s0ozqw4/+4V0M7PxoczLzeyiyLZGh/zVZjY6kn6xma0I64y3PL5g1cx6m9kSM3s+zJ9rZgvCvqaGIcgxs1PCfE1YPjSyjXEhfa2ZXRVJL9jvx8z6mtkzZrbGzFab2WXd4Zib2ffC38k7ZvaUmZ1aqsfczCaaWZ2ZvRNJK/gxTraPTpb7F+FvZbmZPWdmfSPLsjqWufy+isLdy+JDbGjs9cAHgZOBZcD5RSjHIOCiMP1HwDrgfODnwNiQPha4O0xfA7xI7O2Ww4EFIb0/sCH87Bem+4VlC0NeC+tencfy/xPwJPB8mJ8GjArTDwP/EKZvBh4O06OAqWH6/HDsTwHODb+T3oX+/QCTgK+H6ZOBvqV+zIm9DnYjcFrkWH+1VI858JfARcA7kbSCH+Nk++hkua8E+oTpuyPlzvpYZvv7KtanaDvu8i8KlwGzIvPjgHElUK7pwGeBtcCgkDYIWBumfw1cH8m/Niy/Hvh1JP3XIW0QsCaS3iZfJ8s6GJgNfBp4PvxT7or807QeY2LvprgsTPcJ+az9cY/nK+TvBziL2EnV2qWX9DHnxLvB+4dj+DxwVSkfc2AobU+qBT/GyfbRmXK3W/bXwBOJjlG6Y5nL/0g+/uZz+ZRTNVH8HyuuNqQVTbgt/DiwABjo7vG31+8ABobpZOVOlV6bID0fHgD+BWgJ8+8F9rj78QT7ai1fWN4U8mf7ffLhXKAe+K3FqrgeMbPTKfFj7u7bgHuALcB2YsdwMd3jmMd1xTFOto98+RqxOxHSlC9Rei7/I0VRTsGgpJjZGcDvge+6+97oMo9dKpRUn18z+yugzt0XF7ssOehDrBrgIXf/OHCAWHVCqxI95v2AkcSC2fuB04ERRS1UJ3TFMc73PszsVuA48ES+tlmqyikYbAOGROYHh7QuZ2YnEQsET7j7syF5p5kNCssHAXUhPVm5U6UPTpDeWZ8APm9mm4ApxKqKfgn0NbP4G/Oi+2otX1h+FrA7h++TD7VArbsvCPPPEAsOpX7MPwNsdPd6dz8GPEvs99AdjnlcVxzjZPvoFDP7KvBXwJdDkMml3LvJ/vdVHMWqn+rqD7Grww3ErrLiDTwfLUI5DJgMPNAu/Re0bQT7eZj+HG0b2haG9P7E6sH7hc9GoH9Y1r6h7Zo8f4dPcqIB+WnaNo7dHKZvoW3j2LQw/VHaNsBtINb4VtDfD/A68OEw/eNwvEv6mAOXAiuB94TtTgK+XcrHnI5tBgU/xsn20clyjwBWAQPa5cv6WGb7+yrWp2g7LsqXjfVgWEes1f/WIpXhcmK3scuBpeFzDbG6wtlANfCHyD+AAQ+GMq8AKiLb+hpQEz43RtIrgHfCOv9JnhulaBsMPhj+SWvCH/0pIf3UMF8Tln8wsv6toWxrifS6KeTvB7gQqArH/b/CiabkjznwE2BN2Pbj4SRUkscceIpY28YxYndjN3XFMU62j06Wu4ZYfX78f/ThXI9lLr+vYnw0HIWIiJRVm4GIiCShYCAiIgoGIiKiYCAiIigYiIgICgYiBWNmf29mN6TJ82Mz+36C9KHRUTRFCq1P+iwikgt3f7jYZRDJlO4MRGi9El9jZo+Z2Toze8LMPmNmb4Zx8i8J4+b/Vxjjfr6Z/amZ9TKzTe3Gu682s4HRq34z+5CZvWRmi83sdTP7SIIyXGxmy8xsGbGnU0W6jIKByAnnAfcCHwmfLxF7Yvz7wA+JPQ28xN3/NMxPdvcWYsOQ/zWAmV0KbHb3ne22PQH4trtfHLb3qwT7/23Ic0G+v5hIOgoGIidsdPcV4QS/EpjtsUf0VxAbu+ZyYkNC4O6vAO81szOBqcDfhm2MCvOtwgi1fw48bWZLiY3RP6hdnr5AX3efG5Iez//XE0lObQYiJxyJTLdE5luI/a8cS7LePOA8MxsAXAv8tN3yXsTGtL8wj2UVySvdGYhk7nXgywBm9klgl7vvDXcPzwH3Aavdvc0wxB57X8VGM/tiWNfM7IJ2efYAe8zs8pD05YJ+E5F2FAxEMvdj4GIzWw7cBYyOLJsKfIV2VUQRXwZuCo3DK4m9tKa9G4EHQ1WS5avQIpnQqKUiIqI7AxERUTAQEREUDEREBAUDERFBwUBERFAwEBERFAxERAT4H2V+P1a33B1PAAAAAElFTkSuQmCC\n", 236 | "text/plain": [ 237 | "
" 238 | ] 239 | }, 240 | "metadata": { 241 | "needs_background": "light" 242 | }, 243 | "output_type": "display_data" 244 | } 245 | ], 246 | "source": [ 247 | "df = data.groupby('movieId').count()\n", 248 | "ax = df['rating'].plot()\n", 249 | "\n", 250 | "print('Most Popular Movies')\n", 251 | "movies.iloc[df.sort_values('rating', ascending=False)[:10].index]['title'].tolist()\n" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 63, 257 | "metadata": {}, 258 | "outputs": [ 259 | { 260 | "ename": "SyntaxError", 261 | "evalue": "invalid syntax (, line 1)", 262 | "output_type": "error", 263 | "traceback": [ 264 | "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m https://towardsdatascience.com/evaluation-metrics-for-recommender-systems-df56c6611093\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" 265 | ] 266 | } 267 | ], 268 | "source": [ 269 | "https://towardsdatascience.com/evaluation-metrics-for-recommender-systems-df56c6611093" 270 | ] 271 | } 272 | ], 273 | "metadata": { 274 | "kernelspec": { 275 | "display_name": "Python 3", 276 | "language": "python", 277 | "name": "python3" 278 | }, 279 | "language_info": { 280 | "codemirror_mode": { 281 | "name": "ipython", 282 | "version": 3 283 | }, 284 | "file_extension": ".py", 285 | "mimetype": "text/x-python", 286 | "name": "python", 287 | "nbconvert_exporter": "python", 288 | "pygments_lexer": "ipython3", 289 | "version": "3.6.9" 290 | }, 291 | "toc": { 292 | "base_numbering": 1, 293 | "nav_menu": {}, 294 | "number_sections": true, 295 | "sideBar": true, 296 | "skip_h1_title": false, 297 | "title_cell": "Table of Contents", 298 | "title_sidebar": "Contents", 299 | "toc_cell": false, 300 | "toc_position": {}, 301 | "toc_section_display": true, 302 | "toc_window_display": false 303 | } 304 | }, 305 | "nbformat": 4, 306 | "nbformat_minor": 4 307 | } 308 | --------------------------------------------------------------------------------