-
Notifications
You must be signed in to change notification settings - Fork 3
/
dicoding_book_recommender.py
255 lines (177 loc) · 8.48 KB
/
dicoding_book_recommender.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# -*- coding: utf-8 -*-
"""Dicoding_Book_Recommender.ipynb
Automatically generated by Colaboratory.
Original file is located at
https://colab.research.google.com/drive/1roNMLniuz0zNofFddalz2CED8K996-sT
# Acknowledgements
---
Dataset ini diambil dari [https://www.kaggle.com](https://www.kaggle.com/datasets/ruchi798/bookcrossing-dataset)
# Import Dataset from Kaggle
---
Import dataset terlebih dulu lalu di unzip
"""
# from google.colab import drive
# drive.mount('/content/drive')
# !mkdir ~/.kaggle
# !cp /content/drive/MyDrive/kaggle.json ~/.kaggle
# !chmod 600 ~/.kaggle/kaggle.json
# !kaggle datasets download -d ruchi798/bookcrossing-dataset
# !unzip bookcrossing-dataset.zip
"""# Import Library for Exploratory Data Analysis
---
Import library yang akan digunakan untuk data analisis, data visualisasi, data preprocessing dan modeling
"""
# Commented out IPython magic to ensure Python compatibility.
# library for data loading and data analysis
import pandas as pd
import numpy as np
# library for data visualization
from matplotlib import pyplot as plt
import seaborn as sns
# %matplotlib inline
"""# Data Loading"""
path = 'Books Data with Category Language and Summary/Preprocessed_data.csv'
df = pd.read_csv(path, index_col=[0])
df.head()
# check the shape of dataframe
print(f'The data has {df.shape[0]} records and {df.shape[1]} columns.')
"""# Exploratory Data Analysis
---
Dataset ini memiliki 1.031.175 baris data dan 18 kolom :
* user_id : merupakan id dari user
* location : merupakan lokasi user tinggal
* age : merupakan umur dari user
* isbn : merupakan kode pengidentifikasi buku
* rating : merupakan rating yang user berikan untuk buku
* book_title : merupakan judul dari buku
* book_author : merupakan penulis dari buku
* year_of_publication : merupakan tahun publikasi buku
* publisher : merupakan penerbit buku
* img_s/img_m/img_l : merupakan cover dari buku
* summary : merupakan sinopsis dari buku
* language : merupakan bahasa terjemahan buku
* category : merupakan kategori buku
* city : merupakan kota buku tersebut dibeli
* state : merupakan provinsi buku tersebut dibeli
* country : merupakan negara buku tersebut dibeli
### Mengecek data apakaha memiliki missing value atau tidak
"""
df.info()
"""Dapat kita lihat bahwa beberapa kolom di dataset memiliki jumlah yang berbeda. Hal ini mengindikasikan bahwa terdapat missing value pada data"""
print('Total missing value in dataframe:', df.isnull().sum().sum(), 'records')
col_with_missing = [col for col in df.columns if df[col].isnull().any()]
print('Column with missing value:', col_with_missing)
"""Seperti yang kita lihat bahwa kolom city, state, country memiliki missing value. Ada banyak cara dalam menangani missing value, namun pada kasus kali ini kita akan menghapus kolom karena tidak terlalu berpengaruh pada rekomendasi buku"""
df_no_missing = df.drop(col_with_missing, axis=1)
df_no_missing.head()
df_no_missing.info()
print('Total missing value in dataframe:', df_no_missing.isnull().sum().sum(), 'records')
"""Load the rating dataset"""
ratings_path = 'Book reviews/Book reviews/BX-Book-Ratings.csv'
ratings = pd.read_csv(ratings_path, encoding='unicode_escape', sep=';')
ratings.head()
# check the shape of dataframe
print(f'The data has {ratings.shape[0]} records and {ratings.shape[1]} columns.')
ratings.info()
print('Total missing value in dataframe:', df_no_missing.isnull().sum().sum(), 'records')
ratings.rename(columns={'ISBN': 'isbn'}, inplace=True)
ratings.head()
ratings_no_dup = ratings.drop_duplicates(['isbn'])
print(f'The data has {ratings_no_dup.shape[0]} records and {ratings_no_dup.shape[1]} columns.')
"""# Data Preparation
---
Kita akan menghapus banyak kolom pada data kali ini karena banyak kolom pada data tidak memberikan informasi yang relevan terhadap rekomendasi buku seperti user_id, age, rating, dsb.
"""
col_to_drop = ['user_id', 'location', 'age', 'rating', 'year_of_publication', 'img_s', 'img_m', 'img_l', 'Summary', 'Language']
df_dropped = df_no_missing.drop(col_to_drop, axis=1)
df_dropped.head()
# check the shape of dataframe
print(f'The data has {df_dropped.shape[0]} records and {df_dropped.shape[1]} columns.')
# dropping duplicate record
df_no_dup = df_dropped.drop_duplicates(['book_title'])
df_no_dup.head()
# check the shape of dataframe
print(f'The data has {df_no_dup.shape[0]} records and {df_no_dup.shape[1]} columns.')
"""Merge ratings dataframe with book dataframe"""
book_with_rating = df_no_dup.merge(ratings_no_dup, on='isbn').drop(['isbn', 'User-ID'], axis=1)
book_with_rating.head()
book_with_rating.info()
# check the shape of dataframe
print(f'The data has {book_with_rating.shape[0]} records and {book_with_rating.shape[1]} columns.')
"""Selanjutnya kita akan menghapus kategori yang memiliki jumlah buku dibawah 100 dan diatas 1000 agar mengurangi waktu dan ruang komputasi"""
cat_sorted = np.sort(book_with_rating['Category'].unique())
unused_cat = [cat for cat in cat_sorted if len(book_with_rating[book_with_rating['Category'] == cat]) < 100 or len(book_with_rating[book_with_rating['Category'] == cat]) > 1000]
clean_cat_df = book_with_rating.loc[~book_with_rating['Category'].isin(unused_cat)]
clean_cat_df.head()
# check the shape of dataframe
print(f'The data has {clean_cat_df.shape[0]} records and {clean_cat_df.shape[1]} columns.')
"""### Data Preprocessing
Karena data pada kolom category terbungkus dalam tanda kurung siku, maka data harus dibersihkan agar dapat diterima baik oleh model
"""
def clean_category(text):
text = re.sub(r'[\[\]]', '', text)
text = text.replace("'", '')
text = text.replace('"', '')
text = text.replace('.', '')
return text
import re
clean_cat_df['clean_category'] = clean_cat_df['Category'].apply(clean_category)
clean_cat_df.head()
"""Melihat apakah kategori sudah benar benar bersih dari tanda titik di akhir kategori"""
clean_cat_sort = np.sort(clean_cat_df['clean_category'].unique())
for cat in clean_cat_sort:
print(cat)
"""Lalu kita bisa memperbaikin tipografi pada kategori yang sama namun hanya penulisannya berbeda"""
df_clean = clean_cat_df.drop(['Category'], axis=1)
df_clean.head()
# check the shape of dataframe
print(f'The data has {df_clean.shape[0]} records and {df_clean.shape[1]} columns.')
for cat in clean_cat_sort:
print(cat, 'has', len(df_clean[df_clean['clean_category'] == cat]), 'records')
"""# Modeling
---
Untuk model kali ini kita akan menggunakan Content Based Filtering dimana tujuan model ini adalah mencari similarity antara buku
"""
from sklearn.feature_extraction.text import TfidfVectorizer
tf = TfidfVectorizer()
tfidf_matrix = tf.fit_transform(df_clean['clean_category'])
from sklearn.metrics.pairwise import cosine_similarity
# Menghitung cosine similarity pada matrix tf-idf
cosine_sim = cosine_similarity(tfidf_matrix)
cosine_sim_df = pd.DataFrame(cosine_sim, index=df_clean['book_title'], columns=df_clean['book_title'])
cosine_sim_df.sample(5, axis=1).sample(10, axis=0)
def get_recommendations(book_title, similarity_data=cosine_sim_df, items=df_clean[['book_title', 'book_author', 'publisher', 'clean_category', 'Book-Rating']], k=10):
"""
Rekomendasi Buku berdasarkan kemiripan dataframe
Parameter:
---
book_title : tipe data string (str)
Judul Buku (index kemiripan dataframe)
similarity_data : tipe data pd.DataFrame (object)
Kesamaan dataframe, simetrik, dengan book_title sebagai
indeks dan kolom
items : tipe data pd.DataFrame (object)
Mengandung kedua nama dan fitur lainnya yang digunakan untuk mendefinisikan kemiripan
k : tipe data integer (int)
Banyaknya jumlah rekomendasi yang diberikan
---
Pada index ini, kita mengambil k dengan nilai similarity terbesar
pada index matrix yang diberikan (i).
"""
index = similarity_data.loc[:,book_title].to_numpy().argpartition(range(-1, -k, -1))
closest = similarity_data.columns[index[-1:-(k+2):-1]]
closest = closest.drop(book_title, errors='ignore')
return pd.DataFrame(closest).merge(items).head(k)
df_clean[df_clean['book_title'].eq('The Mummies of Urumchi')]
recommended_book = get_recommendations('The Mummies of Urumchi')
recommended_book.sort_values(['Book-Rating'], ascending=False)
"""# Evaluation
---
Untuk evaluasi content based filtering, kita dapat menggunakan precision@k dalam menentukan apakah rekomendasi relevan atau tidak
"""
k = 10
threshold = 5
book_ratings = recommended_book['Book-Rating'].values
book_relevances = book_ratings > threshold
precision = len(book_ratings[book_relevances]) / k
print(f'The precision of the recommendation system is {precision:.1%}')