Construindo um sistema de recomendação de animes em Python
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
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()
display(df_anime.info())print('=='*40)display(df_notas.info())
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()
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)
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
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);
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 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()
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')
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)
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'])
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
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)
recomendar_anime('Sword Art Online',5)
recomendar_anime('Shingeki no Kyojin',5)
recomendar_anime('Code Geass: Hangyaku no Lelouch',5)
recomendar_anime('Elfen Lied',5)
Para terminar, farei uma recomendação utilizando um anime muito popular
recomendar_anime('Naruto',5)