Skip to content
GitLab
Explorer
Connexion
S'inscrire
Navigation principale
Rechercher ou aller à…
Projet
M
MLSMM2156
Gestion
Activité
Membres
Labels
Programmation
Tickets
Tableaux des tickets
Jalons
Wiki
Code
Requêtes de fusion
Dépôt
Branches
Validations
Étiquettes
Graphe du dépôt
Comparer les révisions
Extraits de code
Compilation
Pipelines
Jobs
Planifications de pipeline
Artéfacts
Déploiement
Releases
Registre de paquets
Registre de conteneur
Registre de modèles
Opération
Environnements
Modules Terraform
Surveillance
Incidents
Analyse
Données d'analyse des chaînes de valeur
Analyse des contributeurs
Données d'analyse CI/CD
Données d'analyse du dépôt
Expériences du modèle
Aide
Aide
Support
Documentation de GitLab
Comparer les forfaits GitLab
Forum de la communauté
Contribuer à GitLab
Donner votre avis
Conditions générales et politique de confidentialité
Raccourcis clavier
?
Extraits de code
Groupes
Projets
Afficher davantage de fils d'Ariane
Teo Baldi
MLSMM2156
Validations
13ff9a3e
Valider
13ff9a3e
rédigé
2 years ago
par
Teo Baldi
Parcourir les fichiers
Options
Téléchargements
Correctifs
Plain Diff
complete assignement coding 1 - notebook
parent
7f30596a
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Pipeline
#37072
en échec
2 years ago
Étape : build
Étape : test
Modifications
1
Pipelines
1
Masquer les modifications d'espaces
En ligne
Côte à côte
Affichage de
1 fichier modifié
analytics.ipynb
+1116
-13
1116 ajouts, 13 suppressions
analytics.ipynb
avec
1116 ajouts
et
13 suppressions
analytics.ipynb
+
1116
−
13
Voir le fichier @
13ff9a3e
...
...
@@ -10,7 +10,7 @@
},
{
"cell_type": "code",
"execution_count":
2
,
"execution_count":
1
,
"metadata": {},
"outputs": [],
"source": [
...
...
@@ -21,7 +21,13 @@
"# third parties imports\n",
"import numpy as np \n",
"import pandas as pd\n",
"import random as rd\n",
"\n",
"# -- add new imports here --\n",
"import re\n",
"import seaborn as sns\n",
"import matplotlib.pyplot as plt\n",
"from scipy.sparse import csr_matrix\n",
"\n",
"# local imports\n",
"from constants import Constant as C\n",
...
...
@@ -29,6 +35,23 @@
"from loaders import load_items"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"movieId\n"
]
}
],
"source": [
"print(C.ITEM_ID_COL)"
]
},
{
"cell_type": "markdown",
"metadata": {},
...
...
@@ -39,20 +62,272 @@
},
{
"cell_type": "code",
"execution_count":
10
,
"execution_count":
3
,
"metadata": {},
"outputs": [],
"source": [
"# -- load the items and display the Dataframe"
"# -- load the items and display the Dataframe\n",
"df_items = load_items()"
]
},
{
"cell_type": "code",
"execution_count":
11
,
"execution_count":
4
,
"metadata": {},
"outputs": [],
"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>title</th>\n",
" <th>genres</th>\n",
" </tr>\n",
" <tr>\n",
" <th>movieId</th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Grumpier Old Men (1995)</td>\n",
" <td>Comedy|Romance</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>Cutthroat Island (1995)</td>\n",
" <td>Action|Adventure|Romance</td>\n",
" </tr>\n",
" <tr>\n",
" <th>34</th>\n",
" <td>Babe (1995)</td>\n",
" <td>Children|Drama</td>\n",
" </tr>\n",
" <tr>\n",
" <th>59</th>\n",
" <td>Confessional, The (Confessionnal, Le) (1995)</td>\n",
" <td>Drama|Mystery</td>\n",
" </tr>\n",
" <tr>\n",
" <th>64</th>\n",
" <td>Two if by Sea (1996)</td>\n",
" <td>Comedy|Romance</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>148652</th>\n",
" <td>The Ridiculous 6 (2015)</td>\n",
" <td>Comedy|Western</td>\n",
" </tr>\n",
" <tr>\n",
" <th>151307</th>\n",
" <td>The Lovers and the Despot</td>\n",
" <td>(no genres listed)</td>\n",
" </tr>\n",
" <tr>\n",
" <th>152173</th>\n",
" <td>Michael Jackson's Thriller (1983)</td>\n",
" <td>Horror</td>\n",
" </tr>\n",
" <tr>\n",
" <th>160440</th>\n",
" <td>The Maid's Room (2014)</td>\n",
" <td>Thriller</td>\n",
" </tr>\n",
" <tr>\n",
" <th>160656</th>\n",
" <td>Tallulah (2016)</td>\n",
" <td>Drama</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>912 rows × 2 columns</p>\n",
"</div>"
],
"text/plain": [
" title \\\n",
"movieId \n",
"3 Grumpier Old Men (1995) \n",
"15 Cutthroat Island (1995) \n",
"34 Babe (1995) \n",
"59 Confessional, The (Confessionnal, Le) (1995) \n",
"64 Two if by Sea (1996) \n",
"... ... \n",
"148652 The Ridiculous 6 (2015) \n",
"151307 The Lovers and the Despot \n",
"152173 Michael Jackson's Thriller (1983) \n",
"160440 The Maid's Room (2014) \n",
"160656 Tallulah (2016) \n",
"\n",
" genres \n",
"movieId \n",
"3 Comedy|Romance \n",
"15 Action|Adventure|Romance \n",
"34 Children|Drama \n",
"59 Drama|Mystery \n",
"64 Comedy|Romance \n",
"... ... \n",
"148652 Comedy|Western \n",
"151307 (no genres listed) \n",
"152173 Horror \n",
"160440 Thriller \n",
"160656 Drama \n",
"\n",
"[912 rows x 2 columns]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# -- display relevant informations that can be extracted from the dataset\n",
"display(df_items)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of movies : 912\n"
]
}
],
"source": [
"# Number of movies in the dataset\n",
"n_movies = len(df_items)\n",
"print(f\"Number of movies : \",n_movies)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"MINIMUM AND MAXIMUN YEAR\n",
"°The minimum year : 1921\n",
"°The maximum year : 2016\n"
]
}
],
"source": [
"import re\n",
"\n",
"#EXTRACT THE DATE OF EACH MOVIE (creation of a list and a dico) \n",
"dates = []\n",
"dico = {}\n",
"for title in df_items.iloc[:, 0]:\n",
" match = re.search(r'\\((\\d{4})[\\s-]*\\d*[\\s-]*\\d*\\)', title) \n",
" if match :\n",
" dates.append(int(match.group(1)))\n",
" dico[title] = int(match.group(1))\n",
" else : \n",
" #print(title) #movies with no date\n",
" dico[title] = \"/\"\n",
" \n",
"#print(dates)\n",
"#print(len(dates))\n",
"#print(dico)\n",
"\n",
"# TESTS\n",
"#print(f\"TESTS...\")\n",
"#for key,value in dico.items():\n",
" #if key == \"Stranger Things\" :\n",
" #print(value)\n",
" #if key == \"Fawlty Towers (1975-1979)\" :\n",
" #print(value)\n",
" \n",
"#---------------------------------------------------------------\n",
"# MINIMUM AND MAXIMUN YEAR\n",
"print(f\"\\nMINIMUM AND MAXIMUN YEAR\")\n",
"min_year = min(dates)\n",
"max_year = max(dates)\n",
"print(f\"°The minimum year : \", min_year)\n",
"print(f\"°The maximum year : \", max_year)\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"°There are 20 different genres of movies.\n",
"°Here is the genres list :\n",
" -Comedy (307 movies)\n",
" -Romance (141 movies)\n",
" -Action (149 movies)\n",
" -Adventure (110 movies)\n",
" -Children (64 movies)\n",
" -Drama (454 movies)\n",
" -Mystery (64 movies)\n",
" -Thriller (169 movies)\n",
" -Crime (110 movies)\n",
" -Sci-Fi (73 movies)\n",
" -Documentary (50 movies)\n",
" -Fantasy (68 movies)\n",
" -War (32 movies)\n",
" -Horror (91 movies)\n",
" -Western (21 movies)\n",
" -Animation (45 movies)\n",
" -Musical (38 movies)\n",
" -Film-Noir (11 movies)\n",
" -IMAX (13 movies)\n",
" -(no genres listed) (3 movies)\n"
]
}
],
"source": [
"# -- display relevant informations that can be extracted from the dataset"
"list_genres = []\n",
"for genres in df_items.iloc[:,1]:\n",
" liste = genres.split(\"|\") #with the focntion split, a new list is created\n",
" for i in liste :\n",
" if i not in list_genres :\n",
" list_genres.append(i)\n",
"print(f\"°There are\", len(list_genres), \"different genres of movies.\")\n",
"\n",
"print(f\"°Here is the genres list :\") \n",
"for g in list_genres :\n",
" count = 0 #some films have several genres\n",
" for row in df_items.iloc[:, 1]:\n",
" if g in row :\n",
" count += 1\n",
" print(\" -\" + g + \" (\" + str(count) + \" movies)\")"
]
},
{
...
...
@@ -65,28 +340,856 @@
},
{
"cell_type": "code",
"execution_count":
14
,
"execution_count":
8
,
"metadata": {},
"outputs": [],
"source": [
"# -- load the items and display the Dataframe"
"# -- load the items and display the Dataframe\n",
"df_ratings = load_ratings()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"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>userId</th>\n",
" <th>movieId</th>\n",
" <th>rating</th>\n",
" <th>timestamp</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>15</td>\n",
" <td>34</td>\n",
" <td>3.0</td>\n",
" <td>997938310</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>15</td>\n",
" <td>95</td>\n",
" <td>1.5</td>\n",
" <td>1093028331</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>15</td>\n",
" <td>101</td>\n",
" <td>4.0</td>\n",
" <td>1134522072</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>15</td>\n",
" <td>123</td>\n",
" <td>4.0</td>\n",
" <td>997938358</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>15</td>\n",
" <td>125</td>\n",
" <td>3.5</td>\n",
" <td>1245362506</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5291</th>\n",
" <td>665</td>\n",
" <td>3908</td>\n",
" <td>1.0</td>\n",
" <td>1046967201</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5292</th>\n",
" <td>665</td>\n",
" <td>4052</td>\n",
" <td>4.0</td>\n",
" <td>992838277</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5293</th>\n",
" <td>665</td>\n",
" <td>4351</td>\n",
" <td>4.0</td>\n",
" <td>992837743</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5294</th>\n",
" <td>665</td>\n",
" <td>4643</td>\n",
" <td>4.0</td>\n",
" <td>997239207</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5295</th>\n",
" <td>665</td>\n",
" <td>5502</td>\n",
" <td>4.0</td>\n",
" <td>1046967596</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>5296 rows × 4 columns</p>\n",
"</div>"
],
"text/plain": [
" userId movieId rating timestamp\n",
"0 15 34 3.0 997938310\n",
"1 15 95 1.5 1093028331\n",
"2 15 101 4.0 1134522072\n",
"3 15 123 4.0 997938358\n",
"4 15 125 3.5 1245362506\n",
"... ... ... ... ...\n",
"5291 665 3908 1.0 1046967201\n",
"5292 665 4052 4.0 992838277\n",
"5293 665 4351 4.0 992837743\n",
"5294 665 4643 4.0 997239207\n",
"5295 665 5502 4.0 1046967596\n",
"\n",
"[5296 rows x 4 columns]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# -- display relevant informations that can be extracted from the dataset\n",
"display(df_ratings)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"°Total number of ratings : 5296\n",
"°Number of unique users/raters : 107\n",
"°Number of unique movies : 834\n",
"°Average number of ratings per user: 49.5\n",
"°Average number of ratings per movie: 6.35\n"
]
}
],
"source": [
"# Number of ratings in the dataset\n",
"n_ratings = len(df_ratings)\n",
"print(f\"°Total number of ratings : \", n_ratings)\n",
"\n",
"#----------------------------------------------\n",
"\n",
"# Number of unique users (= number of raters)\n",
"unique_users = df_ratings['userId'].nunique()\n",
"print(f\"°Number of unique users/raters : \", unique_users)\n",
"\n",
"#----------------------------------------------\n",
"\n",
"# Number of unique movies (in the ratings matrix) (= number of rated movies)\n",
"unique_movies = df_ratings['movieId'].nunique()\n",
"print(f\"°Number of unique movies : \", unique_movies)\n",
"\n",
"#----------------------------------------------\n",
"print(f\"°Average number of ratings per user: {round(n_ratings/unique_users, 2)}\")\n",
"print(f\"°Average number of ratings per movie: {round(n_ratings/unique_movies, 2)}\")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": true
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The movie(s) with the most ratings is/are :\n",
" - 1240 with 75 ratings\n",
" - 1210 with 75 ratings\n",
"\n",
"The movie(s) with the less ratings is/are :\n",
" - 2128 with 1 ratings\n",
" - 2481 with 1 ratings\n",
" - 70751 with 1 ratings\n",
" - 34002 with 1 ratings\n",
" - 2674 with 1 ratings\n",
" - 66200 with 1 ratings\n",
" - 78967 with 1 ratings\n",
" - 57418 with 1 ratings\n",
" - 34608 with 1 ratings\n",
" - 41714 with 1 ratings\n",
" - 78111 with 1 ratings\n",
" - 74089 with 1 ratings\n",
" - 76763 with 1 ratings\n",
" - 43635 with 1 ratings\n",
" - 60135 with 1 ratings\n",
" - 4927 with 1 ratings\n",
" - 3302 with 1 ratings\n",
" - 4383 with 1 ratings\n",
" - 5521 with 1 ratings\n",
" - 4831 with 1 ratings\n",
" - 87383 with 1 ratings\n",
" - 5646 with 1 ratings\n",
" - 4716 with 1 ratings\n",
" - 6671 with 1 ratings\n",
" - 6684 with 1 ratings\n",
" - 7300 with 1 ratings\n",
" - 4606 with 1 ratings\n",
" - 7354 with 1 ratings\n",
" - 7881 with 1 ratings\n",
" - 7883 with 1 ratings\n",
" - 7921 with 1 ratings\n",
" - 8003 with 1 ratings\n",
" - 2631 with 1 ratings\n",
" - 8853 with 1 ratings\n",
" - 4082 with 1 ratings\n",
" - 64321 with 1 ratings\n",
" - 8903 with 1 ratings\n",
" - 25842 with 1 ratings\n",
" - 26180 with 1 ratings\n",
" - 26271 with 1 ratings\n",
" - 97168 with 1 ratings\n",
" - 26564 with 1 ratings\n",
" - 26732 with 1 ratings\n",
" - 26731 with 1 ratings\n",
" - 26835 with 1 ratings\n",
" - 1854 with 1 ratings\n",
" - 30712 with 1 ratings\n",
" - 4106 with 1 ratings\n",
" - 2767 with 1 ratings\n",
" - 96565 with 1 ratings\n",
" - 96530 with 1 ratings\n",
" - 124859 with 1 ratings\n",
" - 118326 with 1 ratings\n",
" - 116887 with 1 ratings\n",
" - 109042 with 1 ratings\n",
" - 106542 with 1 ratings\n",
" - 103543 with 1 ratings\n",
" - 103502 with 1 ratings\n",
" - 99992 with 1 ratings\n",
" - 98279 with 1 ratings\n",
" - 95508 with 1 ratings\n",
" - 2032 with 1 ratings\n",
" - 92198 with 1 ratings\n",
" - 92048 with 1 ratings\n",
" - 89427 with 1 ratings\n",
" - 86028 with 1 ratings\n",
" - 80590 with 1 ratings\n",
" - 43558 with 1 ratings\n",
" - 7895 with 1 ratings\n",
" - 7245 with 1 ratings\n",
" - 4662 with 1 ratings\n",
" - 127319 with 1 ratings\n",
" - 132157 with 1 ratings\n",
" - 135536 with 1 ratings\n",
" - 136654 with 1 ratings\n",
" - 140739 with 1 ratings\n",
" - 127728 with 1 ratings\n",
" - 108795 with 1 ratings\n",
" - 99615 with 1 ratings\n",
" - 99609 with 1 ratings\n",
" - 98933 with 1 ratings\n",
" - 60086 with 1 ratings\n",
" - 8453 with 1 ratings\n",
" - 7260 with 1 ratings\n",
" - 6033 with 1 ratings\n",
" - 1903 with 1 ratings\n",
" - 57845 with 1 ratings\n",
" - 8273 with 1 ratings\n",
" - 6679 with 1 ratings\n",
" - 6022 with 1 ratings\n",
" - 160440 with 1 ratings\n",
" - 143255 with 1 ratings\n",
" - 142997 with 1 ratings\n",
" - 139915 with 1 ratings\n",
" - 59 with 1 ratings\n",
" - 1654 with 1 ratings\n",
" - 96849 with 1 ratings\n",
" - 127114 with 1 ratings\n",
" - 304 with 1 ratings\n",
" - 219 with 1 ratings\n",
" - 160656 with 1 ratings\n",
" - 151307 with 1 ratings\n",
" - 147845 with 1 ratings\n",
" - 142258 with 1 ratings\n",
" - 140247 with 1 ratings\n",
" - 136018 with 1 ratings\n",
" - 128606 with 1 ratings\n",
" - 118898 with 1 ratings\n",
" - 1044 with 1 ratings\n",
" - 109205 with 1 ratings\n",
" - 108076 with 1 ratings\n",
" - 105355 with 1 ratings\n",
" - 104339 with 1 ratings\n",
" - 104321 with 1 ratings\n",
" - 103444 with 1 ratings\n",
" - 98611 with 1 ratings\n",
" - 98473 with 1 ratings\n",
" - 97817 with 1 ratings\n",
" - 697 with 1 ratings\n",
" - 775 with 1 ratings\n",
" - 980 with 1 ratings\n",
" - 1349 with 1 ratings\n",
" - 5223 with 1 ratings\n",
" - 46772 with 1 ratings\n",
" - 27441 with 1 ratings\n",
" - 5182 with 1 ratings\n",
" - 4401 with 1 ratings\n",
" - 4319 with 1 ratings\n",
" - 3487 with 1 ratings\n",
" - 1928 with 1 ratings\n",
" - 4003 with 1 ratings\n",
" - 3960 with 1 ratings\n",
" - 3939 with 1 ratings\n",
" - 3662 with 1 ratings\n",
" - 3661 with 1 ratings\n",
" - 3041 with 1 ratings\n",
" - 2855 with 1 ratings\n",
" - 2091 with 1 ratings\n",
" - 1998 with 1 ratings\n",
" - 1550 with 1 ratings\n",
" - 1369 with 1 ratings\n",
" - 889 with 1 ratings\n",
" - 73344 with 1 ratings\n",
" - 112070 with 1 ratings\n",
" - 106870 with 1 ratings\n",
" - 5038 with 1 ratings\n",
" - 4608 with 1 ratings\n",
" - 5122 with 1 ratings\n",
" - 89300 with 1 ratings\n",
" - 85316 with 1 ratings\n",
" - 152173 with 1 ratings\n",
" - 114766 with 1 ratings\n",
" - 96815 with 1 ratings\n",
" - 6414 with 1 ratings\n",
" - 90357 with 1 ratings\n",
" - 88272 with 1 ratings\n",
" - 48972 with 1 ratings\n",
" - 40833 with 1 ratings\n",
" - 6940 with 1 ratings\n",
" - 5696 with 1 ratings\n",
" - 2817 with 1 ratings\n",
" - 5841 with 1 ratings\n",
" - 1624 with 1 ratings\n",
" - 8871 with 1 ratings\n",
" - 8019 with 1 ratings\n",
" - 966 with 1 ratings\n",
" - 9005 with 1 ratings\n",
" - 8745 with 1 ratings\n",
" - 8057 with 1 ratings\n",
" - 6356 with 1 ratings\n",
" - 408 with 1 ratings\n",
" - 3531 with 1 ratings\n",
" - 54785 with 1 ratings\n",
" - 6665 with 1 ratings\n",
" - 32797 with 1 ratings\n",
" - 32892 with 1 ratings\n",
" - 6114 with 1 ratings\n",
" - 4863 with 1 ratings\n",
" - 4500 with 1 ratings\n",
" - 44613 with 1 ratings\n",
" - 1181 with 1 ratings\n",
" - 5773 with 1 ratings\n",
" - 6103 with 1 ratings\n",
" - 8253 with 1 ratings\n",
" - 69746 with 1 ratings\n",
" - 63826 with 1 ratings\n",
" - 136592 with 1 ratings\n",
" - 36289 with 1 ratings\n",
" - 106441 with 1 ratings\n",
" - 68959 with 1 ratings\n",
" - 83506 with 1 ratings\n",
" - 69685 with 1 ratings\n",
" - 33558 with 1 ratings\n",
" - 56069 with 1 ratings\n",
" - 8790 with 1 ratings\n",
" - 98829 with 1 ratings\n",
" - 26422 with 1 ratings\n",
" - 118082 with 1 ratings\n",
" - 1750 with 1 ratings\n",
" - 5343 with 1 ratings\n",
" - 1564 with 1 ratings\n",
" - 31422 with 1 ratings\n",
" - 25962 with 1 ratings\n",
" - 6559 with 1 ratings\n",
" - 2835 with 1 ratings\n",
" - 56095 with 1 ratings\n",
" - 70159 with 1 ratings\n",
" - 2552 with 1 ratings\n",
" - 72104 with 1 ratings\n",
" - 95858 with 1 ratings\n",
" - 6920 with 1 ratings\n",
" - 5440 with 1 ratings\n",
" - 3657 with 1 ratings\n",
" - 2173 with 1 ratings\n",
" - 3240 with 1 ratings\n",
" - 92681 with 1 ratings\n",
" - 80615 with 1 ratings\n",
" - 1826 with 1 ratings\n",
" - 57951 with 1 ratings\n",
" - 51698 with 1 ratings\n",
" - 56336 with 1 ratings\n",
" - 27899 with 1 ratings\n",
" - 72683 with 1 ratings\n",
" - 126106 with 1 ratings\n",
" - 8840 with 1 ratings\n",
" - 7407 with 1 ratings\n",
" - 6739 with 1 ratings\n",
" - 6579 with 1 ratings\n",
" - 2210 with 1 ratings\n",
" - 8722 with 1 ratings\n",
" - 145307 with 1 ratings\n",
" - 120392 with 1 ratings\n",
" - 47714 with 1 ratings\n",
" - 110110 with 1 ratings\n",
" - 6203 with 1 ratings\n",
" - 6794 with 1 ratings\n",
" - 4371 with 1 ratings\n",
" - 4077 with 1 ratings\n",
" - 3799 with 1 ratings\n",
" - 1896 with 1 ratings\n",
" - 25839 with 1 ratings\n",
" - 49394 with 1 ratings\n",
" - 34129 with 1 ratings\n",
" - 133195 with 1 ratings\n",
" - 57038 with 1 ratings\n",
" - 56869 with 1 ratings\n",
" - 49299 with 1 ratings\n",
" - 48165 with 1 ratings\n",
" - 5560 with 1 ratings\n",
" - 5568 with 1 ratings\n",
" - 65088 with 1 ratings\n",
" - 112911 with 1 ratings\n",
" - 71525 with 1 ratings\n",
" - 51939 with 1 ratings\n",
" - 33646 with 1 ratings\n",
" - 5092 with 1 ratings\n",
" - 2824 with 1 ratings\n",
" - 27075 with 1 ratings\n",
" - 80599 with 1 ratings\n",
" - 74486 with 1 ratings\n",
" - 460 with 1 ratings\n",
" - 26915 with 1 ratings\n",
" - 2839 with 1 ratings\n",
" - 78316 with 1 ratings\n",
" - 5054 with 1 ratings\n",
" - 3737 with 1 ratings\n",
" - 939 with 1 ratings\n",
" - 54787 with 1 ratings\n",
" - 53999 with 1 ratings\n",
" - 6800 with 1 ratings\n",
" - 4200 with 1 ratings\n",
" - 6757 with 1 ratings\n",
" - 82167 with 1 ratings\n",
" - 27815 with 1 ratings\n",
" - 7316 with 1 ratings\n",
" - 98369 with 1 ratings\n",
" - 93498 with 1 ratings\n",
" - 89047 with 1 ratings\n",
" - 84160 with 1 ratings\n",
" - 54281 with 1 ratings\n",
" - 4688 with 1 ratings\n",
" - 130490 with 1 ratings\n",
" - 4350 with 1 ratings\n",
" - 25901 with 1 ratings\n",
" - 74156 with 1 ratings\n",
" - 72479 with 1 ratings\n",
" - 71490 with 1 ratings\n",
" - 94939 with 1 ratings\n",
" - 8142 with 1 ratings\n",
" - 5826 with 1 ratings\n",
" - 100517 with 1 ratings\n",
" - 104726 with 1 ratings\n",
" - 6211 with 1 ratings\n",
" - 5796 with 1 ratings\n",
" - 148652 with 1 ratings\n",
" - 112767 with 1 ratings\n",
" - 54910 with 1 ratings\n",
" - 27857 with 1 ratings\n",
" - 26025 with 1 ratings\n",
" - 140755 with 1 ratings\n",
"\n",
"----------------------------------------------------\n",
"\n",
"°The lowest rated movie :\n",
" title genres\n",
"movieId \n",
"5521 Principal, The (1987) Action|Crime|Drama\n",
"With 1 rating(s)\n",
"\n",
"°The best rated movie :\n",
" title genres\n",
"movieId \n",
"1564 For Roseanna (Roseanna's Grave) (1997) Comedy|Drama|Romance\n",
"With 1 rating(s)\n"
]
}
],
"source": [
"# Number of ratings of the most rated movie(s)\n",
"value_counts = df_ratings['movieId'].value_counts()\n",
"\n",
"max_occ = value_counts.max()\n",
"max_indexes = []\n",
"for index, count in value_counts.iteritems():\n",
" if count == max_occ:\n",
" max_indexes.append(index)\n",
"print(\"The movie(s) with the most ratings is/are :\")\n",
"for index in max_indexes:\n",
" print(f\" -\", index, \"with\", max_occ, \"ratings\")\n",
" \n",
"# Number of ratings of the less rated movie(s)\n",
"min_occ = value_counts.min()\n",
"min_indexes = []\n",
"for index, count in value_counts.iteritems():\n",
" if count == min_occ:\n",
" min_indexes.append(index)\n",
"print(\"\\nThe movie(s) with the less ratings is/are :\")\n",
"for index in min_indexes:\n",
" print(f\" -\", index, \"with\", min_occ, \"ratings\")\n",
"\n",
"print(\"\\n----------------------------------------------------\")\n",
"\n",
"# Best and lowest rated movie (based on the average)\n",
"mean_ratings = df_ratings.groupby('movieId')[['rating']].mean()\n",
"lowest_rated = mean_ratings['rating'].idxmin()\n",
"print(f\"\\n°The lowest rated movie :\")\n",
"print(df_items[df_items.index == lowest_rated])\n",
"nbr_ratings = 0\n",
"for id in df_ratings.iloc[:,1] :\n",
" if id == lowest_rated :\n",
" nbr_ratings += 1\n",
"print(\"With\", nbr_ratings, \"rating(s)\")\n",
"\n",
"print(f\"\\n°The best rated movie :\")\n",
"best_rated = mean_ratings['rating'].idxmax()\n",
"print(df_items[df_items.index == best_rated])\n",
"nbr_ratings = 0\n",
"for id in df_ratings.iloc[:,1] :\n",
" if id == best_rated :\n",
" nbr_ratings += 1\n",
"print(\"With\", nbr_ratings, \"rating(s)\")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"°All the possible rating values, from smallest value to value highest : [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]\n",
"°Number of rated movies per rating value :\n",
"3.0 (1049 movies)\n",
"1.5 (109 movies)\n",
"4.0 (1458 movies)\n",
"3.5 (633 movies)\n",
"0.5 (64 movies)\n",
"1.0 (223 movies)\n",
"2.0 (469 movies)\n",
"2.5 (289 movies)\n",
"5.0 (650 movies)\n",
"4.5 (352 movies)\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAHICAYAAACvevVkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAA9hAAAPYQGoP6dpAABDn0lEQVR4nO3deVyVdf7//+cBZHEBRGUrRKcMlxQnF8Tc5RMu42hjKck4ZoyWA5XZp8xvubUxmplppNmMS2VjTqapUya5QBpuGOWelVspkCHgMgLC9fujH+fTkUVE4By4Hvfb7brdPO/rfZ3r9TrXQZ9e13UOFsMwDAEAAJiYk70LAAAAsDcCEQAAMD0CEQAAMD0CEQAAMD0CEQAAMD0CEQAAMD0CEQAAMD0CEQAAMD0CEQAAMD0CEVCGbdu2yWKxaMaMGXbZf4sWLdSiRQubsRkzZshisWjbtm12qenEiROyWCx68MEH7bL/qlBQUKAZM2aoVatWcnNzk8Vi0dq1a+1dVpWw9/ujKtWF9xpqFxd7FwBUpxMnTqhly5Y2Yx4eHvL29labNm109913a8yYMbrtttuqfN99+vRRUlKSattvxykOYSdOnLBrHdXl1Vdf1cyZM9WrVy+NGDFC9erVU+vWre1dlinV9fcaahcCEUzhtttu05///GdJUl5enjIzM7V792698MILevnll/X000/rpZdeksVisW7TtWtXHT58WE2bNrVLzZs3b7bLfstzyy236PDhw/Ly8rJ3KZW2YcMGNWzYUImJiXJ1dbV3OVUqLi5OUVFRat68ub1LuWl14b2G2oVABFO4/fbbS730tX37do0ePVrx8fFydnbWCy+8YF1Xv359u545qI6zVjerLpxNOXPmjJo0aVLnwpAkNW3a1G4BvqrVhfcaahfuIYKp9ejRQxs3bpSbm5tmz56t06dPW9eVdQ/RsWPHNHbsWLVs2VJubm7y8fFRaGioJk6caL08ZrFYlJSUZP1z8VJ8P8Rv7484fPiw7r33XjVp0kQWi8V6+aC0e4h+65///Kfat28vd3d33XLLLXriiSd04cIFmznl3Qd17T0axY9PnjypkydP2tRdvH1593WcPHlSMTExuuWWW+Tq6qpbb71VMTExOnXqVIm5ffr0kcVisd7P06JFC7m5uemOO+7Qm2++WWbPZVm6dKnCwsLUsGFDNWzYUGFhYVq2bJnNnOL7a44fP27TX3mvcTGLxaI+ffrop59+0qhRo9S0aVM1atRIgwcP1g8//CBJOnz4sIYNGyYfHx81atRI9913nzIyMkp9vvXr16tv377y8vKSh4eHQkNDNXfuXF29etU65+TJk3JyclK/fv1KfY6CggI1bdpUQUFBKioqsumxtHuIvvnmG0VFRSkgIECurq4KDg7Wo48+ql9++eW6/Rcrfk9mZ2crLi5OQUFBcnFxsb7WqampiouL05133mntrX379vr73/+ugoIC6/PczHutMu+dc+fOafz48fL19VX9+vXVpUsXrVmzRsuWLZPFYinxXtm6dasGDhyowMBAubm5yc/PTz179tTixYsr/Fqh9uEMEUwvJCREI0aM0Lvvvqu1a9fq0UcfLXPumTNn1LVrV126dEmDBw/WyJEjdenSJR07dkxvvvmm5syZIxcXF02fPl3Lli3TyZMnNX36dOv2HTt2tHm+7777Tt26dVP79u314IMP6pdffqnQmYu5c+dq8+bNGjlypAYPHqzPP/9c8+bN086dO5WcnKx69erd8Ovg7e2t6dOna968eZKkiRMnWtf16dOn3G2//fZb9ejRQz///LOGDBmidu3a6cCBA1qyZInWr1+v7du364477iix3QMPPKDdu3dr4MCBcnZ21qpVqxQbG6t69epp3LhxFar7scce04IFC3TLLbcoJiZGkrR69WqNHTtWX331lV5//XWbHq7tz9vbu0L7OX/+vHr06CF/f3+NGTNG3377rTZs2KAjR47o448/Vs+ePdWpUyc99NBDSk1N1erVq5WVlaUtW7bYPM/cuXP15JNPysfHR6NGjVKDBg20bt06Pfnkk/riiy/00UcfyWKxKDg4WL169VJSUpJ+/PFH3XrrrTbP88knn+iXX37R5MmT5eRU/v9t161bpxEjRsjJyUlDhw5VUFCQDh06pDfeeEOfffaZdu3apcaNG1fodcjLy1O/fv108eJF/fGPf5SLi4v8/PwkSW+//bbWr1+vXr16adCgQbp8+bK2bdumKVOmaM+ePVq9erX1Na/se61YRd87Fy9eVO/evXXo0CF1795dvXr10o8//qioqChFRkaWeN7//Oc/GjJkiLy9vTV06FAFBATo559/1tdff613331X48ePr1B9qIUMoA47fvy4IcmIjIwsd94///lPQ5IxevRo69jWrVsNScb06dOtY/PnzzckGfPmzSvxHL/88ovN4969extl/YgV1yXJmDZtWqlzgoODjeDgYJux6dOnG5IMV1dX4+uvv7aOFxUVGaNGjTIkGXPmzCm3h2trGDNmzHX3e71t+vbta0gy3nrrLZvxhIQEQ5LRr18/m/Hi1yYsLMzIycmxjh85csRwcXExQkJCSt3/tZKSkgxJRps2bYzs7GzreFZWlnHHHXcYkozk5OQK91eW4mP1xBNP2IxPmDDBkGR4e3vbvCeKioqMQYMGGZKM1NRU6/h3331nuLi4GL6+vsapU6es41euXDF69OhhSDLeeecd6/g//vEPQ5Ixa9asEjUNHz7ckGQcOHDAOlb8/ti6dat17Ny5c4anp6dxyy23GCdOnLB5jn/961+GJCMuLq5Cr0NwcLD15+ny5csl1p88edK4evWqzVhRUZHx0EMPGZKM7du3l3i+G32v3eh757nnnjMkGePHj7cZ//zzz63HdenSpdbxP/3pT4YkIy0trURN586dK7VW1A1cMgMkBQYGSvr11HpFeHh4lBjz8fG54f36+/vr2WefveHt/vKXv6hDhw7WxxaLRS+//LKcnZ1LnP6vbqdOndLWrVvVtm3bEmd1HnnkEbVu3VpbtmyxuRxZLD4+Xp6entbHISEhuvvuu3X06NESl/9Ks3z5ckm/Xir67c23jRs3tp6Zq6rXo2HDhnrxxRdtxh544AFJUpMmTfTYY49Zxy0Wi6KioiRJX3/9tXX8/fff19WrV/Xkk08qKCjIOu7m5qZZs2aVqPe+++6Tu7u73nvvPZv9Zmdna8OGDerYsaPatWtXbt3vvPOOcnNzFR8fr+DgYJt1UVFRuuuuu7Ry5crrtW9j9uzZpf4MNG/eXM7OzjZjFotFsbGxkqTPP//8hvZTnoq+d9577z25urrq+eeft9m+f//+uueee8p8/tL6a9KkSRVUDkfFJTPgBgwZMkRTpkxRbGysNm/erAEDBqh379763e9+V6nnCw0NrdTNvT179iwxFhwcrKCgIB08eFD5+fk1dtNwWlqaJKl37942n9KTJCcnJ/Xq1UtHjhxRWlqaTQiQpE6dOpV4vuJLQ9nZ2WrUqFG5+/7qq68klX6ZpW/fvjb13axWrVqpfv36NmMBAQGSpA4dOpTovXjdmTNnKlRveHi43N3dber18vLSH//4R61atUpff/21QkNDJUn//ve/lZeXp9GjR1+37p07d0qSdu3ape+//77E+itXrujcuXM6d+5chW7Idnd3V/v27Utdl5+frzfeeEMrV67UkSNHdPHiRZuvnfjta3GzKvLeyc3N1YkTJ9S2bVvrZb3fuvvuu7Vp0yabsaioKH300Ufq1q2bRo0apf79+6tnz5515mZ1lI1ABOj//qJu1qxZufNatGihnTt3asaMGfrkk0+0atUqSVLr1q31/PPP6/7777+h/Zb2l/TNbOfn56cTJ07owoULNfa/2dzc3HJrKg4GxfN+67f/wy/m4vLrX0uFhYUV2reTk1Opx83Pz08Wi6XU/VZGebWWt+63NxOX91pZLBb5+fnpp59+shkfPXq0Vq1apffee88aiN599105Oztr1KhR1607KytLkpSQkFDuvEuXLlXoH31fX98S4a/Yfffdp/Xr1+uOO+7QyJEj5evrq3r16ik7O1uvv/668vLyrvv8FVWR907x6+3r61vqc5R2HO6//36tXbtWc+fO1aJFi5SQkCCLxaK+ffvq1VdfLXEfIOoOLpkBkvVTOV26dLnu3DvvvFMffvihsrKylJKSomnTpik9PV0jR47Ujh07bmi/Zf3Dcj1lfXopIyNDFovFemal+Gbb3356qVhOTk6l9n2t4n+YyqopPT3dZl5V8vT0VFFRkX7++ecS6zIzM2UYRrXst7LKe60Mw1BGRkaJegcMGKBmzZrpX//6l4qKinTixAlt375dERER8vf3r/A+9+/fL8MwylyuvZxWlrLes3v27NH69esVGRmpQ4cO6e2339ZLL72kGTNmWC8f1rTi3jMzM0tdX9Z7dujQoUpKStL58+f16aef6q9//au2bdumAQMGKDs7u7rKhZ0RiGB63377rVatWiU3Nzfde++9Fd6uXr166tatm2bOnKn58+fLMAxt2LDBur74XoqKnOm4UV988UWJsZMnT+r06dNq166d9XJZ8SeHrj3rIP3f5ZtrOTs731DNxf9jTk5OLvGt3IZhKDk52WZeVfr9738vSaV+zLx4zJH+R19evbt27dKVK1dK1Ovi4qKoqCj99NNP2rp1q1asWCHDMKxfNHo9YWFhkqSUlJSbqv16ii/HDR48uMR9RKW9X6Ubf6/dKE9PT7Vo0ULfffddqaHoyy+/LHf7Ro0aacCAAVq8eLEefPBBZWRkaNeuXdVVLuyMQART27FjhyIjI5WXl6dnnnlGt9xyS7nzU1NTS70EU/w/TXd3d+tY8U3Wpd1MfLPeeecdffPNN9bHhmHo//2//6fCwkKb720JCQlRo0aNtG7dOuulk+J6r71B+Ld1nzt3TleuXKlQLc2bN1ffvn118OBBLVmyxGbd4sWLdfjwYfXr16/E/UNVYcyYMZKkmTNn2hyXnJwczZw502aOIxg1apRcXFw0d+5cm/tp8vPzNXnyZEkq9Tueiu8Vevfdd/Xuu++qQYMGFQ7vY8eOVaNGjfTss8/q4MGDJdZfvnzZep/RzSg+w7R9+3ab8YMHDyo+Pr7UbW70vVYZ0dHRys/Pt/n6C+nXUPrZZ5+VmJ+cnFxqSCsOVL/9GUfdwj1EMIXvvvvO+oVv+fn51l/dsX//fjk7O+u5554r8Rdmad5991299dZb6tWrl2677TZ5enrq0KFD+uSTT+Tj46OxY8da5/br108ffvihhg8froEDB8rd3V2hoaEaMmTITfcTGRmp8PBwRUVFqVmzZtq8ebP27t2rbt262XyPkqurqx599FG9/PLLuuuuuzR06FBduHBB69evV+/evUu9ybZfv37au3evBg4cqJ49e8rV1VW9evVSr169yqxn4cKF6tGjh8aNG6f169erbdu2OnjwoNatW6dmzZpp4cKFN91zaXr16qVHH31UCxYs0J133qnhw4fLMAytXr1aP/74ox577LFy665pt912m2bNmqUnn3xSHTp00IgRI9SgQQOtX79eR48e1dChQ0s989OlSxeFhITo/fffV0FBgUaPHq0GDRpUaJ/Fl9vuv/9+hYaGasCAAWrdurXy8vJ04sQJJSUlqXv37tq4ceNN9da1a1d17dpVq1at0tmzZ9WtWzedOnVK69at0+DBg/Xhhx+W2KYy77UbNXnyZK1evVqLFi3SgQMH1LNnT/34449atWqVhgwZovXr19t8j9Njjz2mM2fOqEePHmrRooUsFou2b9+u3bt3q1u3burRo0eV1QYHU/Of9Adqzm+/76d48fDwMAICAoy+ffsaU6dONb777rtSty3tO3x27txpPPzww8add95peHt7Gx4eHkarVq2MuLg44+TJkzbbFxQUGE8//bTRvHlzw8XFxeY7Vcr6jpXfKu97iLZu3Wq8/fbbRrt27Qw3NzcjICDAePzxx43c3NwS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FxenqKgoBQYGSpJGjRolV1dXxcTE6ODBg/rggw/0+uuva9KkSXbqGgAAOBq7fux+79696tu3r/VxcUgZM2aMli1bpqefflqXLl3S+PHjlZ2drR49emjjxo1yd3e3brNixQrFxcWpf//+cnJy0vDhwzV//nzrei8vL23atEmxsbHq1KmTmjZtqmnTpvGRewAAYGUxDMOwdxGOLjc3V15eXsrJyeF+IsAkQudMt3cJ5fr6f2fau4QaNfZTxz2rv3TgXHuXgDLcyL/fDnsPEQAAQE0hEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANMjEAEAANOrVCDq16+fsrOzS4zn5uaqX79+N1sTAABAjapUINq2bZvy8/NLjF+5ckVffPHFTRcFAABQk1xuZPI333xj/fOhQ4eUnp5ufVxYWKiNGzfqlltuqbrqAAAAasANBaKOHTvKYrHIYrGUemnMw8NDCxYsqLLiAAAAasINXTI7fvy4vv/+exmGod27d+v48ePW5aefflJubq4eeuihKiuusLBQU6dOVcuWLeXh4aHbbrtNL7zwggzDsM4xDEPTpk1TQECAPDw8FBERoWPHjtk8T1ZWlqKjo+Xp6Slvb2/FxMTo4sWLVVYnAACo3W7oDFFwcLAkqaioqFqKudasWbO0cOFCLV++XO3atdPevXs1duxYeXl56bHHHpMkzZ49W/Pnz9fy5cvVsmVLTZ06VZGRkTp06JDc3d0lSdHR0Tp79qwSExNVUFCgsWPHavz48Xr//fdrpA8AAODYbigQ/daxY8e0detWZWZmlghI06ZNu+nCJOnLL7/U0KFDNXjwYElSixYt9K9//Uu7d++W9OvZoXnz5um5557T0KFDJUnvvPOO/Pz8tHbtWkVFRenw4cPauHGj9uzZo86dO0uSFixYoEGDBmnOnDkKDAyskloBAEDtValA9Pbbb2vChAlq2rSp/P39ZbFYrOssFkuVBaLu3btr8eLF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tm1biWNhsViUnp5eMwWXIj4+Xl26dFGjRo3k6+urYcOG6ejRo9fd7t///rdat24td3d3tW/fXp988kkNVFu2yvThaD8bCxcuVIcOHaxfLBceHq5PP/203G0c7ThIN96Hox2H0vz973+XxWLRxIkTy53niMfjtyrShyMejxkzZpSoqXXr1uVuY89jQSCqIR988IEmTZqk6dOna9++fQoNDVVkZKQyMzPL3MbT01Nnz561LidPnqzBiku6dOmSQkNDlZCQUKH5x48f1+DBg9W3b1+lpaVp4sSJ+utf/6rPPvusmist3432Uezo0aM2x8PX17eaKry+pKQkxcbGaufOnUpMTFRBQYHuueceXbp0qcxtvvzySz3wwAOKiYnRV199pWHDhmnYsGE6cOBADVZuqzJ9SI71s3Hrrbfq73//u1JTU7V3717169dPQ4cO1cGDB0ud74jHQbrxPiTHOg7X2rNnj9566y116NCh3HmOejyKVbQPyTGPR7t27Wxq2r59e5lz7X4squbXp+J6unbtasTGxlofFxYWGoGBgUZ8fHyp85cuXWp4eXnVUHU3TpKxZs2acuc8/fTTRrt27WzGRo4caURGRlZjZTemIn1s3brVkGScP3++RmqqjMzMTEOSkZSUVOacESNGGIMHD7YZCwsLMx5++OHqLq/CKtKHo/9sGIZhNG7c2PjHP/5R6rracByKldeHIx+HCxcuGK1atTISExON3r17G48//niZcx35eNxIH454PKZPn26EhoZWeL69jwVniGpAfn6+UlNTFRERYR1zcnJSRESEUlJSytzu4sWLCg4OVlBQ0HX/p+aIUlJSbHqWpMjIyHJ7dmQdO3ZUQECA/ud//kc7duywdzk2cnJyJEk+Pj5lzqkNx6MifUiO+7NRWFiolStX6tKlSwoPDy91Tm04DhXpQ3Lc4xAbG6vBgweXeJ1L48jH40b6kBzzeBw7dkyBgYH63e9+p+joaJ06darMufY+FgSiGnDu3DkVFhaW+DUhfn5+Zd6HEhISoiVLlujjjz/We++9p6KiInXv3l0//vhjTZRcJdLT00vtOTc3V//973/tVNWNCwgI0KJFi7R69WqtXr1aQUFB6tOnj/bt22fv0iT9+ouKJ06cqLvvvlt33nlnmfPKOh72vBfqtyrahyP+bOzfv18NGzaUm5ubHnnkEa1Zs0Zt27Ytda4jH4cb6cMRj4MkrVy5Uvv27VN8fHyF5jvq8bjRPhzxeISFhWnZsmXauHGjFi5cqOPHj6tnz566cOFCqfPtfSxM9as7apPw8HCb/5l1795dbdq00VtvvaUXXnjBjpWZT0hIiEJCQqyPu3fvru+//16vvfaa3n33XTtW9qvY2FgdOHCg3GvztUFF+3DEn42QkBClpaUpJydHH374ocaMGaOkpKQyw4SjupE+HPE4nD59Wo8//rgSExPtfkPxzahMH454PAYOHGj9c4cOHRQWFqbg4GCtWrVKMTExdqmpPASiGtC0aVM5OzsrIyPDZjwjI0P+/v4Veo569erp97//vb777rvqKLFa+Pv7l9qzp6enPDw87FRV1ejatatDBJC4uDht2LBBycnJuvXWW8udW9bxqOh7sDrdSB/XcoSfDVdXV91+++2SpE6dOmnPnj16/fXX9dZbb5WY68jH4Ub6uJYjHIfU1FRlZmbqrrvuso4VFhYqOTlZb7zxhvLy8uTs7GyzjSMej8r0cS1HOB7X8vb21h133FFmTfY+FlwyqwGurq7q1KmTNm/ebB0rKirS5s2by70+/1uFhYXav3+/AgICqqvMKhceHm7TsyQlJiZWuGdHlpaWZtdjYRiG4uLitGbNGm3ZskUtW7a87jaOeDwq08e1HPFno6ioSHl5eaWuc8TjUJby+riWIxyH/v37a//+/UpLS7MunTt3VnR0tNLS0koNEY54PCrTx7Uc4Xhc6+LFi/r+++/LrMnux6JGbt2GsXLlSsPNzc1YtmyZcejQIWP8+PGGt7e3kZ6ebhiGYYwePdp45plnrPNnzpxpfPbZZ8b3339vpKamGlFRUYa7u7tx8OBBe7VgXLhwwfjqq6+Mr776ypBkzJ071/jqq6+MkydPGoZhGM8884wxevRo6/wffvjBqF+/vvHUU08Zhw8fNhISEgxnZ2dj48aN9mrBMIwb7+O1114z1q5daxw7dszYv3+/8fjjjxtOTk7G559/bq8WjAkTJhheXl7Gtm3bjLNnz1qXy5cvW+dc+57asWOH4eLiYsyZM8c4fPiwMX36dKNevXrG/v377dGCYRiV68PRfjaeeeYZIykpyTh+/LjxzTffGM8884xhsViMTZs2lVq/Ix4Hw7jxPhztOJTl2k9n1Zbjca3r9eGIx+PJJ580tm3bZhw/ftzYsWOHERERYTRt2tTIzMw0DMPxjgWBqAYtWLDAaN68ueHq6mp07drV2Llzp3Vd7969jTFjxlgfT5w40TrXz8/PGDRokLFv3z47VP1/ij9+fu1SXPeYMWOM3r17l9imY8eOhqurq/G73/3OWLp0aY3Xfa0b7WPWrFnGbbfdZri7uxs+Pj5Gnz59jC1bttin+P9fafVLsnl9r31PGYZhrFq1yrjjjjsMV1dXo127dsZ//vOfmi38GpXpw9F+Nh566CEjODjYcHV1NZo1a2b079/fGiIMo3YcB8O48T4c7TiU5dogUVuOx7Wu14cjHo+RI0caAQEBhqurq3HLLbcYI0eONL777jvrekc7FhbDMIyaORcFAADgmLiHCAAAmB6BCAAAmB6BCAAAmB6BCAAAmB6BCAAAmB6BCAAAmB6BCAAAmB6BCIDptWjRQvPmzbN3GQDsiEAEwDSWLVsmb2/vEuN79uzR+PHja74gAA6D33YPoE7Iz8+Xq6trpbZt1qxZFVcDoLbhDBGAWqlPnz6Ki4vTxIkT1bRpU0VGRmru3Llq3769GjRooKCgIP3tb3/TxYsXJUnbtm3T2LFjlZOTI4vFIovFohkzZkgqecnMYrHoH//4h+69917Vr19frVq10rp162z2v27dOrVq1Uru7u7q27evli9fLovFouzs7Bp6BQBUJQIRgFpr+fLlcnV11Y4dO7Ro0SI5OTlp/vz5OnjwoJYvX64tW7bo6aefliR1795d8+bNk6enp86ePauzZ8/qf//3f8t87pkzZ2rEiBH65ptvNGjQIEVHRysrK0uSdPz4cd13330aNmyYvv76az388MN69tlna6RnANWDS2YAaq1WrVpp9uzZ1schISHWP7do0UIvvviiHnnkEb355ptydXWVl5eXLBaL/P39r/vcDz74oB544AFJ0ssvv6z58+dr9+7dGjBggN566y2FhITolVdese73wIEDeumll6q4QwA1hUAEoNbq1KmTzePPP/9c8fHxOnLkiHJzc3X16lVduXJFly9fVv369W/ouTt06GD9c4MGDeTp6anMzExJ0tGjR9WlSxeb+V27dq1kFwAcAZfMANRaDRo0sP75xIkT+sMf/qAOHTpo9erVSk1NVUJCgqRfb7i+UfXq1bN5bLFYVFRUdHMFA3BYnCECUCekpqaqqKhIr776qpycfv2/3qpVq2zmuLq6qrCw8Kb3FRISok8++cRmbM+ePTf9vADshzNEAOqE22+/XQUFBVqwYIF++OEHvfvuu1q0aJHNnBYtWujixYvavHmzzp07p8uXL1dqXw8//LCOHDmiyZMn69tvv9WqVau0bNkySb+eSQJQ+xCIANQJoaGhmjt3rmbNmqU777xTK1asUHx8vM2c7t2765FHHtHIkSPVrFkzmxuyb0TLli314Ycf6qOPPlKHDh20cOFC66fM3NzcbroXADXPYhiGYe8iAKC2e+mll7Ro0SKdPn3a3qUAqATuIQKASnjzzTfVpUsXNWnSRDt27NArr7yiuLg4e5cFoJIIRABQCceOHdOLL76orKwsNW/eXE8++aSmTJli77IAVBKXzAAAgOlxUzUAADA9AhEAADA9AhEAADA9AhEAADA9AhEAADA9AhEAADA9AhEAADA9AhEAADA9AhEAADC9/w9PjhpDTshxNgAAAABJRU5ErkJggg==\n",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"°Number of movies that were not rated at all : 78\n"
]
}
],
"source": [
"# All the possible rating values, from smallest value to value highest.\n",
"list_ratings_values = []\n",
"for i in df_ratings.iloc[:, 2]:\n",
" if i not in list_ratings_values :\n",
" list_ratings_values.append(i)\n",
"print(f\"°All the possible rating values, from smallest value to value highest :\", sorted(list_ratings_values))\n",
"\n",
"print(f\"°Number of rated movies per rating value :\")\n",
"for value in list_ratings_values :\n",
" count = 0\n",
" for rating in df_ratings.iloc[:, 2]:\n",
" if rating == value :\n",
" count += 1\n",
" print(str(value) + \" (\" + str(count) + \" movies)\")\n",
" \n",
"sns.countplot(x=\"rating\", data=df_ratings, palette=\"viridis\")\n",
"plt.title(\"Distribution of movie ratings\", fontsize=14)\n",
"plt.show()\n",
"\n",
"#----------------------------------------------\n",
"\n",
"# Number of movies that were not rated at all\n",
"not_rated_movies = n_movies - unique_movies #total movies - rated movies \n",
"print(f\"°Number of movies that were not rated at all :\", not_rated_movies)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 3 - Long-tail property"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1240 75\n",
"858 72\n",
"527 71\n",
"500 61\n",
"1208 60\n",
"590 59\n",
"1073 57\n",
"2987 56\n",
"2011 54\n",
"1225 50\n",
"923 45\n",
"6333 43\n",
"2804 41\n",
"1219 40\n",
"4979 37\n",
"1250 36\n",
"899 34\n",
"2717 33\n",
"784 32\n",
"2006 31\n",
"3504 30\n",
"6537 29\n",
"1345 28\n",
"8528 27\n",
"2724 26\n",
"3101 25\n",
"2881 24\n",
"1172 23\n",
"2087 22\n",
"3 21\n",
"2942 20\n",
"1945 19\n",
"6323 18\n",
"2739 17\n",
"2146 16\n",
"56782 15\n",
"125 14\n",
"1515 13\n",
"3264 12\n",
"4467 11\n",
"109374 10\n",
"3701 9\n",
"2995 8\n",
"1264 7\n",
"1821 6\n",
"1942 5\n",
"8620 4\n",
"4191 3\n",
"2500 2\n",
"2128 1\n",
"Name: movieId, dtype: int64\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAjIAAAG0CAYAAAAozc0BAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAA9hAAAPYQGoP6dpAABZsklEQVR4nO3dd1xT9+I+8CcBEmbC3nuouBWrIk7A4mrdtta6V62KtevW+7ut9t7e2vVt3au2aK12aNVqW2tb3Io4cdRRNihLQbbMnN8fhFwpoIiBk8Dzfr3yavPJyTkPYeTxnM85kQiCIICIiIhID0nFDkBERETUWCwyREREpLdYZIiIiEhvscgQERGR3mKRISIiIr3FIkNERER6i0WGiIiI9BaLDBEREektFhkiIiLSWywyJLojR45AIpFg165dom7/yJEjmrFp06bB09OzWbbv6emJadOmae5v2bIFEokE586da5btDxw4EAMHDmyWbTVWZmYmxo0bBxsbG0gkEqxYsUKUHMuWLYNEIhFl2wCwbds2tGvXDkZGRrC0tBQth65KSkqCRCLBli1bxI5CzYhFhpqERCJp0O3B8qDvrl27hmXLliEpKUnsKLXocraGWLx4MQ4ePIglS5Zg27ZtGDJkSJNtq7i4GMuWLdO5n80bN25g2rRp8PHxweeff45NmzaJHYlIJxiKHYBapm3bttW4/9VXX+H333+vNe7v74/r1683Z7QG+fzzz6FSqR7rOdeuXcO7776LgQMHPtbenJs3b0Iqbdp/Uzws22+//dak29aGQ4cOYeTIkXj99debfFvFxcV49913AaDWnqp//etfeOutt5o8Q12OHDkClUqFlStXwtfXV5QMus7DwwP379+HkZGR2FGoGbHIUJN48cUXa9w/ffo0fv/991rjAHSyyDT1H0JBEFBSUgITExPI5fIm3dajyGQyUbffEFlZWY0+lFJRUQGVSqWVr9PQ0BCGhuL82czKygKAR74OD/5stTYSiQTGxsZix6BmxkNLpDNUKhX++9//wtXVFcbGxggJCUFcXFyt5aKjozFkyBAolUqYmppiwIABOHnyZIO2cevWLYwaNQpmZmawt7fH4sWLUVpaWmu5uubIfPvttwgICICFhQUUCgU6deqElStXAqia1zJ+/HgAwKBBg2odOvP09MSIESNw8OBB9OjRAyYmJti4caPm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\n",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#The distribution of rating frequencies\n",
"\n",
"value_counts1 = df_ratings.iloc[:,1].value_counts() #ratings number of each movie \n",
"#print(value_counts1)\n",
"value_counts2 = value_counts1.drop_duplicates() #handling the possible ties\n",
"print(value_counts2)\n",
"\n",
"plt.plot(value_counts2.values)\n",
"plt.title(\"The distribution of rating frequencies\")\n",
"plt.ylabel(\"Popularity\")\n",
"plt.xlabel(\"Movies\")\n",
"plt.xticks([])\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 4 - Matrix sparsity"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of rated movies : 834\n",
"Number of raters : 107\n",
"Number of cells in the sparsity matrix : 89238\n",
"Total number of ratings : 5296\n",
"The value of the ratings matrix sparsity 0.94\n"
]
}
],
"source": [
"# -- display relevant informations that can be extracted from the dataset"
"#Sparsity\n",
"\n",
"print(f\"Number of rated movies :\", unique_movies) \n",
"\n",
"print(f\"Number of raters :\", unique_users)\n",
"\n",
"nbr_cells = unique_movies*unique_users\n",
"print(f\"Number of cells in the sparsity matrix :\", nbr_cells)\n",
"\n",
"nbr_specified_ratings = n_ratings\n",
"print(f\"Total number of ratings :\", nbr_specified_ratings)\n",
"\n",
"sparsity = 1 - (nbr_specified_ratings/nbr_cells)\n",
"print(f\"The value of the ratings matrix sparsity\", round(sparsity, 2))"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAsAAAAKsCAYAAADiGQvOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAA9hAAAPYQGoP6dpAABjX0lEQVR4nO3de3gV5bn//08AOZRDMAgJWIKIKHgCRMWArVZjkWrVr1irpS2KytYCFdGqbLfisfHw/VbBKuxaN9gqonbjsbtQRcVDIyqilaqASjEqAXeRBFACkvn9wc8FiTnNWs/M8zwz79d15bq2a01m7ucww93sdd8rLwiCQAAAAEBKtLIdAAAAABAnEmAAAACkCgkwAAAAUoUEGAAAAKlCAgwAAIBUIQEGAABAqpAAAwAAIFVIgAEAAJAqJMAAAABIFRJgAAAApAoJ8G7uuusu7bPPPmrfvr2GDRumV1991XZIRr3wwgv64Q9/qF69eikvL0+PPfZYnfeDINA111yjnj17qkOHDiotLdWqVavsBGtIWVmZjjjiCHXu3Fk9evTQaaedphUrVtQ5ZuvWrZowYYK6deumTp06afTo0Vq3bp2liHM3c+ZMHXrooerSpYu6dOmikpIS/eUvf8m8n7TxNuTmm29WXl6eJk+enHktaeO+9tprlZeXV+dnwIABmfeTNt7dffLJJ/rpT3+qbt26qUOHDjrkkEP0+uuvZ95P2rNsn332+cZa5+XlacKECZKSudY7duzQ1Vdfrb59+6pDhw7q16+fbrjhBgVBkDkmaessSZs2bdLkyZPVp08fdejQQcOHD9drr72WeT8JYzaRi2zYsEFjxoxRly5d1LVrV5133nnavHlzuEACBEEQBPPmzQvatm0b/Nd//Vfwj3/8I7jggguCrl27BuvWrbMdmjH/8z//E1x11VXB/PnzA0nBo48+Wuf9m2++OcjPzw8ee+yx4K233gpOOeWUoG/fvsGXX35pJ2ADRo4cGcyePTtYvnx58OabbwY/+MEPguLi4mDz5s2ZYy688MKgd+/ewaJFi4LXX389OOqoo4Lhw4dbjDo3TzzxRPDnP/85WLlyZbBixYrg3//934M99tgjWL58eRAEyRtvfa+++mqwzz77BIceemhw8cUXZ15P2rinTZsWHHTQQcHatWszP5999lnm/aSN92sbNmwI+vTpE5xzzjnBkiVLgg8//DBYuHBh8P7772eOSdqzbP369XXW+emnnw4kBc8991wQBMlc65tuuino1q1b8NRTTwWrV68OHnnkkaBTp07B9OnTM8ckbZ2DIAjOPPPM4MADDwwWL14crFq1Kpg2bVrQpUuX4OOPPw6CIBljNpGLnHjiicGgQYOCV155JXjxxReD/fbbLzj77LNDxUEC/P878sgjgwkTJmT+e8eOHUGvXr2CsrIyi1FFp/6mq62tDYqKioLbbrst89rGjRuDdu3aBQ8++KCFCKOxfv36QFKwePHiIAh2jnGPPfYIHnnkkcwx7777biApKC8vtxWmcXvuuWfw+9//PvHj3bRpU9C/f//g6aefDo455phMApzEcU+bNi0YNGhQg+8lcbxfu+KKK4Kjjz660ffT8Cy7+OKLg379+gW1tbWJXeuTTjopGDduXJ3XTj/99GDMmDFBECRznb/44ougdevWwVNPPVXn9cMOOyy46qqrEjnmbHKRd955J5AUvPbaa5lj/vKXvwR5eXnBJ5980uJr8xEISdu2bdPSpUtVWlqaea1Vq1YqLS1VeXm5xcjis3r1alVWVtaZg/z8fA0bNixRc1BVVSVJKigokCQtXbpU27dvrzPuAQMGqLi4OBHj3rFjh+bNm6ctW7aopKQk8eOdMGGCTjrppDrjk5K7zqtWrVKvXr207777asyYMfroo48kJXe8kvTEE0/o8MMP149+9CP16NFDQ4YM0T333JN5P+nPsm3btun+++/XuHHjlJeXl9i1Hj58uBYtWqSVK1dKkt566y299NJLGjVqlKRkrvNXX32lHTt2qH379nVe79Chg1566aVEjrm+loyxvLxcXbt21eGHH545prS0VK1atdKSJUtafK025sL21//+7/9qx44dKiwsrPN6YWGh3nvvPUtRxauyslKSGpyDr9/zXW1trSZPnqwRI0bo4IMPlrRz3G3btlXXrl3rHOv7uN9++22VlJRo69at6tSpkx599FEdeOCBevPNNxM5XkmaN2+e3njjjTqfl/taEtd52LBhmjNnjg444ACtXbtW1113nb7zne9o+fLliRzv1z788EPNnDlTU6ZM0b//+7/rtdde0y9/+Uu1bdtWY8eOTfyz7LHHHtPGjRt1zjnnSErm3pakK6+8UtXV1RowYIBat26tHTt26KabbtKYMWMkJfPfrM6dO6ukpEQ33HCDBg4cqMLCQj344IMqLy/Xfvvtl8gx19eSMVZWVqpHjx513m/Tpo0KCgpCzQMJMFJjwoQJWr58uV566SXboUTugAMO0Jtvvqmqqir96U9/0tixY7V48WLbYUWmoqJCF198sZ5++ulv/PUkqb7+S5gkHXrooRo2bJj69Omjhx9+WB06dLAYWbRqa2t1+OGH69e//rUkaciQIVq+fLlmzZqlsWPHWo4uevfee69GjRqlXr162Q4lUg8//LAeeOABzZ07VwcddJDefPNNTZ48Wb169Ur0Ov/xj3/UuHHjtPfee6t169Y67LDDdPbZZ2vp0qW2Q0scPgIhaa+99lLr1q2/UTW7bt06FRUVWYoqXl+PM6lzMHHiRD311FN67rnn9O1vfzvzelFRkbZt26aNGzfWOd73cbdt21b77befhg4dqrKyMg0aNEjTp09P7HiXLl2q9evX67DDDlObNm3Upk0bLV68WDNmzFCbNm1UWFiYyHHvrmvXrtp///31/vvvJ3adJalnz5468MAD67w2cODAzMc/kvwsW7NmjZ555hmdf/75mdeSuta/+tWvdOWVV+qss87SIYccop/97Ge65JJLVFZWJim569yvXz8tXrxYmzdvVkVFhV599VVt375d++67b2LHvLuWjLGoqEjr16+v8/5XX32lDRs2hJoHEmDtTBaGDh2qRYsWZV6rra3VokWLVFJSYjGy+PTt21dFRUV15qC6ulpLlizxeg6CINDEiRP16KOP6tlnn1Xfvn3rvD906FDtsccedca9YsUKffTRR16Pu77a2lrV1NQkdrzHH3+83n77bb355puZn8MPP1xjxozJ/N9JHPfuNm/erA8++EA9e/ZM7DpL0ogRI77RynDlypXq06ePpOQ+yyRp9uzZ6tGjh0466aTMa0ld6y+++EKtWtVNUVq3bq3a2lpJyV5nSerYsaN69uypzz//XAsXLtSpp56a+DFLLVvXkpISbdy4sc5fxZ999lnV1tZq2LBhLb9YziV8CTFv3rygXbt2wZw5c4J33nknGD9+fNC1a9egsrLSdmjGbNq0KVi2bFmwbNmyQFLwm9/8Jli2bFmwZs2aIAh2th7p2rVr8Pjjjwd///vfg1NPPdW79ir1XXTRRUF+fn7w/PPP12kj9MUXX2SOufDCC4Pi4uLg2WefDV5//fWgpKQkKCkpsRh1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AnHAAQdo5syZOu6441ReXq7S0lLdfvvteuqpp9SmTRvNnz8/ilgjQxcIAAAAt0SZn7XJ5pcqKiq03377SZIee+wxjR49WuPHj9eIESN07LHHmowPAAAAMCqrj0B06tRJ//rXvyRJf/3rX3XCCSdIktq3b68vv/zSXHQAAACAYVklwCeccILOP/98nX/++Vq5cqV+8IMfSJL+8Y9/aJ999jEZn/N8r4IE4C8Xnz9JrujHLi7Ot4t7w8WYTPF9DFklwHfddZeGDx+uzz77TP/93/+tbt26SZKWLl2qs88+22iArvO9ChKAv1x8/iS5oh+7uDjfLu4NF2MyxfcxhE6Av/rqK82YMUNXXHGFHn/8cZ144omZ96677jpdddVVRgN0ne9VkAD85eLzJ8kV/djFxfl2cW+4GJMpvo8hqy4QnTp10vLlyxPzcQe6QAAAALglyvwsq49AHH/88Vq8eLHRQAAAAIA4ZNUGbdSoUbryyiv19ttva+jQoerYsWOd90855RQjwQEAAACmZfURiFatGv/DcV5ennbs2JFTUHHL5U/s055YrqfeWquTB/XUdaccHFGE8BF7IzfMH3yRxr2axjFnw8V5cjGmxjj3EYja2tpGf3xLfnPlexUkosPeyA3zB1+kca+mcczZcHGeXIzJhqwS4N1t3brVRBze8r0KEtFhb+SG+YMv0rhX0zjmbLg4Ty7GZENWH4HYsWOHfv3rX2vWrFlat26dVq5cqX333VdXX3219tlnH5133nlGgrv22mt13XXX1XntgAMO0HvvvSdpZ/J96aWXat68eaqpqdHIkSN19913q7CwMNR16AIBAADgFuc+AnHTTTdpzpw5uvXWW9W2bdvM6wcffLB+//vfGwtOkg466CCtXbs28/PSSy9l3rvkkkv05JNP6pFHHtHixYv16aef6vTTTzd6fQAAACRLVl0g/vCHP+h3v/udjj/+eF144YWZ1wcNGpT566wpbdq0UVFR0Tder6qq0r333qu5c+fquOOOkyTNnj1bAwcO1CuvvKKjjjrKaBwAAABIhqz+AvzJJ59ov/32+8brtbW12r59e85B7W7VqlXq1auX9t13X40ZM0YfffSRpJ1fu7x9+3aVlpZmjh0wYICKi4tVXl7e5DlrampUXV1d5ydbtr733rdrIB1M7iVb+zKN94OLY3YxJrSMa2vnWjySmzGlTVYJ8IEHHqgXX3zxG6//6U9/0pAhQ3IO6mvDhg3TnDlztGDBAs2cOVOrV6/Wd77zHW3atEmVlZVq27atunbtWud3CgsLVVlZ2eR5y8rKlJ+fn/np3bt31jHa+t57366BdDC5l2ztyzTeDy6O2cWY0DKurZ1r8UhuxpQ2WSXA11xzjSZOnKhbbrlFtbW1mj9/vi644ALddNNNuuaaa4wFN2rUKP3oRz/SoYceqpEjR+p//ud/tHHjRj388MM5nXfq1KmqqqrK/FRUVGR9Llvfe+/bNZAOJveSrX2ZxvvBxTG7GBNaxrW1cy0eyc2Y0iarLhCS9OKLL+r666/XW2+9pc2bN+uwww7TNddco+9///umY6zjiCOOUGlpqU444QQdf/zx+vzzz+v8FbhPnz6aPHmyLrnkkhafky4QAAAAbokyP8uqCE6SvvOd7+jpp582GUuzNm/erA8++EA/+9nPNHToUO2xxx5atGiRRo8eLUlasWKFPvroI5WUlMQaFwAAAPyRVQJcUVGhvLw8ffvb35Ykvfrqq5o7d64OPPBAjR8/3lhwl112mX74wx+qT58++vTTTzVt2jS1bt1aZ599tvLz83XeeedpypQpKigoUJcuXTRp0iSVlJTQAQIAAACNyuozwD/5yU/03HPPSZIqKytVWlqqV199VVdddZWuv/56Y8F9/PHHOvvss3XAAQfozDPPVLdu3fTKK6+oe/fukqTbb79dJ598skaPHq3vfve7Kioq0vz5841dH4D/wlZbU53tJ5vr1ti1w75u+ly+MDUGF+eC54+7skqAly9friOPPFKS9PDDD+uQQw7R3/72Nz3wwAOaM2eOseDmzZunTz/9VDU1Nfr44481b9489evXL/N++/btddddd2nDhg3asmWL5s+f32DPYADpFbbamupsP9lct8auHfZ10+fyhakxuDgXPH/clVUCvH37drVr106S9Mwzz+iUU06RtLMP79q1LBoAd4SttqY62082162xa4d93fS5fGFqDC7OBc8fd2XVBWLYsGH63ve+p5NOOknf//739corr2jQoEF65ZVXdMYZZ+jjjz+OItbI0AUCAADALVHmZ1n9BfiWW27Rf/7nf+qYY47R2WefrUGDBkmSnnjiicxHIwAAAAAXZd0HeMeOHaqurtaee+6Zee2f//ynvvWtb6lHjx7GAowDfwEGAABw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AQAAAMTB2S/CAAAAAKJAAgwAAIBUIQEGAABAqpAAA0AK5OXl6bHHHrMdBgA4gQQYACw455xzlJeXpwsvvPAb702YMEF5eXk655xzjF1v7dq1GjVqlLHzAYDPSIABwJLevXtr3rx5+vLLLzOvbd26VXPnzlVxcbHRaxUVFaWuhRIANIYEGAAsOeyww9S7d2/Nnz8/89r8+fNVXFysIUOGZF6rqanRL3/5S/Xo0UPt27fX0Ucfrddee02SVFtbq29/+9uaOXNmnXMvW7ZMrVq10po1ayR98yMQFRUVOvPMM9W1a1cVFBTo1FNP1T//+c/M+88//7yOPPJIdezYUV27dtWIESMy5wIA35EAA4BF48aN0+zZszP//V//9V8699xz6xxz+eWX67//+79133336Y033tB+++2nkSNHasOGDWrVqpXOPvvsb3wh0AMPPKARI0aoT58+37jm9u3bNXLkSHXu3FkvvviiXn75ZXXq1Eknnniitm3bpq+++kqnnXaajjnmGP39739XeXm5xo8fr7y8vGgmAQBiRgIMABb99Kc/1UsvvaQ1a9ZozZo1evnll/XTn/408/6WLVs0c+ZM3XbbbRo1apQOPPBA3XPPPerQoYPuvfdeSdKYMWP08ssv66OPPpK086/C8+bN05gxYxq85kMPPaTa2lr9/ve/1yGHHKKBAwdq9uzZ+uijj/T888+rurpaVVVVOvnkk9WvXz8NHDhQY8eONf6xDACwhQQYACzq3r27TjrpJM2ZM0ezZ8/WSSedpL322ivz/gcffKDt27drxIgRmdf22GMPHXnkkXr33XclSYMHD9bAgQMzfwVevHix1q9frx/96EcNXvOtt97S+++/r86dO6tTp07q1KmTCgoKtHXrVn3wwQcqKCjQOeeco5EjR+qHP/yhpk+frrVr10Y4CwAQLxJgALBs3LhxmjNnju677z6NGzcuq3OMGTMmkwDPnTtXJ554orp169bgsZs3b9bQoUP15ptv1vlZuXKlfvKTn0iSZs+erfLycg0fPlwPPfSQ9t9/f73yyivZDRAAHEMCDACWff3Z268/m7u7fv36qW3btnr55Zczr23fvl2vvfaaDjzwwMxrP/nJT7R8+XItXbpUf/rTnxr9+IO0s/hu1apV6tGjh/bbb786P/n5+ZnjhgwZoqlTp+pvf/ubDj744G98zhgAfEUCDACWtW7dWu+++67eeecdtW7dus57HTt21EUXXaRf/epXWrBggd555x1dcMEF+uKLL3Teeedljttnn300fPhwnXfeedqxY4dOOeWURq83ZswY7bXXXjr11FP14osvavXq1Xr++ef1y1/+Uh9//LFWr16tqVOnqry8XGvWrNFf//pXrVq1SgMHDoxsDgAgTm1sBwAAkLp06dLoezfffLNqa2v1s5/9TJs2bdLhhx+uhQsXas8996xz3JgxY/SLX/xCP//5z9WhQ4dGz/etb31LL7zwgq644gqdfvrp2rRpk/bee28df/zx6tKli7788ku99957uu+++/Svf/1LPXv21IQJE/Rv//ZvxsYLADblBUEQ2A4CAAAAiAsfgQAAAECqkAADAAAgVUiAAQAAkCokwAAAAEgVEmAAAACkCgkwAAAAUoUEGAAAAKlCAgwAAIBUIQEGAABAqpAAAwAAIFVIgAEAAJAqJMAAAABIlf8PeNWa51pRWwYAAAAASUVORK5CYII=\n",
"text/plain": [
"<Figure size 800x800 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Non-zero values matrix\n",
"\n",
"\"\"\" This first part of the code comes from the following website : https://www.jillcates.com/pydata-workshop/html/tutorial.html \"\"\"\n",
"def create_X(df):\n",
" \"\"\"\n",
" Generates a sparse matrix from ratings dataframe.\n",
"\n",
" Args:\n",
" df: pandas dataframe containing 3 columns (userId, movieId, rating)\n",
"\n",
" Returns:\n",
" X: sparse matrix\n",
" user_mapper: dict that maps user id's to user indices\n",
" user_inv_mapper: dict that maps user indices to user id's\n",
" movie_mapper: dict that maps movie id's to movie indices\n",
" movie_inv_mapper: dict that maps movie indices to movie id's\n",
" \"\"\"\n",
" M = df['userId'].nunique()\n",
" N = df['movieId'].nunique()\n",
"\n",
" user_mapper = dict(zip(np.unique(df[\"userId\"]), list(range(M))))\n",
" movie_mapper = dict(zip(np.unique(df[\"movieId\"]), list(range(N))))\n",
"\n",
" user_inv_mapper = dict(zip(list(range(M)), np.unique(df[\"userId\"])))\n",
" movie_inv_mapper = dict(zip(list(range(N)), np.unique(df[\"movieId\"])))\n",
"\n",
" user_index = [user_mapper[i] for i in df['userId']]\n",
" item_index = [movie_mapper[i] for i in df['movieId']]\n",
"\n",
" X = csr_matrix((df[\"rating\"], (user_index,item_index)), shape=(M,N))\n",
"\n",
" return X, user_mapper, movie_mapper, user_inv_mapper, movie_inv_mapper\n",
"\n",
"X, user_mapper, movie_mapper, user_inv_mapper, movie_inv_mapper = create_X(df_ratings)\n",
"\n",
"\n",
"plt.figure(figsize=(8,8))\n",
"plt.spy(X[:100, :100], markersize=1)\n",
"plt.xlabel('Movies')\n",
"plt.ylabel('Users')\n",
"plt.xticks(range(0, 101, 10))\n",
"plt.yticks(range(0, 101, 10))\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "
mlsmm2156
",
"display_name": "
Python 3
",
"language": "python",
"name": "
mlsmm2156
"
"name": "
python3
"
},
"language_info": {
"codemirror_mode": {
...
...
@@ -98,7 +1201,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.
9.9
"
"version": "3.
7.6
"
}
},
"nbformat": 4,
...
...
%% Cell type:markdown id: tags:
# Analytics Module
The Analytics module provides descriptive statistics on content data, evidence data and model evaluations
%% Cell type:code id: tags:
```
python
# reloads modules automatically before entering the execution of code
%
load_ext
autoreload
%
autoreload
2
# third parties imports
import
numpy
as
np
import
pandas
as
pd
import
random
as
rd
# -- add new imports here --
import
re
import
seaborn
as
sns
import
matplotlib.pyplot
as
plt
from
scipy.sparse
import
csr_matrix
# local imports
from
constants
import
Constant
as
C
from
loaders
import
load_ratings
from
loaders
import
load_items
```
%% Cell type:code id: tags:
```
python
print
(
C
.
ITEM_ID_COL
)
```
%% Output
movieId
%% Cell type:markdown id: tags:
# 1 - Content analytics
Explore and perform descriptive statistics on content data
%% Cell type:code id: tags:
```
python
# -- load the items and display the Dataframe
df_items
=
load_items
()
```
%% Cell type:code id: tags:
```
python
# -- display relevant informations that can be extracted from the dataset
display
(
df_items
)
```
%% Output
%% Cell type:code id: tags:
```
python
# Number of movies in the dataset
n_movies
=
len
(
df_items
)
print
(
f
"
Number of movies :
"
,
n_movies
)
```
%% Output
Number of movies : 912
%% Cell type:code id: tags:
```
python
import
re
#EXTRACT THE DATE OF EACH MOVIE (creation of a list and a dico)
dates
=
[]
dico
=
{}
for
title
in
df_items
.
iloc
[:,
0
]:
match
=
re
.
search
(
r
'
\((\d{4})[\s-]*\d*[\s-]*\d*\)
'
,
title
)
if
match
:
dates
.
append
(
int
(
match
.
group
(
1
)))
dico
[
title
]
=
int
(
match
.
group
(
1
))
else
:
#print(title) #movies with no date
dico
[
title
]
=
"
/
"
#print(dates)
#print(len(dates))
#print(dico)
# TESTS
#print(f"TESTS...")
#for key,value in dico.items():
#if key == "Stranger Things" :
#print(value)
#if key == "Fawlty Towers (1975-1979)" :
#print(value)
#---------------------------------------------------------------
# MINIMUM AND MAXIMUN YEAR
print
(
f
"
\n
MINIMUM AND MAXIMUN YEAR
"
)
min_year
=
min
(
dates
)
max_year
=
max
(
dates
)
print
(
f
"
°The minimum year :
"
,
min_year
)
print
(
f
"
°The maximum year :
"
,
max_year
)
```
%% Output
MINIMUM AND MAXIMUN YEAR
°The minimum year : 1921
°The maximum year : 2016
%% Cell type:code id: tags:
```
python
list_genres
=
[]
for
genres
in
df_items
.
iloc
[:,
1
]:
liste
=
genres
.
split
(
"
|
"
)
#with the focntion split, a new list is created
for
i
in
liste
:
if
i
not
in
list_genres
:
list_genres
.
append
(
i
)
print
(
f
"
°There are
"
,
len
(
list_genres
),
"
different genres of movies.
"
)
print
(
f
"
°Here is the genres list :
"
)
for
g
in
list_genres
:
count
=
0
#some films have several genres
for
row
in
df_items
.
iloc
[:,
1
]:
if
g
in
row
:
count
+=
1
print
(
"
-
"
+
g
+
"
(
"
+
str
(
count
)
+
"
movies)
"
)
```
%% Output
°There are 20 different genres of movies.
°Here is the genres list :
-Comedy (307 movies)
-Romance (141 movies)
-Action (149 movies)
-Adventure (110 movies)
-Children (64 movies)
-Drama (454 movies)
-Mystery (64 movies)
-Thriller (169 movies)
-Crime (110 movies)
-Sci-Fi (73 movies)
-Documentary (50 movies)
-Fantasy (68 movies)
-War (32 movies)
-Horror (91 movies)
-Western (21 movies)
-Animation (45 movies)
-Musical (38 movies)
-Film-Noir (11 movies)
-IMAX (13 movies)
-(no genres listed) (3 movies)
%% Cell type:markdown id: tags:
# 2 - Evidence analytics
Explore and perform descriptive statistics on evidence data
%% Cell type:code id: tags:
```
python
# -- load the items and display the Dataframe
df_ratings
=
load_ratings
()
```
%% Cell type:code id: tags:
```
python
# -- display relevant informations that can be extracted from the dataset
display
(
df_ratings
)
```
%% Output
%% Cell type:code id: tags:
```
python
# Number of ratings in the dataset
n_ratings
=
len
(
df_ratings
)
print
(
f
"
°Total number of ratings :
"
,
n_ratings
)
#----------------------------------------------
# Number of unique users (= number of raters)
unique_users
=
df_ratings
[
'
userId
'
].
nunique
()
print
(
f
"
°Number of unique users/raters :
"
,
unique_users
)
#----------------------------------------------
# Number of unique movies (in the ratings matrix) (= number of rated movies)
unique_movies
=
df_ratings
[
'
movieId
'
].
nunique
()
print
(
f
"
°Number of unique movies :
"
,
unique_movies
)
#----------------------------------------------
print
(
f
"
°Average number of ratings per user:
{
round
(
n_ratings
/
unique_users
,
2
)
}
"
)
print
(
f
"
°Average number of ratings per movie:
{
round
(
n_ratings
/
unique_movies
,
2
)
}
"
)
```
%% Output
°Total number of ratings : 5296
°Number of unique users/raters : 107
°Number of unique movies : 834
°Average number of ratings per user: 49.5
°Average number of ratings per movie: 6.35
%% Cell type:code id: tags:
```
python
# Number of ratings of the most rated movie(s)
value_counts
=
df_ratings
[
'
movieId
'
].
value_counts
()
max_occ
=
value_counts
.
max
()
max_indexes
=
[]
for
index
,
count
in
value_counts
.
iteritems
():
if
count
==
max_occ
:
max_indexes
.
append
(
index
)
print
(
"
The movie(s) with the most ratings is/are :
"
)
for
index
in
max_indexes
:
print
(
f
"
-
"
,
index
,
"
with
"
,
max_occ
,
"
ratings
"
)
# Number of ratings of the less rated movie(s)
min_occ
=
value_counts
.
min
()
min_indexes
=
[]
for
index
,
count
in
value_counts
.
iteritems
():
if
count
==
min_occ
:
min_indexes
.
append
(
index
)
print
(
"
\n
The movie(s) with the less ratings is/are :
"
)
for
index
in
min_indexes
:
print
(
f
"
-
"
,
index
,
"
with
"
,
min_occ
,
"
ratings
"
)
print
(
"
\n
----------------------------------------------------
"
)
# Best and lowest rated movie (based on the average)
mean_ratings
=
df_ratings
.
groupby
(
'
movieId
'
)[[
'
rating
'
]].
mean
()
lowest_rated
=
mean_ratings
[
'
rating
'
].
idxmin
()
print
(
f
"
\n
°The lowest rated movie :
"
)
print
(
df_items
[
df_items
.
index
==
lowest_rated
])
nbr_ratings
=
0
for
id
in
df_ratings
.
iloc
[:,
1
]
:
if
id
==
lowest_rated
:
nbr_ratings
+=
1
print
(
"
With
"
,
nbr_ratings
,
"
rating(s)
"
)
print
(
f
"
\n
°The best rated movie :
"
)
best_rated
=
mean_ratings
[
'
rating
'
].
idxmax
()
print
(
df_items
[
df_items
.
index
==
best_rated
])
nbr_ratings
=
0
for
id
in
df_ratings
.
iloc
[:,
1
]
:
if
id
==
best_rated
:
nbr_ratings
+=
1
print
(
"
With
"
,
nbr_ratings
,
"
rating(s)
"
)
```
%% Output
The movie(s) with the most ratings is/are :
- 1240 with 75 ratings
- 1210 with 75 ratings
The movie(s) with the less ratings is/are :
- 2128 with 1 ratings
- 2481 with 1 ratings
- 70751 with 1 ratings
- 34002 with 1 ratings
- 2674 with 1 ratings
- 66200 with 1 ratings
- 78967 with 1 ratings
- 57418 with 1 ratings
- 34608 with 1 ratings
- 41714 with 1 ratings
- 78111 with 1 ratings
- 74089 with 1 ratings
- 76763 with 1 ratings
- 43635 with 1 ratings
- 60135 with 1 ratings
- 4927 with 1 ratings
- 3302 with 1 ratings
- 4383 with 1 ratings
- 5521 with 1 ratings
- 4831 with 1 ratings
- 87383 with 1 ratings
- 5646 with 1 ratings
- 4716 with 1 ratings
- 6671 with 1 ratings
- 6684 with 1 ratings
- 7300 with 1 ratings
- 4606 with 1 ratings
- 7354 with 1 ratings
- 7881 with 1 ratings
- 7883 with 1 ratings
- 7921 with 1 ratings
- 8003 with 1 ratings
- 2631 with 1 ratings
- 8853 with 1 ratings
- 4082 with 1 ratings
- 64321 with 1 ratings
- 8903 with 1 ratings
- 25842 with 1 ratings
- 26180 with 1 ratings
- 26271 with 1 ratings
- 97168 with 1 ratings
- 26564 with 1 ratings
- 26732 with 1 ratings
- 26731 with 1 ratings
- 26835 with 1 ratings
- 1854 with 1 ratings
- 30712 with 1 ratings
- 4106 with 1 ratings
- 2767 with 1 ratings
- 96565 with 1 ratings
- 96530 with 1 ratings
- 124859 with 1 ratings
- 118326 with 1 ratings
- 116887 with 1 ratings
- 109042 with 1 ratings
- 106542 with 1 ratings
- 103543 with 1 ratings
- 103502 with 1 ratings
- 99992 with 1 ratings
- 98279 with 1 ratings
- 95508 with 1 ratings
- 2032 with 1 ratings
- 92198 with 1 ratings
- 92048 with 1 ratings
- 89427 with 1 ratings
- 86028 with 1 ratings
- 80590 with 1 ratings
- 43558 with 1 ratings
- 7895 with 1 ratings
- 7245 with 1 ratings
- 4662 with 1 ratings
- 127319 with 1 ratings
- 132157 with 1 ratings
- 135536 with 1 ratings
- 136654 with 1 ratings
- 140739 with 1 ratings
- 127728 with 1 ratings
- 108795 with 1 ratings
- 99615 with 1 ratings
- 99609 with 1 ratings
- 98933 with 1 ratings
- 60086 with 1 ratings
- 8453 with 1 ratings
- 7260 with 1 ratings
- 6033 with 1 ratings
- 1903 with 1 ratings
- 57845 with 1 ratings
- 8273 with 1 ratings
- 6679 with 1 ratings
- 6022 with 1 ratings
- 160440 with 1 ratings
- 143255 with 1 ratings
- 142997 with 1 ratings
- 139915 with 1 ratings
- 59 with 1 ratings
- 1654 with 1 ratings
- 96849 with 1 ratings
- 127114 with 1 ratings
- 304 with 1 ratings
- 219 with 1 ratings
- 160656 with 1 ratings
- 151307 with 1 ratings
- 147845 with 1 ratings
- 142258 with 1 ratings
- 140247 with 1 ratings
- 136018 with 1 ratings
- 128606 with 1 ratings
- 118898 with 1 ratings
- 1044 with 1 ratings
- 109205 with 1 ratings
- 108076 with 1 ratings
- 105355 with 1 ratings
- 104339 with 1 ratings
- 104321 with 1 ratings
- 103444 with 1 ratings
- 98611 with 1 ratings
- 98473 with 1 ratings
- 97817 with 1 ratings
- 697 with 1 ratings
- 775 with 1 ratings
- 980 with 1 ratings
- 1349 with 1 ratings
- 5223 with 1 ratings
- 46772 with 1 ratings
- 27441 with 1 ratings
- 5182 with 1 ratings
- 4401 with 1 ratings
- 4319 with 1 ratings
- 3487 with 1 ratings
- 1928 with 1 ratings
- 4003 with 1 ratings
- 3960 with 1 ratings
- 3939 with 1 ratings
- 3662 with 1 ratings
- 3661 with 1 ratings
- 3041 with 1 ratings
- 2855 with 1 ratings
- 2091 with 1 ratings
- 1998 with 1 ratings
- 1550 with 1 ratings
- 1369 with 1 ratings
- 889 with 1 ratings
- 73344 with 1 ratings
- 112070 with 1 ratings
- 106870 with 1 ratings
- 5038 with 1 ratings
- 4608 with 1 ratings
- 5122 with 1 ratings
- 89300 with 1 ratings
- 85316 with 1 ratings
- 152173 with 1 ratings
- 114766 with 1 ratings
- 96815 with 1 ratings
- 6414 with 1 ratings
- 90357 with 1 ratings
- 88272 with 1 ratings
- 48972 with 1 ratings
- 40833 with 1 ratings
- 6940 with 1 ratings
- 5696 with 1 ratings
- 2817 with 1 ratings
- 5841 with 1 ratings
- 1624 with 1 ratings
- 8871 with 1 ratings
- 8019 with 1 ratings
- 966 with 1 ratings
- 9005 with 1 ratings
- 8745 with 1 ratings
- 8057 with 1 ratings
- 6356 with 1 ratings
- 408 with 1 ratings
- 3531 with 1 ratings
- 54785 with 1 ratings
- 6665 with 1 ratings
- 32797 with 1 ratings
- 32892 with 1 ratings
- 6114 with 1 ratings
- 4863 with 1 ratings
- 4500 with 1 ratings
- 44613 with 1 ratings
- 1181 with 1 ratings
- 5773 with 1 ratings
- 6103 with 1 ratings
- 8253 with 1 ratings
- 69746 with 1 ratings
- 63826 with 1 ratings
- 136592 with 1 ratings
- 36289 with 1 ratings
- 106441 with 1 ratings
- 68959 with 1 ratings
- 83506 with 1 ratings
- 69685 with 1 ratings
- 33558 with 1 ratings
- 56069 with 1 ratings
- 8790 with 1 ratings
- 98829 with 1 ratings
- 26422 with 1 ratings
- 118082 with 1 ratings
- 1750 with 1 ratings
- 5343 with 1 ratings
- 1564 with 1 ratings
- 31422 with 1 ratings
- 25962 with 1 ratings
- 6559 with 1 ratings
- 2835 with 1 ratings
- 56095 with 1 ratings
- 70159 with 1 ratings
- 2552 with 1 ratings
- 72104 with 1 ratings
- 95858 with 1 ratings
- 6920 with 1 ratings
- 5440 with 1 ratings
- 3657 with 1 ratings
- 2173 with 1 ratings
- 3240 with 1 ratings
- 92681 with 1 ratings
- 80615 with 1 ratings
- 1826 with 1 ratings
- 57951 with 1 ratings
- 51698 with 1 ratings
- 56336 with 1 ratings
- 27899 with 1 ratings
- 72683 with 1 ratings
- 126106 with 1 ratings
- 8840 with 1 ratings
- 7407 with 1 ratings
- 6739 with 1 ratings
- 6579 with 1 ratings
- 2210 with 1 ratings
- 8722 with 1 ratings
- 145307 with 1 ratings
- 120392 with 1 ratings
- 47714 with 1 ratings
- 110110 with 1 ratings
- 6203 with 1 ratings
- 6794 with 1 ratings
- 4371 with 1 ratings
- 4077 with 1 ratings
- 3799 with 1 ratings
- 1896 with 1 ratings
- 25839 with 1 ratings
- 49394 with 1 ratings
- 34129 with 1 ratings
- 133195 with 1 ratings
- 57038 with 1 ratings
- 56869 with 1 ratings
- 49299 with 1 ratings
- 48165 with 1 ratings
- 5560 with 1 ratings
- 5568 with 1 ratings
- 65088 with 1 ratings
- 112911 with 1 ratings
- 71525 with 1 ratings
- 51939 with 1 ratings
- 33646 with 1 ratings
- 5092 with 1 ratings
- 2824 with 1 ratings
- 27075 with 1 ratings
- 80599 with 1 ratings
- 74486 with 1 ratings
- 460 with 1 ratings
- 26915 with 1 ratings
- 2839 with 1 ratings
- 78316 with 1 ratings
- 5054 with 1 ratings
- 3737 with 1 ratings
- 939 with 1 ratings
- 54787 with 1 ratings
- 53999 with 1 ratings
- 6800 with 1 ratings
- 4200 with 1 ratings
- 6757 with 1 ratings
- 82167 with 1 ratings
- 27815 with 1 ratings
- 7316 with 1 ratings
- 98369 with 1 ratings
- 93498 with 1 ratings
- 89047 with 1 ratings
- 84160 with 1 ratings
- 54281 with 1 ratings
- 4688 with 1 ratings
- 130490 with 1 ratings
- 4350 with 1 ratings
- 25901 with 1 ratings
- 74156 with 1 ratings
- 72479 with 1 ratings
- 71490 with 1 ratings
- 94939 with 1 ratings
- 8142 with 1 ratings
- 5826 with 1 ratings
- 100517 with 1 ratings
- 104726 with 1 ratings
- 6211 with 1 ratings
- 5796 with 1 ratings
- 148652 with 1 ratings
- 112767 with 1 ratings
- 54910 with 1 ratings
- 27857 with 1 ratings
- 26025 with 1 ratings
- 140755 with 1 ratings
----------------------------------------------------
°The lowest rated movie :
title genres
movieId
5521 Principal, The (1987) Action|Crime|Drama
With 1 rating(s)
°The best rated movie :
title genres
movieId
1564 For Roseanna (Roseanna's Grave) (1997) Comedy|Drama|Romance
With 1 rating(s)
%% Cell type:code id: tags:
```
python
# All the possible rating values, from smallest value to value highest.
list_ratings_values
=
[]
for
i
in
df_ratings
.
iloc
[:,
2
]:
if
i
not
in
list_ratings_values
:
list_ratings_values
.
append
(
i
)
print
(
f
"
°All the possible rating values, from smallest value to value highest :
"
,
sorted
(
list_ratings_values
))
print
(
f
"
°Number of rated movies per rating value :
"
)
for
value
in
list_ratings_values
:
count
=
0
for
rating
in
df_ratings
.
iloc
[:,
2
]:
if
rating
==
value
:
count
+=
1
print
(
str
(
value
)
+
"
(
"
+
str
(
count
)
+
"
movies)
"
)
sns
.
countplot
(
x
=
"
rating
"
,
data
=
df_ratings
,
palette
=
"
viridis
"
)
plt
.
title
(
"
Distribution of movie ratings
"
,
fontsize
=
14
)
plt
.
show
()
#----------------------------------------------
# Number of movies that were not rated at all
not_rated_movies
=
n_movies
-
unique_movies
#total movies - rated movies
print
(
f
"
°Number of movies that were not rated at all :
"
,
not_rated_movies
)
```
%% Output
°All the possible rating values, from smallest value to value highest : [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
°Number of rated movies per rating value :
3.0 (1049 movies)
1.5 (109 movies)
4.0 (1458 movies)
3.5 (633 movies)
0.5 (64 movies)
1.0 (223 movies)
2.0 (469 movies)
2.5 (289 movies)
5.0 (650 movies)
4.5 (352 movies)
°Number of movies that were not rated at all : 78
%% Cell type:markdown id: tags:
# 3 - Long-tail property
%% Cell type:code id: tags:
```
python
#The distribution of rating frequencies
value_counts1
=
df_ratings
.
iloc
[:,
1
].
value_counts
()
#ratings number of each movie
#print(value_counts1)
value_counts2
=
value_counts1
.
drop_duplicates
()
#handling the possible ties
print
(
value_counts2
)
plt
.
plot
(
value_counts2
.
values
)
plt
.
title
(
"
The distribution of rating frequencies
"
)
plt
.
ylabel
(
"
Popularity
"
)
plt
.
xlabel
(
"
Movies
"
)
plt
.
xticks
([])
plt
.
show
()
```
%% Output
1240 75
858 72
527 71
500 61
1208 60
590 59
1073 57
2987 56
2011 54
1225 50
923 45
6333 43
2804 41
1219 40
4979 37
1250 36
899 34
2717 33
784 32
2006 31
3504 30
6537 29
1345 28
8528 27
2724 26
3101 25
2881 24
1172 23
2087 22
3 21
2942 20
1945 19
6323 18
2739 17
2146 16
56782 15
125 14
1515 13
3264 12
4467 11
109374 10
3701 9
2995 8
1264 7
1821 6
1942 5
8620 4
4191 3
2500 2
2128 1
Name: movieId, dtype: int64
%% Cell type:markdown id: tags:
# 4 - Matrix sparsity
%% Cell type:code id: tags:
```
python
#Sparsity
print
(
f
"
Number of rated movies :
"
,
unique_movies
)
print
(
f
"
Number of raters :
"
,
unique_users
)
nbr_cells
=
unique_movies
*
unique_users
print
(
f
"
Number of cells in the sparsity matrix :
"
,
nbr_cells
)
nbr_specified_ratings
=
n_ratings
print
(
f
"
Total number of ratings :
"
,
nbr_specified_ratings
)
sparsity
=
1
-
(
nbr_specified_ratings
/
nbr_cells
)
print
(
f
"
The value of the ratings matrix sparsity
"
,
round
(
sparsity
,
2
))
```
%% Output
Number of rated movies : 834
Number of raters : 107
Number of cells in the sparsity matrix : 89238
Total number of ratings : 5296
The value of the ratings matrix sparsity 0.94
%% Cell type:code id: tags:
```
python
# Non-zero values matrix
"""
This first part of the code comes from the following website : https://www.jillcates.com/pydata-workshop/html/tutorial.html
"""
def
create_X
(
df
):
"""
Generates a sparse matrix from ratings dataframe.
Args:
df: pandas dataframe containing 3 columns (userId, movieId, rating)
Returns:
X: sparse matrix
user_mapper: dict that maps user id
'
s to user indices
user_inv_mapper: dict that maps user indices to user id
'
s
movie_mapper: dict that maps movie id
'
s to movie indices
movie_inv_mapper: dict that maps movie indices to movie id
'
s
"""
M
=
df
[
'
userId
'
].
nunique
()
N
=
df
[
'
movieId
'
].
nunique
()
user_mapper
=
dict
(
zip
(
np
.
unique
(
df
[
"
userId
"
]),
list
(
range
(
M
))))
movie_mapper
=
dict
(
zip
(
np
.
unique
(
df
[
"
movieId
"
]),
list
(
range
(
N
))))
user_inv_mapper
=
dict
(
zip
(
list
(
range
(
M
)),
np
.
unique
(
df
[
"
userId
"
])))
movie_inv_mapper
=
dict
(
zip
(
list
(
range
(
N
)),
np
.
unique
(
df
[
"
movieId
"
])))
user_index
=
[
user_mapper
[
i
]
for
i
in
df
[
'
userId
'
]]
item_index
=
[
movie_mapper
[
i
]
for
i
in
df
[
'
movieId
'
]]
X
=
csr_matrix
((
df
[
"
rating
"
],
(
user_index
,
item_index
)),
shape
=
(
M
,
N
))
return
X
,
user_mapper
,
movie_mapper
,
user_inv_mapper
,
movie_inv_mapper
X
,
user_mapper
,
movie_mapper
,
user_inv_mapper
,
movie_inv_mapper
=
create_X
(
df_ratings
)
plt
.
figure
(
figsize
=
(
8
,
8
))
plt
.
spy
(
X
[:
100
,
:
100
],
markersize
=
1
)
plt
.
xlabel
(
'
Movies
'
)
plt
.
ylabel
(
'
Users
'
)
plt
.
xticks
(
range
(
0
,
101
,
10
))
plt
.
yticks
(
range
(
0
,
101
,
10
))
plt
.
show
()
```
%% Output
%% Cell type:code id: tags:
```
python
```
...
...
Ce diff est replié.
Cliquez pour l'agrandir.
Aperçu
0%
Chargement en cours
Veuillez réessayer
ou
joindre un nouveau fichier
.
Annuler
You are about to add
0
people
to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Enregistrer le commentaire
Annuler
Veuillez vous
inscrire
ou vous
se connecter
pour commenter