Made by Nathan W and Erwan C
This project was made for the C programming course at the Telecom Physique Strasbourg engineering school. The goal was to create a movie recommendation algorithm based on the Netflix Prize dataset.
-
✨ How to use ?
-
⚙️ Options Commands Details
-
🎥 Video Tutorial
-
🌳 Project Structure And Files
-
🛠️ Algorithm Explanation
- If GCC is not installed on your computer, run these commands, as the
makefilerequires GCC as the compiler.
$ sudo apt update
$ sudo apt install build-essential-
Go to Netflix Prize dataset and download
nf_prize_dataset.tar.gz -
Extract the downloaded file, and then extract
training_set.tar -
Fork this project and pull it locally with git
Compile the project using the following command :
$ makeExecute the following command to create the binary files created in order to save data with precise structure that are necessary to create the graph of recommendations :
$ ./main -o The program will then ask you to specify the path of the training_set folder.
This command will create :
-
bin_creation/movies.bin(2,4 Go) -
bin_creation/user.bin(2,4 Go)
Details about these binary files
🚨 Important Note : We highly recommend you to download the graphWholeDB.bin(1,3 Go) file to obtain the best results in a tiny amount of time.
Install git-lfs :
$ sudo apt-get install git-lfsDownload the graph :
$ git lfs pullHere's why with an example of process time on a pc with "good" specs for 2023 (CPU : AMD R9, GPU : RTX 3060, RAM : 32Go) :
| Ratings Considered | Time to update graph |
|---|---|
5 |
0.669s |
10 |
3.050s |
20 |
13.053s |
30 |
31.887s |
50 |
78.202s |
100 |
253.987s |
150 |
469.266s |
17652 (ALL) |
4410.273s |
Run the following command to recommend movies using base options :
$ ./main -r <id1,id2,...>You can run the following command to get detail about the different options or check the ⚙️ Options Command Details section
$ ./main -hLet's take a closer look about the different options you can add to the .\main
By the way the order isn't important
Provide a list of movies you like (separated by commas, or in a .txt file with a single movie each line) in order to get recommendation about these
Usage : -r <movieYouLikeid1,movieYouLikeid2,...> (or the path of a .txt)
Example : -r 1,2,3,4,5, -r ../moviesILikedReallyMuch.txt
Specify the number of movies you want to get recommended (by default 10)
Usage : -n <numberOfMoviesRecommended>
Example : -n 20
Path to the txt where the results will be saved if you want them to
Usage : -f <path.txt>
Example : -f /home/movieRecommended.txt
Ignore ratings whose year is greater than year (by default 2006)
📃 Note : ending of the database mid 2005 (so higher year will include every movies)
Usage : -l <year>
Example : -l 2002
Consider only ratings from clientsID given
Usage : -c <client1,client2...>
Example : -c 1,2,3,4,5
Consider only elite clients who have watched a minimum of <minMoviesReviewed> movies
Usage : -e <minmoviesreviewed>
Example : -e 100
Exclude ratings from reviewers <bad_reviewer1>, <bad_reviewer2>, etc.
Usage : -b <bad_reviewer1,bad_reviewer2,...>
Example : -b 1,2,3,4,5
Specify the global execution time of the algorithm
Usage : -t
Creates or recreates the .bin files : movies.bin, users.bin, graph.bin
📃 Pro Tip : combine this option the -z option to specify the number of ratings considered in the graph.bin creation this will influence the time duration of the algorithm.
Usage : -o
Specify the number of ratings considered for each user (by default 30)
Usage : -z <num>
Example : -z 50
Choose the algorithm you want to use (1 or 2) (explanation of the different algorithm)[]
Usage : -a <1 or 2>
Example : -a 2
Save the graph in a .bin file if the following options are not used : -l, -c, -e, -b, -s, -o
Usage : -g
When you use the -g option to create your graph.bin after using ./main -r "likedmovies" -z "number" -g, the next time you will call the function with the same number in the z option, the process time will only be < 1s, as the program detects the graph.bin was created for this amount of ratings considered.
For your information :
-
Average ratings number given by users : 208.25
-
Time taken to deserialize a downloaded/created updated graph instead of generating it each time we use the program : < 1s
graphWholeDB.bin is the serialized version of the updated graph with all the ratings considered. It means that to get a recommendation, it just takes < 1s (time to deserialize the graph)
Here is a demonstration of the program (with the graphWholeDB.bin file downloaded)
< Project >
|
|-- bin_creation/
| |-- movies.c # Parses the dataset/creates the movie table
| |-- movies.bin # Serialized movie table
| |-- user.c # Creates the user table
| |-- user.bin # Serialized user table
|
|-- util/
| |-- getmovietitle.c # Functions to get the title of a movie
| |-- progressbar.c # Progress bar implementation
| |-- maxadvices.c # Function to count the Lines of a file and get
| # the max amount of ratings a movie has
|
|-- algo/
| |-- graphcreation.c # All the functions to create/update the graph
| |-- graphWholeDB.bin # The updated graph of the whole dataset
|
|-- globalVarAndStructures.h # Contains all the structures/global variables
|
|-- ************************************************************************You can use Doxygen to generate documentation. Once installed, use :
$ make docThen open documentation/html/index.html in your browser
We used our knowledge about Graph Theory to create the recommendation algorithm. This logic will allow us to create ties between every movies. The "strength" of ties will be based on weight associated to the edges to use Graph Theory vocabulary. Using user reviews, we modify this weight in order to make the movies more or less distant. With
The Netflix Prize Dataset contains 2 main parts :
-
movie_titles.txtcontaining each 17,700 movies ids, the release year and the title of the movie, and a folder named training_set -
training_setfolder containing 17,770 text files, each one linked to a movie. And in files related to movies, there are the user reviews on the movies, with a user id, a star rating (between 1 and 5) and the date of the rating
You can get more information directly in the dataset.
In order to compute things on data we needed to gather all reviews stored in the training_set folder. We created files that we binarized in order to reuse as we wanted the pre-processed data that will follow precise structures :
bin_creation/movies.bin: a table of movies data, where the every 17,700 movie has stored its reviews and description. We used 2 structures :moviesandratings:
// movie structure
typedef struct movie_temp{
int id;
int release_date;
char title[300];
int nb_ratings;
rating* ratings;
} movie;// rating structure
typedef struct rating_temp{
int id_user;
int id_movie;
int year;
int day;
int month;
int star;
} rating;bin_creation/user.bin: a table of users data, where the every 480,189 users has stored their reviews. We reused theratingstructure that will be associated this time to anuserstructure :
// user structure
typedef struct user_temp{
int id;
int nb_ratings;
rating* ratings;
} user;As we mentioned in the General Idea section we needed to create a graph that represents ties between each movies so the movies will be the vertices and the ties will be the weight associated to the edges.
Every weights are initialized with
Weights are updated based on user reviews. For each user, if he noted the same way two movies, we reduced the weight between the two movies. Otherwise, if the note is different we increased the weight between them.
Nevertheless, between two good notes and two bad notes, even with a same way of noting, we wanted to increase the reduction for positive voting. We have the idea that two movie badly-noted are less close than two movies well-noted.
So we developped a two parameters function
The
And
Visual representation of the
We only needed
float weights[5][5] = {
{-0.25, -0.17, 0.0, 0.46, 1.0},
{-0.17, -0.62, -0.46, 0.0, 0.5},
{ 0.0, -0.46, -1.0, -0.5, 0.0},
{ 0.46, 0.0, -0.5, -1.0, -0.5},
{ 1.0, 0.5, 0.0, -0.5, -1.0}
};For each user, for all the different movie rating combination, the weight is updated; referred to this matrix.
Considering every review was too much time consuming for the graph creation in order to be used in the code, so we created a binary graph with the whole DB and we then binarized it.
Nevertheless, in order to recreate the graph in a more acceptable time, we chosen to consider only 10 to 50 first ratings per user, and the results were quite similar.
This is why when using different option that exclude some ratings (by excluding users, ratings with a certain date...), a graph recreation is needed, and the base ratings considered are 30 in our code but you can modify this value with the -z option.

