{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", " \n", " \"Skills\n", " \n", "

\n", "\n", "# Content Based Filtering\n", "\n", "Estimated time needed: **25** minutes\n", "\n", "## Objectives\n", "\n", "After completing this lab you will be able to:\n", "\n", "* Create a recommendation system using Content Based filtering\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recommendation systems are a collection of algorithms used to recommend items to users based on information taken from the user. These systems have become ubiquitous, and can be commonly seen in online stores, movies databases and job finders. In this notebook, we will explore Content-based recommendation systems and implement a simple version of one using Python and the Pandas library.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Table of contents\n", "\n", "
\n", "
    \n", "
  1. Acquiring the Data
  2. \n", "
  3. Preprocessing
  4. \n", "
  5. Content-Based Filtering
  6. \n", "
\n", "
\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Acquiring the Data\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To acquire and extract the data, simply run the following Bash scripts:\\\n", "Dataset acquired from [GroupLens](http://grouplens.org/datasets/movielens/?utm_medium=Exinfluencer&utm_source=Exinfluencer&utm_content=000026UJ&utm_term=10006555&utm_id=NA-SkillsNetwork-Channel-SkillsNetworkCoursesIBMDeveloperSkillsNetworkML0101ENSkillsNetwork20718538-2022-01-01). Let's download the dataset. To download the data, we will use **`!wget`** to download it from IBM Object Storage.\\\n", "**Did you know?** When it comes to Machine Learning, you will likely be working with large datasets. As a business, where can you host your data? IBM is offering a unique opportunity for businesses, with 10 Tb of IBM Cloud Object Storage: [Sign up now for free](http://cocl.us/ML0101EN-IBM-Offer-CC)\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2022-08-21 03:21:27-- https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBMDeveloperSkillsNetwork-ML0101EN-SkillsNetwork/labs/Module%205/data/moviedataset.zip\n", "Resolving cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud (cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud)... 169.63.118.104\n", "Connecting to cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud (cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud)|169.63.118.104|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 160301210 (153M) [application/zip]\n", "Saving to: ‘moviedataset.zip’\n", "\n", "moviedataset.zip 100%[===================>] 152.88M 28.6MB/s in 5.4s \n", "\n", "2022-08-21 03:21:33 (28.3 MB/s) - ‘moviedataset.zip’ saved [160301210/160301210]\n", "\n", "unziping ...\n", "Archive: moviedataset.zip\n", " inflating: links.csv \n", " inflating: movies.csv \n", " inflating: ratings.csv \n", " inflating: README.txt \n", " inflating: tags.csv \n" ] } ], "source": [ "!wget -O moviedataset.zip https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBMDeveloperSkillsNetwork-ML0101EN-SkillsNetwork/labs/Module%205/data/moviedataset.zip\n", "print('unziping ...')\n", "!unzip -o -j moviedataset.zip " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now you're ready to start working with the data!\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Preprocessing\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's get all of the imports out of the way:\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "#Dataframe manipulation library\n", "import pandas as pd\n", "#Math functions, we'll only need the sqrt function so let's import only that\n", "from math import sqrt\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's read each file into their Dataframes:\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "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", "
movieIdtitlegenres
01Toy Story (1995)Adventure|Animation|Children|Comedy|Fantasy
12Jumanji (1995)Adventure|Children|Fantasy
23Grumpier Old Men (1995)Comedy|Romance
34Waiting to Exhale (1995)Comedy|Drama|Romance
45Father of the Bride Part II (1995)Comedy
\n", "
" ], "text/plain": [ " movieId title \\\n", "0 1 Toy Story (1995) \n", "1 2 Jumanji (1995) \n", "2 3 Grumpier Old Men (1995) \n", "3 4 Waiting to Exhale (1995) \n", "4 5 Father of the Bride Part II (1995) \n", "\n", " genres \n", "0 Adventure|Animation|Children|Comedy|Fantasy \n", "1 Adventure|Children|Fantasy \n", "2 Comedy|Romance \n", "3 Comedy|Drama|Romance \n", "4 Comedy " ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Storing the movie information into a pandas dataframe\n", "movies_df = pd.read_csv('movies.csv')\n", "#Storing the user information into a pandas dataframe\n", "ratings_df = pd.read_csv('ratings.csv')\n", "#Head is a function that gets the first N rows of a dataframe. N's default is 5.\n", "movies_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's also remove the year from the **title** column by using pandas' replace function and store in a new **year** column.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/jupyterlab/conda/envs/python/lib/python3.7/site-packages/ipykernel_launcher.py:7: FutureWarning: The default value of regex will change from True to False in a future version.\n", " import sys\n" ] }, { "data": { "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", "
movieIdtitlegenresyear
01Toy StoryAdventure|Animation|Children|Comedy|Fantasy1995
12JumanjiAdventure|Children|Fantasy1995
23Grumpier Old MenComedy|Romance1995
34Waiting to ExhaleComedy|Drama|Romance1995
45Father of the Bride Part IIComedy1995
\n", "
" ], "text/plain": [ " movieId title \\\n", "0 1 Toy Story \n", "1 2 Jumanji \n", "2 3 Grumpier Old Men \n", "3 4 Waiting to Exhale \n", "4 5 Father of the Bride Part II \n", "\n", " genres year \n", "0 Adventure|Animation|Children|Comedy|Fantasy 1995 \n", "1 Adventure|Children|Fantasy 1995 \n", "2 Comedy|Romance 1995 \n", "3 Comedy|Drama|Romance 1995 \n", "4 Comedy 1995 " ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Using regular expressions to find a year stored between parentheses\n", "#We specify the parantheses so we don't conflict with movies that have years in their titles\n", "movies_df['year'] = movies_df.title.str.extract('(\\(\\d\\d\\d\\d\\))',expand=False)\n", "#Removing the parentheses\n", "movies_df['year'] = movies_df.year.str.extract('(\\d\\d\\d\\d)',expand=False)\n", "#Removing the years from the 'title' column\n", "movies_df['title'] = movies_df.title.str.replace('(\\(\\d\\d\\d\\d\\))', '')\n", "#Applying the strip function to get rid of any ending whitespace characters that may have appeared\n", "movies_df['title'] = movies_df['title'].apply(lambda x: x.strip())\n", "movies_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With that, let's also split the values in the **Genres** column into a **list of Genres** to simplify for future use. This can be achieved by applying Python's split string function on the correct column.\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "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", "
movieIdtitlegenresyear
01Toy Story[Adventure, Animation, Children, Comedy, Fantasy]1995
12Jumanji[Adventure, Children, Fantasy]1995
23Grumpier Old Men[Comedy, Romance]1995
34Waiting to Exhale[Comedy, Drama, Romance]1995
45Father of the Bride Part II[Comedy]1995
\n", "
" ], "text/plain": [ " movieId title \\\n", "0 1 Toy Story \n", "1 2 Jumanji \n", "2 3 Grumpier Old Men \n", "3 4 Waiting to Exhale \n", "4 5 Father of the Bride Part II \n", "\n", " genres year \n", "0 [Adventure, Animation, Children, Comedy, Fantasy] 1995 \n", "1 [Adventure, Children, Fantasy] 1995 \n", "2 [Comedy, Romance] 1995 \n", "3 [Comedy, Drama, Romance] 1995 \n", "4 [Comedy] 1995 " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Every genre is separated by a | so we simply have to call the split function on |\n", "movies_df['genres'] = movies_df.genres.str.split('|')\n", "movies_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since keeping genres in a list format isn't optimal for the content-based recommendation system technique, we will use the One Hot Encoding technique to convert the list of genres to a vector where each column corresponds to one possible value of the feature. This encoding is needed for feeding categorical data. In this case, we store every different genre in columns that contain either 1 or 0. 1 shows that a movie has that genre and 0 shows that it doesn't. Let's also store this dataframe in another variable since genres won't be important for our first recommendation system.\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "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", " \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", "
movieIdtitlegenresyearAdventureAnimationChildrenComedyFantasyRomance...HorrorMysterySci-FiIMAXDocumentaryWarMusicalWesternFilm-Noir(no genres listed)
01Toy Story[Adventure, Animation, Children, Comedy, Fantasy]19951.01.01.01.01.00.0...0.00.00.00.00.00.00.00.00.00.0
12Jumanji[Adventure, Children, Fantasy]19951.00.01.00.01.00.0...0.00.00.00.00.00.00.00.00.00.0
23Grumpier Old Men[Comedy, Romance]19950.00.00.01.00.01.0...0.00.00.00.00.00.00.00.00.00.0
34Waiting to Exhale[Comedy, Drama, Romance]19950.00.00.01.00.01.0...0.00.00.00.00.00.00.00.00.00.0
45Father of the Bride Part II[Comedy]19950.00.00.01.00.00.0...0.00.00.00.00.00.00.00.00.00.0
\n", "

5 rows × 24 columns

\n", "
" ], "text/plain": [ " movieId title \\\n", "0 1 Toy Story \n", "1 2 Jumanji \n", "2 3 Grumpier Old Men \n", "3 4 Waiting to Exhale \n", "4 5 Father of the Bride Part II \n", "\n", " genres year Adventure \\\n", "0 [Adventure, Animation, Children, Comedy, Fantasy] 1995 1.0 \n", "1 [Adventure, Children, Fantasy] 1995 1.0 \n", "2 [Comedy, Romance] 1995 0.0 \n", "3 [Comedy, Drama, Romance] 1995 0.0 \n", "4 [Comedy] 1995 0.0 \n", "\n", " Animation Children Comedy Fantasy Romance ... Horror Mystery \\\n", "0 1.0 1.0 1.0 1.0 0.0 ... 0.0 0.0 \n", "1 0.0 1.0 0.0 1.0 0.0 ... 0.0 0.0 \n", "2 0.0 0.0 1.0 0.0 1.0 ... 0.0 0.0 \n", "3 0.0 0.0 1.0 0.0 1.0 ... 0.0 0.0 \n", "4 0.0 0.0 1.0 0.0 0.0 ... 0.0 0.0 \n", "\n", " Sci-Fi IMAX Documentary War Musical Western Film-Noir \\\n", "0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "\n", " (no genres listed) \n", "0 0.0 \n", "1 0.0 \n", "2 0.0 \n", "3 0.0 \n", "4 0.0 \n", "\n", "[5 rows x 24 columns]" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Copying the movie dataframe into a new one since we won't need to use the genre information in our first case.\n", "moviesWithGenres_df = movies_df.copy()\n", "\n", "#For every row in the dataframe, iterate through the list of genres and place a 1 into the corresponding column\n", "for index, row in movies_df.iterrows():\n", " for genre in row['genres']:\n", " moviesWithGenres_df.at[index, genre] = 1\n", "#Filling in the NaN values with 0 to show that a movie doesn't have that column's genre\n", "moviesWithGenres_df = moviesWithGenres_df.fillna(0)\n", "moviesWithGenres_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, let's look at the ratings dataframe.\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "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", "
userIdmovieIdratingtimestamp
011692.51204927694
1124713.01204927438
21485165.01204927435
3225713.51436165433
421094874.01436165496
\n", "
" ], "text/plain": [ " userId movieId rating timestamp\n", "0 1 169 2.5 1204927694\n", "1 1 2471 3.0 1204927438\n", "2 1 48516 5.0 1204927435\n", "3 2 2571 3.5 1436165433\n", "4 2 109487 4.0 1436165496" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ratings_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every row in the ratings dataframe has a user id associated with at least one movie, a rating and a timestamp showing when they reviewed it. We won't be needing the timestamp column, so let's drop it to save memory.\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/jupyterlab/conda/envs/python/lib/python3.7/site-packages/ipykernel_launcher.py:2: FutureWarning: In a future version of pandas all arguments of DataFrame.drop except for the argument 'labels' will be keyword-only\n", " \n" ] }, { "data": { "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", "
userIdmovieIdrating
011692.5
1124713.0
21485165.0
3225713.5
421094874.0
\n", "
" ], "text/plain": [ " userId movieId rating\n", "0 1 169 2.5\n", "1 1 2471 3.0\n", "2 1 48516 5.0\n", "3 2 2571 3.5\n", "4 2 109487 4.0" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Drop removes a specified row or column from a dataframe\n", "ratings_df = ratings_df.drop('timestamp', 1)\n", "ratings_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Content-Based recommendation system\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's take a look at how to implement **Content-Based** or **Item-Item recommendation systems**. This technique attempts to figure out what a user's favourite aspects of an item is, and then recommends items that present those aspects. In our case, we're going to try to figure out the input's favorite genres from the movies and ratings given.\n", "\n", "Let's begin by creating an input user to recommend movies to:\n", "\n", "Notice: To add more movies, simply increase the amount of elements in the **userInput**. Feel free to add more in! Just be sure to write it in with capital letters and if a movie starts with a \"The\", like \"The Matrix\" then write it in like this: 'Matrix, The' .\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "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", "
titlerating
0Breakfast Club, The5.0
1Toy Story3.5
2Jumanji2.0
3Pulp Fiction5.0
4Akira4.5
\n", "
" ], "text/plain": [ " title rating\n", "0 Breakfast Club, The 5.0\n", "1 Toy Story 3.5\n", "2 Jumanji 2.0\n", "3 Pulp Fiction 5.0\n", "4 Akira 4.5" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "userInput = [\n", " {'title':'Breakfast Club, The', 'rating':5},\n", " {'title':'Toy Story', 'rating':3.5},\n", " {'title':'Jumanji', 'rating':2},\n", " {'title':\"Pulp Fiction\", 'rating':5},\n", " {'title':'Akira', 'rating':4.5}\n", " ] \n", "inputMovies = pd.DataFrame(userInput)\n", "inputMovies" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Add movieId to input user\n", "\n", "With the input complete, let's extract the input movie's ID's from the movies dataframe and add them into it.\n", "\n", "We can achieve this by first filtering out the rows that contain the input movie's title and then merging this subset with the input dataframe. We also drop unnecessary columns for the input to save memory space.\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/jupyterlab/conda/envs/python/lib/python3.7/site-packages/ipykernel_launcher.py:6: FutureWarning: In a future version of pandas all arguments of DataFrame.drop except for the argument 'labels' will be keyword-only\n", " \n" ] }, { "data": { "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", "
movieIdtitlerating
01Toy Story3.5
12Jumanji2.0
2296Pulp Fiction5.0
31274Akira4.5
41968Breakfast Club, The5.0
\n", "
" ], "text/plain": [ " movieId title rating\n", "0 1 Toy Story 3.5\n", "1 2 Jumanji 2.0\n", "2 296 Pulp Fiction 5.0\n", "3 1274 Akira 4.5\n", "4 1968 Breakfast Club, The 5.0" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Filtering out the movies by title\n", "inputId = movies_df[movies_df['title'].isin(inputMovies['title'].tolist())]\n", "#Then merging it so we can get the movieId. It's implicitly merging it by title.\n", "inputMovies = pd.merge(inputId, inputMovies)\n", "#Dropping information we won't use from the input dataframe\n", "inputMovies = inputMovies.drop('genres', 1).drop('year', 1)\n", "#Final input dataframe\n", "#If a movie you added in above isn't here, then it might not be in the original \n", "#dataframe or it might spelled differently, please check capitalisation.\n", "inputMovies" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "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", "
movieIdtitlegenresyear
01Toy Story[Adventure, Animation, Children, Comedy, Fantasy]1995
12Jumanji[Adventure, Children, Fantasy]1995
293296Pulp Fiction[Comedy, Crime, Drama, Thriller]1994
12461274Akira[Action, Adventure, Animation, Sci-Fi]1988
18851968Breakfast Club, The[Comedy, Drama]1985
\n", "
" ], "text/plain": [ " movieId title \\\n", "0 1 Toy Story \n", "1 2 Jumanji \n", "293 296 Pulp Fiction \n", "1246 1274 Akira \n", "1885 1968 Breakfast Club, The \n", "\n", " genres year \n", "0 [Adventure, Animation, Children, Comedy, Fantasy] 1995 \n", "1 [Adventure, Children, Fantasy] 1995 \n", "293 [Comedy, Crime, Drama, Thriller] 1994 \n", "1246 [Action, Adventure, Animation, Sci-Fi] 1988 \n", "1885 [Comedy, Drama] 1985 " ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inputId" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're going to start by learning the input's preferences, so let's get the subset of movies that the input has watched from the Dataframe containing genres defined with binary values.\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "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", " \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", "
movieIdtitlegenresyearAdventureAnimationChildrenComedyFantasyRomance...HorrorMysterySci-FiIMAXDocumentaryWarMusicalWesternFilm-Noir(no genres listed)
01Toy Story[Adventure, Animation, Children, Comedy, Fantasy]19951.01.01.01.01.00.0...0.00.00.00.00.00.00.00.00.00.0
12Jumanji[Adventure, Children, Fantasy]19951.00.01.00.01.00.0...0.00.00.00.00.00.00.00.00.00.0
293296Pulp Fiction[Comedy, Crime, Drama, Thriller]19940.00.00.01.00.00.0...0.00.00.00.00.00.00.00.00.00.0
12461274Akira[Action, Adventure, Animation, Sci-Fi]19881.01.00.00.00.00.0...0.00.01.00.00.00.00.00.00.00.0
18851968Breakfast Club, The[Comedy, Drama]19850.00.00.01.00.00.0...0.00.00.00.00.00.00.00.00.00.0
\n", "

5 rows × 24 columns

\n", "
" ], "text/plain": [ " movieId title \\\n", "0 1 Toy Story \n", "1 2 Jumanji \n", "293 296 Pulp Fiction \n", "1246 1274 Akira \n", "1885 1968 Breakfast Club, The \n", "\n", " genres year Adventure \\\n", "0 [Adventure, Animation, Children, Comedy, Fantasy] 1995 1.0 \n", "1 [Adventure, Children, Fantasy] 1995 1.0 \n", "293 [Comedy, Crime, Drama, Thriller] 1994 0.0 \n", "1246 [Action, Adventure, Animation, Sci-Fi] 1988 1.0 \n", "1885 [Comedy, Drama] 1985 0.0 \n", "\n", " Animation Children Comedy Fantasy Romance ... Horror Mystery \\\n", "0 1.0 1.0 1.0 1.0 0.0 ... 0.0 0.0 \n", "1 0.0 1.0 0.0 1.0 0.0 ... 0.0 0.0 \n", "293 0.0 0.0 1.0 0.0 0.0 ... 0.0 0.0 \n", "1246 1.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 \n", "1885 0.0 0.0 1.0 0.0 0.0 ... 0.0 0.0 \n", "\n", " Sci-Fi IMAX Documentary War Musical Western Film-Noir \\\n", "0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "293 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "1246 1.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "1885 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "\n", " (no genres listed) \n", "0 0.0 \n", "1 0.0 \n", "293 0.0 \n", "1246 0.0 \n", "1885 0.0 \n", "\n", "[5 rows x 24 columns]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Filtering out the movies from the input\n", "userMovies = moviesWithGenres_df[moviesWithGenres_df['movieId'].isin(inputMovies['movieId'].tolist())]\n", "userMovies" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll only need the actual genre table, so let's clean this up a bit by resetting the index and dropping the movieId, title, genres and year columns.\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/jupyterlab/conda/envs/python/lib/python3.7/site-packages/ipykernel_launcher.py:4: FutureWarning: In a future version of pandas all arguments of DataFrame.drop except for the argument 'labels' will be keyword-only\n", " after removing the cwd from sys.path.\n" ] }, { "data": { "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", " \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", "
AdventureAnimationChildrenComedyFantasyRomanceDramaActionCrimeThrillerHorrorMysterySci-FiIMAXDocumentaryWarMusicalWesternFilm-Noir(no genres listed)
01.01.01.01.01.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
11.00.01.00.01.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
20.00.00.01.00.00.01.00.01.01.00.00.00.00.00.00.00.00.00.00.0
31.01.00.00.00.00.00.01.00.00.00.00.01.00.00.00.00.00.00.00.0
40.00.00.01.00.00.01.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", "
" ], "text/plain": [ " Adventure Animation Children Comedy Fantasy Romance Drama Action \\\n", "0 1.0 1.0 1.0 1.0 1.0 0.0 0.0 0.0 \n", "1 1.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 \n", "2 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 \n", "3 1.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 \n", "4 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 \n", "\n", " Crime Thriller Horror Mystery Sci-Fi IMAX Documentary War Musical \\\n", "0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "2 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "3 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 \n", "4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "\n", " Western Film-Noir (no genres listed) \n", "0 0.0 0.0 0.0 \n", "1 0.0 0.0 0.0 \n", "2 0.0 0.0 0.0 \n", "3 0.0 0.0 0.0 \n", "4 0.0 0.0 0.0 " ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Resetting the index to avoid future issues\n", "userMovies = userMovies.reset_index(drop=True)\n", "#Dropping unnecessary issues due to save memory and to avoid issues\n", "userGenreTable = userMovies.drop('movieId', 1).drop('title', 1).drop('genres', 1).drop('year', 1)\n", "userGenreTable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we're ready to start learning the input's preferences!\n", "\n", "To do this, we're going to turn each genre into weights. We can do this by using the input's reviews and multiplying them into the input's genre table and then summing up the resulting table by column. This operation is actually a dot product between a matrix and a vector, so we can simply accomplish by calling the Pandas \"dot\" function.\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 3.5\n", "1 2.0\n", "2 5.0\n", "3 4.5\n", "4 5.0\n", "Name: rating, dtype: float64" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inputMovies['rating']" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Adventure 10.0\n", "Animation 8.0\n", "Children 5.5\n", "Comedy 13.5\n", "Fantasy 5.5\n", "Romance 0.0\n", "Drama 10.0\n", "Action 4.5\n", "Crime 5.0\n", "Thriller 5.0\n", "Horror 0.0\n", "Mystery 0.0\n", "Sci-Fi 4.5\n", "IMAX 0.0\n", "Documentary 0.0\n", "War 0.0\n", "Musical 0.0\n", "Western 0.0\n", "Film-Noir 0.0\n", "(no genres listed) 0.0\n", "dtype: float64" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Dot produt to get weights\n", "userProfile = userGenreTable.transpose().dot(inputMovies['rating'])\n", "#The user profile\n", "userProfile" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we have the weights for every of the user's preferences. This is known as the User Profile. Using this, we can recommend movies that satisfy the user's preferences.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's start by extracting the genre table from the original dataframe:\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/jupyterlab/conda/envs/python/lib/python3.7/site-packages/ipykernel_launcher.py:4: FutureWarning: In a future version of pandas all arguments of DataFrame.drop except for the argument 'labels' will be keyword-only\n", " after removing the cwd from sys.path.\n" ] }, { "data": { "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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
AdventureAnimationChildrenComedyFantasyRomanceDramaActionCrimeThrillerHorrorMysterySci-FiIMAXDocumentaryWarMusicalWesternFilm-Noir(no genres listed)
movieId
11.01.01.01.01.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
21.00.01.00.01.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
30.00.00.01.00.01.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
40.00.00.01.00.01.01.00.00.00.00.00.00.00.00.00.00.00.00.00.0
50.00.00.01.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", "
" ], "text/plain": [ " Adventure Animation Children Comedy Fantasy Romance Drama \\\n", "movieId \n", "1 1.0 1.0 1.0 1.0 1.0 0.0 0.0 \n", "2 1.0 0.0 1.0 0.0 1.0 0.0 0.0 \n", "3 0.0 0.0 0.0 1.0 0.0 1.0 0.0 \n", "4 0.0 0.0 0.0 1.0 0.0 1.0 1.0 \n", "5 0.0 0.0 0.0 1.0 0.0 0.0 0.0 \n", "\n", " Action Crime Thriller Horror Mystery Sci-Fi IMAX Documentary \\\n", "movieId \n", "1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", "\n", " War Musical Western Film-Noir (no genres listed) \n", "movieId \n", "1 0.0 0.0 0.0 0.0 0.0 \n", "2 0.0 0.0 0.0 0.0 0.0 \n", "3 0.0 0.0 0.0 0.0 0.0 \n", "4 0.0 0.0 0.0 0.0 0.0 \n", "5 0.0 0.0 0.0 0.0 0.0 " ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Now let's get the genres of every movie in our original dataframe\n", "genreTable = moviesWithGenres_df.set_index(moviesWithGenres_df['movieId'])\n", "#And drop the unnecessary information\n", "genreTable = genreTable.drop('movieId', 1).drop('title', 1).drop('genres', 1).drop('year', 1)\n", "genreTable.head()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(34208, 20)" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "genreTable.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the input's profile and the complete list of movies and their genres in hand, we're going to take the weighted average of every movie based on the input profile and recommend the top twenty movies that most satisfy it.\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "movieId\n", "1 0.594406\n", "2 0.293706\n", "3 0.188811\n", "4 0.328671\n", "5 0.188811\n", "dtype: float64" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Multiply the genres by the weights and then take the weighted average\n", "recommendationTable_df = ((genreTable*userProfile).sum(axis=1))/(userProfile.sum())\n", "recommendationTable_df.head()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "movieId\n", "5018 0.748252\n", "26093 0.734266\n", "27344 0.720280\n", "148775 0.685315\n", "6902 0.678322\n", "dtype: float64" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Sort our recommendations in descending order\n", "recommendationTable_df = recommendationTable_df.sort_values(ascending=False)\n", "#Just a peek at the values\n", "recommendationTable_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now here's the recommendation table!\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "scrolled": true }, "outputs": [ { "data": { "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", " \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", "
movieIdtitlegenresyear
664673Space Jam[Adventure, Animation, Children, Comedy, Fanta...1996
18241907Mulan[Adventure, Animation, Children, Comedy, Drama...1998
29022987Who Framed Roger Rabbit?[Adventure, Animation, Children, Comedy, Crime...1988
49235018Motorama[Adventure, Comedy, Crime, Drama, Fantasy, Mys...1991
67936902Interstate 60[Adventure, Comedy, Drama, Fantasy, Mystery, S...2002
860526093Wonderful World of the Brothers Grimm, The[Adventure, Animation, Children, Comedy, Drama...1962
878326340Twelve Tasks of Asterix, The (Les douze travau...[Action, Adventure, Animation, Children, Comed...1976
929627344Revolutionary Girl Utena: Adolescence of Utena...[Action, Adventure, Animation, Comedy, Drama, ...1999
982532031Robots[Adventure, Animation, Children, Comedy, Fanta...2005
1171651632Atlantis: Milo's Return[Action, Adventure, Animation, Children, Comed...2003
1175151939TMNT (Teenage Mutant Ninja Turtles)[Action, Adventure, Animation, Children, Comed...2007
1325064645The Wrecking Crew[Action, Adventure, Comedy, Crime, Drama, Thri...1968
1605581132Rubber[Action, Adventure, Comedy, Crime, Drama, Film...2010
1831291335Gruffalo, The[Adventure, Animation, Children, Comedy, Drama]2009
22778108540Ernest & Célestine (Ernest et Célestine)[Adventure, Animation, Children, Comedy, Drama...2012
22881108932The Lego Movie[Action, Adventure, Animation, Children, Comed...2014
25218117646Dragonheart 2: A New Beginning[Action, Adventure, Comedy, Drama, Fantasy, Th...2000
26442122787The 39 Steps[Action, Adventure, Comedy, Crime, Drama, Thri...1959
32854146305Princes and Princesses[Animation, Children, Comedy, Drama, Fantasy, ...2000
33509148775Wizards of Waverly Place: The Movie[Adventure, Children, Comedy, Drama, Fantasy, ...2009
\n", "
" ], "text/plain": [ " movieId title \\\n", "664 673 Space Jam \n", "1824 1907 Mulan \n", "2902 2987 Who Framed Roger Rabbit? \n", "4923 5018 Motorama \n", "6793 6902 Interstate 60 \n", "8605 26093 Wonderful World of the Brothers Grimm, The \n", "8783 26340 Twelve Tasks of Asterix, The (Les douze travau... \n", "9296 27344 Revolutionary Girl Utena: Adolescence of Utena... \n", "9825 32031 Robots \n", "11716 51632 Atlantis: Milo's Return \n", "11751 51939 TMNT (Teenage Mutant Ninja Turtles) \n", "13250 64645 The Wrecking Crew \n", "16055 81132 Rubber \n", "18312 91335 Gruffalo, The \n", "22778 108540 Ernest & Célestine (Ernest et Célestine) \n", "22881 108932 The Lego Movie \n", "25218 117646 Dragonheart 2: A New Beginning \n", "26442 122787 The 39 Steps \n", "32854 146305 Princes and Princesses \n", "33509 148775 Wizards of Waverly Place: The Movie \n", "\n", " genres year \n", "664 [Adventure, Animation, Children, Comedy, Fanta... 1996 \n", "1824 [Adventure, Animation, Children, Comedy, Drama... 1998 \n", "2902 [Adventure, Animation, Children, Comedy, Crime... 1988 \n", "4923 [Adventure, Comedy, Crime, Drama, Fantasy, Mys... 1991 \n", "6793 [Adventure, Comedy, Drama, Fantasy, Mystery, S... 2002 \n", "8605 [Adventure, Animation, Children, Comedy, Drama... 1962 \n", "8783 [Action, Adventure, Animation, Children, Comed... 1976 \n", "9296 [Action, Adventure, Animation, Comedy, Drama, ... 1999 \n", "9825 [Adventure, Animation, Children, Comedy, Fanta... 2005 \n", "11716 [Action, Adventure, Animation, Children, Comed... 2003 \n", "11751 [Action, Adventure, Animation, Children, Comed... 2007 \n", "13250 [Action, Adventure, Comedy, Crime, Drama, Thri... 1968 \n", "16055 [Action, Adventure, Comedy, Crime, Drama, Film... 2010 \n", "18312 [Adventure, Animation, Children, Comedy, Drama] 2009 \n", "22778 [Adventure, Animation, Children, Comedy, Drama... 2012 \n", "22881 [Action, Adventure, Animation, Children, Comed... 2014 \n", "25218 [Action, Adventure, Comedy, Drama, Fantasy, Th... 2000 \n", "26442 [Action, Adventure, Comedy, Crime, Drama, Thri... 1959 \n", "32854 [Animation, Children, Comedy, Drama, Fantasy, ... 2000 \n", "33509 [Adventure, Children, Comedy, Drama, Fantasy, ... 2009 " ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#The final recommendation table\n", "movies_df.loc[movies_df['movieId'].isin(recommendationTable_df.head(20).keys())]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Advantages and Disadvantages of Content-Based Filtering\n", "\n", "##### Advantages\n", "\n", "* Learns user's preferences\n", "* Highly personalized for the user\n", "\n", "##### Disadvantages\n", "\n", "* Doesn't take into account what others think of the item, so low quality item recommendations might happen\n", "* Extracting data is not always intuitive\n", "* Determining what characteristics of the item the user dislikes or likes is not always obvious\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Want to learn more?

\n", "\n", "IBM SPSS Modeler is a comprehensive analytics platform that has many machine learning algorithms. It has been designed to bring predictive intelligence to decisions made by individuals, by groups, by systems – by your enterprise as a whole. A free trial is available through this course, available here: SPSS Modeler\n", "\n", "Also, you can use Watson Studio to run these notebooks faster with bigger datasets. Watson Studio is IBM's leading cloud solution for data scientists, built by data scientists. With Jupyter notebooks, RStudio, Apache Spark and popular libraries pre-packaged in the cloud, Watson Studio enables data scientists to collaborate on their projects without having to install anything. Join the fast-growing community of Watson Studio users today with a free account at Watson Studio\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Thank you for completing this lab!\n", "\n", "## Author\n", "\n", "Saeed Aghabozorgi\n", "\n", "### Other Contributors\n", "\n", "Joseph Santarcangelo\n", "\n", "## Change Log\n", "\n", "| Date (YYYY-MM-DD) | Version | Changed By | Change Description |\n", "| ----------------- | ------- | ---------- | ---------------------------------- |\n", "| 2020-11-03 | 2.1 | Lakshmi | Updated URL of csv |\n", "| 2020-08-27 | 2.0 | Lavanya | Moved lab to course repo in GitLab |\n", "| | | | |\n", "| | | | |\n", "\n", "##

© IBM Corporation 2020. All rights reserved.

\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python", "language": "python", "name": "conda-env-python-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.12" }, "widgets": { "state": {}, "version": "1.1.2" } }, "nbformat": 4, "nbformat_minor": 4 }