Pada project sebelumnya kita telah membuat Simple Recommender System yang dibuat hanya dengan menggunakan formula Weighted Rating, yaitu mengurutkan score yang terdapat komponen average rating secara descending, kita dapat mengetahui (secara estimasi) film mana yang menurut para audience paling menarik.
Content Based Recommender System
Kali ini, kita akan membuat sistem rekomendasi yang menggunakan content/feature dari film/entitas tersebut, kemudian melakukan perhitungan terhadap kesamaannya satu dan yang lain sehingga ketika kita menunjuk ke satu film, kita akan mendapat beberapa film lain yang memiliki kesamaan dengan film tersebut. Hal ini biasa kita sebut sebagai Content Based Recommender System.
Sebagai contoh, berdasarkan kesamaan plot yang ada dan genre yang ada, ketika audience lebih menyukai film Narnia, maka sistem rekomendasi ini juga akan merekomendasikan film seperti Harry Potter atau The Lords of The Rings yang memiliki genre yang mirip.
Dataset
Dataset yang akan digunakan dalam pembahasan ini, meliputi:
movie_rating_df.csv
yang berisi informasi data film-film beserta rating nya.
https://dqlab-dataset.s3-ap-southeast-1.amazonaws.com/movie_rating_df.csvactor_name.csv
yang berisi data aktor yang memainkan film.
https://dqlab-dataset.s3-ap-southeast-1.amazonaws.com/actor_name.csvdirector_writers.csv
yang berisi data direktur dan penulis dari film yang ada.
https://dqlab-dataset.s3-ap-southeast-1.amazonaws.com/directors_writers.csv
Library
Library yang digunakan dalam project ini antara lain:
- numpy untuk perhitungan numerik dengan array atau matriks.
- pandas untuk manipulasi dan analisis data.
- sklearn untuk pemodelan data.
# Load library
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import warnings
warnings.filterwarnings('ignore')
Load Dataset
Melakukan pembacaan file csv ke dalam bentuk dataframe, kemudian melakukan preview data dan info data nya.
Table Movies
# Load file movie_rating.csv
movie_rating_df = pd.read_csv('data/movie_rating.csv')
# Print first five rows
movie_rating_df.head()
tconst | titleType | primaryTitle | originalTitle | isAdult | startYear | endYear | runtimeMinutes | genres | averageRating | numVotes | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | tt0000001 | short | Carmencita | Carmencita | 0 | 1894.0 | NaN | 1.0 | Documentary,Short | 5.6 | 1608 |
1 | tt0000002 | short | Le clown et ses chiens | Le clown et ses chiens | 0 | 1892.0 | NaN | 5.0 | Animation,Short | 6.0 | 197 |
2 | tt0000003 | short | Pauvre Pierrot | Pauvre Pierrot | 0 | 1892.0 | NaN | 4.0 | Animation,Comedy,Romance | 6.5 | 1285 |
3 | tt0000004 | short | Un bon bock | Un bon bock | 0 | 1892.0 | NaN | 12.0 | Animation,Short | 6.1 | 121 |
4 | tt0000005 | short | Blacksmith Scene | Blacksmith Scene | 0 | 1893.0 | NaN | 1.0 | Comedy,Short | 6.1 | 2050 |
# View info data movie_rating_df
movie_rating_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 751614 entries, 0 to 751613
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 tconst 751614 non-null object
1 titleType 751614 non-null object
2 primaryTitle 751614 non-null object
3 originalTitle 751614 non-null object
4 isAdult 751614 non-null int64
5 startYear 751614 non-null float64
6 endYear 16072 non-null float64
7 runtimeMinutes 751614 non-null float64
8 genres 486766 non-null object
9 averageRating 751614 non-null float64
10 numVotes 751614 non-null int64
dtypes: float64(4), int64(2), object(5)
memory usage: 63.1+ MB
Terlihat pada kolom endYear dan genres terdapat missing values karena jumlah data nya lebih sedikit dari keseluruhan data.
Table Actors
# Load file actor_name.csv
actor_df = pd.read_csv('data/actor_name.csv')
# Print five first rows
actor_df.head()
nconst | primaryName | birthYear | deathYear | primaryProfession | knownForTitles | |
---|---|---|---|---|---|---|
0 | nm1774132 | Nathan McLaughlin | 1973 | \N | special_effects,make_up_department | tt0417686,tt1713976,tt1891860,tt0454839 |
1 | nm10683464 | Bridge Andrew | \N | \N | actor | tt7718088 |
2 | nm1021485 | Brandon Fransvaag | \N | \N | miscellaneous | tt0168790 |
3 | nm6940929 | Erwin van der Lely | \N | \N | miscellaneous | tt4232168 |
4 | nm5764974 | Svetlana Shypitsyna | \N | \N | actress | tt3014168 |
# View info data actor_df
actor_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 nconst 1000 non-null object
1 primaryName 1000 non-null object
2 birthYear 1000 non-null object
3 deathYear 1000 non-null object
4 primaryProfession 891 non-null object
5 knownForTitles 1000 non-null object
dtypes: object(6)
memory usage: 47.0+ KB
Terlihat pada kolom birthYear dan deathYear terdapat nilai \\N
yang berarti bernilai NULL karena kesalahan pembacaan data. Lalu pada info data kolom primaryProfession juga terdapat missing values karena jumlah datanya lebih sedikit dibandingkan keseluruhan data.
Table Director_Writers
# Load file directors_writers.csv
director_writers_df = pd.read_csv('data/directors_writers.csv')
# Print first five rows
director_writers_df.head()
tconst | director_name | writer_name | |
---|---|---|---|
0 | tt0011414 | David Kirkland | John Emerson,Anita Loos |
1 | tt0011890 | Roy William Neill | Arthur F. Goodrich,Burns Mantle,Mary Murillo |
2 | tt0014341 | Buster Keaton,John G. Blystone | Jean C. Havez,Clyde Bruckman,Joseph A. Mitchell |
3 | tt0018054 | Cecil B. DeMille | Jeanie Macpherson |
4 | tt0024151 | James Cruze | Max Miller,Wells Root,Jack Jevne |
# View info data director_writers_df
director_writers_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 986 entries, 0 to 985
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 tconst 986 non-null object
1 director_name 986 non-null object
2 writer_name 986 non-null object
dtypes: object(3)
memory usage: 23.2+ KB
Terlihat bahwa tidak ditemukan missing values karena info jumlah data sesuai.
Update Dataframe
Melakukan manipulasi data agar siap diolah lebih lanjut sesuai kebutuhan.
Update Table Director_Writers
Kita akan mengubah kolom director_name dan writer_name dari string menjadi list.
# Change values string to list
director_writers_df['director_name'] = director_writers_df['director_name'].apply(lambda row: row.split(','))
director_writers_df['writer_name'] = director_writers_df['writer_name'].apply(lambda row: row.split(','))
# Print update rows data
director_writers_df.head()
tconst | director_name | writer_name | |
---|---|---|---|
0 | tt0011414 | [David Kirkland] | [John Emerson, Anita Loos] |
1 | tt0011890 | [Roy William Neill] | [Arthur F. Goodrich, Burns Mantle, Mary Murillo] |
2 | tt0014341 | [Buster Keaton, John G. Blystone] | [Jean C. Havez, Clyde Bruckman, Joseph A. Mitc... |
3 | tt0018054 | [Cecil B. DeMille] | [Jeanie Macpherson] |
4 | tt0024151 | [James Cruze] | [Max Miller, Wells Root, Jack Jevne] |
Update Table Actors
Kita hanya akan membutuhkan kolom nconst, primaryName, dan knownForTitles untuk mencocokkan aktor/aktris ini dengan film yang ada.
# Selecting columns
actor_df = actor_df[['nconst','primaryName','knownForTitles']]
# Print update rows data
actor_df.head()
nconst | primaryName | knownForTitles | |
---|---|---|---|
0 | nm1774132 | Nathan McLaughlin | tt0417686,tt1713976,tt1891860,tt0454839 |
1 | nm10683464 | Bridge Andrew | tt7718088 |
2 | nm1021485 | Brandon Fransvaag | tt0168790 |
3 | nm6940929 | Erwin van der Lely | tt4232168 |
4 | nm5764974 | Svetlana Shypitsyna | tt3014168 |
Kita ingin tahu mengenai variasi dari jumlah film yang dapat dibintangi oleh seorang aktor. Tentunya seorang aktor dapat membintangi lebih dari 1 film, bukan? maka akan diperlukan untuk membuat table yang mempunyai relasi 1-1 ke masing-masing title movie tersebut. Kita akan melakukan unnest terhadap table tersebut. Langkah yang dilakukan antara lain,
- Melakukan pengecekan variasi jumlah film yang dibintangi oleh aktor.
- Mengubah kolom knownForTitles menjadi list of list.
# Check counts
print(actor_df['knownForTitles'].apply(lambda x: len(x.split(','))).unique())
[4 1 2 3]
Terlihat jumlah isi data pada kolom knownForTitles paling banyak adalah 4.
# Change values column knownForTitles to list of list
actor_df['knownForTitles'] = actor_df['knownForTitles'].apply(lambda x: x.split(','))
# Print update rows data
actor_df.head()
nconst | primaryName | knownForTitles | |
---|---|---|---|
0 | nm1774132 | Nathan McLaughlin | [tt0417686, tt1713976, tt1891860, tt0454839] |
1 | nm10683464 | Bridge Andrew | [tt7718088] |
2 | nm1021485 | Brandon Fransvaag | [tt0168790] |
3 | nm6940929 | Erwin van der Lely | [tt4232168] |
4 | nm5764974 | Svetlana Shypitsyna | [tt3014168] |
Karena pada data sebelumnya dapat dilihat bahwa seorang aktor dapat membintangi 1 sampai 4 film, diperlukan untuk membuat table yang mempunyai relasi 1-1 dari aktor ke masing-masing judul film tersebut.
# Create empty list
new_df = []
for x in ['knownForTitles']:
# Repeat index
idx = actor_df.index.repeat(actor_df['knownForTitles'].str.len())
# Slicing values to create new rows
temp_df = pd.DataFrame({x: np.concatenate(actor_df[x].values)})
# Change values of index
temp_df.index = idx
# Append to new_df
new_df.append(temp_df)
# Concat the result
df_concat = pd.concat(new_df, axis=1)
# Join values with old data
unnested_df = df_concat.join(actor_df.drop(['knownForTitles'], 1), how='left')
# Convert into list
unnested_df = unnested_df[actor_df.columns.tolist()]
# Print update new rows
unnested_df.head()
nconst | primaryName | knownForTitles | |
---|---|---|---|
0 | nm1774132 | Nathan McLaughlin | tt0417686 |
0 | nm1774132 | Nathan McLaughlin | tt1713976 |
0 | nm1774132 | Nathan McLaughlin | tt1891860 |
0 | nm1774132 | Nathan McLaughlin | tt0454839 |
1 | nm10683464 | Bridge Andrew | tt7718088 |
Selanjutnya, kita akan mengelompokkan isi data primaryName menjadi list group berdasarkan kolom knownForTitles.
# Drop column nconst
unnested_df = unnested_df.drop(['nconst'], axis=1)
# Create empty list
new_df2 = []
for col in ['primaryName']:
# Grouping by column knownForTitles
temp_df2 = unnested_df.groupby(['knownForTitles'])[col].apply(list)
# Append to empty list
new_df2.append(temp_df2)
# Concat data
cast_df = pd.concat(new_df2, axis=1).reset_index()
# Rename columns
cast_df.columns = ['knownForTitles','cast_name']
cast_df.head()
knownForTitles | cast_name | |
---|---|---|
0 | tt0008125 | [Charles Harley] |
1 | tt0009706 | [Charles Harley] |
2 | tt0010304 | [Natalie Talmadge] |
3 | tt0011414 | [Natalie Talmadge] |
4 | tt0011890 | [Natalie Talmadge] |
Terlihat bahwa ada nilai dari kolom cast_name yang sama tetapi memiliki nilai knownForTitles yang berbeda. Hal membuktikan bahwa seorang aktor/aktris pernah membintangi film yang berbeda.
Join Tables
Tahap berikutnya adalah melakukan penggabungan tabel atau dataframe yang telah di update sebelumnya, yakni:
- Inner Join antara
cast_df
danmovie_rating_df
(field knownForTitles dan tconst) - Left Join antara
cast_movies_df
dengandirector_writer_df
(field tconst dan tconst)
# Join cast_df and movie_rating_df
cast_movies_df = pd.merge(cast_df, movie_rating_df, left_on='knownForTitles', right_on='tconst', how='inner')
# Join base_df and director_writers_df
base_df = pd.merge(cast_movies_df, director_writers_df, left_on='tconst', right_on='tconst', how='left')
# Print first five rows
base_df.head()
knownForTitles | cast_name | tconst | titleType | primaryTitle | originalTitle | isAdult | startYear | endYear | runtimeMinutes | genres | averageRating | numVotes | director_name | writer_name | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | tt0011414 | [Natalie Talmadge] | tt0011414 | movie | The Love Expert | The Love Expert | 0 | 1920.0 | NaN | 60.0 | Comedy,Romance | 4.9 | 136 | [David Kirkland] | [John Emerson, Anita Loos] |
1 | tt0011890 | [Natalie Talmadge] | tt0011890 | movie | Yes or No | Yes or No | 0 | 1920.0 | NaN | 72.0 | NaN | 6.3 | 7 | [Roy William Neill] | [Arthur F. Goodrich, Burns Mantle, Mary Murillo] |
2 | tt0014341 | [Natalie Talmadge] | tt0014341 | movie | Our Hospitality | Our Hospitality | 0 | 1923.0 | NaN | 65.0 | Comedy,Romance,Thriller | 7.8 | 9621 | [Buster Keaton, John G. Blystone] | [Jean C. Havez, Clyde Bruckman, Joseph A. Mitc... |
3 | tt0018054 | [Reeka Roberts] | tt0018054 | movie | The King of Kings | The King of Kings | 0 | 1927.0 | NaN | 155.0 | Biography,Drama,History | 7.3 | 1826 | [Cecil B. DeMille] | [Jeanie Macpherson] |
4 | tt0024151 | [James Hackett] | tt0024151 | movie | I Cover the Waterfront | I Cover the Waterfront | 0 | 1933.0 | NaN | 80.0 | Drama,Romance | 6.3 | 455 | [James Cruze] | [Max Miller, Wells Root, Jack Jevne] |
Data Cleaning
Setelah melakukan join table sebelumnya, sekarang hal yang akan kembali kita lakukan adalah melakukan cleaning pada data yang sudah dihasilkan.
# View info data base_df
base_df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1060 entries, 0 to 1059
Data columns (total 15 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 knownForTitles 1060 non-null object
1 cast_name 1060 non-null object
2 tconst 1060 non-null object
3 titleType 1060 non-null object
4 primaryTitle 1060 non-null object
5 originalTitle 1060 non-null object
6 isAdult 1060 non-null int64
7 startYear 1060 non-null float64
8 endYear 110 non-null float64
9 runtimeMinutes 1060 non-null float64
10 genres 745 non-null object
11 averageRating 1060 non-null float64
12 numVotes 1060 non-null int64
13 director_name 986 non-null object
14 writer_name 986 non-null object
dtypes: float64(4), int64(2), object(9)
memory usage: 132.5+ KB
# Check NULL columns
null_cols = base_df.columns[base_df.isnull().any()]
base_df[null_cols].isnull().sum()
endYear 950
genres 315
director_name 74
writer_name 74
dtype: int64
Dari hasil diatas diketahui bahwa:
- Kolom knownForTitles dan tconst memiliki nilai yang sama.
- Kolom endYear, genres, director_name, writer_name terdapat missing values.
Untuk mengatasi hal tersebut yang akan kita lakukan antara lain:
- Menghapus kolom knownForTitles
- Mengisi missing values dengan
Unknown
kecuali kolom endYear.
# Drop colomn knownForTitles
base_df = base_df.drop(['knownForTitles'], axis=1)
# Fill missing values column genres with 'Unknown'
base_df['genres'] = base_df['genres'].fillna('Unknown')
# Fill missing values column director_name and writer_name with 'Unknown'
base_df[['director_name','writer_name']] = base_df[['director_name','writer_name']].fillna('Unknown')
# Convert values column genres into list
base_df['genres'] = base_df['genres'].apply(lambda x: x.split(','))
Reformat Table
Hal selanjutnya yang akan kita lakukan adalah melakukan reformat pada table base_df
, seperti menhapus kolom yang tidak diperlukan dan mengubah nama kolom.
# Drop unnecessary columns
clean_df = base_df.drop(['tconst','isAdult','endYear','originalTitle'], axis=1)
# Ordering columns
clean_df = clean_df[['primaryTitle','titleType','startYear','runtimeMinutes',
'genres','averageRating','numVotes','cast_name',
'director_name','writer_name']]
# Rename columns
clean_df.columns = ['title','type','start','duration','genres','rating','votes',
'cast_name','director_name','writer_name']
# Print clean data
clean_df.head()
title | type | start | duration | genres | rating | votes | cast_name | director_name | writer_name | |
---|---|---|---|---|---|---|---|---|---|---|
0 | The Love Expert | movie | 1920.0 | 60.0 | [Comedy, Romance] | 4.9 | 136 | [Natalie Talmadge] | [David Kirkland] | [John Emerson, Anita Loos] |
1 | Yes or No | movie | 1920.0 | 72.0 | [Unknown] | 6.3 | 7 | [Natalie Talmadge] | [Roy William Neill] | [Arthur F. Goodrich, Burns Mantle, Mary Murillo] |
2 | Our Hospitality | movie | 1923.0 | 65.0 | [Comedy, Romance, Thriller] | 7.8 | 9621 | [Natalie Talmadge] | [Buster Keaton, John G. Blystone] | [Jean C. Havez, Clyde Bruckman, Joseph A. Mitc... |
3 | The King of Kings | movie | 1927.0 | 155.0 | [Biography, Drama, History] | 7.3 | 1826 | [Reeka Roberts] | [Cecil B. DeMille] | [Jeanie Macpherson] |
4 | I Cover the Waterfront | movie | 1933.0 | 80.0 | [Drama, Romance] | 6.3 | 455 | [James Hackett] | [James Cruze] | [Max Miller, Wells Root, Jack Jevne] |
Metadata
Untuk membuat sistem rekomendasi kita akan mengambil metadata yang dibutuhkan, yaitu kolom title, cast_name, genres, director_name, dan writer_name.
# Selecting metadata
feature_df = clean_df[['title', 'cast_name', 'genres', 'director_name','writer_name']]
# Print first five rows
feature_df.head()
title | cast_name | genres | director_name | writer_name | |
---|---|---|---|---|---|
0 | The Love Expert | [Natalie Talmadge] | [Comedy, Romance] | [David Kirkland] | [John Emerson, Anita Loos] |
1 | Yes or No | [Natalie Talmadge] | [Unknown] | [Roy William Neill] | [Arthur F. Goodrich, Burns Mantle, Mary Murillo] |
2 | Our Hospitality | [Natalie Talmadge] | [Comedy, Romance, Thriller] | [Buster Keaton, John G. Blystone] | [Jean C. Havez, Clyde Bruckman, Joseph A. Mitc... |
3 | The King of Kings | [Reeka Roberts] | [Biography, Drama, History] | [Cecil B. DeMille] | [Jeanie Macpherson] |
4 | I Cover the Waterfront | [James Hackett] | [Drama, Romance] | [James Cruze] | [Max Miller, Wells Root, Jack Jevne] |
Building Content Based Recommender System
Step 1
Membuat fungsi untuk menghilangkan spasi dari setiap baris dan setiap elemennya, dengan cara membuat lower case
terlebih dahulu kemudian mengecek apakah bertipe list atau string biasa.
# Step 1
def sanitize(x):
try:
# Check if values is list
if isinstance(x, list):
return [i.replace(' ','').lower() for i in x]
# If values is string
else:
return [x.replace(' ','').lower()]
except:
print(x)
# Column with list group
feature_cols = ['cast_name','genres','writer_name','director_name']
# Apply function sanitize
for col in feature_cols:
feature_df[col] = feature_df[col].apply(sanitize)
Step 2
Membuat fungsi untuk membuat metadata soup (menggabungkan semua feature menjadi 1 bagian kalimat) untuk setiap judulnya.
# Create function metadata soup
def soup_feature(x):
return ' '.join(x['cast_name']) + ' ' + ' '.join(x['genres']) + ' ' + ' '.join(x['director_name']) + ' ' + ' '.join(x['writer_name'])
# Create new column soup
feature_df['soup'] = feature_df.apply(soup_feature, axis=1)
# Check soup result
feature_df['soup'].head()
0 natalietalmadge comedy romance davidkirkland j...
1 natalietalmadge unknown roywilliamneill arthur...
2 natalietalmadge comedy romance thriller buster...
3 reekaroberts biography drama history cecilb.de...
4 jameshackett drama romance jamescruze maxmille...
Name: soup, dtype: object
Step 3
Menyiapkan CountVectorizer (stop_words = english)
dan fit
dengan soup yang kita buat sebelumnya. CountVectorizer adalah tipe paling sederhana dari vectorizer.
Sebagai contoh terdapat 3 text A, B, dan C, dimana text nya adalah
- A: The Sun is a star
- B: My Love is like a red, red rose
- C : Mary had a little lamb
Untuk mengkonversi teks berikut menjadi bentuk vector menggunakan CountVectorizer. Langkah-langkahnya adalah:
- Menghitung ukuran dari vocabulary. Vocabulary adalah jumlah dari kata unik yang ada dari text tersebut. Maka hasil vocabulary dari set ketiga text tersebut adalah: the, sun, is, a, star, my, love, like, red, rose, mary, had, little, lamb. Secara total, ukuran vocabulary adalah 14.
- Tidak include stop words (english), seperti as, is, a, the, dan sebagainya karena kata tersebut sudah umum sekali. Dengan mengeliminasi stop words, maka clean size vocabulary kita adalah like, little, lamb, love, mary, red, rose, sun, star (sorted alphabet ascending).
Dengan menggunakan CountVectorizer, maka hasil yang kita dapatkan adalah sebagai berikut:
- A : (0,0,0,0,0,0,0,1,1), terdiri atas sun:1, star:1
- B : (1,0,0,1,0,2,1,0,0), terdiri atas like:1, love:1, red:2, rose:1
- C : (0,1,1,0,1,0,0,0,0), terdiri atas little:1, lamb:1, mary:1
# Create CountVectorizer
count = CountVectorizer(stop_words='english')
count_matrix = count.fit_transform(feature_df['soup'])
print(count_matrix.shape)
(1060, 10026)
Step 4
Membuat model similarity antara count matrix. Pada langkah ini, kita akan menghitung score cosine similarity dari setiap pasangan judul (berdasarkan semua kombinasi pasangan yang ada, dengan kata lain kita akan membuat 675 x 675 matrix, dimana cell di kolom i dan j menunjukkan similarity score antara judul i dan j. Kita dapat dengan mudah melihat bahwa matrix ini simetris dan setiap elemen pada diagonal adalah 1, karena itu adalah similarity score dengan dirinya sendiri.
Kita akan menggunakan formula cosine similarity untuk membuat model. Score ini sangatlah berguna dan mudah untuk dihitung. Formula untuk perhitungan cosine similarity antara 2 text, adalah sebagai berikut:
$cosine(x,y)=\frac{x.y^T}{ | x | . | y | }$ |
Output yang didapat antara range -1 sampai 1. Score yang hampir mencapai 1 artinya kedua entitas tersebut sangatlah mirip sedangkan score yang hampir mencapai -1 artinya kedua entitas tersebut adalah beda.
# Using cosine similarity
cosine_sim = cosine_similarity(count_matrix, count_matrix)
# Print output
print(cosine_sim)
[[1. 0.15430335 0.35355339 ... 0. 0. 0.13608276]
[0.15430335 1. 0.10910895 ... 0. 0. 0. ]
[0.35355339 0.10910895 1. ... 0. 0.08703883 0.09622504]
...
[0. 0. 0. ... 1. 0. 0. ]
[0. 0. 0.08703883 ... 0. 1. 0.10050378]
[0.13608276 0. 0.09622504 ... 0. 0.10050378 1. ]]
Step 5
Selanjutnya yang harus dilakukan adalah reverse mapping dengan judul sebagai index nya.
# Create indices from column title
indices = pd.Series(feature_df.index, index=feature_df['title']).drop_duplicates()
# Create function recommender system
def content_based_recommender(title):
# Get index title
idx = indices[title]
# Convert array similarity score to list
sim_scores = list(enumerate(cosine_sim[idx]))
# Sort by highest score
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# Get similarity score (index 1 to 11)
sim_scores = sim_scores[1:11]
# Get index title based similarity score
movie_indices = [i[0] for i in sim_scores]
# Get rows with index title
return base_df.iloc[movie_indices]
# Apply function recommender system
content_based_recommender('The Love Expert')
cast_name | tconst | titleType | primaryTitle | originalTitle | isAdult | startYear | endYear | runtimeMinutes | genres | averageRating | numVotes | director_name | writer_name | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
344 | [Anat Dychtwald] | tt0237123 | tvSeries | Coupling | Coupling | 0 | 2000.0 | 2004.0 | 30.0 | [Comedy, Romance] | 8.5 | 41571 | [Martin Dennis] | [Steven Moffat] |
1052 | [Metin Namlisesli] | tt9124840 | movie | Organik Ask | Organik Ask | 0 | 2018.0 | NaN | 100.0 | [Comedy, Romance] | 4.3 | 89 | [Kamil Cetin] | [Volkan Girgin] |
2 | [Natalie Talmadge] | tt0014341 | movie | Our Hospitality | Our Hospitality | 0 | 1923.0 | NaN | 65.0 | [Comedy, Romance, Thriller] | 7.8 | 9621 | [Buster Keaton, John G. Blystone] | [Jean C. Havez, Clyde Bruckman, Joseph A. Mitc... |
24 | [Constance De Mattiazzi] | tt0043762 | movie | Lullaby of Broadway | Lullaby of Broadway | 0 | 1951.0 | NaN | 92.0 | [Comedy, Musical, Romance] | 6.8 | 893 | [David Butler] | [Earl Baldwin] |
398 | [Wai Chi Wong] | tt0308670 | movie | Oi ching bak min bau | Oi ching bak min bau | 0 | 2001.0 | NaN | 101.0 | [Comedy, Romance] | 6.8 | 47 | [Steven Lo] | [Canny Leung, Chi Shan Leung] |
441 | [Matthew Fuchs] | tt0396269 | movie | Wedding Crashers | Wedding Crashers | 0 | 2005.0 | NaN | 119.0 | [Comedy, Romance] | 6.9 | 323737 | [David Dobkin] | [Steve Faber, Bob Fisher] |
142 | [Harvey J. Alperin] | tt0094889 | movie | Cocktail | Cocktail | 0 | 1988.0 | NaN | 104.0 | [Comedy, Drama, Romance] | 5.9 | 76694 | [Roger Donaldson] | [Heywood Gould] |
325 | [Tim Horsely] | tt0198284 | movie | After Sex | After Sex | 0 | 2000.0 | NaN | 96.0 | [Comedy, Drama, Romance] | 4.4 | 753 | [Cameron Thor] | [Thomas M. Kostigen] |
345 | [Ngan-Ying Poon] | tt0237501 | movie | Ninth Happiness | Gau sing bou hei | 0 | 1998.0 | NaN | 86.0 | [Comedy, Musical, Romance] | 5.9 | 118 | [Clifton Ko] | [Raymond To] |
410 | [Catherine May] | tt0340109 | movie | Fast Food High | Fast Food High | 0 | 2003.0 | NaN | 92.0 | [Comedy, Drama, Romance] | 5.2 | 174 | [Nisha Ganatra] | [Tassie Cameron, Jackie May] |
Hasil diatas adalah rekomendasi 10 film terbaik berdasarkan kemiripan data film The Love Expert.