Construindo um sistema de recomendação de animes em Python

Patrick Gomes
7 min readAug 24, 2020

--

Usando apenas a biblioteca Pandas, criaremos um sistema de recomendação simples baseado nos gêneros do anime e nas avaliações dos usuários

Python-chan

Os dados se encontram em: https://www.kaggle.com/CooperUnion/anime-recommendations-database

Meu Github: https://github.com/Patotricks15

Os sistemas de recomendação estão nas mais avançadas empresas de e-commerce, tanto em produtos como em mercadorias. Entender o comportamento dos consumidores é essencial para que possamos modelar um sistema que acompanhe esse comportamento com a finalidade de satisfazer o consumidor e gerar lucros para a empresa.

Ter um conhecimento básico de estatística e matrizes facilitará o entendimento, mas explicarei cada passo ao longo do tutorial

PANDAS

A biblioteca Pandas foi criada para manipulação de dados na linguagem Python. O nome é derivado do termo inglês “panel data”(dados em painel), um termo usado em estatística e econometria para conjunto de dados que incluem várias unidades amostrais. Utilizaremos aqui as seguintes funcionalidades do Pandas:

  • Filtração e limpeza de dados.
  • Ferramentas para fundir(merging) ou juntar(join) conjuntos de dados.
  • Uso do objeto “DataFrame” para manipulação de dados, com indexação integrada.
  • Reformatação e pivoteamento de matrizes (dados).
  • Inserir e deletar colunas em conjuntos de dados.
  • Divisão (slicing), fancy indexing, e subsettingde grandes conjuntos de dados.

VAMOS AO CÓDIGO!!

Começaremos importando as bibliotecas Pandas e Warnings (Matplotlib e Seaborn serão usados uma única vez, só porque gosto de observar o comportamento da distribuição, mas não é necessário para a construção do recomendador)

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('darkgrid')
import warnings
warnings.filterwarnings("ignore")

Depois chamamos os dados do MyAnimeList. Os animes recomendados são baseados em:

  • Avaliação de cada usuário
  • Gênero do anime selecionado
df_anime = pd.read_csv('anime.csv')
df_notas = pd.read_csv('rating.csv')

Para analisar os dois dataframes juntos, usamos “display”

display(df_anime.head())
df_notas.head()
Exibindo os dataframes
display(df_anime.info())print('=='*40)display(df_notas.info())
Mostrando as informações dos dataframes

Agora uniremos os dois dataframes em um só, pela coluna ‘anime_id’, excluindo o ‘rating’ do df_anime (porque o que nos interessa aqui é a nota dada pelo usuário, o ‘user_id’)

df = pd.merge(df_notas, df_anime.drop('rating', axis=1),on='anime_id' )print(df.shape)df.head()
Usando df.shape para mostrar o tamanho do dataframe, e df.head() para mostrá-lo

Como podemos observar, o mesmo anime aparece diversas vezes, pois diversos usuários deram uma nota para ele. Então faremos um agrupamento dos animes pelo nome, e pegaremos a contagem das avaliações. Com isso, teremos os animes com mais avaliações (não as maiores avaliações, mas aqueles com mais QUANTIDADE DE VOTOS, sejam eles altos ou baixos).

Depois de pegar esses animes, apenas os colocaremos em ordem descrescente, colocando depois do código um ‘.sort_values(ascending=False)’

df.groupby('name')['rating'].count().sort_values(ascending=False).head(600)
Exibindo 600 animes que foram agrupados pelo nome

Depois disso, criaremos um dataframe para a média das notas: Criar o dataframe Criar uma coluna com os números de avaliações (as quantidades) e depois com a avaliação média

notas = pd.DataFrame(df.groupby('name')['rating'].mean())notas['numero de avaliações'] = pd.DataFrame(df.groupby('name')['rating'].count())notas['avaliação média'] = pd.DataFrame(df.groupby('name')['rating'].mean().round(2))notas
Nota + número de avaliações + avaliação média
fig = plt.figure(figsize=(10,5))sns.distplot(notas['rating'])plt.xlim(0,10)plt.title('Distribuição das notas (entre 0 e 10)', fontsize=15);
Selecionei apenas as positivas porque são as que contribuem para a correlação aumentar

Agora criar um novo dataframe para os generos, usando o nome e o genero dos animes no df_anime original, Depois colocar o nome do anime como indice do dataframe do genero

genero = pd.DataFrame(data=df_anime[['name','genre']])genero.set_index('name', inplace=True)genero

Agora que nossos dados já estão organizados, é hora de criar o sistema de recomendação

O primeiro passo é criar uma função para verificar o genero do anime, essa função será usado dentro do nosso sistema

def verificar_genero(lista_genero, string): #Definir a função, as variaveis são a lista de genero e uma stringif any(x in string for x in lista_genero): #Se existir um x nessa string que está dentro da lista de generosreturn True #Vai retornar Trueelse: #Se nãoreturn False #Vai retornar False

Depois da função que verifica o gênero, criamos o sistema de recomendação

#Definir a função para recomendar o anime, usando como variável o nome do anime e o número n de animes que será recomendadodef recomendar_anime(nome_do_anime, n):#Localizar o anime escolhido dentro do dataframe genero que criamos lá em cima, depois pegar os valores contidos nessa série e splitar    genero_anime = genero.loc[nome_do_anime].values[0].split(', ')#Colocar o nome dos animes do mesmo gênero que o escolhido e colocar numa lista    cols = df_anime[df_anime['genre'].apply(lambda x: verificar_genero(genero_anime, str(x)))]['name'].tolist()#Pegar o nome dos animes que foram selecionados e fazer uma      pivot_table com o df (que foi unido lá em cima) e a nota que cada usuário deu pra esse anime     matriz_de_animes = df[df['name'].isin(cols)].pivot_table(index='user_id', columns='name', values='rating')#Verificar a nota de cada usuário para aquele anime selecionado    anime_nota = matriz_de_animes[nome_do_anime]#Fazer a correlação entre os nomes dos animes e suas notas    anime_parecido = matriz_de_animes.corrwith(anime_nota)#Criar um dataframe com essa correlação e colocar numa coluna 'correlação'    anime_correlacionado = pd.DataFrame(anime_parecido, columns=['correlação'])#Pegar o dataframe de notas que criamos lá em cima e selecionar as    colunas de numero de avaliações e avaliação média    anime_correlacionado = anime_correlacionado.join(notas[['numero de avaliações', 'avaliação média']])#Excluir os dados nulos    anime_correlacionado.dropna(inplace=True)#Selecionar apenas os animes com número de avaliação maior que 5 mil, colocá-los em ordem descrescente    animes_recomendados = anime_correlacionado[anime_correlacionado['numero de avaliações'] > 3000].sort_values('correlação', ascending=False)#Criar nosso dataframe final, que vai ser a tabela dos animes recomendados    animes_recomendados= animes_recomendados.rename_axis('Animes recomendados')    print(f'Anime escolhido: {nome_do_anime}')    return animes_recomendados.head(n+1)

Tutorial

Agora mostrarei o código passo-a-passo (ou melhor, linha-a-linha), usando o anime Death Note como exemplo, pois ele foi o mais votado no conjunto de dados.

genero_anime = genero.loc['Death Note'].values[0].split(', ')
Aqui colocamos os generos do anime escolhido numa lista

Aqui pegamos essa lista e verificamos quais os animes pertencem aos mesmos gêneros (não necessariamente TODOS os gêneros, mas pelo menos um), e jogamo-os dentre de uma lista

cols = df_anime[df_anime['genre'].apply(lambda x: verificar_genero(genero_anime, str(x)))]['name'].tolist()
(Tem muito mais ao todo, esse foi só um exemplo de como fica)

Agora criamos uma matriz (um dataframe é uma matriz) com esses animes como colunas e os usuários como indice, preenchendo os valores com as notas dadas por cada usuário

matriz_de_animes = df[df['name'].isin(cols)].pivot_table(index='user_id', columns='name', values='rating')
Essas foram as notas que cada usuário deu pra cada anime

Agora selecionamos o anime que escolhemos

anime_nota = matriz_de_animes['Death Note']

E veremos os animes parecidos, e faremos isso usando .corrwith

Isso nos trará o seguinte: a correlação entre a nota dos usuários para todos os animes e a nota dos usuários para AQUELE anime em específico (aqui o Death Note)

anime_parecido = matriz_de_animes.corrwith(anime_nota)
A correlação de cada anime com o Death Note

Agora criamos um dataframe com esses animes parecidos com Death Note, usando “correlação” como coluna

anime_correlacionado = pd.DataFrame(anime_parecido, columns=['correlação'])
A correlação de cada anime com Death Note, agora em uma tabela
anime_correlacionado = anime_correlacionado.join(notas[['numero de avaliações', 'avaliação média']])

Agora colocaremos mais duas colunas: número de avaliações e avaliação média

Animes correlacionados + número de avaliações + avaliação média
anime_correlacionado.dropna(inplace=True)

Depois excluímos os dados nulos e colocamos em ordem decrescente de correlação, para que os animes mais correlacionados apareçam no topo, e utilizaremos apenas animes com mais de 4 mil votos, pois animes com poucas avaliações possuem dados insuficientes (usei 4 mil porque é basicamente 10% do anime com maior votos)

animes_recomendados = anime_correlacionado[anime_correlacionado['numero de avaliações'] > 4000].sort_values('correlação', ascending=False)

Agora chamamos o índice “name” de “Animes recomendados”

animes_recomendados= animes_recomendados.rename_axis('Animes recomendados')

Testando com outros animes

recomendar_anime('Death Note', 5)
Recomendando 5 animes para quem deu nota positiva para Death Note
recomendar_anime('Sword Art Online',5)
Recomendando 5 animes para quem deu nota positiva para Sword Art Online
recomendar_anime('Shingeki no Kyojin',5)
Recomendando 5 animes para quem deu nota positiva para Shingeki no Kyojin
recomendar_anime('Code Geass: Hangyaku no Lelouch',5)
Recomendando 5 animes para quem deu nota positiva para Code Geass: Hangyaku no Lelouch
recomendar_anime('Elfen Lied',5)
Recomendando 5 animes para quem deu nota positiva para Elfen Lied

Para terminar, farei uma recomendação utilizando um anime muito popular

recomendar_anime('Naruto',5)
Recomendando 5 animes para quem votou positivamente em Naruto

Obrigado por ler até aqui!!

--

--