Introduction

In [1]:
import sys
print(sys.version)
print(sys.executable)
3.12.10 | packaged by conda-forge | (main, Apr 10 2025, 22:08:16) [MSC v.1943 64 bit (AMD64)]
C:\Users\Sumedha\.conda\envs\py312\python.exe
In [2]:
import pandas as pd
import numpy as np
import scipy

%load_ext watermark
%watermark -iv
pandas: 2.2.3
scipy : 1.15.3
sys   : 3.12.10 | packaged by conda-forge | (main, Apr 10 2025, 22:08:16) [MSC v.1943 64 bit (AMD64)]
numpy : 1.26.4

Data

In [3]:
if 'google.colab' in sys.modules:
    !wget -O books.csv "https://raw.githubusercontent.com/zygmuntz/goodbooks-10k/master/books.csv"
    !wget -O ratings.csv "https://raw.githubusercontent.com/zygmuntz/goodbooks-10k/master/ratings.csv"
In [4]:
from pathlib import Path
if 'google.colab' not in sys.modules:
    path_data = Path.home() / 'github/Recommender_System/data/goodbooks_10k'
    books = pd.read_csv(path_data / 'books.csv').head(1000)
    ratings = pd.read_csv(path_data / 'ratings.csv').head(5000)

print(books.shape)
print(ratings.shape)

display(books.head(2))
display(ratings.head(2))
(1000, 23)
(5000, 3)
book_id goodreads_book_id best_book_id work_id books_count isbn isbn13 authors original_publication_year original_title ... ratings_count work_ratings_count work_text_reviews_count ratings_1 ratings_2 ratings_3 ratings_4 ratings_5 image_url small_image_url
0 1 2767052 2767052 2792775 272 439023483 9.780439e+12 Suzanne Collins 2008.0 The Hunger Games ... 4780653 4942365 155254 66715 127936 560092 1481305 2706317 https://images.gr-assets.com/books/1447303603m... https://images.gr-assets.com/books/1447303603s...
1 2 3 3 4640799 491 439554934 9.780440e+12 J.K. Rowling, Mary GrandPré 1997.0 Harry Potter and the Philosopher's Stone ... 4602479 4800065 75867 75504 101676 455024 1156318 3011543 https://images.gr-assets.com/books/1474154022m... https://images.gr-assets.com/books/1474154022s...

2 rows × 23 columns

user_id book_id rating
0 1 258 5
1 2 4081 4
In [5]:
print(books.shape)
print(books.columns)
books.head(2)
(1000, 23)
Index(['book_id', 'goodreads_book_id', 'best_book_id', 'work_id',
       'books_count', 'isbn', 'isbn13', 'authors', 'original_publication_year',
       'original_title', 'title', 'language_code', 'average_rating',
       'ratings_count', 'work_ratings_count', 'work_text_reviews_count',
       'ratings_1', 'ratings_2', 'ratings_3', 'ratings_4', 'ratings_5',
       'image_url', 'small_image_url'],
      dtype='object')
Out[5]:
book_id goodreads_book_id best_book_id work_id books_count isbn isbn13 authors original_publication_year original_title ... ratings_count work_ratings_count work_text_reviews_count ratings_1 ratings_2 ratings_3 ratings_4 ratings_5 image_url small_image_url
0 1 2767052 2767052 2792775 272 439023483 9.780439e+12 Suzanne Collins 2008.0 The Hunger Games ... 4780653 4942365 155254 66715 127936 560092 1481305 2706317 https://images.gr-assets.com/books/1447303603m... https://images.gr-assets.com/books/1447303603s...
1 2 3 3 4640799 491 439554934 9.780440e+12 J.K. Rowling, Mary GrandPré 1997.0 Harry Potter and the Philosopher's Stone ... 4602479 4800065 75867 75504 101676 455024 1156318 3011543 https://images.gr-assets.com/books/1474154022m... https://images.gr-assets.com/books/1474154022s...

2 rows × 23 columns

In [6]:
books_cols = ['book_id', 'authors', 'original_publication_year', 'title', 'average_rating']
books2 = books[books_cols]
books2.head(2)
Out[6]:
book_id authors original_publication_year title average_rating
0 1 Suzanne Collins 2008.0 The Hunger Games (The Hunger Games, #1) 4.34
1 2 J.K. Rowling, Mary GrandPré 1997.0 Harry Potter and the Sorcerer's Stone (Harry P... 4.44
In [7]:
print(ratings.shape)
print(ratings.columns)
ratings.head(2)
(5000, 3)
Index(['user_id', 'book_id', 'rating'], dtype='object')
Out[7]:
user_id book_id rating
0 1 258 5
1 2 4081 4
In [8]:
df = pd.merge(books, ratings, on="book_id", how="inner")
print(df.shape)
df.head(2)
(3031, 25)
Out[8]:
book_id goodreads_book_id best_book_id work_id books_count isbn isbn13 authors original_publication_year original_title ... work_text_reviews_count ratings_1 ratings_2 ratings_3 ratings_4 ratings_5 image_url small_image_url user_id rating
0 2 3 3 4640799 491 439554934 9.780440e+12 J.K. Rowling, Mary GrandPré 1997.0 Harry Potter and the Philosopher's Stone ... 75867 75504 101676 455024 1156318 3011543 https://images.gr-assets.com/books/1474154022m... https://images.gr-assets.com/books/1474154022s... 4 5
1 2 3 3 4640799 491 439554934 9.780440e+12 J.K. Rowling, Mary GrandPré 1997.0 Harry Potter and the Philosopher's Stone ... 75867 75504 101676 455024 1156318 3011543 https://images.gr-assets.com/books/1474154022m... https://images.gr-assets.com/books/1474154022s... 15 4

2 rows × 25 columns

Model based recommender engine: SVD

In [9]:
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import svds

def svd_recommender(user_id, ratings_df, books_df, n_recommendations=5):
    # Create user-item matrix
    user_item_matrix = ratings_df.pivot_table(index='user_id', columns='book_id', values='rating').fillna(0)

    # Convert to sparse matrix
    sparse_matrix = csr_matrix(user_item_matrix.values)

    # Normalize by user mean
    user_means = np.array(user_item_matrix.mean(axis=1))
    user_item_normalized = sparse_matrix - user_means.reshape(-1, 1)

    # Perform SVD
    U, sigma, Vt = svds(user_item_normalized, k=min(50, min(sparse_matrix.shape)-1))
    sigma = np.diag(sigma)

    # Reconstruct matrix
    predicted_ratings = np.dot(np.dot(U, sigma), Vt) + user_means.reshape(-1, 1)
    preds_df = pd.DataFrame(predicted_ratings,
                          columns=user_item_matrix.columns,
                          index=user_item_matrix.index)

    # Get user predictions
    user_preds = preds_df.loc[user_id].sort_values(ascending=False)

    # Remove already rated
    rated_books = ratings_df[ratings_df['user_id'] == user_id]['book_id']
    user_preds = user_preds.drop(rated_books, errors='ignore')

    # Get top recommendations
    top_book_ids = user_preds.head(n_recommendations).index

    # Ensure we're using the correct column name (either 'id' or 'book_id')
    book_id_col = 'book_id'
    df_out = books_df[books_df[book_id_col].isin(top_book_ids)]

    return df_out


user_id = ratings['user_id'].iloc[0]
df_out = svd_recommender(user_id, ratings, books)
df_out
Out[9]:
book_id goodreads_book_id best_book_id work_id books_count isbn isbn13 authors original_publication_year original_title ... ratings_count work_ratings_count work_text_reviews_count ratings_1 ratings_2 ratings_3 ratings_4 ratings_5 image_url small_image_url
93 94 320 320 3295655 555 60531045 9.780061e+12 Gabriel García Márquez, Gregory Rabassa 1967.0 Cien años de soledad ... 490565 575667 21671 27340 37646 87675 155809 267197 https://images.gr-assets.com/books/1327881361m... https://images.gr-assets.com/books/1327881361s...
120 121 7604 7604 1268631 370 NaN NaN Vladimir Nabokov, Craig Raine 1955.0 Lolita ... 469836 517990 18107 21091 37205 108664 165477 185553 https://images.gr-assets.com/books/1377756377m... https://images.gr-assets.com/books/1377756377s...
322 323 9717 9717 4489585 274 571224385 9.780571e+12 Milan Kundera, Michael Henry Heim 1984.0 Nesnesitelná lehkost bytí ... 205279 247980 10682 4894 12964 42199 84519 103404 https://images.gr-assets.com/books/1265401884m... https://images.gr-assets.com/books/1265401884s...
324 325 4473 4473 1734019 138 552135399 9.780552e+12 John Irving 1989.0 A Prayer for Owen Meany ... 226964 246452 11197 4679 9761 33669 76223 122120 https://images.gr-assets.com/books/1260470010m... https://images.gr-assets.com/books/1260470010s...
819 820 28921 28921 3333111 170 571225381 9.780571e+12 Kazuo Ishiguro 1989.0 The Remains of the Day ... 105892 121763 7455 1500 5028 21065 46702 47468 https://images.gr-assets.com/books/1327128714m... https://images.gr-assets.com/books/1327128714s...

5 rows × 23 columns

In [ ]: