Load packages

Step 1: Exploratory Analysis

Load data

First, we need to start by loading the data.

# Location of the folder with movies & ratings data 
setwd("C:/Users/manon/Desktop/Machine Learning for Big Data/Project/Part 1/ml-25m")

list.files()
## [1] "genome-scores.csv" "genome-tags.csv"   "links.csv"        
## [4] "movies.csv"        "ratings.csv"       "README.txt"       
## [7] "tags.csv"
rm(list = ls())

movieData <- fread("movies.csv",
                   stringsAsFactors=FALSE)
ratingData <- fread("ratings.csv")

ICE

Any project starts by Inspecting, Clean and Explore the data.

Inspect

# General informations
glimpse(movieData)
## Rows: 62,423
## Columns: 3
## $ movieId <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,…
## $ title   <chr> "Toy Story (1995)", "Jumanji (1995)", "Grumpier Old Men (1995)…
## $ genres  <chr> "Adventure|Animation|Children|Comedy|Fantasy", "Adventure|Chil…
glimpse(ratingData)
## Rows: 25,000,095
## Columns: 4
## $ userId    <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ movieId   <int> 296, 306, 307, 665, 899, 1088, 1175, 1217, 1237, 1250, 1260,…
## $ rating    <dbl> 5.0, 3.5, 5.0, 5.0, 3.5, 4.0, 3.5, 3.5, 5.0, 4.0, 3.5, 4.0, …
## $ timestamp <int> 1147880044, 1147868817, 1147868828, 1147878820, 1147868510, …

Clean

cat("There are:", "\n",
    length(unique(ratingData$userId)), "unique userIds in ratingData", "\n",
    length(unique(ratingData$movieId)), "unique movieIds in ratingData", "\n",
    length(unique(movieData$movieId)), "unique movieIds in movieData", "\n",
    length(unique(movieData$title)), "unique titles in movieData")
## There are: 
##  162541 unique userIds in ratingData 
##  59047 unique movieIds in ratingData 
##  62423 unique movieIds in movieData 
##  62325 unique titles in movieData

There are different lengths of unique movieId in the movieData and ratingData. Therefore, we need to clean and remove duplicates.

# Look for any movie title duplicates
repeatMovies <- names(which(table(movieData$title) > 1))
removeRows <- integer()

# Check, remove and store all duplicates in a vector
for(i in repeatMovies){
  repeatMovieLoc <- which(movieData$title == i)
  tempGenre <- paste(movieData$genres[repeatMovieLoc],
                     collapse="|")
  tempGenre <- paste(unique(unlist(strsplit(tempGenre,
                                            split = "\\|")[[1]])),
                     collapse = "|")
  movieData$genres[repeatMovieLoc[1]] <- tempGenre

  repeatMovieIdLoc <- which(ratingData$movieId %in% movieData$movieId[repeatMovieLoc[-1]])
  ratingData$movieId[repeatMovieIdLoc] <- movieData$movieId[repeatMovieLoc[1]]
  removeRows <- c(removeRows,
                  repeatMovieLoc[-1])}

movieData$movieId[removeRows]
##  [1] 204982 151797 114240 198719 207249 144450 156686 181675 184893 203649
## [11] 198947 115777 191775 138656  85070 200376 199035 206674 193559 128862
## [21]  67459 171555 122940 144426 192405 144606 192243 196353 168634 194845
## [31] 144440 181655 180205 174651 143703 202599 147002 160868 180465 175857
## [41] 198507 163206 168866 118818 185925 168088 136820  65665 194570 140890
## [51] 143978 199556 124757 199346 144748 164568 192003 181751 191867 172427
## [61] 206125 181329 150310  26982 191713 164667 197425 121586 205761 200914
## [71] 180029 168358 199916 163246 160356 194652 169530 151375 154943 194078
## [81] 150776 193447 190881 190241 158763 138870 178403 206117 183459 206925
## [91] 173729 150732 144830 174449 148482 179333  64997 181385
movieData <- movieData[-removeRows,]
movieData[movieData$title == repeatMovies[1],]
##    movieId    title                            genres
## 1:   71057 9 (2009) Adventure|Animation|Sci-Fi|Comedy
movieData[movieData$title == repeatMovies[2],]
##    movieId             title                                genres
## 1:  136696 Absolution (2015) Action|Adventure|Crime|Thriller|Drama
# Let's remove non useful variables to increase the speed
rm(i,
   removeRows,
   repeatMovieIdLoc,
   repeatMovieLoc,
   repeatMovies,
   tempGenre)

# Take best rating if a userId rated a movie multiple times
ratingData_dt <- as.data.table(ratingData)
ratingData <- ratingData_dt[,
                            .(rating = max(rating)),
                            by = .(userId, movieId)]

uniqueN(ratingData,
        by = "movieId")
## [1] 58958
uniqueN(movieData,
        by = "movieId")
## [1] 62325
moviesNotInRatingData <- setdiff(unique(movieData[,
                                                  movieId]),
                                 unique(ratingData[,
                                                   movieId]))
rm(ratingData_dt)
# Check that the data is cleaned
str(movieData)
## Classes 'data.table' and 'data.frame':   62325 obs. of  3 variables:
##  $ movieId: int  1 2 3 4 5 6 7 8 9 10 ...
##  $ title  : chr  "Toy Story (1995)" "Jumanji (1995)" "Grumpier Old Men (1995)" "Waiting to Exhale (1995)" ...
##  $ genres : chr  "Adventure|Animation|Children|Comedy|Fantasy" "Adventure|Children|Fantasy" "Comedy|Romance" "Comedy|Drama|Romance" ...
##  - attr(*, ".internal.selfref")=<externalptr>
summary(movieData)    
##     movieId          title              genres         
##  Min.   :     1   Length:62325       Length:62325      
##  1st Qu.: 82061   Class :character   Class :character  
##  Median :137938   Mode  :character   Mode  :character  
##  Mean   :122150                                        
##  3rd Qu.:173169                                        
##  Max.   :209171
head(movieData)
##    movieId                              title
## 1:       1                   Toy Story (1995)
## 2:       2                     Jumanji (1995)
## 3:       3            Grumpier Old Men (1995)
## 4:       4           Waiting to Exhale (1995)
## 5:       5 Father of the Bride Part II (1995)
## 6:       6                        Heat (1995)
##                                         genres
## 1: Adventure|Animation|Children|Comedy|Fantasy
## 2:                  Adventure|Children|Fantasy
## 3:                              Comedy|Romance
## 4:                        Comedy|Drama|Romance
## 5:                                      Comedy
## 6:                       Action|Crime|Thriller
summary(ratingData)   
##      userId          movieId           rating     
##  Min.   :     1   Min.   :     1   Min.   :0.500  
##  1st Qu.: 40510   1st Qu.:  1196   1st Qu.:3.000  
##  Median : 80914   Median :  2947   Median :3.500  
##  Mean   : 81189   Mean   : 21384   Mean   :3.534  
##  3rd Qu.:121557   3rd Qu.:  8623   3rd Qu.:4.000  
##  Max.   :162541   Max.   :209171   Max.   :5.000
head(ratingData)
##    userId movieId rating
## 1:      1     296    5.0
## 2:      1     306    3.5
## 3:      1     307    5.0
## 4:      1     665    5.0
## 5:      1     899    3.5
## 6:      1    1088    4.0
# Check that there are no more duplicates
length(movieData) == length(unique(movieData))
## [1] TRUE
length(ratingData) == length(unique(ratingData))
## [1] TRUE
# Vector with unique users
users <- unique(ratingData$userId)
# Vector with unique movies
movies <- unique(ratingData$movieId)

# Create a new column
ratingData$row <- match(ratingData$userId,
                        users)
# Create a new row
ratingData$col <- match(ratingData$movieId,
                        movies)

# Create matrix
ratingData_sparse <- sparseMatrix(i = ratingData$row,
                                  j = ratingData$col,
                                  x = ratingData$rating,
                                  dimnames = list(users,
                                                  movies))

# Check matrix size
dim(ratingData_sparse)
## [1] 162541  58958

Visualisations

Build a histogram of show frequency (of ratings) of all movies

# Create plot
mean_rating_by_movies <- ratingData %>%
  group_by(movieId) %>%
  summarise(mean_rating = mean(rating))
  

ggplot(mean_rating_by_movies,
       aes(x = mean_rating))+ 
  geom_histogram(binwidth = 0.3,
                 colour = "#000000",
                 fill = "#004CA3")+ 
  theme_classic()+
  labs(title = "Distribution of average movie ratings by movie", 
       x = "Mean movie rating",
       y = "Count",
       caption = "MovieLens 25M Dataset. (2021, 2 mars). GroupLens. https://grouplens.org/datasets/movielens/25m/")

The distribution of average movie ratings grouped by movies is normally distributed.

Build a histogram of show frequency (of ratings) of all users

# Create plot
mean_rating_by_user <- ratingData %>%
  group_by(userId) %>%
  summarise(mean_rating = mean(rating))
  

ggplot(mean_rating_by_user,
       aes(x = mean_rating))+ 
  geom_histogram(binwidth = 0.3,
                 colour = "#000000",
                 fill = "#004CA3")+ 
  theme_classic()+
  labs(title = "Distribution of average movie ratings by user", 
       x = "Mean movie rating",
       y = "Count",
       caption = "MovieLens 25M Dataset. (2021, 2 mars). GroupLens. https://grouplens.org/datasets/movielens/25m/")

Once again, the distribution of average movie ratings grouped by movies is normally distributed.

Step 2: Data Engineering

ratingData_sparse <- as(ratingData_sparse,
                        "realRatingMatrix")

# Let's filter for users who rated at least 50 movies & for movies rated by at least 20 users
movie_ratings <- ratingData_sparse[rowCounts(ratingData_sparse) >= 50,
                                 colCounts(ratingData_sparse) >= 20]

# Check matrix size
dim(movie_ratings)
## [1] 102492  18424

There are now 102492 rows and 18424 columns.

Step 3: Model Build

The data is too large, so that we will take only 1% of it.

# Ensure reproducibility
set.seed(123)

# Number of rows in the movie_ratings matrix
n <- nrow(movie_ratings)

# Selecting 1% randomly
sample_index <- sample(1:n,
                       floor(n * 0.01))

# Extract the rows that corresponds to the selected index
sample_ratings <- movie_ratings[sample_index, ]

Item Based Collaborative Filtering

Sys.time()
## [1] "2023-02-17 12:31:56 GMT"
set.seed(1)

# Split the data into train and test sets
e <- evaluationScheme(sample_ratings,
                      method = "split",
                      train = 0.8,
                      given = -5)

# Train the model using the train dataset
recommen_model_IB <- Recommender(getData(e,
                                  "train"),
                          method = "IBCF", 
                          param = list(normalize = "center",
                                     method = "Cosine",
                                     k=350))

# Make predictions on the test dataset
prediction_IB <- predict(object = recommen_model_IB,
                      newdata = getData(e,
                                        "known"),
                      type = "ratings")

# Calculate RMSE, MAE and MSE using the raw dataset
calcPredictionAccuracy(x = prediction_IB,
                       data = getData(e,
                                      "unknown"))
##      RMSE       MSE       MAE 
## 1.2573427 1.5809108 0.9336016
Sys.time()
## [1] "2023-02-17 12:41:18 GMT"

User Based Collaborative Filtering

Sys.time()
## [1] "2023-02-17 12:41:18 GMT"
set.seed(1)

# Train the model using the train dataset
recommen_model_UB <- Recommender(getData(e,
                                  "train"),
                                 method = "UBCF",
                                 param = list(normalize = "center",
                                              method = "Cosine",
                                              nn = 25))

# Make predictions on the test dataset
prediction_UB <- predict(object = recommen_model_UB,
                      newdata = getData(e,
                                        "known"),
                      type = "ratings")

# Calculate RMSE, MAE and MSE using the raw dataset
calcPredictionAccuracy(x = prediction_UB,
                       data = getData(e,
                                      "unknown"))
##     RMSE      MSE      MAE 
## 1.121416 1.257573 0.861866
Sys.time()
## [1] "2023-02-17 12:41:39 GMT"

Model Based Collaborative Filtering using Matrix Factorisation

Sys.time()
## [1] "2023-02-17 12:41:39 GMT"
set.seed(1)

# Train the model using the train dataset
recommen_model_MF <- Recommender(getData(e,
                                         "train"),
                                 method = "LIBMF",
                                 param = list(normalize = "center",
                                              method = "Cosine",
                                              nn = 25))
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
# Make predictions on the test dataset
prediction_MF <- predict(object = recommen_model_MF,
                         newdata = getData(e,
                                           "known"),
                         type = "ratings")

# Calculate RMSE, MAE and MSE using the raw dataset
calcPredictionAccuracy(x = prediction_MF,
                       data = getData(e,
                                      "unknown"))
##      RMSE       MSE       MAE 
## 0.8612074 0.7416782 0.6585435
Sys.time()
## [1] "2023-02-17 12:41:41 GMT"

With the 3 models above, we have tried several values for nn = nearest neighbours; increasing its value increases the accuracy; however, this may lead to overfitting. Here we are normalizing the data by centring. To increase our accuracy, we could normalize the data by scaling the data to obtain a unit variance. Additionally, we can change the method for Pearson correlation instead of Cosine or Jaccard. Finally, we can also change the values for the train-test split to decrease the RMSE.

Step 4: Report on Performance of model and parameter tuning

Assess performance of the above models for different values of ‘m’ and ‘n’. Hint: Try m <- c(10, 20, 50, 100, 200); m <- n; modelNames <- c(IBCF, UBCF, LIBMF); expand.grid(m,n,modelNames)

# Set our variables with diff values
n <- c(10, 20, 50, 100, 200)
m <- c(10, 20, 50, 100, 200)
modelNames <- c('UBCF', 'LIBMF', 'IBCF')

Which model and combination of m & n provides the lowest RMSE?

Sys.time()
## [1] "2023-02-17 12:41:41 GMT"
# Create the loop
for (user_ratings in n)
{for (movie_views in m)
  # Select for specific m and n
  {movie_ratings_test <- movie_ratings[rowCounts(movie_ratings) >= user_ratings,
                                       colCounts(movie_ratings) >= movie_views]

  print(paste('n:',
              user_ratings))
  print(paste('m:',
              movie_views))
  
  # Number of rows
  rows <- nrow(movie_ratings_test)
  
  # Select 1% randomly
  sample_index_loop <- sample(1:rows,
                              floor(rows * 0.01))
  
  # Extract the rows that corresponds to the selected index
  sample_ratings_loop <- movie_ratings_test[sample_index_loop, ]
  
  
  # Split the data into train and test sets
  ex <- evaluationScheme(sample_ratings_loop,
                         method = "split",
                         train = 0.8,
                         given = -5)
  
  print("IBCF Model")
  
  # Train the model using the train dataset
  recommen_model_IB_loop <- Recommender(getData(ex,"train"),
                                        method = "IBCF",
                                        param = list(normalize = "center",
                                                   method = "Cosine",
                                                   k = 350))
  
  # Make predictions on the test dataset
  prediction_IB_loop <- predict(object = recommen_model_IB_loop,
                                newdata = getData(ex,"known"),
                                type = "ratings")

  ## Calculate RMSE using the raw dataset
  rmse_ibcf <- calcPredictionAccuracy(x = prediction_IB_loop,
                                      data = getData(ex,
                                                     "unknown"))[1]
  
  print(paste('IBCF RMSE:',
              rmse_ibcf))
  
  
  rm(recommen_model_IB_loop,
     prediction_IB_loop)
  cat("\n")
  
  print("UBCF Model")
  
  # Train the model using the train dataset
  recommen_model_UB_loop <- Recommender(getData(ex,
                                                "train"),
                                        method = "UBCF",
                                        param = list(normalize = "center",
                                                     method="Cosine",
                                                     nn=25))
  
  # Make predictions on the test dataset
  prediction_UB_loop <- predict(object = recommen_model_UB_loop,
                                newdata = getData(ex,
                                                  "known"),
                                type = "ratings")
  
  # Calculate RMSE using the raw dataset
  rmse_ubcf <- calcPredictionAccuracy(x = prediction_UB_loop,
                                      data = getData(ex,
                                                     "unknown"))[1]
  
  print(paste('UBCF RMSE:',
              rmse_ubcf))
  
  cat("\n")
  rm(recommen_model_UB_loop,
     prediction_UB_loop)
  
  print("LIBMF Model")
  
  # Train the model using the train dataset
  recommen_model_MF_loop <- Recommender(getData(ex,"train"),
                                        method = "LIBMF",
                                        param = list(normalize = "center",
                                                     method = "Cosine",
                                                     nn = 25))

  # Make predictions on the test dataset
  prediction_MF_loop <- predict(object = recommen_model_MF_loop,
                                newdata = getData(ex,"known"),
                                type = "ratings")

  # Calculate RMSE using the raw dataset
  libmf_rmse <- calcPredictionAccuracy(x = prediction_MF_loop,
                                       data = getData(ex,
                                                      "unknown"))[1]
  
  print(paste("LIBMF RMSE:",
              libmf_rmse))
  
  cat("\n")
  
  rm(recommen_model_MF_loop,
     prediction_MF_loop,
     rmse_ibcf,
     rmse_ubcf,
     libmf_rmse)}}
## [1] "n: 10"
## [1] "m: 10"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.1882230538876"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.16140240485439"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.854793025904336"
## 
## [1] "n: 10"
## [1] "m: 20"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.12306800917008"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.09941323681204"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.898782735482257"
## 
## [1] "n: 10"
## [1] "m: 50"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.26706970295878"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.05388044135557"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.845678500495641"
## 
## [1] "n: 10"
## [1] "m: 100"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.30877184067928"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.18413587604366"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.915089322060591"
## 
## [1] "n: 10"
## [1] "m: 200"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.24609067026213"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.15777883757339"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.858692078892004"
## 
## [1] "n: 20"
## [1] "m: 10"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.19983154537529"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.03584898163918"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.864442827195303"
## 
## [1] "n: 20"
## [1] "m: 20"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.31985577318174"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.14406439188789"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.93107012816652"
## 
## [1] "n: 20"
## [1] "m: 50"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.32538275106031"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.17527868111311"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.953090096816827"
## 
## [1] "n: 20"
## [1] "m: 100"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.06577308393388"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.10704291439767"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.896373837116283"
## 
## [1] "n: 20"
## [1] "m: 200"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.40081212119729"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.22079208401"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.88476687080873"
## 
## [1] "n: 50"
## [1] "m: 10"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.12720116538251"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.07308416918533"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.833701053000709"
## 
## [1] "n: 50"
## [1] "m: 20"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.12911194716437"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.08464381404247"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.874747328618476"
## 
## [1] "n: 50"
## [1] "m: 50"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.25122381169581"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.09960350846892"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.839734870378786"
## 
## [1] "n: 50"
## [1] "m: 100"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.41044103336379"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.20620538567181"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.911031532375238"
## 
## [1] "n: 50"
## [1] "m: 200"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.19801342210762"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.12721624640745"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.852876367478503"
## 
## [1] "n: 100"
## [1] "m: 10"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.07152852705394"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.0157345748978"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.917809707604635"
## 
## [1] "n: 100"
## [1] "m: 20"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.3441288471845"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.01355555256172"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.871577283268878"
## 
## [1] "n: 100"
## [1] "m: 50"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.00120051906827"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 0.945797510194363"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.819297663799955"
## 
## [1] "n: 100"
## [1] "m: 100"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.19777423369073"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.03597237831968"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.84763621442214"
## 
## [1] "n: 100"
## [1] "m: 200"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.20827332077492"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 0.987362104505953"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.838107186610573"
## 
## [1] "n: 200"
## [1] "m: 10"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.37458474326731"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 0.966747970369552"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.928204058781772"
## 
## [1] "n: 200"
## [1] "m: 20"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.17610705515282"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 0.917256561566694"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.901185843070476"
## 
## [1] "n: 200"
## [1] "m: 50"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.14903199481146"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.00331422555645"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.884794723121913"
## 
## [1] "n: 200"
## [1] "m: 100"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.16351866540423"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 0.871344743628109"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.887013919933386"
## 
## [1] "n: 200"
## [1] "m: 200"
## [1] "IBCF Model"
## [1] "IBCF RMSE: 1.14222805625901"
## 
## [1] "UBCF Model"
## [1] "UBCF RMSE: 1.07493972562489"
## 
## [1] "LIBMF Model"
## Available parameter (with default values):
## dim   =  10
## costp_l2  =  0.01
## costq_l2  =  0.01
## nthread   =  1
## verbose   =  FALSE
## [1] "LIBMF RMSE: 0.946760444841742"
Sys.time()
## [1] "2023-02-17 15:17:56 GMT"

Thanks to our loop, the best RMSE is 0.819297663799955 for n = 100 and M = 50. We would get a better RMSE with a computer that can run the entire dataset instead of 1%. Another technique we could try to decrease our RMSE would be using a different similarity measure, such as Pearson correlation or Jaccard similarity. Finally, we could experiment with different values of k for IBCF, nn for UBCF and the number of factors for MF.