Anime recommendation
Anime Recommendation Project ~
About Project:
Every streaming content has its own viewers and each content has it’s rating. Viewers leave some good ratings for the content if they like it. But where does it apply? Viewers can spend hours scrolling through hundreds, sometimes thousands of anime’s never finding an content they like. Business need to be provided suggestions based on their likes and needs in order to create a better streaming environment that boosts revenue and increases the time spent on a website.
@ Importing the libraries.
In [3]:
import pandas as pd
import numpy as np
@ Importing the data
In [4]:
rating_df = pd.read_csv(r"C:UsersSumanth ReddyOneDriveDocumentsanime-recommendation/animelist.csv",low_memory=False,usecols=["user_id", "anime_id", "rating"],nrows=50000000)
In [5]:
rating_df.head(-15)
Out[5]:
| user_id | anime_id | rating | |
|---|---|---|---|
| 0 | 0 | 67 | 9 |
| 1 | 0 | 6702 | 7 |
| 2 | 0 | 242 | 10 |
| 3 | 0 | 4898 | 0 |
| 4 | 0 | 21 | 10 |
| ... | ... | ... | ... |
| 49999980 | 162169 | 1690 | 0 |
| 49999981 | 162169 | 31478 | 0 |
| 49999982 | 162169 | 5356 | 0 |
| 49999983 | 162169 | 28999 | 0 |
| 49999984 | 162169 | 2167 | 0 |
49999985 rows × 3 columns
In [6]:
rating_df.shape
Out[6]:
(50000000, 3)
In [12]:
rating_df.isnull().sum()
Out[12]:
user_id 0 anime_id 0 rating 0 dtype: int64
In [13]:
rating_df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 50000000 entries, 0 to 49999999 Data columns (total 3 columns): # Column Dtype --- ------ ----- 0 user_id int64 1 anime_id int64 2 rating int64 dtypes: int64(3) memory usage: 1.1 GB
Collaborative Filtering
In [14]:

# User should rate atleast 400 animes
n_ratings = rating_df['user_id'].value_counts()
rating_df = rating_df[rating_df['user_id'].isin(n_ratings[n_ratings >= 400].index)].copy()
print('Users who rated more than 400 animes are :',len(rating_df))
Users who rated more than 400 animes are : 32651275
In [15]:
# Scaling between (0 , 1.0)
min_rating = min(rating_df['rating'])
max_rating = max(rating_df['rating'])
rating_df['rating'] = rating_df["rating"].apply(lambda x: (x - min_rating) / (max_rating - min_rating)).values.astype(np.float64)
In [16]:
# Average rating by the users
AvgRating = np.mean(rating_df['rating'])
print('Average rating by the user:', AvgRating)
. . .
In [17]:
# Removing Duplicated Rows from the data
duplicates = rating_df.duplicated()
if duplicates.sum() > 0:
print('> {} duplicates'.format(duplicates.sum()))
rating_df = rating_df[~duplicates]
print('> {} duplicates'.format(rating_df.duplicated().sum()))
> 1 duplicates > 0 duplicates

Top users who rated the animes
In [18]:
g = rating_df.groupby('user_id')['rating'].count()
top_users = g.dropna().sort_values(ascending=False)[:20]
top_ratings = rating_df.join(top_users,rsuffix='_r',how='inner',on='user_id')
g = rating_df.groupby('anime_id')['rating'].count()
top_animes = g.dropna().sort_values(ascending=False)[:20]
top_ratings = top_ratings.join(top_animes,rsuffix='_r',how='inner',on='anime_id')
pd.crosstab(top_ratings.user_id, top_ratings.anime_id, top_ratings.rating, aggfunc=np.sum)
Out[18]:
| anime_id | 226 | 1535 | 1575 | 2001 | 2167 | 4224 | 5081 | 5114 | 6547 | 6746 | 9253 | 9989 | 10620 | 11757 | 15809 | 16498 | 19815 | 20507 | 22319 | 30276 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| user_id | ||||||||||||||||||||
| 11100 | 0.4 | 0.7 | 0.9 | 0.0 | 0.5 | 0.0 | 0.7 | 0.0 | 0.6 | 0.0 | 0.0 | 0.0 | 0.4 | 0.5 | 0.7 | 0.5 | 0.6 | 0.0 | 0.2 | 0.0 |
| 16869 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | NaN |
| 20807 | 1.0 | 1.0 | 1.0 | 1.0 | 0.8 | 1.0 | 0.9 | 1.0 | 1.0 | 1.0 | 1.0 | 0.9 | 1.0 | 1.0 | 0.8 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
| 22022 | 0.0 | 0.8 | 0.8 | 0.9 | 0.8 | 0.8 | 0.8 | 0.9 | 0.8 | 0.0 | 0.9 | 0.9 | 0.7 | 0.8 | 0.0 | 0.7 | 0.7 | 0.7 | 0.9 | 0.8 |
| 50485 | 0.0 | 0.6 | 1.0 | 1.0 | 0.9 | 1.0 | 1.0 | 1.0 | 1.0 | 0.9 | 1.0 | 0.9 | 0.6 | 1.0 | 1.0 | 0.9 | 1.0 | 1.0 | 1.0 | 0.8 |
| 61445 | 0.7 | 1.0 | 0.0 | 0.0 | 0.0 | 0.9 | 0.0 | 0.0 | 0.9 | 0.0 | 0.7 | 0.0 | 1.0 | 0.8 | 0.0 | 1.0 | 1.0 | 0.0 | 1.0 | 0.9 |
| 63900 | 0.7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.9 | 0.0 | 0.0 | 0.0 | 0.8 | 0.0 | 0.0 | 0.0 | 0.8 | 0.0 | 0.8 | 0.8 | 0.8 |
| 68042 | 0.8 | 1.0 | 0.9 | 1.0 | 0.7 | 0.9 | 1.0 | 0.9 | 0.7 | 0.8 | 0.9 | 0.8 | 0.4 | 0.4 | 0.6 | 0.9 | 0.3 | 0.2 | 0.5 | 0.9 |
| 82327 | 0.4 | 0.9 | 0.7 | 0.7 | 0.3 | 0.4 | 0.8 | 0.4 | 0.6 | 0.5 | 1.0 | 0.9 | 0.8 | 0.3 | 0.7 | 0.9 | 0.3 | 0.7 | 0.6 | 0.8 |
| 85472 | 0.7 | 0.0 | 0.0 | 1.0 | 0.9 | 0.9 | 0.0 | 0.0 | 1.0 | 0.0 | 0.8 | 0.0 | 0.8 | 0.9 | 0.8 | 0.8 | 0.7 | 0.7 | 0.7 | 0.9 |
| 92529 | 0.9 | 0.9 | 1.0 | 1.0 | 0.0 | 0.8 | 0.0 | 0.9 | 0.9 | 0.0 | 1.0 | 0.0 | 0.8 | 0.9 | 0.8 | 0.9 | 0.9 | 0.8 | 0.8 | 1.0 |
| 107650 | 0.9 | 0.9 | 1.0 | 0.6 | 0.2 | 0.2 | 0.1 | 0.6 | 0.4 | 0.5 | 1.0 | 0.3 | 0.9 | 0.8 | 0.4 | 0.8 | 0.5 | 0.4 | 0.8 | 0.5 |
| 122341 | 0.2 | 0.3 | 0.2 | 0.7 | 0.4 | 0.3 | 0.3 | 0.8 | 0.5 | 0.6 | 0.7 | 0.3 | 0.2 | 0.1 | 0.3 | 0.1 | 0.4 | 0.4 | 0.2 | 0.6 |
| 128647 | 0.0 | 0.0 | 0.9 | 0.9 | 0.0 | 0.7 | 0.9 | 0.7 | 0.0 | 0.9 | 0.9 | 1.0 | 0.7 | 0.8 | 0.7 | 1.0 | 1.0 | 0.7 | 0.8 | 1.0 |
| 130106 | 0.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 |
| 131988 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 138375 | 0.9 | 0.9 | 0.9 | 1.0 | 0.8 | 0.8 | 0.8 | 0.6 | 0.9 | 0.8 | 0.6 | 0.5 | 0.0 | 0.7 | 0.0 | 0.8 | NaN | NaN | 0.7 | 0.9 |
| 140590 | 0.3 | 0.6 | 0.5 | 0.8 | 0.6 | 0.8 | 0.8 | 0.9 | 0.5 | 0.8 | 0.9 | 0.7 | 0.3 | 0.4 | 0.6 | 0.5 | 0.7 | 0.7 | 0.5 | 0.7 |
| 147331 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 156207 | 0.7 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.7 | 1.0 | 0.0 | 0.8 | 0.0 | 0.0 | 1.0 | 0.0 | 0.8 | 1.0 | 1.0 | 1.0 | 1.0 | 0.0 |
Data Preprocessing
In [19]:
# Now encoding the categorical data
user_ids = rating_df["user_id"].unique().tolist()
user2user_encoded = {x:i for i ,x in enumerate(user_ids)}
user_encoded2user = {x:i for i, x in enumerate(user_ids)}
rating_df["user"] = rating_df["user_id"].map(user2user_encoded)
n_users = len(user2user_encoded)
anime_ids = rating_df["anime_id"].unique().tolist()
anime2anime_encoded = {x: i for i, x in enumerate(anime_ids)}
anime_encoded2anime = {i: x for i, x in enumerate(anime_ids)}
rating_df['anime'] = rating_df['anime_id'].map(anime2anime_encoded)
n_animes = len(anime2anime_encoded)
print(" Number of users : {} ".format(n_users))
print('-'*30)
print(" Number of animes : {}".format(n_animes))
print('-'*30)
print(" Minimum rating : {} ".format(min(rating_df['rating'])))
print('-'*30)
print(" Max Rating : {}".format(max(rating_df['rating'])))
Number of users : 41946 ------------------------------ Number of animes : 17558 ------------------------------ Minimum rating : 0.0 ------------------------------ Max Rating : 1.0
Shuffling
In [20]:
rating_df = rating_df.sample(frac=1, random_state=69)
X = rating_df[['user','anime']].values
y = rating_df['rating']
Splitting
In [21]:
test_set_size = 30000 # 30k for the test set
train_indices = rating_df.shape[0] - test_set_size
X_train, X_test, y_train, y_test = (
X[:train_indices],
X[train_indices:],
y[:train_indices],
y[train_indices:],
)
print(' >> Train set ratings : {}'.format(len(y_train)))
print('-'*30)
print(' >> Test set ratings : {}'.format(len(y_test)))
>> Train set ratings : 32621274 ------------------------------ >> Test set ratings : 30000

Train and Test arrays
In [22]:
X_train_array = [X_train[:, 0], X_train[:, 1]]
X_test_array = [X_test[:, 0], X_test[:, 1]]
Building the model
In [23]:
import keras
from keras import layers
from keras.models import Model
from keras.optimizers import Adam
In [24]:
# Embedding layers
from keras.layers import Add, Activation, Lambda, BatchNormalization, Concatenate, Dropout, Input, Embedding, Dot, Reshape, Dense, Flatten
def RecommenderNet():
embedding_size = 128
user = Input(name = 'user', shape = [1])
user_embedding = Embedding(name = 'user_embedding',
input_dim = n_users,
output_dim = embedding_size)(user)
anime = Input(name = 'anime', shape = [1])
anime_embedding = Embedding(name = 'anime_embedding',
input_dim = n_animes,
output_dim = embedding_size)(anime)
#x = Concatenate()([user_embedding, anime_embedding])
x = Dot(name = 'dot_product', normalize = True, axes = 2)([user_embedding, anime_embedding])
x = Flatten()(x)
x = Dense(1, kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation("sigmoid")(x)
model = Model(inputs=[user, anime], outputs=x)
model.compile(loss='binary_crossentropy', metrics=["mae", "mse"], optimizer='Adam')
return model
model = RecommenderNet()
model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
user (InputLayer) [(None, 1)] 0 []
anime (InputLayer) [(None, 1)] 0 []
user_embedding (Embedding) (None, 1, 128) 5369088 ['user[0][0]']
anime_embedding (Embedding) (None, 1, 128) 2247424 ['anime[0][0]']
dot_product (Dot) (None, 1, 1) 0 ['user_embedding[0][0]',
'anime_embedding[0][0]']
flatten (Flatten) (None, 1) 0 ['dot_product[0][0]']
dense (Dense) (None, 1) 2 ['flatten[0][0]']
batch_normalization (BatchNorm (None, 1) 4 ['dense[0][0]']
alization)
activation (Activation) (None, 1) 0 ['batch_normalization[0][0]']
==================================================================================================
Total params: 7,616,518
Trainable params: 7,616,516
Non-trainable params: 2
__________________________________________________________________________________________________

In [25]:
# Callbacks
from tensorflow.keras.callbacks import Callback, ModelCheckpoint, LearningRateScheduler, TensorBoard, EarlyStopping, ReduceLROnPlateau
start_lr = 0.00001
min_lr = 0.00001
max_lr = 0.00005
batch_size = 30000
rampup_epochs = 5
sustain_epochs = 0
exp_decay = .8
def lrfn(epoch):
if epoch < rampup_epochs:
return (max_lr - start_lr)/rampup_epochs * epoch + start_lr
elif epoch < rampup_epochs + sustain_epochs:
return max_lr
else:
return (max_lr - min_lr) * exp_decay**(epoch-rampup_epochs-sustain_epochs) + min_lr
lr_callback = LearningRateScheduler(lambda epoch: lrfn(epoch), verbose=0)
checkpoint_filepath = './weights.h5'
model_checkpoints = ModelCheckpoint(filepath=checkpoint_filepath,
save_weights_only=True,
monitor='val_loss',
mode='min',
save_best_only=True)
early_stopping = EarlyStopping(patience = 3, monitor='val_loss',
mode='min', restore_best_weights=True)
my_callbacks = [
model_checkpoints,
lr_callback,
early_stopping,
]
In [26]:
# Model training
history = model.fit(
x=X_train_array,
y=y_train,
batch_size=batch_size,
epochs=20,
verbose=1,
validation_data=(X_test_array, y_test),
callbacks=my_callbacks
)
model.load_weights(checkpoint_filepath)
Epoch 1/20 1088/1088 [==============================] - 150s 135ms/step - loss: 0.7999 - mae: 0.3845 - mse: 0.1991 - val_loss: 0.7979 - val_mae: 0.3836 - val_mse: 0.1980 - lr: 1.0000e-05 Epoch 2/20 1088/1088 [==============================] - 147s 135ms/step - loss: 0.7929 - mae: 0.3825 - mse: 0.1965 - val_loss: 0.7916 - val_mae: 0.3823 - val_mse: 0.1957 - lr: 1.8000e-05 Epoch 3/20 1088/1088 [==============================] - 147s 135ms/step - loss: 0.7803 - mae: 0.3784 - mse: 0.1916 - val_loss: 0.7794 - val_mae: 0.3787 - val_mse: 0.1911 - lr: 2.6000e-05 Epoch 4/20 1088/1088 [==============================] - 147s 135ms/step - loss: 0.7510 - mae: 0.3665 - mse: 0.1796 - val_loss: 0.7353 - val_mae: 0.3591 - val_mse: 0.1728 - lr: 3.4000e-05 Epoch 5/20 1088/1088 [==============================] - 141s 129ms/step - loss: 0.6672 - mae: 0.3248 - mse: 0.1438 - val_loss: 0.6358 - val_mae: 0.3066 - val_mse: 0.1299 - lr: 4.2000e-05 Epoch 6/20 1088/1088 [==============================] - 146s 134ms/step - loss: 0.5992 - mae: 0.2867 - mse: 0.1142 - val_loss: 0.5938 - val_mae: 0.2825 - val_mse: 0.1117 - lr: 5.0000e-05 Epoch 7/20 1088/1088 [==============================] - 135s 124ms/step - loss: 0.5738 - mae: 0.2710 - mse: 0.1031 - val_loss: 0.5766 - val_mae: 0.2717 - val_mse: 0.1042 - lr: 4.2000e-05 Epoch 8/20 1088/1088 [==============================] - 135s 124ms/step - loss: 0.5594 - mae: 0.2613 - mse: 0.0970 - val_loss: 0.5653 - val_mae: 0.2640 - val_mse: 0.0995 - lr: 3.5600e-05 Epoch 9/20 1088/1088 [==============================] - 137s 126ms/step - loss: 0.5491 - mae: 0.2540 - mse: 0.0928 - val_loss: 0.5574 - val_mae: 0.2583 - val_mse: 0.0962 - lr: 3.0480e-05 Epoch 10/20 1088/1088 [==============================] - 137s 126ms/step - loss: 0.5414 - mae: 0.2484 - mse: 0.0897 - val_loss: 0.5515 - val_mae: 0.2539 - val_mse: 0.0939 - lr: 2.6384e-05 Epoch 11/20 1088/1088 [==============================] - 136s 125ms/step - loss: 0.5353 - mae: 0.2438 - mse: 0.0873 - val_loss: 0.5468 - val_mae: 0.2503 - val_mse: 0.0920 - lr: 2.3107e-05 Epoch 12/20 1088/1088 [==============================] - 137s 126ms/step - loss: 0.5301 - mae: 0.2400 - mse: 0.0852 - val_loss: 0.5429 - val_mae: 0.2474 - val_mse: 0.0906 - lr: 2.0486e-05 Epoch 13/20 1088/1088 [==============================] - 139s 128ms/step - loss: 0.5256 - mae: 0.2367 - mse: 0.0835 - val_loss: 0.5397 - val_mae: 0.2449 - val_mse: 0.0893 - lr: 1.8389e-05 Epoch 14/20 1088/1088 [==============================] - 136s 125ms/step - loss: 0.5218 - mae: 0.2339 - mse: 0.0821 - val_loss: 0.5369 - val_mae: 0.2428 - val_mse: 0.0883 - lr: 1.6711e-05 Epoch 15/20 1088/1088 [==============================] - 142s 130ms/step - loss: 0.5184 - mae: 0.2314 - mse: 0.0808 - val_loss: 0.5346 - val_mae: 0.2410 - val_mse: 0.0874 - lr: 1.5369e-05 Epoch 16/20 1088/1088 [==============================] - 138s 127ms/step - loss: 0.5155 - mae: 0.2292 - mse: 0.0797 - val_loss: 0.5325 - val_mae: 0.2393 - val_mse: 0.0867 - lr: 1.4295e-05 Epoch 17/20 1088/1088 [==============================] - 138s 127ms/step - loss: 0.5128 - mae: 0.2272 - mse: 0.0787 - val_loss: 0.5307 - val_mae: 0.2378 - val_mse: 0.0860 - lr: 1.3436e-05 Epoch 18/20 1088/1088 [==============================] - 137s 126ms/step - loss: 0.5104 - mae: 0.2253 - mse: 0.0778 - val_loss: 0.5291 - val_mae: 0.2365 - val_mse: 0.0854 - lr: 1.2749e-05 Epoch 19/20 1088/1088 [==============================] - 137s 126ms/step - loss: 0.5082 - mae: 0.2237 - mse: 0.0770 - val_loss: 0.5276 - val_mae: 0.2353 - val_mse: 0.0849 - lr: 1.2199e-05 Epoch 20/20 1088/1088 [==============================] - 135s 124ms/step - loss: 0.5061 - mae: 0.2221 - mse: 0.0762 - val_loss: 0.5263 - val_mae: 0.2342 - val_mse: 0.0844 - lr: 1.1759e-05
In [29]:
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(history.history['loss'][0:-2])
plt.plot(history.history["val_loss"][0:-2])
plt.title("model loss")
plt.ylabel("loss")
plt.xlabel("epoch")
plt.legend(["train","test"],loc="upper left")
plt.show()

Extracting weights from model
In [30]:
def extract_weights(name,model):
weight_layer = model.get_layer(name)
weights = weight_layer.get_weights()[0]
weights = weights / np.linalg.norm(weights, axis = 1).reshape((-1,1))
return weights
anime_weights = extract_weights('anime_embedding',model)
user_weights = extract_weights('user_embedding',model)
Anime Meta data
In [27]:
df = pd.read_csv(r'C:/Users/Sumanth Reddy/OneDrive/Documents/anime-recommendation/anime.csv.zip')
df = df.replace("Unknown",np.nan)
In [31]:
df.head()
Out[31]:
| MAL_ID | Name | Score | Genders | English name | Japanese name | Type | Episodes | Aired | Premiered | ... | Score-10 | Score-9 | Score-8 | Score-7 | Score-6 | Score-5 | Score-4 | Score-3 | Score-2 | Score-1 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Cowboy Bebop | 8.78 | Action, Adventure, Comedy, Drama, Sci-Fi, Space | Cowboy Bebop | カウボーイビバップ | TV | 26 | Apr 3, 1998 to Apr 24, 1999 | Spring 1998 | ... | 229170.0 | 182126.0 | 131625.0 | 62330.0 | 20688.0 | 8904.0 | 3184.0 | 1357.0 | 741.0 | 1580.0 |
| 1 | 5 | Cowboy Bebop: Tengoku no Tobira | 8.39 | Action, Drama, Mystery, Sci-Fi, Space | Cowboy Bebop:The Movie | カウボーイビバップ 天国の扉 | Movie | 1 | Sep 1, 2001 | NaN | ... | 30043.0 | 49201.0 | 49505.0 | 22632.0 | 5805.0 | 1877.0 | 577.0 | 221.0 | 109.0 | 379.0 |
| 2 | 6 | Trigun | 8.24 | Action, Sci-Fi, Adventure, Comedy, Drama, Shounen | Trigun | トライガン | TV | 26 | Apr 1, 1998 to Sep 30, 1998 | Spring 1998 | ... | 50229.0 | 75651.0 | 86142.0 | 49432.0 | 15376.0 | 5838.0 | 1965.0 | 664.0 | 316.0 | 533.0 |
| 3 | 7 | Witch Hunter Robin | 7.27 | Action, Mystery, Police, Supernatural, Drama, ... | Witch Hunter Robin | Witch Hunter ROBIN (ウイッチハンターロビン) | TV | 26 | Jul 2, 2002 to Dec 24, 2002 | Summer 2002 | ... | 2182.0 | 4806.0 | 10128.0 | 11618.0 | 5709.0 | 2920.0 | 1083.0 | 353.0 | 164.0 | 131.0 |
| 4 | 8 | Bouken Ou Beet | 6.98 | Adventure, Fantasy, Shounen, Supernatural | Beet the Vandel Buster | 冒険王ビィト | TV | 52 | Sep 30, 2004 to Sep 29, 2005 | Fall 2004 | ... | 312.0 | 529.0 | 1242.0 | 1713.0 | 1068.0 | 634.0 | 265.0 | 83.0 | 50.0 | 27.0 |
5 rows × 35 columns
In [32]:
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 17562 entries, 0 to 17561 Data columns (total 35 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 MAL_ID 17562 non-null int64 1 Name 17562 non-null object 2 Score 12421 non-null object 3 Genders 17499 non-null object 4 English name 6997 non-null object 5 Japanese name 17514 non-null object 6 Type 17525 non-null object 7 Episodes 17046 non-null object 8 Aired 17253 non-null object 9 Premiered 4745 non-null object 10 Producers 9768 non-null object 11 Licensors 3946 non-null object 12 Studios 10483 non-null object 13 Source 13995 non-null object 14 Duration 17007 non-null object 15 Rating 16874 non-null object 16 Ranked 15800 non-null object 17 Popularity 17562 non-null int64 18 Members 17562 non-null int64 19 Favorites 17562 non-null int64 20 Watching 17562 non-null int64 21 Completed 17562 non-null int64 22 On-Hold 17562 non-null int64 23 Dropped 17562 non-null int64 24 Plan to Watch 17562 non-null int64 25 Score-10 17125 non-null object 26 Score-9 14395 non-null object 27 Score-8 16191 non-null object 28 Score-7 17059 non-null object 29 Score-6 17051 non-null object 30 Score-5 16978 non-null object 31 Score-4 16585 non-null object 32 Score-3 16255 non-null object 33 Score-2 15965 non-null object 34 Score-1 17103 non-null object dtypes: int64(9), object(26) memory usage: 4.7+ MB
In [34]:
df.columns
Out[34]:
Index(['MAL_ID', 'Name', 'Score', 'Genders', 'English name', 'Japanese name',
'Type', 'Episodes', 'Aired', 'Premiered', 'Producers', 'Licensors',
'Studios', 'Source', 'Duration', 'Rating', 'Ranked', 'Popularity',
'Members', 'Favorites', 'Watching', 'Completed', 'On-Hold', 'Dropped',
'Plan to Watch', 'Score-10', 'Score-9', 'Score-8', 'Score-7', 'Score-6',
'Score-5', 'Score-4', 'Score-3', 'Score-2', 'Score-1'],
dtype='object')
In [36]:
# Fixing the names
def getAnimeName(anime_id):
try:
name = df[df.anime_id == anime_id].eng_version.values[0]
if name is np.nan:
name = df[df.anime_id == anime_id].Name.values[0]
except:
print('Error')
return name
df['anime_id'] = df['MAL_ID']
df['eng_version'] = df['English name']
df["eng_version"] = df.anime_id.apply(lambda x:getAnimeName(x))
df.sort_values(by = ["Score"],
inplace=True,
ascending = False,
kind = 'quicksort',
na_position ='last')
df = df[["anime_id", "eng_version",
"Score", "Genders", "Episodes",
"Type", "Premiered", "Members"]]
In [37]:
def getAnimeFrame(anime):
if isinstance(anime, int):
return df[df.anime_id == anime]
if isinstance(anime, str):
return df[df.eng_version == anime]
Synopsis data
In [47]:
cols = ["MAL_ID", "Name", "Genders", "sypnopsis"]
sypnopsis_df = pd.read_csv(r'C:/Users/Sumanth Reddy/OneDrive/Documents/anime-recommendation/anime_with_synopsis.csv.zip', usecols=cols)
def getSypnopsis(anime):
if isinstance(anime, int):
return sypnopsis_df[sypnopsis_df.MAL_ID == anime].sypnopsis.values[0]
if isinstance(anime, str):
return sypnopsis_df[sypnopsis_df.Name == anime].sypnopsis.values[0]
Task 1 : Finding Similar Animes
(Item Based Recommendation )
In [48]:
#pd.reset_option('all')
pd.set_option("max_colwidth", None)
def find_similar_animes(name, n=10, return_dist=False, neg=False):
try:
index = getAnimeFrame(name).anime_id.values[0]
encoded_index = anime2anime_encoded.get(index)
weights = anime_weights
dists = np.dot(weights, weights[encoded_index])
sorted_dists = np.argsort(dists)
n = n + 1
if neg:
closest = sorted_dists[:n]
else:
closest = sorted_dists[-n:]
print('Animes closest to {} are : '.format(name))
if return_dist:
return dists, closest
rindex = df
SimilarityArr = []
for close in closest:
decoded_id = anime_encoded2anime.get(close)
sypnopsis = getSypnopsis(decoded_id)
anime_frame = getAnimeFrame(decoded_id)
anime_name = anime_frame.eng_version.values[0]
genre = anime_frame.Genders.values[0]
similarity = dists[close]
SimilarityArr.append({"anime_id": decoded_id, "name": anime_name,
"similarity": similarity,"genre": genre,
'sypnopsis': sypnopsis})
Frame = pd.DataFrame(SimilarityArr).sort_values(by="similarity", ascending=False)
return Frame[Frame.anime_id != index].drop(['anime_id'], axis=1)
except:
print('{}!, Not Found in Anime list'.format(name))
These are some of my favourite animes
and similar recommendations to them
@ Dragon Ball Z


In [49]:
find_similar_animes('Dragon Ball Z', n=5, neg=False)
Animes closest to Dragon Ball Z are :
Out[49]:
| name | similarity | genre | sypnopsis | |
|---|---|---|---|---|
| 4 | Dragon Ball | 0.948332 | Adventure, Comedy, Fantasy, Martial Arts, Shounen, Super Power | Gokuu Son is a young boy who lives in the woods all alone—that is, until a girl named Bulma runs into him in her search for a set of magical objects called the "Dragon Balls." Since the artifacts are said to grant one wish to whoever collects all seven, Bulma hopes to gather them and wish for a perfect boyfriend. Gokuu happens to be in possession of a dragon ball, but unfortunately for Bulma, he refuses to part ways with it, so she makes him a deal: he can tag along on her journey if he lets her borrow the dragon ball's power. With that, the two set off on the journey of a lifetime. They don't go on the journey alone. On the way, they meet the old Muten-Roshi and wannabe disciple Kuririn, with whom Gokuu trains to become a stronger martial artist for the upcoming World Martial Arts Tournament. However, it's not all fun and games; the ability to make any wish come true is a powerful one, and there are others who would do much worse than just wishing for a boyfriend. To stop those who would try to abuse the legendary power, they train to become stronger fighters, using their newfound strength to help the people around them along the way. |
| 3 | Naruto | 0.915158 | Action, Adventure, Comedy, Super Power, Martial Arts, Shounen | oments prior to Naruto Uzumaki's birth, a huge demon known as the Kyuubi, the Nine-Tailed Fox, attacked Konohagakure, the Hidden Leaf Village, and wreaked havoc. In order to put an end to the Kyuubi's rampage, the leader of the village, the Fourth Hokage, sacrificed his life and sealed the monstrous beast inside the newborn Naruto. Now, Naruto is a hyperactive and knuckle-headed ninja still living in Konohagakure. Shunned because of the Kyuubi inside him, Naruto struggles to find his place in the village, while his burning desire to become the Hokage of Konohagakure leads him not only to some great new friends, but also some deadly foes. |
| 2 | Death Note | 0.885588 | Mystery, Police, Psychological, Supernatural, Thriller, Shounen | shinigami, as a god of death, can kill any person—provided they see their victim's face and write their victim's name in a notebook called a Death Note. One day, Ryuk, bored by the shinigami lifestyle and interested in seeing how a human would use a Death Note, drops one into the human realm. High school student and prodigy Light Yagami stumbles upon the Death Note and—since he deplores the state of the world—tests the deadly notebook by writing a criminal's name in it. When the criminal dies immediately following his experiment with the Death Note, Light is greatly surprised and quickly recognizes how devastating the power that has fallen into his hands could be. With this divine capability, Light decides to extinguish all criminals in order to build a new world where crime does not exist and people worship him as a god. Police, however, quickly discover that a serial killer is targeting criminals and, consequently, try to apprehend the culprit. To do this, the Japanese investigators count on the assistance of the best detective in the world: a young and eccentric man known only by the name of L. |
| 1 | Code Geass:Lelouch of the Rebellion R2 | 0.880383 | Action, Military, Sci-Fi, Super Power, Drama, Mecha | One year has passed since the Black Rebellion, a failed uprising against the Holy Britannian Empire led by the masked vigilante Zero, who is now missing. At a loss without their revolutionary leader, Area 11's resistance group—the Black Knights—find themselves too powerless to combat the brutality inflicted upon the Elevens by Britannia, which has increased significantly in order to crush any hope of a future revolt. Lelouch Lamperouge, having lost all memory of his double life, is living peacefully alongside his friends as a high school student at Ashford Academy. His former partner C.C., unable to accept this turn of events, takes it upon herself to remind him of his past purpose, hoping that the mastermind Zero will rise once again to finish what he started, in this thrilling conclusion to the series. |
| 0 | Attack on Titan | 0.880062 | Action, Military, Mystery, Super Power, Drama, Fantasy, Shounen | Centuries ago, mankind was slaughtered to near extinction by monstrous humanoid creatures called titans, forcing humans to hide in fear behind enormous concentric walls. What makes these giants truly terrifying is that their taste for human flesh is not born out of hunger but what appears to be out of pleasure. To ensure their survival, the remnants of humanity began living within defensive barriers, resulting in one hundred years without a single titan encounter. However, that fragile calm is soon shattered when a colossal titan manages to breach the supposedly impregnable outer wall, reigniting the fight for survival against the man-eating abominations. After witnessing a horrific personal loss at the hands of the invading creatures, Eren Yeager dedicates his life to their eradication by enlisting into the Survey Corps, an elite military unit that combats the merciless humanoids outside the protection of the walls. Based on Hajime Isayama's award-winning manga, Shingeki no Kyojin follows Eren, along with his adopted sister Mikasa Ackerman and his childhood friend Armin Arlert, as they join the brutal war against the titans and race to discover a way of defeating them before the last walls are breached. |
@. Your Name

In [53]:
find_similar_animes('Your Name.', n=5, neg=False)
Animes closest to Your Name. are :
Out[53]:
| name | similarity | genre | sypnopsis | |
|---|---|---|---|---|
| 4 | A Silent Voice | 0.980767 | Drama, School, Shounen | s a wild youth, elementary school student Shouya Ishida sought to beat boredom in the cruelest ways. When the deaf Shouko Nishimiya transfers into his class, Shouya and the rest of his class thoughtlessly bully her for fun. However, when her mother notifies the school, he is singled out and blamed for everything done to her. With Shouko transferring out of the school, Shouya is left at the mercy of his classmates. He is heartlessly ostracized all throughout elementary and middle school, while teachers turn a blind eye. Now in his third year of high school, Shouya is still plagued by his wrongdoings as a young boy. Sincerely regretting his past actions, he sets out on a journey of redemption: to meet Shouko once more and make amends. Koe no Katachi tells the heartwarming tale of Shouya's reunion with Shouko and his honest attempts to redeem himself, all while being continually haunted by the shadows of his past. |
| 3 | One Punch Man | 0.963683 | Action, Sci-Fi, Comedy, Parody, Super Power, Supernatural | The seemingly ordinary and unimpressive Saitama has a rather unique hobby: being a hero. In order to pursue his childhood dream, he trained relentlessly for three years—and lost all of his hair in the process. Now, Saitama is incredibly powerful, so much so that no enemy is able to defeat him in battle. In fact, all it takes to defeat evildoers with just one punch has led to an unexpected problem—he is no longer able to enjoy the thrill of battling and has become quite bored. This all changes with the arrival of Genos, a 19-year-old cyborg, who wishes to be Saitama's disciple after seeing what he is capable of. Genos proposes that the two join the Hero Association in order to become certified heroes that will be recognized for their positive contributions to society, and Saitama, shocked that no one knows who he is, quickly agrees. And thus begins the story of One Punch Man , an action-comedy that follows an eccentric individual who longs to fight strong enemies that can hopefully give him the excitement he once felt and just maybe, he'll become popular in the process. |
| 2 | Attack on Titan | 0.958043 | Action, Military, Mystery, Super Power, Drama, Fantasy, Shounen | Centuries ago, mankind was slaughtered to near extinction by monstrous humanoid creatures called titans, forcing humans to hide in fear behind enormous concentric walls. What makes these giants truly terrifying is that their taste for human flesh is not born out of hunger but what appears to be out of pleasure. To ensure their survival, the remnants of humanity began living within defensive barriers, resulting in one hundred years without a single titan encounter. However, that fragile calm is soon shattered when a colossal titan manages to breach the supposedly impregnable outer wall, reigniting the fight for survival against the man-eating abominations. After witnessing a horrific personal loss at the hands of the invading creatures, Eren Yeager dedicates his life to their eradication by enlisting into the Survey Corps, an elite military unit that combats the merciless humanoids outside the protection of the walls. Based on Hajime Isayama's award-winning manga, Shingeki no Kyojin follows Eren, along with his adopted sister Mikasa Ackerman and his childhood friend Armin Arlert, as they join the brutal war against the titans and race to discover a way of defeating them before the last walls are breached. |
| 1 | My Hero Academia 2 | 0.946817 | Action, Comedy, Super Power, School, Shounen | UA Academy, not even a violent attack can disrupt their most prestigious event: the school sports festival. Renowned across Japan, this festival is an opportunity for aspiring heroes to showcase their abilities, both to the public and potential recruiters. However, the path to glory is never easy, especially for Izuku Midoriya—whose quirk possesses great raw power but is also cripplingly inefficient. Pitted against his talented classmates, such as the fire and ice wielding Shouto Todoroki, Izuku must utilize his sharp wits and master his surroundings to achieve victory and prove to the world his worth. |
| 0 | My Hero Academia | 0.943160 | Action, Comedy, School, Shounen, Super Power | The appearance of "quirks," newly discovered super powers, has been steadily increasing over the years, with 80 percent of humanity possessing various abilities from manipulation of elements to shapeshifting. This leaves the remainder of the world completely powerless, and Izuku Midoriya is one such individual. Since he was a child, the ambitious middle schooler has wanted nothing more than to be a hero. Izuku's unfair fate leaves him admiring heroes and taking notes on them whenever he can. But it seems that his persistence has borne some fruit: Izuku meets the number one hero and his personal idol, All Might. All Might's quirk is a unique ability that can be inherited, and he has chosen Izuku to be his successor! Enduring many months of grueling training, Izuku enrolls in UA High, a prestigious high school famous for its excellent hero training program, and this year's freshmen look especially promising. With his bizarre but talented classmates and the looming threat of a villainous organization, Izuku will soon learn what it really means to be a hero. |
@. Sword Art Online


In [54]:
find_similar_animes('Sword Art Online', n=5, neg=False)
Animes closest to Sword Art Online are :
Out[54]:
| name | similarity | genre | sypnopsis | |
|---|---|---|---|---|
| 4 | No Game, No Life | 0.944005 | Game, Adventure, Comedy, Supernatural, Ecchi, Fantasy | No Game No Life is a surreal comedy that follows Sora and Shiro, shut-in NEET siblings and the online gamer duo behind the legendary username "Blank." They view the real world as just another lousy game; however, a strange e-mail challenging them to a chess match changes everything—the brother and sister are plunged into an otherworldly realm where they meet Tet, the God of Games. The mysterious god welcomes Sora and Shiro to Disboard, a world where all forms of conflict—from petty squabbles to the fate of whole countries—are settled not through war, but by way of high-stake games. This system works thanks to a fundamental rule wherein each party must wager something they deem to be of equal value to the other party's wager. In this strange land where the very idea of humanity is reduced to child's play, the indifferent genius gamer duo of Sora and Shiro have finally found a real reason to keep playing games: to unite the sixteen races of Disboard, defeat Tet, and become the gods of this new, gaming-is-everything world. |
| 3 | Tokyo Ghoul | 0.917430 | Action, Mystery, Horror, Psychological, Supernatural, Drama, Seinen | Tokyo has become a cruel and merciless city—a place where vicious creatures called "ghouls" exist alongside humans. The citizens of this once great metropolis live in constant fear of these bloodthirsty savages and their thirst for human flesh. However, the greatest threat these ghouls pose is their dangerous ability to masquerade as humans and blend in with society. Based on the best-selling supernatural horror manga by Sui Ishida, Tokyo Ghoul follows Ken Kaneki, a shy, bookish college student, who is instantly drawn to Rize Kamishiro, an avid reader like himself. However, Rize is not exactly who she seems, and this unfortunate meeting pushes Kaneki into the dark depths of the ghouls' inhuman world. In a twist of fate, Kaneki is saved by the enigmatic waitress Touka Kirishima, and thus begins his new, secret life as a half-ghoul/half-human who must find a way to integrate into both societies. |
| 2 | Angel Beats! | 0.903669 | Action, Comedy, Drama, School, Supernatural | Otonashi awakens only to learn he is dead. A rifle-toting girl named Yuri explains that they are in the afterlife, and Otonashi realizes the only thing he can remember about himself is his name. Yuri tells him that she leads the Shinda Sekai Sensen (Afterlife Battlefront) and wages war against a girl named Tenshi. Unable to believe Yuri's claims that Tenshi is evil, Otonashi attempts to speak with her, but the encounter doesn't go as he intended. Otonashi decides to join the SSS and battle Tenshi, but he finds himself oddly drawn to her. While trying to regain his memories and understand Tenshi, he gradually unravels the mysteries of the afterlife. |
| 1 | The Future Diary | 0.898363 | Action, Mystery, Psychological, Shounen, Supernatural, Thriller | onely high school student, Yukiteru Amano, spends his days writing a diary on his cellphone, while conversing with his two seemingly imaginary friends Deus Ex Machina, who is the god of time and space, and Murmur, the god's servant. Revealing himself to be an actual entity, Deus grants Yukiteru a "Random Diary," which shows highly descriptive entries based on the future and forces him into a bloody battle royale with 11 other holders of similarly powerful future diaries. With the last person standing designated as the new god of time and space, Yukiteru must find and kill the other 11 in order to survive. He reluctantly teams up with his obsessive stalker Yuno Gasai (who also possesses such a diary), and she takes it upon herself to ensure his safety. But there's more to the girl than meets the eye, as she might have other plans for her unrequited love... |
| 0 | High School of the Dead | 0.896227 | Action, Horror, Supernatural, Ecchi, Shounen | It happened suddenly: The dead began to rise and Japan was thrown into total chaos. As these monsters begin terrorizing a high school, Takashi Kimuro is forced to kill his best friend when he gets bitten and joins the ranks of the walking dead. Vowing to protect Rei Miyamoto, the girlfriend of the man he just executed, they narrowly escape their death trap of a school, only to be greeted with a society that has already fallen. Soon, Takashi and Rei band together with other students on a journey to find their family members and uncover what caused this overwhelming pandemic. Joining them is Saeko Busujima, the beautiful president of the Kendo Club; Kouta Hirano, an otaku with a fetish for firearms; Saya Takagi, the daughter of an influential politician; and Shizuka Marikawa, their hot school nurse. But will the combined strength of these individuals be enough to conquer this undead apocalypse? |
@. Black Cover
In [55]:
find_similar_animes('Black Clover', n=5, neg=False)
Animes closest to Black Clover are :
Out[55]:
| name | similarity | genre | sypnopsis | |
|---|---|---|---|---|
| 4 | Twin Star Exorcists | 0.560382 | Action, Supernatural, Romance, Fantasy, Shounen | agano, a parallel realm filled with monsters known as "Kegare," is a place where exorcists deal with all impurities. Benio Adashino is a prodigy exorcist who is recognized for her strength and is summoned to Tokyo by the Exorcist Union. On her way, she plummets into the arms of Rokuro Enmadou, a young exorcist with a troubled past. But the impurities of Magano do not rest. When these two exorcists witness a couple of children stolen by a Kegare, Benio rushes to save them, dragging Rokuro along with her into Magano. Engaged in a fight she is on the verge of being defeated in, Benio is saved by Rokuro, revealing himself capable of being her rival in talent. Sousei no Onmyouji tells the story of two talented exorcists who are destined to become the "Twin Star Exorcists" and the prophesised parents of the Miko—the reincarnation of Abe no Seimei—who will cleanse the world of all impurities. |
| 3 | Assassins Pride | 0.543194 | Fantasy | On the brink of extinction, mankind has downsized and now solely resides in the city-state of Flandore, living in cities encased by glass domes. Beyond the domes exist vicious lycanthropes who thrive in the darkness; among the citizens inside, a clear distinction between the nobility and commoners is in place. The blood of nobles enables them to utilize mana, granting them abilities that exceed human limits and greatly assist them in defeating lycanthropes. Already 13 years of age, noble Melida Angel has yet to manifest her mana, and attends an elite academy where she is mistreated for her lack thereof. In order to help her, Kufa Vampir is ordered by the Angel family to become Melida's tutor. While Kufa seems to be a mere mentor, an ulterior motive lurks behind his job—he is to assassinate her if he confirms that she does not possess mana. Kufa's investigation eventually leads him to determine he must eliminate Melida. However, Kufa is struck by her unwavering determination, spirit, and belief in herself when he witnesses her in a fight, choosing instead to offer a way she can manifest her magic. As Melida learns to use mana with the help of Kufa's teachings, Kufa forsakes his mission and jeopardizes everything to keep his discovery of Melida unknown to the Angel family and his own guild. However, both Kufa and Melida will soon realize that hiding their secret will not be the only challenge they face, as unforeseen trouble is waiting just around the corner. |
| 2 | One Punch Man 2nd Season | 0.541118 | Action, Sci-Fi, Comedy, Parody, Super Power, Supernatural | In the wake of defeating Boros and his mighty army, Saitama has returned to his unremarkable everyday life in Z-City. However, unbeknownst to him, the number of monsters appearing is still continuously on the rise, putting a strain on the Hero Association’s resources. Their top executives decide on the bold move of recruiting hoodlums in order to help in their battle. But during the first meeting with these potential newcomers, a mysterious man calling himself Garou makes his appearance. Claiming to be a monster, he starts mercilessly attacking the crowd. The mysterious Garou continues his rampage against the Hero Association, crushing every hero he encounters. He turns out to be the legendary martial artist Silverfang’s best former disciple and seems driven by unknown motives. Regardless, this beast of a man seems unstoppable. Intrigued by this puzzling new foe and with an insatiable thirst for money, Saitama decides to seize the opportunity and joins the interesting martial arts competition. As the tournament commences and Garou continues his rampage, a new great menace reveals itself, threatening the entire human world. Could this finally be the earth-shattering catastrophe predicted by the great seer Madame Shibabawa? |
| 1 | Boruto:Naruto Next Generations | 0.539181 | Action, Adventure, Super Power, Martial Arts, Shounen | Following the successful end of the Fourth Shinobi World War, Konohagakure has been enjoying a period of peace, prosperity, and extraordinary technological advancement. This is all due to the efforts of the Allied Shinobi Forces and the village's Seventh Hokage, Naruto Uzumaki. Now resembling a modern metropolis, Konohagakure has changed, particularly the life of a shinobi. Under the watchful eye of Naruto and his old comrades, a new generation of shinobi has stepped up to learn the ways of the ninja. Boruto Uzumaki is often the center of attention as the son of the Seventh Hokage. Despite having inherited Naruto's boisterous and stubborn demeanor, Boruto is considered a prodigy and is able to unleash his potential with the help of supportive friends and family. Unfortunately, this has only worsened his arrogance and his desire to surpass Naruto which, along with his father's busy lifestyle, has strained their relationship. However, a sinister force brewing within the village may threaten Boruto's carefree life. New friends and familiar faces join Boruto as a new story begins in Boruto: Naruto Next Generations . |
| 0 | Fire Force | 0.528236 | Action, Supernatural, Shounen | Spontaneous Human Combustion: a chaotic phenomenon that has plagued humanity for years, randomly transforming ordinary people into flaming, violent creatures known as Infernals. While Infernals make up the first-generation accounts of Human Combustion, the second and third generations became known as pyrokinetics—people gifted with the ability to manipulate and control their flames while remaining human. To combat the Infernal threat and discover the cause, the Tokyo Armed Forces, Fire Defense Agency, and Holy Church of Sol produced their answer: the Special Fire Force. Young and eager third-generation pyrokinetic Shinra Kusakabe, nicknamed Devil's Footprints for his explosive ability to ignite his feet at will, becomes a member of the lively Special Fire Force Company 8. Upholding the brigade's duty to extinguish the blazing Infernals and lay their souls to rest, Shinra is determined to become a hero who will save the lives of those threatened by the flame terror. However, this is not the hero's game Shinra imagined. The Fire Force is a fractured mess of feuding brigades, abnormal Infernal sightings are increasing all over Tokyo, and a shadowy group is claiming to have answers to the strange fire that caused the death of Shinra's family 12 years ago. Faced with many obstacles within and outside the Fire Force, Shinra fights to uncover the truth behind the burning mysteries that have kept him in the dark. |
Task 2 : Finding similar users
(User Bases Recommendation)
In [126]:
print(' || Picking up random user || ')
print('-'*30)
ratings_per_user = rating_df.groupby('user_id').size()
random_user = ratings_per_user[ratings_per_user < 500].sample(1, random_state=None).index[0]
print(' >> user_id:', random_user)
. . .
In [127]:
#pd.reset_option('all')
pd.set_option("max_colwidth", None)
def find_similar_users(item_input, n=10,return_dist=False, neg=False):
try:
index = item_input
encoded_index = user2user_encoded.get(index)
weights = user_weights
dists = np.dot(weights, weights[encoded_index])
sorted_dists = np.argsort(dists)
n = n + 1
if neg:
closest = sorted_dists[:n]
else:
closest = sorted_dists[-n:]
print('> users similar to # {}'.format(item_input))
if return_dist:
return dists, closest
rindex = df
SimilarityArr = []
for close in closest:
similarity = dists[close]
if isinstance(item_input, int):
decoded_id = user_encoded2user.get(close)
SimilarityArr.append({"similar_users": decoded_id,
"similarity": similarity})
Frame = pd.DataFrame(SimilarityArr).sort_values(by="similarity",
ascending=False)
return Frame
except:
print('{}!, not Found in User list'.format(name))
In [132]:
similar_users = find_similar_users(int(random_user),
n=5,
neg=False)
similar_users = similar_users[similar_users.similarity > 0.4]
similar_users = similar_users[similar_users.similar_users != random_user]
similar_users.head(3)
> users similar to # 68038
Out[132]:
| similar_users | similarity | |
|---|---|---|
| 5 | 4472.0 | 1.000000 |
| 4 | 3403.0 | 0.518738 |
| 3 | NaN | 0.513568 |
User Preferences
In [139]:
from wordcloud import WordCloud
from collections import defaultdict
import matplotlib.pyplot as plt
def showWordCloud(all_genres):
genres_cloud = WordCloud(width=700, height=400,
background_color='white',
colormap='gnuplot').generate_from_frequencies(all_genres)
plt.figure(figsize=(10,8))
plt.imshow(genres_cloud, interpolation='bilinear')
plt.axis('off')
plt.show()
def getFavGenre(frame, plot=False):
frame.dropna(inplace=False)
all_genres = defaultdict(int)
genres_list = []
for genres in frame['Genders']:
if isinstance(genres, str):
for genre in genres.split(','):
genres_list.append(genre)
all_genres[genre.strip()] += 1
if plot:
showWordCloud(all_genres)
return genres_list
def get_user_preferences(user_id, plot=False, verbose=0):
animes_watched_by_user = rating_df[rating_df.user_id==user_id]
user_rating_percentile = np.percentile(animes_watched_by_user.rating, 75)
animes_watched_by_user = animes_watched_by_user[animes_watched_by_user.rating >= user_rating_percentile]
top_animes_user = (
animes_watched_by_user.sort_values(by="rating", ascending=False)#.head(10)
.anime_id.values
)
anime_df_rows = df[df["anime_id"].isin(top_animes_user)]
anime_df_rows = anime_df_rows[["eng_version", "Genders"]]
if verbose != 0:
print("> User #{} has rated {} movies (avg. rating = {:.1f})".format(
user_id, len(animes_watched_by_user),
animes_watched_by_user['rating'].mean(),
))
print('> preferred genres')
if plot:
getFavGenre(anime_df_rows, plot)
return anime_df_rows#.eng_version.values
In [141]:
user_pref = get_user_preferences(random_user, plot=True, verbose=1)
print('> animes highly rated by this user')
pd.DataFrame(user_pref).head(10)
> User #68038 has rated 119 movies (avg. rating = 0.9) > preferred genres

> animes highly rated by this user
Out[141]:
| eng_version | Genders | |
|---|---|---|
| 9913 | Gintama Season 4 | Action, Comedy, Historical, Parody, Samurai, Sci-Fi, Shounen |
| 6006 | Gintama Season 2 | Action, Sci-Fi, Comedy, Historical, Parody, Samurai, Shounen |
| 7261 | Gintama:Enchousen | Action, Comedy, Historical, Parody, Samurai, Sci-Fi, Shounen |
| 12242 | Gintama Season 5 | Action, Comedy, Historical, Parody, Samurai, Sci-Fi, Shounen |
| 3537 | Clannad ~After Story~ | Slice of Life, Comedy, Supernatural, Drama, Romance |
| 7249 | Gintama Movie 2: Kanketsu-hen - Yorozuya yo Eien Nare | Action, Sci-Fi, Comedy, Historical, Parody, Samurai, Shounen |
| 833 | Gintama | Action, Comedy, Historical, Parody, Samurai, Sci-Fi, Shounen |
| 14293 | Gintama.:Silver Soul Arc - Second Half War | Action, Sci-Fi, Comedy, Historical, Parody, Samurai, Shounen |
| 13901 | Gintama.:Silver Soul Arc | Action, Comedy, Historical, Parody, Samurai, Sci-Fi, Shounen |
| 8883 | Your Lie in April | Drama, Music, Romance, School, Shounen |
Ranking Based Recommendation for the users
In [153]:
print("Showing recommendations for user: {}".format(random_user))
print("===" * 25)
animes_watched_by_user = rating_df[rating_df.user_id==random_user]
anime_not_watched_df = df[
~df["anime_id"].isin(animes_watched_by_user.anime_id.values)
]
anime_not_watched = list(
set(anime_not_watched_df['anime_id']).intersection(set(anime2anime_encoded.keys()))
)
anime_not_watched = [[anime2anime_encoded.get(x)] for x in anime_not_watched]
user_encoder = user2user_encoded.get(random_user)
user_anime_array = np.hstack(
([[user_encoder]] * len(anime_not_watched), anime_not_watched)
)
user_anime_array = [user_anime_array[:, 0], user_anime_array[:, 1]]
ratings = model.predict(user_anime_array).flatten()
top_ratings_indices = (-ratings).argsort()[:10]
recommended_anime_ids = [
anime_encoded2anime.get(anime_not_watched[x][0]) for x in top_ratings_indices
]
Results = []
top_rated_ids = []
for index, anime_id in enumerate(anime_not_watched):
rating = ratings[index]
id_ = anime_encoded2anime.get(anime_id[0])
if id_ in recommended_anime_ids:
top_rated_ids.append(id_)
try:
condition = (df.anime_id == id_)
name = df[condition]['eng_version'].values[0]
genre = df[condition].Genders.values[0]
score = df[condition].Score.values[0]
sypnopsis = getSypnopsis(int(id_))
except:
continue
Results.append({#"anime_id": id_,
"name": name,
"pred_rating": rating,
"genre": genre,
'sypnopsis': sypnopsis})
print("---" * 25)
print("> Top 10 anime recommendations")
print("---" * 25)
Results = pd.DataFrame(Results).sort_values(by='pred_rating', ascending=False)
Results
Showing recommendations for user: 68038 =========================================================================== 534/534 [==============================] - 0s 684us/step --------------------------------------------------------------------------- > Top 10 anime recommendations ---------------------------------------------------------------------------
Out[153]:
| name | pred_rating | genre | sypnopsis | |
|---|---|---|---|---|
| 1 | Initial D First Stage | 0.870103 | Action, Cars, Drama, Seinen, Sports | Unlike his friends, Takumi Fujiwara is not particularly interested in cars, with little to no knowledge about the world of car enthusiasts and street racers. The son of a tofu shop owner, he is tasked to deliver tofu every morning without fail, driving along the mountain of Akina. Thus, conversations regarding cars or driving in general would only remind Takumi of the tiring daily routine forced upon him. One night, the Akagi Red Suns, an infamous team of street racers, visit the town of Akina to challenge the local mountain pass. Led by their two aces, Ryousuke and Keisuke Takahashi, the Red Suns plan to conquer every racing course in Kanto, establishing themselves as the fastest crew in the region. However, much to their disbelief, one of their aces is overtaken by an old Toyota AE86 during a drive back home from Akina. After the incident, the Takahashi brothers are cautious of a mysterious driver geared with remarkable technique and experience in the local roads—the AE86 of Mount Akina. |
| 4 | Psycho-Pass | 0.865857 | Action, Sci-Fi, Police, Psychological | Justice, and the enforcement of it, has changed. In the 22nd century, Japan enforces the Sibyl System, an objective means of determining the threat level of each citizen by examining their mental state for signs of criminal intent, known as their Psycho-Pass. Inspectors uphold the law by subjugating, often with lethal force, anyone harboring the slightest ill-will; alongside them are Enforcers, jaded Inspectors that have become latent criminals, granted relative freedom in exchange for carrying out the Inspectors' dirty work. Into this world steps Akane Tsunemori, a young woman with an honest desire to uphold justice. However, as she works alongside veteran Enforcer Shinya Kougami, she soon learns that the Sibyl System's judgments are not as perfect as her fellow Inspectors assume. With everything she has known turned on its head, Akane wrestles with the question of what justice truly is, and whether it can be upheld through the use of a system that may already be corrupt. |
| 9 | Kiznaiver | 0.865752 | Sci-Fi, Drama, Romance | Katsuhira Agata is a quiet and reserved teenage boy whose sense of pain has all but vanished. His friend, Chidori Takashiro, can only faintly remember the days before Katsuhira had undergone this profound change. Now, his muffled and complacent demeanor make Katsuhira a constant target for bullies, who exploit him for egregious sums of money. But their fists only just manage to make him blink, as even emotions are far from his grasp. However, one day Katsuhira, Chidori, and four other teenagers are abducted and forced to join the Kizuna System as official "Kiznaivers." Those taking part are connected through pain: if one member is injured, the others will feel an equal amount of agony. These individuals must become the lab rats and scapegoats of an incomplete system designed with world peace in mind. With their fates literally intertwined, the Kiznaivers must expose their true selves to each other, or risk failing much more than just the Kizuna System. |
| 2 | Initial D Second Stage | 0.864080 | Action, Cars, Drama, Seinen, Sports | ccumulating an impressive series of victories with his AE86, Takumi Fujiwara has imposed himself as street racing's newest rising star. However, his newly found confidence of winning at his home turf of Mount Akina has been put in jeopardy by a new Emperor team exclusively using a car model favored by most professional racing pilots: the Mitsubishi four-wheel drive Lancer Evolutions—also known as Lan Evos. The Emperor team leader, Kyouichi Sudou, looks down on Takumi and regards him as an inferior pilot for driving an antique car that lacks the makings of a true modern race car. Kyouichi's elitist philosophy is also the reason why his team is only made of Lan Evo drivers. Will Takumi be able to keep his perfect track record intact against the highly skilled and mechanically superior Emperor team, or does his hot streak end here? |
| 6 | Free! - Iwatobi Swim Club | 0.860426 | Slice of Life, Comedy, Sports, Drama, School | Haruka Nanase has a love for water and a passion for swimming. In elementary school, he competed in and won a relay race with his three friends Rin Matsuoka, Nagisa Hazuki, and Makoto Tachibana. After claiming victory at the tournament, the four friends went their separate ways. Years later, they reunite as high school students; however, Rin couldn't care less about returning to the way things used to be. Not only does he attend a different school, but the sole thing important to him is proving that he is a better swimmer than Haruka. After the bitter reunion, Haruka, Nagisa, and Makoto decide to form the Iwatobi High School Swim Club, but they will need a fourth member if they hope to take part in the upcoming tournament. Enter Rei Ryuugazaki, a former member of the track team whom Nagisa recruits. As the time to compete draws near, the four develop a close bond while training intensely to come out on top and settle things between Haruka and Rin once and for all. |
| 7 | Terror in Resonance | 0.846884 | Mystery, Psychological, Thriller | Painted in red, the word "VON" is all that is left behind after a terrorist attack on a nuclear facility in Japan. The government is shattered by their inability to act, and the police are left frantically searching for ways to crack down the perpetrators. The public are clueless—until, six months later, a strange video makes its way onto the internet. In it, two teenage boys who identify themselves only as "Sphinx" directly challenge the police, threatening to cause destruction and mayhem across Tokyo. Unable to stop the mass panic quickly spreading through the city and desperate for any leads in their investigation, the police struggle to act effectively against these terrorists, with Detective Kenjirou Shibazaki caught in the middle of it all. Zankyou no Terror tells the story of Nine and Twelve, the two boys behind the masked figures of Sphinx. They should not exist, yet they stand strong in a world of deception and secrets while they make the city fall around them, all in the hopes of burying their own tragic truth. |
| 8 | The Heroic Legend of Arslan | 0.846722 | Action, Adventure, Drama, Fantasy, Historical, Shounen | The year is 320. Under the rule of the belligerent King Andragoras III, the Kingdom of Pars is at war with the neighboring empire, Lusitania. Though different from his father in many aspects, Arslan, the young prince, sets out to prove his valor on the battlefield for the very first time. However, when the king is betrayed by one of his most trusted officials, the Parsian army is decimated and the capital city of Ecbatana is sieged. With the army in shambles and the Lusitanians out for his head, Arslan is forced to go on the run. With a respected general by his side, Daryun, Arslan soon sets off on a journey in search of allies that will help him take back his home. However, the enemies that the prince faces are far from limited to just those occupying his kingdom. Armies of other kingdoms stand ready to conquer Ecbatana. Moreover, the mastermind behind Lusitania's victory, an enigmatic man hiding behind a silver mask, poses a dangerous threat to Arslan and his company as he possesses a secret that could jeopardize Arslan's right to succession. With the odds stacked against him, Arslan must find the strength and courage to overcome these obstacles, and allies who will help him fight in the journey that will help prepare him for the day he becomes king. |
| 5 | Gargantia on the Verdurous Planet | 0.839460 | Action, Sci-Fi, Adventure, Mecha | In the distant future, a majority of humans have left the Earth, and the Galactic Alliance of Humanity is founded to guide exploration and ensure the prosperity of mankind. However, a significant threat arises in the form of strange creatures called Hideauze, resulting in an interstellar war to prevent humanity's extinction. Armed with Chamber, an autonomous robot, 16-year-old lieutenant Ledo of the Galactic Alliance joins the battle against the monsters. In an unfortunate turn of events, Ledo loses control during the battle and is cast out to the far reaches of space, crash-landing on a waterlogged Earth. On the blue planet, Gargantia—a large fleet of scavenger ships—comes across Chamber and retrieves it from the ocean, thinking they have salvaged something of value. Mistaking their actions for hostility, Ledo sneaks aboard and takes a young messenger girl named Amy hostage, only to realize that the residents of Gargantia are not as dangerous as he had believed. Faced with uncertainty, and unable to communicate with his comrades in space, Ledo attempts to get his bearings and acclimate to a new lifestyle. But his peaceful days are about to be short-lived, as there is more to this ocean-covered planet than meets the eye. |
| 3 | Bakuman. | 0.839379 | Comedy, Drama, Romance, Shounen | h the serialization of their new manga, "Detective Trap," the writer-artist team, Akito Takagi and Moritaka Mashiro, better known by their pseudonym Muto Ashirogi, are one step closer to becoming world-renowned mangaka. For Mashiro, however, serialization is just the first step. Having promised to marry his childhood sweetheart and aspiring voice actress, Azuki Miho, once his manga gets an anime adaptation, Mashiro must continue his to popularize Ashirogi's work. A tremendously competitive cast of ambitious mangaka—including the wild genius, Eiji Niizuma; the elegant student, Yuriko Aoki, and her older admirer and partner, Takurou Nakai; the lazy prodigy, Kazuya Hiramaru; and the abrasive artist, Shinta Fukuda—both support and compete against Muto Ashirogi in creating the next big hit. As they adjust to their young and seemingly untested new editor, the dynamic duo struggle to maintain their current serialization, secure the top stop in Shounen Jack, and ultimately, achieve an anime adaptation of their manga. With new rivals and friends, Bakuman. 2nd Season continues Takagi and Mashiro's inspiring story of hard work and young love. |
| 0 | Bungo Stray Dogs 2 | 0.839371 | Action, Mystery, Seinen, Super Power, Supernatural | Despite their differences in position, three men—the youngest senior executive of the Port Mafia, Osamu Dazai, the lowest ranking member, Sakunosuke Oda, and the intelligence agent, Angou Sakaguchi—gather at the Lupin Bar at the end of the day to relax and take delight in the company of friends. However, one night, Angou disappears. A photograph taken at the bar is all that is left of the three together. Fast forward to the present, and Dazai is now a member of the Armed Detective Agency. The Guild, an American gifted organization, has entered the fray and is intent on taking the Agency's work permit. They must now divide their attention between the two groups, the Guild and the Port Mafia, who oppose their very existence. |



In [147]:
model.save('anime_model.h5')
from IPython.display import FileLink
FileLink(r'./anime_model.h5')
Thanks & Regards,
Sai Sumanth Reddy
