{ "cells": [ { "cell_type": "code", "execution_count": 1, "source": [ "# This Notebook is created with VS Code on Windows\r\n", "# Create python virtual environment\r\n", "!python -m venv .venv\r\n", "# If you want to use it on macOS/Linux\r\n", "# You may need to run sudo apt-get install python3-venv first\r\n", "#python3 -m venv .venv\r\n", "\r\n", "# Install Python Packages\r\n", "!pip install --user --upgrade pip\r\n", "!pip install --upgrade setuptools\r\n", "!pip install --user seaborn\r\n", "!pip install --user numpy\r\n", "!pip install --user pandas\r\n", "!pip install --user matplotlib\r\n", "!pip install --user plotly\r\n", "!pip install --user nbformat\r\n", "!pip install --user surprise\r\n" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Requirement already satisfied: pip in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (21.2.4)\n", "Requirement already satisfied: setuptools in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (57.4.0)\n", "Requirement already satisfied: seaborn in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (0.11.2)\n", "Requirement already satisfied: matplotlib>=2.2 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from seaborn) (3.3.4)\n", "Requirement already satisfied: numpy>=1.15 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from seaborn) (1.19.5)\n", "Requirement already satisfied: scipy>=1.0 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from seaborn) (1.6.1)\n", "Requirement already satisfied: pandas>=0.23 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from seaborn) (1.2.2)\n", "Requirement already satisfied: kiwisolver>=1.0.1 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib>=2.2->seaborn) (1.3.1)\n", "Requirement already satisfied: cycler>=0.10 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib>=2.2->seaborn) (0.10.0)\n", "Requirement already satisfied: python-dateutil>=2.1 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from matplotlib>=2.2->seaborn) (2.8.1)\n", "Requirement already satisfied: pillow>=6.2.0 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib>=2.2->seaborn) (8.1.0)\n", "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib>=2.2->seaborn) (2.4.7)\n", "Requirement already satisfied: six in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from cycler>=0.10->matplotlib>=2.2->seaborn) (1.15.0)\n", "Requirement already satisfied: pytz>=2017.3 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from pandas>=0.23->seaborn) (2021.1)\n", "Requirement already satisfied: numpy in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (1.19.5)\n", "Requirement already satisfied: pandas in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (1.2.2)\n", "Requirement already satisfied: pytz>=2017.3 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from pandas) (2021.1)\n", "Requirement already satisfied: python-dateutil>=2.7.3 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from pandas) (2.8.1)\n", "Requirement already satisfied: numpy>=1.16.5 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from pandas) (1.19.5)\n", "Requirement already satisfied: six>=1.5 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from python-dateutil>=2.7.3->pandas) (1.15.0)\n", "Requirement already satisfied: matplotlib in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (3.3.4)\n", "Requirement already satisfied: pillow>=6.2.0 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib) (8.1.0)\n", "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib) (2.4.7)\n", "Requirement already satisfied: python-dateutil>=2.1 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from matplotlib) (2.8.1)\n", "Requirement already satisfied: cycler>=0.10 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib) (0.10.0)\n", "Requirement already satisfied: kiwisolver>=1.0.1 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib) (1.3.1)\n", "Requirement already satisfied: numpy>=1.15 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from matplotlib) (1.19.5)\n", "Requirement already satisfied: six in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from cycler>=0.10->matplotlib) (1.15.0)\n", "Requirement already satisfied: plotly in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (5.3.0)\n", "Requirement already satisfied: tenacity>=6.2.0 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from plotly) (8.0.1)\n", "Requirement already satisfied: six in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from plotly) (1.15.0)\n", "Requirement already satisfied: nbformat in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (5.1.3)\n", "Requirement already satisfied: jupyter-core in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from nbformat) (4.7.1)\n", "Requirement already satisfied: jsonschema!=2.5.0,>=2.4 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from nbformat) (3.2.0)\n", "Requirement already satisfied: ipython-genutils in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from nbformat) (0.2.0)\n", "Requirement already satisfied: traitlets>=4.1 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from nbformat) (5.0.5)\n", "Requirement already satisfied: six>=1.11.0 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from jsonschema!=2.5.0,>=2.4->nbformat) (1.15.0)\n", "Requirement already satisfied: attrs>=17.4.0 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from jsonschema!=2.5.0,>=2.4->nbformat) (21.2.0)\n", "Requirement already satisfied: pyrsistent>=0.14.0 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from jsonschema!=2.5.0,>=2.4->nbformat) (0.18.0)\n", "Requirement already satisfied: setuptools in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from jsonschema!=2.5.0,>=2.4->nbformat) (57.4.0)\n", "Requirement already satisfied: pywin32>=1.0 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from jupyter-core->nbformat) (300)\n", "Collecting surprise\n", " Using cached surprise-0.1-py2.py3-none-any.whl (1.8 kB)\n", "Collecting scikit-surprise\n", " Using cached scikit-surprise-1.1.1.tar.gz (11.8 MB)\n", "Requirement already satisfied: joblib>=0.11 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from scikit-surprise->surprise) (1.0.1)\n", "Requirement already satisfied: numpy>=1.11.2 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from scikit-surprise->surprise) (1.19.5)\n", "Requirement already satisfied: scipy>=1.0.0 in c:\\users\\oli\\appdata\\local\\programs\\python\\python38\\lib\\site-packages (from scikit-surprise->surprise) (1.6.1)\n", "Requirement already satisfied: six>=1.10.0 in c:\\users\\oli\\appdata\\roaming\\python\\python38\\site-packages (from scikit-surprise->surprise) (1.15.0)\n", "Building wheels for collected packages: scikit-surprise\n", " Building wheel for scikit-surprise (setup.py): started\n", " Building wheel for scikit-surprise (setup.py): finished with status 'done'\n", " Created wheel for scikit-surprise: filename=scikit_surprise-1.1.1-cp38-cp38-win_amd64.whl size=734505 sha256=072c5d6f9bb826bb28bec68bdb20bf4de7eec14a769df95110fef9cd4def197d\n", " Stored in directory: c:\\users\\oli\\appdata\\local\\pip\\cache\\wheels\\20\\91\\57\\2965d4cff1b8ac7ed1b6fa25741882af3974b54a31759e10b6\n", "Successfully built scikit-surprise\n", "Installing collected packages: scikit-surprise, surprise\n", "Successfully installed scikit-surprise-1.1.1 surprise-0.1\n" ] }, { "output_type": "stream", "name": "stderr", "text": [ " WARNING: The script surprise.exe is installed in 'C:\\Users\\Oli\\AppData\\Roaming\\Python\\Python38\\Scripts' which is not on PATH.\n", " Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\n" ] } ], "metadata": {} }, { "cell_type": "code", "execution_count": 2, "source": [ "import numpy as np # maths\r\n", "import pandas as pd # data processing\r\n", "import matplotlib.pyplot as plt\r\n", "import seaborn as sns\r\n", "import os\r\n", "import re\r\n", "\r\n", "from plotly.offline import init_notebook_mode, iplot\r\n", "import plotly.graph_objs as go\r\n", "import plotly.offline as py\r\n", "py.init_notebook_mode(connected=True)\r\n", "\r\n", "import warnings\r\n", "warnings.filterwarnings('ignore')\r\n", "\r\n", "plt.style.use('fivethirtyeight')\r\n", "plt.rcParams['figure.figsize'] = [18, 8]" ], "outputs": [ { "output_type": "display_data", "data": { "text/html": [ " \n", " " ] }, "metadata": {} } ], "metadata": {} }, { "cell_type": "code", "execution_count": 3, "source": [ "# Import Tables\r\n", "reviews = pd.read_csv('./ml-1m/ratings.dat', names=['userId', 'movieId', 'rating', 'timestamp'], delimiter='::', engine='python')\r\n", "movies = pd.read_csv('./ml-1m/movies.dat', names=['movieId', 'title', 'genres'], delimiter='::', engine='python')\r\n", "users = pd.read_csv('./ml-1m/users.dat', names=['userId', 'gender', 'age', 'occupation', 'zip'], delimiter='::', engine='python')\r\n", "\r\n", "# Print Table shape\r\n", "print('Reviews shape:', reviews.shape)\r\n", "print('Users shape:', users.shape)\r\n", "print('Movies shape:', movies.shape)" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Reviews shape: (1000209, 4)\n", "Users shape: (6040, 5)\n", "Movies shape: (3883, 3)\n" ] } ], "metadata": {} }, { "cell_type": "code", "execution_count": 4, "source": [ "# Drop unused Attributes\r\n", "reviews.drop(['timestamp'], axis=1, inplace=True) # Time\r\n", "users.drop(['zip'], axis=1, inplace=True) # Zip-Code\r\n", "\r\n", "# Extract the movie year from title to extra attrbute\r\n", "movies['release_year'] = movies['title'].str.extract(r'(?:\\((\\d{4})\\))?\\s*$', expand=False)" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 5, "source": [ "# Print movie table\r\n", "movies.head()" ], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " movieId title genres \\\n", "0 1 Toy Story (1995) Animation|Children's|Comedy \n", "1 2 Jumanji (1995) Adventure|Children's|Fantasy \n", "2 3 Grumpier Old Men (1995) Comedy|Romance \n", "3 4 Waiting to Exhale (1995) Comedy|Drama \n", "4 5 Father of the Bride Part II (1995) Comedy \n", "\n", " release_year \n", "0 1995 \n", "1 1995 \n", "2 1995 \n", "3 1995 \n", "4 1995 " ], "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
movieIdtitlegenresrelease_year
01Toy Story (1995)Animation|Children's|Comedy1995
12Jumanji (1995)Adventure|Children's|Fantasy1995
23Grumpier Old Men (1995)Comedy|Romance1995
34Waiting to Exhale (1995)Comedy|Drama1995
45Father of the Bride Part II (1995)Comedy1995
\n", "
" ] }, "metadata": {}, "execution_count": 5 } ], "metadata": {} }, { "cell_type": "code", "execution_count": 6, "source": [ "# Changed feature values based on README_users.txt\r\n", "ages_map = {1: 'Under 18',\r\n", " 18: '18 - 24',\r\n", " 25: '25 - 34',\r\n", " 35: '35 - 44',\r\n", " 45: '45 - 49',\r\n", " 50: '50 - 55',\r\n", " 56: '56+'}\r\n", "\r\n", "occupations_map = {0: 'Not specified',\r\n", " 1: 'Academic / Educator',\r\n", " 2: 'Artist',\r\n", " 3: 'Clerical / Admin',\r\n", " 4: 'College / Grad Student',\r\n", " 5: 'Customer Service',\r\n", " 6: 'Doctor / Health Care',\r\n", " 7: 'Executive / Managerial',\r\n", " 8: 'Farmer',\r\n", " 9: 'Homemaker',\r\n", " 10: 'K-12 student',\r\n", " 11: 'Lawyer',\r\n", " 12: 'Programmer',\r\n", " 13: 'Retired',\r\n", " 14: 'Sales / Marketing',\r\n", " 15: 'Scientist',\r\n", " 16: 'Self-Employed',\r\n", " 17: 'Technician / Engineer',\r\n", " 18: 'Tradesman / Craftsman',\r\n", " 19: 'Unemployed',\r\n", " 20: 'Writer'}\r\n", "\r\n", "gender_map = {'M': 'Male', 'F': 'Female'}\r\n", "\r\n", "users['age'] = users['age'].map(ages_map)\r\n", "users['occupation'] = users['occupation'].map(occupations_map)\r\n", "users['gender'] = users['gender'].map(gender_map)" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 7, "source": [ "# Plot age kategories\r\n", "\r\n", "age_reindex = ['Under 18', '18 - 24', '25 - 34', '35 - 44', '45 - 49', '50 - 55', '56+']\r\n", "age_counts = users['age'].value_counts().reindex(age_reindex)\r\n", "sns.barplot(x=age_counts.values,\r\n", " y=age_counts.index,\r\n", " palette='magma').set_title(\r\n", " 'Users age', fontsize=12)\r\n", "\r\n", "plt.show()" ], "outputs": [ { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/svg+xml": "\r\n\r\n\r\n\r\n \r\n \r\n \r\n \r\n 2021-08-31T13:50:41.214400\r\n image/svg+xml\r\n \r\n \r\n Matplotlib v3.3.4, https://matplotlib.org/\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n", "image/png": "iVBORw0KGgoAAAANSUhEUgAABLkAAAICCAYAAAAj5h9BAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAA7p0lEQVR4nO3de5hVdd03/vcwo0KgDiKOnAkYQAFFQSV79PGAGKmQecBD2mMmd6gPeecBPOWxkDykpmKPilqpUai3h0wrj6SYlsfUmxvFAyKijkIBIsrM749+7asJBJQZNktfr+va1zVrfb97fT9rvmvP3vs9a69dMX/+/IYAAAAAQIG1KHcBAAAAALCmhFwAAAAAFJ6QCwAAAIDCE3IBAAAAUHhCLgAAAAAKT8gFAAAAQOEJuQAAAAAoPCEXAECZVFdXZ9asWY3WTZgwIaNHjy5TRQAAxSXkAgD4jPvoo4/KXQIAQLMTcgEArKPq6uoyatSodO3aNd27d8/w4cNTX1+fJJk7d24OO+yw9OzZM1tttVWuvPLK0v0mTJiQww8/PKNHj06XLl1y44035i9/+Ut22WWXdOnSJbW1tTnllFNWOOb8+fMzatSo9OzZM926dcuoUaMyZ86cUvsrr7yS4cOHp3Pnzhk5cmROOOGERmeePf744xk2bFi6du2aL3/5y5k2bVoz/XYAABoTcgEArKMuu+yydOzYMS+99FJmzpyZ008/PRUVFamvr89BBx2U/v3754UXXsjtt9+eSZMm5d577y3d96677sqIESPy6quv5oADDsj48ePzne98J7Nnz86TTz6Zfffdd4Vj1tfX55BDDsmzzz6bv/71r2nZsmVOPPHEUvtRRx2VQYMGZdasWRk/fnymTJlSanvjjTdy4IEH5oQTTsgrr7ySc889N4cffnjeeeed5vslAQD8/4RcAADrqKqqqrz55puZPXt21ltvvey4446pqKjIE088kbq6uowbNy7rr79+unfvnm9+85u5+eabS/fdbrvtsvfee6dFixZp1apVqqqqMmvWrNTV1aVNmzbZbrvtVjjmJptskpEjR+YLX/hCNtxwwxx//PF5+OGHkySzZ8/OE088kVNOOSXrr79+vvSlL2X48OGl+/7qV7/KHnvskWHDhqVFixbZdddds8022+R3v/td8/6iAAAi5AIAKJvKysp8+OGHjdZ99NFHqaqqSpKMHTs2PXr0yL777putt946P/7xj5P8I2yaO3duunbtWrpddNFFefvtt0vb6dy5c6PtXnbZZXnppZey3XbbZdddd83dd9+9wpoWL16c4447Lv3790+XLl2y1157ZcGCBVm2bFnefPPNtG3bNl/4whdK/Tt16lT6efbs2bntttsa1fXoo49m3rx5a/aLAgBYDVXlLgAA4POqc+fOee2119KnT5/SuldffTU9e/ZMkmy44Yb5wQ9+kB/84Ad5/vnnM2LEiGy77bbp1KlTunXrlieeeOJjt11RUdFouWfPnrnmmmtSX1+fO+64I9/85jcza9astG7dulG/yy67LDNnzsy9996bmpqaPPPMM9l5553T0NCQmpqavPfee1m8eHEp6PrX63V16tQpo0aNyqWXXrrGvxsAgE/KmVwAAGXy9a9/PRdccEHmzJmT+vr6PPDAA7n77rszcuTIJMndd9+dWbNmpaGhIRtttFEqKytTUVGRQYMGpU2bNrn44ovz/vvvZ9myZXn++edXGnpNmTIl77zzTlq0aJGNN944SdKixfIvBRcuXJhWrVpl4403znvvvZeJEyeW2rp27Zptttkm5513XpYuXZrHHnus0RlhBx54YO6+++7ce++9WbZsWZYsWZJp06Y1CsIAAJqLkAsAoExOOumkbL/99vnKV76S7t275/vf/37+3//7f9lyyy2TJC+99FJGjhyZTp06ZdiwYTnyyCOz8847p7KyMlOmTMmzzz6brbfeOj169MjYsWPzt7/97WPHuvfeezNkyJB06tQp48ePzzXXXJNWrVot12/MmDF5//3307NnzwwdOjRDhw5t1H7VVVfl8ccfT48ePXLuuedm3333zfrrr5/kH2em3XjjjbnwwgvTs2fP9OvXLz/5yU9K3wgJANCcKubPn99Q7iIAACimI444IrW1tTnllFPKXQoA8DnnTC4AAFbbE088kZdffjn19fX5wx/+kLvuuit77bVXucsCAHDheQAAVt+8efNy2GGH5d13303Hjh1z4YUXZuutty53WQAAPq4IAAAAQPH5uCIAAAAAhSfkAgAAAKDwhFwAAAAAFJ6QCwAAAIDCE3LBapo5c2a5S6CMzP/nm/nHMfD5Zv4/38z/55v5/3wz/8Uj5AIAAACg8IRcAAAAABSekAsAAACAwqsqdwFFtE2fr5W7BNbAkzP+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oPCEXAAAAAAUnpALAAAAgMITcgEAAABQeEIuAAAAAApPyAUAAABA4Qm5AAAAACg8IRcAAAAAhSfkAgAAAKDwhFwAAAAAFJ6QCwAAAIDCE3IBAAAAUHhCLgAAAAAKT8gFAAAAQOEJuQAAAAAoPCEXAAAAAIUn5AIAAACg8IRcAAAAABSekAsAAACAwmvWkOvhhx/OQQcdlC222CLV1dW54YYbluuzcOHCnHjiidlyyy2z+eabZ/Dgwbn88svXeOxXX301xx57bLbeeutsvvnm2XrrrXPWWWfl/fffX2H/urq6Up11dXVrPD4AAAAAa09Vc2580aJF2XLLLXPwwQfnO9/5zgr7nHrqqXnggQdy5ZVXplu3bnnkkUfy3e9+N+3atctBBx30qceeOXNmli1blosuuig9e/bMjBkzctxxx+Xdd9/NJZdcslz/o48+OgMGDMjcuXM/9ZgAAAAAlEeznsk1bNiwfP/738/IkSPTosWKh3rssccyatSo7LzzzunWrVsOPvjgDB48OH/5y1/WaOyhQ4dm0qRJ2X333dO9e/fsueeeOf7443P77bcv13fSpEl5//33c8wxx6zRmAAAAACUR9mvyTVkyJDcfffdef3115Mkf/rTn/LXv/41u+++e5OP9fe//z3V1dWN1j399NO55JJLcuWVV35sEAcAAADAuq3sqc7EiRPTv3//9O/fP5tuumn22muvnHnmmfnKV77SpOO89tpr+clPfpIjjzyytG7RokU58sgjM3HixHTs2LFJxwMAAABg7Sl7yPXTn/40jz32WG666aY88MAD+eEPf5jTTz89f/jDH1bYf/bs2enUqVPpduGFF65yjLfeeiv7779/dt1110YfSRw3blyGDBmSkSNHNtn+AAAAALD2NeuF51fl/fffz9lnn53rrrsuw4cPT5L0798/zz77bH7yk59k6NChy92nQ4cOmTZtWmm5bdu2Kx1j3rx5GTFiRLbYYov89Kc/TUVFRantwQcfzJw5c3LTTTclSRoaGpIkvXv3znHHHZfTTz99jfcRAAAAgOZX1pDrww8/zIcffpjKyspG6ysrK1NfX7/C+1RVVaVHjx6rtf0333wz++yzT/r27ZtrrrkmVVWNd/fWW2/N0qVLS8tPPPFEjj322Nx5553p2bPnJ9wbAAAAAMqlWUOuhQsXZtasWUmS+vr6vP7663nm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FwAAAACFJ+QCAAAAoPCEXAAAAAAUnpALAAAAgMITcgEAAABQeEIuAAAAAApPyAUAAABA4Qm5AAAAACg8IRcAAAAAhSfkAgAAAKDwhFwAAAAAFN5aC7kuuuiiVFdX58QTT2y0fsyYMamurm50Gzp0aJOOvWTJknz5y19OdXV1nnzyyUZtDz74YIYNG5bOnTund+/eOeOMM/LRRx816fgAAAAANK+1EnI9/vjjue6669KvX78Vtu+yyy6ZMWNG6fbrX/+6Scc//fTT06lTp+XWP/vssznggAOyyy675KGHHsrkyZPz29/+NmeeeWaTjg8AAABA82r2kGvBggU56qijctlll6W6unqFfTbYYIPU1NSUbm3btm2y8X/zm99k2rRpOeecc5Zru/XWW9OnT5+ccsop6dGjR/7X//pfOeuss3L11Vfn73//e5PVAAAAAEDzavaQ67jjjsvIkSOz8847f2yf6dOnp1evXhk0aFDGjh2bt99+u0nGnjNnTo4//vhcddVVadmy5XLtH3zwwXLrW7VqlSVLluSpp55qkhoAAAAAaH7NGnJdf/31mTVrVk477bSP7TN06NBceeWVue2223LuuefmL3/5S0aMGJEPPvhgjcZetmxZjjrqqBxzzDEZMGDACvvsvvvu+fOf/5wpU6bko48+yhtvvJGJEycmSebNm7dG4wMAAACw9jRbyDVz5sycffbZufrqq7Peeut9bL/99tsvX/3qV9OvX78MHz48U6dOzcyZM3PPPfessP/s2bPTqVOn0u3CCy9cYb8LL7ww66+/fo499tiPHXu33XbLOeeckxNPPDE1NTUZPHhwhg0bliRp0cIXTwIAAAAURVVzbfixxx5LXV1dhgwZUlq3bNmyPPLII5k8eXLeeOONbLDBBsvdr0OHDunYsWNmzZq1wu126NAh06ZNKy1/3PW7HnzwwUyfPj2bbrppo/VDhw7N17/+9Vx11VVJkmOPPTbHHHNM3nzzzVRXV+e1117LWWedle7du3/SXQYAAACgTJot5Nprr72yzTbbNFp3zDHHpGfPnvne976X9ddff4X3q6ury9y5c1NTU7PC9qqqqvTo0WOV419++eVZvHhxafnNN98shVs77LBDo74VFRXp0KFDkmTq1Knp3Llztt5661WOAQAAAMC6odlCrurq6uW+TfELX/hC2rZtmy233DJJsnDh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hFwAAAAAFJ6QCwAAAIDCE3IBAAAAUHhCLgAAAAAKT8gFAAAAQOFVlbuAImp5+KnlLoEymDlzZmpra8tdBgAAALACzuQCAAAAoPCEXAAAAAAUnpALAAAAgMITcgEAAABQeEIuAAAAAApPyAUAAABA4Qm5AAAAACg8IRcAAAAAhSfkAgAAAKDwhFwAAAAAFJ6QCwAAAIDCE3IBAAAAUHhV5S6giBr+elO5S6AMeiVp+Oufy13GOqGi/8HlLgEAAAAacSYXAAAAAIUn5AIAAACg8IRcAAAAABSekAsAAACAwhNyAQAAAFB4Qi4AAAAACk/IBQAAAEDhCbkAAAAAKDwhFwAAAACFJ+QCAAAAoPCEXAAAAAAUnpALAAAAgMITcgEAAABQeEIuAAAAAApPyAUAAABA4Qm5AAAAACg8IRcAAAAAhSfkAgAAAKDwhFwAAAAAFJ6QCwAAAIDCE3IBAAAAUHhCLgAAAAAKr1Ah14QJE1JdXd3o1rt370Z9XnzxxXzjG99I165d06FDh+y8886ZMWNGmSoGAAAAYG2oKncBn1RtbW3uvPPO0nJlZWXp51deeSV77rlnDjrooNx+++2prq7O//zP/6R169Yfu70BAwbkiiuuyE477dSsdQMAAADQfAoXclVVVaWmpmaFbeeee2522223/OAHPyit6969+1qqDAAAAIByKdTHFZN/nK3Vt2/fbLXVVvnWt76VV155JUlSX1+fu+++O3369Ml+++2Xnj17Ztddd80tt9xS3oIBAAAAaHaFCrkGDx6cK664IlOnTs2ll16aefPmZdiwYXn33Xfz9ttvZ+HChbnooouy66675tZbb81+++2Xo446Kvfcc0+5SwcAAACgGRXq44p77LFHo+XBgwdn4MCBufHGG7PffvslSb761a/m2GOPTZJstdVWeeqpp3LVVVdlzz33TJLsv//+mT59emkbixcvzgEHHNDo2l5z5sxp7l0BAAAAoAkVKuT6d23atEnfvn0za9astGvXLlVVVenTp0+jPr179270kcVLL700S5YsKS3vvffeOfPMMzN48OC1VjcAAAAATavQIdeSJUsyc+bM7LTTTll//fWz7bbbZubMmY36vPjii+nSpUtpuWPHjo3aKysr06FDh/To0WOt1AwAAABA0ytUyHXaaaflK1/5Sjp37px33nkn559/fhYvXpyDDz44STJ27NgcccQR2XHHHbPzzjtn2rRpueWWW3LDDTeUuXIAAAAAmlOhQq433ngj3/72t1NXV5dNN900gwcPzu9///t07do1yT8+enjxxRfnoosuyvjx49OjR49ceeWVpetxAQAAAPDZVKiQa/Lkyavsc+ihh+bQQw9d7W0+++yza1ISAAAAAOuAFuUuAAAAAADWlJALAAAAgMITcgEAAABQeEIuAAAAAApPyAUAAABA4Qm5AAAAACg8IRcAAAAAhSfkAgAAAKDwhFwAAAAAFJ6QCwAAAIDCE3IBAAAAUHhCLgAAAAAKT8gFAAAAQOEJuQAAAAAoPCEXAAAAAIUn5AIAAACg8IRcAAAAABSekAsAAACAwhNyAQAAAFB4Qi4AAAAACq+q3AUUUUX/g8tdAmUwc+bM1NbWlrsMAAAAYAWcyQUAAABA4Qm5AAAAACg8IRcAAAAAhSfkAgAAAKDwKubPn99Q7iIAAAAAYE04kwsAAACAwhNyAQAAAFB4Qi4AAAAACk/IBQAAAEDhCbkAAAAAKDwh12q6+uqrs9VWW6Wmpib/+3//7zzyyCPlLok1dNFFF2XXXXdNly5d0rNnz4waNSrPP/98oz5jxoxJdXV1o9vQoUMb9fnggw9y4oknpkePHunYsWMOOuigzJkzZ23uCp/ChAkTlpvb3r17l9obGhoyYcKE9O3bN5tvvnn22muvvPDCC422MX/+/IwePTpdu3ZN165dM3r06MyfP38t7wmfxoABA5ab/+rq6hx44IFJVn18JKt3jLDuePjhh3PQQQdliy22SHV1dW644YZG7U31mH/uuefy1a9+NZtvvnm22GKLTJw4MQ0Nvsi6nFY29x9++GHOOOOM7LjjjunYsWP69OmTb3/725k9e3ajbey1117L/U341re+1aiP54R116oe/031em/27NkZNWpUOnbsmB49euSkk07K0qVLm33/WLlVzf+KXg9UV1fnhBNOKPXxnqCYVuf9nuf/zx4h12q45ZZbMn78+Bx//PF56KGHsv322+eAAw5Y7gUQxfLHP/4xRx55ZO65557cfvvtqaqqyte+9rW89957jfrtsssumTFjRun261//ulH7ySefnDvuuCPXXHNN7rrrrvz973/PqFGjsmzZsrW5O3wKtbW1jeb2X8PrSy65JJdffnkmTpyY++67L+3bt8++++6bv//976U+3/72t/PMM89k6tSpmTp1ap555pn8x3/8Rzl2hU/o/vvvbzT3Dz74YCoqKvK1r32t1Gdlx0eyescI645FixZlyy23zHnnnZdWrVot194Uj/m//e1v2XfffbPZZpvlvvvuy3nnnZef/OQnueyyy9bKPrJiK5v7xYsX5+mnn84JJ5yQBx98MDfeeGPmzJmT/fffPx999FGjvoceemijvwk//vGPG7V7Tlh3rerxn6z5671ly5Zl1KhRWbhwYe66665cc801uf3223Pqqac2+/6xcqua/3+d9xkzZuSXv/xlkjR6TZB4T1BEq/N+z/P/Z0/F/PnzxYursPvuu6dfv3659NJLS+u23XbbjBw5MmeccUYZK6MpLVy4MF27ds0NN9yQ4cOHJ/nHf23efffdTJkyZYX3WbBgQXr16pXLL7+8dAbI66+/ngEDBmTq1KnZfffd11r9fDITJkzI7bffnunTpy/X1tDQkL59++aoo44q/Rfv/fffT21tbc4555wcccQRmTFjRnbYYYfcfffdGTJkSJJk+vTpGT58eB5//PHU1tau1f1hzVxwwQW59NJLM2PGjLRq1Wqlx0eyescI665OnTrlRz/6UQ499NAkTfeYv+aaa3LmmWfmf/7nf0pvpM4///xMnjw5zz//fCoqKsqzw5T8+9yvyH//939nyJAhefjhh9OvX78k/ziTa8stt8z555+/wvt4TiiOFR0DTfF67/e//30OPPDAPPvss+ncuXOSZMqUKRk7dmxmzpyZjTbaqPl3jlVanb8BY8eOzSOPPJI///nPpXXeE3w2/Pv7Pc//n03O5FqFpUuX5qmnnspuu+3WaP1uu+2WP/3pT2WqiuawcOHC1NfXp7q6utH66dOnp1evXhk0aFDGjh2bt99+u9T21FNP5cMPP2x0fHTu3Dl9+vRxfBTAK6+8kr59+2arrbbKt771rbzyyitJkldffTXz5s1rNK+tWrXKjjvuWJrXxx57LG3atMkOO+xQ6jNkyJC0bt3a3BdMQ0NDfv7zn2fUqFGN/sP7ccdHsnrHCMXRVI/5xx57LF/60pcaHUe777575s6dm1dffXUt7Q1r6p//vf/31wM333xzevTokSFDhuS0005r9F9+zwnFt6av9x577LH06dOnFHAl/3j8f/DBB3nqqafW2n6wZhYuXJhbbrkl3/zmN5dr856g+P79/Z7n/8+mqnIXsK6rq6vLsmXL0r59+0br27dvn7feeqtMVdEcxo8fnwEDBmT77bcvrRs6dGj22WefdOvWLa+99lrOPffcjBgxIg888EA22GCDvPXWW6msrEy7du0abcvxse4bPHhwrrjiitTW1uadd97J+eefn2HDhuXRRx/NvHnzkmSFj/u5c+cmSd566620a9eu0X9mKioqsummm5r7grn//vvz6quv5vDDDy+tW9nxsckmm6zWMUJxNNVj/q233krHjh2X28Y/27p3795cu0ATWbp0aU477bR85StfSadOnUrrDzjggHTp0iWbb755/vu//ztnnXVWnnvuudx6661JPCcUXVO83nvrrbeW+xvSrl27VFZWOgYKZOrUqVm6dGkOPvjgRuu9J/hs+Pf3e57/P5uEXJDklFNOyaOPPpq77747lZWVpfX77bdf6ed+/fpl4MCBGTBgQO65556MGDGiHKXSRPbYY49Gy4MHD87AgQNz4403ZrvttitTVZTD9ddfn2233TYDBgworVvZ8XHssceu7RKBteCjjz7K6NGjs2DBgtx0002N2v7P//k/pZ/79euX7t27Z/fdd89TTz2VgQMHrt1CaXJe7/FP119/fb761a9m0003bbTeMVJ8H/d+j88eH1dchX/+B+ZfT0dNkrfffjubbbZZmaqiKZ188sm5+eabc/vtt68yZe/QoUM6duyYWbNmJUk222yzLFu2LHV1dY36OT6Kp02bNunbt29mzZqVmpqaJFnp436zzTZLXV1do29NaWhoyDvvvGPuC+Ttt9/OXXfdtcKPJfyrfz0+kqzWMUJxNNVjfrPNNlvhNv7Zxrrro48+ypFHHpnnnnsut912WzbZZJOV9t9mm21SWVnZ6PWA54TPjk/zem9Fj/9/fiLEMVAMzzzzTJ588slVviZIvCcomo97v+f5/7NJyLUK66+/fgYOHJj777+/0fr777+/0edyKaZx48aV/uD17t17lf3r6uoyd+7c0h/EgQMHZr311mt0fMyZM6d0gUKKY8mSJZk5c2ZqamrSrVu31NTUNJrXJUuWZPr06aV53X777bNw4cI89thjpT6PPfZYFi1aZO4L5MYbb8wGG2zQ6D+0K/Kvx0eS1TpGKI6mesxvv/32mT59epYsWVLqc//996dDhw7p1q3bWtobPqkPP/wwRxxxRJ577rnccccdpcf5yjz33HNZtmxZqa/nhM+WT/N6b/vtt8+MGTMyZ86cUp/7778/G2ywgbP9CuL6669Pt27dsssuu6yyr/cExbGy93ue/z+bfFxxNRxzzDH5j//4jwwaNCg77LBDJk+enDfffNO3ZxXcCSeckClTpuQXv/hFqqurS5/Jbt26ddq0aZOFCxfmvPPOy4gRI1JTU5PXXnstZ599dtq3b5+99947SbLxxhvnsMMOyxlnnJH27dunbdu2OfXUU9OvX7/VeoKkfP55zZXOnTuXrrm0ePHiHHzwwamoqMiYMWNy0UUXpba2Nr169coFF1yQ1q1bZ//990+S9OnTJ0OHDs1//ud/5uKLL06S/Od//mf23HNP36JVEA0NDfnZz36Wr3/962nTpk2jtpUdH0lW6xhh3bJw4cLSf9zr6+vz+uuv55lnnknbtm3TpUuXJnnM77///pk4cWKOPvronHDCCXnxxRdz8cUX56STTvLNSmW0srnv0KFDvvnNb+bJJ5/MTTfdlIqKitLrgY022iitWrXKyy+/nF/96lcZNmxYNtlkk8yYMSOnnXZattpqq9I3bXlOWLet7Bho27Ztk7ze22233bLFFlvkO9/5Ts4999y89957+f73v5/DDz/cNyuW2ar+/ifJ4sWL8+tf/zpjx45d7u+19wTFtar3e031mt/z/7qlYv78+Q2r7sbVV1+dSy65JPPmzcsWW2yRH/7wh/nyl79c7rJYA//+rUn/NG7cuJx88sl5//33c+ihh+aZZ57JggULUlNTk5122imnnnpqo2/O+eCDD3Laaadl6tSpWbJkSXbeeedceOGFjfqw7vnWt76VRx55JHV1ddl0000zePDgnHrqqenbt2+SfwQg5513Xq677rrMnz8/gwYNygUXXJAtt9yytI358+fnpJNOym9/+9skyfDhw/OjH/3oY48t1i0PPfRQRowYkXvvvTeDBg1q1Laq4yNZvWOEdce0adOyzz77LLf+4IMPzqRJk5rsMf/cc8/lhBNOyBNPPJHq6uocccQRGTdunBe5ZbSyuR8/fny23nrrFd7v8ssvz6GHHprXX389o0ePzgsvvJBFixalU6dOGTZsWMaPH5+2bduW+ntOWHet7Bi46KKLmuz13uzZs3PCCSfkoYceSsuWLXPAAQfknHPOyQYbbLBW9pMVW9Xf/yT5xS9+ke9+97v561//mg4dOjTq5z1Bca3q/V7SdK/5Pf+vO4RcAAAAABSea3IBAAAAUHhCLgAAAAAKT8gFAAAAQOEJuQAAAAAoPCEXAAAAAIUn5AIAAACg8IRcAAAAABSekAsAAACAwhNyAQAAAFB4/x9nBGN4jbN+fgAAAABJRU5ErkJggg==" }, "metadata": {} } ], "metadata": {} }, { "cell_type": "code", "execution_count": 8, "source": [ "# Plot gender of users\r\n", "gender_counts = users['gender'].value_counts()\r\n", "colors1 = ['lightblue', 'pink']\r\n", "pie = go.Pie(labels=gender_counts.index,\r\n", " values=gender_counts.values,\r\n", " marker=dict(colors=colors1),\r\n", " hole=0.5)\r\n", "layout = go.Layout(title='Gender Users', font=dict(size=12), legend=dict(orientation='h'))\r\n", "\r\n", "fig = go.Figure(data=[pie], layout=layout)\r\n", "py.iplot(fig)" ], "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "
" ], "application/vnd.plotly.v1+json": { "config": { "linkText": "Export to plot.ly", "plotlyServerURL": "https://plot.ly", "showLink": false }, "data": [ { "hole": 0.5, "labels": [ "Male", "Female" ], "marker": { "colors": [ "lightblue", "pink" ] }, "type": "pie", "values": [ 4331, 1709 ] } ], "layout": { "font": { "size": 12 }, "legend": { "orientation": "h" }, "template": { "data": { "bar": [ { "error_x": { "color": "#2a3f5f" }, "error_y": { "color": "#2a3f5f" }, "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "bar" } ], "barpolar": [ { "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "barpolar" } ], "carpet": [ { "aaxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "baxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "type": "carpet" } ], "choropleth": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "choropleth" } ], "contour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "contour" } ], "contourcarpet": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "contourcarpet" } ], "heatmap": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "heatmap" } ], "heatmapgl": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "heatmapgl" } ], "histogram": [ { "marker": { "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "histogram" } ], "histogram2d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "histogram2d" } ], "histogram2dcontour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "histogram2dcontour" } ], "mesh3d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "mesh3d" } ], "parcoords": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "parcoords" } ], "pie": [ { "automargin": true, "type": "pie" } ], "scatter": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter" } ], "scatter3d": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter3d" } ], "scattercarpet": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattercarpet" } ], "scattergeo": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergeo" } ], "scattergl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergl" } ], "scattermapbox": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermapbox" } ], "scatterpolar": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolar" } ], "scatterpolargl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolargl" } ], "scatterternary": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterternary" } ], "surface": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "surface" } ], "table": [ { "cells": { "fill": { "color": "#EBF0F8" }, "line": { "color": "white" } }, "header": { "fill": { "color": "#C8D4E3" }, "line": { "color": "white" } }, "type": "table" } ] }, "layout": { "annotationdefaults": { "arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1 }, "autotypenumbers": "strict", "coloraxis": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "colorscale": { "diverging": [ [ 0, "#8e0152" ], [ 0.1, "#c51b7d" ], [ 0.2, "#de77ae" ], [ 0.3, "#f1b6da" ], [ 0.4, "#fde0ef" ], [ 0.5, "#f7f7f7" ], [ 0.6, "#e6f5d0" ], [ 0.7, "#b8e186" ], [ 0.8, "#7fbc41" ], [ 0.9, "#4d9221" ], [ 1, "#276419" ] ], "sequential": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "sequentialminus": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ] }, "colorway": [ "#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52" ], "font": { "color": "#2a3f5f" }, "geo": { "bgcolor": "white", "lakecolor": "white", "landcolor": "#E5ECF6", "showlakes": true, "showland": true, "subunitcolor": "white" }, "hoverlabel": { "align": "left" }, "hovermode": "closest", "mapbox": { "style": "light" }, "paper_bgcolor": "white", "plot_bgcolor": "#E5ECF6", "polar": { "angularaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "radialaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "scene": { "xaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "yaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "zaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" } }, "shapedefaults": { "line": { "color": "#2a3f5f" } }, "ternary": { "aaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "baxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "caxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "title": { "x": 0.05 }, "xaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 }, "yaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 } } }, "title": { "text": "Gender Users" } } } }, "metadata": {} } ], "metadata": {} }, { "cell_type": "code", "execution_count": 9, "source": [ "# Merge reviews, movie and user dataset\r\n", "final_df = reviews.merge(movies, on='movieId', how='left').merge(users, on='userId', how='left')\r\n", "print('final_df shape:', final_df.shape)\r\n", "final_df.head()" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "final_df shape: (1000209, 9)\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ " userId movieId rating title \\\n", "0 1 1193 5 One Flew Over the Cuckoo's Nest (1975) \n", "1 1 661 3 James and the Giant Peach (1996) \n", "2 1 914 3 My Fair Lady (1964) \n", "3 1 3408 4 Erin Brockovich (2000) \n", "4 1 2355 5 Bug's Life, A (1998) \n", "\n", " genres release_year gender age occupation \n", "0 Drama 1975 Female Under 18 K-12 student \n", "1 Animation|Children's|Musical 1996 Female Under 18 K-12 student \n", "2 Musical|Romance 1964 Female Under 18 K-12 student \n", "3 Drama 2000 Female Under 18 K-12 student \n", "4 Animation|Children's|Comedy 1998 Female Under 18 K-12 student " ], "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
userIdmovieIdratingtitlegenresrelease_yeargenderageoccupation
0111935One Flew Over the Cuckoo's Nest (1975)Drama1975FemaleUnder 18K-12 student
116613James and the Giant Peach (1996)Animation|Children's|Musical1996FemaleUnder 18K-12 student
219143My Fair Lady (1964)Musical|Romance1964FemaleUnder 18K-12 student
3134084Erin Brockovich (2000)Drama2000FemaleUnder 18K-12 student
4123555Bug's Life, A (1998)Animation|Children's|Comedy1998FemaleUnder 18K-12 student
\n", "
" ] }, "metadata": {}, "execution_count": 9 } ], "metadata": {} }, { "cell_type": "code", "execution_count": 10, "source": [ "final_df[final_df['age'] == '18 - 24']['title'].value_counts()[:10].to_frame()" ], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " title\n", "American Beauty (1999) 715\n", "Star Wars: Episode VI - Return of the Jedi (1983) 586\n", "Star Wars: Episode V - The Empire Strikes Back ... 579\n", "Matrix, The (1999) 567\n", "Star Wars: Episode IV - A New Hope (1977) 562\n", "Braveheart (1995) 544\n", "Saving Private Ryan (1998) 543\n", "Jurassic Park (1993) 541\n", "Terminator 2: Judgment Day (1991) 529\n", "Men in Black (1997) 514" ], "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
title
American Beauty (1999)715
Star Wars: Episode VI - Return of the Jedi (1983)586
Star Wars: Episode V - The Empire Strikes Back (1980)579
Matrix, The (1999)567
Star Wars: Episode IV - A New Hope (1977)562
Braveheart (1995)544
Saving Private Ryan (1998)543
Jurassic Park (1993)541
Terminator 2: Judgment Day (1991)529
Men in Black (1997)514
\n", "
" ] }, "metadata": {}, "execution_count": 10 } ], "metadata": {} }, { "cell_type": "code", "execution_count": 11, "source": [ "# Print movie / user sum\r\n", "n_movies = final_df['movieId'].nunique()\r\n", "n_users = final_df['userId'].nunique()\r\n", "\r\n", "print('Number of movies:', n_movies)\r\n", "print('Number of users:', n_users) " ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Number of movies: 3706\n", "Number of users: 6040\n" ] } ], "metadata": {} }, { "cell_type": "code", "execution_count": 12, "source": [ "# implement SVD with Python SurPRISE, a Python Recommendation Framework\r\n", "\r\n", "from surprise import Reader, Dataset, SVD, SVDpp\r\n", "from surprise import accuracy\r\n", "\r\n", "reader = Reader(rating_scale=(1, 5))\r\n", "dataset = Dataset.load_from_df(final_df[['userId', 'movieId', 'rating']], reader=reader)\r\n", "\r\n", "svd = SVD(n_factors=50)\r\n", "svd_plusplus = SVDpp(n_factors=50)\r\n", "\r\n", "# train with SVD\r\n", "trainset = dataset.build_full_trainset()\r\n", "svd.fit(trainset)\r\n", "# train with SVD++, ATTENTION this take a LONG TIME\r\n", "# svd_plusplus.fit(trainset)\r\n" ], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 12 } ], "metadata": {} }, { "cell_type": "code", "execution_count": 15, "source": [ "# Show titels instead of ids\r\n", "id_2_names = dict()\r\n", "for idx, names in zip(movies['movieId'], movies['title']):\r\n", " id_2_names[idx] = names" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 16, "source": [ "# function for test set\r\n", "def Build_Anti_Testset4User(user_id):\r\n", " \r\n", " fill = trainset.global_mean\r\n", " anti_testset = list()\r\n", " u = trainset.to_inner_uid(user_id)\r\n", " \r\n", " # ur == users ratings\r\n", " user_items = set([item_inner_id for (item_inner_id, rating) in trainset.ur[u]])\r\n", " \r\n", " anti_testset += [(trainset.to_raw_uid(u), trainset.to_raw_iid(i), fill) for\r\n", " i in trainset.all_items() if i not in user_items]\r\n", " \r\n", " return anti_testset" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 19, "source": [ "# Implement Top-X Chart recommender\r\n", "def TopXRec_SVD(user_id, num_recommender=10, latest=False):\r\n", " \r\n", " testSet = Build_Anti_Testset4User(user_id)\r\n", " predict = svd.test(testSet) # here you can change to SVD++\r\n", " \r\n", " recommendation = list()\r\n", " \r\n", " for userID, movieID, actualRating, estimatedRating, _ in predict:\r\n", " intMovieID = int(movieID)\r\n", " recommendation.append((intMovieID, estimatedRating))\r\n", " \r\n", " recommendation.sort(key=lambda x: x[1], reverse=True)\r\n", " \r\n", " movie_names = []\r\n", " movie_ratings = []\r\n", " \r\n", " for name, ratings in recommendation[:20]:\r\n", " movie_names.append(id_2_names[name])\r\n", " movie_ratings.append(ratings)\r\n", " \r\n", " movie_dataframe = pd.DataFrame({'title': movie_names,\r\n", " 'rating': movie_ratings}).merge(movies[['title', 'release_year']],\r\n", " on='title', how='left')\r\n", " \r\n", " if latest == True:\r\n", " return movie_dataframe.sort_values('release_year', ascending=False)[['title', 'rating']].head(num_recommender)\r\n", " \r\n", " else:\r\n", " return movie_dataframe.drop('release_year', axis=1).head(num_recommender)" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 37, "source": [ "TopXRec_SVD(363, num_recommender=10)\r\n", "TopXRec_SVD(363, num_recommender=10, latest=True)" ], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " title rating\n", "11 Wallace & Gromit: The Best of Aardman Animatio... 4.705187\n", "2 Close Shave, A (1995) 4.832506\n", "12 Shawshank Redemption, The (1994) 4.700431\n", "1 Wrong Trousers, The (1993) 4.869839\n", "19 Grand Day Out, A (1992) 4.672615\n", "4 Blade Runner (1982) 4.819672\n", "15 Apocalypse Now (1979) 4.683492\n", "7 One Flew Over the Cuckoo's Nest (1975) 4.738641\n", "16 Young Frankenstein (1974) 4.682844\n", "10 Monty Python and the Holy Grail (1974) 4.705298" ], "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
titlerating
11Wallace & Gromit: The Best of Aardman Animatio...4.705187
2Close Shave, A (1995)4.832506
12Shawshank Redemption, The (1994)4.700431
1Wrong Trousers, The (1993)4.869839
19Grand Day Out, A (1992)4.672615
4Blade Runner (1982)4.819672
15Apocalypse Now (1979)4.683492
7One Flew Over the Cuckoo's Nest (1975)4.738641
16Young Frankenstein (1974)4.682844
10Monty Python and the Holy Grail (1974)4.705298
\n", "
" ] }, "metadata": {}, "execution_count": 37 } ], "metadata": {} }, { "cell_type": "code", "execution_count": 36, "source": [], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " title rating\n", "11 Wallace & Gromit: The Best of Aardman Animatio... 4.705187\n", "2 Close Shave, A (1995) 4.832506\n", "12 Shawshank Redemption, The (1994) 4.700431\n", "1 Wrong Trousers, The (1993) 4.869839\n", "19 Grand Day Out, A (1992) 4.672615\n", "4 Blade Runner (1982) 4.819672\n", "15 Apocalypse Now (1979) 4.683492\n", "7 One Flew Over the Cuckoo's Nest (1975) 4.738641\n", "16 Young Frankenstein (1974) 4.682844\n", "10 Monty Python and the Holy Grail (1974) 4.705298" ], "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
titlerating
11Wallace & Gromit: The Best of Aardman Animatio...4.705187
2Close Shave, A (1995)4.832506
12Shawshank Redemption, The (1994)4.700431
1Wrong Trousers, The (1993)4.869839
19Grand Day Out, A (1992)4.672615
4Blade Runner (1982)4.819672
15Apocalypse Now (1979)4.683492
7One Flew Over the Cuckoo's Nest (1975)4.738641
16Young Frankenstein (1974)4.682844
10Monty Python and the Holy Grail (1974)4.705298
\n", "
" ] }, "metadata": {}, "execution_count": 36 } ], "metadata": {} }, { "cell_type": "code", "execution_count": 43, "source": [ "# Evaluation\r\n", "\r\n", "# Than predict ratings for all pairs not in training set.\r\n", "testset = trainset.build_anti_testset()\r\n", "predictions_svd = svd.test(testset)\r\n", "print('SVD - RMSE:', accuracy.rmse(predictions_svd, verbose=False))\r\n", "print('SVD - MAE:', accuracy.mae(predictions_svd, verbose=False))" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "SVD - RMSE: 0.7015239638211899\n", "SVD - MAE: 0.5429390320069348\n" ] } ], "metadata": {} } ], "metadata": { "orig_nbformat": 4, "language_info": { "name": "python", "version": "3.8.8", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" }, "kernelspec": { "name": "python3", "display_name": "Python 3.8.8 64-bit" }, "interpreter": { "hash": "53e4db133e7a886bd36ef8c79c0b5519f0af174d53fdba9ad5d5d94e6d9f4b55" } }, "nbformat": 4, "nbformat_minor": 2 }