Anime Hybrid Recommendation System¶
Dataset Description¶
Anime Dataset 2023 Dataset¶
Summary:¶
Anime is a popular form of Japanese animated entertainment known for its unique art style, diverse genres, and rich storytelling. It includes TV series, movies, and OVAs that appeal to a wide range of audiences worldwide. With thousands of titles spanning action, romance, fantasy, and more, anime has built a passionate global fanbase. This diversity makes anime data especially suitable for building a great recommendation system. This dataset(Anime Dataset 2023) contains detailed metadata for each anime entry, including identifiers, content details, ratings, and user interaction metrics. The following are the descriptions of each column:
Column Descriptions:¶
anime_id: Unique identifier for each anime.Name: Original name of the anime.English name: Official English-translated title.Other name: Alternate titles in native languages (e.g., Japanese, Chinese, Korean).Score: Average user rating for the anime.Genres: Comma-separated list of genres associated with the anime.Synopsis: Brief summary of the anime’s plot.Type: Format of the anime (e.g., TV, Movie, OVA).Episodes: Total number of episodes.Aired: Airing date range (start to end).Premiered: Season and year of initial release.Status: Current airing status (e.g., Finished Airing, Currently Airing).Producers: Companies involved in the production.Licensors: Distribution or licensing companies (e.g., streaming platforms).Studios: Animation studios that created the anime.Source: Origin of the story (e.g., manga, novel, original).Duration: Length of a single episode.Rating: Age restriction or content rating (e.g., PG-13, R).Rank: Position in ranking based on ratings or popularity.Popularity: Popularity rank among all anime.Favorites: Number of users who marked the anime as a favorite.Scored By: Number of users who rated the anime.Members: Total users who added the anime to their list (watching, completed, etc.).Image URL: Link to the anime’s cover image or poster.
Mount Google Drive¶
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
Installations¶
!pip install numpy==1.26.4 --force-reinstall --no-cache-dir
Collecting numpy==1.26.4
Downloading numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (61 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/61.0 kB ? eta -:--:--
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.0/61.0 kB 33.4 MB/s eta 0:00:00
Downloading numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (18.3 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/18.3 MB ? eta -:--:--
━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.0/18.3 MB 32.3 MB/s eta 0:00:01
━━━━━━━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━ 6.9/18.3 MB 101.8 MB/s eta 0:00:01
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╺━━━━━━━━━━ 13.3/18.3 MB 185.2 MB/s eta 0:00:01
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 18.3/18.3 MB 187.5 MB/s eta 0:00:00
Installing collected packages: numpy
Attempting uninstall: numpy
Found existing installation: numpy 2.0.2
Uninstalling numpy-2.0.2:
Successfully uninstalled numpy-2.0.2
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
thinc 8.3.6 requires numpy<3.0.0,>=2.0.0, but you have numpy 1.26.4 which is incompatible.
opencv-python-headless 4.12.0.88 requires numpy<2.3.0,>=2; python_version >= "3.9", but you have numpy 1.26.4 which is incompatible.
opencv-python 4.12.0.88 requires numpy<2.3.0,>=2; python_version >= "3.9", but you have numpy 1.26.4 which is incompatible.
opencv-contrib-python 4.12.0.88 requires numpy<2.3.0,>=2; python_version >= "3.9", but you have numpy 1.26.4 which is incompatible.
Successfully installed numpy-1.26.4
!pip install surprise
Collecting surprise
Downloading surprise-0.1-py2.py3-none-any.whl.metadata (327 bytes)
Collecting scikit-surprise (from surprise)
Downloading scikit_surprise-1.1.4.tar.gz (154 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/154.4 kB ? eta -:--:--
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 154.4/154.4 kB 4.4 MB/s eta 0:00:00
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing metadata (pyproject.toml) ... done
Requirement already satisfied: joblib>=1.2.0 in /usr/local/lib/python3.11/dist-packages (from scikit-surprise->surprise) (1.5.1)
Requirement already satisfied: numpy>=1.19.5 in /usr/local/lib/python3.11/dist-packages (from scikit-surprise->surprise) (1.26.4)
Requirement already satisfied: scipy>=1.6.0 in /usr/local/lib/python3.11/dist-packages (from scikit-surprise->surprise) (1.16.0)
Downloading surprise-0.1-py2.py3-none-any.whl (1.8 kB)
Building wheels for collected packages: scikit-surprise
Building wheel for scikit-surprise (pyproject.toml) ... done
Created wheel for scikit-surprise: filename=scikit_surprise-1.1.4-cp311-cp311-linux_x86_64.whl size=2469541 sha256=4435555033e13403a54af1dec61f311298d670776920c86a305030189ace65b9
Stored in directory: /root/.cache/pip/wheels/2a/8f/6e/7e2899163e2d85d8266daab4aa1cdabec7a6c56f83c015b5af
Successfully built scikit-surprise
Installing collected packages: scikit-surprise, surprise
Successfully installed scikit-surprise-1.1.4 surprise-0.1
Imports¶
import pandas as pd
import numpy as np
from pathlib import Path
from IPython.display import Image, HTML
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MultiLabelBinarizer
import re
from collections import defaultdict
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from scipy.sparse import csr_matrix
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from nltk import pos_tag
import string
import spacy
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')
from surprise import SVD
Data Understanding¶
Data Loading¶
- Use pathlib for
pathsafety
data_path = Path('/content/drive/MyDrive/Anime Recommender System')
- Loading Anime Data
anime_df = pd.read_csv(data_path/'anime-dataset-2023.csv')
anime_df.columns
Index(['anime_id', 'Name', 'English name', 'Other name', 'Score', 'Genres',
'Synopsis', 'Type', 'Episodes', 'Aired', 'Premiered', 'Status',
'Producers', 'Licensors', 'Studios', 'Source', 'Duration', 'Rating',
'Rank', 'Popularity', 'Favorites', 'Scored By', 'Members', 'Image URL'],
dtype='object') - Showing first 5 rows
anime_df.head()
| anime_id | Name | English name | Other name | Score | Genres | Synopsis | Type | Episodes | Aired | ... | Studios | Source | Duration | Rating | Rank | Popularity | Favorites | Scored By | Members | Image URL | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Cowboy Bebop | Cowboy Bebop | カウボーイビバップ | 8.75 | Action, Award Winning, Sci-Fi | Crime is timeless. By the year 2071, humanity ... | TV | 26.0 | Apr 3, 1998 to Apr 24, 1999 | ... | Sunrise | Original | 24 min per ep | R - 17+ (violence & profanity) | 41.0 | 43 | 78525 | 914193.0 | 1771505 | https://cdn.myanimelist.net/images/anime/4/196... |
| 1 | 5 | Cowboy Bebop: Tengoku no Tobira | Cowboy Bebop: The Movie | カウボーイビバップ 天国の扉 | 8.38 | Action, Sci-Fi | Another day, another bounty—such is the life o... | Movie | 1.0 | Sep 1, 2001 | ... | Bones | Original | 1 hr 55 min | R - 17+ (violence & profanity) | 189.0 | 602 | 1448 | 206248.0 | 360978 | https://cdn.myanimelist.net/images/anime/1439/... |
| 2 | 6 | Trigun | Trigun | トライガン | 8.22 | Action, Adventure, Sci-Fi | Vash the Stampede is the man with a $$60,000,0... | TV | 26.0 | Apr 1, 1998 to Sep 30, 1998 | ... | Madhouse | Manga | 24 min per ep | PG-13 - Teens 13 or older | 328.0 | 246 | 15035 | 356739.0 | 727252 | https://cdn.myanimelist.net/images/anime/7/203... |
| 3 | 7 | Witch Hunter Robin | Witch Hunter Robin | Witch Hunter ROBIN (ウイッチハンターロビン) | 7.25 | Action, Drama, Mystery, Supernatural | Robin Sena is a powerful craft user drafted in... | TV | 26.0 | Jul 3, 2002 to Dec 25, 2002 | ... | Sunrise | Original | 25 min per ep | PG-13 - Teens 13 or older | 2764.0 | 1795 | 613 | 42829.0 | 111931 | https://cdn.myanimelist.net/images/anime/10/19... |
| 4 | 8 | Bouken Ou Beet | Beet the Vandel Buster | 冒険王ビィト | 6.94 | Adventure, Fantasy, Supernatural | It is the dark century and the people are suff... | TV | 52.0 | Sep 30, 2004 to Sep 29, 2005 | ... | Toei Animation | Manga | 23 min per ep | PG - Children | 4240.0 | 5126 | 14 | 6413.0 | 15001 | https://cdn.myanimelist.net/images/anime/7/215... |
5 rows × 24 columns
- Loading Ratings Data
ratings_df = pd.read_csv(data_path/'users-score-2023.csv')[:1000]
- Showing first 5 rows of the data
ratings_df.head()
| user_id | Username | anime_id | Anime Title | rating | |
|---|---|---|---|---|---|
| 0 | 1 | Xinil | 21 | One Piece | 9 |
| 1 | 1 | Xinil | 48 | .hack//Sign | 7 |
| 2 | 1 | Xinil | 320 | A Kite | 5 |
| 3 | 1 | Xinil | 49 | Aa! Megami-sama! | 8 |
| 4 | 1 | Xinil | 304 | Aa! Megami-sama! Movie | 8 |
Data Exploration¶
Anime data size¶
anime_df.shape
(24905, 24)
- The anime dataset contains
24,905entries (rows) and24features (columns)
User Ratings data size¶
ratings_df.shape
(24325191, 5)
- The ratings dataset contains
24,325,191records and5columns. - This large volume of
user-animeinteraction data provides a strong foundation forCollaborative Filteringand other recommendation techniques. The richness and scale of the dataset make it ideal for training robust models.
Check for Missing Values in Anime Dataset¶
anime_df.isnull().sum()
| 0 | |
|---|---|
| anime_id | 0 |
| Name | 0 |
| English name | 0 |
| Other name | 0 |
| Score | 0 |
| Genres | 0 |
| Synopsis | 0 |
| Type | 0 |
| Episodes | 0 |
| Aired | 0 |
| Premiered | 0 |
| Status | 0 |
| Producers | 0 |
| Licensors | 0 |
| Studios | 0 |
| Source | 0 |
| Duration | 0 |
| Rating | 0 |
| Rank | 0 |
| Popularity | 0 |
| Favorites | 0 |
| Scored By | 0 |
| Members | 0 |
| Image URL | 0 |
- No missing values.
Remove commas or other non-numeric characters from Score column (if any)¶
Cleaning the 'Score' and 'Scored By' columns in the anime dataset by removing non-numeric characters using regular expressions:
'Score': Removes any character that is not a digit or a decimal point (e.g., "N/A", text, etc.).'Scored By': Removes all characters except digits to ensure the field contains only numeric values.
anime_df['Score'] = anime_df['Score'].replace('[^0-9.]', '', regex=True)
anime_df['Scored By'] = anime_df['Scored By'].replace('[^0-9]', '', regex=True)
Convert to Numeric¶
anime_df['Score'] = pd.to_numeric(anime_df['Score'], errors='coerce')
anime_df['Scored By'] = pd.to_numeric(anime_df['Scored By'], errors='coerce')
Fill missing values with the median of each column¶
anime_df['Score'].fillna(anime_df['Score'].median(), inplace=True)
anime_df['Scored By'].fillna(anime_df['Scored By'].median(), inplace=True)
Extract the first 4-digit number from Aired as the release year¶
anime_df['release_year'] = anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
Split genres, handle Unknown¶
anime_df['Genres'] = anime_df['Genres'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
Multi-hot encode¶
mlb_genres = MultiLabelBinarizer()
genres_encoded = mlb_genres.fit_transform(anime_df['Genres'])
Split studios, handle Unknown¶
anime_df['Studios'] = anime_df['Studios'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
Multi-hot encode¶
mlb_studios = MultiLabelBinarizer()
studios_encoded = mlb_studios.fit_transform(anime_df['Studios'])
Add weighted Rating feature which is the IMDB popularity function quotient¶
C = anime_df['Score'].mean()
m = anime_df['Scored By'].quantile(0.65)
anime_df['weighted_rating'] = (
(anime_df['Scored By'] / (anime_df['Scored By'] + m)) * anime_df['Score'] +
(m / (anime_df['Scored By'] + m)) * C
)
One-hot encode Type feature¶
ohe_type = OneHotEncoder(sparse_output=False)
type_encoded = ohe_type.fit_transform(anime_df[['Type']])
One-hot encode Source feature¶
ohe_source = OneHotEncoder(sparse_output=False)
source_encoded = ohe_source.fit_transform(anime_df[['Source']])
Convert to numeric, coercing errors to NaN¶
anime_df['Episodes'] = pd.to_numeric(anime_df['Episodes'], errors='coerce')
Impute missing values with the median¶
median_episodes = anime_df['Episodes'].median()
anime_df['Episodes'].fillna(median_episodes, inplace=True)
Bin episodes¶
bins = [0, 1, 12, 24, 50, np.inf]
labels = ['1', '2-12', '13-24', '25-50', '51+']
anime_df['Episodes_Binned'] = pd.cut(anime_df['Episodes'], bins=bins, labels=labels)
One-hot encode Episodes feature¶
ohe_episodes = OneHotEncoder(sparse_output=False)
episodes_encoded = ohe_episodes.fit_transform(anime_df[['Episodes_Binned']])
Clean text function¶
def clean_text(text):
text = text.lower()
text = re.sub(r'[^\w\s]', '', text) # Remove punctuation
return text
Apply cleaning¶
anime_df['Synopsis'] = anime_df['Synopsis'].apply(clean_text)
TF-IDF vectorization¶
tfidf = TfidfVectorizer(stop_words='english')
synopsis_encoded = tfidf.fit_transform(anime_df['Synopsis'])
Feature Selection¶
anime_df = anime_df[ [
'anime_id',
'Name',
'Score',
'Genres',
'Synopsis',
'Type',
'Episodes',
'Aired',
'Status',
'Studios',
'Source',
'Scored By',
'Image URL'
]]
- Save the new df
# Define the path and filename
output_path = '/content/drive/MyDrive/Anime Recommender System/anime_filtered.csv'
# Save the DataFrame to CSV
anime_df.to_csv(output_path, index=False)
Building Content-Based Recommendation System¶
Compute cosine similarities with sparse matrices¶
Computing cosine similarity matrices for different encoded content features of anime:
genres_sim: Measures how similar anime titles are based on genre vectors.studios_sim: Measures similarity based on shared animation studios.synopsis_sim: Measures textual similarity between anime plot summaries (e.g., using TF-IDF).
Cosine similarity returns values between 0 (completely dissimilar) and 1 (identical), making it ideal for comparing sparse or high-dimensional feature encodings.
genres_sim = cosine_similarity(genres_encoded)
studios_sim = cosine_similarity(studios_encoded)
synopsis_sim = cosine_similarity(synopsis_encoded)
Binary Similarity Match Function¶
This function computes a binary similarity matrix for an encoded dataset using dot product matching:
- It calculates whether two entries share at least one common feature.
- If they do, the dot product is non-zero →
True→ converted to1. - If no match is found →
False→ converted to0.
The result is a symmetric binary matrix (1 = match, 0 = no match), useful for exact-match filtering (e.g., genre or studio overlap).
def match_similarity(encoded_data):
return (encoded_data @ encoded_data.T).astype(bool).astype(int)
Compute Exact Match Similarities (Type, Source, Episodes)¶
The match_similarity() function to compute binary similarity matrices for the following encoded features:
type_sim: Matches anime with the same format (e.g., TV, Movie, OVA).source_sim: Matches anime with the same source material (e.g., Manga, Light Novel, Original).episodes_sim: Matches anime with the same number (or encoded range) of episodes.
Each matrix contains:
1→ at least one shared encoded value (i.e., a match)0→ no match
These matrices are useful for filtering or boosting recommendations that share structural traits with the input anime.
type_sim = match_similarity(type_encoded)
source_sim = match_similarity(source_encoded)
episodes_sim = match_similarity(episodes_encoded)
Combine Feature-Based Similarities with Equal Weights¶
Combining multiple similarity matrices into a single composite similarity score by applying equal weights to each of the six selected features:
Features used:
genres_simsynopsis_simtype_simstudios_simepisodes_simsource_sim
Each feature is assigned an equal weight of
1/6, assuming equal importance across all content aspects.
The resulting combined_sim matrix represents a hybrid similarity score that captures both semantic (synopsis, genres) and structural (type, source, etc.) similarities — useful for content-based recommendations.
# Number of features
num_features = 6
# Equal weights (1/6 for each)
weights = [1 / num_features] * num_features
# Combine similarities
combined_sim = (weights[0] * genres_sim +
weights[1] * synopsis_sim +
weights[2] * type_sim +
weights[3] * studios_sim +
weights[4] * episodes_sim +
weights[5] * source_sim)
Hybrid Anime Recommendation Function (Content-Based + Weighted Rating)¶
The followig function, get_recommendations2(), generates personalized anime recommendations based on content similarity and weighted rating scores.
Parameters:¶
title(str): Anime title to base the recommendations on.n(int): Number of recommendations to return (default = 10).similarity_weight(float): Controls how much weight to give similarity vs. rating in final scoring.
How it works:¶
- Locates the given anime in
anime_df. - Computes cosine similarity scores using the
combined_simmatrix. - Selects top N most similar titles, excluding the anime itself.
- Fetches relevant columns: name, genres, image, scores, etc.
- Merges similarity scores with existing weighted ratings.
- Calculates a final score using a weighted average: $$\text{final_score} = (1 - w)\cdot\text{weighted_rating} + w \cdot \text{similarity_score}$$
- Returns top N results sorted by this final score.
def get_recommendations2(title, n=10, similarity_weight=0.85):
"""
Recommend anime based on a given title using cosine similarity and weighted ratings.
Parameters:
- title (str): Name of the anime to base recommendations on.
- n (int): Number of recommendations to return (default: 10).
- similarity_weight (float): Weight for similarity in final score (default: 0.7).
Returns:
- DataFrame: Top N recommended animes with relevant details.
Raises:
- ValueError: If the title is not found in anime_df.
"""
# Check if title exists and get its label index
matching_animes = anime_df[anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
label = matching_animes.index[0]
# Convert label index to positional index
pos = anime_df.index.get_loc(label)
# Get pairwise similarity scores
sim_scores = list(enumerate(combined_sim[pos]))
# Sort by similarity score in descending order
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# Get top-N matches (excluding self), adjust n if fewer animes exist
max_recommendations = min(102, len(anime_df) - 1)
top_sim = sim_scores[1:max_recommendations + 1]
sim_indices = [i for i, _ in top_sim] # Indices of top similar animes
sim_scores_top = [score for _, score in top_sim] # Corresponding similarity scores
# Fetch recommended animes
recommended_animes = anime_df.iloc[sim_indices][
['Name', 'anime_id', 'weighted_rating', 'Image URL', 'Type', 'Genres', 'Score']
].copy()
# Prepare for merging
qualified_animes = recommended_animes.copy()
qualified_animes.reset_index(drop=True, inplace=True)
# Create similarity DataFrame with correct scores
similarity_df = pd.DataFrame({
"anime_id": anime_df.iloc[sim_indices]["anime_id"].values,
"similarity_score": sim_scores_top
})
# Merge on correct column
qualified_animes = qualified_animes.merge(similarity_df, on="anime_id", how="left")
# Handle any unexpected NaNs in similarity_score
qualified_animes["similarity_score"] = qualified_animes["similarity_score"].fillna(
qualified_animes["similarity_score"].min()
)
# Calculate final score
qualified_animes['final_score'] = (
(1 - similarity_weight) * qualified_animes['weighted_rating'] +
similarity_weight * qualified_animes['similarity_score']
)
# Return top-N sorted by final score
return qualified_animes.sort_values('final_score', ascending=False).head(n)[
['Name', 'anime_id', 'Image URL', 'similarity_score', 'weighted_rating', 'final_score']
]
- Lets test it.
get_recommendations2(title='Darling in the FranXX', n=24)
| Name | anime_id | Image URL | similarity_score | weighted_rating | final_score | |
|---|---|---|---|---|---|---|
| 1 | Lycoris Recoil | 50709 | https://cdn.myanimelist.net/images/anime/1392/... | 0.684209 | 8.183950 | 1.809170 |
| 67 | Vivy: Fluorite Eye's Song | 46095 | https://cdn.myanimelist.net/images/anime/1637/... | 0.597510 | 8.394183 | 1.767011 |
| 8 | Kill la Kill | 18679 | https://cdn.myanimelist.net/images/anime/1464/... | 0.647670 | 8.036835 | 1.756045 |
| 77 | Psycho-Pass | 13601 | https://cdn.myanimelist.net/images/anime/5/433... | 0.587892 | 8.335466 | 1.750028 |
| 14 | Plastic Memories | 27775 | https://cdn.myanimelist.net/images/anime/4/727... | 0.645286 | 7.904120 | 1.734111 |
| 30 | Senki Zesshou Symphogear XV | 32843 | https://cdn.myanimelist.net/images/anime/1899/... | 0.620386 | 7.975510 | 1.723654 |
| 12 | Texhnolyze | 26 | https://cdn.myanimelist.net/images/anime/1027/... | 0.646544 | 7.717234 | 1.707147 |
| 33 | Carole & Tuesday | 37435 | https://cdn.myanimelist.net/images/anime/1611/... | 0.620216 | 7.851340 | 1.704885 |
| 53 | Mahou Shoujo Lyrical Nanoha A's | 77 | https://cdn.myanimelist.net/images/anime/4/676... | 0.599961 | 7.889765 | 1.693432 |
| 13 | Uchuu Patrol Luluco | 32681 | https://cdn.myanimelist.net/images/anime/4/790... | 0.645950 | 7.512696 | 1.675962 |
| 7 | Guilty Crown | 10793 | https://cdn.myanimelist.net/images/anime/1566/... | 0.648123 | 7.417078 | 1.663466 |
| 86 | Charlotte | 28999 | https://cdn.myanimelist.net/images/anime/12/74... | 0.584486 | 7.747445 | 1.658930 |
| 25 | Senki Zesshou Symphogear AXZ | 32836 | https://cdn.myanimelist.net/images/anime/3/865... | 0.622573 | 7.472081 | 1.649999 |
| 11 | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | https://cdn.myanimelist.net/images/anime/7/765... | 0.647148 | 7.309665 | 1.646526 |
| 21 | Senki Zesshou Symphogear G | 15793 | https://cdn.myanimelist.net/images/anime/2/866... | 0.625689 | 7.396078 | 1.641247 |
| 61 | Re:Creators | 34561 | https://cdn.myanimelist.net/images/anime/11/85... | 0.598739 | 7.539231 | 1.639813 |
| 50 | Noein: Mou Hitori no Kimi e | 584 | https://cdn.myanimelist.net/images/anime/1/584... | 0.600546 | 7.503532 | 1.635994 |
| 52 | Irozuku Sekai no Ashita kara | 37497 | https://cdn.myanimelist.net/images/anime/1424/... | 0.600328 | 7.504221 | 1.635912 |
| 80 | Ima, Soko ni Iru Boku | 160 | https://cdn.myanimelist.net/images/anime/1094/... | 0.585873 | 7.559278 | 1.631883 |
| 32 | Senki Zesshou Symphogear GX | 21573 | https://cdn.myanimelist.net/images/anime/9/866... | 0.620225 | 7.358336 | 1.630941 |
| 10 | Soukyuu no Fafner: Dead Aggressor - Exodus | 17080 | https://cdn.myanimelist.net/images/anime/5/685... | 0.647159 | 7.177703 | 1.626741 |
| 89 | AKB0048: Next Stage | 14941 | https://cdn.myanimelist.net/images/anime/11/44... | 0.583954 | 7.527017 | 1.625413 |
| 56 | Suisei no Gargantia | 16524 | https://cdn.myanimelist.net/images/anime/11/48... | 0.599312 | 7.439220 | 1.625298 |
| 31 | True Tears | 2129 | https://cdn.myanimelist.net/images/anime/1733/... | 0.620261 | 7.274290 | 1.618366 |
df = get_recommendations2(title='Darling in the FranXX', n=67)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Name | anime_id | Image URL | similarity_score | weighted_rating | final_score | |
|---|---|---|---|---|---|---|
| 1 | Lycoris Recoil | 50709 | ![]() | 0.684209 | 8.183950 | 1.809170 |
| 67 | Vivy: Fluorite Eye's Song | 46095 | ![]() | 0.597510 | 8.394183 | 1.767011 |
| 8 | Kill la Kill | 18679 | ![]() | 0.647670 | 8.036835 | 1.756045 |
| 77 | Psycho-Pass | 13601 | ![]() | 0.587892 | 8.335466 | 1.750028 |
| 14 | Plastic Memories | 27775 | ![]() | 0.645286 | 7.904120 | 1.734111 |
| 30 | Senki Zesshou Symphogear XV | 32843 | ![]() | 0.620386 | 7.975510 | 1.723654 |
| 12 | Texhnolyze | 26 | ![]() | 0.646544 | 7.717234 | 1.707147 |
| 33 | Carole & Tuesday | 37435 | ![]() | 0.620216 | 7.851340 | 1.704885 |
| 53 | Mahou Shoujo Lyrical Nanoha A's | 77 | ![]() | 0.599961 | 7.889765 | 1.693432 |
| 13 | Uchuu Patrol Luluco | 32681 | ![]() | 0.645950 | 7.512696 | 1.675962 |
| 7 | Guilty Crown | 10793 | ![]() | 0.648123 | 7.417078 | 1.663466 |
| 86 | Charlotte | 28999 | ![]() | 0.584486 | 7.747445 | 1.658930 |
| 25 | Senki Zesshou Symphogear AXZ | 32836 | ![]() | 0.622573 | 7.472081 | 1.649999 |
| 11 | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | ![]() | 0.647148 | 7.309665 | 1.646526 |
| 21 | Senki Zesshou Symphogear G | 15793 | ![]() | 0.625689 | 7.396078 | 1.641247 |
| 61 | Re:Creators | 34561 | ![]() | 0.598739 | 7.539231 | 1.639813 |
| 50 | Noein: Mou Hitori no Kimi e | 584 | ![]() | 0.600546 | 7.503532 | 1.635994 |
| 52 | Irozuku Sekai no Ashita kara | 37497 | ![]() | 0.600328 | 7.504221 | 1.635912 |
| 80 | Ima, Soko ni Iru Boku | 160 | ![]() | 0.585873 | 7.559278 | 1.631883 |
| 32 | Senki Zesshou Symphogear GX | 21573 | ![]() | 0.620225 | 7.358336 | 1.630941 |
| 10 | Soukyuu no Fafner: Dead Aggressor - Exodus | 17080 | ![]() | 0.647159 | 7.177703 | 1.626741 |
| 89 | AKB0048: Next Stage | 14941 | ![]() | 0.583954 | 7.527017 | 1.625413 |
| 56 | Suisei no Gargantia | 16524 | ![]() | 0.599312 | 7.439220 | 1.625298 |
| 31 | True Tears | 2129 | ![]() | 0.620261 | 7.274290 | 1.618366 |
| 15 | Kurau Phantom Memory | 483 | ![]() | 0.644687 | 7.126334 | 1.616934 |
| 0 | Engage Kiss | 51417 | ![]() | 0.695188 | 6.829794 | 1.615379 |
| 70 | Mahou Shoujo Lyrical Nanoha | 76 | ![]() | 0.596609 | 7.367979 | 1.612314 |
| 62 | Vandread: The Second Stage | 181 | ![]() | 0.598314 | 7.336459 | 1.609036 |
| 27 | Solty Rei | 152 | ![]() | 0.621817 | 7.159682 | 1.602497 |
| 20 | Buddy Complex | 21437 | ![]() | 0.625902 | 7.088736 | 1.595327 |
| 2 | Senkou no Night Raid | 6973 | ![]() | 0.680460 | 6.775170 | 1.594666 |
| 4 | IGPX: Immortal Grand Prix (2005) | 3270 | ![]() | 0.649499 | 6.950303 | 1.594620 |
| 16 | Seikimatsu Occult Gakuin | 6974 | ![]() | 0.638213 | 7.013750 | 1.594544 |
| 9 | Classroom☆Crisis | 30383 | ![]() | 0.647517 | 6.949188 | 1.592768 |
| 92 | AKB0048 | 12149 | ![]() | 0.583592 | 7.302190 | 1.591382 |
| 76 | Kemonozume | 1454 | ![]() | 0.588238 | 7.268849 | 1.590330 |
| 18 | Senki Zesshou Symphogear | 11751 | ![]() | 0.627148 | 7.004872 | 1.583807 |
| 38 | IGPX: Immortal Grand Prix (2005) 2nd Season | 1410 | ![]() | 0.617851 | 7.033195 | 1.580153 |
| 48 | Kiddy Grade | 274 | ![]() | 0.602237 | 7.121073 | 1.580062 |
| 66 | Vandread | 180 | ![]() | 0.597592 | 7.139197 | 1.578832 |
| 3 | Muteki Choujin Zanbot 3 | 2200 | ![]() | 0.655473 | 6.752812 | 1.570074 |
| 55 | Last Exile: Ginyoku no Fam | 10336 | ![]() | 0.599367 | 7.004832 | 1.560187 |
| 29 | Blassreiter | 3407 | ![]() | 0.620965 | 6.861296 | 1.557015 |
| 65 | Dual! Parallel Lun-Lun Monogatari | 992 | ![]() | 0.597798 | 6.960033 | 1.552133 |
| 47 | Soukou no Strain | 1602 | ![]() | 0.604473 | 6.914741 | 1.551013 |
| 45 | Koutetsu Tenshi Kurumi | 554 | ![]() | 0.614259 | 6.695615 | 1.526463 |
| 71 | Futakoi Alternative | 126 | ![]() | 0.596225 | 6.797654 | 1.526439 |
| 6 | Choujikuu Kidan Southern Cross | 4503 | ![]() | 0.648304 | 6.497439 | 1.525674 |
| 49 | Double Decker! Doug & Kirill | 37496 | ![]() | 0.600664 | 6.767241 | 1.525651 |
| 17 | M3: Sono Kuroki Hagane | 23133 | ![]() | 0.634980 | 6.553907 | 1.522819 |
| 44 | Concrete Revolutio: Choujin Gensou | 31147 | ![]() | 0.616619 | 6.642999 | 1.520576 |
| 69 | Kiddy GiRL-AND | 3349 | ![]() | 0.596910 | 6.739195 | 1.518253 |
| 59 | Chikyuu Shoujo Arjuna | 812 | ![]() | 0.599021 | 6.725342 | 1.517969 |
| 63 | I My Me! Strawberry Eggs | 509 | ![]() | 0.597997 | 6.728957 | 1.517641 |
| 5 | Uchuu Kuubo Blue Noah | 5763 | ![]() | 0.649470 | 6.417003 | 1.514600 |
| 22 | Cardfight!! Vanguard: overDress Season 2 | 48862 | ![]() | 0.625543 | 6.538178 | 1.512438 |
| 19 | Chikyuu Bouei Kazoku | 1962 | ![]() | 0.626203 | 6.518807 | 1.510094 |
| 23 | Ginga Tetsudou Monogatari: Eien e no Bunkiten | 2717 | ![]() | 0.625000 | 6.498912 | 1.506087 |
| 88 | Juubee-chan 2: Siberia Yagyuu no Gyakushuu | 636 | ![]() | 0.583973 | 6.720193 | 1.504406 |
| 93 | Shinkon Gattai Godannar!! 2nd Season | 1104 | ![]() | 0.583333 | 6.713147 | 1.502805 |
| 37 | Weiß Kreuz Glühen | 446 | ![]() | 0.618422 | 6.483184 | 1.498136 |
| 81 | Koutetsushin Jeeg | 2157 | ![]() | 0.585548 | 6.665282 | 1.497508 |
| 95 | Fireball Charming | 10348 | ![]() | 0.583333 | 6.646644 | 1.492830 |
| 85 | Juubee-chan: Lovely Gantai no Himitsu | 635 | ![]() | 0.584569 | 6.632706 | 1.491789 |
| 97 | Godzilla: S.P | 43229 | ![]() | 0.583333 | 6.633579 | 1.490870 |
| 101 | Hikari to Mizu no Daphne | 1082 | ![]() | 0.576568 | 6.667648 | 1.490230 |
| 73 | Shinkon Gattai Godannar!! | 1103 | ![]() | 0.593183 | 6.571968 | 1.490001 |
get_recommendations2(title='Detective Conan', n=24)
| Name | anime_id | Image URL | similarity_score | weighted_rating | final_score | |
|---|---|---|---|---|---|---|
| 2 | Dr. Stone: New World | 48549 | https://cdn.myanimelist.net/images/anime/1316/... | 0.777778 | 8.205729 | 1.891970 |
| 1 | Dr. Stone: Stone Wars | 40852 | https://cdn.myanimelist.net/images/anime/1711/... | 0.778460 | 8.164181 | 1.886318 |
| 10 | Kamisama Hajimemashita◎ | 25681 | https://cdn.myanimelist.net/images/anime/8/691... | 0.724089 | 8.194550 | 1.844658 |
| 53 | Mushishi Zoku Shou 2nd Season | 24701 | https://cdn.myanimelist.net/images/anime/9/680... | 0.599115 | 8.689948 | 1.812740 |
| 70 | Mushishi Zoku Shou | 21939 | https://cdn.myanimelist.net/images/anime/13/58... | 0.598093 | 8.664767 | 1.808094 |
| 49 | Kaguya-sama wa Kokurasetai? Tensai-tachi no Re... | 40591 | https://cdn.myanimelist.net/images/anime/1764/... | 0.599410 | 8.635077 | 1.804760 |
| 58 | Grand Blue | 37105 | https://cdn.myanimelist.net/images/anime/1302/... | 0.598806 | 8.420849 | 1.772112 |
| 39 | Kaguya-sama wa Kokurasetai: Tensai-tachi no Re... | 37999 | https://cdn.myanimelist.net/images/anime/1295/... | 0.600087 | 8.406546 | 1.771056 |
| 64 | Karakai Jouzu no Takagi-san 3 | 49721 | https://cdn.myanimelist.net/images/anime/1861/... | 0.598334 | 8.382613 | 1.765976 |
| 94 | Tensei shitara Slime Datta Ken 2nd Season | 39551 | https://cdn.myanimelist.net/images/anime/1271/... | 0.596752 | 8.382957 | 1.764683 |
| 26 | Dr. Stone | 38691 | https://cdn.myanimelist.net/images/anime/1613/... | 0.611544 | 8.286605 | 1.762803 |
| 63 | Tensei shitara Slime Datta Ken 2nd Season Part 2 | 41487 | https://cdn.myanimelist.net/images/anime/1033/... | 0.598389 | 8.321181 | 1.756808 |
| 57 | Gin no Saji 2nd Season | 19363 | https://cdn.myanimelist.net/images/anime/8/579... | 0.598812 | 8.232757 | 1.743904 |
| 24 | Eizouken ni wa Te wo Dasu na! | 39792 | https://cdn.myanimelist.net/images/anime/1680/... | 0.613046 | 8.106870 | 1.737120 |
| 67 | Asobi Asobase | 37171 | https://cdn.myanimelist.net/images/anime/1139/... | 0.598220 | 8.175252 | 1.734775 |
| 80 | Kuragehime | 8129 | https://cdn.myanimelist.net/images/anime/3/248... | 0.597555 | 8.082057 | 1.720231 |
| 43 | Gin no Saji | 16918 | https://cdn.myanimelist.net/images/anime/6/492... | 0.599737 | 8.066827 | 1.719801 |
| 47 | Komi-san wa, Comyushou desu. 2nd Season | 50631 | https://cdn.myanimelist.net/images/anime/1108/... | 0.599512 | 8.055599 | 1.717925 |
| 73 | Kuroshitsuji: Book of Circus | 22145 | https://cdn.myanimelist.net/images/anime/6/648... | 0.597837 | 8.055676 | 1.716513 |
| 92 | Kanata no Astra | 39198 | https://cdn.myanimelist.net/images/anime/1784/... | 0.596770 | 8.058838 | 1.716080 |
| 93 | Karakai Jouzu no Takagi-san 2 | 38993 | https://cdn.myanimelist.net/images/anime/1393/... | 0.596756 | 8.043034 | 1.713698 |
| 0 | Meitantei Conan: Hannin no Hanzawa-san | 50010 | https://cdn.myanimelist.net/images/anime/1560/... | 0.838107 | 6.668162 | 1.712615 |
| 46 | Kakushigoto | 40716 | https://cdn.myanimelist.net/images/anime/1048/... | 0.599589 | 7.969162 | 1.705025 |
| 34 | High Score Girl II | 39570 | https://cdn.myanimelist.net/images/anime/1560/... | 0.600566 | 7.934028 | 1.700585 |
df = get_recommendations2(title='Hunter x Hunter (2011)', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Name | anime_id | Image URL | similarity_score | weighted_rating | final_score | |
|---|---|---|---|---|---|---|
| 22 | Fullmetal Alchemist: Brotherhood | 5114 | ![]() | 0.647648 | 9.097636 | 1.915146 |
| 17 | Monster | 19 | ![]() | 0.666667 | 8.858186 | 1.895395 |
| 2 | Hunter x Hunter | 136 | ![]() | 0.731087 | 8.397139 | 1.880995 |
| 9 | Hajime no Ippo | 263 | ![]() | 0.667818 | 8.744465 | 1.879315 |
| 1 | Cardcaptor Sakura | 232 | ![]() | 0.756345 | 8.145421 | 1.864706 |
| 35 | Yuu☆Yuu☆Hakusho | 392 | ![]() | 0.641396 | 8.448490 | 1.812460 |
| 6 | Naruto: Shippuuden | 1735 | ![]() | 0.669019 | 8.257899 | 1.807351 |
| 5 | Diamond no Ace: Act II | 38731 | ![]() | 0.669196 | 8.184648 | 1.796514 |
| 28 | Dragon Ball Z | 813 | ![]() | 0.644659 | 8.156166 | 1.771385 |
| 14 | D.Gray-man | 1482 | ![]() | 0.666992 | 8.009810 | 1.768414 |
| 12 | Naruto | 20 | ![]() | 0.667359 | 7.988501 | 1.765530 |
| 48 | Diamond no Ace: Second Season | 30230 | ![]() | 0.619072 | 8.255112 | 1.764478 |
| 15 | Bleach | 269 | ![]() | 0.666977 | 7.917476 | 1.754552 |
| 62 | Trigun | 6 | ![]() | 0.612214 | 8.210988 | 1.752030 |
| 47 | Fullmetal Alchemist | 121 | ![]() | 0.629099 | 8.106510 | 1.750711 |
| 84 | Rurouni Kenshin: Meiji Kenkaku Romantan | 45 | ![]() | 0.596837 | 8.275545 | 1.748643 |
| 24 | Dragon Ball | 223 | ![]() | 0.646483 | 7.955681 | 1.742863 |
| 65 | Black Clover | 34572 | ![]() | 0.611341 | 8.136070 | 1.740051 |
| 49 | Diamond no Ace | 18689 | ![]() | 0.617851 | 8.073270 | 1.736164 |
| 88 | Black Lagoon: The Second Barrage | 1519 | ![]() | 0.596225 | 8.159754 | 1.730754 |
| 25 | InuYasha | 249 | ![]() | 0.646463 | 7.853153 | 1.727466 |
| 11 | Claymore | 1818 | ![]() | 0.667495 | 7.732521 | 1.727249 |
| 7 | Fairy Tail (2014) | 22043 | ![]() | 0.667871 | 7.645865 | 1.714570 |
| 8 | Dragon Quest: Dai no Daibouken (2020) | 40906 | ![]() | 0.667840 | 7.643075 | 1.714126 |
Conclusion¶
The recommendation results for Darling in the FranXX and Detective Conan show that the system effectively identifies thematically similar anime by combining content similarity and weighted user ratings. The suggestions align well with genre, tone, and narrative style, demonstrating that the hybrid model captures both semantic and structural relevance. However, most of the top results tend to favor well-established and popular titles.
To improve diversity and promote discovery, the system can be enhanced by giving priority to newer anime titles, such as by adjusting the weighting formula to favor recent release years or applying a novelty/recency boost to the final score.
Lets do some improvements¶
Generate Recency Score Based on Release Year¶
This helper function adds a recency_score column to a DataFrame using the anime's release year:
It creates a numeric score between 0 and 1, where:
0represents the oldest anime1represents the most recent anime
Missing values in
release_yearare replaced with the minimum year to avoid distortion.This normalized score can be used to boost newer anime in recommendation results, helping balance popularity with recency and discovery.
This is a key step in promoting fresh or underrated titles within the system.
def _apply_recency_boost(df):
"""
Uses the numeric 'release_year' column to create a 0–1 scaled 'recency_score'.
"""
# Use 'release_year' directly
df['year'] = df['release_year']
# Fallback for missing years (fill with oldest year)
df['year'] = df['year'].fillna(df['year'].min())
# Scale to [0, 1]
min_year = df['year'].min()
max_year = df['year'].max()
year_range = max_year - min_year if max_year != min_year else 1
df['recency_score'] = (df['year'] - min_year) / year_range
return df
Hybrid Recommendation Function with Recency Boost¶
This enhanced version of the recommendation function combines:
- Content similarity (from hybrid features like genres, synopsis, etc.)
- Weighted user ratings
- Recency score (based on the anime's release year)
How It Works:¶
Computes similarity scores using
combined_simApplies
_apply_recency_boost()to normalize the anime's release year to arecency_score(0–1)Combines:
similarity_score(e.g., 85%)weighted_rating(remaining weight)recency_score(separate weight layered on top)
This setup promotes newer titles while still prioritizing relevance and quality.
Returns:¶
A DataFrame with:
- Anime name, ID, image
- Similarity score, weighted rating, recency score, final score, and release year
def get_recommendations3(title, n=10, similarity_weight=0.85, recency_weight=0.3):
"""
Recommend anime based on a given title using cosine similarity, weighted ratings, and recency.
Parameters:
- title (str): Name of the anime to base recommendations on.
- n (int): Number of recommendations to return (default: 10).
- similarity_weight (float): Weight for similarity in final score (default: 0.85).
- recency_weight (float): Weight for recency in final score (default: 0.1).
Returns:
- DataFrame: Top N recommended animes with relevant details.
Raises:
- ValueError: If the title is not found in anime_df or weights are invalid.
"""
# Check if title exists and get its label index
matching_animes = anime_df[anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
label = matching_animes.index[0]
# Convert label index to positional index
pos = anime_df.index.get_loc(label)
# Get pairwise similarity scores
sim_scores = list(enumerate(combined_sim[pos]))
# Sort by similarity score in descending order
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# Get top-N matches (excluding self), adjust n if fewer animes exist
max_recommendations = min(102, len(anime_df) - 1)
top_sim = sim_scores[1:max_recommendations + 1]
sim_indices = [i for i, _ in top_sim] # Indices of top similar animes
sim_scores_top = [score for _, score in top_sim] # Corresponding similarity scores
# Fetch recommended animes, including Release_date for recency
recommended_animes = anime_df.iloc[sim_indices][
['Name', 'anime_id', 'weighted_rating', 'Image URL', 'Type', 'Genres', 'Score', 'release_year']
].copy()
# Apply recency boost to add recency_score
recommended_animes = _apply_recency_boost(recommended_animes)
# Prepare for merging
qualified_animes = recommended_animes.copy()
qualified_animes.reset_index(drop=True, inplace=True)
# Create similarity DataFrame with correct scores
similarity_df = pd.DataFrame({
"anime_id": anime_df.iloc[sim_indices]["anime_id"].values,
"similarity_score": sim_scores_top
})
# Merge on correct column
qualified_animes = qualified_animes.merge(similarity_df, on="anime_id", how="left")
# Handle any unexpected NaNs in similarity_score
qualified_animes["similarity_score"] = qualified_animes["similarity_score"].fillna(
qualified_animes["similarity_score"].min()
)
# Calculate the weight for weighted_rating
rating_weight = 1 - similarity_weight
if rating_weight < 0:
raise ValueError("The sum of similarity_weight and recency_weight must be less than or equal to 1.")
# Calculate final score with recency
qualified_animes['final_score'] = (
rating_weight * qualified_animes['weighted_rating'] +
similarity_weight * qualified_animes['similarity_score'])
qualified_animes['recency_score'] = recency_weight * qualified_animes['recency_score'] + (1 - recency_weight) * qualified_animes['final_score']
# Return top-N sorted by final score
return qualified_animes.sort_values('final_score', ascending=False).head(n)[
['Name', 'anime_id', 'Image URL', 'similarity_score', 'weighted_rating', 'recency_score','release_year', 'final_score']
]
Lets show some tests.¶
df = get_recommendations3(title='Hunter x Hunter (2011)', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Name | anime_id | Image URL | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|
| 22 | Fullmetal Alchemist: Brotherhood | 5114 | ![]() | 0.647648 | 9.097636 | 1.570602 | 2009.0 | 1.915146 |
| 17 | Monster | 19 | ![]() | 0.666667 | 8.858186 | 1.531776 | 2004.0 | 1.895395 |
| 2 | Hunter x Hunter | 136 | ![]() | 0.731087 | 8.397139 | 1.496697 | 1999.0 | 1.880995 |
| 9 | Hajime no Ippo | 263 | ![]() | 0.667818 | 8.744465 | 1.500521 | 2000.0 | 1.879315 |
| 1 | Cardcaptor Sakura | 232 | ![]() | 0.756345 | 8.145421 | 1.480294 | 1998.0 | 1.864706 |
| 35 | Yuu☆Yuu☆Hakusho | 392 | ![]() | 0.641396 | 8.448490 | 1.413722 | 1992.0 | 1.812460 |
| 6 | Naruto: Shippuuden | 1735 | ![]() | 0.669019 | 8.257899 | 1.485146 | 2007.0 | 1.807351 |
| 5 | Diamond no Ace: Act II | 38731 | ![]() | 0.669196 | 8.184648 | 1.537560 | 2019.0 | 1.796514 |
| 28 | Dragon Ball Z | 813 | ![]() | 0.644659 | 8.156166 | 1.369969 | 1989.0 | 1.771385 |
| 14 | D.Gray-man | 1482 | ![]() | 0.666992 | 8.009810 | 1.452890 | 2006.0 | 1.768414 |
| 12 | Naruto | 20 | ![]() | 0.667359 | 7.988501 | 1.430871 | 2002.0 | 1.765530 |
| 48 | Diamond no Ace: Second Season | 30230 | ![]() | 0.619072 | 8.255112 | 1.495135 | 2015.0 | 1.764478 |
| 15 | Bleach | 269 | ![]() | 0.666977 | 7.917476 | 1.433186 | 2004.0 | 1.754552 |
| 62 | Trigun | 6 | ![]() | 0.612214 | 8.210988 | 1.401421 | 1998.0 | 1.752030 |
| 47 | Fullmetal Alchemist | 121 | ![]() | 0.629099 | 8.106510 | 1.425498 | 2003.0 | 1.750711 |
| 84 | Rurouni Kenshin: Meiji Kenkaku Romantan | 45 | ![]() | 0.596837 | 8.275545 | 1.389050 | 1996.0 | 1.748643 |
| 24 | Dragon Ball | 223 | ![]() | 0.646483 | 7.955681 | 1.335004 | 1986.0 | 1.742863 |
| 65 | Black Clover | 34572 | ![]() | 0.611341 | 8.136070 | 1.488035 | 2017.0 | 1.740051 |
| 49 | Diamond no Ace | 18689 | ![]() | 0.617851 | 8.073270 | 1.465315 | 2013.0 | 1.736164 |
| 88 | Black Lagoon: The Second Barrage | 1519 | ![]() | 0.596225 | 8.159754 | 1.426528 | 2006.0 | 1.730754 |
| 25 | InuYasha | 249 | ![]() | 0.646463 | 7.853153 | 1.394226 | 2000.0 | 1.727466 |
| 11 | Claymore | 1818 | ![]() | 0.667495 | 7.732521 | 1.429074 | 2007.0 | 1.727249 |
| 7 | Fairy Tail (2014) | 22043 | ![]() | 0.667871 | 7.645865 | 1.455199 | 2014.0 | 1.714570 |
| 8 | Dragon Quest: Dai no Daibouken (2020) | 40906 | ![]() | 0.667840 | 7.643075 | 1.484888 | 2020.0 | 1.714126 |
get_recommendations3(title='Hunter x Hunter (2011)', n=24)
| Name | anime_id | Image URL | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|
| 22 | Fullmetal Alchemist: Brotherhood | 5114 | https://cdn.myanimelist.net/images/anime/1208/... | 0.647648 | 9.097636 | 1.570602 | 2009.0 | 1.915146 |
| 17 | Monster | 19 | https://cdn.myanimelist.net/images/anime/10/18... | 0.666667 | 8.858186 | 1.531776 | 2004.0 | 1.895395 |
| 2 | Hunter x Hunter | 136 | https://cdn.myanimelist.net/images/anime/1305/... | 0.731087 | 8.397139 | 1.496697 | 1999.0 | 1.880995 |
| 9 | Hajime no Ippo | 263 | https://cdn.myanimelist.net/images/anime/4/863... | 0.667818 | 8.744465 | 1.500521 | 2000.0 | 1.879315 |
| 1 | Cardcaptor Sakura | 232 | https://cdn.myanimelist.net/images/anime/8/607... | 0.756345 | 8.145421 | 1.480294 | 1998.0 | 1.864706 |
| 35 | Yuu☆Yuu☆Hakusho | 392 | https://cdn.myanimelist.net/images/anime/1228/... | 0.641396 | 8.448490 | 1.413722 | 1992.0 | 1.812460 |
| 6 | Naruto: Shippuuden | 1735 | https://cdn.myanimelist.net/images/anime/1565/... | 0.669019 | 8.257899 | 1.485146 | 2007.0 | 1.807351 |
| 5 | Diamond no Ace: Act II | 38731 | https://cdn.myanimelist.net/images/anime/1153/... | 0.669196 | 8.184648 | 1.537560 | 2019.0 | 1.796514 |
| 28 | Dragon Ball Z | 813 | https://cdn.myanimelist.net/images/anime/1607/... | 0.644659 | 8.156166 | 1.369969 | 1989.0 | 1.771385 |
| 14 | D.Gray-man | 1482 | https://cdn.myanimelist.net/images/anime/13/75... | 0.666992 | 8.009810 | 1.452890 | 2006.0 | 1.768414 |
| 12 | Naruto | 20 | https://cdn.myanimelist.net/images/anime/13/17... | 0.667359 | 7.988501 | 1.430871 | 2002.0 | 1.765530 |
| 48 | Diamond no Ace: Second Season | 30230 | https://cdn.myanimelist.net/images/anime/9/743... | 0.619072 | 8.255112 | 1.495135 | 2015.0 | 1.764478 |
| 15 | Bleach | 269 | https://cdn.myanimelist.net/images/anime/3/404... | 0.666977 | 7.917476 | 1.433186 | 2004.0 | 1.754552 |
| 62 | Trigun | 6 | https://cdn.myanimelist.net/images/anime/7/203... | 0.612214 | 8.210988 | 1.401421 | 1998.0 | 1.752030 |
| 47 | Fullmetal Alchemist | 121 | https://cdn.myanimelist.net/images/anime/10/75... | 0.629099 | 8.106510 | 1.425498 | 2003.0 | 1.750711 |
| 84 | Rurouni Kenshin: Meiji Kenkaku Romantan | 45 | https://cdn.myanimelist.net/images/anime/1346/... | 0.596837 | 8.275545 | 1.389050 | 1996.0 | 1.748643 |
| 24 | Dragon Ball | 223 | https://cdn.myanimelist.net/images/anime/1887/... | 0.646483 | 7.955681 | 1.335004 | 1986.0 | 1.742863 |
| 65 | Black Clover | 34572 | https://cdn.myanimelist.net/images/anime/2/883... | 0.611341 | 8.136070 | 1.488035 | 2017.0 | 1.740051 |
| 49 | Diamond no Ace | 18689 | https://cdn.myanimelist.net/images/anime/5/542... | 0.617851 | 8.073270 | 1.465315 | 2013.0 | 1.736164 |
| 88 | Black Lagoon: The Second Barrage | 1519 | https://cdn.myanimelist.net/images/anime/3/837... | 0.596225 | 8.159754 | 1.426528 | 2006.0 | 1.730754 |
| 25 | InuYasha | 249 | https://cdn.myanimelist.net/images/anime/1589/... | 0.646463 | 7.853153 | 1.394226 | 2000.0 | 1.727466 |
| 11 | Claymore | 1818 | https://cdn.myanimelist.net/images/anime/3/218... | 0.667495 | 7.732521 | 1.429074 | 2007.0 | 1.727249 |
| 7 | Fairy Tail (2014) | 22043 | https://cdn.myanimelist.net/images/anime/3/605... | 0.667871 | 7.645865 | 1.455199 | 2014.0 | 1.714570 |
| 8 | Dragon Quest: Dai no Daibouken (2020) | 40906 | https://cdn.myanimelist.net/images/anime/1499/... | 0.667840 | 7.643075 | 1.484888 | 2020.0 | 1.714126 |
Conclusion:¶
Adding a recency score to the recommendation system introduces a valuable balance between relevance and novelty. While traditional hybrid models tend to favor older, highly rated classics, integrating recency allows newer anime to surface more prominently without sacrificing quality. This improves content discovery, keeps recommendations timely, and better reflects user interest in modern trends.
To further enhance the quality of recommendations, it is essential to fine-tune the weights assigned to each content feature (e.g., genres, synopsis, studio, type). While equal weighting provides a neutral baseline, not all features contribute equally to user preferences. For instance, genres and synopsis often carry more semantic relevance than production studios or episode count. By adjusting these weights based on empirical performance or user feedback, the system can better align with real-world viewing behavior, resulting in more accurate, engaging, and personalized recommendations.
lets adjust the weights of the features¶
# New weights based on user preference
weights = [0.35, 0.25, 0.15, 0.10, 0.075, 0.075, 0.05]
# Combine similarities
combined_sim2 = (weights[0] * synopsis_sim +
weights[1] * genres_sim +
weights[2] * type_sim +
weights[3] * studios_sim +
weights[4] * episodes_sim +
weights[5] * source_sim)
This function refines anime recommendations by introducing a flexible weighting scheme for similarity, rating, and recency. It uses a new similarity matrix (combined_sim2) generated from custom-weighted feature similarities, allowing more control over what influences the recommendations.
How It Works:¶
Retrieves anime similar to the given
titleusing the custom similarity matrix.Calculates a
recency_scorescaled from 0–1 based on release year.Computes a final score based on:
similarity_weight(semantic closeness)weighted_rating(user consensus)recency_weight(favoring newer content)
Applies a composite scoring formula:
$$\text{final_score} = \left[ \text{rating} \cdot w_r + \text{similarity} \cdot w_s \right] \cdot (1 - w_{rec}) + \text{recency} \cdot w_{rec}$$
where $w_r + w_s + w_{rec} = 1$
Explaination:¶
Feature importance isn't uniform—genres and synopsis usually reflect content better than studio or type. By customizing the weights used in the similarity matrix (combined_sim2), the system can prioritize features that align more closely with user preferences, improving both accuracy and relevance of recommendations.
def get_recommendations4(title, n=10, similarity_weight=0.7, recency_weight=0.1):
# Check if title exists and get its label index
matching_animes = anime_df[anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
label = matching_animes.index[0]
pos = anime_df.index.get_loc(label)
sim_scores = list(enumerate(combined_sim2[pos]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
max_recommendations = min(102, len(anime_df) - 1)
top_sim = sim_scores[1:max_recommendations + 1]
sim_indices = [i for i, _ in top_sim]
sim_scores_top = [score for _, score in top_sim]
recommended_animes = anime_df.iloc[sim_indices][
['Name', 'anime_id', 'weighted_rating', 'Image URL', 'Type', 'Genres', 'Score', 'release_year']
].copy()
recommended_animes = _apply_recency_boost(recommended_animes)
qualified_animes = recommended_animes.copy()
qualified_animes.reset_index(drop=True, inplace=True)
similarity_df = pd.DataFrame({
"anime_id": anime_df.iloc[sim_indices]["anime_id"].values,
"similarity_score": sim_scores_top
})
qualified_animes = qualified_animes.merge(similarity_df, on="anime_id", how="left")
qualified_animes["similarity_score"] = qualified_animes["similarity_score"].fillna(
qualified_animes["similarity_score"].min()
)
rating_weight = 1 - similarity_weight - recency_weight
if rating_weight < 0:
raise ValueError("The sum of similarity_weight and recency_weight must be less than or equal to 1.")
qualified_animes['final_score'] = (
rating_weight * qualified_animes['weighted_rating'] +
similarity_weight * qualified_animes['similarity_score'])*(1-recency_weight) +(
recency_weight * qualified_animes['recency_score']
)
return qualified_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'similarity_score', 'weighted_rating', 'recency_score', 'release_year', 'final_score']
]
- lets do somme tests
df = get_recommendations4(title='Hunter x Hunter', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|
| 0 | ![]() | Hunter x Hunter (2011) | 11061 | 0.685284 | 9.037173 | 0.793103 | 2011.0 | 2.137730 |
| 23 | ![]() | Fullmetal Alchemist: Brotherhood | 5114 | 0.518537 | 9.097636 | 0.758621 | 2009.0 | 2.040115 |
| 49 | ![]() | Bleach: Sennen Kessen-hen | 41467 | 0.483582 | 9.048079 | 0.982759 | 2022.0 | 2.031586 |
| 45 | ![]() | One Piece | 21 | 0.487591 | 8.686696 | 0.586207 | 1999.0 | 1.929409 |
| 7 | ![]() | Naruto: Shippuuden | 1735 | 0.553618 | 8.257899 | 0.724138 | 2007.0 | 1.907615 |
| 36 | ![]() | Yuu☆Yuu☆Hakusho | 392 | 0.505619 | 8.448490 | 0.465517 | 1992.0 | 1.885820 |
| 67 | ![]() | Jigokuraku | 46569 | 0.476893 | 8.224549 | 1.000000 | 2023.0 | 1.880862 |
| 9 | ![]() | D.Gray-man | 1482 | 0.552713 | 8.009810 | 0.706897 | 2006.0 | 1.860664 |
| 73 | ![]() | Magi: The Kingdom of Magic | 18115 | 0.475000 | 8.212752 | 0.827586 | 2013.0 | 1.860304 |
| 53 | ![]() | Hunter x Hunter: Original Video Animation | 137 | 0.481455 | 8.258539 | 0.637931 | 2002.0 | 1.853647 |
| 83 | ![]() | Black Clover | 34572 | 0.471095 | 8.136070 | 0.896552 | 2017.0 | 1.850938 |
| 8 | ![]() | Naruto | 20 | 0.553181 | 7.988501 | 0.637931 | 2002.0 | 1.850228 |
| 3 | ![]() | Bleach | 269 | 0.556985 | 7.917476 | 0.672414 | 2004.0 | 1.843288 |
| 82 | ![]() | Hunter x Hunter: Greed Island Final | 139 | 0.471418 | 8.207576 | 0.672414 | 2004.0 | 1.841598 |
| 25 | ![]() | Dragon Ball Z | 813 | 0.517795 | 8.156166 | 0.413793 | 1989.0 | 1.835700 |
| 43 | ![]() | Fullmetal Alchemist | 121 | 0.493649 | 8.106510 | 0.655172 | 2003.0 | 1.835688 |
| 62 | ![]() | Hunter x Hunter: Greed Island | 138 | 0.477618 | 8.142559 | 0.655172 | 2003.0 | 1.832077 |
| 63 | ![]() | Magi: The Labyrinth of Magic | 14513 | 0.477189 | 8.014632 | 0.810345 | 2012.0 | 1.824297 |
| 2 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | 0.559280 | 7.643075 | 0.948276 | 2020.0 | 1.822927 |
| 5 | ![]() | Fairy Tail (2014) | 22043 | 0.554866 | 7.645865 | 0.844828 | 2014.0 | 1.810304 |
| 20 | ![]() | InuYasha | 249 | 0.522848 | 7.853153 | 0.603448 | 2000.0 | 1.803306 |
| 6 | ![]() | Fairy Tail: Final Series | 35972 | 0.554754 | 7.561180 | 0.913793 | 2018.0 | 1.801886 |
| 60 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | 0.478409 | 7.839802 | 0.879310 | 2016.0 | 1.800493 |
| 22 | ![]() | Dragon Ball | 223 | 0.520077 | 7.955681 | 0.362069 | 1986.0 | 1.795878 |
df = get_recommendations4(title='Darling in the FranXX', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|
| 52 | ![]() | Shigatsu wa Kimi no Uso | 23273 | 0.465837 | 8.646914 | 0.808511 | 2014.0 | 1.930773 |
| 99 | ![]() | Vivy: Fluorite Eye's Song | 46095 | 0.447036 | 8.394183 | 0.957447 | 2021.0 | 1.888330 |
| 18 | ![]() | Lycoris Recoil | 50709 | 0.492501 | 8.183950 | 0.978723 | 2022.0 | 1.881259 |
| 83 | ![]() | Kidou Senshi Gundam: Tekketsu no Orphans 2nd Season | 33051 | 0.450011 | 8.188491 | 0.851064 | 2016.0 | 1.842542 |
| 9 | ![]() | Plastic Memories | 27775 | 0.518499 | 7.904120 | 0.829787 | 2015.0 | 1.832374 |
| 29 | ![]() | Senki Zesshou Symphogear XV | 32843 | 0.482099 | 7.975510 | 0.914894 | 2019.0 | 1.830804 |
| 32 | ![]() | Carole & Tuesday | 37435 | 0.481744 | 7.851340 | 0.914894 | 2019.0 | 1.808229 |
| 85 | ![]() | Kidou Senshi Gundam 00 | 2581 | 0.449888 | 8.084751 | 0.659574 | 2007.0 | 1.804642 |
| 63 | ![]() | Kidou Senshi Gundam 00 Second Season | 3927 | 0.455764 | 8.048096 | 0.680851 | 2008.0 | 1.803874 |
| 8 | ![]() | Texhnolyze | 26 | 0.521140 | 7.717234 | 0.574468 | 2003.0 | 1.774867 |
| 72 | ![]() | Mahou Shoujo Lyrical Nanoha A's | 77 | 0.452184 | 7.889765 | 0.617021 | 2005.0 | 1.766736 |
| 78 | ![]() | Terra e... (TV) | 2158 | 0.450818 | 7.748906 | 0.659574 | 2007.0 | 1.744776 |
| 4 | ![]() | Guilty Crown | 10793 | 0.524456 | 7.417078 | 0.744681 | 2011.0 | 1.739949 |
| 22 | ![]() | Senki Zesshou Symphogear AXZ | 32836 | 0.486692 | 7.472081 | 0.872340 | 2017.0 | 1.738824 |
| 64 | ![]() | Kidou Senshi Gundam SEED | 93 | 0.454633 | 7.724560 | 0.553191 | 2002.0 | 1.732159 |
| 13 | ![]() | Kiznaiver | 31798 | 0.501954 | 7.374965 | 0.851064 | 2016.0 | 1.728831 |
| 7 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | 0.522408 | 7.309665 | 0.829787 | 2015.0 | 1.727836 |
| 86 | ![]() | Re:Creators | 34561 | 0.449618 | 7.539231 | 0.872340 | 2017.0 | 1.727555 |
| 71 | ![]() | Irozuku Sekai no Ashita kara | 37497 | 0.452954 | 7.504221 | 0.893617 | 2018.0 | 1.725483 |
| 16 | ![]() | Senki Zesshou Symphogear G | 15793 | 0.493236 | 7.396078 | 0.787234 | 2013.0 | 1.720756 |
| 49 | ![]() | SSSS.Dynazenon | 40870 | 0.467888 | 7.375668 | 0.957447 | 2021.0 | 1.718134 |
| 60 | ![]() | Kidou Senshi Zeta Gundam | 85 | 0.457266 | 7.828992 | 0.191489 | 1985.0 | 1.716445 |
| 59 | ![]() | Macross | 1088 | 0.458960 | 7.828247 | 0.127660 | 1982.0 | 1.710995 |
| 31 | ![]() | Senki Zesshou Symphogear GX | 21573 | 0.481761 | 7.358336 | 0.829787 | 2015.0 | 1.710989 |
get_recommendations4(title='Darling in the FranXX', n=24)
| Image URL | Name | anime_id | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|
| 52 | https://cdn.myanimelist.net/images/anime/3/671... | Shigatsu wa Kimi no Uso | 23273 | 0.465837 | 8.646914 | 0.808511 | 2014.0 | 1.930773 |
| 99 | https://cdn.myanimelist.net/images/anime/1637/... | Vivy: Fluorite Eye's Song | 46095 | 0.447036 | 8.394183 | 0.957447 | 2021.0 | 1.888330 |
| 18 | https://cdn.myanimelist.net/images/anime/1392/... | Lycoris Recoil | 50709 | 0.492501 | 8.183950 | 0.978723 | 2022.0 | 1.881259 |
| 83 | https://cdn.myanimelist.net/images/anime/6/808... | Kidou Senshi Gundam: Tekketsu no Orphans 2nd S... | 33051 | 0.450011 | 8.188491 | 0.851064 | 2016.0 | 1.842542 |
| 9 | https://cdn.myanimelist.net/images/anime/4/727... | Plastic Memories | 27775 | 0.518499 | 7.904120 | 0.829787 | 2015.0 | 1.832374 |
| 29 | https://cdn.myanimelist.net/images/anime/1899/... | Senki Zesshou Symphogear XV | 32843 | 0.482099 | 7.975510 | 0.914894 | 2019.0 | 1.830804 |
| 32 | https://cdn.myanimelist.net/images/anime/1611/... | Carole & Tuesday | 37435 | 0.481744 | 7.851340 | 0.914894 | 2019.0 | 1.808229 |
| 85 | https://cdn.myanimelist.net/images/anime/3/132... | Kidou Senshi Gundam 00 | 2581 | 0.449888 | 8.084751 | 0.659574 | 2007.0 | 1.804642 |
| 63 | https://cdn.myanimelist.net/images/anime/9/127... | Kidou Senshi Gundam 00 Second Season | 3927 | 0.455764 | 8.048096 | 0.680851 | 2008.0 | 1.803874 |
| 8 | https://cdn.myanimelist.net/images/anime/1027/... | Texhnolyze | 26 | 0.521140 | 7.717234 | 0.574468 | 2003.0 | 1.774867 |
| 72 | https://cdn.myanimelist.net/images/anime/4/676... | Mahou Shoujo Lyrical Nanoha A's | 77 | 0.452184 | 7.889765 | 0.617021 | 2005.0 | 1.766736 |
| 78 | https://cdn.myanimelist.net/images/anime/1013/... | Terra e... (TV) | 2158 | 0.450818 | 7.748906 | 0.659574 | 2007.0 | 1.744776 |
| 4 | https://cdn.myanimelist.net/images/anime/1566/... | Guilty Crown | 10793 | 0.524456 | 7.417078 | 0.744681 | 2011.0 | 1.739949 |
| 22 | https://cdn.myanimelist.net/images/anime/3/865... | Senki Zesshou Symphogear AXZ | 32836 | 0.486692 | 7.472081 | 0.872340 | 2017.0 | 1.738824 |
| 64 | https://cdn.myanimelist.net/images/anime/1792/... | Kidou Senshi Gundam SEED | 93 | 0.454633 | 7.724560 | 0.553191 | 2002.0 | 1.732159 |
| 13 | https://cdn.myanimelist.net/images/anime/6/784... | Kiznaiver | 31798 | 0.501954 | 7.374965 | 0.851064 | 2016.0 | 1.728831 |
| 7 | https://cdn.myanimelist.net/images/anime/7/765... | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | 0.522408 | 7.309665 | 0.829787 | 2015.0 | 1.727836 |
| 86 | https://cdn.myanimelist.net/images/anime/11/85... | Re:Creators | 34561 | 0.449618 | 7.539231 | 0.872340 | 2017.0 | 1.727555 |
| 71 | https://cdn.myanimelist.net/images/anime/1424/... | Irozuku Sekai no Ashita kara | 37497 | 0.452954 | 7.504221 | 0.893617 | 2018.0 | 1.725483 |
| 16 | https://cdn.myanimelist.net/images/anime/2/866... | Senki Zesshou Symphogear G | 15793 | 0.493236 | 7.396078 | 0.787234 | 2013.0 | 1.720756 |
| 49 | https://cdn.myanimelist.net/images/anime/1880/... | SSSS.Dynazenon | 40870 | 0.467888 | 7.375668 | 0.957447 | 2021.0 | 1.718134 |
| 60 | https://cdn.myanimelist.net/images/anime/6/111... | Kidou Senshi Zeta Gundam | 85 | 0.457266 | 7.828992 | 0.191489 | 1985.0 | 1.716445 |
| 59 | https://cdn.myanimelist.net/images/anime/1/244... | Macross | 1088 | 0.458960 | 7.828247 | 0.127660 | 1982.0 | 1.710995 |
| 31 | https://cdn.myanimelist.net/images/anime/9/866... | Senki Zesshou Symphogear GX | 21573 | 0.481761 | 7.358336 | 0.829787 | 2015.0 | 1.710989 |
Conclusion:¶
Effect of Fine-Tuned Feature Weights and Recency in get_recommendations4
The updated results for Darling in the FranXX using get_recommendations4() demonstrate the impact of fine-tuning feature similarity weights and applying a recency-aware scoring formula.
🔍 Key Improvements:¶
- The top results now prioritize recent anime (e.g.,
Lycoris Recoil(2022),Vivy(2021),Plastic Memories(2015),SSSS.Dynazenon(2021)). - Older but highly relevant series (like
TexhnolyzeorGundam titles) are still present, but appear lower than in earlier versions — showing a more balanced mix of new and classic titles. - The final scores reflect this trade-off: titles with moderate similarity but high recency and rating (like
Shigatsu wa Kimi no UsoorVivy) outperform older titles with similar genres but less recency impact.
By tuning the similarity matrix (combined_sim2) and adjusting the final score formula to incorporate recency, the system produces more modern, user-relevant recommendations while maintaining thematic consistency. This approach increases discovery of newer anime without disregarding high-quality classics — striking a thoughtful balance between relevance and freshness.
Lets adjust the weifhts one more time to get more satidying results¶
Combine Similarities Using Fine-Tuned Feature Weights¶
This cell creates a new hybrid content similarity matrix (combined_sim3) by assigning custom weights to each encoded feature, based on assumed or collected user preferences:
- Genres:
30%— important for thematic alignment - Synopsis:
35%— most descriptive of plot and tone - Type:
15%— format (e.g., TV vs. Movie) moderately impacts relevance - Studios:
10%— aesthetic/style influence - Episodes:
5%— minor but helpful for pacing or length matching - Source:
5%— light effect (e.g., manga vs. original)
These weights reflect the relative importance of each feature in shaping user preferences, resulting in a more personalized similarity matrix.
# Fine-tuned weights based on user preferences
weights = [0.30, 0.35, 0.15, 0.10, 0.05, 0.05]
# Combine similarities
combined_sim3 = (weights[0] * genres_sim + # Genres: 0.30
weights[1] * synopsis_sim + # Synopsis: 0.35
weights[2] * type_sim + # Type: 0.15
weights[3] * studios_sim + # Studios: 0.10
weights[4] * episodes_sim + # Episodes: 0.05
weights[5] * source_sim # Source: 0.05
)
Create Normalized Recency Score from Release Year¶
This utility function creates a recency_score ranging from 0 to 1 based on the anime's release year. It helps prioritize newer titles during recommendation scoring.
Methodology:¶
Reads the anime's
release_yearcolumn.Fills missing years with the minimum known year (to avoid boosting incomplete data).
Applies min-max normalization:
$$\text{recency_score} = \frac{(\text{year} - \text{min})}{(\text{max}-\text{ min})}$$
Returns the same DataFrame with a new column:
recency_score
This allows the system to boost newer anime titles when computing the final recommendation score.
def _apply_recency_boost(df):
"""
Uses the numeric 'release_year' column to create a 0–1 scaled 'recency_score'.
"""
# Use 'release_year' directly
df['year'] = df['release_year']
# Fallback for missing years (fill with oldest year)
df['year'] = df['year'].fillna(df['year'].min())
# Scale to [0, 1]
min_year = df['year'].min()
max_year = df['year'].max()
year_range = max_year - min_year if max_year != min_year else 1
df['recency_score'] = (df['year'] - min_year) / year_range
return df
Final Hybrid Recommendation Function with Fine-Tuned Weights and Recency Boost¶
get_recommendations101()delivers anime recommendations by combining:- Fine-tuned similarity scores from
combined_sim3, based on weighted feature similarity (genres, synopsis, type, etc.) - Weighted user ratings for quality assessment
- Recency scoring to boost newer releases
- Fine-tuned similarity scores from
Methodology:
- Finds the input title in
anime_df. - Retrieves the most similar anime using
combined_sim3, which includes custom-tuned feature weights. - Applies
_apply_recency_boost()to normalize each anime’s release year into arecency_score(0–1). - Calculates a composite score:
$$ \text{final_score} = \left[ \text{rating} \cdot (1 - w_s) + \text{similarity} \cdot w_s \right] \cdot (1 - w_r) + \text{recency} \cdot w_r $$
Where:
- $w_s$: similarity weight (default 0.85)
- $w_r$: recency weight (default 0.20)
- Finds the input title in
Output:
Returns the top N recommendations, sorted by the
final_score, including:- Anime name, type, image, rating, similarity, recency score, and release year
def get_recommendations101(title, n=10, similarity_weight=0.85, recency_weight=0.2):
# Check if title exists and get its label index
matching_animes = anime_df[anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
label = matching_animes.index[0]
pos = anime_df.index.get_loc(label)
sim_scores = list(enumerate(combined_sim3[pos]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
max_recommendations = min(52, len(anime_df) - 1)
top_sim = sim_scores[1:max_recommendations + 1]
sim_indices = [i for i, _ in top_sim]
sim_scores_top = [score for _, score in top_sim]
recommended_animes = anime_df.iloc[sim_indices][
['Name', 'anime_id', 'weighted_rating', 'Image URL', 'Type', 'Genres', 'Score', 'release_year']
].copy()
recommended_animes = _apply_recency_boost(recommended_animes)
qualified_animes = recommended_animes.copy()
qualified_animes.reset_index(drop=True, inplace=True)
similarity_df = pd.DataFrame({
"anime_id": anime_df.iloc[sim_indices]["anime_id"].values,
"similarity_score": sim_scores_top
})
qualified_animes = qualified_animes.merge(similarity_df, on="anime_id", how="left")
qualified_animes["similarity_score"] = qualified_animes["similarity_score"].fillna(
qualified_animes["similarity_score"].min()
)
rating_weight = 1 - similarity_weight
if rating_weight < 0:
raise ValueError("The sum of similarity_weight and recency_weight must be less than or equal to 1.")
qualified_animes['final_score'] = (
rating_weight * qualified_animes['weighted_rating'] +
similarity_weight * qualified_animes['similarity_score'])* (1-recency_weight) + (recency_weight * qualified_animes['recency_score'])
return qualified_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name','Type', 'anime_id', 'similarity_score', 'weighted_rating', 'recency_score', 'release_year', 'final_score']
]
- Lets do some tests
df = get_recommendations101(title='Darling in the FranXX', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Type | anime_id | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|---|
| 37 | ![]() | 86 Part 2 | TV | 48569 | 0.474157 | 8.692836 | 0.977778 | 2021.0 | 1.561122 |
| 32 | ![]() | Shigatsu wa Kimi no Uso | TV | 23273 | 0.476193 | 8.646914 | 0.822222 | 2014.0 | 1.525885 |
| 30 | ![]() | 86 | TV | 41457 | 0.477333 | 8.270491 | 0.977778 | 2021.0 | 1.512601 |
| 10 | ![]() | Plastic Memories | TV | 27775 | 0.511800 | 7.904120 | 0.844444 | 2015.0 | 1.465407 |
| 38 | ![]() | Kidou Senshi Gundam 00 Second Season | TV | 3927 | 0.474065 | 8.048096 | 0.688889 | 2008.0 | 1.425914 |
| 3 | ![]() | Kiznaiver | TV | 31798 | 0.520255 | 7.374965 | 0.866667 | 2016.0 | 1.412103 |
| 26 | ![]() | SSSS.Dynazenon | TV | 40870 | 0.478243 | 7.375668 | 0.977778 | 2021.0 | 1.405841 |
| 8 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | TV | 30549 | 0.515709 | 7.309665 | 0.844444 | 2015.0 | 1.396731 |
| 43 | ![]() | Senki Zesshou Symphogear AXZ | TV | 32836 | 0.472047 | 7.472081 | 0.888889 | 2017.0 | 1.395420 |
| 5 | ![]() | Guilty Crown | TV | 10793 | 0.517757 | 7.417078 | 0.755556 | 2011.0 | 1.393235 |
| 9 | ![]() | Texhnolyze | TV | 26 | 0.514441 | 7.717234 | 0.577778 | 2003.0 | 1.391444 |
| 50 | ![]() | Terra e... (TV) | TV | 2158 | 0.469119 | 7.748906 | 0.666667 | 2007.0 | 1.382203 |
| 7 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus | TV | 17080 | 0.515733 | 7.177703 | 0.844444 | 2015.0 | 1.380912 |
| 16 | ![]() | Grancrest Senki | TV | 34279 | 0.488715 | 7.208601 | 0.911111 | 2018.0 | 1.379580 |
| 24 | ![]() | Senki Zesshou Symphogear G | TV | 15793 | 0.478591 | 7.396078 | 0.800000 | 2013.0 | 1.372971 |
| 36 | ![]() | Kidou Senshi Gundam SEED | TV | 93 | 0.474354 | 7.724560 | 0.555556 | 2002.0 | 1.360619 |
| 48 | ![]() | Macross Δ | TV | 28013 | 0.469734 | 7.213781 | 0.866667 | 2016.0 | 1.358406 |
| 6 | ![]() | Classroom☆Crisis | TV | 30383 | 0.516485 | 6.949188 | 0.844444 | 2015.0 | 1.354001 |
| 17 | ![]() | Engage Kiss | TV | 51417 | 0.486690 | 6.829794 | 1.000000 | 2022.0 | 1.350524 |
| 23 | ![]() | Buddy Complex | TV | 21437 | 0.479038 | 7.088736 | 0.822222 | 2014.0 | 1.340838 |
| 19 | ![]() | Star Driver: Kagayaki no Takuto | TV | 8934 | 0.484278 | 7.159537 | 0.733333 | 2010.0 | 1.335120 |
| 28 | ![]() | Hypnosis Mic: Division Rap Battle - Rhyme Anima | TV | 40803 | 0.477640 | 6.764323 | 0.955556 | 2020.0 | 1.327625 |
| 12 | ![]() | Kidou Senshi Gundam SEED Destiny | TV | 94 | 0.507264 | 7.158412 | 0.600000 | 2004.0 | 1.323949 |
| 21 | ![]() | Senki Zesshou Symphogear | TV | 11751 | 0.481656 | 7.004872 | 0.777778 | 2012.0 | 1.323666 |
df = get_recommendations101(title='Jujutsu Kaisen', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Type | anime_id | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|---|
| 2 | ![]() | Chainsaw Man | TV | 44511 | 0.553409 | 8.584003 | 0.974359 | 2022.0 | 1.601270 |
| 19 | ![]() | Bleach: Sennen Kessen-hen | TV | 41467 | 0.462662 | 9.048079 | 0.974359 | 2022.0 | 1.595251 |
| 17 | ![]() | Shingeki no Kyojin: The Final Season | TV | 40028 | 0.474939 | 8.796520 | 0.923077 | 2020.0 | 1.563156 |
| 4 | ![]() | Jigokuraku | TV | 46569 | 0.551048 | 8.224549 | 1.000000 | 2023.0 | 1.561658 |
| 36 | ![]() | Vinland Saga Season 2 | TV | 49387 | 0.452953 | 8.771356 | 1.000000 | 2023.0 | 1.560571 |
| 34 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | TV | 47778 | 0.453753 | 8.794506 | 0.948718 | 2021.0 | 1.553637 |
| 7 | ![]() | Kimetsu no Yaiba | TV | 38000 | 0.506242 | 8.498085 | 0.897436 | 2019.0 | 1.543502 |
| 27 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | TV | 51019 | 0.457070 | 8.467293 | 1.000000 | 2023.0 | 1.526882 |
| 25 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | TV | 49926 | 0.457433 | 8.380549 | 0.948718 | 2021.0 | 1.506464 |
| 16 | ![]() | Dorohedoro | TV | 38668 | 0.481031 | 8.048611 | 0.923077 | 2020.0 | 1.477550 |
| 13 | ![]() | Noragami Aragoto | TV | 30503 | 0.497718 | 8.156397 | 0.794872 | 2015.0 | 1.476190 |
| 10 | ![]() | Heion Sedai no Idaten-tachi | TV | 42625 | 0.501803 | 7.609725 | 0.948718 | 2021.0 | 1.444137 |
| 29 | ![]() | Toaru Kagaku no Railgun S | TV | 16049 | 0.456802 | 8.005639 | 0.743590 | 2013.0 | 1.420020 |
| 26 | ![]() | Magi: Sinbad no Bouken (TV) | TV | 31741 | 0.457099 | 7.839802 | 0.820513 | 2016.0 | 1.415706 |
| 42 | ![]() | Noragami | TV | 20507 | 0.451205 | 7.947757 | 0.769231 | 2014.0 | 1.414397 |
| 1 | ![]() | The God of High School | TV | 41353 | 0.553590 | 7.067536 | 0.923077 | 2020.0 | 1.409161 |
| 5 | ![]() | Garo: Honoo no Kokuin | TV | 23311 | 0.547912 | 7.317473 | 0.769231 | 2014.0 | 1.404523 |
| 6 | ![]() | Shingeki no Bahamut: Virgin Soul | TV | 30736 | 0.509020 | 7.409368 | 0.846154 | 2017.0 | 1.404489 |
| 18 | ![]() | Nanatsu no Taizai: Imashime no Fukkatsu | TV | 34577 | 0.464332 | 7.586863 | 0.871795 | 2018.0 | 1.400528 |
| 30 | ![]() | D.Gray-man Hallow | TV | 32370 | 0.456173 | 7.671143 | 0.820513 | 2016.0 | 1.394837 |
| 15 | ![]() | Akame ga Kill! | TV | 22199 | 0.494949 | 7.468480 | 0.769231 | 2014.0 | 1.386629 |
| 20 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou | TV | 40496 | 0.458735 | 7.375983 | 0.923077 | 2020.0 | 1.381673 |
| 41 | ![]() | Nanatsu no Taizai | TV | 23755 | 0.451250 | 7.668250 | 0.769231 | 2014.0 | 1.380887 |
| 22 | ![]() | Shingeki no Bahamut: Genesis | TV | 21843 | 0.458067 | 7.607811 | 0.769231 | 2014.0 | 1.378269 |
df = get_recommendations101(title='Naruto: Shippuuden', n=10)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Type | anime_id | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|---|
| 3 | ![]() | Bleach: Sennen Kessen-hen | TV | 41467 | 0.608061 | 9.048079 | 0.976744 | 2022.0 | 1.694600 |
| 15 | ![]() | Hunter x Hunter (2011) | TV | 11061 | 0.554940 | 9.037173 | 0.720930 | 2011.0 | 1.606006 |
| 0 | ![]() | Naruto | TV | 20 | 0.774972 | 7.988501 | 0.511628 | 2002.0 | 1.587927 |
| 38 | ![]() | Fullmetal Alchemist: Brotherhood | TV | 5114 | 0.517224 | 9.097636 | 0.674419 | 2009.0 | 1.578312 |
| 17 | ![]() | Black Clover | TV | 34572 | 0.554523 | 8.136070 | 0.860465 | 2017.0 | 1.525497 |
| 8 | ![]() | Akatsuki no Yona | TV | 25013 | 0.566378 | 8.022766 | 0.790698 | 2014.0 | 1.506009 |
| 2 | ![]() | Bleach | TV | 269 | 0.652481 | 7.917476 | 0.558140 | 2004.0 | 1.505412 |
| 29 | ![]() | Magi: The Kingdom of Magic | TV | 18115 | 0.521890 | 8.212752 | 0.767442 | 2013.0 | 1.493904 |
| 13 | ![]() | Dragon Quest: Dai no Daibouken (2020) | TV | 40906 | 0.555825 | 7.643075 | 0.930233 | 2020.0 | 1.481176 |
| 41 | ![]() | One Piece | TV | 21 | 0.514408 | 8.686696 | 0.441860 | 1999.0 | 1.480573 |
get_recommendations101(title='Naruto: Shippuuden', n=24)
| Image URL | Name | Type | anime_id | similarity_score | weighted_rating | recency_score | release_year | final_score | |
|---|---|---|---|---|---|---|---|---|---|
| 3 | https://cdn.myanimelist.net/images/anime/1908/... | Bleach: Sennen Kessen-hen | TV | 41467 | 0.608061 | 9.048079 | 0.976744 | 2022.0 | 1.694600 |
| 15 | https://cdn.myanimelist.net/images/anime/1337/... | Hunter x Hunter (2011) | TV | 11061 | 0.554940 | 9.037173 | 0.720930 | 2011.0 | 1.606006 |
| 0 | https://cdn.myanimelist.net/images/anime/13/17... | Naruto | TV | 20 | 0.774972 | 7.988501 | 0.511628 | 2002.0 | 1.587927 |
| 38 | https://cdn.myanimelist.net/images/anime/1208/... | Fullmetal Alchemist: Brotherhood | TV | 5114 | 0.517224 | 9.097636 | 0.674419 | 2009.0 | 1.578312 |
| 17 | https://cdn.myanimelist.net/images/anime/2/883... | Black Clover | TV | 34572 | 0.554523 | 8.136070 | 0.860465 | 2017.0 | 1.525497 |
| 8 | https://cdn.myanimelist.net/images/anime/9/642... | Akatsuki no Yona | TV | 25013 | 0.566378 | 8.022766 | 0.790698 | 2014.0 | 1.506009 |
| 2 | https://cdn.myanimelist.net/images/anime/3/404... | Bleach | TV | 269 | 0.652481 | 7.917476 | 0.558140 | 2004.0 | 1.505412 |
| 29 | https://cdn.myanimelist.net/images/anime/13/55... | Magi: The Kingdom of Magic | TV | 18115 | 0.521890 | 8.212752 | 0.767442 | 2013.0 | 1.493904 |
| 13 | https://cdn.myanimelist.net/images/anime/1499/... | Dragon Quest: Dai no Daibouken (2020) | TV | 40906 | 0.555825 | 7.643075 | 0.930233 | 2020.0 | 1.481176 |
| 41 | https://cdn.myanimelist.net/images/anime/6/732... | One Piece | TV | 21 | 0.514408 | 8.686696 | 0.441860 | 1999.0 | 1.480573 |
| 4 | https://cdn.myanimelist.net/images/anime/1228/... | Yuu☆Yuu☆Hakusho | TV | 392 | 0.601279 | 8.448490 | 0.279070 | 1992.0 | 1.478502 |
| 19 | https://cdn.myanimelist.net/images/anime/1305/... | Hunter x Hunter | TV | 136 | 0.553618 | 8.397139 | 0.441860 | 1999.0 | 1.472489 |
| 11 | https://cdn.myanimelist.net/images/anime/1536/... | Fairy Tail: Final Series | TV | 35972 | 0.556090 | 7.561180 | 0.883721 | 2018.0 | 1.462227 |
| 7 | https://cdn.myanimelist.net/images/anime/3/605... | Fairy Tail (2014) | TV | 22043 | 0.567061 | 7.645865 | 0.790698 | 2014.0 | 1.461245 |
| 10 | https://cdn.myanimelist.net/images/anime/13/75... | D.Gray-man | TV | 1482 | 0.556384 | 8.009810 | 0.604651 | 2006.0 | 1.460448 |
| 39 | https://cdn.myanimelist.net/images/anime/10/78... | Magi: Sinbad no Bouken (TV) | TV | 31741 | 0.516638 | 7.839802 | 0.837209 | 2016.0 | 1.459532 |
| 22 | https://cdn.myanimelist.net/images/anime/1620/... | Naruto: Shippuuden Movie 6 - Road to Ninja | Movie | 13667 | 0.549877 | 7.668692 | 0.744186 | 2012.0 | 1.442996 |
| 42 | https://cdn.myanimelist.net/images/anime/1498/... | Tokyo Ghoul | TV | 22319 | 0.513830 | 7.788620 | 0.790698 | 2014.0 | 1.442179 |
| 36 | https://cdn.myanimelist.net/images/anime/12/80... | D.Gray-man Hallow | TV | 32370 | 0.518820 | 7.671143 | 0.837209 | 2016.0 | 1.440776 |
| 26 | https://cdn.myanimelist.net/images/anime/10/59... | Dragon Ball Kai (2014) | TV | 22777 | 0.524658 | 7.658538 | 0.790698 | 2014.0 | 1.433931 |
| 20 | https://cdn.myanimelist.net/images/anime/6/508... | Juuni Kokuki | TV | 153 | 0.550869 | 7.957254 | 0.511628 | 2002.0 | 1.431787 |
| 18 | https://cdn.myanimelist.net/images/anime/5/181... | Fairy Tail | TV | 6702 | 0.554407 | 7.567877 | 0.674419 | 2009.0 | 1.420026 |
| 1 | https://cdn.myanimelist.net/images/anime/1091/... | Boruto: Naruto Next Generations | TV | 34566 | 0.764856 | 6.061365 | 0.860465 | 2017.0 | 1.419559 |
| 25 | https://cdn.myanimelist.net/images/anime/1102/... | Dragon Ball Kai | TV | 6033 | 0.525941 | 7.719814 | 0.674419 | 2009.0 | 1.418901 |
**Final Analysis of get_recommendations101() for *Naruto: Shippuuden***¶
The output of get_recommendations101() demonstrates the most balanced and refined results compared to previous recommendation versions. It effectively integrates:
- Semantic relevance through a finely tuned similarity matrix (
combined_sim3) - Quality assurance via weighted user ratings
- Freshness bias using a normalized recency score
Why These Are the Best Results Yet¶
1. Relevance & Genre Alignment¶
The top recommendations (e.g., Bleach: Sennen Kessen-hen, Hunter x Hunter, Naruto, Fullmetal Alchemist: Brotherhood) are deeply thematically aligned with Naruto: Shippuuden. They all:
- Fall within the shounen, action, fantasy, or adventure genres
- Include strong narratives, ensemble casts, and character development arcs
This confirms that the custom similarity weights are functioning well — giving priority to genres and plot structure (e.g., synopsis_weight = 0.35, genre_weight = 0.30).
2. Quality Scores Still Matter¶
Highly rated titles (e.g., Hunter x Hunter (2011), Fullmetal Alchemist: Brotherhood) appear prominently — despite being older — thanks to their exceptional user scores.
This proves the system does not over-prioritize recency, maintaining a strong quality bias through weighted_rating.
3. Smart Recency Boost¶
Newer anime like:
- Bleach: Sennen Kessen-hen (2022)
- Black Clover (2017)
- Dragon Quest: Dai no Daibouken (2020) appear near the top of the list, showing that modern titles are correctly being elevated in the ranking — without overshadowing the all-time greats.
This affirms that the recency weight (0.2) strikes the right balance between discovery and classic appeal.
Conclusion: Best Results So Far¶
Compared to previous versions (v2, v3, and v4):
- The results here are more current, more contextually aligned, and less biased toward only legacy titles
- The hybrid scoring formula allows older masterpieces and modern hits to coexist fairly
- The tuning of content feature weights in
combined_sim3allows the system to understand what truly matters to the viewer
Lets put them into a class¶
The AnimeRecommender class is a content-based hybrid recommendation system that suggests similar anime using genre, synopsis, type, studio, episode, and source features. It preprocesses data, computes weighted similarities, and enhances results with a recency boost to favor newer titles. It supports both item-based and user-based recommendations with optimized performance using sparse matrices.
class AnimeRecommender:
def __init__(self, anime_df, ratings_df):
# Data loading and type optimization
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
self._preprocess_data()
self._create_feature_matrices()
# Similarity weights
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
"""Data cleaning and feature engineering with memory optimization"""
# Handle missing values
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
self.anime_df['Genres'] = self.anime_df['Genres'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
self.anime_df['Studios'] = self.anime_df['Studios'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
# Weighted rating
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
"""Create sparse feature matrices for fast similarity calculations"""
# Multi-label genres and studios
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
# TF-IDF for synopsis
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'])
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
def _clean_text(self, text_series: pd.Series) -> pd.Series:
"""Clean text data by lowercasing and removing punctuation.
Args:
text_series: Series of text to clean.
Returns:
Cleaned text Series.
"""
text = text_series.str.lower()
text = text.str.replace(r'[^\w\s]', '', regex=True)
return text
def _calculate_anime_similarity(self, idx: int) -> tuple[np.ndarray, np.ndarray]:
"""Calculate weighted similarity for a single anime using sparse operations.
Args:
idx: Index of the target anime.
Returns:
Tuple of similarity scores and corresponding indices.
"""
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
top_indices = sim_scores[:, 1].argsort()[-(52):-1][::-1]
sim_scores_top = sim_scores[top_indices, 1]
return sim_scores_top, top_indices
def _apply_recency_boost(self, df):
df['year'] = df['release_year']
df['year'] = df['year'].fillna(df['year'].min())
min_year = df['year'].min()
max_year = df['year'].max()
year_range = max_year - min_year if max_year != min_year else 1
df['recency_score'] = (df['year'] - min_year) / year_range
# current_year = 2025.0
# age = current_year - df['year']
# df['recency_score'] = 1 / (1 + np.exp(-(10 - age) / 2))
return df
def get_anime_recommendations(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
# Compute similarity vectors
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
# Fetch recommended anime
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'release_year']
].copy()
recommended_animes = self._apply_recency_boost(recommended_animes)
recommended_animes['similarity_score'] = sim_scores_top
# Compute final score
recommended_animes['final_score'] = (
(1-similarity_weight) * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score'])* (1-recency_weight) + (recency_weight * recommended_animes['recency_score'])
return recommended_animes.sort_values('final_score', ascending=False).head(n)
- create an inastance from it
recommender = AnimeRecommender(anime_df, ratings_df)
Lets test the recommendations
df = recommender.get_anime_recommendations('Kimetsu no Yaiba', n=10)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [Action, Fantasy] | 8.80 | 8.794506 | 2021.0 | 2021.0 | 0.948718 | 0.606117 | 1.657244 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [Action, Fantasy] | 8.49 | 8.467293 | 2023.0 | 2023.0 | 1.000000 | 0.642217 | 1.652783 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [Action, Fantasy] | 8.39 | 8.380549 | 2021.0 | 2021.0 | 0.948718 | 0.604693 | 1.606601 |
| 16236 | ![]() | Jujutsu Kaisen | 40748 | TV | [Action, Award Winning, Fantasy] | 8.64 | 8.637287 | 2020.0 | 2020.0 | 0.923077 | 0.506242 | 1.565335 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [Action, Fantasy] | 8.59 | 8.584003 | 2022.0 | 2022.0 | 0.974359 | 0.454961 | 1.534326 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | [Action, Fantasy] | 8.62 | 8.615747 | 2020.0 | 2020.0 | 0.923077 | 0.453053 | 1.526581 |
| 6589 | ![]() | Fate/Zero 2nd Season | 11741 | TV | [Action, Fantasy, Supernatural] | 8.55 | 8.544452 | 2012.0 | 2012.0 | 0.717949 | 0.456234 | 1.479163 |
| 14835 | ![]() | Toaru Kagaku no Railgun T | 38481 | TV | [Action, Fantasy, Sci-Fi] | 8.17 | 8.134640 | 2020.0 | 2020.0 | 0.923077 | 0.450000 | 1.466772 |
| 9821 | ![]() | Fate/stay night: Unlimited Blade Works 2nd Season | 28701 | TV | [Action, Fantasy, Supernatural] | 8.32 | 8.313739 | 2015.0 | 2015.0 | 0.794872 | 0.454354 | 1.465584 |
| 10527 | ![]() | Noragami Aragoto | 30503 | TV | [Action, Fantasy] | 8.16 | 8.156397 | 2015.0 | 2015.0 | 0.794872 | 0.449720 | 1.443551 |
df = recommender.get_anime_recommendations('Jujutsu Kaisen', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [Action, Fantasy] | 8.59 | 8.584003 | 2022.0 | 2022.0 | 0.974359 | 0.553409 | 1.601270 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | 41467 | TV | [Action, Adventure, Fantasy] | 9.07 | 9.048079 | 2022.0 | 2022.0 | 0.974359 | 0.462662 | 1.595251 |
| 15822 | ![]() | Shingeki no Kyojin: The Final Season | 40028 | TV | [Action, Drama] | 8.80 | 8.796520 | 2020.0 | 2020.0 | 0.923077 | 0.474939 | 1.563156 |
| 19600 | ![]() | Jigokuraku | 46569 | TV | [Action, Adventure, Fantasy] | 8.26 | 8.224549 | 2023.0 | 2023.0 | 1.000000 | 0.551048 | 1.561658 |
| 21303 | ![]() | Vinland Saga Season 2 | 49387 | TV | [Action, Adventure, Drama] | 8.81 | 8.771356 | 2023.0 | 2023.0 | 1.000000 | 0.452953 | 1.560571 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [Action, Fantasy] | 8.80 | 8.794506 | 2021.0 | 2021.0 | 0.948718 | 0.453753 | 1.553637 |
| 14539 | ![]() | Kimetsu no Yaiba | 38000 | TV | [Action, Award Winning, Fantasy] | 8.50 | 8.498085 | 2019.0 | 2019.0 | 0.897436 | 0.506242 | 1.543502 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [Action, Fantasy] | 8.49 | 8.467293 | 2023.0 | 2023.0 | 1.000000 | 0.457070 | 1.526882 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [Action, Fantasy] | 8.39 | 8.380549 | 2021.0 | 2021.0 | 0.948718 | 0.457433 | 1.506464 |
| 14942 | ![]() | Dorohedoro | 38668 | TV | [Action, Comedy, Fantasy, Horror] | 8.06 | 8.048611 | 2020.0 | 2020.0 | 0.923077 | 0.481031 | 1.477550 |
| 10527 | ![]() | Noragami Aragoto | 30503 | TV | [Action, Fantasy] | 8.16 | 8.156397 | 2015.0 | 2015.0 | 0.794872 | 0.497718 | 1.476190 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [Action, Adventure, Fantasy] | 7.64 | 7.609725 | 2021.0 | 2021.0 | 0.948718 | 0.501803 | 1.444137 |
| 7354 | ![]() | Toaru Kagaku no Railgun S | 16049 | TV | [Action, Fantasy, Sci-Fi] | 8.02 | 8.005639 | 2013.0 | 2013.0 | 0.743590 | 0.456802 | 1.420020 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [Action, Adventure, Fantasy] | 7.85 | 7.839802 | 2016.0 | 2016.0 | 0.820513 | 0.457099 | 1.415706 |
| 8268 | ![]() | Noragami | 20507 | TV | [Action, Fantasy] | 7.95 | 7.947757 | 2014.0 | 2014.0 | 0.769231 | 0.451205 | 1.414397 |
| 16560 | ![]() | The God of High School | 41353 | TV | [Action, Fantasy] | 7.07 | 7.067536 | 2020.0 | 2020.0 | 0.923077 | 0.553590 | 1.409161 |
| 8868 | ![]() | Garo: Honoo no Kokuin | 23311 | TV | [Action, Fantasy] | 7.35 | 7.317473 | 2014.0 | 2014.0 | 0.769231 | 0.547912 | 1.404523 |
| 10588 | ![]() | Shingeki no Bahamut: Virgin Soul | 30736 | TV | [Action, Adventure, Fantasy] | 7.43 | 7.409368 | 2017.0 | 2017.0 | 0.846154 | 0.509020 | 1.404489 |
| 12429 | ![]() | Nanatsu no Taizai: Imashime no Fukkatsu | 34577 | TV | [Action, Adventure, Fantasy] | 7.59 | 7.586863 | 2018.0 | 2018.0 | 0.871795 | 0.464332 | 1.400528 |
| 11291 | ![]() | D.Gray-man Hallow | 32370 | TV | [Action, Adventure, Fantasy] | 7.70 | 7.671143 | 2016.0 | 2016.0 | 0.820513 | 0.456173 | 1.394837 |
| 8598 | ![]() | Akame ga Kill! | 22199 | TV | [Action, Fantasy] | 7.47 | 7.468480 | 2014.0 | 2014.0 | 0.769231 | 0.494949 | 1.386629 |
| 16089 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou | 40496 | TV | [Action, Fantasy] | 7.38 | 7.375983 | 2020.0 | 2020.0 | 0.923077 | 0.458735 | 1.381673 |
| 8981 | ![]() | Nanatsu no Taizai | 23755 | TV | [Action, Adventure, Fantasy] | 7.67 | 7.668250 | 2014.0 | 2014.0 | 0.769231 | 0.451250 | 1.380887 |
| 8512 | ![]() | Shingeki no Bahamut: Genesis | 21843 | TV | [Action, Adventure, Fantasy] | 7.62 | 7.607811 | 2014.0 | 2014.0 | 0.769231 | 0.458067 | 1.378269 |
df = recommender.get_anime_recommendations('Darling in the FranXX', n=10)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20964 | ![]() | 86 Part 2 | 48569 | TV | [Action, Drama, Sci-Fi] | 8.71 | 8.692836 | 2021.0 | 2021.0 | 0.977778 | 0.474157 | 1.561122 |
| 8855 | ![]() | Shigatsu wa Kimi no Uso | 23273 | TV | [Drama, Romance] | 8.65 | 8.646914 | 2014.0 | 2014.0 | 0.822222 | 0.476193 | 1.525885 |
| 16608 | ![]() | 86 | 41457 | TV | [Action, Drama, Sci-Fi] | 8.28 | 8.270491 | 2021.0 | 2021.0 | 0.977778 | 0.477333 | 1.512601 |
| 9604 | ![]() | Plastic Memories | 27775 | TV | [Drama, Romance, Sci-Fi] | 7.91 | 7.904120 | 2015.0 | 2015.0 | 0.844444 | 0.511800 | 1.465407 |
| 3375 | ![]() | Kidou Senshi Gundam 00 Second Season | 3927 | TV | [Action, Drama, Sci-Fi] | 8.08 | 8.048096 | 2008.0 | 2008.0 | 0.688889 | 0.474065 | 1.425914 |
| 11072 | ![]() | Kiznaiver | 31798 | TV | [Drama, Romance, Sci-Fi] | 7.38 | 7.374965 | 2016.0 | 2016.0 | 0.866667 | 0.520255 | 1.412103 |
| 16309 | ![]() | SSSS.Dynazenon | 40870 | TV | [Action, Sci-Fi] | 7.42 | 7.375668 | 2021.0 | 2021.0 | 0.977778 | 0.478243 | 1.405841 |
| 10540 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | TV | [Action, Drama, Sci-Fi] | 7.61 | 7.309665 | 2015.0 | 2015.0 | 0.844444 | 0.515709 | 1.396731 |
| 11508 | ![]() | Senki Zesshou Symphogear AXZ | 32836 | TV | [Action, Sci-Fi] | 7.60 | 7.472081 | 2017.0 | 2017.0 | 0.888889 | 0.472047 | 1.395420 |
| 6358 | ![]() | Guilty Crown | 10793 | TV | [Action, Drama, Sci-Fi] | 7.42 | 7.417078 | 2011.0 | 2011.0 | 0.755556 | 0.517757 | 1.393235 |
Despite offering functional recommendations, the earlier version of the AnimeRecommender class suffered from a notable limitation in how it processed anime synopses. Specifically, the system applied only basic text normalization—lowercasing and punctuation removal—before generating TF-IDF vectors. This naive approach overlooked crucial linguistic elements such as synonyms, verb/noun variations, and context-specific word usage. As a result, the system often failed to capture the deeper semantic similarity between anime plots, especially when two shows shared core themes but used different vocabulary. This limitation diluted the effectiveness of synopsis-based matching, which is arguably one of the most important content features. To address these shortcomings, the updated class integrates a full NLP pipeline—leveraging tokenization, stopword removal, POS tagging, and lemmatization—to produce cleaner and more semantically rich representations, enabling more accurate and meaningful recommendations.
nltk.download('punkt')
nltk.download('stopwords')
nltk.download('wordnet')
nltk.download('averaged_perceptron_tagger_eng')
nltk.download('universal_tagset')
class AnimeRecommender:
def __init__(self, anime_df, ratings_df):
"""Initialize the recommender with anime and ratings data."""
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
# Initialize NLP tools
self.lemmatizer = WordNetLemmatizer()
self.stop_words = set(stopwords.words('english'))
self._preprocess_data()
self._create_feature_matrices()
# Similarity weights
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
"""Data cleaning and feature engineering with memory optimization."""
# Handle missing values
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['Release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
self.anime_df['Genres'] = self.anime_df['Genres'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
self.anime_df['Studios'] = self.anime_df['Studios'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
# Weighted rating
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
"""Create sparse feature matrices for fast similarity calculations."""
# Multi-label genres and studios
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
# TF-IDF for synopsis with enhanced cleaning
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'].fillna(''))
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
@staticmethod
def get_wordnet_pos(tag):
"""Map NLTK POS tag to WordNet POS tag for lemmatization.
Args:
tag: NLTK POS tag (e.g., 'NN', 'VB').
Returns:
WordNet POS tag ('n', 'v', 'a', 'r').
"""
if tag.startswith('J'):
return 'a' # adjective
elif tag.startswith('V'):
return 'v' # verb
elif tag.startswith('N'):
return 'n' # noun
elif tag.startswith('R'):
return 'r' # adverb
else:
return 'n' # default to noun
def _lemmatize_text(self, text: str) -> str:
"""Lemmatize and clean a single text string.
Args:
text: Input text to clean.
Returns:
Cleaned and lemmatized text as a string.
"""
text = text.lower()
tokens = word_tokenize(text)
tagged_tokens = nltk.pos_tag(tokens, tagset='universal')
cleaned_tokens = []
for token, tag in tagged_tokens:
if token not in string.punctuation and token not in self.stop_words:
wordnet_pos = self.get_wordnet_pos(tag)
lemma = self.lemmatizer.lemmatize(token, pos=wordnet_pos)
cleaned_tokens.append(lemma)
return ' '.join(cleaned_tokens)
def _clean_text(self, text_series: pd.Series) -> pd.Series:
"""Clean text data by lowercasing, tokenizing, removing punctuation and stopwords, and lemmatizing.
Args:
text_series: Series of text to clean.
Returns:
Cleaned text Series.
"""
return text_series.apply(self._lemmatize_text)
def _calculate_anime_similarity(self, idx: int) -> tuple[np.ndarray, np.ndarray]:
"""Calculate weighted similarity for a single anime using sparse operations.
Args:
idx: Index of the target anime.
Returns:
Tuple of similarity scores and corresponding indices.
"""
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
top_indices = sim_scores[:, 1].argsort()[-(52):-1][::-1]
sim_scores_top = sim_scores[top_indices, 1]
return sim_scores_top, top_indices
def _apply_recency_boost(self, df):
"""Apply a recency boost based on release year."""
df['year'] = df['Release_year']
df['year'] = df['year'].fillna(df['year'].min())
min_year = df['year'].min()
max_year = df['year'].max()
year_range = max_year - min_year if max_year != min_year else 1
df['recency_score'] = (df['year'] - min_year) / year_range
return df
def get_anime_recommendations(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
"""Generate anime recommendations based on a given title."""
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year']
].copy()
recommended_animes = self._apply_recency_boost(recommended_animes)
recommended_animes['similarity_score'] = sim_scores_top
recommended_animes['final_score'] = (
(1 - similarity_weight) * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']
) * (1 - recency_weight) + (recency_weight * recommended_animes['recency_score'])
return recommended_animes.sort_values('final_score', ascending=False).head(n)
[nltk_data] Downloading package punkt to /root/nltk_data... [nltk_data] Package punkt is already up-to-date! [nltk_data] Downloading package stopwords to /root/nltk_data... [nltk_data] Package stopwords is already up-to-date! [nltk_data] Downloading package wordnet to /root/nltk_data... [nltk_data] Package wordnet is already up-to-date! [nltk_data] Downloading package averaged_perceptron_tagger_eng to [nltk_data] /root/nltk_data... [nltk_data] Package averaged_perceptron_tagger_eng is already up-to- [nltk_data] date! [nltk_data] Downloading package universal_tagset to /root/nltk_data... [nltk_data] Package universal_tagset is already up-to-date!
- Lets make and instance of the new class then test it...
recommender = AnimeRecommender(anime_df, ratings_df)
df = recommender.get_anime_recommendations('Bakemonogatari', n=10)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12861 | ![]() | Owarimonogatari 2nd Season | 35247 | TV | [Comedy, Mystery, Supernatural] | 8.88 | 8.856249 | 2017.0 | 2017.0 | 0.76 | 0.642811 | 1.651861 |
| 7556 | ![]() | Monogatari Series: Second Season | 17074 | TV | [Comedy, Mystery, Romance, Supernatural] | 8.77 | 8.757932 | 2013.0 | 2013.0 | 0.60 | 0.652100 | 1.614380 |
| 10808 | ![]() | Owarimonogatari | 31181 | TV | [Comedy, Mystery, Supernatural] | 8.45 | 8.434069 | 2015.0 | 2015.0 | 0.68 | 0.606948 | 1.560813 |
| 11052 | ![]() | Kizumonogatari III: Reiketsu-hen | 31758 | Movie | [Action, Mystery, Supernatural] | 8.79 | 8.773543 | 2017.0 | 2017.0 | 0.76 | 0.475048 | 1.527858 |
| 6553 | ![]() | Nisemonogatari | 11597 | TV | [Comedy, Mystery, Supernatural, Ecchi] | 8.14 | 8.132676 | 2012.0 | 2012.0 | 0.56 | 0.636405 | 1.520676 |
| 7281 | ![]() | Nekomonogatari: Kuro | 15689 | TV | [Comedy, Romance, Supernatural, Ecchi] | 7.93 | 7.921766 | 2012.0 | 2012.0 | 0.56 | 0.634826 | 1.494293 |
| 13907 | ![]() | Zoku Owarimonogatari | 36999 | Movie | [Comedy, Mystery, Supernatural] | 8.45 | 8.414520 | 2018.0 | 2018.0 | 0.80 | 0.468357 | 1.488225 |
| 14179 | ![]() | Seishun Buta Yarou wa Bunny Girl Senpai no Yume wo Minai | 37450 | TV | [Drama, Romance, Supernatural] | 8.24 | 8.236870 | 2018.0 | 2018.0 | 0.80 | 0.476443 | 1.472405 |
| 9660 | ![]() | Tsukimonogatari | 28025 | TV | [Comedy, Mystery, Supernatural, Ecchi] | 8.09 | 8.077023 | 2014.0 | 2014.0 | 0.64 | 0.550759 | 1.471759 |
| 5670 | ![]() | Kizumonogatari I: Tekketsu-hen | 9260 | Movie | [Action, Mystery, Supernatural] | 8.37 | 8.357224 | 2016.0 | 2016.0 | 0.72 | 0.473680 | 1.468969 |
df = recommender.get_anime_recommendations('Darling in the FranXX', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20964 | ![]() | 86 Part 2 | 48569 | TV | [Action, Drama, Sci-Fi] | 8.71 | 8.692836 | 2021.0 | 2021.0 | 0.977778 | 0.476150 | 1.562478 |
| 8855 | ![]() | Shigatsu wa Kimi no Uso | 23273 | TV | [Drama, Romance] | 8.65 | 8.646914 | 2014.0 | 2014.0 | 0.822222 | 0.479830 | 1.528358 |
| 16608 | ![]() | 86 | 41457 | TV | [Action, Drama, Sci-Fi] | 8.28 | 8.270491 | 2021.0 | 2021.0 | 0.977778 | 0.477433 | 1.512669 |
| 21948 | ![]() | Lycoris Recoil | 50709 | TV | [Action] | 8.20 | 8.183950 | 2022.0 | 2022.0 | 1.000000 | 0.472867 | 1.503623 |
| 9604 | ![]() | Plastic Memories | 27775 | TV | [Drama, Romance, Sci-Fi] | 7.91 | 7.904120 | 2015.0 | 2015.0 | 0.844444 | 0.517298 | 1.469146 |
| 3375 | ![]() | Kidou Senshi Gundam 00 Second Season | 3927 | TV | [Action, Drama, Sci-Fi] | 8.08 | 8.048096 | 2008.0 | 2008.0 | 0.688889 | 0.480428 | 1.430240 |
| 11072 | ![]() | Kiznaiver | 31798 | TV | [Drama, Romance, Sci-Fi] | 7.38 | 7.374965 | 2016.0 | 2016.0 | 0.866667 | 0.522201 | 1.413426 |
| 16309 | ![]() | SSSS.Dynazenon | 40870 | TV | [Action, Sci-Fi] | 7.42 | 7.375668 | 2021.0 | 2021.0 | 0.977778 | 0.479599 | 1.406763 |
| 10540 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | TV | [Action, Drama, Sci-Fi] | 7.61 | 7.309665 | 2015.0 | 2015.0 | 0.844444 | 0.515613 | 1.396666 |
| 16 | ![]() | Texhnolyze | 26 | TV | [Action, Drama, Sci-Fi] | 7.76 | 7.717234 | 2003.0 | 2003.0 | 0.577778 | 0.517278 | 1.393373 |
| 6358 | ![]() | Guilty Crown | 10793 | TV | [Action, Drama, Sci-Fi] | 7.42 | 7.417078 | 2011.0 | 2011.0 | 0.755556 | 0.517905 | 1.393336 |
| 7558 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus | 17080 | TV | [Action, Drama, Sci-Fi] | 7.37 | 7.177703 | 2015.0 | 2015.0 | 0.844444 | 0.519534 | 1.383496 |
| 12271 | ![]() | Grancrest Senki | 34279 | TV | [Action, Drama, Fantasy, Romance] | 7.22 | 7.208601 | 2018.0 | 2018.0 | 0.911111 | 0.489675 | 1.380234 |
| 7299 | ![]() | Senki Zesshou Symphogear G | 15793 | TV | [Action, Sci-Fi] | 7.46 | 7.396078 | 2013.0 | 2013.0 | 0.800000 | 0.477679 | 1.372351 |
| 9655 | ![]() | Macross Δ | 28013 | TV | [Action, Romance, Sci-Fi] | 7.27 | 7.213781 | 2016.0 | 2016.0 | 0.866667 | 0.481872 | 1.366660 |
| 72 | ![]() | Kidou Senshi Gundam SEED | 93 | TV | [Action, Award Winning, Drama, Romance, Sci-Fi] | 7.75 | 7.724560 | 2002.0 | 2002.0 | 0.555556 | 0.476327 | 1.361961 |
| 10460 | ![]() | Classroom☆Crisis | 30383 | TV | [Drama, Romance, Sci-Fi] | 6.97 | 6.949188 | 2015.0 | 2015.0 | 0.844444 | 0.517915 | 1.354973 |
| 22263 | ![]() | Engage Kiss | 51417 | TV | [Action, Comedy, Romance] | 6.84 | 6.829794 | 2022.0 | 2022.0 | 1.000000 | 0.488282 | 1.351607 |
| 8424 | ![]() | Buddy Complex | 21437 | TV | [Action, Sci-Fi] | 7.12 | 7.088736 | 2014.0 | 2014.0 | 0.822222 | 0.481030 | 1.342193 |
| 5559 | ![]() | Star Driver: Kagayaki no Takuto | 8934 | TV | [Action, Romance, Sci-Fi] | 7.19 | 7.159537 | 2010.0 | 2010.0 | 0.733333 | 0.484168 | 1.335045 |
| 16266 | ![]() | Hypnosis Mic: Division Rap Battle - Rhyme Anima | 40803 | TV | [Action, Sci-Fi] | 6.80 | 6.764323 | 2020.0 | 2020.0 | 0.955556 | 0.481011 | 1.329917 |
| 113 | ![]() | Gunslinger Girl | 134 | TV | [Action, Drama, Sci-Fi] | 7.39 | 7.367269 | 2003.0 | 2003.0 | 0.577778 | 0.480128 | 1.326115 |
| 73 | ![]() | Kidou Senshi Gundam SEED Destiny | 94 | TV | [Action, Drama, Romance, Sci-Fi] | 7.18 | 7.158412 | 2004.0 | 2004.0 | 0.600000 | 0.510245 | 1.325976 |
| 69 | ![]() | Shin Kidou Senki Gundam Wing | 90 | TV | [Action, Drama, Sci-Fi] | 7.69 | 7.664281 | 1995.0 | 1995.0 | 0.400000 | 0.475999 | 1.323393 |
df = recommender.get_anime_recommendations('One Piece', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 14699 | ![]() | One Piece Movie 14: Stampede | 38234 | Movie | [Action, Adventure, Fantasy] | 8.22 | 8.190025 | 2019.0 | 2019.0 | 0.868421 | 0.578450 | 1.549833 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [Action, Adventure, Fantasy] | 7.74 | 7.643075 | 2020.0 | 2020.0 | 0.894737 | 0.615499 | 1.514656 |
| 13409 | ![]() | One Piece: Episode of East Blue - Luffy to 4-nin no Nakama no Daibouken | 36215 | Special | [Action, Adventure, Fantasy] | 7.88 | 7.758384 | 2017.0 | 2017.0 | 0.815789 | 0.600029 | 1.502183 |
| 6823 | ![]() | One Piece Film: Z | 12859 | Movie | [Action, Adventure, Fantasy] | 8.14 | 8.121859 | 2012.0 | 2012.0 | 0.684211 | 0.574520 | 1.502139 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [Action, Adventure, Fantasy] | 7.64 | 7.609725 | 2021.0 | 2021.0 | 0.921053 | 0.558974 | 1.477480 |
| 10865 | ![]() | Drifters | 31339 | TV | [Action, Adventure, Comedy, Fantasy] | 7.90 | 7.889898 | 2016.0 | 2016.0 | 0.789474 | 0.520367 | 1.458532 |
| 3514 | ![]() | One Piece Film: Strong World | 4155 | Movie | [Action, Adventure, Fantasy] | 8.08 | 8.060354 | 2009.0 | 2009.0 | 0.605263 | 0.542080 | 1.456910 |
| 1574 | ![]() | Naruto: Shippuuden | 1735 | TV | [Action, Adventure, Fantasy] | 8.26 | 8.257899 | 2007.0 | 2007.0 | 0.552632 | 0.519085 | 1.454452 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [Action, Adventure, Fantasy] | 7.85 | 7.839802 | 2016.0 | 2016.0 | 0.789474 | 0.522789 | 1.454167 |
| 7119 | ![]() | Magi: The Labyrinth of Magic | 14513 | TV | [Action, Adventure, Fantasy] | 8.02 | 8.014632 | 2012.0 | 2012.0 | 0.684211 | 0.517040 | 1.450185 |
| 8747 | ![]() | Dragon Ball Kai (2014) | 22777 | TV | [Action, Adventure, Comedy, Fantasy] | 7.68 | 7.658538 | 2014.0 | 2014.0 | 0.736842 | 0.559808 | 1.447062 |
| 8104 | ![]() | Kyousou Giga (TV) | 19703 | TV | [Action, Fantasy] | 7.71 | 7.666199 | 2013.0 | 2013.0 | 0.710526 | 0.550316 | 1.436264 |
| 4416 | ![]() | Dragon Ball Kai | 6033 | TV | [Action, Adventure, Comedy, Fantasy] | 7.73 | 7.719814 | 2009.0 | 2009.0 | 0.605263 | 0.568661 | 1.434120 |
| 10577 | ![]() | Dragon Ball Super | 30694 | TV | [Action, Adventure, Comedy, Fantasy] | 7.43 | 7.426386 | 2015.0 | 2015.0 | 0.763158 | 0.569198 | 1.430852 |
| 115 | ![]() | Hunter x Hunter | 136 | TV | [Action, Adventure, Fantasy] | 8.41 | 8.397139 | 1999.0 | 1999.0 | 0.342105 | 0.520165 | 1.429790 |
| 8558 | ![]() | Fairy Tail (2014) | 22043 | TV | [Action, Adventure, Fantasy] | 7.65 | 7.645865 | 2014.0 | 2014.0 | 0.736842 | 0.521548 | 1.419525 |
| 23543 | ![]() | Hirogaru Sky! Precure | 53716 | TV | [Action, Fantasy] | 7.69 | 6.923455 | 2023.0 | 2023.0 | 0.973684 | 0.560162 | 1.406462 |
| 21578 | ![]() | Edens Zero 2nd Season | 50002 | TV | [Action, Adventure, Fantasy, Sci-Fi] | 7.43 | 7.164798 | 2023.0 | 2023.0 | 0.973684 | 0.517489 | 1.406405 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [Action, Adventure, Fantasy] | 7.04 | 6.978657 | 2022.0 | 2022.0 | 0.947368 | 0.550000 | 1.400913 |
| 6018 | ![]() | Toriko | 10033 | TV | [Action, Adventure, Comedy, Fantasy, Gourmet] | 7.52 | 7.481460 | 2011.0 | 2011.0 | 0.657895 | 0.546293 | 1.400833 |
| 734 | ![]() | Dragon Ball Z | 813 | TV | [Action, Adventure, Comedy, Fantasy] | 8.16 | 8.156166 | 1989.0 | 1989.0 | 0.078947 | 0.570347 | 1.382365 |
| 11036 | ![]() | Nanatsu no Taizai: Seisen no Shirushi | 31722 | TV | [Action, Adventure, Fantasy] | 6.98 | 6.977496 | 2016.0 | 2016.0 | 0.789474 | 0.557771 | 1.374478 |
| 4038 | ![]() | One Piece: Romance Dawn Story | 5252 | OVA | [Action, Adventure, Fantasy] | 7.34 | 7.278879 | 2008.0 | 2008.0 | 0.578947 | 0.563277 | 1.372283 |
| 18914 | ![]() | Orient | 45560 | TV | [Action, Adventure, Fantasy] | 6.60 | 6.590130 | 2022.0 | 2022.0 | 0.947368 | 0.557117 | 1.359129 |
df = recommender.get_anime_recommendations('Sankarea', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 7481 | ![]() | Kami nomi zo Shiru Sekai: Megami-hen | 16706 | TV | [Comedy, Romance, Supernatural] | 8.00 | 7.985126 | 2013.0 | 2013.0 | 0.756098 | 0.488362 | 1.441521 |
| 6356 | ![]() | Kore wa Zombie desu ka? of the Dead | 10790 | TV | [Action, Comedy, Supernatural, Ecchi] | 7.49 | 7.480967 | 2012.0 | 2012.0 | 0.731707 | 0.557525 | 1.423175 |
| 6042 | ![]() | Kami nomi zo Shiru Sekai II | 10080 | TV | [Comedy, Romance, Supernatural] | 7.88 | 7.868894 | 2011.0 | 2011.0 | 0.707317 | 0.484432 | 1.415144 |
| 10393 | ![]() | Prison School | 30240 | TV | [Comedy, Romance, Ecchi] | 7.61 | 7.606261 | 2015.0 | 2015.0 | 0.804878 | 0.491384 | 1.407868 |
| 9339 | ![]() | Junjou Romantica 3 | 25649 | TV | [Boys Love, Comedy, Drama, Romance] | 7.62 | 7.585614 | 2015.0 | 2015.0 | 0.804878 | 0.493986 | 1.407160 |
| 11346 | ![]() | Sakamoto desu ga? | 32542 | TV | [Comedy] | 7.55 | 7.544551 | 2016.0 | 2016.0 | 0.829268 | 0.488381 | 1.403299 |
| 6868 | ![]() | Sankarea OVA | 13055 | OVA | [Comedy, Horror, Romance, Supernatural, Ecchi] | 7.20 | 7.176518 | 2012.0 | 2012.0 | 0.731707 | 0.578102 | 1.400633 |
| 101 | ![]() | Full Moon wo Sagashite | 122 | TV | [Comedy, Drama, Romance, Supernatural] | 7.94 | 7.883657 | 2002.0 | 2002.0 | 0.487805 | 0.507293 | 1.388559 |
| 5400 | ![]() | Kami nomi zo Shiru Sekai | 8525 | TV | [Comedy, Romance, Supernatural] | 7.66 | 7.653154 | 2010.0 | 2010.0 | 0.682927 | 0.489389 | 1.387748 |
| 5528 | ![]() | Kore wa Zombie desu ka? | 8841 | TV | [Action, Comedy, Supernatural, Ecchi] | 7.35 | 7.344986 | 2011.0 | 2011.0 | 0.707317 | 0.534388 | 1.386245 |
| 3825 | ![]() | Junjou Romantica 2 | 4814 | TV | [Boys Love, Comedy, Drama, Romance] | 7.71 | 7.688145 | 2008.0 | 2008.0 | 0.634146 | 0.484164 | 1.378638 |
| 13735 | ![]() | Yuragi-sou no Yuuna-san | 36726 | TV | [Comedy, Romance, Supernatural, Ecchi] | 7.01 | 7.001053 | 2018.0 | 2018.0 | 0.878049 | 0.525513 | 1.373085 |
| 7477 | ![]() | Sankarea: Wagahai mo... Zombie de Aru... | 16694 | Special | [Comedy, Horror, Romance, Supernatural, Ecchi] | 7.21 | 7.180576 | 2012.0 | 2012.0 | 0.731707 | 0.533066 | 1.370496 |
| 6447 | ![]() | Inu x Boku SS | 11013 | TV | [Comedy, Romance, Supernatural] | 7.39 | 7.382382 | 2012.0 | 2012.0 | 0.731707 | 0.490564 | 1.365811 |
| 2803 | ![]() | Junjou Romantica | 3092 | TV | [Boys Love, Comedy, Drama, Romance] | 7.50 | 7.486325 | 2008.0 | 2008.0 | 0.634146 | 0.493713 | 1.360913 |
| 23138 | ![]() | Megami no Café Terrace | 52973 | TV | [Comedy, Romance, Ecchi] | 7.00 | 6.920619 | 2023.0 | 2023.0 | 1.000000 | 0.484855 | 1.360176 |
| 8260 | ![]() | Inari, Konkon, Koi Iroha. | 20457 | TV | [Comedy, Romance, Supernatural] | 7.20 | 7.177983 | 2014.0 | 2014.0 | 0.780488 | 0.487832 | 1.349181 |
| 801 | ![]() | Amaenaide yo!! Katsu!! | 886 | TV | [Comedy, Romance, Supernatural, Ecchi] | 6.65 | 6.633160 | 2006.0 | 2006.0 | 0.585366 | 0.623298 | 1.336895 |
| 10872 | ![]() | Tonkatsu DJ Agetarou | 31370 | TV | [Comedy] | 7.13 | 6.967501 | 2016.0 | 2016.0 | 0.829268 | 0.491784 | 1.336367 |
| 15019 | ![]() | Midara na Ao-chan wa Benkyou ga Dekinai | 38778 | TV | [Comedy, Romance, Ecchi] | 6.72 | 6.715492 | 2019.0 | 2019.0 | 0.902439 | 0.492061 | 1.320949 |
| 554 | ![]() | Amaenaide yo!! | 591 | TV | [Comedy, Romance, Supernatural, Ecchi] | 6.45 | 6.446868 | 2005.0 | 2005.0 | 0.560976 | 0.621510 | 1.308446 |
| 16752 | ![]() | Megami-ryou no Ryoubo-kun. | 41812 | TV | [Comedy, Romance, Ecchi] | 6.52 | 6.516778 | 2021.0 | 2021.0 | 0.951220 | 0.493035 | 1.307521 |
| 4541 | ![]() | Omamori Himari | 6324 | TV | [Action, Comedy, Romance, Supernatural, Ecchi] | 6.84 | 6.833340 | 2010.0 | 2010.0 | 0.682927 | 0.502122 | 1.298029 |
| 2932 | ![]() | Hatenkou Yuugi | 3298 | TV | [Adventure, Comedy, Drama, Fantasy, Romance] | 7.11 | 7.037438 | 2008.0 | 2008.0 | 0.634146 | 0.480274 | 1.297908 |
df = recommender.get_anime_recommendations('Kimetsu no Yaiba', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [Action, Fantasy] | 8.49 | 8.467293 | 2023.0 | 2023.0 | 1.000000 | 0.675826 | 1.675637 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [Action, Fantasy] | 8.80 | 8.794506 | 2021.0 | 2021.0 | 0.948718 | 0.627257 | 1.671619 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [Action, Fantasy] | 8.39 | 8.380549 | 2021.0 | 2021.0 | 0.948718 | 0.627586 | 1.622168 |
| 16236 | ![]() | Jujutsu Kaisen | 40748 | TV | [Action, Award Winning, Fantasy] | 8.64 | 8.637287 | 2020.0 | 2020.0 | 0.923077 | 0.510968 | 1.568548 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | [Action, Fantasy] | 8.62 | 8.615747 | 2020.0 | 2020.0 | 0.923077 | 0.493573 | 1.554134 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [Action, Fantasy] | 8.59 | 8.584003 | 2022.0 | 2022.0 | 0.974359 | 0.456532 | 1.535394 |
| 6589 | ![]() | Fate/Zero 2nd Season | 11741 | TV | [Action, Fantasy, Supernatural] | 8.55 | 8.544452 | 2012.0 | 2012.0 | 0.717949 | 0.460878 | 1.482321 |
| 14835 | ![]() | Toaru Kagaku no Railgun T | 38481 | TV | [Action, Fantasy, Sci-Fi] | 8.17 | 8.134640 | 2020.0 | 2020.0 | 0.923077 | 0.454360 | 1.469737 |
| 9821 | ![]() | Fate/stay night: Unlimited Blade Works 2nd Season | 28701 | TV | [Action, Fantasy, Supernatural] | 8.32 | 8.313739 | 2015.0 | 2015.0 | 0.794872 | 0.453259 | 1.464839 |
| 10527 | ![]() | Noragami Aragoto | 30503 | TV | [Action, Fantasy] | 8.16 | 8.156397 | 2015.0 | 2015.0 | 0.794872 | 0.449990 | 1.443735 |
| 7799 | ![]() | Magi: The Kingdom of Magic | 18115 | TV | [Action, Adventure, Fantasy] | 8.22 | 8.212752 | 2013.0 | 2013.0 | 0.743590 | 0.454806 | 1.443516 |
| 6044 | ![]() | Fate/Zero | 10087 | TV | [Action, Fantasy, Supernatural] | 8.28 | 8.275991 | 2011.0 | 2011.0 | 0.692308 | 0.457940 | 1.442980 |
| 8617 | ![]() | Fate/stay night: Unlimited Blade Works | 22297 | TV | [Action, Fantasy, Supernatural] | 8.19 | 8.185116 | 2014.0 | 2014.0 | 0.769231 | 0.450000 | 1.442060 |
| 8268 | ![]() | Noragami | 20507 | TV | [Action, Fantasy] | 7.95 | 7.947757 | 2014.0 | 2014.0 | 0.769231 | 0.452160 | 1.415046 |
| 7119 | ![]() | Magi: The Labyrinth of Magic | 14513 | TV | [Action, Adventure, Fantasy] | 8.02 | 8.014632 | 2012.0 | 2012.0 | 0.717949 | 0.452839 | 1.413276 |
| 438 | ![]() | Koukaku Kidoutai: Stand Alone Complex | 467 | TV | [Action, Award Winning, Sci-Fi] | 8.42 | 8.397903 | 2002.0 | 2002.0 | 0.461538 | 0.456112 | 1.410212 |
| 5958 | ![]() | Ao no Exorcist | 9919 | TV | [Action, Fantasy] | 7.50 | 7.498247 | 2011.0 | 2011.0 | 0.692308 | 0.526891 | 1.396537 |
| 13644 | ![]() | Hug tto! Precure | 36593 | TV | [Action, Fantasy] | 7.85 | 7.459017 | 2018.0 | 2018.0 | 0.871795 | 0.454490 | 1.378495 |
| 368 | ![]() | Yuu☆Yuu☆Hakusho | 392 | TV | [Action, Fantasy] | 8.46 | 8.448490 | 1992.0 | 1992.0 | 0.205128 | 0.462824 | 1.369565 |
| 1654 | ![]() | Claymore | 1818 | TV | [Action, Adventure, Fantasy] | 7.74 | 7.732521 | 2007.0 | 2007.0 | 0.589744 | 0.472259 | 1.366987 |
| 8598 | ![]() | Akame ga Kill! | 22199 | TV | [Action, Fantasy] | 7.47 | 7.468480 | 2014.0 | 2014.0 | 0.769231 | 0.460057 | 1.362903 |
| 11869 | ![]() | Ao no Exorcist: Kyoto Fujouou-hen | 33506 | TV | [Action, Fantasy] | 7.35 | 7.345707 | 2017.0 | 2017.0 | 0.846154 | 0.452590 | 1.358477 |
| 10662 | ![]() | Tales of Zestiria the Cross | 30911 | TV | [Action, Adventure, Fantasy] | 7.24 | 7.229445 | 2016.0 | 2016.0 | 0.820513 | 0.469140 | 1.350651 |
| 12175 | ![]() | Tales of Zestiria the Cross 2nd Season | 34086 | TV | [Action, Adventure, Fantasy] | 7.31 | 7.288806 | 2017.0 | 2017.0 | 0.846154 | 0.450000 | 1.349888 |
df = recommender.get_anime_recommendations('Jujutsu Kaisen', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [Action, Fantasy] | 8.59 | 8.584003 | 2022.0 | 2022.0 | 0.974359 | 0.550663 | 1.599403 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | 41467 | TV | [Action, Adventure, Fantasy] | 9.07 | 9.048079 | 2022.0 | 2022.0 | 0.974359 | 0.462249 | 1.594971 |
| 15822 | ![]() | Shingeki no Kyojin: The Final Season | 40028 | TV | [Action, Drama] | 8.80 | 8.796520 | 2020.0 | 2020.0 | 0.923077 | 0.478012 | 1.565246 |
| 21303 | ![]() | Vinland Saga Season 2 | 49387 | TV | [Action, Adventure, Drama] | 8.81 | 8.771356 | 2023.0 | 2023.0 | 1.000000 | 0.455136 | 1.562055 |
| 19600 | ![]() | Jigokuraku | 46569 | TV | [Action, Adventure, Fantasy] | 8.26 | 8.224549 | 2023.0 | 2023.0 | 1.000000 | 0.551210 | 1.561769 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [Action, Fantasy] | 8.80 | 8.794506 | 2021.0 | 2021.0 | 0.948718 | 0.453632 | 1.553554 |
| 14539 | ![]() | Kimetsu no Yaiba | 38000 | TV | [Action, Award Winning, Fantasy] | 8.50 | 8.498085 | 2019.0 | 2019.0 | 0.897436 | 0.510968 | 1.546716 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [Action, Fantasy] | 8.49 | 8.467293 | 2023.0 | 2023.0 | 1.000000 | 0.457350 | 1.527073 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [Action, Fantasy] | 8.39 | 8.380549 | 2021.0 | 2021.0 | 0.948718 | 0.458439 | 1.507148 |
| 14942 | ![]() | Dorohedoro | 38668 | TV | [Action, Comedy, Fantasy, Horror] | 8.06 | 8.048611 | 2020.0 | 2020.0 | 0.923077 | 0.487813 | 1.482162 |
| 10527 | ![]() | Noragami Aragoto | 30503 | TV | [Action, Fantasy] | 8.16 | 8.156397 | 2015.0 | 2015.0 | 0.794872 | 0.498468 | 1.476700 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [Action, Adventure, Fantasy] | 7.64 | 7.609725 | 2021.0 | 2021.0 | 0.948718 | 0.511230 | 1.450547 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [Action, Adventure, Fantasy] | 7.85 | 7.839802 | 2016.0 | 2016.0 | 0.820513 | 0.462403 | 1.419313 |
| 7354 | ![]() | Toaru Kagaku no Railgun S | 16049 | TV | [Action, Fantasy, Sci-Fi] | 8.02 | 8.005639 | 2013.0 | 2013.0 | 0.743590 | 0.453568 | 1.417821 |
| 8268 | ![]() | Noragami | 20507 | TV | [Action, Fantasy] | 7.95 | 7.947757 | 2014.0 | 2014.0 | 0.769231 | 0.454787 | 1.416833 |
| 10588 | ![]() | Shingeki no Bahamut: Virgin Soul | 30736 | TV | [Action, Adventure, Fantasy] | 7.43 | 7.409368 | 2017.0 | 2017.0 | 0.846154 | 0.525272 | 1.415540 |
| 16560 | ![]() | The God of High School | 41353 | TV | [Action, Fantasy] | 7.07 | 7.067536 | 2020.0 | 2020.0 | 0.923077 | 0.555402 | 1.410393 |
| 8868 | ![]() | Garo: Honoo no Kokuin | 23311 | TV | [Action, Fantasy] | 7.35 | 7.317473 | 2014.0 | 2014.0 | 0.769231 | 0.553839 | 1.408553 |
| 12429 | ![]() | Nanatsu no Taizai: Imashime no Fukkatsu | 34577 | TV | [Action, Adventure, Fantasy] | 7.59 | 7.586863 | 2018.0 | 2018.0 | 0.871795 | 0.466171 | 1.401779 |
| 11291 | ![]() | D.Gray-man Hallow | 32370 | TV | [Action, Adventure, Fantasy] | 7.70 | 7.671143 | 2016.0 | 2016.0 | 0.820513 | 0.459543 | 1.397129 |
| 8598 | ![]() | Akame ga Kill! | 22199 | TV | [Action, Fantasy] | 7.47 | 7.468480 | 2014.0 | 2014.0 | 0.769231 | 0.496125 | 1.387429 |
| 16089 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou | 40496 | TV | [Action, Fantasy] | 7.38 | 7.375983 | 2020.0 | 2020.0 | 0.923077 | 0.464245 | 1.385420 |
| 8981 | ![]() | Nanatsu no Taizai | 23755 | TV | [Action, Adventure, Fantasy] | 7.67 | 7.668250 | 2014.0 | 2014.0 | 0.769231 | 0.452539 | 1.381763 |
| 8512 | ![]() | Shingeki no Bahamut: Genesis | 21843 | TV | [Action, Adventure, Fantasy] | 7.62 | 7.607811 | 2014.0 | 2014.0 | 0.769231 | 0.462259 | 1.381119 |
df = recommender.get_anime_recommendations('Akame ga Kill!', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [Action, Fantasy] | 8.80 | 8.794506 | 2021.0 | 2021.0 | 0.948718 | 0.505584 | 1.588881 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [Action, Fantasy] | 8.59 | 8.584003 | 2022.0 | 2022.0 | 0.974359 | 0.505496 | 1.568690 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [Action, Fantasy] | 8.49 | 8.467293 | 2023.0 | 2023.0 | 1.000000 | 0.515085 | 1.566333 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [Action, Fantasy] | 8.39 | 8.380549 | 2021.0 | 2021.0 | 0.948718 | 0.505834 | 1.539376 |
| 19600 | ![]() | Jigokuraku | 46569 | TV | [Action, Adventure, Fantasy] | 8.26 | 8.224549 | 2023.0 | 2023.0 | 1.000000 | 0.501412 | 1.527906 |
| 10527 | ![]() | Noragami Aragoto | 30503 | TV | [Action, Fantasy] | 8.16 | 8.156397 | 2015.0 | 2015.0 | 0.794872 | 0.551604 | 1.512833 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [Action, Adventure, Fantasy] | 7.85 | 7.839802 | 2016.0 | 2016.0 | 0.820513 | 0.512621 | 1.453461 |
| 8268 | ![]() | Noragami | 20507 | TV | [Action, Fantasy] | 7.95 | 7.947757 | 2014.0 | 2014.0 | 0.769231 | 0.508175 | 1.453136 |
| 7354 | ![]() | Toaru Kagaku no Railgun S | 16049 | TV | [Action, Fantasy, Sci-Fi] | 8.02 | 8.005639 | 2013.0 | 2013.0 | 0.743590 | 0.503693 | 1.451906 |
| 11291 | ![]() | D.Gray-man Hallow | 32370 | TV | [Action, Adventure, Fantasy] | 7.70 | 7.671143 | 2016.0 | 2016.0 | 0.820513 | 0.509217 | 1.430907 |
| 12429 | ![]() | Nanatsu no Taizai: Imashime no Fukkatsu | 34577 | TV | [Action, Adventure, Fantasy] | 7.59 | 7.586863 | 2018.0 | 2018.0 | 0.871795 | 0.502224 | 1.426295 |
| 16139 | ![]() | Utawarerumono: Futari no Hakuoro | 40590 | TV | [Action, Drama, Fantasy] | 7.53 | 7.267058 | 2022.0 | 2022.0 | 0.974359 | 0.521446 | 1.421502 |
| 8981 | ![]() | Nanatsu no Taizai | 23755 | TV | [Action, Adventure, Fantasy] | 7.67 | 7.668250 | 2014.0 | 2014.0 | 0.769231 | 0.502232 | 1.415554 |
| 14114 | ![]() | Goblin Slayer | 37349 | TV | [Action, Adventure, Fantasy] | 7.42 | 7.417105 | 2018.0 | 2018.0 | 0.871795 | 0.513614 | 1.413669 |
| 9652 | ![]() | K: Return of Kings | 27991 | TV | [Action, Fantasy] | 7.56 | 7.546610 | 2015.0 | 2015.0 | 0.794872 | 0.510923 | 1.411995 |
| 16089 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou | 40496 | TV | [Action, Fantasy] | 7.38 | 7.375983 | 2020.0 | 2020.0 | 0.923077 | 0.500714 | 1.410219 |
| 368 | ![]() | Yuu☆Yuu☆Hakusho | 392 | TV | [Action, Fantasy] | 8.46 | 8.448490 | 1992.0 | 1992.0 | 0.205128 | 0.505614 | 1.398662 |
| 11869 | ![]() | Ao no Exorcist: Kyoto Fujouou-hen | 33506 | TV | [Action, Fantasy] | 7.35 | 7.345707 | 2017.0 | 2017.0 | 0.846154 | 0.505658 | 1.394563 |
| 5958 | ![]() | Ao no Exorcist | 9919 | TV | [Action, Fantasy] | 7.50 | 7.498247 | 2011.0 | 2011.0 | 0.692308 | 0.504723 | 1.381463 |
| 16560 | ![]() | The God of High School | 41353 | TV | [Action, Fantasy] | 7.07 | 7.067536 | 2020.0 | 2020.0 | 0.923077 | 0.507955 | 1.378129 |
| 7271 | ![]() | Hakkenden: Touhou Hakken Ibun | 15613 | TV | [Action, Fantasy, Mystery] | 7.39 | 7.350037 | 2013.0 | 2013.0 | 0.743590 | 0.507876 | 1.376078 |
| 8868 | ![]() | Garo: Honoo no Kokuin | 23311 | TV | [Action, Fantasy] | 7.35 | 7.317473 | 2014.0 | 2014.0 | 0.769231 | 0.503839 | 1.374554 |
| 22625 | ![]() | Yuusha Party wo Tsuihou sareta Beast Tamer, Saikyoushu no Nekomimi Shoujo to Deau | 52046 | TV | [Action, Fantasy] | 6.94 | 6.927540 | 2022.0 | 2022.0 | 0.974359 | 0.506214 | 1.370402 |
| 8641 | ![]() | Break Blade | 22433 | TV | [Action, Fantasy] | 7.25 | 7.210151 | 2014.0 | 2014.0 | 0.769231 | 0.512079 | 1.367278 |
df = recommender.get_anime_recommendations('Detective Conan', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20949 | ![]() | Dr. Stone: New World | 48549 | TV | [Adventure, Comedy, Sci-Fi] | 8.25 | 8.205729 | 2023.0 | 2023.0 | 1.000000 | 0.552148 | 1.560148 |
| 16299 | ![]() | Dr. Stone: Stone Wars | 40852 | TV | [Adventure, Comedy, Sci-Fi] | 8.17 | 8.164181 | 2021.0 | 2021.0 | 0.948718 | 0.553571 | 1.545874 |
| 14959 | ![]() | Dr. Stone | 38691 | TV | [Adventure, Comedy, Sci-Fi] | 8.29 | 8.286605 | 2019.0 | 2019.0 | 0.897436 | 0.502220 | 1.515390 |
| 12160 | ![]() | Detective Conan: Episode One - The Great Detective Turned Small | 34036 | Special | [Adventure, Comedy, Mystery] | 8.24 | 8.034132 | 2016.0 | 2016.0 | 0.820513 | 0.518734 | 1.480937 |
| 5985 | ![]() | Detective Conan Movie 15: Quarter of Silence | 9963 | Movie | [Adventure, Comedy, Mystery] | 8.00 | 7.900168 | 2011.0 | 2011.0 | 0.692308 | 0.547287 | 1.458637 |
| 21584 | ![]() | Meitantei Conan: Hannin no Hanzawa-san | 50010 | TV | [Comedy, Mystery] | 6.80 | 6.668162 | 2022.0 | 2022.0 | 0.974359 | 0.668725 | 1.449785 |
| 1368 | ![]() | Detective Conan Movie 10: Requiem of the Detectives | 1506 | Movie | [Adventure, Comedy, Mystery] | 8.04 | 7.951632 | 2006.0 | 2006.0 | 0.564103 | 0.551315 | 1.441911 |
| 1233 | ![]() | Detective Conan Movie 05: Countdown to Heaven | 1364 | Movie | [Adventure, Comedy, Mystery] | 8.12 | 8.040221 | 2001.0 | 2001.0 | 0.435897 | 0.554862 | 1.429312 |
| 7148 | ![]() | Detective Conan Movie 17: Private Eye in the Distant Sea | 14735 | Movie | [Adventure, Comedy, Mystery] | 7.67 | 7.569965 | 2013.0 | 2013.0 | 0.743590 | 0.541679 | 1.425456 |
| 1236 | ![]() | Detective Conan Movie 08: Magician of the Silver Sky | 1367 | Movie | [Adventure, Comedy, Mystery] | 8.06 | 7.972434 | 2004.0 | 2004.0 | 0.512821 | 0.538254 | 1.425269 |
| 21157 | ![]() | Lupin III: Part 6 | 49040 | TV | [Action, Adventure, Comedy, Mystery] | 7.17 | 6.964761 | 2021.0 | 2021.0 | 0.948718 | 0.570820 | 1.413673 |
| 1232 | ![]() | Detective Conan Movie 04: Captured in Her Eyes | 1363 | Movie | [Adventure, Comedy, Mystery] | 8.03 | 7.953159 | 2000.0 | 2000.0 | 0.410256 | 0.534993 | 1.400226 |
| 709 | ![]() | Detective Conan Movie 01: The Timed Skyscraper | 779 | Movie | [Adventure, Comedy, Mystery] | 7.85 | 7.792512 | 1997.0 | 1997.0 | 0.333333 | 0.583547 | 1.398580 |
| 4584 | ![]() | Detective Conan OVA 09: The Stranger in 10 Years... | 6438 | OVA | [Adventure, Comedy, Mystery] | 7.91 | 7.695124 | 2009.0 | 2009.0 | 0.641026 | 0.504250 | 1.394510 |
| 6672 | ![]() | Detective Conan Movie 16: The Eleventh Striker | 12117 | Movie | [Adventure, Comedy, Mystery, Sports] | 7.71 | 7.620433 | 2012.0 | 2012.0 | 0.717949 | 0.494738 | 1.394464 |
| 710 | ![]() | Detective Conan Movie 02: The Fourteenth Target | 780 | Movie | [Adventure, Comedy, Mystery] | 7.88 | 7.813251 | 1998.0 | 1998.0 | 0.358974 | 0.565530 | 1.393945 |
| 1367 | ![]() | Detective Conan Movie 09: Strategy Above the Depths | 1505 | Movie | [Adventure, Comedy, Mystery] | 7.79 | 7.706469 | 2005.0 | 2005.0 | 0.538462 | 0.512610 | 1.381043 |
| 8519 | ![]() | The Disappearance of Conan Edogawa: The Worst Two Days in History | 21867 | Special | [Adventure, Comedy, Mystery] | 7.77 | 7.469457 | 2014.0 | 2014.0 | 0.769231 | 0.486419 | 1.380946 |
| 5428 | ![]() | Detective Conan OVA 10: Kid in Trap Island | 8609 | OVA | [Adventure, Comedy, Mystery] | 7.76 | 7.527941 | 2010.0 | 2010.0 | 0.666667 | 0.495336 | 1.373515 |
| 711 | ![]() | Detective Conan Movie 03: The Last Wizard of the Century | 781 | Movie | [Adventure, Comedy, Mystery] | 8.03 | 7.958093 | 1999.0 | 1999.0 | 0.384615 | 0.497186 | 1.369981 |
| 1235 | ![]() | Detective Conan Movie 07: Crossroad in the Ancient Capital | 1366 | Movie | [Adventure, Comedy, Mystery] | 7.80 | 7.720456 | 2003.0 | 2003.0 | 0.487179 | 0.507417 | 1.368934 |
| 12306 | ![]() | Alice to Zouroku | 34350 | TV | [Adventure, Mystery] | 7.10 | 7.074564 | 2017.0 | 2017.0 | 0.846154 | 0.502045 | 1.359569 |
| 16290 | ![]() | Kanojo, Okarishimasu | 40839 | TV | [Comedy, Romance] | 7.06 | 7.057983 | 2020.0 | 2020.0 | 0.923077 | 0.477707 | 1.356414 |
| 1988 | ![]() | Detective Conan Movie 11: Jolly Roger in the Deep Azure | 2171 | Movie | [Adventure, Comedy, Mystery] | 7.43 | 7.361065 | 2007.0 | 2007.0 | 0.589744 | 0.516618 | 1.352577 |
df = recommender.get_anime_recommendations('Tensei shitara Slime Datta Ken', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 15568 | ![]() | Tensei shitara Slime Datta Ken 2nd Season | 39551 | TV | [Action, Adventure, Comedy, Fantasy] | 8.39 | 8.382957 | 2021.0 | 2021.0 | 0.943396 | 0.667592 | 1.648596 |
| 16626 | ![]() | Tensei shitara Slime Datta Ken 2nd Season Part 2 | 41487 | TV | [Action, Adventure, Comedy, Fantasy] | 8.33 | 8.321181 | 2021.0 | 2021.0 | 0.943396 | 0.649520 | 1.628894 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | 41467 | TV | [Action, Adventure, Fantasy] | 9.07 | 9.048079 | 2022.0 | 2022.0 | 0.962264 | 0.514879 | 1.628340 |
| 19600 | ![]() | Jigokuraku | 46569 | TV | [Action, Adventure, Fantasy] | 8.26 | 8.224549 | 2023.0 | 2023.0 | 0.981132 | 0.523541 | 1.539180 |
| 16627 | ![]() | Tensura Nikki: Tensei shitara Slime Datta Ken | 41488 | TV | [Comedy, Fantasy] | 7.59 | 7.574482 | 2021.0 | 2021.0 | 0.943396 | 0.561880 | 1.479696 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [Action, Adventure, Fantasy] | 7.85 | 7.839802 | 2016.0 | 2016.0 | 0.849057 | 0.520858 | 1.464771 |
| 13221 | ![]() | Lupin III: Part 5 | 35857 | TV | [Action, Adventure, Comedy, Mystery] | 8.13 | 7.955901 | 2018.0 | 2018.0 | 0.886792 | 0.483682 | 1.460971 |
| 10865 | ![]() | Drifters | 31339 | TV | [Action, Adventure, Comedy, Fantasy] | 7.90 | 7.889898 | 2016.0 | 2016.0 | 0.849057 | 0.506194 | 1.460811 |
| 12429 | ![]() | Nanatsu no Taizai: Imashime no Fukkatsu | 34577 | TV | [Action, Adventure, Fantasy] | 7.59 | 7.586863 | 2018.0 | 2018.0 | 0.886792 | 0.525673 | 1.445240 |
| 11291 | ![]() | D.Gray-man Hallow | 32370 | TV | [Action, Adventure, Fantasy] | 7.70 | 7.671143 | 2016.0 | 2016.0 | 0.849057 | 0.516394 | 1.441496 |
| 12531 | ![]() | Mahoujin Guruguru (2017) | 34745 | TV | [Adventure, Comedy, Fantasy] | 7.81 | 7.597635 | 2017.0 | 2017.0 | 0.867925 | 0.523176 | 1.441061 |
| 21510 | ![]() | Tensei shitara Slime Datta Ken Movie: Guren no Kizuna-hen | 49877 | Movie | [Action, Adventure, Comedy, Fantasy] | 7.63 | 7.587295 | 2022.0 | 2022.0 | 0.962264 | 0.496722 | 1.440699 |
| 8981 | ![]() | Nanatsu no Taizai | 23755 | TV | [Action, Adventure, Fantasy] | 7.67 | 7.668250 | 2014.0 | 2014.0 | 0.811321 | 0.516120 | 1.433416 |
| 13124 | ![]() | Yama no Susume Third Season | 35672 | TV | [Adventure, Comedy, Slice of Life] | 7.59 | 7.401887 | 2018.0 | 2018.0 | 0.886792 | 0.528555 | 1.425003 |
| 4416 | ![]() | Dragon Ball Kai | 6033 | TV | [Action, Adventure, Comedy, Fantasy] | 7.73 | 7.719814 | 2009.0 | 2009.0 | 0.716981 | 0.517971 | 1.421994 |
| 8747 | ![]() | Dragon Ball Kai (2014) | 22777 | TV | [Action, Adventure, Comedy, Fantasy] | 7.68 | 7.658538 | 2014.0 | 2014.0 | 0.811321 | 0.500000 | 1.421289 |
| 14524 | ![]() | Kumo desu ga, Nani ka? | 37984 | TV | [Action, Adventure, Comedy, Fantasy, Mystery] | 7.45 | 7.441162 | 2021.0 | 2021.0 | 0.943396 | 0.499163 | 1.421050 |
| 11801 | ![]() | Cardcaptor Sakura: Clear Card-hen | 33354 | TV | [Adventure, Comedy, Fantasy, Romance] | 7.65 | 7.600925 | 2018.0 | 2018.0 | 0.886792 | 0.480401 | 1.416142 |
| 15029 | ![]() | Tensei shitara Slime Datta Ken OVA | 38793 | OVA | [Action, Adventure, Comedy, Fantasy] | 7.49 | 7.472148 | 2019.0 | 2019.0 | 0.905660 | 0.494457 | 1.414021 |
| 8423 | ![]() | Yama no Susume Second Season | 21435 | TV | [Adventure, Comedy, Slice of Life] | 7.56 | 7.433861 | 2014.0 | 2014.0 | 0.811321 | 0.528944 | 1.414009 |
| 10577 | ![]() | Dragon Ball Super | 30694 | TV | [Action, Adventure, Comedy, Fantasy] | 7.43 | 7.426386 | 2015.0 | 2015.0 | 0.830189 | 0.509236 | 1.403485 |
| 734 | ![]() | Dragon Ball Z | 813 | TV | [Action, Adventure, Comedy, Fantasy] | 8.16 | 8.156166 | 1989.0 | 1989.0 | 0.339623 | 0.513078 | 1.395558 |
| 7347 | ![]() | Tokyo Ravens | 16011 | TV | [Action, Fantasy, Romance] | 7.43 | 7.421522 | 2013.0 | 2013.0 | 0.792453 | 0.480625 | 1.375898 |
| 12183 | ![]() | Knight's & Magic | 34104 | TV | [Action, Fantasy] | 7.08 | 7.070405 | 2017.0 | 2017.0 | 0.867925 | 0.518234 | 1.374433 |
df = recommender.get_anime_recommendations('Darling in the FranXX', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20964 | ![]() | 86 Part 2 | 48569 | TV | [Action, Drama, Sci-Fi] | 8.71 | 8.692836 | 2021.0 | 2021.0 | 0.977778 | 0.476150 | 1.562478 |
| 8855 | ![]() | Shigatsu wa Kimi no Uso | 23273 | TV | [Drama, Romance] | 8.65 | 8.646914 | 2014.0 | 2014.0 | 0.822222 | 0.479830 | 1.528358 |
| 16608 | ![]() | 86 | 41457 | TV | [Action, Drama, Sci-Fi] | 8.28 | 8.270491 | 2021.0 | 2021.0 | 0.977778 | 0.477433 | 1.512669 |
| 21948 | ![]() | Lycoris Recoil | 50709 | TV | [Action] | 8.20 | 8.183950 | 2022.0 | 2022.0 | 1.000000 | 0.472867 | 1.503623 |
| 9604 | ![]() | Plastic Memories | 27775 | TV | [Drama, Romance, Sci-Fi] | 7.91 | 7.904120 | 2015.0 | 2015.0 | 0.844444 | 0.517298 | 1.469146 |
| 3375 | ![]() | Kidou Senshi Gundam 00 Second Season | 3927 | TV | [Action, Drama, Sci-Fi] | 8.08 | 8.048096 | 2008.0 | 2008.0 | 0.688889 | 0.480428 | 1.430240 |
| 11072 | ![]() | Kiznaiver | 31798 | TV | [Drama, Romance, Sci-Fi] | 7.38 | 7.374965 | 2016.0 | 2016.0 | 0.866667 | 0.522201 | 1.413426 |
| 16309 | ![]() | SSSS.Dynazenon | 40870 | TV | [Action, Sci-Fi] | 7.42 | 7.375668 | 2021.0 | 2021.0 | 0.977778 | 0.479599 | 1.406763 |
| 10540 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | TV | [Action, Drama, Sci-Fi] | 7.61 | 7.309665 | 2015.0 | 2015.0 | 0.844444 | 0.515613 | 1.396666 |
| 16 | ![]() | Texhnolyze | 26 | TV | [Action, Drama, Sci-Fi] | 7.76 | 7.717234 | 2003.0 | 2003.0 | 0.577778 | 0.517278 | 1.393373 |
| 6358 | ![]() | Guilty Crown | 10793 | TV | [Action, Drama, Sci-Fi] | 7.42 | 7.417078 | 2011.0 | 2011.0 | 0.755556 | 0.517905 | 1.393336 |
| 7558 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus | 17080 | TV | [Action, Drama, Sci-Fi] | 7.37 | 7.177703 | 2015.0 | 2015.0 | 0.844444 | 0.519534 | 1.383496 |
| 12271 | ![]() | Grancrest Senki | 34279 | TV | [Action, Drama, Fantasy, Romance] | 7.22 | 7.208601 | 2018.0 | 2018.0 | 0.911111 | 0.489675 | 1.380234 |
| 7299 | ![]() | Senki Zesshou Symphogear G | 15793 | TV | [Action, Sci-Fi] | 7.46 | 7.396078 | 2013.0 | 2013.0 | 0.800000 | 0.477679 | 1.372351 |
| 9655 | ![]() | Macross Δ | 28013 | TV | [Action, Romance, Sci-Fi] | 7.27 | 7.213781 | 2016.0 | 2016.0 | 0.866667 | 0.481872 | 1.366660 |
| 72 | ![]() | Kidou Senshi Gundam SEED | 93 | TV | [Action, Award Winning, Drama, Romance, Sci-Fi] | 7.75 | 7.724560 | 2002.0 | 2002.0 | 0.555556 | 0.476327 | 1.361961 |
| 10460 | ![]() | Classroom☆Crisis | 30383 | TV | [Drama, Romance, Sci-Fi] | 6.97 | 6.949188 | 2015.0 | 2015.0 | 0.844444 | 0.517915 | 1.354973 |
| 22263 | ![]() | Engage Kiss | 51417 | TV | [Action, Comedy, Romance] | 6.84 | 6.829794 | 2022.0 | 2022.0 | 1.000000 | 0.488282 | 1.351607 |
| 8424 | ![]() | Buddy Complex | 21437 | TV | [Action, Sci-Fi] | 7.12 | 7.088736 | 2014.0 | 2014.0 | 0.822222 | 0.481030 | 1.342193 |
| 5559 | ![]() | Star Driver: Kagayaki no Takuto | 8934 | TV | [Action, Romance, Sci-Fi] | 7.19 | 7.159537 | 2010.0 | 2010.0 | 0.733333 | 0.484168 | 1.335045 |
| 16266 | ![]() | Hypnosis Mic: Division Rap Battle - Rhyme Anima | 40803 | TV | [Action, Sci-Fi] | 6.80 | 6.764323 | 2020.0 | 2020.0 | 0.955556 | 0.481011 | 1.329917 |
| 113 | ![]() | Gunslinger Girl | 134 | TV | [Action, Drama, Sci-Fi] | 7.39 | 7.367269 | 2003.0 | 2003.0 | 0.577778 | 0.480128 | 1.326115 |
| 73 | ![]() | Kidou Senshi Gundam SEED Destiny | 94 | TV | [Action, Drama, Romance, Sci-Fi] | 7.18 | 7.158412 | 2004.0 | 2004.0 | 0.600000 | 0.510245 | 1.325976 |
| 69 | ![]() | Shin Kidou Senki Gundam Wing | 90 | TV | [Action, Drama, Sci-Fi] | 7.69 | 7.664281 | 1995.0 | 1995.0 | 0.400000 | 0.475999 | 1.323393 |
df = recommender.get_anime_recommendations('Kimetsu no Yaiba', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [Action, Fantasy] | 8.49 | 8.467293 | 2023.0 | 2023.0 | 1.000000 | 0.675826 | 1.675637 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [Action, Fantasy] | 8.80 | 8.794506 | 2021.0 | 2021.0 | 0.948718 | 0.627257 | 1.671619 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [Action, Fantasy] | 8.39 | 8.380549 | 2021.0 | 2021.0 | 0.948718 | 0.627586 | 1.622168 |
| 16236 | ![]() | Jujutsu Kaisen | 40748 | TV | [Action, Award Winning, Fantasy] | 8.64 | 8.637287 | 2020.0 | 2020.0 | 0.923077 | 0.510968 | 1.568548 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | [Action, Fantasy] | 8.62 | 8.615747 | 2020.0 | 2020.0 | 0.923077 | 0.493573 | 1.554134 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [Action, Fantasy] | 8.59 | 8.584003 | 2022.0 | 2022.0 | 0.974359 | 0.456532 | 1.535394 |
| 6589 | ![]() | Fate/Zero 2nd Season | 11741 | TV | [Action, Fantasy, Supernatural] | 8.55 | 8.544452 | 2012.0 | 2012.0 | 0.717949 | 0.460878 | 1.482321 |
| 14835 | ![]() | Toaru Kagaku no Railgun T | 38481 | TV | [Action, Fantasy, Sci-Fi] | 8.17 | 8.134640 | 2020.0 | 2020.0 | 0.923077 | 0.454360 | 1.469737 |
| 9821 | ![]() | Fate/stay night: Unlimited Blade Works 2nd Season | 28701 | TV | [Action, Fantasy, Supernatural] | 8.32 | 8.313739 | 2015.0 | 2015.0 | 0.794872 | 0.453259 | 1.464839 |
| 10527 | ![]() | Noragami Aragoto | 30503 | TV | [Action, Fantasy] | 8.16 | 8.156397 | 2015.0 | 2015.0 | 0.794872 | 0.449990 | 1.443735 |
| 7799 | ![]() | Magi: The Kingdom of Magic | 18115 | TV | [Action, Adventure, Fantasy] | 8.22 | 8.212752 | 2013.0 | 2013.0 | 0.743590 | 0.454806 | 1.443516 |
| 6044 | ![]() | Fate/Zero | 10087 | TV | [Action, Fantasy, Supernatural] | 8.28 | 8.275991 | 2011.0 | 2011.0 | 0.692308 | 0.457940 | 1.442980 |
| 8617 | ![]() | Fate/stay night: Unlimited Blade Works | 22297 | TV | [Action, Fantasy, Supernatural] | 8.19 | 8.185116 | 2014.0 | 2014.0 | 0.769231 | 0.450000 | 1.442060 |
| 8268 | ![]() | Noragami | 20507 | TV | [Action, Fantasy] | 7.95 | 7.947757 | 2014.0 | 2014.0 | 0.769231 | 0.452160 | 1.415046 |
| 7119 | ![]() | Magi: The Labyrinth of Magic | 14513 | TV | [Action, Adventure, Fantasy] | 8.02 | 8.014632 | 2012.0 | 2012.0 | 0.717949 | 0.452839 | 1.413276 |
| 438 | ![]() | Koukaku Kidoutai: Stand Alone Complex | 467 | TV | [Action, Award Winning, Sci-Fi] | 8.42 | 8.397903 | 2002.0 | 2002.0 | 0.461538 | 0.456112 | 1.410212 |
| 5958 | ![]() | Ao no Exorcist | 9919 | TV | [Action, Fantasy] | 7.50 | 7.498247 | 2011.0 | 2011.0 | 0.692308 | 0.526891 | 1.396537 |
| 13644 | ![]() | Hug tto! Precure | 36593 | TV | [Action, Fantasy] | 7.85 | 7.459017 | 2018.0 | 2018.0 | 0.871795 | 0.454490 | 1.378495 |
| 368 | ![]() | Yuu☆Yuu☆Hakusho | 392 | TV | [Action, Fantasy] | 8.46 | 8.448490 | 1992.0 | 1992.0 | 0.205128 | 0.462824 | 1.369565 |
| 1654 | ![]() | Claymore | 1818 | TV | [Action, Adventure, Fantasy] | 7.74 | 7.732521 | 2007.0 | 2007.0 | 0.589744 | 0.472259 | 1.366987 |
| 8598 | ![]() | Akame ga Kill! | 22199 | TV | [Action, Fantasy] | 7.47 | 7.468480 | 2014.0 | 2014.0 | 0.769231 | 0.460057 | 1.362903 |
| 11869 | ![]() | Ao no Exorcist: Kyoto Fujouou-hen | 33506 | TV | [Action, Fantasy] | 7.35 | 7.345707 | 2017.0 | 2017.0 | 0.846154 | 0.452590 | 1.358477 |
| 10662 | ![]() | Tales of Zestiria the Cross | 30911 | TV | [Action, Adventure, Fantasy] | 7.24 | 7.229445 | 2016.0 | 2016.0 | 0.820513 | 0.469140 | 1.350651 |
| 12175 | ![]() | Tales of Zestiria the Cross 2nd Season | 34086 | TV | [Action, Adventure, Fantasy] | 7.31 | 7.288806 | 2017.0 | 2017.0 | 0.846154 | 0.450000 | 1.349888 |
While the earlier version of the recommender provided reasonable content-based recommendations, it lacked nuanced control over temporal relevance and user-perceived freshness of anime titles. Specifically, although a recency boost was introduced, it treated all past anime equally without factoring in the anime's current airing status or episode count. This led to scenarios where long-running or still-airing shows were penalized unfairly, and older titles with high episode counts were underrepresented despite still holding popularity. Moreover, there was no mechanism to discourage the system from over-recommending outdated or short-lived anime. To resolve these issues, the updated class introduces a refined age penalty mechanism, dynamically adjusting scores based on whether the anime is currently airing and how many episodes it has. This creates a fairer scoring environment where timeless titles aren't overly suppressed, and still-airing or large-episode anime retain their momentum in the recommendation list. As a result, recommendations now better reflect both content similarity and user-relevant freshness, offering a more practical and satisfying user experience.
AnimeRecommender Class – Advanced Multi-Factor Hybrid Recommendation System¶
Summary:¶
The AnimeRecommender class implements an intelligent, multi-layered recommendation system for anime titles. It integrates content-based similarity, recency weighting, user behavior, and temporal penalty modeling to deliver personalized and high-quality results.
Methodology Overview:¶
The system follows a structured content-based recommendation approach with natural language processing, multi-hot encoding, and temporal scoring. Its major components include:
Core Components & Workflow:¶
1. Initialization & Data Assignment¶
Accepts two dataframes:
anime_df: Metadata about anime titles.ratings_df: User rating data.
Initializes key NLP tools:
- NLTK-based
WordNetLemmatizer stopwords,tokenizers, andPOS taggers
- NLTK-based
2. Data Preprocessing (_preprocess_data)¶
Cleans and converts:
Score,Scored By, andEpisodescolumns to numeric.
Extracts:
- Release year from
Aired. - Multi-label fields:
Genres,Studios.
- Release year from
Computes:
- IMDb-style weighted rating:
$$ \text{weighted_rating} = \frac{v}{v + m}R + \frac{m}{v + m}C $$
3. Feature Engineering (_create_feature_matrices)¶
Encodes core features into machine-readable format:
| Feature | Encoding Method |
|---|---|
| Genres, Studios | MultiLabelBinarizer |
| Type, Source | OneHotEncoder |
| Episodes (Binned) | Binned + OneHotEncoder |
| Synopsis | TF-IDF with NLTK preprocessing |
Text cleaning uses:
- Tokenization
- Stopword removal
- POS tagging
- Lemmatization
4. Similarity Calculation (_calculate_anime_similarity)¶
Calculates weighted cosine similarity between all anime using:
| Feature | Weight |
|---|---|
| Genres | 0.30 |
| Synopsis (TF-IDF) | 0.35 |
| Type | 0.15 |
| Studios | 0.10 |
| Episodes | 0.05 |
| Source | 0.05 |
Returns top N most similar anime based on the composite similarity score.
5. Recency Boost (_calculate_recency_boost)¶
Scales anime by how recent they are:
$$ \text{recency_boost} = \frac{(\text{year} - \text{min})}{\text{max} - \text{min}} $$
Used to amplify new titles in final score.
6. Age Penalty (in get_anime_recommendations1/2)¶
Introduced to penalize:
- Old, short, completed series
- While rewarding long or currently airing ones
7. Final Score Combination¶
The final score formula blends:
similarity_scoreweighted_ratingrecency_score- Age penalty (divides or subtracts)
🔍 Available Recommendation Modes:¶
| Method | Description |
|---|---|
get_anime_recommendations | Basic similarity + recency weighting |
get_anime_recommendations1 | Adds age penalty for smarter ranking |
get_anime_recommendations2 | Alternative penalty scoring logic |
get_recommendations_by_genres | Genre-driven recommendations with IMDb-style popularity sorting |
nltk.download('punkt')
nltk.download('stopwords')
nltk.download('wordnet')
nltk.download('averaged_perceptron_tagger_eng')
nltk.download('universal_tagset')
nltk.download('punkt_tab')
class AnimeRecommender:
def __init__(self, anime_df, ratings_df):
"""Initialize the recommender with anime and ratings data."""
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
# Initialize NLP tools
self.lemmatizer = WordNetLemmatizer()
self.stop_words = set(stopwords.words('english'))
self._preprocess_data()
self._create_feature_matrices()
# Similarity weights
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
"""Data cleaning and feature engineering with memory optimization."""
# Handle missing values
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['Release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
self.anime_df['Genres'] = self.anime_df['Genres'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
self.anime_df['Studios'] = self.anime_df['Studios'].apply(lambda x: x.split(', ') if x != 'UNKNOWN' else [])
# Weighted rating
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
"""Create sparse feature matrices for fast similarity calculations."""
# Multi-label genres and studios
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
# TF-IDF for synopsis with enhanced cleaning
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'].fillna(''))
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
@staticmethod
def get_wordnet_pos(tag):
"""Map NLTK POS tag to WordNet POS tag for lemmatization."""
if tag.startswith('J'):
return 'a' # adjective
elif tag.startswith('V'):
return 'v' # verb
elif tag.startswith('N'):
return 'n' # noun
elif tag.startswith('R'):
return 'r' # adverb
else:
return 'n' # default to noun
def _lemmatize_text(self, text: str) -> str:
"""Lemmatize and clean a single text string."""
text = text.lower()
tokens = word_tokenize(text)
tagged_tokens = nltk.pos_tag(tokens, tagset='universal')
cleaned_tokens = []
for token, tag in tagged_tokens:
if token not in string.punctuation and token not in self.stop_words:
wordnet_pos = self.get_wordnet_pos(tag)
lemma = self.lemmatizer.lemmatize(token, pos=wordnet_pos)
cleaned_tokens.append(lemma)
return ' '.join(cleaned_tokens)
def _clean_text(self, text_series: pd.Series) -> pd.Series:
"""Clean text data by lowercasing, tokenizing, removing punctuation and stopwords, and lemmatizing."""
return text_series.apply(self._lemmatize_text)
def _calculate_anime_similarity(self, idx: int) -> tuple[np.ndarray, np.ndarray]:
"""Calculate weighted similarity for a single anime using sparse operations."""
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
# Ensure that the number of recommendations requested doesn't exceed the number of available animes minus 1 (to exclude self)
num_recs_to_get = min(52, len(self.anime_df) - 1)
# Sort by similarity score in descending order and get top N+1 (including self)
sorted_sim_scores = sim_scores[sim_scores[:, 1].argsort()[::-1]]
# Exclude the first element (self) and take the next num_recs_to_get
top_sim = sorted_sim_scores[1:num_recs_to_get + 1]
top_indices = top_sim[:, 0].astype(int)
sim_scores_top = top_sim[:, 1]
return sim_scores_top, top_indices
def _calculate_recency_boost(self, release_year):
"""Calculate a simple recency boost score based on the release year."""
# Assuming a linear boost, you can adjust this formula
current_year = 2025 # Adjust if needed
# Handle potential NaN or non-numeric release_year
year = release_year if pd.notna(release_year) else self.anime_df['Release_year'].min()
min_year = self.anime_df['Release_year'].min()
max_year = self.anime_df['Release_year'].max()
year_range = max_year - min_year if max_year != min_year else 1
# Scale the year to a 0-1 range
scaled_year = (year - min_year) / year_range
# You can adjust the recency boost formula based on scaled_year
# Example: a simple linear boost where newer anime get a higher score
recency_boost = scaled_year # A value between 0 and 1
return recency_boost
def get_anime_recommendations(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
"""Generate anime recommendations based on a given title."""
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year']
].copy()
# Apply the recency boost directly to the recommended animes
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
recommended_animes['similarity_score'] = sim_scores_top
rating_weight = 1 - similarity_weight
# Ensure all weight components are non-negative
rating_weight = max(0, rating_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
qualified_animes = recommended_animes.copy() # Use a consistent variable name
# Compute final score
qualified_animes['final_score'] = (
(1-similarity_weight) * qualified_animes['weighted_rating'] +
similarity_weight * qualified_animes['similarity_score'])* (1-recency_weight) + (recency_weight * qualified_animes['recency_score'])
return qualified_animes.sort_values('final_score', ascending=False).head(n)
def get_recommendations_by_genres(self, genres, n=10):
"""Generate anime recommendations based on specified genres with IMDb-like popularity.
Args:
genres (str or List[str]): A single genre or list of genres to base recommendations on.
n (int, optional): Number of recommendations to return. Defaults to 10.
Returns:
DataFrame: Top n anime recommendations with title, genres, final score, and popularity score.
"""
# Ensure genres is a list
if isinstance(genres, str):
genres = [genres]
# Validate genres
valid_genres = set(self.mlb_genres.classes_)
genres = [g for g in genres if g in valid_genres]
if not genres:
raise ValueError("No valid genres provided.")
# Create a genre vector for input genres
genre_vector = self.mlb_genres.transform([genres]).toarray()[0]
# Calculate similarity score based on genre overlap
similarity_scores = (self.genres_encoded.toarray() @ genre_vector).ravel()
# Create a copy of the anime DataFrame for processing
recommendations = self.anime_df[['Name', 'Genres', 'Release_year', 'Episodes', 'Status', 'weighted_rating','Image URL']].copy()
recommendations['similarity_score'] = similarity_scores
# Filter out anime with no genre overlap
recommendations = recommendations[recommendations['similarity_score'] > 0]
# Calculate age penalty
current_year = 2025 # Current year as of May 21, 2025
recommendations['end_year'] = recommendations['Release_year'].fillna(current_year)
recommendations['episodes'] = pd.to_numeric(recommendations['Episodes'], errors='coerce').fillna(self.median_episodes)
# Apply age penalty: 0 for still airing, reduced penalty for large anime
recommendations['age_penalty'] = recommendations.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
# Sort by final score and select top n
recommendations = recommendations.sort_values(by=['weighted_rating', 'age_penalty'],
ascending=[ False, True]).head(n)
# Return relevant columns
return recommendations[['Image URL','Name', 'Genres', 'age_penalty', 'weighted_rating']]
def get_anime_recommendations1(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
"""Generate anime recommendations based on a given title with age penalty."""
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year', 'Episodes', 'Status']
].copy()
# Calculate similarity and recency scores
recommended_animes['similarity_score'] = sim_scores_top
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
# Calculate age penalty
current_year = 2025 # Current year as of May 25, 2025
recommended_animes['end_year'] = recommended_animes['Release_year'].fillna(current_year)
recommended_animes['episodes'] = pd.to_numeric(recommended_animes['Episodes'], errors='coerce').fillna(self.median_episodes)
recommended_animes['age_penalty'] = recommended_animes.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
# Ensure weights are non-negative
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
# Compute final score incorporating age penalty
recommended_animes['final_score'] = (
(rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']) * (1 - recency_weight) +
recency_weight * recommended_animes['recency_score']
) / (1 + recommended_animes['age_penalty'] / 10) # Scale down age penalty impact
# Sort by final score and select top n
return recommended_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'final_score', 'age_penalty']
]
def get_anime_recommendations2(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
"""Generate anime recommendations based on a given title with age penalty."""
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year', 'Episodes', 'Status']
].copy()
# Calculate similarity and recency scores
recommended_animes['similarity_score'] = sim_scores_top
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
# Calculate age penalty
current_year = 2025 # Current year as of May 25, 2025
recommended_animes['end_year'] = recommended_animes['Release_year'].fillna(current_year)
recommended_animes['episodes'] = pd.to_numeric(recommended_animes['Episodes'], errors='coerce').fillna(self.median_episodes)
recommended_animes['age_penalty'] = recommended_animes.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
# Ensure weights are non-negative
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
# Compute final score incorporating age penalty
recommended_animes['final_score'] = (
(rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']) * (1 - recency_weight) )+(
recency_weight * recommended_animes['age_penalty']
)
# Sort by final score and select top n
return recommended_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'final_score', 'age_penalty']
]
[nltk_data] Downloading package punkt to /root/nltk_data... [nltk_data] Package punkt is already up-to-date! [nltk_data] Downloading package stopwords to /root/nltk_data... [nltk_data] Package stopwords is already up-to-date! [nltk_data] Downloading package wordnet to /root/nltk_data... [nltk_data] Package wordnet is already up-to-date! [nltk_data] Downloading package averaged_perceptron_tagger_eng to [nltk_data] /root/nltk_data... [nltk_data] Package averaged_perceptron_tagger_eng is already up-to- [nltk_data] date! [nltk_data] Downloading package universal_tagset to /root/nltk_data... [nltk_data] Package universal_tagset is already up-to-date! [nltk_data] Downloading package punkt_tab to /root/nltk_data... [nltk_data] Package punkt_tab is already up-to-date!
- Lets make an instance and show some test...
AnimeRecommender = AnimeRecommender(anime_df, ratings_df)
df = AnimeRecommender.get_anime_recommendations1('Darling in the FranXX', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 21948 | ![]() | Lycoris Recoil | 50709 | TV | [Action] | 8.20 | 8.183950 | 1.166947 | 2.837500 |
| 20964 | ![]() | 86 Part 2 | 48569 | TV | [Action, Drama, Sci-Fi] | 8.71 | 8.692836 | 1.130083 | 3.800000 |
| 16608 | ![]() | 86 | 41457 | TV | [Action, Drama, Sci-Fi] | 8.28 | 8.270491 | 1.092670 | 3.816667 |
| 22263 | ![]() | Engage Kiss | 51417 | TV | [Action, Comedy, Romance] | 6.84 | 6.829794 | 1.048531 | 2.837500 |
| 16309 | ![]() | SSSS.Dynazenon | 40870 | TV | [Action, Sci-Fi] | 7.42 | 7.375668 | 1.017247 | 3.800000 |
| 21073 | ![]() | Cardfight!! Vanguard: overDress Season 2 | 48862 | TV | [Action, Drama] | 6.67 | 6.538178 | 0.946119 | 3.783333 |
| 16266 | ![]() | Hypnosis Mic: Division Rap Battle - Rhyme Anima | 40803 | TV | [Action, Sci-Fi] | 6.80 | 6.764323 | 0.902663 | 4.729167 |
| 12271 | ![]() | Grancrest Senki | 34279 | TV | [Action, Drama, Fantasy, Romance] | 7.22 | 7.208601 | 0.849723 | 6.300000 |
| 11508 | ![]() | Senki Zesshou Symphogear AXZ | 32836 | TV | [Action, Sci-Fi] | 7.60 | 7.472081 | 0.798264 | 7.566667 |
| 13591 | ![]() | Beatless | 36516 | TV | [Action, Drama, Romance, Sci-Fi] | 6.21 | 6.215648 | 0.776584 | 6.416667 |
| 8855 | ![]() | Shigatsu wa Kimi no Uso | 23273 | TV | [Drama, Romance] | 8.65 | 8.646914 | 0.772093 | 9.991667 |
| 11072 | ![]() | Kiznaiver | 31798 | TV | [Drama, Romance, Sci-Fi] | 7.38 | 7.374965 | 0.767345 | 8.550000 |
| 9655 | ![]() | Macross Δ | 28013 | TV | [Action, Romance, Sci-Fi] | 7.27 | 7.213781 | 0.763750 | 8.025000 |
| 9604 | ![]() | Plastic Memories | 27775 | TV | [Drama, Romance, Sci-Fi] | 7.91 | 7.904120 | 0.761493 | 9.458333 |
| 10540 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | TV | [Action, Drama, Sci-Fi] | 7.61 | 7.309665 | 0.724244 | 9.458333 |
| 7558 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus | 17080 | TV | [Action, Drama, Sci-Fi] | 7.37 | 7.177703 | 0.717476 | 9.458333 |
| 10460 | ![]() | Classroom☆Crisis | 30383 | TV | [Drama, Romance, Sci-Fi] | 6.97 | 6.949188 | 0.702818 | 9.458333 |
| 8424 | ![]() | Buddy Complex | 21437 | TV | [Action, Sci-Fi] | 7.12 | 7.088736 | 0.665246 | 10.404167 |
| 8823 | ![]() | M3: Sono Kuroki Hagane | 23133 | TV | [Action, Drama, Mystery, Sci-Fi] | 6.57 | 6.553907 | 0.654233 | 9.900000 |
| 7299 | ![]() | Senki Zesshou Symphogear G | 15793 | TV | [Action, Sci-Fi] | 7.46 | 7.396078 | 0.651114 | 11.350000 |
| 6358 | ![]() | Guilty Crown | 10793 | TV | [Action, Drama, Sci-Fi] | 7.42 | 7.417078 | 0.623462 | 12.716667 |
| 6594 | ![]() | Senki Zesshou Symphogear | 11751 | TV | [Action, Sci-Fi] | 7.03 | 7.004872 | 0.602644 | 12.295833 |
| 5559 | ![]() | Star Driver: Kagayaki no Takuto | 8934 | TV | [Action, Romance, Sci-Fi] | 7.19 | 7.159537 | 0.580523 | 13.437500 |
| 3375 | ![]() | Kidou Senshi Gundam 00 Second Season | 3927 | TV | [Action, Drama, Sci-Fi] | 8.08 | 8.048096 | 0.579084 | 15.229167 |
df = AnimeRecommender.get_anime_recommendations2('Darling in the FranXX', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 2013 | ![]() | Muteki Choujin Zanbot 3 | 2200 | TV | [Action, Drama, Sci-Fi] | 7.16 | 6.752812 | 9.855553 | 43.400000 |
| 4292 | ![]() | Uchuu Kuubo Blue Noah | 5763 | TV | [Action, Drama, Sci-Fi] | 6.53 | 6.417003 | 9.404405 | 41.400000 |
| 59 | ![]() | Kidou Senshi Gundam | 80 | TV | [Action, Drama, Sci-Fi] | 7.76 | 7.718750 | 8.803062 | 37.758333 |
| 988 | ![]() | Macross | 1088 | TV | [Action, Romance, Sci-Fi] | 7.90 | 7.828247 | 8.575618 | 36.550000 |
| 3675 | ![]() | Choujikuu Kidan Southern Cross | 4503 | TV | [Action, Drama, Sci-Fi] | 6.56 | 6.497439 | 8.550448 | 37.070833 |
| 4663 | ![]() | Chou Kousoku Galvion | 6636 | TV | [Action, Sci-Fi] | 6.04 | 6.330598 | 8.534154 | 37.241667 |
| 2369 | ![]() | Soukou Kihei Votoms | 2582 | TV | [Action, Drama, Sci-Fi] | 7.70 | 7.395743 | 7.793458 | 32.900000 |
| 64 | ![]() | Kidou Senshi Zeta Gundam | 85 | TV | [Drama, Romance, Sci-Fi] | 7.90 | 7.828992 | 7.596149 | 31.666667 |
| 1325 | ![]() | Uchuu no Kishi Tekkaman Blade | 1459 | TV | [Action, Adventure, Drama, Romance, Sci-Fi] | 7.45 | 7.190315 | 6.436900 | 26.262500 |
| 195 | ![]() | Kidou Senkan Nadesico | 218 | TV | [Action, Romance, Sci-Fi] | 7.49 | 7.397964 | 6.396828 | 25.858333 |
| 69 | ![]() | Shin Kidou Senki Gundam Wing | 90 | TV | [Action, Drama, Sci-Fi] | 7.69 | 7.664281 | 6.018393 | 23.875000 |
| 1789 | ![]() | Chikyuu Bouei Kazoku | 1962 | TV | [Action, Comedy, Drama, Sci-Fi] | 6.85 | 6.518807 | 5.647813 | 22.700000 |
| 113 | ![]() | Gunslinger Girl | 134 | TV | [Action, Drama, Sci-Fi] | 7.39 | 7.367269 | 5.372226 | 20.808333 |
| 16 | ![]() | Texhnolyze | 26 | TV | [Action, Drama, Sci-Fi] | 7.76 | 7.717234 | 5.274484 | 19.983333 |
| 92 | ![]() | Uchuu no Stellvia | 113 | TV | [Action, Romance, Sci-Fi] | 7.37 | 7.215704 | 5.122776 | 19.616667 |
| 453 | ![]() | Kurau Phantom Memory | 483 | TV | [Action, Drama, Sci-Fi] | 7.29 | 7.126334 | 4.982339 | 18.900000 |
| 2911 | ![]() | IGPX: Immortal Grand Prix (2005) | 3270 | TV | [Action, Drama, Sci-Fi] | 7.07 | 6.950303 | 4.976938 | 18.916667 |
| 55 | ![]() | Soukyuu no Fafner: Dead Aggressor | 75 | TV | [Action, Drama, Sci-Fi] | 7.26 | 7.166644 | 4.945383 | 18.812500 |
| 72 | ![]() | Kidou Senshi Gundam SEED | 93 | TV | [Action, Award Winning, Drama, Romance, Sci-Fi] | 7.75 | 7.724560 | 4.892516 | 18.208333 |
| 130 | ![]() | Solty Rei | 152 | TV | [Action, Sci-Fi] | 7.27 | 7.159682 | 4.782494 | 18.000000 |
| 1279 | ![]() | IGPX: Immortal Grand Prix (2005) 2nd Season | 1410 | TV | [Action, Sci-Fi] | 7.25 | 7.033195 | 4.758792 | 17.970833 |
| 775 | ![]() | Gunparade Orchestra | 858 | TV | [Drama, Romance, Sci-Fi] | 5.84 | 6.161658 | 4.665074 | 18.000000 |
| 793 | ![]() | Zegapain | 878 | TV | [Action, Romance, Sci-Fi] | 7.29 | 7.163313 | 4.580063 | 16.941667 |
| 73 | ![]() | Kidou Senshi Gundam SEED Destiny | 94 | TV | [Action, Drama, Romance, Sci-Fi] | 7.18 | 7.158412 | 4.530976 | 16.625000 |
df = AnimeRecommender.get_anime_recommendations('Naruto: Shippuuden', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | Release_year | recency_score | similarity_score | final_score | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 16617 | ![]() | Bleach: Sennen Kessen-hen | 41467 | TV | [Action, Adventure, Fantasy] | 9.07 | 9.048079 | 2022.0 | 0.972222 | 0.611729 | 1.696190 |
| 10 | ![]() | Naruto | 20 | TV | [Action, Adventure, Fantasy] | 7.99 | 7.988501 | 2002.0 | 0.787037 | 0.783057 | 1.648506 |
| 6456 | ![]() | Hunter x Hunter (2011) | 11061 | TV | [Action, Adventure, Fantasy] | 9.04 | 9.037173 | 2011.0 | 0.870370 | 0.555852 | 1.636514 |
| 3961 | ![]() | Fullmetal Alchemist: Brotherhood | 5114 | TV | [Action, Adventure, Drama, Fantasy] | 9.10 | 9.097636 | 2009.0 | 0.851852 | 0.523833 | 1.618293 |
| 368 | ![]() | Yuu☆Yuu☆Hakusho | 392 | TV | [Action, Fantasy] | 8.46 | 8.448490 | 1992.0 | 0.694444 | 0.601456 | 1.561698 |
| 245 | ![]() | Bleach | 269 | TV | [Action, Adventure, Fantasy] | 7.92 | 7.917476 | 2004.0 | 0.805556 | 0.654016 | 1.555939 |
| 11 | ![]() | One Piece | 21 | TV | [Action, Adventure, Fantasy] | 8.69 | 8.686696 | 1999.0 | 0.759259 | 0.519085 | 1.547233 |
| 12428 | ![]() | Black Clover | 34572 | TV | [Action, Comedy, Fantasy] | 8.14 | 8.136070 | 2017.0 | 0.925926 | 0.560497 | 1.542652 |
| 115 | ![]() | Hunter x Hunter | 136 | TV | [Action, Adventure, Fantasy] | 8.41 | 8.397139 | 1999.0 | 0.759259 | 0.554684 | 1.536694 |
| 9225 | ![]() | Akatsuki no Yona | 25013 | TV | [Action, Adventure, Fantasy, Romance] | 8.03 | 8.022766 | 2014.0 | 0.898148 | 0.567099 | 1.527989 |
| 7799 | ![]() | Magi: The Kingdom of Magic | 18115 | TV | [Action, Adventure, Fantasy] | 8.22 | 8.212752 | 2013.0 | 0.888889 | 0.531031 | 1.524409 |
| 1344 | ![]() | D.Gray-man | 1482 | TV | [Action, Adventure, Fantasy] | 8.02 | 8.009810 | 2006.0 | 0.824074 | 0.557886 | 1.505355 |
| 131 | ![]() | Juuni Kokuki | 153 | TV | [Action, Adventure, Fantasy] | 8.02 | 7.957254 | 2002.0 | 0.787037 | 0.554957 | 1.489649 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [Action, Adventure, Fantasy] | 7.74 | 7.643075 | 2020.0 | 0.953704 | 0.557394 | 1.486937 |
| 8558 | ![]() | Fairy Tail (2014) | 22043 | TV | [Action, Adventure, Fantasy] | 7.65 | 7.645865 | 2014.0 | 0.898148 | 0.571065 | 1.485457 |
| 7001 | ![]() | Naruto: Shippuuden Movie 6 - Road to Ninja | 13667 | Movie | [Action, Adventure, Fantasy] | 7.68 | 7.668692 | 2012.0 | 0.879630 | 0.569754 | 1.483602 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [Action, Adventure, Fantasy] | 7.85 | 7.839802 | 2016.0 | 0.916667 | 0.515753 | 1.474822 |
| 13260 | ![]() | Fairy Tail: Final Series | 35972 | TV | [Action, Adventure, Fantasy] | 7.57 | 7.561180 | 2018.0 | 0.935185 | 0.557004 | 1.473141 |
| 734 | ![]() | Dragon Ball Z | 813 | TV | [Action, Adventure, Comedy, Fantasy] | 8.16 | 8.156166 | 1989.0 | 0.666667 | 0.523575 | 1.468104 |
| 8619 | ![]() | Tokyo Ghoul | 22319 | TV | [Action, Fantasy, Horror] | 7.79 | 7.788620 | 2014.0 | 0.898148 | 0.515360 | 1.464708 |
| 11291 | ![]() | D.Gray-man Hallow | 32370 | TV | [Action, Adventure, Fantasy] | 7.70 | 7.671143 | 2016.0 | 0.916667 | 0.520391 | 1.457736 |
| 4694 | ![]() | Fairy Tail | 6702 | TV | [Action, Adventure, Fantasy] | 7.57 | 7.567877 | 2009.0 | 0.851852 | 0.556568 | 1.456982 |
| 8747 | ![]() | Dragon Ball Kai (2014) | 22777 | TV | [Action, Adventure, Comedy, Fantasy] | 7.68 | 7.658538 | 2014.0 | 0.898148 | 0.526743 | 1.456840 |
| 4416 | ![]() | Dragon Ball Kai | 6033 | TV | [Action, Adventure, Comedy, Fantasy] | 7.73 | 7.719814 | 2009.0 | 0.851852 | 0.528874 | 1.456383 |
df = AnimeRecommender.get_anime_recommendations1('High School DxD', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 15566 | ![]() | Yahari Ore no Seishun Love Comedy wa Machigatteiru. Kan | 39547 | TV | [Comedy, Romance] | 8.36 | 8.350713 | 1.047413 | 4.750000 |
| 14836 | ![]() | Ore wo Suki nano wa Omae dake ka yo | 38483 | TV | [Comedy, Romance] | 7.32 | 7.312971 | 0.903399 | 5.700000 |
| 16209 | ![]() | Monster Musume no Oishasan | 40708 | TV | [Comedy, Fantasy, Romance, Ecchi] | 6.53 | 6.527310 | 0.897358 | 4.750000 |
| 12273 | ![]() | High School DxD Hero | 34281 | TV | [Action, Comedy, Romance, Ecchi] | 7.25 | 7.244283 | 0.896247 | 6.650000 |
| 15419 | ![]() | Kawaikereba Hentai demo Suki ni Natte Kuremasu ka? | 39326 | TV | [Comedy, Romance, Ecchi] | 6.50 | 6.498662 | 0.858891 | 5.700000 |
| 10586 | ![]() | Saenai Heroine no Sodatekata ♭ | 30727 | TV | [Comedy, Romance, Ecchi] | 7.76 | 7.746675 | 0.846892 | 7.633333 |
| 9152 | ![]() | High School DxD BorN | 24703 | TV | [Action, Comedy, Romance, Ecchi] | 7.42 | 7.416314 | 0.816511 | 9.500000 |
| 12969 | ![]() | Imouto sae Ireba Ii. | 35413 | TV | [Comedy, Romance, Ecchi] | 7.28 | 7.269535 | 0.816102 | 7.600000 |
| 12272 | ![]() | Gamers! | 34280 | TV | [Comedy, Romance] | 6.76 | 6.758171 | 0.769588 | 7.600000 |
| 8856 | ![]() | Saenai Heroine no Sodatekata | 23277 | TV | [Comedy, Romance, Ecchi] | 7.48 | 7.473676 | 0.750873 | 9.500000 |
| 10393 | ![]() | Prison School | 30240 | TV | [Comedy, Romance, Ecchi] | 7.61 | 7.606261 | 0.748234 | 9.500000 |
| 7245 | ![]() | High School DxD New | 15451 | TV | [Action, Comedy, Romance, Ecchi] | 7.48 | 7.476668 | 0.743072 | 11.400000 |
| 11554 | ![]() | Eromanga-sensei | 32901 | TV | [Comedy, Drama, Romance, Ecchi] | 6.36 | 6.360103 | 0.740590 | 7.600000 |
| 10886 | ![]() | Netoge no Yome wa Onnanoko ja Nai to Omotta? | 31404 | TV | [Comedy, Romance, Ecchi] | 6.71 | 6.707898 | 0.736670 | 8.550000 |
| 12335 | ![]() | Hajimete no Gal | 34403 | TV | [Comedy, Romance, Ecchi] | 6.29 | 6.290573 | 0.730968 | 7.666667 |
| 11376 | ![]() | Okusama ga Seitokaichou!+! | 32603 | TV | [Comedy, Romance, Ecchi] | 6.59 | 6.585942 | 0.713720 | 8.550000 |
| 9255 | ![]() | Ore ga Ojousama Gakkou ni "Shomin Sample" Toshite Gets♥Sareta Ken | 25099 | TV | [Comedy, Romance, Ecchi] | 6.69 | 6.685905 | 0.702583 | 9.500000 |
| 13675 | ![]() | Ore ga Suki nano wa Imouto dakedo Imouto ja Nai | 36632 | TV | [Comedy, Romance, Ecchi] | 4.87 | 4.918538 | 0.692166 | 6.708333 |
| 7178 | ![]() | Boku wa Tomodachi ga Sukunai Next | 14967 | TV | [Comedy, Romance, Ecchi] | 7.29 | 7.284973 | 0.680695 | 11.400000 |
| 9845 | ![]() | Okusama ga Seitokaichou! | 28819 | TV | [Comedy, Romance, Ecchi] | 6.58 | 6.577678 | 0.678750 | 9.500000 |
| 8766 | ![]() | Seireitsukai no Blade Dance | 22877 | TV | [Action, Comedy, Fantasy, Romance, Ecchi] | 6.69 | 6.687074 | 0.677346 | 10.450000 |
| 7795 | ![]() | Nourin | 18095 | TV | [Comedy, Romance, Ecchi] | 6.75 | 6.741877 | 0.669791 | 10.450000 |
| 7149 | ![]() | Chuunibyou demo Koi ga Shitai! | 14741 | TV | [Comedy, Romance] | 7.71 | 7.706904 | 0.655911 | 12.350000 |
| 7281 | ![]() | Nekomonogatari: Kuro | 15689 | TV | [Comedy, Romance, Supernatural, Ecchi] | 7.93 | 7.921766 | 0.648867 | 12.783333 |
df = AnimeRecommender.get_recommendations_by_genres(['Action', 'Fantasy'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | age_penalty | weighted_rating | |
|---|---|---|---|---|---|
| 3961 | ![]() | Fullmetal Alchemist: Brotherhood | [Action, Adventure, Drama, Fantasy] | 11.733333 | 9.097636 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | [Action, Adventure, Fantasy] | 2.837500 | 9.048079 |
| 14865 | ![]() | Shingeki no Kyojin Season 3 Part 2 | [Action, Drama] | 5.750000 | 9.046816 |
| 9880 | ![]() | Gintama° | [Action, Comedy, Sci-Fi] | 7.875000 | 9.040355 |
| 6456 | ![]() | Hunter x Hunter (2011) | [Action, Adventure, Fantasy] | 7.000000 | 9.037173 |
| 22348 | ![]() | Shingeki no Kyojin: The Final Season - Kanketsu-hen | [Action, Drama, Suspense] | 0.000000 | 9.020218 |
| 5989 | ![]() | Gintama' | [Action, Comedy, Sci-Fi] | 11.025000 | 9.019494 |
| 7240 | ![]() | Gintama': Enchousen | [Action, Comedy, Sci-Fi] | 12.295833 | 9.000788 |
| 15525 | ![]() | Gintama: The Final | [Action, Comedy, Drama, Sci-Fi] | 3.983333 | 8.968517 |
| 12179 | ![]() | Gintama. | [Action, Comedy, Sci-Fi] | 7.600000 | 8.947376 |
| 833 | ![]() | Gintama | [Action, Comedy, Sci-Fi] | 9.500000 | 8.928261 |
| 2647 | ![]() | Code Geass: Hangyaku no Lelouch R2 | [Action, Award Winning, Drama, Sci-Fi] | 15.229167 | 8.906124 |
| 14527 | ![]() | Violet Evergarden Movie | [Award Winning, Drama, Fantasy] | 4.979167 | 8.881617 |
| 7228 | ![]() | Gintama Movie 2: Kanketsu-hen - Yorozuya yo Eien Nare | [Action, Comedy, Sci-Fi] | 11.950000 | 8.876192 |
| 14206 | ![]() | Gintama.: Shirogane no Tamashii-hen - Kouhan-sen | [Action, Comedy, Sci-Fi] | 6.591667 | 8.831842 |
| 15822 | ![]() | Shingeki no Kyojin: The Final Season | [Action, Drama] | 4.666667 | 8.796520 |
| 14219 | ![]() | Mob Psycho 100 II | [Action, Comedy, Supernatural] | 5.675000 | 8.795299 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | [Action, Fantasy] | 3.816667 | 8.794506 |
| 11052 | ![]() | Kizumonogatari III: Reiketsu-hen | [Action, Mystery, Supernatural] | 7.966667 | 8.773543 |
| 21303 | ![]() | Vinland Saga Season 2 | [Action, Adventure, Drama] | 1.800000 | 8.771356 |
| 13818 | ![]() | Gintama.: Shirogane no Tamashii-hen | [Action, Comedy, Sci-Fi] | 6.650000 | 8.767080 |
| 20972 | ![]() | Shingeki no Kyojin: The Final Season Part 2 | [Action, Drama] | 2.850000 | 8.763341 |
| 0 | ![]() | Cowboy Bebop | [Action, Award Winning, Sci-Fi] | 24.075000 | 8.745454 |
| 14226 | ![]() | Vinland Saga | [Action, Adventure, Drama] | 5.400000 | 8.734590 |
| 21680 | ![]() | Mob Psycho 100 III | [Action, Comedy, Supernatural] | 2.850000 | 8.703707 |
| 1431 | ![]() | Code Geass: Hangyaku no Lelouch | [Action, Award Winning, Drama, Sci-Fi] | 17.020833 | 8.696970 |
| 20964 | ![]() | 86 Part 2 | [Action, Drama, Sci-Fi] | 3.800000 | 8.692836 |
| 18920 | ![]() | Mushoku Tensei: Isekai Ittara Honki Dasu Part 2 | [Drama, Fantasy, Ecchi] | 3.800000 | 8.691501 |
| 16191 | ![]() | Kingdom 3rd Season | [Action] | 4.458333 | 8.691051 |
| 11 | ![]() | One Piece | [Action, Adventure, Fantasy] | 0.000000 | 8.686696 |
| 25 | ![]() | Rurouni Kenshin: Meiji Kenkaku Romantan - Tsuioku-hen | [Action, Drama, Romance] | 25.566667 | 8.679878 |
| 11800 | ![]() | Violet Evergarden | [Drama, Fantasy] | 6.620833 | 8.665611 |
| 142 | ![]() | Mononoke Hime | [Action, Adventure, Award Winning, Fantasy] | 27.883333 | 8.664782 |
| 11630 | ![]() | Fate/stay night Movie: Heaven's Feel - III. Spring Song | [Action, Fantasy, Supernatural] | 4.979167 | 8.656426 |
| 404 | ![]() | Howl no Ugoku Shiro | [Adventure, Award Winning, Drama, Fantasy, Romance] | 20.912500 | 8.655388 |
| 12435 | ![]() | Made in Abyss | [Adventure, Drama, Fantasy, Mystery, Sci-Fi] | 7.566667 | 8.654291 |
| 16236 | ![]() | Jujutsu Kaisen | [Action, Award Winning, Fantasy] | 4.500000 | 8.637287 |
| 16412 | ![]() | Made in Abyss: Retsujitsu no Ougonkyou | [Adventure, Drama, Fantasy, Mystery, Sci-Fi] | 2.850000 | 8.635083 |
| 1822 | ![]() | Tengen Toppa Gurren Lagann | [Action, Adventure, Award Winning, Sci-Fi] | 15.975000 | 8.625455 |
| 13176 | ![]() | Shingeki no Kyojin Season 3 | [Action, Drama] | 6.650000 | 8.617390 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | [Action, Fantasy] | 4.979167 | 8.615747 |
| 21734 | ![]() | Spy x Family | [Action, Award Winning, Comedy] | 2.850000 | 8.615184 |
| 13832 | ![]() | Made in Abyss Movie 3: Fukaki Tamashii no Reimei | [Adventure, Drama, Fantasy, Mystery, Sci-Fi] | 4.979167 | 8.612744 |
| 16931 | ![]() | Cyberpunk: Edgerunners | [Action, Sci-Fi] | 2.875000 | 8.600006 |
| 23341 | ![]() | JoJo no Kimyou na Bouken Part 6: Stone Ocean Part 3 | [Action, Adventure, Supernatural] | 2.825000 | 8.590229 |
| 18172 | ![]() | Chainsaw Man | [Action, Fantasy] | 2.850000 | 8.584003 |
| 3271 | ![]() | Evangelion: 3.0+1.0 Thrice Upon a Time | [Action, Award Winning, Drama, Sci-Fi, Suspense] | 3.983333 | 8.582592 |
| 14531 | ![]() | JoJo no Kimyou na Bouken Part 5: Ougon no Kaze | [Action, Adventure, Supernatural] | 5.862500 | 8.573965 |
| 6714 | ![]() | Ookami Kodomo no Ame to Yuki | [Award Winning, Fantasy, Slice of Life] | 12.945833 | 8.571330 |
| 21676 | ![]() | Kingdom 4th Season | [Action] | 2.675000 | 8.566972 |
- Install spaCy
This downloads the small English language model (en_core_web_sm) which includes:
- Vocabulary
- Syntax
- Named entities
!pip install spacy
!python -m spacy download en_core_web_sm
Requirement already satisfied: spacy in /usr/local/lib/python3.11/dist-packages (3.8.7)
Requirement already satisfied: spacy-legacy<3.1.0,>=3.0.11 in /usr/local/lib/python3.11/dist-packages (from spacy) (3.0.12)
Requirement already satisfied: spacy-loggers<2.0.0,>=1.0.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (1.0.5)
Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (1.0.13)
Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /usr/local/lib/python3.11/dist-packages (from spacy) (2.0.11)
Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /usr/local/lib/python3.11/dist-packages (from spacy) (3.0.10)
Requirement already satisfied: thinc<8.4.0,>=8.3.4 in /usr/local/lib/python3.11/dist-packages (from spacy) (8.3.6)
Requirement already satisfied: wasabi<1.2.0,>=0.9.1 in /usr/local/lib/python3.11/dist-packages (from spacy) (1.1.3)
Requirement already satisfied: srsly<3.0.0,>=2.4.3 in /usr/local/lib/python3.11/dist-packages (from spacy) (2.5.1)
Requirement already satisfied: catalogue<2.1.0,>=2.0.6 in /usr/local/lib/python3.11/dist-packages (from spacy) (2.0.10)
Requirement already satisfied: weasel<0.5.0,>=0.1.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (0.4.1)
Requirement already satisfied: typer<1.0.0,>=0.3.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (0.16.0)
Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (4.67.1)
Requirement already satisfied: numpy>=1.19.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (2.0.2)
Requirement already satisfied: requests<3.0.0,>=2.13.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (2.32.3)
Requirement already satisfied: pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4 in /usr/local/lib/python3.11/dist-packages (from spacy) (2.11.7)
Requirement already satisfied: jinja2 in /usr/local/lib/python3.11/dist-packages (from spacy) (3.1.6)
Requirement already satisfied: setuptools in /usr/local/lib/python3.11/dist-packages (from spacy) (75.2.0)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (25.0)
Requirement already satisfied: langcodes<4.0.0,>=3.2.0 in /usr/local/lib/python3.11/dist-packages (from spacy) (3.5.0)
Requirement already satisfied: language-data>=1.2 in /usr/local/lib/python3.11/dist-packages (from langcodes<4.0.0,>=3.2.0->spacy) (1.3.0)
Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.11/dist-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy) (0.7.0)
Requirement already satisfied: pydantic-core==2.33.2 in /usr/local/lib/python3.11/dist-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy) (2.33.2)
Requirement already satisfied: typing-extensions>=4.12.2 in /usr/local/lib/python3.11/dist-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy) (4.14.1)
Requirement already satisfied: typing-inspection>=0.4.0 in /usr/local/lib/python3.11/dist-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy) (0.4.1)
Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (3.4.2)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (2.4.0)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (2025.7.9)
Requirement already satisfied: blis<1.4.0,>=1.3.0 in /usr/local/lib/python3.11/dist-packages (from thinc<8.4.0,>=8.3.4->spacy) (1.3.0)
Requirement already satisfied: confection<1.0.0,>=0.0.1 in /usr/local/lib/python3.11/dist-packages (from thinc<8.4.0,>=8.3.4->spacy) (0.1.5)
Requirement already satisfied: click>=8.0.0 in /usr/local/lib/python3.11/dist-packages (from typer<1.0.0,>=0.3.0->spacy) (8.2.1)
Requirement already satisfied: shellingham>=1.3.0 in /usr/local/lib/python3.11/dist-packages (from typer<1.0.0,>=0.3.0->spacy) (1.5.4)
Requirement already satisfied: rich>=10.11.0 in /usr/local/lib/python3.11/dist-packages (from typer<1.0.0,>=0.3.0->spacy) (14.0.0)
Requirement already satisfied: cloudpathlib<1.0.0,>=0.7.0 in /usr/local/lib/python3.11/dist-packages (from weasel<0.5.0,>=0.1.0->spacy) (0.21.1)
Requirement already satisfied: smart-open<8.0.0,>=5.2.1 in /usr/local/lib/python3.11/dist-packages (from weasel<0.5.0,>=0.1.0->spacy) (7.3.0.post1)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.11/dist-packages (from jinja2->spacy) (3.0.2)
Requirement already satisfied: marisa-trie>=1.1.0 in /usr/local/lib/python3.11/dist-packages (from language-data>=1.2->langcodes<4.0.0,>=3.2.0->spacy) (1.2.1)
Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.11/dist-packages (from rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy) (3.0.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.11/dist-packages (from rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy) (2.19.2)
Requirement already satisfied: wrapt in /usr/local/lib/python3.11/dist-packages (from smart-open<8.0.0,>=5.2.1->weasel<0.5.0,>=0.1.0->spacy) (1.17.2)
Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.11/dist-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy) (0.1.2)
Collecting en-core-web-sm==3.8.0
Downloading https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl (12.8 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.8/12.8 MB 48.4 MB/s eta 0:00:00
✔ Download and installation successful
You can now load the package via spacy.load('en_core_web_sm')
⚠ Restart to reload dependencies
If you are in a Jupyter or Colab notebook, you may need to restart Python in
order to load all the package's dependencies. You can do this by selecting the
'Restart kernel' or 'Restart runtime' option.
The following AnimeRecommender class solves key limitations in the previous version by replacing the outdated NLTK pipeline with a cleaner and more efficient spaCy NLP model, improving genre and studio normalization, and introducing consistent recency scoring and age penalty handling. It also adds personalized user-based recommendations and a simpler genre-based filtering method. These changes lead to smarter, fresher, and more relevant recommendations that better reflect user preferences and anime trends.
AnimeRecommender Class — Hybrid NLP & Metadata-Based Anime Recommendation Engine¶
Overview:¶
The AnimeRecommender class implements a hybrid recommendation engine that blends NLP-enhanced content filtering, metadata-driven similarity, and user-based collaborative signals. It dynamically adjusts recommendation priorities using recency scoring and age-based penalties, ensuring results are relevant, personalized, and trend-aware.
Class Methodology & Core Components:¶
__init__()¶
- Loads
anime_dfandratings_dfdatasets. - Initializes spaCy NLP pipeline and vector encoders.
- Applies preprocessing and feature engineering.
- Sets default feature weights:
weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
_preprocess_data()¶
- Cleans numerical and string fields.
- Extracts
Release_yearfromAired. - Converts genres and studios to normalized lists.
- Calculates IMDb-style weighted rating:
$$ \text{weighted\_rating} = \left( \frac{v}{v + m} \right) R + \left( \frac{m}{v + m} \right) C $$
Where:
- $R$: anime's mean score
- $v$: number of votes (Scored By)
- $m$: 65th percentile of all vote counts
- $C$: global mean score
_create_feature_matrices()¶
Creates efficient encodings for similarity computation:
| Feature | Encoding Method |
|---|---|
Genres | MultiLabelBinarizer (sparse) |
Studios | MultiLabelBinarizer |
Type | OneHotEncoder |
Source | OneHotEncoder |
Episodes | Binned + OneHotEncoder |
Synopsis | spaCy → Lemmatized → TF-IDF |
📊 Similarity Calculation¶
_calculate_anime_similarity(idx)¶
Generates weighted similarity score:
$$ \text{similarity} = \sum w_i \cdot \text{sim}_i $$
Where $w_i$ are feature weights and $\text{sim}_i$ is cosine or binary match similarity for:
- Genres
- Synopsis
- Type
- Studios
- Episodes
- Source
Recency Scoring¶
_calculate_recency_boost(release_year)¶
Applies min-max normalization on release year:
$$ \text{recency_score} = \frac{\text{year} - \text{min\_year}}{\text{max_year} - \text{min_year}} $$
Final Scoring Formulas¶
1. Without Age Penalty (in get_anime_recommendations()):¶
$$\text{final_score} = \left[ (1 - w_s) \cdot \text{weighted_rating} + w_s \cdot \text{similarity} \right] \cdot (1 - w_r) + w_r \cdot \text{recency_score} $$
2. With Age Penalty (in get_anime_recommendations1() and 2()):¶
$$ \text{final_score} = \frac{ \left[ (1 - w_s) \cdot \text{weighted_rating} + w_s \cdot \text{similarity} \right] \cdot (1 - w_r) + w_r \cdot \text{recency_score} }{1 + \frac{\text{age_penalty}}{10}} $$
Recommendation Strategies¶
| Method | Description |
|---|---|
get_anime_recommendations | Based on title; includes similarity, rating, and recency boost. |
get_anime_recommendations1/2 | Adds age_penalty to suppress outdated content unless still airing. |
get_user_recommendations | Based on user’s top-rated anime; scores by similarity × rating × recency. |
get_recommendations_by_genres | Genre-based matching using binary or semantic overlap. |
simple() | Lightweight genre intersection without encoders. |
Design Highlights¶
| Feature | Included |
|---|---|
| Weighted metadata-based similarity (multi-feature) | ✅ |
| NLP-powered synopsis matching via spaCy + TF-IDF | ✅ |
| Recency boosting to promote newer anime | ✅ |
| Age penalty to deprioritize outdated shows | ✅ |
| IMDb-like popularity-adjusted scoring | ✅ |
| Modular functions for extensibility | ✅ |
class AnimeRecommender:
def __init__(self, anime_df, ratings_df):
self.nlp = spacy.load("en_core_web_sm")
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
self.nlp = spacy.load("en_core_web_sm")
self.current_year = datetime.now().year
self._preprocess_data()
self._create_feature_matrices()
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['Release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
def normalize_genre_list(genre_string):
if genre_string == 'UNKNOWN' or pd.isna(genre_string):
return []
genres = re.split(r',\s*', genre_string)
cleaned = [re.sub(r'[^\w\s]', '', g).strip().lower() for g in genres]
return list(set(cleaned))
self.anime_df['Genres'] = self.anime_df['Genres'].apply(normalize_genre_list)
self.anime_df['Studios'] = self.anime_df['Studios'].apply(normalize_genre_list)
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'].fillna(''))
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
def _clean_text(self, text_series: pd.Series) -> pd.Series:
return text_series.apply(self._spacy_clean)
def _spacy_clean(self, text: str) -> str:
doc = self.nlp(text.lower())
return ' '.join(
token.lemma_ for token in doc
if not token.is_stop and not token.is_punct and token.is_alpha
)
def _calculate_recency_boost(self, release_year):
year = release_year if pd.notna(release_year) else self.anime_df['Release_year'].min()
min_year = self.anime_df['Release_year'].min()
max_year = self.anime_df['Release_year'].max()
year_range = max_year - min_year if max_year != min_year else 1
scaled_year = (year - min_year) / year_range
return scaled_year
def _calculate_anime_similarity(self, idx: int):
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
num_recs_to_get = min(52, len(self.anime_df) - 1)
sorted_sim_scores = sim_scores[sim_scores[:, 1].argsort()[::-1]]
top_sim = sorted_sim_scores[1:num_recs_to_get + 1]
top_indices = top_sim[:, 0].astype(int)
sim_scores_top = top_sim[:, 1]
return sim_scores_top, top_indices
def get_user_recommendations(self, user_id, n=10, top_k=20):
user_ratings = self.ratings_df[self.ratings_df['user_id'] == user_id]
if user_ratings.empty:
return pd.DataFrame()
top_rated = (
user_ratings
.merge(self.anime_df, on='anime_id')
.nlargest(top_k, 'rating')
[['anime_id', 'rating', 'Release_year']]
)
candidate_scores = defaultdict(float)
anime_id_to_index = pd.Series(self.anime_df.index, index=self.anime_df['anime_id']).to_dict()
for _, row in top_rated.iterrows():
anime_id = row['anime_id']
if anime_id in anime_id_to_index:
anime_idx = anime_id_to_index[anime_id]
sim_scores_top, top_indices = self._calculate_anime_similarity(anime_idx)
recency = self._calculate_recency_boost(row['Release_year'])
weighted_scores = sim_scores_top * row['rating'] * recency
for i, rec_idx in enumerate(top_indices):
if rec_idx < len(self.anime_df):
candidate_scores[rec_idx] += weighted_scores[i]
watched_ids = set(user_ratings['anime_id'])
recommended_anime_indices = sorted(candidate_scores.keys(), key=lambda k: candidate_scores[k], reverse=True)
recommended_anime_indices = [idx for idx in recommended_anime_indices if self.anime_df.iloc[idx]['anime_id'] not in watched_ids]
top_n_indices = recommended_anime_indices[:n]
recommendations = (
self.anime_df
.iloc[top_n_indices]
.assign(predicted_score=lambda x: x.index.map(candidate_scores))
[['Name', 'Image URL', 'anime_id', 'Type', 'Genres', 'Score', 'predicted_score']]
)
return recommendations
def get_anime_recommendations(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year']
].copy()
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
recommended_animes['similarity_score'] = sim_scores_top
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
recommended_animes['final_score'] = (
(rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']) * (1 - recency_weight) +
recency_weight * recommended_animes['recency_score']
)
return recommended_animes.sort_values('final_score', ascending=False).head(n)
def get_anime_recommendations1(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year', 'Episodes', 'Status']
].copy()
recommended_animes['similarity_score'] = sim_scores_top
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
recommended_animes['end_year'] = recommended_animes['Release_year'].fillna(self.current_year)
recommended_animes['episodes'] = pd.to_numeric(recommended_animes['Episodes'], errors='coerce').fillna(self.median_episodes)
recommended_animes['age_penalty'] = recommended_animes.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(self.current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
recommended_animes['final_score'] = (
(rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']) * (1 - recency_weight) +
recency_weight * recommended_animes['recency_score']
) / (1 + recommended_animes['age_penalty'] / 10)
return recommended_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'final_score', 'age_penalty']
]
def get_anime_recommendations2(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year', 'Episodes', 'Status']
].copy()
recommended_animes['similarity_score'] = sim_scores_top
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
recommended_animes['end_year'] = recommended_animes['Release_year'].fillna(self.current_year)
recommended_animes['episodes'] = pd.to_numeric(recommended_animes['Episodes'], errors='coerce').fillna(self.median_episodes)
recommended_animes['age_penalty'] = recommended_animes.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(self.current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
recommended_animes['final_score'] = (
(rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']) * (1 - recency_weight) +
recency_weight * recommended_animes['recency_score']
) / (1 + recommended_animes['age_penalty'] / 10)
return recommended_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'final_score', 'age_penalty']
]
def get_recommendations_by_genres(self, genres, n=10):
if isinstance(genres, str):
genres = [genres]
valid_genres = set(self.mlb_genres.classes_)
genres = [g.strip().lower() for g in genres if g.strip().lower() in valid_genres]
if not genres:
raise ValueError("No valid genres provided.")
genre_vector = self.mlb_genres.transform([genres]).toarray()[0]
similarity_scores = (self.genres_encoded.toarray() @ genre_vector).ravel()
recommendations = self.anime_df.copy()
recommendations['similarity_score'] = similarity_scores
recommendations = recommendations[recommendations['similarity_score'] > 0]
current_year = self.current_year
recommendations['end_year'] = recommendations['Release_year'].fillna(current_year)
recommendations['episodes'] = pd.to_numeric(recommendations['Episodes'], errors='coerce').fillna(self.median_episodes)
recommendations['age_penalty'] = recommendations.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
recommendations = recommendations.sort_values(
by=['weighted_rating', 'age_penalty'], ascending=[False, True]
).head(n)
return recommendations[['Image URL', 'Name', 'Genres', 'age_penalty', 'weighted_rating']]
def get_recommendations_by_genres_simple(self, genres, n=10):
if isinstance(genres, str):
genres = [genres]
input_genres = {g.strip().lower() for g in genres}
if not input_genres:
raise ValueError("No valid genres provided.")
if not isinstance(self.anime_df['Genres'].iloc[0], set):
self.anime_df['Genres'] = self.anime_df['Genres'].apply(
lambda g_list: {g.strip().lower() for g in g_list}
)
self.anime_df['genre_similarity'] = self.anime_df['Genres'].apply(
lambda anime_genres: len(input_genres & anime_genres)
)
recommendations = self.anime_df[self.anime_df['genre_similarity'] > 0].copy()
current_year = self.current_year
recommendations['end_year'] = recommendations['Release_year'].fillna(current_year)
recommendations['episodes'] = pd.to_numeric(recommendations['Episodes'], errors='coerce').fillna(self.median_episodes)
recommendations['age_penalty'] = recommendations.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
recommendations = recommendations.sort_values(
by=['genre_similarity', 'weighted_rating', 'age_penalty'], ascending=[False, False, True]
).head(n)
return recommendations[['Image URL', 'Name', 'Genres', 'genre_similarity', 'weighted_rating', 'age_penalty']]
AnimeRecommender = AnimeRecommender(anime_df, ratings_df)
df = AnimeRecommender.get_anime_recommendations1('Darling in the FranXX', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 21948 | ![]() | Lycoris Recoil | 50709 | TV | [action] | 8.20 | 8.183950 | 1.166601 | 2.837500 |
| 20964 | ![]() | 86 Part 2 | 48569 | TV | [scifi, drama, action] | 8.71 | 8.692836 | 1.129940 | 3.800000 |
| 16608 | ![]() | 86 | 41457 | TV | [scifi, drama, action] | 8.28 | 8.270491 | 1.092477 | 3.816667 |
| 22263 | ![]() | Engage Kiss | 51417 | TV | [action, comedy, romance] | 6.84 | 6.829794 | 1.048399 | 2.837500 |
| 16309 | ![]() | SSSS.Dynazenon | 40870 | TV | [scifi, action] | 7.42 | 7.375668 | 1.017294 | 3.800000 |
| 21073 | ![]() | Cardfight!! Vanguard: overDress Season 2 | 48862 | TV | [drama, action] | 6.67 | 6.538178 | 0.945743 | 3.783333 |
| 16266 | ![]() | Hypnosis Mic: Division Rap Battle - Rhyme Anima | 40803 | TV | [scifi, action] | 6.80 | 6.764323 | 0.902413 | 4.729167 |
| 12271 | ![]() | Grancrest Senki | 34279 | TV | [drama, action, romance, fantasy] | 7.22 | 7.208601 | 0.849582 | 6.300000 |
| 13591 | ![]() | Beatless | 36516 | TV | [scifi, drama, action, romance] | 6.21 | 6.215648 | 0.776463 | 6.416667 |
| 8855 | ![]() | Shigatsu wa Kimi no Uso | 23273 | TV | [drama, romance] | 8.65 | 8.646914 | 0.771948 | 9.991667 |
| 11072 | ![]() | Kiznaiver | 31798 | TV | [scifi, drama, romance] | 7.38 | 7.374965 | 0.767270 | 8.550000 |
| 9655 | ![]() | Macross Δ | 28013 | TV | [scifi, action, romance] | 7.27 | 7.213781 | 0.763383 | 8.025000 |
| 9604 | ![]() | Plastic Memories | 27775 | TV | [scifi, drama, romance] | 7.91 | 7.904120 | 0.761342 | 9.458333 |
| 10540 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | TV | [scifi, drama, action] | 7.61 | 7.309665 | 0.724167 | 9.458333 |
| 7558 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus | 17080 | TV | [scifi, drama, action] | 7.37 | 7.177703 | 0.717187 | 9.458333 |
| 10460 | ![]() | Classroom☆Crisis | 30383 | TV | [scifi, drama, romance] | 6.97 | 6.949188 | 0.702850 | 9.458333 |
| 11157 | ![]() | Bubuki Buranki | 32023 | TV | [scifi, drama, action] | 6.25 | 6.257899 | 0.677197 | 8.550000 |
| 8424 | ![]() | Buddy Complex | 21437 | TV | [scifi, action] | 7.12 | 7.088736 | 0.665043 | 10.404167 |
| 8823 | ![]() | M3: Sono Kuroki Hagane | 23133 | TV | [scifi, drama, action, mystery] | 6.57 | 6.553907 | 0.654097 | 9.900000 |
| 7299 | ![]() | Senki Zesshou Symphogear G | 15793 | TV | [scifi, action] | 7.46 | 7.396078 | 0.650975 | 11.350000 |
| 6358 | ![]() | Guilty Crown | 10793 | TV | [scifi, drama, action] | 7.42 | 7.417078 | 0.623158 | 12.716667 |
| 6594 | ![]() | Senki Zesshou Symphogear | 11751 | TV | [scifi, action] | 7.03 | 7.004872 | 0.602205 | 12.295833 |
| 5559 | ![]() | Star Driver: Kagayaki no Takuto | 8934 | TV | [scifi, action, romance] | 7.19 | 7.159537 | 0.580298 | 13.437500 |
| 3375 | ![]() | Kidou Senshi Gundam 00 Second Season | 3927 | TV | [scifi, drama, action] | 8.08 | 8.048096 | 0.579181 | 15.229167 |
df = AnimeRecommender.get_anime_recommendations1('Darling in the FranXX', n=10)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /tmp/ipython-input-39-418889061.py in <cell line: 0>() ----> 1 df = AnimeRecommender.get_anime_recommendations1('Guilty Crown', n=10) 2 df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>') 3 4 display(HTML(df.to_html(escape=False))) TypeError: AnimeRecommender.get_anime_recommendations1() missing 1 required positional argument: 'title'
df = AnimeRecommender.get_anime_recommendations1('Fate/stay night: Unlimited Blade Works', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 23475 | ![]() | Dead Mount Death Play | 53613 | TV | [action, fantasy, supernatural] | 7.31 | 7.240476 | 1.407744 | 0.000000 |
| 20893 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou II | 48417 | TV | [action, fantasy] | 6.91 | 6.884430 | 1.332888 | 0.000000 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [action, fantasy] | 8.49 | 8.467293 | 1.329885 | 1.908333 |
| 21139 | ![]() | Mahou Shoujo Magical Destroyers | 48981 | TV | [action, fantasy] | 6.49 | 6.470741 | 1.285783 | 0.000000 |
| 24370 | ![]() | Sinbi Apateu: Zero | 55047 | TV | [fantasy, supernatural] | 6.39 | 6.387130 | 1.272720 | 0.000000 |
| 21905 | ![]() | Yu☆Gi☆Oh! Go Rush!! | 50607 | TV | [action, fantasy] | 5.70 | 6.168963 | 1.244499 | 0.000000 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [action, fantasy] | 8.59 | 8.584003 | 1.193523 | 2.850000 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [action, fantasy] | 8.80 | 8.794506 | 1.171410 | 3.816667 |
| 21790 | ![]() | Mononogatari | 50384 | TV | [action, supernatural] | 7.20 | 7.113428 | 1.144930 | 1.900000 |
| 24875 | ![]() | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.137021 | 0.000000 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [action, fantasy] | 8.39 | 8.380549 | 1.133320 | 3.883333 |
| 24201 | ![]() | Dead Mount Death Play Part 2 | 54743 | TV | [action, fantasy, supernatural] | 6.39 | 6.387130 | 1.087136 | 1.983333 |
| 23250 | ![]() | Fate/strange Fake: Whispers of Dawn | 53127 | Special | [action, fantasy, supernatural] | 6.39 | 6.387130 | 1.083347 | 1.991667 |
| 21685 | ![]() | Seiken Gakuin no Makentsukai | 50184 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.076166 | 0.000000 |
| 11630 | ![]() | Fate/stay night Movie: Heaven's Feel - III. Spring Song | 33050 | Movie | [action, fantasy, supernatural] | 8.68 | 8.656426 | 1.073761 | 4.979167 |
| 22436 | ![]() | Hyouken no Majutsushi ga Sekai wo Suberu | 51711 | TV | [action, fantasy] | 6.36 | 6.360717 | 1.066844 | 1.900000 |
| 23417 | ![]() | Boushoku no Berserk | 53439 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.061446 | 1.983333 |
| 11629 | ![]() | Fate/stay night Movie: Heaven's Feel - II. Lost Butterfly | 33049 | Movie | [action, fantasy, supernatural] | 8.50 | 8.482620 | 1.004332 | 5.975000 |
| 14613 | ![]() | Fate/Grand Order: Zettai Majuu Sensen Babylonia | 38084 | TV | [action, fantasy, supernatural] | 7.94 | 7.918371 | 0.956556 | 5.475000 |
| 15844 | ![]() | BNA | 40060 | TV | [action, fantasy] | 7.35 | 7.341161 | 0.937340 | 4.750000 |
| 14163 | ![]() | Sarazanmai | 37426 | TV | [action, fantasy, supernatural] | 7.49 | 7.449953 | 0.905291 | 5.725000 |
| 9312 | ![]() | Fate/stay night Movie: Heaven's Feel - I. Presage Flower | 25537 | Movie | [action, fantasy, supernatural] | 8.17 | 8.156913 | 0.900007 | 7.966667 |
| 9821 | ![]() | Fate/stay night: Unlimited Blade Works 2nd Season | 28701 | TV | [action, fantasy, supernatural] | 8.32 | 8.313739 | 0.878341 | 9.458333 |
| 12475 | ![]() | Fate/Apocrypha | 34662 | TV | [action, fantasy, supernatural] | 7.19 | 7.184197 | 0.869493 | 7.166667 |
df = AnimeRecommender.get_anime_recommendations1('Kimetsu no Yaiba', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [action, fantasy] | 8.49 | 8.467293 | 1.405850 | 1.908333 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | [action, fantasy] | 8.80 | 8.794506 | 1.213726 | 3.816667 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [action, fantasy] | 8.59 | 8.584003 | 1.194630 | 2.850000 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [action, fantasy] | 8.39 | 8.380549 | 1.171226 | 3.883333 |
| 24875 | ![]() | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.137021 | 0.000000 |
| 16236 | ![]() | Jujutsu Kaisen | 40748 | TV | [action, award winning, fantasy] | 8.64 | 8.637287 | 1.085981 | 4.500000 |
| 22190 | ![]() | Ragna Crimson | 51297 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.058089 | 1.983333 |
| 21741 | ![]() | Delicious Party♡Precure | 50281 | TV | [action, fantasy] | 6.96 | 6.708747 | 1.049547 | 2.437500 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | [action, fantasy] | 8.62 | 8.615747 | 1.042566 | 4.979167 |
| 17997 | ![]() | Tropical-Rouge! Precure | 44191 | TV | [action, fantasy] | 7.41 | 7.052018 | 1.021119 | 3.233333 |
| 14835 | ![]() | Toaru Kagaku no Railgun T | 38481 | TV | [scifi, action, fantasy] | 8.17 | 8.134640 | 1.019212 | 4.479167 |
| 16152 | ![]() | Healin' Good♡Precure | 40610 | TV | [action, fantasy] | 6.92 | 6.729515 | 0.927384 | 4.062500 |
| 14899 | ![]() | Star☆Twinkle Precure | 38578 | TV | [action, fantasy] | 7.53 | 7.150101 | 0.915191 | 4.775000 |
| 13644 | ![]() | Hug tto! Precure | 36593 | TV | [action, fantasy] | 7.85 | 7.459017 | 0.892823 | 5.570833 |
| 8472 | ![]() | Yu☆Gi☆Oh! Arc-V | 21639 | TV | [action, fantasy] | 6.70 | 6.683704 | 0.830336 | 5.500000 |
| 11182 | ![]() | Sousei no Onmyouji | 32105 | TV | [action, romance, fantasy] | 7.30 | 7.292304 | 0.797023 | 7.125000 |
| 11869 | ![]() | Ao no Exorcist: Kyoto Fujouou-hen | 33506 | TV | [action, fantasy] | 7.35 | 7.345707 | 0.780590 | 7.600000 |
| 11105 | ![]() | Mahoutsukai Precure! | 31884 | TV | [action, fantasy] | 7.26 | 6.999381 | 0.778346 | 7.125000 |
| 12175 | ![]() | Tales of Zestiria the Cross 2nd Season | 34086 | TV | [action, adventure, fantasy] | 7.31 | 7.288806 | 0.777519 | 7.566667 |
| 9821 | ![]() | Fate/stay night: Unlimited Blade Works 2nd Season | 28701 | TV | [action, fantasy, supernatural] | 8.32 | 8.313739 | 0.764396 | 9.458333 |
| 11605 | ![]() | Katsugeki/Touken Ranbu | 33018 | TV | [action, fantasy] | 6.73 | 6.711748 | 0.758336 | 7.566667 |
| 10527 | ![]() | Noragami Aragoto | 30503 | TV | [action, fantasy] | 8.16 | 8.156397 | 0.753569 | 9.458333 |
| 10662 | ![]() | Tales of Zestiria the Cross | 30911 | TV | [action, adventure, fantasy] | 7.24 | 7.229445 | 0.736357 | 8.550000 |
| 8617 | ![]() | Fate/stay night: Unlimited Blade Works | 22297 | TV | [action, fantasy, supernatural] | 8.19 | 8.185116 | 0.718117 | 10.450000 |
df = AnimeRecommender.get_anime_recommendations1('One Piece', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 23543 | ![]() | Hirogaru Sky! Precure | 53716 | TV | [action, fantasy] | 7.69 | 6.923455 | 1.408175 | 0.000000 |
| 21578 | ![]() | Edens Zero 2nd Season | 50002 | TV | [scifi, action, adventure, fantasy] | 7.43 | 7.164798 | 1.407991 | 0.000000 |
| 24186 | ![]() | Mahoutsukai Precure! 2 | 54717 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.214710 | 0.991667 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [action, adventure, fantasy] | 7.74 | 7.643075 | 1.182576 | 2.916667 |
| 21472 | ![]() | Fairy Tail: 100 Years Quest | 49785 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.143662 | 0.000000 |
| 24174 | ![]() | Boruto: Naruto Next Generations Part 2 | 54687 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.140456 | 0.000000 |
| 22481 | ![]() | Nanatsu no Taizai: Mokushiroku no Yonkishi | 51794 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.121166 | 1.983333 |
| 24042 | ![]() | Ishura | 54449 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.121126 | 0.000000 |
| 24175 | ![]() | Naruto (Shinsaku Anime) | 54688 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.117063 | 1.966667 |
| 22788 | ![]() | Shangri-La Frontier: Kusoge Hunter, Kamige ni Idoman to su | 52347 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.116004 | 1.983333 |
| 23754 | ![]() | Bleach: Sennen Kessen-hen - Ketsubetsu-tan | 53998 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.115509 | 1.983333 |
| 24185 | ![]() | Kibou no Chikara: Otona Precure '23 | 54716 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.098529 | 1.983333 |
| 24677 | ![]() | Naruto (2023) | 55453 | TV | [action, comedy, adventure, fantasy] | 6.39 | 6.387130 | 1.098318 | 1.983333 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [action, adventure, fantasy] | 7.04 | 6.978657 | 1.094073 | 2.850000 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [action, adventure, fantasy] | 7.64 | 7.609725 | 1.075481 | 3.816667 |
| 18914 | ![]() | Orient | 45560 | TV | [action, adventure, fantasy] | 6.60 | 6.590130 | 1.061672 | 2.850000 |
| 14699 | ![]() | One Piece Movie 14: Stampede | 38234 | Movie | [action, adventure, fantasy] | 8.22 | 8.190025 | 0.980617 | 5.975000 |
| 10577 | ![]() | Dragon Ball Super | 30694 | TV | [action, comedy, adventure, fantasy] | 7.43 | 7.426386 | 0.973232 | 5.000000 |
| 16405 | ![]() | Digimon Adventure: | 41074 | TV | [action, comedy, adventure, fantasy] | 6.42 | 6.416737 | 0.966149 | 3.604167 |
| 13291 | ![]() | Golden Kamuy | 36028 | TV | [action, adventure] | 7.87 | 7.852865 | 0.890325 | 6.650000 |
| 8558 | ![]() | Fairy Tail (2014) | 22043 | TV | [action, adventure, fantasy] | 7.65 | 7.645865 | 0.888932 | 6.325000 |
| 13409 | ![]() | One Piece: Episode of East Blue - Luffy to 4-nin no Nakama no Daibouken | 36215 | Special | [action, adventure, fantasy] | 7.88 | 7.758384 | 0.850474 | 7.966667 |
| 6018 | ![]() | Toriko | 10033 | TV | [comedy, gourmet, action, adventure, fantasy] | 7.52 | 7.481460 | 0.849102 | 7.000000 |
| 8747 | ![]() | Dragon Ball Kai (2014) | 22777 | TV | [action, comedy, adventure, fantasy] | 7.68 | 7.658538 | 0.812629 | 8.204167 |
df = AnimeRecommender.get_anime_recommendations1('Bleach: Sennen Kessen-hen', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 19600 | ![]() | Jigokuraku | 46569 | TV | [action, adventure, fantasy] | 8.26 | 8.224549 | 1.563704 | 0.000000 |
| 11 | ![]() | One Piece | 21 | TV | [action, adventure, fantasy] | 8.69 | 8.686696 | 1.539619 | 0.000000 |
| 22017 | ![]() | Saikyou Onmyouji no Isekai Tenseiki | 50932 | TV | [action, adventure, fantasy] | 7.20 | 7.181995 | 1.179488 | 1.891667 |
| 23754 | ![]() | Bleach: Sennen Kessen-hen - Ketsubetsu-tan | 53998 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.143882 | 1.983333 |
| 21472 | ![]() | Fairy Tail: 100 Years Quest | 49785 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.112952 | 0.000000 |
| 22481 | ![]() | Nanatsu no Taizai: Mokushiroku no Yonkishi | 51794 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.090871 | 1.983333 |
| 12428 | ![]() | Black Clover | 34572 | TV | [action, comedy, fantasy] | 8.14 | 8.136070 | 1.076231 | 4.000000 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [action, adventure, fantasy] | 7.04 | 6.978657 | 1.072318 | 2.850000 |
| 16630 | ![]() | Nanatsu no Taizai: Funnu no Shinpan | 41491 | TV | [action, adventure, fantasy] | 6.58 | 6.578161 | 1.002656 | 3.600000 |
| 12427 | ![]() | Boruto: Naruto Next Generations | 34566 | TV | [action, adventure, fantasy] | 6.06 | 6.061365 | 0.947646 | 4.000000 |
| 6456 | ![]() | Hunter x Hunter (2011) | 11061 | TV | [action, adventure, fantasy] | 9.04 | 9.037173 | 0.943020 | 7.000000 |
| 15440 | ![]() | Radiant 2nd Season | 39355 | TV | [action, adventure, fantasy] | 7.58 | 7.532102 | 0.929037 | 5.475000 |
| 14165 | ![]() | Tensei shitara Slime Datta Ken | 37430 | TV | [action, comedy, adventure, fantasy] | 8.14 | 8.136214 | 0.928519 | 6.300000 |
| 13260 | ![]() | Fairy Tail: Final Series | 35972 | TV | [action, adventure, fantasy] | 7.57 | 7.561180 | 0.927846 | 5.512500 |
| 16774 | ![]() | Hanyou no Yashahime: Sengoku Otogizoushi | 41911 | TV | [action, adventure, fantasy] | 6.70 | 6.684564 | 0.922382 | 4.500000 |
| 12429 | ![]() | Nanatsu no Taizai: Imashime no Fukkatsu | 34577 | TV | [action, adventure, fantasy] | 7.59 | 7.586863 | 0.904220 | 6.300000 |
| 14331 | ![]() | Overlord III | 37675 | TV | [action, adventure, fantasy] | 7.92 | 7.914924 | 0.893133 | 6.620833 |
| 13570 | ![]() | Sword Art Online: Alicization | 36474 | TV | [action, adventure, fantasy] | 7.56 | 7.556488 | 0.892171 | 6.300000 |
| 15653 | ![]() | Nanatsu no Taizai: Kamigami no Gekirin | 39701 | TV | [action, adventure, fantasy] | 6.46 | 6.459664 | 0.870117 | 5.400000 |
| 13843 | ![]() | Arifureta Shokugyou de Sekai Saikyou | 36882 | TV | [action, adventure, fantasy] | 6.70 | 6.698515 | 0.857293 | 5.675000 |
| 14409 | ![]() | Gunjou no Magmell | 37806 | TV | [action, adventure, fantasy] | 6.10 | 6.106922 | 0.830180 | 5.675000 |
| 1574 | ![]() | Naruto: Shippuuden | 1735 | TV | [action, adventure, fantasy] | 8.26 | 8.257899 | 0.829226 | 9.000000 |
| 10588 | ![]() | Shingeki no Bahamut: Virgin Soul | 30736 | TV | [action, adventure, fantasy] | 7.43 | 7.409368 | 0.827490 | 7.200000 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [action, adventure, fantasy] | 7.85 | 7.839802 | 0.813310 | 8.512500 |
df = AnimeRecommender.get_anime_recommendations1('Hunter x Hunter (2011)', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 11 | ![]() | One Piece | 21 | TV | [action, adventure, fantasy] | 8.69 | 8.686696 | 1.539138 | 0.000000 |
| 21093 | ![]() | Overlord IV | 48895 | TV | [action, adventure, fantasy] | 8.09 | 8.076119 | 1.198494 | 2.837500 |
| 22758 | ![]() | Ore dake Level Up na Ken | 52299 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.191686 | 0.991667 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [action, adventure, fantasy] | 7.74 | 7.643075 | 1.148750 | 2.916667 |
| 21472 | ![]() | Fairy Tail: 100 Years Quest | 49785 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.116855 | 0.000000 |
| 10577 | ![]() | Dragon Ball Super | 30694 | TV | [action, comedy, adventure, fantasy] | 7.43 | 7.426386 | 0.946701 | 5.000000 |
| 13260 | ![]() | Fairy Tail: Final Series | 35972 | TV | [action, adventure, fantasy] | 7.57 | 7.561180 | 0.946578 | 5.512500 |
| 12427 | ![]() | Boruto: Naruto Next Generations | 34566 | TV | [action, adventure, fantasy] | 6.06 | 6.061365 | 0.922746 | 4.000000 |
| 14331 | ![]() | Overlord III | 37675 | TV | [action, adventure, fantasy] | 7.92 | 7.914924 | 0.910894 | 6.620833 |
| 8558 | ![]() | Fairy Tail (2014) | 22043 | TV | [action, adventure, fantasy] | 7.65 | 7.645865 | 0.902034 | 6.325000 |
| 12736 | ![]() | Overlord II | 35073 | TV | [action, adventure, fantasy] | 7.76 | 7.755981 | 0.898896 | 6.620833 |
| 15653 | ![]() | Nanatsu no Taizai: Kamigami no Gekirin | 39701 | TV | [action, adventure, fantasy] | 6.46 | 6.459664 | 0.850289 | 5.400000 |
| 6018 | ![]() | Toriko | 10033 | TV | [comedy, gourmet, action, adventure, fantasy] | 7.52 | 7.481460 | 0.837125 | 7.000000 |
| 1574 | ![]() | Naruto: Shippuuden | 1735 | TV | [action, adventure, fantasy] | 8.26 | 8.257899 | 0.808763 | 9.000000 |
| 4694 | ![]() | Fairy Tail | 6702 | TV | [action, adventure, fantasy] | 7.57 | 7.567877 | 0.807406 | 8.000000 |
| 11058 | ![]() | Nejimaki Seirei Senki: Tenkyou no Alderamin | 31764 | TV | [action, adventure, fantasy] | 7.65 | 7.635808 | 0.799579 | 8.512500 |
| 16703 | ![]() | Monkateu | 41667 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 0.798462 | 6.266667 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [action, adventure, fantasy] | 7.85 | 7.839802 | 0.795099 | 8.512500 |
| 8747 | ![]() | Dragon Ball Kai (2014) | 22777 | TV | [action, comedy, adventure, fantasy] | 7.68 | 7.658538 | 0.793952 | 8.204167 |
| 10134 | ![]() | Overlord | 29803 | TV | [action, adventure, fantasy] | 7.91 | 7.907027 | 0.774153 | 9.458333 |
| 3961 | ![]() | Fullmetal Alchemist: Brotherhood | 5114 | TV | [drama, action, adventure, fantasy] | 9.10 | 9.097636 | 0.742336 | 11.733333 |
| 4416 | ![]() | Dragon Ball Kai | 6033 | TV | [action, comedy, adventure, fantasy] | 7.73 | 7.719814 | 0.740963 | 9.533333 |
| 7799 | ![]() | Magi: The Kingdom of Magic | 18115 | TV | [action, adventure, fantasy] | 8.22 | 8.212752 | 0.726797 | 10.750000 |
| 245 | ![]() | Bleach | 269 | TV | [action, adventure, fantasy] | 7.92 | 7.917476 | 0.726078 | 10.500000 |
df = AnimeRecommender.get_anime_recommendations1('Bleach', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 19600 | ![]() | Jigokuraku | 46569 | TV | [action, adventure, fantasy] | 8.26 | 8.224549 | 1.533767 | 0.000000 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | 41467 | TV | [action, adventure, fantasy] | 9.07 | 9.048079 | 1.403018 | 2.837500 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [action, adventure, fantasy] | 7.74 | 7.643075 | 1.150671 | 2.916667 |
| 23754 | ![]() | Bleach: Sennen Kessen-hen - Ketsubetsu-tan | 53998 | TV | [action, adventure, fantasy] | 6.39 | 6.387130 | 1.143882 | 1.983333 |
| 12428 | ![]() | Black Clover | 34572 | TV | [action, comedy, fantasy] | 8.14 | 8.136070 | 1.101288 | 4.000000 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [action, adventure, fantasy] | 7.04 | 6.978657 | 1.071986 | 2.850000 |
| 16630 | ![]() | Nanatsu no Taizai: Funnu no Shinpan | 41491 | TV | [action, adventure, fantasy] | 6.58 | 6.578161 | 0.976640 | 3.600000 |
| 12427 | ![]() | Boruto: Naruto Next Generations | 34566 | TV | [action, adventure, fantasy] | 6.06 | 6.061365 | 0.972418 | 4.000000 |
| 6456 | ![]() | Hunter x Hunter (2011) | 11061 | TV | [action, adventure, fantasy] | 9.04 | 9.037173 | 0.962227 | 7.000000 |
| 13260 | ![]() | Fairy Tail: Final Series | 35972 | TV | [action, adventure, fantasy] | 7.57 | 7.561180 | 0.947759 | 5.512500 |
| 10577 | ![]() | Dragon Ball Super | 30694 | TV | [action, comedy, adventure, fantasy] | 7.43 | 7.426386 | 0.946675 | 5.000000 |
| 8558 | ![]() | Fairy Tail (2014) | 22043 | TV | [action, adventure, fantasy] | 7.65 | 7.645865 | 0.903553 | 6.325000 |
| 1574 | ![]() | Naruto: Shippuuden | 1735 | TV | [action, adventure, fantasy] | 8.26 | 8.257899 | 0.843707 | 9.000000 |
| 4694 | ![]() | Fairy Tail | 6702 | TV | [action, adventure, fantasy] | 7.57 | 7.567877 | 0.809000 | 8.000000 |
| 11043 | ![]() | Magi: Sinbad no Bouken (TV) | 31741 | TV | [action, adventure, fantasy] | 7.85 | 7.839802 | 0.795354 | 8.512500 |
| 8747 | ![]() | Dragon Ball Kai (2014) | 22777 | TV | [action, comedy, adventure, fantasy] | 7.68 | 7.658538 | 0.793952 | 8.204167 |
| 11291 | ![]() | D.Gray-man Hallow | 32370 | TV | [action, adventure, fantasy] | 7.70 | 7.671143 | 0.784868 | 8.512500 |
| 14405 | ![]() | Tokyo Ghoul:re 2nd Season | 37799 | TV | [horror, action, fantasy] | 6.43 | 6.429838 | 0.783645 | 6.650000 |
| 13588 | ![]() | Tokyo Ghoul:re | 36511 | TV | [horror, action, fantasy] | 6.37 | 6.370037 | 0.779183 | 6.650000 |
| 9225 | ![]() | Akatsuki no Yona | 25013 | TV | [action, romance, adventure, fantasy] | 8.03 | 8.022766 | 0.766408 | 9.900000 |
| 3961 | ![]() | Fullmetal Alchemist: Brotherhood | 5114 | TV | [drama, action, adventure, fantasy] | 9.10 | 9.097636 | 0.743270 | 11.733333 |
| 4416 | ![]() | Dragon Ball Kai | 6033 | TV | [action, comedy, adventure, fantasy] | 7.73 | 7.719814 | 0.742782 | 9.533333 |
| 1344 | ![]() | D.Gray-man | 1482 | TV | [action, adventure, fantasy] | 8.02 | 8.009810 | 0.728096 | 10.845833 |
| 10 | ![]() | Naruto | 20 | TV | [action, adventure, fantasy] | 7.99 | 7.988501 | 0.725557 | 11.500000 |
df = AnimeRecommender.get_anime_recommendations1('Kimetsu no Yaiba: Yuukaku-hen', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | [action, fantasy] | 8.49 | 8.467293 | 1.437633 | 1.908333 |
| 20893 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou II | 48417 | TV | [action, fantasy] | 6.91 | 6.884430 | 1.383529 | 0.000000 |
| 23543 | ![]() | Hirogaru Sky! Precure | 53716 | TV | [action, fantasy] | 7.69 | 6.923455 | 1.374168 | 0.000000 |
| 21905 | ![]() | Yu☆Gi☆Oh! Go Rush!! | 50607 | TV | [action, fantasy] | 5.70 | 6.168963 | 1.277073 | 0.000000 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | [action, fantasy] | 8.39 | 8.380549 | 1.245945 | 3.883333 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | [action, fantasy] | 8.59 | 8.584003 | 1.245837 | 2.850000 |
| 21793 | ![]() | Mato Seihei no Slave | 50392 | TV | [action, ecchi, fantasy] | 6.39 | 6.387130 | 1.208660 | 0.991667 |
| 24875 | ![]() | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.208456 | 0.000000 |
| 21207 | ![]() | High Card | 49154 | TV | [action, fantasy] | 7.14 | 7.089661 | 1.169196 | 1.900000 |
| 23669 | ![]() | Ao no Exorcist (Shin Series) | 53889 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.140456 | 0.000000 |
| 21515 | ![]() | Tensei shitara Ken deshita | 49891 | TV | [action, fantasy] | 7.55 | 7.531441 | 1.122883 | 2.850000 |
| 22748 | ![]() | Nokemono-tachi no Yoru | 52274 | TV | [action, fantasy] | 6.58 | 6.550538 | 1.115787 | 1.891667 |
| 22190 | ![]() | Ragna Crimson | 51297 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.115509 | 1.983333 |
| 22042 | ![]() | Jujutsu Kaisen 2nd Season | 51009 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.115509 | 1.983333 |
| 22049 | ![]() | Helck | 51020 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.108854 | 1.983333 |
| 21685 | ![]() | Seiken Gakuin no Makentsukai | 50184 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.108668 | 0.000000 |
| 22436 | ![]() | Hyouken no Majutsushi ga Sekai wo Suberu | 51711 | TV | [action, fantasy] | 6.36 | 6.360717 | 1.095980 | 1.900000 |
| 23417 | ![]() | Boushoku no Berserk | 53439 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.090731 | 1.983333 |
| 24265 | ![]() | Kikansha no Mahou wa Tokubetsu desu | 54852 | TV | [action, fantasy] | 6.39 | 6.387130 | 1.089456 | 1.983333 |
| 21031 | ![]() | Gaikotsu Kishi-sama, Tadaima Isekai e Odekakechuu | 48760 | TV | [action, fantasy] | 7.17 | 7.159906 | 1.088318 | 2.850000 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [action, adventure, fantasy] | 7.04 | 6.978657 | 1.074215 | 2.850000 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | [action, fantasy] | 8.62 | 8.615747 | 1.070059 | 4.979167 |
| 14539 | ![]() | Kimetsu no Yaiba | 38000 | TV | [action, award winning, fantasy] | 8.50 | 8.498085 | 1.066900 | 5.350000 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [action, adventure, fantasy] | 7.64 | 7.609725 | 1.059244 | 3.816667 |
df = AnimeRecommender.get_recommendations_by_genres_simple(['horror', 'action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | genre_similarity | weighted_rating | age_penalty | |
|---|---|---|---|---|---|---|
| 23 | ![]() | Kenpuu Denki Berserk | {horror, drama, action, adventure, fantasy} | 2 | 8.548288 | 25.083333 |
| 708 | ![]() | Hellsing Ultimate | {horror, action, supernatural} | 2 | 8.342544 | 18.208333 |
| 8677 | ![]() | Kiseijuu: Sei no Kakuritsu | {scifi, horror, action} | 2 | 8.336886 | 9.900000 |
| 6671 | ![]() | Berserk: Ougon Jidai-hen III - Kourin | {horror, drama, action, adventure, fantasy} | 2 | 8.176690 | 11.950000 |
| 28 | ![]() | Akira | {horror, supernatural, action, scifi, adventure} | 2 | 8.153587 | 36.845833 |
| 14942 | ![]() | Dorohedoro | {horror, action, comedy, fantasy} | 2 | 8.048611 | 4.750000 |
| 509 | ![]() | Vampire Hunter D (2000) | {horror, romance, drama, action, scifi, fantasy} | 2 | 7.880489 | 24.895833 |
| 6670 | ![]() | Berserk: Ougon Jidai-hen II - Doldrey Kouryaku | {horror, drama, action, adventure, fantasy} | 2 | 7.850457 | 12.945833 |
| 8619 | ![]() | Tokyo Ghoul | {horror, action, fantasy} | 2 | 7.788620 | 10.450000 |
| 12774 | ![]() | Devilman: Crybaby | {horror, action, supernatural, avant garde} | 2 | 7.756334 | 6.708333 |
| 6099 | ![]() | Berserk: Ougon Jidai-hen I - Haou no Tamago | {horror, drama, action, adventure, fantasy} | 2 | 7.683875 | 12.945833 |
| 23140 | ![]() | Berserk: Ougon Jidai-hen - Memorial Edition | {horror, drama, action, adventure, fantasy} | 2 | 7.673791 | 2.837500 |
| 12419 | ![]() | Koutetsujou no Kabaneri Movie 3: Unato Kessen | {drama, action, horror, fantasy} | 2 | 7.653065 | 5.975000 |
| 3155 | ![]() | JoJo no Kimyou na Bouken: Phantom Blood | {horror, action, adventure} | 2 | 7.630798 | 17.925000 |
| 128 | ![]() | Blood+ | {horror, mystery, supernatural, drama, action} | 2 | 7.606274 | 15.833333 |
| 11751 | ![]() | Ajin Part 2 | {horror, action, mystery, supernatural} | 2 | 7.567470 | 8.512500 |
| 246 | ![]() | Hellsing | {horror, action, supernatural} | 2 | 7.484577 | 22.700000 |
| 4801 | ![]() | Hellsing: Digest for Freaks | {horror, action, supernatural} | 2 | 7.479373 | 18.920833 |
| 202 | ![]() | Elfen Lied | {horror, supernatural, drama, action, romance} | 2 | 7.477739 | 19.862500 |
| 11876 | ![]() | Koutetsujou no Kabaneri Movie 2: Moeru Inochi | {drama, action, horror, fantasy} | 2 | 7.439158 | 7.966667 |
| 11875 | ![]() | Koutetsujou no Kabaneri Movie 1: Tsudou Hikari | {drama, action, horror, fantasy} | 2 | 7.434561 | 8.962500 |
| 883 | ![]() | Change!! Getter Robo: Sekai Saigo no Hi | {scifi, horror, action, adventure} | 2 | 7.428132 | 25.537500 |
| 12182 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Kibou-hen | {horror, action, mystery} | 2 | 7.402755 | 8.962500 |
| 10960 | ![]() | Ajin | {horror, action, mystery, supernatural} | 2 | 7.393813 | 8.512500 |
| 11171 | ![]() | Gantz:O | {scifi, drama, action, horror} | 2 | 7.379877 | 8.962500 |
| 11613 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Zetsubou-hen | {horror, action, mystery} | 2 | 7.369897 | 8.587500 |
| 10642 | ![]() | Ajin Part 1: Shoudou | {horror, action, mystery, supernatural} | 2 | 7.338186 | 9.958333 |
| 10513 | ![]() | Tokyo Ghoul: "Jack" | {horror, action, fantasy} | 2 | 7.288512 | 9.958333 |
| 1320 | ![]() | Kemonozume | {horror, action, romance, supernatural} | 2 | 7.268849 | 17.970833 |
| 9798 | ![]() | Koutetsujou no Kabaneri | {drama, action, horror, fantasy} | 2 | 7.266267 | 8.550000 |
| 2961 | ![]() | Mnemosyne: Mnemosyne no Musume-tachi | {horror, supernatural, girls love, action, scifi} | 2 | 7.238974 | 16.575000 |
| 3920 | ![]() | Shikabane Hime: Kuro | {horror, action, supernatural} | 2 | 7.223877 | 15.200000 |
| 11210 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Mirai-hen | {horror, action, mystery} | 2 | 7.221937 | 8.550000 |
| 10849 | ![]() | Tokyo Ghoul: "Pinto" | {horror, action, fantasy} | 2 | 7.187825 | 9.958333 |
| 3717 | ![]() | Shikabane Hime: Aka | {horror, action, supernatural} | 2 | 7.159253 | 16.079167 |
| 4784 | ![]() | Deadman Wonderland | {scifi, horror, action, supernatural} | 2 | 7.147983 | 13.300000 |
| 6297 | ![]() | Blood-C: The Last Dark | {horror, action, supernatural} | 2 | 7.132560 | 12.945833 |
| 12206 | ![]() | Super Danganronpa 2.5: Komaeda Nagito to Sekai no Hakaimono | {horror, action, suspense, mystery} | 2 | 7.115634 | 7.966667 |
| 2459 | ![]() | Tokyo Majin Gakuen Kenpucho: Tou Dai Ni Maku | {horror, mystery, drama, action, fantasy} | 2 | 7.109664 | 17.100000 |
| 11155 | ![]() | Ajin OVA | {horror, action, mystery, supernatural} | 2 | 7.108905 | 8.925000 |
| 6461 | ![]() | Hellsing: The Dawn | {horror, action, supernatural} | 2 | 7.098920 | 13.825000 |
| 8733 | ![]() | Terra Formars: Bugs 2-hen | {scifi, horror, action} | 2 | 7.086941 | 10.908333 |
| 5207 | ![]() | Highschool of the Dead | {horror, action, ecchi, supernatural} | 2 | 7.068681 | 14.250000 |
| 2708 | ![]() | Shin Getter Robo | {scifi, horror, action, adventure} | 2 | 7.067195 | 19.862500 |
| 642 | ![]() | Kyuuketsuhime Miyu (TV) | {drama, action, horror} | 2 | 7.061079 | 24.966667 |
| 16394 | ![]() | Dorohedoro: Ma no Omake | {horror, action, comedy, fantasy} | 2 | 7.055402 | 4.875000 |
| 5804 | ![]() | Biohazard: Damnation | {scifi, horror, action} | 2 | 7.047338 | 12.945833 |
| 10643 | ![]() | Ajin Part 2: Shoutotsu | {horror, action, mystery, supernatural} | 2 | 7.029073 | 8.962500 |
| 1696 | ![]() | Tokyo Majin Gakuen Kenpucho: Tou | {horror, supernatural, drama, action, fantasy} | 2 | 7.028130 | 16.950000 |
| 10644 | ![]() | Ajin Part 3: Shougeki | {horror, action, mystery, supernatural} | 2 | 7.026792 | 8.962500 |
df = AnimeRecommender.get_anime_recommendations1('Chainsaw Man', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 19600 | ![]() | Jigokuraku | 46569 | TV | {action, adventure, fantasy} | 8.26 | 8.224549 | 1.560568 | 0.000000 |
| 22709 | ![]() | Mashle | 52211 | TV | {action, comedy, fantasy} | 7.59 | 7.553219 | 1.449163 | 0.000000 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | {action, fantasy} | 8.49 | 8.467293 | 1.336205 | 1.908333 |
| 21139 | ![]() | Mahou Shoujo Magical Destroyers | 48981 | TV | {action, fantasy} | 6.49 | 6.470741 | 1.317749 | 0.000000 |
| 20972 | ![]() | Shingeki no Kyojin: The Final Season Part 2 | 48583 | TV | {drama, action} | 8.77 | 8.763341 | 1.238309 | 2.850000 |
| 21403 | ![]() | Chiyu Mahou no Machigatta Tsukaikata: Senjou wo Kakeru Kaifuku Youin | 49613 | TV | {action, fantasy} | 6.39 | 6.387130 | 1.191462 | 0.991667 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | {action, fantasy} | 8.80 | 8.794506 | 1.175615 | 3.816667 |
| 22042 | ![]() | Jujutsu Kaisen 2nd Season | 51009 | TV | {action, fantasy} | 6.39 | 6.387130 | 1.172255 | 1.983333 |
| 21207 | ![]() | High Card | 49154 | TV | {action, fantasy} | 7.14 | 7.089661 | 1.170209 | 1.900000 |
| 24875 | ![]() | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | {action, fantasy} | 6.39 | 6.387130 | 1.140456 | 0.000000 |
| 23669 | ![]() | Ao no Exorcist (Shin Series) | 53889 | TV | {action, fantasy} | 6.39 | 6.387130 | 1.140456 | 0.000000 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | {action, fantasy} | 8.39 | 8.380549 | 1.134483 | 3.883333 |
| 21515 | ![]() | Tensei shitara Ken deshita | 49891 | TV | {action, fantasy} | 7.55 | 7.531441 | 1.125021 | 2.850000 |
| 22190 | ![]() | Ragna Crimson | 51297 | TV | {action, fantasy} | 6.39 | 6.387130 | 1.120044 | 1.983333 |
| 22748 | ![]() | Nokemono-tachi no Yoru | 52274 | TV | {action, fantasy} | 6.58 | 6.550538 | 1.117246 | 1.891667 |
| 23663 | ![]() | Arknights: Fuyukomori Kaerimichi | 53881 | TV | {action, fantasy} | 6.39 | 6.387130 | 1.110861 | 0.000000 |
| 16236 | ![]() | Jujutsu Kaisen | 40748 | TV | {action, award winning, fantasy} | 8.64 | 8.637287 | 1.104581 | 4.500000 |
| 22436 | ![]() | Hyouken no Majutsushi ga Sekai wo Suberu | 51711 | TV | {action, fantasy} | 6.36 | 6.360717 | 1.096955 | 1.900000 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | {action, adventure, fantasy} | 7.64 | 7.609725 | 1.093722 | 3.816667 |
| 22481 | ![]() | Nanatsu no Taizai: Mokushiroku no Yonkishi | 51794 | TV | {action, adventure, fantasy} | 6.39 | 6.387130 | 1.093422 | 1.983333 |
| 21031 | ![]() | Gaikotsu Kishi-sama, Tadaima Isekai e Odekakechuu | 48760 | TV | {action, fantasy} | 7.17 | 7.159906 | 1.087822 | 2.850000 |
| 22069 | ![]() | Kuro no Shoukanshi | 51064 | TV | {action, fantasy} | 7.10 | 7.087070 | 1.086927 | 2.850000 |
| 21696 | ![]() | Arknights: Reimei Zensou | 50205 | TV | {action, fantasy} | 7.13 | 7.087613 | 1.076639 | 2.900000 |
| 14942 | ![]() | Dorohedoro | 38668 | TV | {horror, action, comedy, fantasy} | 8.06 | 8.048611 | 1.047240 | 4.750000 |
df = AnimeRecommender.get_anime_recommendations2('Tensei shitara Slime Datta Ken', n=24)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 19600 | ![]() | Jigokuraku | 46569 | TV | [fantasy, action, adventure] | 8.26 | 8.224549 | 1.538809 | 0.000000 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | 41467 | TV | [fantasy, action, adventure] | 9.07 | 9.048079 | 1.269965 | 2.837500 |
| 15568 | ![]() | Tensei shitara Slime Datta Ken 2nd Season | 39551 | TV | [fantasy, action, adventure, comedy] | 8.39 | 8.382957 | 1.197764 | 3.800000 |
| 23458 | ![]() | Tensei shitara Slime Datta Ken 3rd Season | 53580 | TV | [fantasy, action, adventure, comedy] | 6.39 | 6.387130 | 1.196618 | 0.991667 |
| 16626 | ![]() | Tensei shitara Slime Datta Ken 2nd Season Part 2 | 41487 | TV | [fantasy, action, adventure, comedy] | 8.33 | 8.321181 | 1.184758 | 3.800000 |
| 21510 | ![]() | Tensei shitara Slime Datta Ken Movie: Guren no Kizuna-hen | 49877 | Movie | [fantasy, action, adventure, comedy] | 7.63 | 7.587295 | 1.107159 | 2.987500 |
| 24677 | ![]() | Naruto (2023) | 55453 | TV | [fantasy, action, adventure, comedy] | 6.39 | 6.387130 | 1.092246 | 1.983333 |
| 21472 | ![]() | Fairy Tail: 100 Years Quest | 49785 | TV | [fantasy, action, adventure] | 6.39 | 6.387130 | 1.092210 | 0.000000 |
| 24116 | ![]() | Tensei shitara Slime Datta Ken: Coleus no Yume | 54565 | OVA | [fantasy, action, adventure, comedy] | 6.39 | 6.387130 | 1.077132 | 1.975000 |
| 16627 | ![]() | Tensura Nikki: Tensei shitara Slime Datta Ken | 41488 | TV | [fantasy, comedy] | 7.59 | 7.574482 | 1.075618 | 3.800000 |
| 14524 | ![]() | Kumo desu ga, Nani ka? | 37984 | TV | [fantasy, comedy, action, adventure, mystery] | 7.45 | 7.441162 | 1.047976 | 3.600000 |
| 22052 | ![]() | Duel Masters King Max | 51037 | TV | [fantasy, action, adventure, comedy] | 6.39 | 6.387130 | 1.019977 | 2.787500 |
| 16630 | ![]() | Nanatsu no Taizai: Funnu no Shinpan | 41491 | TV | [fantasy, action, adventure] | 6.58 | 6.578161 | 0.982486 | 3.600000 |
| 22003 | ![]() | Hoshi no Samidare | 50891 | TV | [action, adventure, comedy, drama] | 5.64 | 5.721517 | 0.955434 | 2.700000 |
| 10577 | ![]() | Dragon Ball Super | 30694 | TV | [fantasy, action, adventure, comedy] | 7.43 | 7.426386 | 0.946056 | 5.000000 |
| 13221 | ![]() | Lupin III: Part 5 | 35857 | TV | [action, adventure, mystery, comedy] | 8.13 | 7.955901 | 0.902684 | 6.300000 |
| 15029 | ![]() | Tensei shitara Slime Datta Ken OVA | 38793 | OVA | [fantasy, action, adventure, comedy] | 7.49 | 7.472148 | 0.895563 | 5.875000 |
| 12429 | ![]() | Nanatsu no Taizai: Imashime no Fukkatsu | 34577 | TV | [fantasy, action, adventure] | 7.59 | 7.586863 | 0.892227 | 6.300000 |
| 14800 | ![]() | Arad: Gyakuten no Wa | 38416 | TV | [fantasy, action, adventure, comedy] | 5.82 | 6.337777 | 0.878517 | 4.729167 |
| 11801 | ![]() | Cardcaptor Sakura: Clear Card-hen | 33354 | TV | [romance, adventure, fantasy, comedy] | 7.65 | 7.600925 | 0.871620 | 6.358333 |
| 13124 | ![]() | Yama no Susume Third Season | 35672 | TV | [adventure, slice of life, comedy] | 7.59 | 7.401887 | 0.863175 | 6.620833 |
| 15653 | ![]() | Nanatsu no Taizai: Kamigami no Gekirin | 39701 | TV | [fantasy, action, adventure] | 6.46 | 6.459664 | 0.856502 | 5.400000 |
| 12531 | ![]() | Mahoujin Guruguru (2017) | 34745 | TV | [adventure, fantasy, comedy] | 7.81 | 7.597635 | 0.845437 | 7.200000 |
| 14409 | ![]() | Gunjou no Magmell | 37806 | TV | [fantasy, action, adventure] | 6.10 | 6.106922 | 0.810942 | 5.675000 |
df['Name']
| Name | |
|---|---|
| 19600 | Jigokuraku |
| 16617 | Bleach: Sennen Kessen-hen |
| 15568 | Tensei shitara Slime Datta Ken 2nd Season |
| 23458 | Tensei shitara Slime Datta Ken 3rd Season |
| 16626 | Tensei shitara Slime Datta Ken 2nd Season Part 2 |
| 21510 | Tensei shitara Slime Datta Ken Movie: Guren no... |
| 24677 | Naruto (2023) |
| 21472 | Fairy Tail: 100 Years Quest |
| 24116 | Tensei shitara Slime Datta Ken: Coleus no Yume |
| 16627 | Tensura Nikki: Tensei shitara Slime Datta Ken |
| 14524 | Kumo desu ga, Nani ka? |
| 22052 | Duel Masters King Max |
| 16630 | Nanatsu no Taizai: Funnu no Shinpan |
| 22003 | Hoshi no Samidare |
| 10577 | Dragon Ball Super |
| 13221 | Lupin III: Part 5 |
| 15029 | Tensei shitara Slime Datta Ken OVA |
| 12429 | Nanatsu no Taizai: Imashime no Fukkatsu |
| 14800 | Arad: Gyakuten no Wa |
| 11801 | Cardcaptor Sakura: Clear Card-hen |
| 13124 | Yama no Susume Third Season |
| 15653 | Nanatsu no Taizai: Kamigami no Gekirin |
| 12531 | Mahoujin Guruguru (2017) |
| 14409 | Gunjou no Magmell |
The AnimeRecommender1 class builds upon the earlier AnimeRecommender implementation by refining the genre-based recommendation logic and improving the robustness of set operations. Previously, genre filtering was prone to inconsistencies due to format mismatches or missing preprocessing. This updated version resolves that by dynamically converting genre lists into standardized sets, ensuring accurate genre matching regardless of input format. Additionally, the revised logic supports full subset matching using issubset() to only recommend anime that include all user-specified genres. This enhancement makes genre filtering more precise and eliminates false matches—effectively solving a subtle reliability issue in the earlier system.
Genre-Based Recommendation Methodology (Improved Genre Subset Matching)¶
The enhanced genre-based recommendation method implemented in the AnimeRecommender1 class adopts a rigorous set-theoretic approach to ensure precise alignment between user-specified genre preferences and the genre composition of candidate anime titles. Unlike prior implementations that relied on partial overlaps or genre count similarity, the improved method enforces a subset constraint, thereby ensuring that only anime whose genre sets fully contain all requested genres are considered for recommendation.
1. Genre Input Normalization¶
User-provided genre queries are first preprocessed into standardized, lowercase string sets:
input_genres = {g.strip().lower() for g in genres}
This step mitigates inconsistencies arising from formatting variations and capitalization in the genre metadata.
2. Genre Representation Alignment¶
Each anime's Genres list is dynamically converted into a Python set to enable direct subset comparisons:
genre_sets = self.anime_df['Genres'].apply(lambda g_list: {g.strip().lower() for g in g_list})
This ensures that genre comparisons are type-consistent, precise, and scalable across large datasets.
3. Subset Filtering¶
A boolean mask is computed for each anime entry, evaluating whether the user-specified genres are a subset of the anime's genres:
self.anime_df['has_all_genres'] = genre_sets.apply(lambda anime_genres: input_genres.issubset(anime_genres))
Only anime entries where this condition holds true are retained for further ranking.
4. Temporal Adjustment via Age Penalty¶
To discourage overly outdated or long-running anime, an age penalty function is applied:
$$ \text{age_penalty} = \begin{cases} 0 & \text{if status is 'Currently Airing'} \\ (\text{current_year} - \text{end_year}) \cdot \left(1 - 0.5 \cdot \min\left(\frac{\text{episodes}}{120}, 1\right)\right) & \text{otherwise} \end{cases} $$
This formula penalizes older anime while accounting for their length, with shorter series receiving reduced penalties.
5. Ranking and Recommendation Output¶
The final list is sorted based on:
- Weighted rating (IMDb-style formula integrating rating value and popularity).
- Age penalty, in ascending order.
Only the top-N anime entries that meet the genre subset condition and rank highest after age adjustment are returned as recommendations.
Comparative Advantage¶
This revised methodology improves upon earlier implementations by enforcing stricter semantic coherence between user intent and system output. It avoids the inclusion of marginally related anime and provides a more targeted and trustworthy recommendation experience, particularly for users with specific genre preferences.
class AnimeRecommender1:
def __init__(self, anime_df, ratings_df):
self.nlp = spacy.load("en_core_web_sm")
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
self.nlp = spacy.load("en_core_web_sm")
self.current_year = datetime.now().year
self._preprocess_data()
self._create_feature_matrices()
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['Release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
def normalize_genre_list(genre_string):
if genre_string == 'UNKNOWN' or pd.isna(genre_string):
return []
genres = re.split(r',\s*', genre_string)
cleaned = [re.sub(r'[^\w\s]', '', g).strip().lower() for g in genres]
return list(set(cleaned))
self.anime_df['Genres'] = self.anime_df['Genres'].apply(normalize_genre_list)
self.anime_df['Studios'] = self.anime_df['Studios'].apply(normalize_genre_list)
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'].fillna(''))
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
def _clean_text(self, text_series: pd.Series) -> pd.Series:
return text_series.apply(self._spacy_clean)
def _spacy_clean(self, text: str) -> str:
doc = self.nlp(text.lower())
return ' '.join(
token.lemma_ for token in doc
if not token.is_stop and not token.is_punct and token.is_alpha
)
def _calculate_recency_boost(self, release_year):
year = release_year if pd.notna(release_year) else self.anime_df['Release_year'].min()
min_year = self.anime_df['Release_year'].min()
max_year = self.anime_df['Release_year'].max()
year_range = max_year - min_year if max_year != min_year else 1
scaled_year = (year - min_year) / year_range
return scaled_year
def _calculate_anime_similarity(self, idx: int):
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
num_recs_to_get = min(52, len(self.anime_df) - 1)
sorted_sim_scores = sim_scores[sim_scores[:, 1].argsort()[::-1]]
top_sim = sorted_sim_scores[1:num_recs_to_get + 1]
top_indices = top_sim[:, 0].astype(int)
sim_scores_top = top_sim[:, 1]
return sim_scores_top, top_indices
def get_user_recommendations(self, user_id, n=10, top_k=20):
user_ratings = self.ratings_df[self.ratings_df['user_id'] == user_id]
if user_ratings.empty:
return pd.DataFrame()
top_rated = (
user_ratings
.merge(self.anime_df, on='anime_id')
.nlargest(top_k, 'rating')
[['anime_id', 'rating', 'Release_year']]
)
candidate_scores = defaultdict(float)
anime_id_to_index = pd.Series(self.anime_df.index, index=self.anime_df['anime_id']).to_dict()
for _, row in top_rated.iterrows():
anime_id = row['anime_id']
if anime_id in anime_id_to_index:
anime_idx = anime_id_to_index[anime_id]
sim_scores_top, top_indices = self._calculate_anime_similarity(anime_idx)
recency = self._calculate_recency_boost(row['Release_year'])
weighted_scores = sim_scores_top * row['rating'] * recency
for i, rec_idx in enumerate(top_indices):
if rec_idx < len(self.anime_df):
candidate_scores[rec_idx] += weighted_scores[i]
watched_ids = set(user_ratings['anime_id'])
recommended_anime_indices = sorted(candidate_scores.keys(), key=lambda k: candidate_scores[k], reverse=True)
recommended_anime_indices = [idx for idx in recommended_anime_indices if self.anime_df.iloc[idx]['anime_id'] not in watched_ids]
top_n_indices = recommended_anime_indices[:n]
recommendations = (
self.anime_df
.iloc[top_n_indices]
.assign(predicted_score=lambda x: x.index.map(candidate_scores))
[['Name', 'Image URL', 'anime_id', 'Type', 'Genres', 'Score', 'predicted_score']]
)
return recommendations
def get_anime_recommendations(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year']
].copy()
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
recommended_animes['similarity_score'] = sim_scores_top
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
recommended_animes['final_score'] = (
(rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']) * (1 - recency_weight) +
recency_weight * recommended_animes['recency_score']
)
return recommended_animes.sort_values('final_score', ascending=False).head(n)
def get_anime_recommendations1(self, title, n=10, similarity_weight=0.85, recency_weight=0.2):
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year', 'Episodes', 'Status']
].copy()
recommended_animes['similarity_score'] = sim_scores_top
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
recommended_animes['end_year'] = recommended_animes['Release_year'].fillna(self.current_year)
recommended_animes['episodes'] = pd.to_numeric(recommended_animes['Episodes'], errors='coerce').fillna(self.median_episodes)
recommended_animes['age_penalty'] = recommended_animes.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(self.current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
recommended_animes['final_score'] = (
(rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']) * (1 - recency_weight) +
recency_weight * recommended_animes['recency_score']
) / (1 + recommended_animes['age_penalty'] / 10)
return recommended_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'final_score', 'age_penalty']
]
def get_recommendations_by_genres_simple(self, genres, n=10):
if isinstance(genres, str):
genres = [genres]
input_genres = {g.strip().lower() for g in genres}
if not input_genres:
raise ValueError("No valid genres provided.")
# Always safely convert genre lists to sets on-the-fly
genre_sets = self.anime_df['Genres'].apply(lambda g_list: {g.strip().lower() for g in g_list})
self.anime_df['has_all_genres'] = genre_sets.apply(
lambda anime_genres: input_genres.issubset(anime_genres)
)
recommendations = self.anime_df[self.anime_df['has_all_genres']].copy()
current_year = self.current_year
recommendations['end_year'] = recommendations['Release_year'].fillna(current_year)
recommendations['episodes'] = pd.to_numeric(recommendations['Episodes'], errors='coerce').fillna(self.median_episodes)
recommendations['age_penalty'] = recommendations.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
recommendations = recommendations.sort_values(
by=['weighted_rating', 'age_penalty'], ascending=[False, True]
).head(n)
return recommendations[['Image URL', 'Name', 'Genres', 'weighted_rating', 'age_penalty']]
Lets make an instance and do some tests...¶
AnimeRecommender1 = AnimeRecommender1(anime_df, ratings_df)
df = AnimeRecommender1.get_recommendations_by_genres_simple(['horror', 'action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | |
|---|---|---|---|---|---|
| 23 | ![]() | Kenpuu Denki Berserk | [horror, drama, action, adventure, fantasy] | 8.548288 | 25.083333 |
| 708 | ![]() | Hellsing Ultimate | [horror, action, supernatural] | 8.342544 | 18.208333 |
| 8677 | ![]() | Kiseijuu: Sei no Kakuritsu | [scifi, horror, action] | 8.336886 | 9.900000 |
| 6671 | ![]() | Berserk: Ougon Jidai-hen III - Kourin | [horror, drama, action, adventure, fantasy] | 8.176690 | 11.950000 |
| 28 | ![]() | Akira | [horror, supernatural, action, scifi, adventure] | 8.153587 | 36.845833 |
| 14942 | ![]() | Dorohedoro | [horror, action, comedy, fantasy] | 8.048611 | 4.750000 |
| 509 | ![]() | Vampire Hunter D (2000) | [horror, drama, action, scifi, fantasy, romance] | 7.880489 | 24.895833 |
| 6670 | ![]() | Berserk: Ougon Jidai-hen II - Doldrey Kouryaku | [horror, drama, action, adventure, fantasy] | 7.850457 | 12.945833 |
| 8619 | ![]() | Tokyo Ghoul | [horror, action, fantasy] | 7.788620 | 10.450000 |
| 12774 | ![]() | Devilman: Crybaby | [horror, action, supernatural, avant garde] | 7.756334 | 6.708333 |
| 6099 | ![]() | Berserk: Ougon Jidai-hen I - Haou no Tamago | [horror, drama, action, adventure, fantasy] | 7.683875 | 12.945833 |
| 23140 | ![]() | Berserk: Ougon Jidai-hen - Memorial Edition | [horror, drama, action, adventure, fantasy] | 7.673791 | 2.837500 |
| 12419 | ![]() | Koutetsujou no Kabaneri Movie 3: Unato Kessen | [drama, action, horror, fantasy] | 7.653065 | 5.975000 |
| 3155 | ![]() | JoJo no Kimyou na Bouken: Phantom Blood | [horror, action, adventure] | 7.630798 | 17.925000 |
| 128 | ![]() | Blood+ | [horror, mystery, supernatural, drama, action] | 7.606274 | 15.833333 |
| 11751 | ![]() | Ajin Part 2 | [horror, action, mystery, supernatural] | 7.567470 | 8.512500 |
| 246 | ![]() | Hellsing | [horror, action, supernatural] | 7.484577 | 22.700000 |
| 4801 | ![]() | Hellsing: Digest for Freaks | [horror, action, supernatural] | 7.479373 | 18.920833 |
| 202 | ![]() | Elfen Lied | [horror, supernatural, drama, action, romance] | 7.477739 | 19.862500 |
| 11876 | ![]() | Koutetsujou no Kabaneri Movie 2: Moeru Inochi | [drama, action, horror, fantasy] | 7.439158 | 7.966667 |
| 11875 | ![]() | Koutetsujou no Kabaneri Movie 1: Tsudou Hikari | [drama, action, horror, fantasy] | 7.434561 | 8.962500 |
| 883 | ![]() | Change!! Getter Robo: Sekai Saigo no Hi | [scifi, horror, action, adventure] | 7.428132 | 25.537500 |
| 12182 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Kibou-hen | [horror, action, mystery] | 7.402755 | 8.962500 |
| 10960 | ![]() | Ajin | [horror, action, mystery, supernatural] | 7.393813 | 8.512500 |
| 11171 | ![]() | Gantz:O | [scifi, drama, action, horror] | 7.379877 | 8.962500 |
| 11613 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Zetsubou-hen | [horror, action, mystery] | 7.369897 | 8.587500 |
| 10642 | ![]() | Ajin Part 1: Shoudou | [horror, action, mystery, supernatural] | 7.338186 | 9.958333 |
| 10513 | ![]() | Tokyo Ghoul: "Jack" | [horror, action, fantasy] | 7.288512 | 9.958333 |
| 1320 | ![]() | Kemonozume | [horror, action, romance, supernatural] | 7.268849 | 17.970833 |
| 9798 | ![]() | Koutetsujou no Kabaneri | [drama, action, horror, fantasy] | 7.266267 | 8.550000 |
| 2961 | ![]() | Mnemosyne: Mnemosyne no Musume-tachi | [horror, supernatural, girls love, action, scifi] | 7.238974 | 16.575000 |
| 3920 | ![]() | Shikabane Hime: Kuro | [horror, action, supernatural] | 7.223877 | 15.200000 |
| 11210 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Mirai-hen | [horror, action, mystery] | 7.221937 | 8.550000 |
| 10849 | ![]() | Tokyo Ghoul: "Pinto" | [horror, action, fantasy] | 7.187825 | 9.958333 |
| 3717 | ![]() | Shikabane Hime: Aka | [horror, action, supernatural] | 7.159253 | 16.079167 |
| 4784 | ![]() | Deadman Wonderland | [scifi, horror, action, supernatural] | 7.147983 | 13.300000 |
| 6297 | ![]() | Blood-C: The Last Dark | [horror, action, supernatural] | 7.132560 | 12.945833 |
| 12206 | ![]() | Super Danganronpa 2.5: Komaeda Nagito to Sekai no Hakaimono | [horror, action, suspense, mystery] | 7.115634 | 7.966667 |
| 2459 | ![]() | Tokyo Majin Gakuen Kenpucho: Tou Dai Ni Maku | [horror, mystery, drama, action, fantasy] | 7.109664 | 17.100000 |
| 11155 | ![]() | Ajin OVA | [horror, action, mystery, supernatural] | 7.108905 | 8.925000 |
| 6461 | ![]() | Hellsing: The Dawn | [horror, action, supernatural] | 7.098920 | 13.825000 |
| 8733 | ![]() | Terra Formars: Bugs 2-hen | [scifi, horror, action] | 7.086941 | 10.908333 |
| 5207 | ![]() | Highschool of the Dead | [horror, action, ecchi, supernatural] | 7.068681 | 14.250000 |
| 2708 | ![]() | Shin Getter Robo | [scifi, horror, action, adventure] | 7.067195 | 19.862500 |
| 642 | ![]() | Kyuuketsuhime Miyu (TV) | [drama, action, horror] | 7.061079 | 24.966667 |
| 16394 | ![]() | Dorohedoro: Ma no Omake | [horror, action, comedy, fantasy] | 7.055402 | 4.875000 |
| 5804 | ![]() | Biohazard: Damnation | [scifi, horror, action] | 7.047338 | 12.945833 |
| 10643 | ![]() | Ajin Part 2: Shoutotsu | [horror, action, mystery, supernatural] | 7.029073 | 8.962500 |
| 1696 | ![]() | Tokyo Majin Gakuen Kenpucho: Tou | [horror, supernatural, drama, action, fantasy] | 7.028130 | 16.950000 |
| 10644 | ![]() | Ajin Part 3: Shougeki | [horror, action, mystery, supernatural] | 7.026792 | 8.962500 |
df = AnimeRecommender1.get_recommendations_by_genres_simple([ 'scifi','action','romance',] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | |
|---|---|---|---|---|---|
| 509 | ![]() | Vampire Hunter D (2000) | [horror, drama, action, scifi, fantasy, romance] | 7.880489 | 24.895833 |
| 3131 | ![]() | Macross F | [scifi, action, award winning, romance] | 7.850851 | 15.229167 |
| 988 | ![]() | Macross | [scifi, action, romance] | 7.828247 | 36.550000 |
| 989 | ![]() | Macross: Do You Remember Love? | [scifi, action, romance] | 7.823890 | 40.829167 |
| 4922 | ![]() | Macross F Movie 2: Sayonara no Tsubasa | [scifi, action, award winning, romance] | 7.758669 | 13.941667 |
| 16612 | ![]() | Date A Live IV | [scifi, action, romance] | 7.746146 | 2.850000 |
| 72 | ![]() | Kidou Senshi Gundam SEED | [award winning, drama, action, scifi, romance] | 7.724560 | 18.208333 |
| 4074 | ![]() | Macross F Movie 1: Itsuwari no Utahime | [scifi, action, romance] | 7.646773 | 15.933333 |
| 373 | ![]() | Seikai no Senki II | [scifi, action, romance] | 7.612668 | 23.000000 |
| 1181 | ![]() | Urusei Yatsura | [comedy, drama, action, scifi, adventure, romance] | 7.608737 | 22.000000 |
| 74 | ![]() | Turn A Gundam | [award winning, drama, action, scifi, adventure, romance] | 7.606676 | 20.583333 |
| 1752 | ![]() | Urusei Yatsura 2: Beautiful Dreamer | [comedy, drama, action, scifi, adventure, romance] | 7.585621 | 40.829167 |
| 372 | ![]() | Seikai no Senki | [scifi, action, romance] | 7.528374 | 23.645833 |
| 266 | ![]() | Seikai no Monshou | [scifi, action, romance] | 7.528017 | 24.591667 |
| 1104 | ![]() | Macross Plus Movie Edition | [scifi, action, adventure, romance] | 7.462419 | 29.875000 |
| 374 | ![]() | Seikai no Senki III | [scifi, action, romance] | 7.440590 | 19.833333 |
| 8305 | ![]() | Mahouka Koukou no Rettousei | [scifi, action, romance, fantasy] | 7.406821 | 9.808333 |
| 195 | ![]() | Kidou Senkan Nadesico | [scifi, action, romance] | 7.397964 | 25.858333 |
| 143 | ![]() | RahXephon | [award winning, mystery, drama, action, scifi, romance] | 7.349977 | 20.508333 |
| 9140 | ![]() | Date A Live Movie: Mayuri Judgment | [scifi, action, romance] | 7.315063 | 9.958333 |
| 911 | ![]() | Tenchi Muyou! in Love | [scifi, action, comedy, romance] | 7.278265 | 28.879167 |
| 16090 | ![]() | Mahouka Koukou no Rettousei: Raihousha-hen | [scifi, action, romance, fantasy] | 7.269632 | 4.729167 |
| 538 | ![]() | Saber Marionette J | [comedy, drama, action, scifi, adventure, romance] | 7.260175 | 25.979167 |
| 6597 | ![]() | Accel World | [scifi, action, romance] | 7.226102 | 11.700000 |
| 92 | ![]() | Uchuu no Stellvia | [scifi, action, romance] | 7.215704 | 19.616667 |
| 9655 | ![]() | Macross Δ | [scifi, action, romance] | 7.213781 | 8.025000 |
| 13216 | ![]() | Darling in the FranXX | [scifi, drama, action, romance] | 7.208504 | 6.300000 |
| 1325 | ![]() | Uchuu no Kishi Tekkaman Blade | [drama, action, scifi, adventure, romance] | 7.190315 | 26.262500 |
| 8000 | ![]() | Date A Live II | [scifi, action, romance] | 7.186278 | 10.541667 |
| 13676 | ![]() | Date A Live III | [scifi, action, romance] | 7.182833 | 5.700000 |
| 793 | ![]() | Zegapain | [scifi, action, romance] | 7.163313 | 16.941667 |
| 5559 | ![]() | Star Driver: Kagayaki no Takuto | [scifi, action, romance] | 7.159537 | 13.437500 |
| 73 | ![]() | Kidou Senshi Gundam SEED Destiny | [scifi, drama, action, romance] | 7.158412 | 16.625000 |
| 7047 | ![]() | Accel World EX | [scifi, action, romance] | 7.079104 | 12.891667 |
| 1266 | ![]() | Macross 7 | [comedy, drama, action, scifi, adventure, romance] | 7.076136 | 24.670833 |
| 6198 | ![]() | Aquarion Evol | [scifi, action, romance, fantasy] | 7.053481 | 11.591667 |
| 449 | ![]() | Sousei no Aquarion | [scifi, action, romance, fantasy] | 7.052144 | 17.833333 |
| 3078 | ![]() | Genius Party | [action, avant garde, scifi, fantasy, romance] | 7.047031 | 17.475000 |
| 1755 | ![]() | Urusei Yatsura Movie 5: Kanketsu-hen | [comedy, drama, action, scifi, adventure, romance] | 7.042509 | 36.845833 |
| 1086 | ![]() | Saber Marionette J Again | [scifi, action, comedy, romance] | 7.031205 | 27.300000 |
| 2100 | ![]() | Kidou Senshi Gundam SEED Destiny Special Edition | [scifi, drama, action, romance] | 7.001912 | 18.683333 |
| 986 | ![]() | Kenran Butou Sai: The Mars Daybreak | [comedy, action, scifi, adventure, romance] | 6.999471 | 18.725000 |
| 11040 | ![]() | Gakusen Toshi Asterisk 2nd Season | [comedy, action, ecchi, scifi, fantasy, romance] | 6.995652 | 8.550000 |
| 462 | ![]() | Armitage III: Poly-Matrix | [scifi, action, adventure, romance] | 6.922447 | 28.879167 |
| 979 | ![]() | Armitage III | [scifi, action, adventure, romance] | 6.919472 | 29.500000 |
| 514 | ![]() | Wonderful Days | [scifi, drama, action, romance] | 6.918831 | 21.908333 |
| 2181 | ![]() | Cybersix | [scifi, action, adventure, romance] | 6.917548 | 24.591667 |
| 903 | ![]() | Blue Seed | [horror, comedy, mystery, drama, action, ecchi, scifi, adventure, romance] | 6.910652 | 27.641667 |
| 1774 | ![]() | Photon | [comedy, drama, action, ecchi, scifi, adventure, romance] | 6.889656 | 27.300000 |
| 693 | ![]() | RahXephon: Tagen Hensoukyoku | [scifi, drama, action, romance] | 6.876021 | 21.908333 |
df = AnimeRecommender1.get_anime_recommendations1('Kizumonogatari III: Reiketsu-hen' , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|
| 21051 | ![]() | Mahou Shoujo Madoka★Magica Movie 4: Walpurgis no Kaiten | 48820 | Movie | [drama, suspense, mystery, supernatural] | 6.39 | 6.387130 | 1.088235 | 0.000000 |
| 16445 | ![]() | Princess Principal: Crown Handler Movie 3 | 41141 | Movie | [action, mystery] | 6.39 | 6.387130 | 1.055164 | 1.991667 |
| 21930 | ![]() | Mahoutsukai no Yoru | 50668 | Movie | [mystery, supernatural] | 6.39 | 6.387130 | 1.055164 | 1.991667 |
| 22375 | ![]() | City Hunter Movie: Tenshi no Namida | 51585 | Movie | [action, mystery] | 6.39 | 6.387130 | 1.055164 | 1.991667 |
| 14410 | ![]() | Princess Principal: Crown Handler Movie 1 | 37807 | Movie | [action, mystery] | 7.52 | 7.311564 | 0.982313 | 3.983333 |
| 16444 | ![]() | Princess Principal: Crown Handler Movie 2 | 41140 | Movie | [action, mystery] | 7.71 | 7.316857 | 0.982012 | 3.983333 |
| 13907 | ![]() | Zoku Owarimonogatari | 36999 | Movie | [comedy, mystery, supernatural] | 8.45 | 8.414520 | 0.958951 | 6.825000 |
| 11051 | ![]() | Kizumonogatari II: Nekketsu-hen | 31757 | Movie | [action, mystery, supernatural] | 8.58 | 8.565061 | 0.956823 | 8.962500 |
| 5670 | ![]() | Kizumonogatari I: Tekketsu-hen | 9260 | Movie | [action, mystery, supernatural] | 8.37 | 8.357224 | 0.897322 | 8.962500 |
| 12647 | ![]() | Bungou Stray Dogs: Dead Apple | 34944 | Movie | [action, mystery, supernatural] | 7.92 | 7.901468 | 0.870743 | 6.970833 |
| 11076 | ![]() | Kuroshitsuji Movie: Book of the Atlantic | 31812 | Movie | [action, comedy, mystery, supernatural] | 8.25 | 8.211884 | 0.826493 | 7.966667 |
| 14089 | ![]() | K: Seven Stories Movie 4 - Lost Small World - Ori no Mukou ni | 37305 | Movie | [action, supernatural] | 7.44 | 7.189447 | 0.817666 | 6.970833 |
| 14090 | ![]() | K: Seven Stories Movie 5 - Memory of Red - Burn | 37306 | Movie | [action, supernatural] | 7.62 | 7.329238 | 0.807656 | 6.970833 |
| 14087 | ![]() | K: Seven Stories Movie 2 - Side:Blue - Tenrou no Gotoku | 37303 | Movie | [action, supernatural] | 7.30 | 7.102172 | 0.791741 | 6.970833 |
| 13791 | ![]() | Servamp Movie: Alice in the Garden | 36803 | Movie | [action, supernatural] | 7.24 | 7.063517 | 0.787954 | 6.970833 |
| 20810 | ![]() | Sinbi Apateu Movie: Haneuldokkaebi Dae Yoleumungandeu | 48271 | Movie | [mystery, supernatural] | 6.39 | 6.387130 | 0.787424 | 5.975000 |
| 12975 | ![]() | Donten ni Warau Gaiden: Shukumei, Soutou no Fuuma | 35424 | Movie | [action, supernatural] | 7.51 | 7.001060 | 0.784547 | 6.970833 |
| 14091 | ![]() | K: Seven Stories Movie 6 - Circle Vision - Nameless Song | 37307 | Movie | [action, supernatural] | 7.19 | 6.978058 | 0.781912 | 6.970833 |
| 14088 | ![]() | K: Seven Stories Movie 3 - Side:Green - Uwagaki Sekai | 37304 | Movie | [action, supernatural] | 7.14 | 6.960402 | 0.781520 | 6.970833 |
| 13786 | ![]() | Owarimonogatari 2nd Season Recaps | 36796 | Special | [comedy, mystery, supernatural] | 7.80 | 7.565795 | 0.780914 | 7.933333 |
| 12352 | ![]() | Overlord Movie 2: Shikkoku no Eiyuu | 34428 | Movie | [action, fantasy, supernatural] | 7.66 | 7.577513 | 0.779492 | 7.966667 |
| 12209 | ![]() | Overlord Movie 1: Fushisha no Ou | 34161 | Movie | [action, fantasy, supernatural] | 7.61 | 7.535257 | 0.776669 | 7.966667 |
| 12976 | ![]() | Donten ni Warau Gaiden: Ouka, Tenbou no Kakehashi | 35425 | Movie | [action, supernatural] | 7.16 | 6.780010 | 0.774289 | 6.970833 |
| 11150 | ![]() | Detective Conan Movie 20: The Darkest Nightmare | 32005 | Movie | [action, mystery] | 8.17 | 8.037584 | 0.766312 | 8.962500 |
| 20809 | ![]() | Sinbi Apateu: Geumbich Dokkaebiwa Bimil-ui Dong-gul | 48270 | Movie | [mystery, supernatural] | 6.39 | 6.387130 | 0.740127 | 6.970833 |
| 11838 | ![]() | Walking Meat | 33438 | Movie | [action, supernatural] | 6.39 | 6.387130 | 0.740127 | 6.970833 |
| 12747 | ![]() | Donten ni Warau Gaiden: Ketsubetsu, Yamainu no Chikai | 35086 | Movie | [action, supernatural] | 7.40 | 6.987897 | 0.739563 | 7.966667 |
| 10643 | ![]() | Ajin Part 2: Shoutotsu | 30869 | Movie | [horror, action, mystery, supernatural] | 7.22 | 7.029073 | 0.709749 | 8.962500 |
| 10644 | ![]() | Ajin Part 3: Shougeki | 30870 | Movie | [horror, action, mystery, supernatural] | 7.24 | 7.026792 | 0.709605 | 8.962500 |
| 9753 | ![]() | Detective Conan Movie 19: The Hellfire Sunflowers | 28479 | Movie | [action, mystery] | 7.72 | 7.618908 | 0.700617 | 9.958333 |
| 10642 | ![]() | Ajin Part 1: Shoudou | 30868 | Movie | [horror, action, mystery, supernatural] | 7.44 | 7.338186 | 0.691993 | 9.958333 |
| 8418 | ![]() | Detective Conan Movie 18: The Sniper from Another Dimension | 21419 | Movie | [action, mystery] | 8.07 | 7.951886 | 0.687619 | 10.954167 |
| 7156 | ![]() | Kara no Kyoukai Movie: Mirai Fukuin | 14807 | Movie | [drama, mystery, supernatural] | 8.00 | 7.946199 | 0.656134 | 11.950000 |
| 3944 | ![]() | Bakemonogatari | 5081 | TV | [romance, mystery, supernatural] | 8.33 | 8.325189 | 0.597078 | 15.000000 |
| 10409 | ![]() | Ghost Messenger Movie | 30278 | Movie | [action, supernatural] | 5.68 | 6.305062 | 0.591791 | 10.954167 |
| 7096 | ![]() | Persona 4 the Animation: The Factor of Hope | 14267 | Movie | [action, adventure, mystery, supernatural] | 7.27 | 7.027294 | 0.580441 | 12.945833 |
| 4008 | ![]() | Kara no Kyoukai Movie 7: Satsujin Kousatsu (Go) | 5205 | Movie | [suspense, mystery, supernatural, action, romance] | 8.39 | 8.358043 | 0.578928 | 15.933333 |
| 4594 | ![]() | Detective Conan Movie 14: The Lost Ship in the Sky | 6467 | Movie | [action, mystery] | 8.11 | 8.018776 | 0.577354 | 14.937500 |
| 6537 | ![]() | Un-Go: Inga-ron | 11531 | Movie | [mystery, supernatural] | 7.54 | 7.441425 | 0.573560 | 13.941667 |
| 6319 | ![]() | Towa no Quon 2: Konton no Ranbu | 10713 | Movie | [scifi, action, mystery, supernatural] | 7.32 | 7.245869 | 0.566480 | 13.941667 |
| 4130 | ![]() | Detective Conan Movie 13: The Raven Chaser | 5460 | Movie | [action, mystery] | 8.21 | 8.115840 | 0.558738 | 15.933333 |
| 3568 | ![]() | Kara no Kyoukai Movie 5: Mujun Rasen | 4282 | Movie | [suspense, mystery, supernatural, drama, action, romance] | 8.53 | 8.500891 | 0.558579 | 16.929167 |
| 3268 | ![]() | Kara no Kyoukai Movie 3: Tsuukaku Zanryuu | 3783 | Movie | [suspense, mystery, supernatural, drama, action] | 8.02 | 7.998942 | 0.542886 | 16.929167 |
| 3567 | ![]() | Kara no Kyoukai Movie 4: Garan no Dou | 4280 | Movie | [action, suspense, mystery, supernatural] | 7.83 | 7.810155 | 0.540824 | 16.929167 |
| 4007 | ![]() | Kara no Kyoukai Movie 6: Boukyaku Rokuon | 5204 | Movie | [suspense, mystery, supernatural, action, romance] | 7.46 | 7.442779 | 0.517242 | 16.929167 |
| 3267 | ![]() | Kara no Kyoukai Movie 2: Satsujin Kousatsu (Zen) | 3782 | Movie | [suspense, mystery, supernatural, action, romance] | 7.78 | 7.763108 | 0.510976 | 17.925000 |
| 2379 | ![]() | Kara no Kyoukai Movie 1: Fukan Fuukei | 2593 | Movie | [action, suspense, mystery, supernatural] | 7.57 | 7.558499 | 0.508916 | 17.925000 |
| 3993 | ![]() | First Squad: The Moment of Truth | 5178 | Movie | [action, supernatural] | 5.95 | 5.984949 | 0.459673 | 15.933333 |
| 672 | ![]() | Vampire Hunter D | 732 | Movie | [scifi, horror, action, supernatural] | 7.01 | 6.986076 | 0.256773 | 39.833333 |
| 7841 | ![]() | Dokkaebi Bangmang-I | 18325 | Movie | [action, supernatural] | 6.39 | 6.387130 | 0.245057 | 38.837500 |
Now lets add the Collaborative filtering:¶
class AnimeRecommender1:
def __init__(self, anime_df, ratings_df):
self.nlp = spacy.load("en_core_web_sm")
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
self.nlp = spacy.load("en_core_web_sm")
self.current_year = datetime.now().year
self._preprocess_data()
self._create_feature_matrices()
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['Release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
def normalize_genre_list(genre_string):
if genre_string == 'UNKNOWN' or pd.isna(genre_string):
return []
genres = re.split(r',\s*', genre_string)
cleaned = [re.sub(r'[^\w\s]', '', g).strip().lower() for g in genres]
return list(set(cleaned))
self.anime_df['Genres'] = self.anime_df['Genres'].apply(normalize_genre_list)
self.anime_df['Studios'] = self.anime_df['Studios'].apply(normalize_genre_list)
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'].fillna(''))
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
def _clean_text(self, text_series: pd.Series) -> pd.Series:
return text_series.apply(self._spacy_clean)
def _spacy_clean(self, text: str) -> str:
doc = self.nlp(text.lower())
return ' '.join(
token.lemma_ for token in doc
if not token.is_stop and not token.is_punct and token.is_alpha
)
def _calculate_recency_boost(self, release_year):
year = release_year if pd.notna(release_year) else self.anime_df['Release_year'].min()
min_year = self.anime_df['Release_year'].min()
max_year = self.anime_df['Release_year'].max()
year_range = max_year - min_year if max_year != min_year else 1
scaled_year = (year - min_year) / year_range
return scaled_year
def _calculate_anime_similarity(self, idx: int):
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
num_recs_to_get = min(52, len(self.anime_df) - 1)
sorted_sim_scores = sim_scores[sim_scores[:, 1].argsort()[::-1]]
top_sim = sorted_sim_scores[1:num_recs_to_get + 1]
top_indices = top_sim[:, 0].astype(int)
sim_scores_top = top_sim[:, 1]
return sim_scores_top, top_indices
def get_anime_recommendations1(self, title, user_id, n=10,best_svd_model=SVD_model, similarity_weight=0.85, recency_weight=0.2, svd_weight=0.6):
matching_animes = self.anime_df[self.anime_df['Name'] == title]
if matching_animes.empty:
raise ValueError(f"Anime '{title}' not found in the database.")
idx = matching_animes.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recommended_animes = self.anime_df.iloc[top_indices][
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'Release_year', 'Episodes', 'Status']
].copy()
recommended_animes['similarity_score'] = sim_scores_top
recommended_animes['recency_score'] = recommended_animes['Release_year'].apply(self._calculate_recency_boost)
recommended_animes['end_year'] = recommended_animes['Release_year'].fillna(self.current_year)
recommended_animes['episodes'] = pd.to_numeric(recommended_animes['Episodes'], errors='coerce').fillna(self.median_episodes)
recommended_animes['age_penalty'] = recommended_animes.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(self.current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
# 🔄 Predict SVD ratings for this user if model and anime_id mapping exists
svd_preds = []
for anime_id in recommended_animes['anime_id']:
try:
pred = best_svd_model.predict(user_id, anime_id).est
except Exception:
pred = best_svd_model.trainset.global_mean # fallback
svd_preds.append(pred)
recommended_animes['svd_pred'] = svd_preds
# 🔄 Normalize weights
rating_weight = max(0, 1 - similarity_weight)
similarity_weight = max(0, similarity_weight)
recency_weight = max(0, recency_weight)
svd_weight = max(0, min(svd_weight, 1))
# 🔄 Blend collaborative + content score
content_score = (
rating_weight * recommended_animes['weighted_rating'] +
similarity_weight * recommended_animes['similarity_score']
)
recommended_animes['final_score'] = (
((1 - svd_weight) * content_score + svd_weight * recommended_animes['svd_pred']) * (1 - recency_weight) +
recency_weight * recommended_animes['recency_score']
) / (1 + recommended_animes['age_penalty'] / 10)
return recommended_animes.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'svd_pred', 'final_score', 'age_penalty']
]
def get_recommendations_by_genres_simple(self, genres, n=10):
if isinstance(genres, str):
genres = [genres]
input_genres = {g.strip().lower() for g in genres}
if not input_genres:
raise ValueError("No valid genres provided.")
# Always safely convert genre lists to sets on-the-fly
genre_sets = self.anime_df['Genres'].apply(lambda g_list: {g.strip().lower() for g in g_list})
self.anime_df['has_all_genres'] = genre_sets.apply(
lambda anime_genres: input_genres.issubset(anime_genres)
)
recommendations = self.anime_df[self.anime_df['has_all_genres']].copy()
current_year = self.current_year
recommendations['end_year'] = recommendations['Release_year'].fillna(current_year)
recommendations['episodes'] = pd.to_numeric(recommendations['Episodes'], errors='coerce').fillna(self.median_episodes)
recommendations['age_penalty'] = recommendations.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
recommendations = recommendations.sort_values(
by=['weighted_rating', 'age_penalty'], ascending=[False, True]
).head(n)
return recommendations[['Image URL', 'Name', 'Genres', 'weighted_rating', 'age_penalty']]
- lets load the
SVDmodel
import pickle
with open('/content/drive/MyDrive/Anime Recommender System/svd_best_model13.pkl', 'rb') as f:
SVD_model = pickle.load(f)
recommender = AnimeRecommender1(anime_df, ratings_df)
df = recommender.get_anime_recommendations1(
title='One Piece',
user_id=1,
best_svd_model=SVD_model,
n=24,
similarity_weight=0.60,
recency_weight=0.2,
svd_weight=0.9
)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | svd_pred | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|---|
| 23543 | ![]() | Hirogaru Sky! Precure | 53716 | TV | [fantasy, action] | 7.69 | 6.923455 | 7.775836 | 6.043347 | 0.000000 |
| 21578 | ![]() | Edens Zero 2nd Season | 50002 | TV | [fantasy, adventure, scifi, action] | 7.43 | 7.164798 | 7.302414 | 5.708149 | 0.000000 |
| 21472 | ![]() | Fairy Tail: 100 Years Quest | 49785 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 5.645024 | 0.000000 |
| 24174 | ![]() | Boruto: Naruto Next Generations Part 2 | 54687 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 5.644798 | 0.000000 |
| 24042 | ![]() | Ishura | 54449 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 5.643434 | 0.000000 |
| 24186 | ![]() | Mahoutsukai Precure! 2 | 54717 | TV | [fantasy, action] | 6.39 | 6.387130 | 7.519458 | 5.315576 | 0.991667 |
| 23754 | ![]() | Bleach: Sennen Kessen-hen - Ketsubetsu-tan | 53998 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 8.180449 | 5.271495 | 1.983333 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [fantasy, adventure, action] | 7.74 | 7.643075 | 8.136377 | 4.895325 | 2.916667 |
| 24175 | ![]() | Naruto (Shinsaku Anime) | 54688 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 4.881137 | 1.966667 |
| 22481 | ![]() | Nanatsu no Taizai: Mokushiroku no Yonkishi | 51794 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 4.874748 | 1.983333 |
| 22788 | ![]() | Shangri-La Frontier: Kusoge Hunter, Kamige ni Idoman to su | 52347 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 4.874384 | 1.983333 |
| 24185 | ![]() | Kibou no Chikara: Otona Precure '23 | 54716 | TV | [fantasy, action] | 6.39 | 6.387130 | 7.519458 | 4.873150 | 1.983333 |
| 24677 | ![]() | Naruto (2023) | 55453 | TV | [fantasy, adventure, comedy, action] | 6.39 | 6.387130 | 7.519458 | 4.873135 | 1.983333 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [fantasy, adventure, action] | 7.04 | 6.978657 | 7.047267 | 4.294314 | 2.850000 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [fantasy, adventure, action] | 7.64 | 7.609725 | 7.489363 | 4.237840 | 3.816667 |
| 18914 | ![]() | Orient | 45560 | TV | [fantasy, adventure, action] | 6.60 | 6.590130 | 6.404930 | 3.925004 | 2.850000 |
| 10577 | ![]() | Dragon Ball Super | 30694 | TV | [fantasy, adventure, comedy, action] | 7.43 | 7.426386 | 7.376076 | 3.838155 | 5.000000 |
| 14699 | ![]() | One Piece Movie 14: Stampede | 38234 | Movie | [fantasy, adventure, action] | 8.22 | 8.190025 | 7.467511 | 3.665383 | 5.975000 |
| 16405 | ![]() | Digimon Adventure: | 41074 | TV | [fantasy, adventure, comedy, action] | 6.42 | 6.416737 | 6.315851 | 3.652153 | 3.604167 |
| 8558 | ![]() | Fairy Tail (2014) | 22043 | TV | [fantasy, adventure, action] | 7.65 | 7.645865 | 7.630431 | 3.640551 | 6.325000 |
| 13291 | ![]() | Golden Kamuy | 36028 | TV | [adventure, action] | 7.87 | 7.852865 | 7.637980 | 3.581137 | 6.650000 |
| 13409 | ![]() | One Piece: Episode of East Blue - Luffy to 4-nin no Nakama no Daibouken | 36215 | Special | [fantasy, adventure, action] | 7.88 | 7.758384 | 7.977352 | 3.454295 | 7.966667 |
| 10865 | ![]() | Drifters | 31339 | TV | [fantasy, adventure, comedy, action] | 7.90 | 7.889898 | 8.058146 | 3.376119 | 8.550000 |
| 6018 | ![]() | Toriko | 10033 | TV | [adventure, action, gourmet, fantasy, comedy] | 7.52 | 7.481460 | 7.320828 | 3.359241 | 7.000000 |
class AnimeRecommender1:
def __init__(self, anime_df, ratings_df, svd_model):
self.nlp = spacy.load("en_core_web_sm")
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
self.svd_model = svd_model
self.current_year = datetime.now().year
self._preprocess_data()
self._create_feature_matrices()
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['Release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
def normalize_genre_list(genre_string):
if genre_string == 'UNKNOWN' or pd.isna(genre_string):
return []
genres = re.split(r',\s*', genre_string)
return list(set([re.sub(r'[^\w\s]', '', g).strip().lower() for g in genres]))
self.anime_df['Genres'] = self.anime_df['Genres'].apply(normalize_genre_list)
self.anime_df['Studios'] = self.anime_df['Studios'].apply(normalize_genre_list)
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'].fillna(''))
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
def _clean_text(self, text_series):
return text_series.apply(self._spacy_clean)
def _spacy_clean(self, text):
doc = self.nlp(text.lower())
return ' '.join(token.lemma_ for token in doc if not token.is_stop and not token.is_punct and token.is_alpha)
def _calculate_recency_boost(self, release_year):
year = release_year if pd.notna(release_year) else self.anime_df['Release_year'].min()
min_year = self.anime_df['Release_year'].min()
max_year = self.anime_df['Release_year'].max()
return (year - min_year) / (max_year - min_year) if max_year != min_year else 0
def _calculate_anime_similarity(self, idx):
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
sorted_sim_scores = sim_scores[sim_scores[:, 1].argsort()[::-1]]
top_sim = sorted_sim_scores[1:min(52, len(self.anime_df))]
top_indices = top_sim[:, 0].astype(int)
sim_scores_top = top_sim[:, 1]
return sim_scores_top, top_indices
def get_anime_recommendations1(self, title, user_id, n=10, similarity_weight=0.85, recency_weight=0.2, svd_weight=0.6):
match = self.anime_df[self.anime_df['Name'] == title]
if match.empty:
raise ValueError(f"Anime '{title}' not found.")
idx = match.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recs = self.anime_df.iloc[top_indices].copy()
recs['similarity_score'] = sim_scores_top
recs['recency_score'] = recs['Release_year'].apply(self._calculate_recency_boost)
recs['end_year'] = recs['Release_year'].fillna(self.current_year)
recs['episodes'] = pd.to_numeric(recs['Episodes'], errors='coerce').fillna(self.median_episodes)
recs['age_penalty'] = recs.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(self.current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)), axis=1
)
svd_preds = []
for anime_id in recs['anime_id']:
try:
pred = self.svd_model.predict(user_id, anime_id).est
except Exception:
pred = self.svd_model.trainset.global_mean
svd_preds.append(pred)
recs['svd_pred'] = svd_preds
rating_weight = max(0, 1 - similarity_weight)
content_score = rating_weight * recs['weighted_rating'] + similarity_weight * recs['similarity_score']
recs['final_score'] = (
((1 - svd_weight) * content_score + svd_weight * recs['svd_pred']) * (1 - recency_weight) +
recency_weight * recs['recency_score']
) / (1 + recs['age_penalty'] / 10)
return recs.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'Genres', 'Score', 'weighted_rating', 'svd_pred', 'final_score', 'age_penalty']
]
recommender = AnimeRecommender1(anime_df, ratings_df,svd_model=SVD_model)
df = recommender.get_anime_recommendations1(
title='Fairy Tail: 100 Years Quest',
user_id=1,
n=24
)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | svd_pred | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|---|
| 11 | ![]() | One Piece | 21 | TV | [fantasy, adventure, action] | 8.69 | 8.686696 | 8.614288 | 4.854554 | 0.000000 |
| 21578 | ![]() | Edens Zero 2nd Season | 50002 | TV | [fantasy, adventure, scifi, action] | 7.43 | 7.164798 | 7.302414 | 4.188849 | 0.000000 |
| 24174 | ![]() | Boruto: Naruto Next Generations Part 2 | 54687 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 4.065522 | 0.000000 |
| 24886 | ![]() | Dekisokonai to Yobareta Motoeiyuu wa Jikka kara Tsuihou sareta node Sukikatte ni Ikiru Koto ni Shita | 55717 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 4.058088 | 0.000000 |
| 22406 | ![]() | Nozomanu Fushi no Boukensha | 51648 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 4.056716 | 0.000000 |
| 23556 | ![]() | Sokushi Cheat ga Saikyou sugite, Isekai no Yatsura ga Marude Aite ni Naranai n desu ga. | 53730 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 4.055115 | 0.000000 |
| 22758 | ![]() | Ore dake Level Up na Ken | 52299 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 3.873439 | 0.991667 |
| 23754 | ![]() | Bleach: Sennen Kessen-hen - Ketsubetsu-tan | 53998 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 8.180449 | 3.821219 | 1.983333 |
| 24175 | ![]() | Naruto (Shinsaku Anime) | 54688 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 3.563505 | 1.966667 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [fantasy, adventure, action] | 7.74 | 7.643075 | 8.136377 | 3.563224 | 2.916667 |
| 22481 | ![]() | Nanatsu no Taizai: Mokushiroku no Yonkishi | 51794 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 3.558762 | 1.983333 |
| 22788 | ![]() | Shangri-La Frontier: Kusoge Hunter, Kamige ni Idoman to su | 52347 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 3.558162 | 1.983333 |
| 24677 | ![]() | Naruto (2023) | 55453 | TV | [fantasy, adventure, comedy, action] | 6.39 | 6.387130 | 7.519458 | 3.550593 | 1.983333 |
| 22190 | ![]() | Ragna Crimson | 51297 | TV | [fantasy, action] | 6.39 | 6.387130 | 7.519458 | 3.548639 | 1.983333 |
| 23432 | ![]() | Boukensha ni Naritai to Miyako ni Deteitta Musume ga S-Rank ni Natteta | 53494 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.519458 | 3.548144 | 1.983333 |
| 15568 | ![]() | Tensei shitara Slime Datta Ken 2nd Season | 39551 | TV | [fantasy, adventure, comedy, action] | 8.39 | 8.382957 | 8.160297 | 3.371147 | 3.800000 |
| 16644 | ![]() | Itai no wa Iya nanode Bougyoryoku ni Kyokufuri Shitai to Omoimasu. 2 | 41514 | TV | [fantasy, adventure, comedy, action] | 7.21 | 7.182646 | 6.747344 | 3.294321 | 1.900000 |
| 16626 | ![]() | Tensei shitara Slime Datta Ken 2nd Season Part 2 | 41487 | TV | [fantasy, adventure, comedy, action] | 8.33 | 8.321181 | 7.845924 | 3.260111 | 3.800000 |
| 16095 | ![]() | Arifureta Shokugyou de Sekai Saikyou 2nd Season | 40507 | TV | [fantasy, adventure, action] | 7.18 | 7.169850 | 7.147056 | 3.197214 | 2.850000 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [fantasy, adventure, action] | 7.04 | 6.978657 | 7.047267 | 3.163184 | 2.850000 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [fantasy, adventure, action] | 7.64 | 7.609725 | 7.489363 | 3.117344 | 3.816667 |
| 17034 | ![]() | Kaizoku Oujo | 42544 | TV | [fantasy, adventure, action] | 7.08 | 7.060602 | 7.377949 | 3.053535 | 3.800000 |
| 15554 | ![]() | Kuutei Dragons | 39531 | TV | [fantasy, adventure] | 6.96 | 6.898991 | 7.957808 | 3.038907 | 4.750000 |
| 16630 | ![]() | Nanatsu no Taizai: Funnu no Shinpan | 41491 | TV | [fantasy, adventure, action] | 6.58 | 6.578161 | 6.988408 | 2.946293 | 3.600000 |
df = recommender.get_anime_recommendations1(
title='Fairy Tail: 100 Years Quest',
user_id=2,
n=24
)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | Genres | Score | weighted_rating | svd_pred | final_score | age_penalty | |
|---|---|---|---|---|---|---|---|---|---|---|
| 11 | ![]() | One Piece | 21 | TV | [fantasy, adventure, action] | 8.69 | 8.686696 | 9.239926 | 5.154860 | 0.000000 |
| 21578 | ![]() | Edens Zero 2nd Season | 50002 | TV | [fantasy, adventure, scifi, action] | 7.43 | 7.164798 | 7.746519 | 4.402019 | 0.000000 |
| 24174 | ![]() | Boruto: Naruto Next Generations Part 2 | 54687 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 4.272992 | 0.000000 |
| 24886 | ![]() | Dekisokonai to Yobareta Motoeiyuu wa Jikka kara Tsuihou sareta node Sukikatte ni Ikiru Koto ni Shita | 55717 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 4.265558 | 0.000000 |
| 22406 | ![]() | Nozomanu Fushi no Boukensha | 51648 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 4.264185 | 0.000000 |
| 23556 | ![]() | Sokushi Cheat ga Saikyou sugite, Isekai no Yatsura ga Marude Aite ni Naranai n desu ga. | 53730 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 4.262585 | 0.000000 |
| 22758 | ![]() | Ore dake Level Up na Ken | 52299 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 4.062191 | 0.991667 |
| 23754 | ![]() | Bleach: Sennen Kessen-hen - Ketsubetsu-tan | 53998 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 8.528788 | 3.960748 | 1.983333 |
| 24175 | ![]() | Naruto (Shinsaku Anime) | 54688 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 3.736878 | 1.966667 |
| 22481 | ![]() | Nanatsu no Taizai: Mokushiroku no Yonkishi | 51794 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 3.731894 | 1.983333 |
| 22788 | ![]() | Shangri-La Frontier: Kusoge Hunter, Kamige ni Idoman to su | 52347 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 3.731294 | 1.983333 |
| 24677 | ![]() | Naruto (2023) | 55453 | TV | [fantasy, adventure, comedy, action] | 6.39 | 6.387130 | 7.951687 | 3.723724 | 1.983333 |
| 22190 | ![]() | Ragna Crimson | 51297 | TV | [fantasy, action] | 6.39 | 6.387130 | 7.951687 | 3.721771 | 1.983333 |
| 23432 | ![]() | Boukensha ni Naritai to Miyako ni Deteitta Musume ga S-Rank ni Natteta | 53494 | TV | [fantasy, adventure, action] | 6.39 | 6.387130 | 7.951687 | 3.721275 | 1.983333 |
| 16329 | ![]() | Dragon Quest: Dai no Daibouken (2020) | 40906 | TV | [fantasy, adventure, action] | 7.74 | 7.643075 | 8.212294 | 3.591435 | 2.916667 |
| 16644 | ![]() | Itai no wa Iya nanode Bougyoryoku ni Kyokufuri Shitai to Omoimasu. 2 | 41514 | TV | [fantasy, adventure, comedy, action] | 7.21 | 7.182646 | 7.376227 | 3.547988 | 1.900000 |
| 15568 | ![]() | Tensei shitara Slime Datta Ken 2nd Season | 39551 | TV | [fantasy, adventure, comedy, action] | 8.39 | 8.382957 | 8.324226 | 3.428166 | 3.800000 |
| 16626 | ![]() | Tensei shitara Slime Datta Ken 2nd Season Part 2 | 41487 | TV | [fantasy, adventure, comedy, action] | 8.33 | 8.321181 | 8.293030 | 3.415627 | 3.800000 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | [fantasy, adventure, action] | 7.04 | 6.978657 | 7.495493 | 3.330615 | 2.850000 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | [fantasy, adventure, action] | 7.64 | 7.609725 | 7.953487 | 3.278584 | 3.816667 |
| 16095 | ![]() | Arifureta Shokugyou de Sekai Saikyou 2nd Season | 40507 | TV | [fantasy, adventure, action] | 7.18 | 7.169850 | 7.293229 | 3.251816 | 2.850000 |
| 15554 | ![]() | Kuutei Dragons | 39531 | TV | [fantasy, adventure] | 6.96 | 6.898991 | 8.164820 | 3.106274 | 4.750000 |
| 21032 | ![]() | Saihate no Paladin | 48761 | TV | [fantasy, adventure, action] | 6.84 | 6.833423 | 7.528400 | 3.096776 | 3.800000 |
| 17034 | ![]() | Kaizoku Oujo | 42544 | TV | [fantasy, adventure, action] | 7.08 | 7.060602 | 7.421295 | 3.068612 | 3.800000 |
class AnimeRecommender1:
def __init__(self, anime_df, ratings_df, svd_model):
self.nlp = spacy.load("en_core_web_sm")
self.anime_df = anime_df.copy()
self.ratings_df = ratings_df.copy()
self.svd_model = svd_model
self.current_year = datetime.now().year
self._preprocess_data()
self._create_feature_matrices()
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _preprocess_data(self):
self.anime_df['Score'] = self.anime_df['Score'].replace('[^0-9.]', '', regex=True)
self.anime_df['Scored By'] = self.anime_df['Scored By'].replace('[^0-9]', '', regex=True)
self.anime_df['Score'] = pd.to_numeric(self.anime_df['Score'], errors='coerce')
self.anime_df['Scored By'] = pd.to_numeric(self.anime_df['Scored By'], errors='coerce')
self.anime_df['Score'].fillna(self.anime_df['Score'].median(), inplace=True)
self.anime_df['Scored By'].fillna(self.anime_df['Scored By'].median(), inplace=True)
self.anime_df['Release_year'] = self.anime_df['Aired'].str.extract(r'(\d{4})').astype(float)
def normalize_genre_list(genre_string):
if genre_string == 'UNKNOWN' or pd.isna(genre_string):
return []
genres = re.split(r',\s*', genre_string)
return list(set([re.sub(r'[^\w\s]', '', g).strip().lower() for g in genres]))
self.anime_df['Genres'] = self.anime_df['Genres'].apply(normalize_genre_list)
self.anime_df['Studios'] = self.anime_df['Studios'].apply(normalize_genre_list)
C = self.anime_df['Score'].mean()
m = self.anime_df['Scored By'].quantile(0.65)
self.anime_df['weighted_rating'] = (
(self.anime_df['Scored By'] / (self.anime_df['Scored By'] + m)) * self.anime_df['Score'] +
(m / (self.anime_df['Scored By'] + m)) * C
)
def _create_feature_matrices(self):
self.mlb_genres = MultiLabelBinarizer(sparse_output=True)
self.genres_encoded = self.mlb_genres.fit_transform(self.anime_df['Genres'])
self.mlb_studios = MultiLabelBinarizer(sparse_output=True)
self.studios_encoded = self.mlb_studios.fit_transform(self.anime_df['Studios'])
self.ohe_type = OneHotEncoder(sparse_output=True)
self.type_encoded = self.ohe_type.fit_transform(self.anime_df[['Type']])
self.ohe_source = OneHotEncoder(sparse_output=True)
self.source_encoded = self.ohe_source.fit_transform(self.anime_df[['Source']])
self.anime_df['Episodes'] = pd.to_numeric(self.anime_df['Episodes'], errors='coerce')
self.median_episodes = self.anime_df['Episodes'].median()
self.anime_df['Episodes'].fillna(self.median_episodes, inplace=True)
self.bins = [0, 1, 12, 24, 50, np.inf]
self.labels = ['1', '2-12', '13-24', '25-50', '51+']
self.anime_df['Episodes_Binned'] = pd.cut(self.anime_df['Episodes'], bins=self.bins, labels=self.labels)
self.ohe_episodes = OneHotEncoder(sparse_output=True)
self.episodes_encoded = self.ohe_episodes.fit_transform(self.anime_df[['Episodes_Binned']])
self.anime_df['Synopsis'] = self._clean_text(self.anime_df['Synopsis'].fillna(''))
self.tfidf = TfidfVectorizer(stop_words='english')
self.synopsis_encoded = self.tfidf.fit_transform(self.anime_df['Synopsis'])
def _clean_text(self, text_series):
return text_series.apply(self._spacy_clean)
def _spacy_clean(self, text):
doc = self.nlp(text.lower())
return ' '.join(token.lemma_ for token in doc if not token.is_stop and not token.is_punct and token.is_alpha)
def _calculate_recency_boost(self, release_year):
year = release_year if pd.notna(release_year) else self.anime_df['Release_year'].min()
min_year = self.anime_df['Release_year'].min()
max_year = self.anime_df['Release_year'].max()
return (year - min_year) / (max_year - min_year) if max_year != min_year else 0
def _calculate_anime_similarity(self, idx):
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
sorted_sim_scores = sim_scores[sim_scores[:, 1].argsort()[::-1]]
top_sim = sorted_sim_scores[1:min(52, len(self.anime_df))]
top_indices = top_sim[:, 0].astype(int)
sim_scores_top = top_sim[:, 1]
return sim_scores_top, top_indices
def get_anime_recommendations1(self, title, user_id, n=10, similarity_weight=0.85, recency_weight=0.2):
match = self.anime_df[self.anime_df['Name'] == title]
if match.empty:
raise ValueError(f"Anime '{title}' not found.")
idx = match.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recs = self.anime_df.iloc[top_indices].copy()
recs['similarity_score'] = sim_scores_top
recs['recency_score'] = recs['Release_year'].apply(self._calculate_recency_boost)
recs['end_year'] = recs['Release_year'].fillna(self.current_year)
recs['episodes'] = pd.to_numeric(recs['Episodes'], errors='coerce').fillna(self.median_episodes)
recs['age_penalty'] = recs.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(self.current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)), axis=1
)
if user_id not in set(self.ratings_df['user_id']):
# Cold-start fallback: no SVD available, use content-based only
content_score = (
(1 - similarity_weight) * recs['weighted_rating'] + similarity_weight * recs['similarity_score']
)
recs['final_score'] = (
content_score * (1 - recency_weight) + recs['recency_score'] * recency_weight
) / (1 + recs['age_penalty'] / 10)
recs['svd_pred'] = np.nan
else:
svd_preds = []
for anime_id in recs['anime_id']:
try:
pred = self.svd_model.predict(user_id, anime_id).est
except Exception:
pred = self.svd_model.trainset.global_mean
svd_preds.append(pred)
user_rating_count = self.ratings_df[self.ratings_df['user_id'] == user_id].shape[0]
if user_rating_count < 10:
svd_weight = 0.5
elif user_rating_count < 50:
svd_weight = 0.6
else:
svd_weight = 0.7
recs['svd_pred'] = svd_preds
rating_weight = max(0, 1 - similarity_weight)
content_score = rating_weight * recs['weighted_rating'] + similarity_weight * recs['similarity_score']
recs['final_score'] = (
((1 - svd_weight) * content_score + svd_weight * recs['svd_pred']) * (1 - recency_weight) +
recency_weight * recs['recency_score']
) / (1 + recs['age_penalty'] / 10)
return recs.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'svd_pred', 'final_score']
]
def get_recommendations_by_genres_simple(self, genres, n=10):
if isinstance(genres, str):
genres = [genres]
input_genres = {g.strip().lower() for g in genres}
if not input_genres:
raise ValueError("No valid genres provided.")
# Normalize genres per anime
genre_sets = self.anime_df['Genres'].apply(lambda g_list: {g.strip().lower() for g in g_list})
self.anime_df['has_all_genres'] = genre_sets.apply(
lambda anime_genres: input_genres.issubset(anime_genres)
)
recommendations = self.anime_df[self.anime_df['has_all_genres']].copy()
current_year = self.current_year
recommendations['end_year'] = recommendations['Release_year'].fillna(current_year)
recommendations['episodes'] = pd.to_numeric(recommendations['Episodes'], errors='coerce').fillna(self.median_episodes)
# 🔥 Stronger age penalty
recommendations['age_penalty'] = recommendations.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
recommendations = recommendations.sort_values(
by=['weighted_rating', 'age_penalty'], ascending=[False, True]
).head(n)
return recommendations[['Image URL', 'Name', 'Genres', 'weighted_rating', 'age_penalty']]
anime_df = pd.read_csv('/content/drive/MyDrive/Anime Recommender System/anime_filtered.csv')
recommender = AnimeRecommender1(anime_df, ratings_df, svd_model=SVD_model)
df = recommender.get_anime_recommendations1(
title='Kimetsu no Yaiba: Yuukaku-hen',
user_id=1,
n=24
)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | svd_pred | final_score | |
|---|---|---|---|---|---|---|
| 23543 | ![]() | Hirogaru Sky! Precure | 53716 | TV | 7.775836 | 4.904126 |
| 21905 | ![]() | Yu☆Gi☆Oh! Go Rush!! | 50607 | TV | 7.334715 | 4.626674 |
| 24875 | ![]() | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | 7.519458 | 4.573433 |
| 23669 | ![]() | Ao no Exorcist (Shin Series) | 53889 | TV | 7.519458 | 4.553033 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | 8.193908 | 4.399936 |
| 21793 | ![]() | Mato Seihei no Slave | 50392 | TV | 7.519458 | 4.319778 |
| 20893 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou II | 48417 | TV | 6.535789 | 4.212508 |
| 22042 | ![]() | Jujutsu Kaisen 2nd Season | 51009 | TV | 7.853121 | 4.119205 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | 8.046383 | 3.986269 |
| 22190 | ![]() | Ragna Crimson | 51297 | TV | 7.519458 | 3.963279 |
| 23417 | ![]() | Boushoku no Berserk | 53439 | TV | 7.519458 | 3.955846 |
| 24265 | ![]() | Kikansha no Mahou wa Tokubetsu desu | 54852 | TV | 7.519458 | 3.955463 |
| 22049 | ![]() | Helck | 51020 | TV | 7.421664 | 3.915582 |
| 22748 | ![]() | Nokemono-tachi no Yoru | 52274 | TV | 7.093905 | 3.790933 |
| 21207 | ![]() | High Card | 49154 | TV | 7.034771 | 3.776708 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | 8.047120 | 3.716786 |
| 21515 | ![]() | Tensei shitara Ken deshita | 49891 | TV | 7.288049 | 3.618903 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | 7.047267 | 3.499370 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | 7.489363 | 3.450843 |
| 20904 | ![]() | The Legend of Heroes: Sen no Kiseki - Northern War | 48441 | TV | 6.423645 | 3.450117 |
| 14539 | ![]() | Kimetsu no Yaiba | 38000 | TV | 8.049135 | 3.342700 |
| 21031 | ![]() | Gaikotsu Kishi-sama, Tadaima Isekai e Odekakechuu | 48760 | TV | 6.608047 | 3.312190 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | 7.743131 | 3.304943 |
| 18914 | ![]() | Orient | 45560 | TV | 6.404930 | 3.209440 |
df = recommender.get_anime_recommendations1(
title='Kimetsu no Yaiba: Yuukaku-hen',
user_id=4,
n=24
)
df
# df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
# display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | svd_pred | final_score | |
|---|---|---|---|---|---|---|
| 23543 | https://cdn.myanimelist.net/images/anime/1762/... | Hirogaru Sky! Precure | 53716 | TV | 7.597894 | 4.805134 |
| 21905 | https://cdn.myanimelist.net/images/anime/1624/... | Yu☆Gi☆Oh! Go Rush!! | 50607 | TV | 7.136368 | 4.515923 |
| 24875 | https://cdn.myanimelist.net/images/anime/1230/... | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | 7.079678 | 4.327156 |
| 23669 | https://cdn.myanimelist.net/images/anime/1187/... | Ao no Exorcist (Shin Series) | 53889 | TV | 7.079678 | 4.306756 |
| 21685 | https://cdn.myanimelist.net/images/anime/1926/... | Seiken Gakuin no Makentsukai | 50184 | TV | 7.079678 | 4.297561 |
| 21793 | https://cdn.myanimelist.net/images/anime/1406/... | Mato Seihei no Slave | 50392 | TV | 7.079678 | 4.097866 |
| 22048 | https://cdn.myanimelist.net/images/anime/1765/... | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | 7.531537 | 4.092264 |
| 22042 | https://cdn.myanimelist.net/images/anime/1600/... | Jujutsu Kaisen 2nd Season | 51009 | TV | 7.485360 | 3.947345 |
| 18172 | https://cdn.myanimelist.net/images/anime/1806/... | Chainsaw Man | 44511 | TV | 7.947433 | 3.943268 |
| 20893 | https://cdn.myanimelist.net/images/anime/1475/... | Maou Gakuin no Futekigousha: Shijou Saikyou no... | 48417 | TV | 5.865756 | 3.838076 |
| 22190 | https://cdn.myanimelist.net/images/anime/1702/... | Ragna Crimson | 51297 | TV | 7.079678 | 3.757763 |
| 23417 | https://cdn.myanimelist.net/images/anime/1801/... | Boushoku no Berserk | 53439 | TV | 7.079678 | 3.750914 |
| 24265 | https://cdn.myanimelist.net/images/anime/1630/... | Kikansha no Mahou wa Tokubetsu desu | 54852 | TV | 7.079678 | 3.750230 |
| 22049 | https://cdn.myanimelist.net/images/anime/1879/... | Helck | 51020 | TV | 7.062594 | 3.749607 |
| 22748 | https://cdn.myanimelist.net/images/anime/1087/... | Nokemono-tachi no Yoru | 52274 | TV | 6.824937 | 3.664806 |
| 21207 | https://cdn.myanimelist.net/images/anime/1803/... | High Card | 49154 | TV | 6.672107 | 3.606303 |
| 21533 | https://cdn.myanimelist.net/images/anime/1065/... | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | 7.760512 | 3.601069 |
| 21515 | https://cdn.myanimelist.net/images/anime/1191/... | Tensei shitara Ken deshita | 49891 | TV | 6.736706 | 3.379105 |
| 22235 | https://cdn.myanimelist.net/images/anime/1085/... | Orient: Awajishima Gekitou-hen | 51368 | TV | 6.756744 | 3.373771 |
| 17060 | https://cdn.myanimelist.net/images/anime/1293/... | Heion Sedai no Idaten-tachi | 42625 | TV | 7.155274 | 3.316798 |
| 21031 | https://cdn.myanimelist.net/images/anime/1361/... | Gaikotsu Kishi-sama, Tadaima Isekai e Odekakechuu | 48760 | TV | 6.526765 | 3.277065 |
| 16060 | https://cdn.myanimelist.net/images/anime/1704/... | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | 7.647820 | 3.269365 |
| 20904 | https://cdn.myanimelist.net/images/anime/1894/... | The Legend of Heroes: Sen no Kiseki - Northern... | 48441 | TV | 5.901256 | 3.204889 |
| 14539 | https://cdn.myanimelist.net/images/anime/1286/... | Kimetsu no Yaiba | 38000 | TV | 7.660849 | 3.202840 |
df = recommender.get_anime_recommendations1(
title='Kimetsu no Yaiba: Yuukaku-hen',
user_id=499999999,
n=24
)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | svd_pred | final_score | |
|---|---|---|---|---|---|---|
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | NaN | 1.437633 |
| 20893 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou II | 48417 | TV | NaN | 1.383529 |
| 23543 | ![]() | Hirogaru Sky! Precure | 53716 | TV | NaN | 1.374168 |
| 21905 | ![]() | Yu☆Gi☆Oh! Go Rush!! | 50607 | TV | NaN | 1.277073 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | NaN | 1.245945 |
| 18172 | ![]() | Chainsaw Man | 44511 | TV | NaN | 1.245837 |
| 21793 | ![]() | Mato Seihei no Slave | 50392 | TV | NaN | 1.208660 |
| 24875 | ![]() | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | NaN | 1.208456 |
| 21207 | ![]() | High Card | 49154 | TV | NaN | 1.169196 |
| 23669 | ![]() | Ao no Exorcist (Shin Series) | 53889 | TV | NaN | 1.140456 |
| 21515 | ![]() | Tensei shitara Ken deshita | 49891 | TV | NaN | 1.122883 |
| 22748 | ![]() | Nokemono-tachi no Yoru | 52274 | TV | NaN | 1.115787 |
| 22190 | ![]() | Ragna Crimson | 51297 | TV | NaN | 1.115509 |
| 22042 | ![]() | Jujutsu Kaisen 2nd Season | 51009 | TV | NaN | 1.115509 |
| 22049 | ![]() | Helck | 51020 | TV | NaN | 1.108854 |
| 22436 | ![]() | Hyouken no Majutsushi ga Sekai wo Suberu | 51711 | TV | NaN | 1.095980 |
| 23417 | ![]() | Boushoku no Berserk | 53439 | TV | NaN | 1.090731 |
| 24265 | ![]() | Kikansha no Mahou wa Tokubetsu desu | 54852 | TV | NaN | 1.089456 |
| 21031 | ![]() | Gaikotsu Kishi-sama, Tadaima Isekai e Odekakechuu | 48760 | TV | NaN | 1.088318 |
| 22235 | ![]() | Orient: Awajishima Gekitou-hen | 51368 | TV | NaN | 1.074215 |
| 16060 | ![]() | Kimetsu no Yaiba Movie: Mugen Ressha-hen | 40456 | Movie | NaN | 1.070059 |
| 14539 | ![]() | Kimetsu no Yaiba | 38000 | TV | NaN | 1.066900 |
| 17060 | ![]() | Heion Sedai no Idaten-tachi | 42625 | TV | NaN | 1.059244 |
| 18914 | ![]() | Orient | 45560 | TV | NaN | 1.040878 |
df = recommender.get_recommendations_by_genres_simple([ 'scifi','action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | |
|---|---|---|---|---|---|
| 9880 | ![]() | Gintama° | [scifi, comedy, action] | 9.040355 | 12.481034 |
| 5989 | ![]() | Gintama' | [scifi, comedy, action] | 9.019494 | 18.689781 |
| 7240 | ![]() | Gintama': Enchousen | [scifi, comedy, action] | 9.000788 | 20.537456 |
| 15525 | ![]() | Gintama: The Final | [drama, comedy, action, scifi] | 8.968517 | 5.256040 |
| 12179 | ![]() | Gintama. | [scifi, comedy, action] | 8.947376 | 11.519446 |
| 833 | ![]() | Gintama | [scifi, comedy, action] | 8.928261 | 17.118840 |
| 2647 | ![]() | Code Geass: Hangyaku no Lelouch R2 | [drama, award winning, scifi, action] | 8.906124 | 26.838975 |
| 7228 | ![]() | Gintama Movie 2: Kanketsu-hen - Yorozuya yo Eien Nare | [scifi, comedy, action] | 8.876192 | 19.642834 |
| 14206 | ![]() | Gintama.: Shirogane no Tamashii-hen - Kouhan-sen | [scifi, comedy, action] | 8.831842 | 9.727805 |
| 13818 | ![]() | Gintama.: Shirogane no Tamashii-hen | [scifi, comedy, action] | 8.767080 | 9.813892 |
| 0 | ![]() | Cowboy Bebop | [award winning, scifi, action] | 8.745454 | 46.541358 |
| 1431 | ![]() | Code Geass: Hangyaku no Lelouch | [drama, award winning, scifi, action] | 8.696970 | 30.671254 |
| 20964 | ![]() | 86 Part 2 | [drama, scifi, action] | 8.692836 | 5.014130 |
| 1822 | ![]() | Tengen Toppa Gurren Lagann | [scifi, adventure, award winning, action] | 8.625455 | 28.477074 |
| 16931 | ![]() | Cyberpunk: Edgerunners | [scifi, action] | 8.600006 | 3.581476 |
| 3271 | ![]() | Evangelion: 3.0+1.0 Thrice Upon a Time | [suspense, scifi, award winning, action, drama] | 8.582592 | 5.256040 |
| 3711 | ![]() | Tengen Toppa Gurren Lagann Movie 2: Lagann-hen | [scifi, action] | 8.531098 | 27.741545 |
| 728 | ![]() | Koukaku Kidoutai: Stand Alone Complex 2nd GIG | [scifi, action, mystery] | 8.483671 | 34.424345 |
| 13213 | ![]() | Gintama.: Porori-hen | [scifi, comedy, action] | 8.481844 | 11.468922 |
| 5008 | ![]() | Gintama Movie 1: Shinyaku Benizakura-hen | [scifi, comedy, action] | 8.479563 | 25.674156 |
| 438 | ![]() | Koukaku Kidoutai: Stand Alone Complex | [award winning, scifi, action] | 8.397903 | 38.395112 |
| 19267 | ![]() | Vivy: Fluorite Eye's Song | [suspense, scifi, action] | 8.394183 | 4.992138 |
| 1 | ![]() | Cowboy Bebop: Tengoku no Tobira | [scifi, action] | 8.363114 | 45.127383 |
| 20 | ![]() | Neon Genesis Evangelion | [avant garde, suspense, scifi, award winning, action, drama] | 8.346630 | 52.813875 |
| 8677 | ![]() | Kiseijuu: Sei no Kakuritsu | [horror, scifi, action] | 8.336886 | 15.992403 |
| 6993 | ![]() | Psycho-Pass | [suspense, scifi, action, mystery] | 8.335466 | 19.723195 |
| 3269 | ![]() | Evangelion: 2.0 You Can (Not) Advance | [drama, scifi, action] | 8.299379 | 27.741545 |
| 16608 | ![]() | 86 | [drama, scifi, action] | 8.270491 | 5.036122 |
| 4677 | ![]() | Redline | [scifi, action] | 8.262984 | 27.741545 |
| 24 | ![]() | Koukaku Kidoutai | [suspense, scifi, award winning, action, mystery] | 8.259542 | 58.983721 |
| 4817 | ![]() | Gintama: Shiroyasha Koutan | [scifi, comedy, action] | 8.251004 | 29.834953 |
| 6654 | ![]() | Uchuu Senkan Yamato 2199 | [drama, scifi, action] | 8.242538 | 19.361302 |
| 8526 | ![]() | Gintama: Yorinuki Gintama-san on Theater 2D | [scifi, comedy, action] | 8.229437 | 21.532663 |
| 6424 | ![]() | Kidou Senshi Gundam: The Origin | [scifi, action] | 8.216677 | 15.452709 |
| 2 | ![]() | Trigun | [adventure, scifi, action] | 8.210988 | 46.541358 |
| 11631 | ![]() | Kidou Senshi Gundam: Tekketsu no Orphans 2nd Season | [drama, scifi, action] | 8.188491 | 12.511755 |
| 18480 | ![]() | World Trigger 3rd Season | [scifi, action] | 8.187516 | 4.970146 |
| 28 | ![]() | Akira | [adventure, supernatural, scifi, horror, action] | 8.153587 | 75.862776 |
| 14835 | ![]() | Toaru Kagaku no Railgun T | [fantasy, scifi, action] | 8.134640 | 6.180039 |
| 5862 | ![]() | Gintama: Shinyaku Benizakura-hen | [scifi, comedy, action] | 8.125551 | 25.674156 |
| 2368 | ![]() | Kidou Senshi Gundam 00 | [drama, scifi, action] | 8.084751 | 28.744465 |
| 1845 | ![]() | Darker than Black: Kuro no Keiyakusha | [scifi, action, mystery] | 8.053130 | 28.744465 |
| 3375 | ![]() | Kidou Senshi Gundam 00 Second Season | [drama, scifi, action] | 8.048096 | 26.838975 |
| 4547 | ![]() | Kidou Senshi Gundam Unicorn | [drama, scifi, action] | 8.039891 | 25.029616 |
| 10831 | ![]() | Kidou Senshi Gundam: Tekketsu no Orphans | [drama, scifi, action] | 8.025321 | 14.198002 |
| 203 | ![]() | FLCL | [avant garde, scifi, comedy, action] | 8.023111 | 46.401565 |
| 7354 | ![]() | Toaru Kagaku no Railgun S | [fantasy, scifi, action] | 8.005639 | 17.752520 |
| 16330 | ![]() | World Trigger 2nd Season | [scifi, action] | 8.005060 | 5.014130 |
| 2532 | ![]() | Evangelion: 1.0 You Are (Not) Alone | [drama, award winning, scifi, action] | 7.991460 | 31.953149 |
| 23296 | ![]() | Kidou Senshi Gundam: Suisei no Majo Season 2 | [scifi, action] | 7.985896 | 0.000000 |
import os
import joblib
import spacy
import pandas as pd
import numpy as np
from sklearn.preprocessing import MultiLabelBinarizer, OneHotEncoder
from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.sparse import load_npz, save_npz
from datetime import datetime
class AnimeRecommender1:
def __init__(self, ratings_df, svd_model, raw_anime_csv="/content/drive/MyDrive/Anime Recommender System/anime-dataset-2023.csv",
cache_dir="/content/drive/MyDrive/Anime Recommender System/cache"):
self.nlp = spacy.load("en_core_web_sm", disable=["parser", "ner"])
self.ratings_df = ratings_df.copy()
self.svd_model = svd_model
self.current_year = datetime.now().year
self.cache_dir = cache_dir
self.raw_anime_csv = raw_anime_csv
os.makedirs(cache_dir, exist_ok=True)
self._ensure_cache()
self.weights = {
'genres': 0.30,
'synopsis': 0.35,
'type': 0.15,
'studios': 0.10,
'episodes': 0.05,
'source': 0.05
}
def _ensure_cache(self):
cd = self.cache_dir
cache_files = [
"anime_df_cleaned.pkl", "tfidf_matrix.npz", "tfidf_vocab.pkl",
"genres_encoded.npz", "studios_encoded.npz", "type_encoded.npz",
"source_encoded.npz", "episodes_encoded.npz"
]
all_exist = all(os.path.exists(os.path.join(cd, f)) for f in cache_files)
if not all_exist:
print("🛠️ Generating and caching features (first run)...")
self._generate_and_cache_all()
else:
print("🔁 Loading cached anime data and feature matrices...")
self._load_cached_data()
def _generate_and_cache_all(self):
cd = self.cache_dir
anime_df = pd.read_csv(self.raw_anime_csv)
def fast_normalize(col):
return col.fillna('').str.lower().str.replace(r'[^\w\s,]', '', regex=True).str.split(',\s*')
anime_df['Score'] = pd.to_numeric(anime_df['Score'].str.replace('[^0-9.]', '', regex=True), errors='coerce')
anime_df['Scored By'] = pd.to_numeric(anime_df['Scored By'].str.replace('[^0-9]', '', regex=True), errors='coerce')
anime_df['Score'].fillna(anime_df['Score'].median(), inplace=True)
anime_df['Scored By'].fillna(anime_df['Scored By'].median(), inplace=True)
anime_df['Release_year'] = pd.to_numeric(anime_df['Aired'].str.extract(r'(\d{4})')[0], errors='coerce')
anime_df['Genres'] = fast_normalize(anime_df['Genres']).apply(lambda lst: list(set([g.strip() for g in lst if g != ''])))
anime_df['Studios'] = fast_normalize(anime_df['Studios']).apply(lambda lst: list(set([g.strip() for g in lst if g != ''])))
C = anime_df['Score'].mean()
m = anime_df['Scored By'].quantile(0.65)
anime_df['weighted_rating'] = ((anime_df['Scored By'] / (anime_df['Scored By'] + m)) * anime_df['Score'] +
(m / (anime_df['Scored By'] + m)) * C)
mlb_genres = MultiLabelBinarizer(sparse_output=True)
genres_encoded = mlb_genres.fit_transform(anime_df['Genres'])
save_npz(os.path.join(cd, "genres_encoded.npz"), genres_encoded)
mlb_studios = MultiLabelBinarizer(sparse_output=True)
studios_encoded = mlb_studios.fit_transform(anime_df['Studios'])
save_npz(os.path.join(cd, "studios_encoded.npz"), studios_encoded)
ohe_type = OneHotEncoder(sparse_output=True)
type_encoded = ohe_type.fit_transform(anime_df[['Type']])
save_npz(os.path.join(cd, "type_encoded.npz"), type_encoded)
ohe_source = OneHotEncoder(sparse_output=True)
source_encoded = ohe_source.fit_transform(anime_df[['Source']])
save_npz(os.path.join(cd, "source_encoded.npz"), source_encoded)
anime_df['Episodes'] = pd.to_numeric(anime_df['Episodes'], errors='coerce')
median_episodes = anime_df['Episodes'].median()
anime_df['Episodes'].fillna(median_episodes, inplace=True)
bins = [0, 1, 12, 24, 50, np.inf]
labels = ['1', '2-12', '13-24', '25-50', '51+']
anime_df['Episodes_Binned'] = pd.cut(anime_df['Episodes'], bins=bins, labels=labels)
ohe_episodes = OneHotEncoder(sparse_output=True)
episodes_encoded = ohe_episodes.fit_transform(anime_df[['Episodes_Binned']])
save_npz(os.path.join(cd, "episodes_encoded.npz"), episodes_encoded)
anime_df['Synopsis'] = anime_df['Synopsis'].fillna('')
anime_df['cleaned_synopsis'] = self._parallel_clean(anime_df['Synopsis'])
joblib.dump(anime_df['cleaned_synopsis'], os.path.join(cd, "synopsis_cleaned.pkl"))
tfidf = TfidfVectorizer(stop_words='english', max_features=10000)
tfidf_matrix = tfidf.fit_transform(anime_df['cleaned_synopsis'])
save_npz(os.path.join(cd, "tfidf_matrix.npz"), tfidf_matrix)
joblib.dump(tfidf.vocabulary_, os.path.join(cd, "tfidf_vocab.pkl"))
joblib.dump(anime_df, os.path.join(cd, "anime_df_cleaned.pkl"))
print("✅ All preprocessing completed and cached.")
def _parallel_clean(self, texts):
docs = list(self.nlp.pipe(texts, batch_size=64))
return [
' '.join(token.lemma_ for token in doc if token.is_alpha and not token.is_stop and not token.is_punct)
for doc in docs
]
def _load_cached_data(self):
cd = self.cache_dir
self.anime_df = joblib.load(os.path.join(cd, "anime_df_cleaned.pkl"))
self.synopsis_encoded = load_npz(os.path.join(cd, "tfidf_matrix.npz"))
self.tfidf_vocab = joblib.load(os.path.join(cd, "tfidf_vocab.pkl"))
self.genres_encoded = load_npz(os.path.join(cd, "genres_encoded.npz"))
self.studios_encoded = load_npz(os.path.join(cd, "studios_encoded.npz"))
self.type_encoded = load_npz(os.path.join(cd, "type_encoded.npz"))
self.source_encoded = load_npz(os.path.join(cd, "source_encoded.npz"))
self.episodes_encoded = load_npz(os.path.join(cd, "episodes_encoded.npz"))
self.median_episodes = self.anime_df['Episodes'].median()
print("✅ All cached data loaded successfully.")
def _calculate_recency_boost(self, release_year):
year = release_year if pd.notna(release_year) else self.anime_df['Release_year'].min()
min_year = self.anime_df['Release_year'].min()
max_year = self.anime_df['Release_year'].max()
return (year - min_year) / (max_year - min_year) if max_year != min_year else 0
def _calculate_anime_similarity(self, idx):
from sklearn.metrics.pairwise import cosine_similarity
genres_sim = cosine_similarity(self.genres_encoded[idx:idx+1], self.genres_encoded)[0]
studios_sim = cosine_similarity(self.studios_encoded[idx:idx+1], self.studios_encoded)[0]
synopsis_sim = cosine_similarity(self.synopsis_encoded[idx:idx+1], self.synopsis_encoded)[0]
type_sim = (self.anime_df['Type'] == self.anime_df.iloc[idx]['Type']).astype(float).values
source_sim = (self.anime_df['Source'] == self.anime_df.iloc[idx]['Source']).astype(float).values
episodes_sim = (self.anime_df['Episodes_Binned'] == self.anime_df.iloc[idx]['Episodes_Binned']).astype(float).values
combined_sim = (
self.weights['genres'] * genres_sim +
self.weights['synopsis'] * synopsis_sim +
self.weights['type'] * type_sim +
self.weights['studios'] * studios_sim +
self.weights['episodes'] * episodes_sim +
self.weights['source'] * source_sim
)
sim_scores = np.array(list(enumerate(combined_sim)))
sorted_sim_scores = sim_scores[sim_scores[:, 1].argsort()[::-1]]
top_sim = sorted_sim_scores[1:min(52, len(self.anime_df))]
top_indices = top_sim[:, 0].astype(int)
sim_scores_top = top_sim[:, 1]
return sim_scores_top, top_indices
def get_anime_recommendations1(self, title, user_id, n=10, similarity_weight=0.85, recency_weight=0.2):
match = self.anime_df[self.anime_df['Name'] == title]
if match.empty:
raise ValueError(f"Anime '{title}' not found.")
idx = match.index[0]
sim_scores_top, top_indices = self._calculate_anime_similarity(idx)
recs = self.anime_df.iloc[top_indices].copy()
recs['similarity_score'] = sim_scores_top
recs['recency_score'] = recs['Release_year'].apply(self._calculate_recency_boost)
recs['end_year'] = recs['Release_year'].fillna(self.current_year)
recs['episodes'] = pd.to_numeric(recs['Episodes'], errors='coerce').fillna(self.median_episodes)
recs['age_penalty'] = recs.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(self.current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)), axis=1
)
if user_id not in set(self.ratings_df['user_id']):
content_score = (
(1 - similarity_weight) * recs['weighted_rating'] + similarity_weight * recs['similarity_score']
)
recs['final_score'] = (
content_score * (1 - recency_weight) + recs['recency_score'] * recency_weight
) / (1 + recs['age_penalty'] / 10)
recs['svd_pred'] = np.nan
else:
svd_preds = []
for anime_id in recs['anime_id']:
try:
pred = self.svd_model.predict(user_id, anime_id).est
except Exception:
pred = self.svd_model.trainset.global_mean
svd_preds.append(pred)
user_rating_count = self.ratings_df[self.ratings_df['user_id'] == user_id].shape[0]
if user_rating_count < 10:
svd_weight = 0.5
elif user_rating_count < 50:
svd_weight = 0.6
else:
svd_weight = 0.7
recs['svd_pred'] = svd_preds
rating_weight = max(0, 1 - similarity_weight)
content_score = rating_weight * recs['weighted_rating'] + similarity_weight * recs['similarity_score']
recs['final_score'] = (
((1 - svd_weight) * content_score + svd_weight * recs['svd_pred']) * (1 - recency_weight) +
recency_weight * recs['recency_score']
) / (1 + recs['age_penalty'] / 10)
return recs.sort_values('final_score', ascending=False).head(n)[
['Image URL', 'Name', 'anime_id', 'Type', 'svd_pred', 'final_score']
]
def get_recommendations_by_genres_simple(self, genres, n=10):
if isinstance(genres, str):
genres = [genres]
input_genres = {g.strip().lower() for g in genres}
if not input_genres:
raise ValueError("No valid genres provided.")
genre_sets = self.anime_df['Genres'].apply(lambda g_list: {g.strip().lower() for g in g_list})
self.anime_df['has_all_genres'] = genre_sets.apply(
lambda anime_genres: input_genres.issubset(anime_genres)
)
recommendations = self.anime_df[self.anime_df['has_all_genres']].copy()
current_year = self.current_year
recommendations['end_year'] = recommendations['Release_year'].fillna(current_year)
recommendations['episodes'] = pd.to_numeric(recommendations['Episodes'], errors='coerce').fillna(self.median_episodes)
recommendations['age_penalty'] = recommendations.apply(
lambda row: 0 if row['Status'] == 'Currently Airing' else
(current_year - row['end_year']) * (1 - 0.5 * min(row['episodes'] / 120, 1)),
axis=1
)
recommendations['final_score'] = recommendations['weighted_rating'] / (1 + (recommendations['age_penalty'] / 10))
recommendations = recommendations.sort_values(by='final_score', ascending=False).head(n)
return recommendations[['Image URL', 'Name', 'Genres', 'weighted_rating', 'age_penalty', 'final_score']]
recommender = AnimeRecommender1(ratings_df, svd_model=SVD_model)
🔁 Loading cached anime data and feature matrices... ✅ All cached data loaded successfully.
df = recommender.get_anime_recommendations1(
title='Fate/stay night: Unlimited Blade Works 2nd Season',
user_id=20000000000,
n=24
)
# df
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | svd_pred | final_score | |
|---|---|---|---|---|---|---|
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | 51019 | TV | NaN | 1.304393 |
| 20430 | ![]() | Kimetsu no Yaiba: Yuukaku-hen | 47778 | TV | NaN | 1.149130 |
| 21533 | ![]() | Kimetsu no Yaiba: Mugen Ressha-hen | 49926 | TV | NaN | 1.110795 |
| 24875 | ![]() | Kimetsu no Yaiba: Hashira Geiko-hen | 55701 | TV | NaN | 1.103021 |
| 11630 | ![]() | Fate/stay night Movie: Heaven's Feel - III. Spring Song | 33050 | Movie | NaN | 1.068723 |
| 11629 | ![]() | Fate/stay night Movie: Heaven's Feel - II. Lost Butterfly | 33049 | Movie | NaN | 1.004636 |
| 16352 | ![]() | Enen no Shouboutai: Ni no Shou | 40956 | TV | NaN | 0.995372 |
| 14539 | ![]() | Kimetsu no Yaiba | 38000 | TV | NaN | 0.988310 |
| 14613 | ![]() | Fate/Grand Order: Zettai Majuu Sensen Babylonia | 38084 | TV | NaN | 0.976281 |
| 16089 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou | 40496 | TV | NaN | 0.946744 |
| 16524 | ![]() | Mars Red | 41265 | TV | NaN | 0.939426 |
| 14945 | ![]() | Enen no Shouboutai | 38671 | TV | NaN | 0.927888 |
| 16358 | ![]() | Back Arrow | 40964 | TV | NaN | 0.926908 |
| 16560 | ![]() | The God of High School | 41353 | TV | NaN | 0.916826 |
| 16558 | ![]() | Noblesse | 41345 | TV | NaN | 0.900579 |
| 9312 | ![]() | Fate/stay night Movie: Heaven's Feel - I. Presage Flower | 25537 | Movie | NaN | 0.888241 |
| 14163 | ![]() | Sarazanmai | 37426 | TV | NaN | 0.887312 |
| 12475 | ![]() | Fate/Apocrypha | 34662 | TV | NaN | 0.847556 |
| 15488 | ![]() | Granbelm | 39417 | TV | NaN | 0.833975 |
| 8617 | ![]() | Fate/stay night: Unlimited Blade Works | 22297 | TV | NaN | 0.828481 |
| 13373 | ![]() | Garo: Vanishing Line | 36144 | TV | NaN | 0.805367 |
| 13076 | ![]() | Toji no Miko | 35589 | TV | NaN | 0.802411 |
| 12175 | ![]() | Tales of Zestiria the Cross 2nd Season | 34086 | TV | NaN | 0.796874 |
| 13289 | ![]() | Persona 5 the Animation | 36023 | TV | NaN | 0.785652 |
df = recommender.get_recommendations_by_genres_simple([ 'scifi','romance', 'action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | final_score | |
|---|---|---|---|---|---|---|
| 21951 | ![]() | Mahouka Koukou no Rettousei (Zoku-hen) | [fantasy, action, scifi, romance] | 6.387130 | 0.000000 | 6.387130 |
| 22702 | ![]() | Date A Live V | [action, scifi, romance] | 6.387130 | 0.000000 | 6.387130 |
| 24772 | Macross (Shinsaku Animation) | [action, scifi, romance] | 6.387130 | 0.000000 | 6.387130 | |
| 16612 | ![]() | Date A Live IV | [action, scifi, romance] | 7.746146 | 2.850000 | 6.028129 |
| 16090 | ![]() | Mahouka Koukou no Rettousei: Raihousha-hen | [fantasy, action, scifi, romance] | 7.269632 | 4.729167 | 4.935535 |
| 20157 | ![]() | Gekijou Tanpen Macross Frontier: Toki no Meikyuu | [action, scifi, romance] | 6.541423 | 3.983333 | 4.678014 |
| 20436 | ![]() | Ai Zai Xiyuan Qian 2nd Season | [fantasy, action, scifi, romance] | 6.386648 | 3.733333 | 4.650471 |
| 13676 | ![]() | Date A Live III | [action, scifi, romance] | 7.182833 | 5.700000 | 4.575053 |
| 13216 | ![]() | Darling in the FranXX | [action, drama, scifi, romance] | 7.208504 | 6.300000 | 4.422395 |
| 17902 | ![]() | Chu Feng: Yi Dian Zhi Zi | [fantasy, action, scifi, romance] | 6.346420 | 4.750000 | 4.302657 |
| 16311 | ![]() | Ai Zai Xiyuan Qian | [fantasy, action, scifi, romance] | 6.358454 | 5.700000 | 4.049971 |
| 14559 | ![]() | Beatless Final Stage | [action, drama, scifi, romance] | 6.758023 | 6.883333 | 4.002778 |
| 9655 | ![]() | Macross Δ | [action, scifi, romance] | 7.213781 | 8.025000 | 4.002098 |
| 13591 | ![]() | Beatless | [action, drama, scifi, romance] | 6.215648 | 6.416667 | 3.786182 |
| 11040 | ![]() | Gakusen Toshi Asterisk 2nd Season | [fantasy, ecchi, action, comedy, romance, scifi] | 6.995652 | 8.550000 | 3.771241 |
| 8305 | ![]() | Mahouka Koukou no Rettousei | [fantasy, action, scifi, romance] | 7.406821 | 9.808333 | 3.739245 |
| 9140 | ![]() | Date A Live Movie: Mayuri Judgment | [action, scifi, romance] | 7.315063 | 9.958333 | 3.665167 |
| 8000 | ![]() | Date A Live II | [action, scifi, romance] | 7.186278 | 10.541667 | 3.498391 |
| 10538 | ![]() | Gakusen Toshi Asterisk | [fantasy, ecchi, action, comedy, romance, scifi] | 6.817911 | 9.500000 | 3.496365 |
| 11057 | ![]() | Accel World: Infinite∞Burst | [action, scifi, romance] | 6.606556 | 8.962500 | 3.484011 |
| 14070 | ![]() | Beatless Intermission | [action, drama, scifi, romance] | 5.819581 | 6.883333 | 3.446939 |
| 13895 | ![]() | Cutie Honey Universe | [action, scifi, romance, comedy] | 5.669904 | 6.650000 | 3.405348 |
| 10864 | ![]() | Hundred | [action, scifi, romance, ecchi] | 6.310695 | 8.550000 | 3.401992 |
| 6597 | ![]() | Accel World | [action, scifi, romance] | 7.226102 | 11.700000 | 3.330001 |
| 11628 | ![]() | Zegapain ADP | [action, scifi, romance] | 6.290003 | 8.962500 | 3.317075 |
| 11087 | ![]() | Masou Gakuen HxH | [fantasy, ecchi, action, comedy, romance, scifi] | 6.133505 | 8.550000 | 3.306472 |
| 6198 | ![]() | Aquarion Evol | [fantasy, action, scifi, romance] | 7.053481 | 11.591667 | 3.266761 |
| 4922 | ![]() | Macross F Movie 2: Sayonara no Tsubasa | [action, scifi, romance, award winning] | 7.758669 | 13.941667 | 3.240655 |
| 8481 | ![]() | Captain Earth | [action, scifi, romance] | 6.399017 | 9.854167 | 3.223009 |
| 7775 | ![]() | Freezing Vibration | [ecchi, action, drama, romance, scifi] | 6.713316 | 11.400000 | 3.137064 |
| 3131 | ![]() | Macross F | [action, scifi, romance, award winning] | 7.850851 | 15.229167 | 3.111815 |
| 7047 | ![]() | Accel World EX | [action, scifi, romance] | 7.079104 | 12.891667 | 3.092437 |
| 10459 | ![]() | Aquarion Logos | [fantasy, action, scifi, romance] | 5.845082 | 8.916667 | 3.089911 |
| 6621 | ![]() | Zetman | [supernatural, action, drama, romance, horror, scifi] | 6.827165 | 12.295833 | 3.062081 |
| 5559 | ![]() | Star Driver: Kagayaki no Takuto | [action, scifi, romance] | 7.159537 | 13.437500 | 3.054736 |
| 10175 | ![]() | Chu Feng: B.E.E | [fantasy, action, comedy, romance, scifi] | 5.994350 | 9.750000 | 3.035114 |
| 6822 | ![]() | Star Driver the Movie | [action, scifi, romance] | 6.542405 | 11.950000 | 2.980594 |
| 4074 | ![]() | Macross F Movie 1: Itsuwari no Utahime | [action, scifi, romance] | 7.646773 | 15.933333 | 2.948627 |
| 5727 | ![]() | Freezing | [ecchi, action, drama, romance, scifi] | 6.795726 | 13.300000 | 2.916621 |
| 7212 | ![]() | Macross FB7: Ore no Uta wo Kike! | [action, scifi, romance] | 6.332608 | 12.945833 | 2.759807 |
| 72 | ![]() | Kidou Senshi Gundam SEED | [award winning, action, drama, romance, scifi] | 7.724560 | 18.208333 | 2.738396 |
| 73 | ![]() | Kidou Senshi Gundam SEED Destiny | [action, drama, scifi, romance] | 7.158412 | 16.625000 | 2.688605 |
| 793 | ![]() | Zegapain | [action, scifi, romance] | 7.163313 | 16.941667 | 2.658823 |
| 3923 | ![]() | Kurozuka | [action, drama, romance, horror, scifi] | 6.844540 | 16.150000 | 2.617415 |
| 3078 | ![]() | Genius Party | [fantasy, action, romance, scifi, avant garde] | 7.047031 | 17.475000 | 2.564889 |
| 449 | ![]() | Sousei no Aquarion | [fantasy, action, scifi, romance] | 7.052144 | 17.833333 | 2.533704 |
| 2564 | ![]() | Dragonaut: The Resonance | [fantasy, action, drama, romance, scifi] | 6.589017 | 16.125000 | 2.522112 |
| 3045 | ![]() | Kemeko Deluxe! | [ecchi, action, comedy, romance, scifi] | 6.534407 | 16.150000 | 2.498817 |
| 4633 | ![]() | Urusei Yatsura: The Shougaibutsu Suieitaikai | [action, scifi, romance, comedy] | 6.727476 | 16.929167 | 2.498212 |
| 374 | ![]() | Seikai no Senki III | [action, scifi, romance] | 7.440590 | 19.833333 | 2.494053 |
df = recommender.get_recommendations_by_genres_simple(['fantasy', 'adventure', 'action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | final_score | |
|---|---|---|---|---|---|---|
| 11 | ![]() | One Piece | [fantasy, action, adventure] | 8.686696 | 0.000000 | 8.686696 |
| 19600 | ![]() | Jigokuraku | [fantasy, action, adventure] | 8.224549 | 0.000000 | 8.224549 |
| 21578 | ![]() | Edens Zero 2nd Season | [fantasy, action, scifi, adventure] | 7.164798 | 0.000000 | 7.164798 |
| 23659 | ![]() | Pokemon (2023) | [fantasy, action, adventure, comedy] | 7.112476 | 0.000000 | 7.112476 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | [fantasy, action, adventure] | 9.048079 | 2.837500 | 7.048163 |
| 22054 | ![]() | Doupo Cangqiong: Nian Fan | [fantasy, action, adventure] | 6.968240 | 0.000000 | 6.968240 |
| 21385 | ![]() | Tunshi Xingkong 2nd Season | [fantasy, action, scifi, adventure] | 6.952457 | 0.000000 | 6.952457 |
| 23239 | ![]() | Dungeon ni Deai wo Motomeru no wa Machigatteiru Darou ka IV: Fuka Shou - Yakusai-hen | [fantasy, action, adventure] | 8.199359 | 1.908333 | 6.885396 |
| 20156 | ![]() | Wanmei Shijie | [fantasy, action, adventure] | 6.836767 | 0.000000 | 6.836767 |
| 22968 | ![]() | Shen Yin Wangzuo 2nd Season | [fantasy, action, adventure] | 6.798776 | 0.000000 | 6.798776 |
| 21388 | ![]() | Yi Nian Yong Heng: Chuan Cheng Pian | [fantasy, action, adventure] | 6.779368 | 0.000000 | 6.779368 |
| 23047 | ![]() | Isekai de Cheat Skill wo Te ni Shita Ore wa, Genjitsu Sekai wo mo Musou Suru: Level Up wa Jinsei wo Kaeta | [fantasy, action, adventure] | 6.591589 | 0.000000 | 6.591589 |
| 16770 | ![]() | Ling Jian Zun 4th Season | [fantasy, action, adventure, romance] | 6.578649 | 0.000000 | 6.578649 |
| 21122 | ![]() | Wu Shang Shen Di 2nd Season | [fantasy, action, adventure, supernatural] | 6.526427 | 0.000000 | 6.526427 |
| 22469 | ![]() | Xingchen Bian 5th Season | [fantasy, action, adventure, romance] | 6.476171 | 0.000000 | 6.476171 |
| 23948 | ![]() | Yao Shen Ji 6th Season | [fantasy, action, adventure, romance] | 6.456534 | 0.000000 | 6.456534 |
| 23122 | ![]() | Bai Lian Cheng Shen | [fantasy, action, adventure] | 6.448433 | 0.000000 | 6.448433 |
| 24035 | ![]() | Lian Qi Shi Wan Nian | [fantasy, action, adventure] | 6.425276 | 0.000000 | 6.425276 |
| 23023 | ![]() | Wan Jie Du Zun 2nd Season | [fantasy, action, adventure] | 6.422149 | 0.000000 | 6.422149 |
| 23427 | ![]() | Binghuo Mochu 2 | [fantasy, action, adventure] | 6.421938 | 0.000000 | 6.421938 |
| 22936 | ![]() | Wangu Shenhua | [fantasy, action, adventure] | 6.410487 | 0.000000 | 6.410487 |
| 23854 | ![]() | Jian Yu Feng Yun 2nd Season | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 21377 | ![]() | Shanhe Jian Xin 2nd Season | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 23155 | ![]() | Arifureta Shokugyou de Sekai Saikyou 3rd Season | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 23300 | ![]() | Jun You Yun 2nd Season | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 22249 | ![]() | Liaotian Qun De Richang Shenghuo | [fantasy, action, adventure, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 23568 | ![]() | Knights of the Zodiac: Saint Seiya 3rd Season | [fantasy, action, scifi, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 23556 | ![]() | Sokushi Cheat ga Saikyou sugite, Isekai no Yatsura ga Marude Aite ni Naranai n desu ga. | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 23482 | ![]() | Bye-Bye, Earth | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 21094 | ![]() | Overlord Movie 3: Sei Oukoku-hen | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 24039 | ![]() | Fanren Xiu Xian Chuan: Chongzhi Ban | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 24042 | ![]() | Ishura | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 22406 | ![]() | Nozomanu Fushi no Boukensha | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 23451 | ![]() | Sword Art Online (Original Movie) | [fantasy, action, adventure, romance] | 6.387130 | 0.000000 | 6.387130 |
| 23433 | ![]() | Jing Cui Xian Zun Part 2 | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 24156 | ![]() | Fangkemeng: Bu Yuan Zhi Dao de Fangxing Baokemeng?! | [fantasy, action, adventure, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 21801 | ![]() | Tian Bao Fuyao Lu 3rd Season | [fantasy, action, adventure, supernatural] | 6.387130 | 0.000000 | 6.387130 |
| 24174 | ![]() | Boruto: Naruto Next Generations Part 2 | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 22183 | ![]() | Emo Faze | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 21796 | ![]() | Tian Guan Ci Fu 2nd Season | [fantasy, supernatural, adventure, action, drama] | 6.387130 | 0.000000 | 6.387130 |
| 24886 | ![]() | Dekisokonai to Yobareta Motoeiyuu wa Jikka kara Tsuihou sareta node Sukikatte ni Ikiru Koto ni Shita | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 21759 | ![]() | Seirei Gensouki 2nd Season | [fantasy, action, adventure, romance] | 6.387130 | 0.000000 | 6.387130 |
| 22556 | ![]() | Da Wang Rao Ming 2 | [fantasy, action, adventure, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 21652 | ![]() | Yuan Zun | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 21514 | ![]() | Tsuki ga Michibiku Isekai Douchuu 2nd Season | [fantasy, action, adventure, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 24828 | MY WIFE IS A DEMON QUEEN | [fantasy, action, adventure, romance] | 6.387130 | 0.000000 | 6.387130 | |
| 21472 | ![]() | Fairy Tail: 100 Years Quest | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 24797 | ![]() | Gu Wu Gaoshou Zai Dushi 3rd Season | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 24777 | ![]() | Si Ge Yongzhe | [fantasy, adventure, action, comedy, romance] | 6.387130 | 0.000000 | 6.387130 |
| 24765 | ![]() | Jian Gu | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
df = recommender.get_recommendations_by_genres_simple(['horror', 'action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | final_score | |
|---|---|---|---|---|---|---|
| 14170 | ![]() | Chimera | [horror, action, supernatural] | 6.387130 | 0.000000 | 6.387130 |
| 23140 | ![]() | Berserk: Ougon Jidai-hen - Memorial Edition | [fantasy, adventure, action, drama, horror] | 7.673791 | 2.837500 | 5.977636 |
| 14942 | ![]() | Dorohedoro | [fantasy, action, comedy, horror] | 8.048611 | 4.750000 | 5.456685 |
| 23844 | ![]() | Zom 100: Zombie ni Naru made ni Shitai 100 no Koto | [supernatural, suspense, action, comedy, horror] | 6.387130 | 1.983333 | 5.330011 |
| 24008 | ![]() | Biohazard: Death Island | [horror, action, scifi] | 6.387130 | 1.991667 | 5.326307 |
| 23881 | ![]() | Muja | [horror, action] | 6.387130 | 2.987500 | 4.917906 |
| 23021 | ![]() | Kinemaquia PV | [horror, action, supernatural] | 6.343686 | 2.987500 | 4.884455 |
| 17645 | ![]() | Tenkuu Shinpan | [horror, action, mystery] | 6.706470 | 3.800000 | 4.859761 |
| 12419 | ![]() | Koutetsujou no Kabaneri Movie 3: Unato Kessen | [fantasy, action, drama, horror] | 7.653065 | 5.975000 | 4.790651 |
| 16394 | ![]() | Dorohedoro: Ma no Omake | [fantasy, action, comedy, horror] | 7.055402 | 4.875000 | 4.743128 |
| 21089 | ![]() | Ling Long: Incarnation Special | [suspense, action, drama, horror, scifi] | 6.535984 | 3.983333 | 4.674125 |
| 17924 | ![]() | Ling Long: Incarnation Final Chapter | [suspense, action, drama, horror, scifi] | 6.527820 | 3.966667 | 4.673857 |
| 12774 | ![]() | Devilman: Crybaby | [horror, action, supernatural, avant garde] | 7.756334 | 6.708333 | 4.642195 |
| 23604 | ![]() | Chikajou no Mamono | [horror, action, suspense] | 6.387130 | 3.983333 | 4.567673 |
| 17238 | ![]() | Biohazard: Infinite Darkness | [horror, action, scifi] | 6.256654 | 3.933333 | 4.490422 |
| 16922 | ![]() | Ling Long: Incarnation Part 2 | [suspense, action, drama, horror, scifi] | 6.647807 | 4.875000 | 4.469114 |
| 16836 | ![]() | Bem Movie: Become Human | [horror, action, supernatural] | 6.621090 | 4.979167 | 4.420199 |
| 15634 | ![]() | Ling Long: Incarnation | [suspense, action, drama, horror, scifi] | 6.728516 | 5.850000 | 4.245121 |
| 8677 | ![]() | Kiseijuu: Sei no Kakuritsu | [horror, action, scifi] | 8.336886 | 9.900000 | 4.189390 |
| 11876 | ![]() | Koutetsujou no Kabaneri Movie 2: Moeru Inochi | [fantasy, action, drama, horror] | 7.439158 | 7.966667 | 4.140533 |
| 17242 | ![]() | Ling Long: Incarnation Middle Chapter | [suspense, action, drama, horror, scifi] | 6.589379 | 5.975000 | 4.124807 |
| 11751 | ![]() | Ajin Part 2 | [horror, action, supernatural, mystery] | 7.567470 | 8.512500 | 4.087762 |
| 10960 | ![]() | Ajin | [horror, action, supernatural, mystery] | 7.393813 | 8.512500 | 3.993957 |
| 17537 | ![]() | Fire Emblem Heroes Book III Movie:Cohort of the Dead | [fantasy, action, supernatural, horror] | 6.355354 | 5.975000 | 3.978312 |
| 11613 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Zetsubou-hen | [horror, action, mystery] | 7.369897 | 8.587500 | 3.964975 |
| 12206 | ![]() | Super Danganronpa 2.5: Komaeda Nagito to Sekai no Hakaimono | [horror, action, suspense, mystery] | 7.115634 | 7.966667 | 3.960464 |
| 11875 | ![]() | Koutetsujou no Kabaneri Movie 1: Tsudou Hikari | [fantasy, action, drama, horror] | 7.434561 | 8.962500 | 3.920665 |
| 9798 | ![]() | Koutetsujou no Kabaneri | [fantasy, action, drama, horror] | 7.266267 | 8.550000 | 3.917125 |
| 12182 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Kibou-hen | [horror, action, mystery] | 7.402755 | 8.962500 | 3.903892 |
| 11210 | ![]() | Danganronpa 3: The End of Kibougamine Gakuen - Mirai-hen | [horror, action, mystery] | 7.221937 | 8.550000 | 3.893227 |
| 11171 | ![]() | Gantz:O | [horror, action, drama, scifi] | 7.379877 | 8.962500 | 3.891827 |
| 14405 | ![]() | Tokyo Ghoul:re 2nd Season | [fantasy, action, horror] | 6.429838 | 6.650000 | 3.861764 |
| 14081 | ![]() | Mo Ri Shu Guang | [supernatural, suspense, action, horror, boys love] | 6.306844 | 6.416667 | 3.841732 |
| 11091 | ![]() | Biohazard: Vendetta | [horror, action, scifi] | 6.895031 | 7.966667 | 3.837679 |
| 13588 | ![]() | Tokyo Ghoul:re | [fantasy, action, horror] | 6.370037 | 6.650000 | 3.825848 |
| 13670 | ![]() | Ajin Part 2 OVA | [horror, action, supernatural, mystery] | 6.871137 | 7.966667 | 3.824381 |
| 8619 | ![]() | Tokyo Ghoul | [fantasy, action, horror] | 7.788620 | 10.450000 | 3.808616 |
| 24801 | ![]() | Castlevania (Netflix animated series) | [fantasy, action, adventure, horror] | 6.387130 | 6.933333 | 3.771927 |
| 24830 | ![]() | Castlevania | [fantasy, adventure, action, drama, horror] | 6.387130 | 6.933333 | 3.771927 |
| 24340 | ![]() | Life of the Dead Episode 7 | [horror, action, suspense] | 6.387130 | 6.970833 | 3.763592 |
| 11155 | ![]() | Ajin OVA | [horror, action, supernatural, mystery] | 7.108905 | 8.925000 | 3.756357 |
| 12168 | ![]() | Berserk 2nd Season | [fantasy, adventure, action, drama, horror] | 6.556780 | 7.600000 | 3.725443 |
| 6671 | ![]() | Berserk: Ougon Jidai-hen III - Kourin | [fantasy, adventure, action, drama, horror] | 8.176690 | 11.950000 | 3.725144 |
| 10643 | ![]() | Ajin Part 2: Shoutotsu | [horror, action, supernatural, mystery] | 7.029073 | 8.962500 | 3.706828 |
| 10644 | ![]() | Ajin Part 3: Shougeki | [horror, action, supernatural, mystery] | 7.026792 | 8.962500 | 3.705625 |
| 10642 | ![]() | Ajin Part 1: Shoudou | [horror, action, supernatural, mystery] | 7.338186 | 9.958333 | 3.676753 |
| 12408 | ![]() | Terra Formars: Earth-hen | [horror, action, scifi] | 6.218910 | 6.941667 | 3.670778 |
| 13765 | ![]() | Xue Se Cang Qiong | [fantasy, suspense, adventure, mystery, action, horror] | 6.286284 | 7.133333 | 3.669037 |
| 14277 | ![]() | Devilman: Crybaby - Digest Eizou | [horror, action, supernatural] | 6.215971 | 6.970833 | 3.662737 |
| 10513 | ![]() | Tokyo Ghoul: "Jack" | [fantasy, action, horror] | 7.288512 | 9.958333 | 3.651864 |
df = recommender.get_recommendations_by_genres_simple([ 'romance','action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | final_score | |
|---|---|---|---|---|---|---|
| 16770 | ![]() | Ling Jian Zun 4th Season | [fantasy, action, adventure, romance] | 6.578649 | 0.000000 | 6.578649 |
| 21268 | ![]() | Wan Jie Xian Zong 5th Season | [fantasy, action, romance] | 6.487966 | 0.000000 | 6.487966 |
| 22469 | ![]() | Xingchen Bian 5th Season | [fantasy, action, adventure, romance] | 6.476171 | 0.000000 | 6.476171 |
| 23948 | ![]() | Yao Shen Ji 6th Season | [fantasy, action, adventure, romance] | 6.456534 | 0.000000 | 6.456534 |
| 22139 | ![]() | Long Wang Dian 2nd Season | [action, romance, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 24777 | ![]() | Si Ge Yongzhe | [fantasy, adventure, action, comedy, romance] | 6.387130 | 0.000000 | 6.387130 |
| 24775 | ![]() | Ruler of the Land | [fantasy, action, drama, comedy, romance, ecchi] | 6.387130 | 0.000000 | 6.387130 |
| 24828 | MY WIFE IS A DEMON QUEEN | [fantasy, action, adventure, romance] | 6.387130 | 0.000000 | 6.387130 | |
| 22702 | ![]() | Date A Live V | [action, scifi, romance] | 6.387130 | 0.000000 | 6.387130 |
| 22734 | ![]() | Baozou Xia Ri | [action, romance, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 21759 | ![]() | Seirei Gensouki 2nd Season | [fantasy, action, adventure, romance] | 6.387130 | 0.000000 | 6.387130 |
| 23451 | ![]() | Sword Art Online (Original Movie) | [fantasy, action, adventure, romance] | 6.387130 | 0.000000 | 6.387130 |
| 23414 | ![]() | Maou no Ore ga Dorei Elf wo Yome ni Shitanda ga, Dou Medereba Ii? | [fantasy, action, romance] | 6.387130 | 0.000000 | 6.387130 |
| 17900 | ![]() | Bai She Chuan: Bai Suzhen | [fantasy, action, romance] | 6.387130 | 0.000000 | 6.387130 |
| 24772 | Macross (Shinsaku Animation) | [action, scifi, romance] | 6.387130 | 0.000000 | 6.387130 | |
| 22893 | ![]() | Ta Chengle Bing Jiao Junwang De Bai Yueguang | [fantasy, action, romance] | 6.387130 | 0.000000 | 6.387130 |
| 21951 | ![]() | Mahouka Koukou no Rettousei (Zoku-hen) | [fantasy, action, scifi, romance] | 6.387130 | 0.000000 | 6.387130 |
| 21904 | ![]() | Ayakashi Triangle | [supernatural, action, comedy, romance, ecchi] | 6.323771 | 0.000000 | 6.323771 |
| 16612 | ![]() | Date A Live IV | [action, scifi, romance] | 7.746146 | 2.850000 | 6.028129 |
| 21739 | ![]() | Sword Art Online: Progressive Movie - Kuraki Yuuyami no Scherzo | [fantasy, action, adventure, romance] | 7.555626 | 2.987500 | 5.817614 |
| 23932 | ![]() | Kekkon Yubiwa Monogatari | [fantasy, action, romance, ecchi] | 6.387130 | 0.991667 | 5.810884 |
| 23649 | ![]() | Yozakura-san Chi no Daisakusen | [action, romance, comedy] | 6.387130 | 0.991667 | 5.810884 |
| 21534 | ![]() | Genjitsu Shugi Yuusha no Oukoku Saikenki Part 2 | [fantasy, action, romance] | 7.423565 | 2.837500 | 5.782719 |
| 20994 | ![]() | Koi wa Sekai Seifuku no Ato de | [action, romance, comedy] | 7.419155 | 2.850000 | 5.773662 |
| 22923 | ![]() | Tensei Kizoku no Isekai Boukenroku: Jichou wo Shiranai Kamigami no Shito | [fantasy, action, romance] | 6.635464 | 1.900000 | 5.576020 |
| 24445 | ![]() | Da Zhu Zai Nian Fan | [fantasy, action, adventure, romance] | 6.387130 | 1.566667 | 5.522014 |
| 21738 | ![]() | Yao Shen Ji 5th Season | [fantasy, action, adventure, romance] | 6.756378 | 2.266667 | 5.507917 |
| 18096 | ![]() | Doupo Cangqiong 4th Season | [fantasy, supernatural, adventure, action, romance] | 7.368729 | 3.600000 | 5.418183 |
| 22555 | ![]() | Hua Jianghu: Bu Liang Ren VI | [fantasy, action, drama, romance] | 6.387130 | 1.900000 | 5.367336 |
| 18174 | ![]() | Koroshi Ai | [action, romance] | 6.883943 | 2.850000 | 5.357154 |
| 21908 | ![]() | Rurouni Kenshin: Meiji Kenkaku Romantan (2023) | [action, romance] | 6.387130 | 1.983333 | 5.330011 |
| 21563 | ![]() | Kimi to Boku no Saigo no Senjou, Aruiwa Sekai ga Hajimaru Seisen Season II | [fantasy, action, romance] | 6.387130 | 1.983333 | 5.330011 |
| 20432 | ![]() | Sekai Saikou no Ansatsusha, Isekai Kizoku ni Tensei suru | [fantasy, mystery, action, drama, romance] | 7.353479 | 3.800000 | 5.328608 |
| 22263 | ![]() | Engage Kiss | [action, romance, comedy] | 6.829794 | 2.837500 | 5.320190 |
| 21929 | ![]() | Xingchen Bian: Po Tian Mi Ju | [fantasy, action, adventure, romance] | 6.733938 | 2.800000 | 5.260889 |
| 16716 | ![]() | Genjitsu Shugi Yuusha no Oukoku Saikenki | [fantasy, action, romance] | 7.231725 | 3.783333 | 5.246717 |
| 22505 | ![]() | Shu Ling Ji 3 | [fantasy, action, romance] | 6.387130 | 2.250000 | 5.213984 |
| 22053 | ![]() | Doupo Cangqiong: Yuanqi | [fantasy, action, adventure, romance] | 6.706171 | 2.962500 | 5.173517 |
| 24222 | ![]() | Ayakashi Triangle Recap | [supernatural, action, comedy, romance, ecchi] | 6.128855 | 1.991667 | 5.110928 |
| 18004 | ![]() | Seirei Gensouki | [fantasy, action, adventure, romance] | 7.053035 | 3.800000 | 5.110895 |
| 18181 | ![]() | Isekai Meikyuu de Harem wo | [fantasy, adventure, action, romance, ecchi] | 6.566849 | 2.850000 | 5.110389 |
| 16227 | ![]() | Yao Shen Ji 4th Season | [fantasy, action, adventure, romance] | 6.967184 | 3.916667 | 5.006360 |
| 16090 | ![]() | Mahouka Koukou no Rettousei: Raihousha-hen | [fantasy, action, scifi, romance] | 7.269632 | 4.729167 | 4.935535 |
| 21302 | ![]() | Xingchen Bian: Xingchen Yao Hai | [fantasy, action, adventure, romance] | 6.694893 | 3.800000 | 4.851372 |
| 22814 | ![]() | Long Wang Dian | [action, romance, comedy] | 6.387130 | 3.166667 | 4.850985 |
| 21383 | ![]() | Xue Ying Ling Zhu 3rd Season | [fantasy, action, adventure, romance] | 6.533166 | 3.566667 | 4.815602 |
| 16905 | ![]() | Wu Dong Qian Kun 2nd Season | [fantasy, action, adventure, romance] | 6.974597 | 4.750000 | 4.728540 |
| 17200 | ![]() | Wan Jie Xian Zong 4th Season | [fantasy, action, romance] | 6.602748 | 4.000000 | 4.716249 |
| 13994 | ![]() | Douluo Dalu | [fantasy, action, adventure, romance] | 7.641607 | 6.241667 | 4.704940 |
| 18012 | ![]() | Xie Wang Zhui Qi: Yishi Qingcheng | [fantasy, action, drama, comedy, romance] | 6.925415 | 4.750000 | 4.695197 |
df = recommender.get_recommendations_by_genres_simple(['fantasy', 'action'] , n=50)
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | Genres | weighted_rating | age_penalty | final_score | |
|---|---|---|---|---|---|---|
| 11 | ![]() | One Piece | [fantasy, action, adventure] | 8.686696 | 0.000000 | 8.686696 |
| 19600 | ![]() | Jigokuraku | [fantasy, action, adventure] | 8.224549 | 0.000000 | 8.224549 |
| 22709 | ![]() | Mashle | [fantasy, action, comedy] | 7.553219 | 0.000000 | 7.553219 |
| 22090 | ![]() | NieR:Automata Ver1.1a | [fantasy, action, scifi] | 7.355421 | 0.000000 | 7.355421 |
| 13312 | ![]() | Fate/Grand Order | [fantasy, action] | 7.285004 | 0.000000 | 7.285004 |
| 23475 | ![]() | Dead Mount Death Play | [fantasy, action, supernatural] | 7.240476 | 0.000000 | 7.240476 |
| 21578 | ![]() | Edens Zero 2nd Season | [fantasy, action, scifi, adventure] | 7.164798 | 0.000000 | 7.164798 |
| 23659 | ![]() | Pokemon (2023) | [fantasy, action, adventure, comedy] | 7.112476 | 0.000000 | 7.112476 |
| 22048 | ![]() | Kimetsu no Yaiba: Katanakaji no Sato-hen | [fantasy, action] | 8.467293 | 1.908333 | 7.110393 |
| 16617 | ![]() | Bleach: Sennen Kessen-hen | [fantasy, action, adventure] | 9.048079 | 2.837500 | 7.048163 |
| 22054 | ![]() | Doupo Cangqiong: Nian Fan | [fantasy, action, adventure] | 6.968240 | 0.000000 | 6.968240 |
| 21385 | ![]() | Tunshi Xingkong 2nd Season | [fantasy, action, scifi, adventure] | 6.952457 | 0.000000 | 6.952457 |
| 23543 | ![]() | Hirogaru Sky! Precure | [fantasy, action] | 6.923455 | 0.000000 | 6.923455 |
| 23239 | ![]() | Dungeon ni Deai wo Motomeru no wa Machigatteiru Darou ka IV: Fuka Shou - Yakusai-hen | [fantasy, action, adventure] | 8.199359 | 1.908333 | 6.885396 |
| 20893 | ![]() | Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou II | [fantasy, action] | 6.884430 | 0.000000 | 6.884430 |
| 20156 | ![]() | Wanmei Shijie | [fantasy, action, adventure] | 6.836767 | 0.000000 | 6.836767 |
| 20973 | ![]() | Black Clover: Mahou Tei no Ken | [fantasy, action, comedy] | 8.162355 | 1.991667 | 6.806690 |
| 22968 | ![]() | Shen Yin Wangzuo 2nd Season | [fantasy, action, adventure] | 6.798776 | 0.000000 | 6.798776 |
| 21388 | ![]() | Yi Nian Yong Heng: Chuan Cheng Pian | [fantasy, action, adventure] | 6.779368 | 0.000000 | 6.779368 |
| 16584 | ![]() | Wushen Zhuzai | [fantasy, action, drama] | 6.778840 | 0.000000 | 6.778840 |
| 22429 | ![]() | Kaminaki Sekai no Kamisama Katsudou | [fantasy, action, comedy] | 6.759057 | 0.000000 | 6.759057 |
| 18172 | ![]() | Chainsaw Man | [fantasy, action] | 8.584003 | 2.850000 | 6.680158 |
| 16856 | ![]() | Arknights | [fantasy, action] | 6.602246 | 0.000000 | 6.602246 |
| 23047 | ![]() | Isekai de Cheat Skill wo Te ni Shita Ore wa, Genjitsu Sekai wo mo Musou Suru: Level Up wa Jinsei wo Kaeta | [fantasy, action, adventure] | 6.591589 | 0.000000 | 6.591589 |
| 16770 | ![]() | Ling Jian Zun 4th Season | [fantasy, action, adventure, romance] | 6.578649 | 0.000000 | 6.578649 |
| 21122 | ![]() | Wu Shang Shen Di 2nd Season | [fantasy, action, adventure, supernatural] | 6.526427 | 0.000000 | 6.526427 |
| 20844 | ![]() | Kage no Jitsuryokusha ni Naritakute! | [fantasy, action, comedy] | 8.315181 | 2.750000 | 6.521711 |
| 21331 | ![]() | Ni Tian Zhizun | [fantasy, action] | 6.517756 | 0.000000 | 6.517756 |
| 21268 | ![]() | Wan Jie Xian Zong 5th Season | [fantasy, action, romance] | 6.487966 | 0.000000 | 6.487966 |
| 22469 | ![]() | Xingchen Bian 5th Season | [fantasy, action, adventure, romance] | 6.476171 | 0.000000 | 6.476171 |
| 21139 | ![]() | Mahou Shoujo Magical Destroyers | [fantasy, action] | 6.470741 | 0.000000 | 6.470741 |
| 21386 | ![]() | Quanzhi Fashi VI | [fantasy, action] | 6.464126 | 0.000000 | 6.464126 |
| 23948 | ![]() | Yao Shen Ji 6th Season | [fantasy, action, adventure, romance] | 6.456534 | 0.000000 | 6.456534 |
| 23122 | ![]() | Bai Lian Cheng Shen | [fantasy, action, adventure] | 6.448433 | 0.000000 | 6.448433 |
| 17589 | ![]() | Jue Shi Wu Hun | [fantasy, action] | 6.441052 | 0.000000 | 6.441052 |
| 24035 | ![]() | Lian Qi Shi Wan Nian | [fantasy, action, adventure] | 6.425276 | 0.000000 | 6.425276 |
| 23023 | ![]() | Wan Jie Du Zun 2nd Season | [fantasy, action, adventure] | 6.422149 | 0.000000 | 6.422149 |
| 23427 | ![]() | Binghuo Mochu 2 | [fantasy, action, adventure] | 6.421938 | 0.000000 | 6.421938 |
| 22936 | ![]() | Wangu Shenhua | [fantasy, action, adventure] | 6.410487 | 0.000000 | 6.410487 |
| 22893 | ![]() | Ta Chengle Bing Jiao Junwang De Bai Yueguang | [fantasy, action, romance] | 6.387130 | 0.000000 | 6.387130 |
| 22866 | ![]() | Xian Wu Chuan | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 21451 | ![]() | Xiuluo Wu Shen | [fantasy, action] | 6.387130 | 0.000000 | 6.387130 |
| 22864 | ![]() | Yi Nian Yong Heng Xiao Juchang | [fantasy, action, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 22737 | ![]() | Wo Cong Zhu Tian Wan Jie Guilai | [fantasy, action] | 6.387130 | 0.000000 | 6.387130 |
| 21435 | ![]() | Fate/kaleid liner Prisma☆Illya (Zoku-hen) | [fantasy, action] | 6.387130 | 0.000000 | 6.387130 |
| 21237 | ![]() | Youjo Senki II | [fantasy, action] | 6.387130 | 0.000000 | 6.387130 |
| 24266 | ![]() | Maou 2099 | [fantasy, action, scifi] | 6.387130 | 0.000000 | 6.387130 |
| 24156 | ![]() | Fangkemeng: Bu Yuan Zhi Dao de Fangxing Baokemeng?! | [fantasy, action, adventure, comedy] | 6.387130 | 0.000000 | 6.387130 |
| 24196 | ![]() | Bai Lian Feisheng Lu | [fantasy, action, adventure] | 6.387130 | 0.000000 | 6.387130 |
| 22249 | ![]() | Liaotian Qun De Richang Shenghuo | [fantasy, action, adventure, comedy] | 6.387130 | 0.000000 | 6.387130 |
recommender = AnimeRecommender1(ratings_df, svd_model=SVD_model)
🛠️ Generating and caching features (first run)... ✅ All preprocessing completed and cached. ✅ All cached data loaded successfully.
df = recommender.get_anime_recommendations1(
title='Darling in the FranXX',
user_id=1,
n=24
)
# df
df['Image URL'] = df['Image URL'].apply(lambda x: f'<img src="{x}" width="100"/>')
display(HTML(df.to_html(escape=False)))
| Image URL | Name | anime_id | Type | svd_pred | final_score | |
|---|---|---|---|---|---|---|
| 21948 | ![]() | Lycoris Recoil | 50709 | TV | 7.479177 | 3.720411 |
| 20964 | ![]() | 86 Part 2 | 48569 | TV | 7.972586 | 3.672763 |
| 16608 | ![]() | 86 | 41457 | TV | 7.866722 | 3.614240 |
| 16309 | ![]() | SSSS.Dynazenon | 40870 | TV | 7.568760 | 3.475140 |
| 21073 | ![]() | Cardfight!! Vanguard: overDress Season 2 | 48862 | TV | 7.434672 | 3.402961 |
| 22263 | ![]() | Engage Kiss | 51417 | TV | 6.319085 | 3.177520 |
| 16266 | ![]() | Hypnosis Mic: Division Rap Battle - Rhyme Anima | 40803 | TV | 7.165139 | 3.086045 |
| 13591 | ![]() | Beatless | 36516 | TV | 6.985848 | 2.695937 |
| 11508 | ![]() | Senki Zesshou Symphogear AXZ | 32836 | TV | 7.403490 | 2.673891 |
| 8855 | ![]() | Shigatsu wa Kimi no Uso | 23273 | TV | 8.366180 | 2.638317 |
| 10540 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus Part 2 | 30549 | TV | 8.175402 | 2.635609 |
| 12271 | ![]() | Grancrest Senki | 34279 | TV | 6.684960 | 2.632233 |
| 9655 | ![]() | Macross Δ | 28013 | TV | 7.449904 | 2.615518 |
| 11072 | ![]() | Kiznaiver | 31798 | TV | 7.236734 | 2.484391 |
| 9604 | ![]() | Plastic Memories | 27775 | TV | 7.475861 | 2.445603 |
| 7558 | ![]() | Soukyuu no Fafner: Dead Aggressor - Exodus | 17080 | TV | 7.318508 | 2.387288 |
| 10460 | ![]() | Classroom☆Crisis | 30383 | TV | 6.959962 | 2.279592 |
| 7299 | ![]() | Senki Zesshou Symphogear G | 15793 | TV | 7.600541 | 2.247569 |
| 11157 | ![]() | Bubuki Buranki | 32023 | TV | 6.501526 | 2.235694 |
| 8424 | ![]() | Buddy Complex | 21437 | TV | 7.055803 | 2.198688 |
| 6358 | ![]() | Guilty Crown | 10793 | TV | 7.615118 | 2.118129 |
| 8823 | ![]() | M3: Sono Kuroki Hagane | 23133 | TV | 6.585564 | 2.113426 |
| 3375 | ![]() | Kidou Senshi Gundam 00 Second Season | 3927 | TV | 7.836909 | 1.961011 |
| 5559 | ![]() | Star Driver: Kagayaki no Takuto | 8934 | TV | 7.229457 | 1.954424 |














































































































































































































































































































































































































































































































































































































































































































































