diff --git a/.gitignore b/.gitignore index 5a71212..9b4a811 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ data/* -references/* \ No newline at end of file +references/* +.vscode/* + +notebooks/ir/__pycache__ \ No newline at end of file diff --git a/data/external/20_newsgroups.tar.gz b/data/external/20_newsgroups.tar.gz deleted file mode 100644 index 90fbec1..0000000 Binary files a/data/external/20_newsgroups.tar.gz and /dev/null differ diff --git a/data/external/disney.zip b/data/external/disney.zip deleted file mode 100644 index 70763bb..0000000 Binary files a/data/external/disney.zip and /dev/null differ diff --git a/data/external/hbo.zip b/data/external/hbo.zip deleted file mode 100644 index 0cb2a79..0000000 Binary files a/data/external/hbo.zip and /dev/null differ diff --git a/data/external/mini_newsgroups.tar.gz b/data/external/mini_newsgroups.tar.gz deleted file mode 100644 index e08cfb7..0000000 Binary files a/data/external/mini_newsgroups.tar.gz and /dev/null differ diff --git a/data/external/netflix.zip b/data/external/netflix.zip deleted file mode 100644 index 72aceaf..0000000 Binary files a/data/external/netflix.zip and /dev/null differ diff --git a/data/external/prime.zip b/data/external/prime.zip deleted file mode 100644 index 2a5d159..0000000 Binary files a/data/external/prime.zip and /dev/null differ diff --git a/mestrado.txt b/mestrado.txt new file mode 100644 index 0000000..62402cb --- /dev/null +++ b/mestrado.txt @@ -0,0 +1,21 @@ +,P@10,P@20,P@50,P@100,MAP +sci.electronics,0.6159999999999999,0.4364999999999999,0.27180000000000015,0.18290000000000003,0.06493071922960482 +sci.space,0.768,0.639,0.48619999999999997,0.38199999999999995,0.1143772078391998 +talk.religion.misc,0.5519999999999998,0.445,0.34600000000000003,0.28730000000000006,0.11591463378185779 +comp.os.ms-windows.misc,0.606,0.49700000000000005,0.3721999999999999,0.3000000000000001,0.10501764162724292 +rec.autos,0.6970000000000005,0.5854999999999999,0.4396000000000001,0.33390000000000003,0.11019011226028123 +comp.sys.ibm.pc.hardware,0.6250000000000001,0.5429999999999999,0.4383999999999999,0.3625,0.10993849478322587 +comp.windows.x,0.6369999999999998,0.5425000000000001,0.3946000000000002,0.299,0.08237684398942668 +soc.religion.christian,0.7019999999999998,0.601,0.5042,0.44619999999999993,0.19892892193094946 +rec.sport.baseball,0.7920000000000001,0.6584999999999998,0.4820000000000001,0.3688000000000002,0.11417109332378343 +rec.sport.hockey,0.845,0.7500000000000003,0.5944,0.49690000000000006,0.17412300208308884 +talk.politics.misc,0.6579999999999998,0.5815,0.4812000000000002,0.39429999999999993,0.1352739974882998 +sci.med,0.6869999999999998,0.5399999999999998,0.3734000000000001,0.25739999999999996,0.08095142347826613 +talk.politics.mideast,0.8450000000000002,0.7830000000000003,0.6940000000000001,0.6105000000000002,0.22010328482719863 +misc.forsale,0.48700000000000004,0.3764999999999999,0.2849999999999999,0.24280000000000007,0.09401872135945151 +comp.graphics,0.5799999999999997,0.493,0.3758000000000001,0.29419999999999996,0.08780809794087686 +talk.politics.guns,0.6629999999999996,0.5519999999999997,0.45099999999999996,0.38289999999999985,0.15254688205033698 +rec.motorcycles,0.7420000000000002,0.6205000000000002,0.44120000000000004,0.3203000000000001,0.1023700132113394 +sci.crypt,0.804,0.7475000000000004,0.6539999999999998,0.5644,0.20572400923308665 +comp.sys.mac.hardware,0.5979999999999999,0.4899999999999999,0.35120000000000007,0.2827,0.09117354230801979 +alt.atheism,0.6479999999999998,0.5439999999999997,0.439,0.3604999999999998,0.13762753620690352 diff --git a/notebooks/00_preprocessing.ipynb b/notebooks/00_preprocessing.ipynb deleted file mode 100644 index 18ddb45..0000000 --- a/notebooks/00_preprocessing.ipynb +++ /dev/null @@ -1,311 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Pré-Processamento dos Documentos\n", - "\n", - "A limpeza dos dados é um processo essencial para garantir a qualidade e a confiabilidade das informações armazenadas em um banco de dados. A limpeza dos dados envolve a identificação e a correção de erros, inconsistências, duplicidades e valores ausentes nos dados. A arquitetura do armazenamento é a forma como os dados são organizados, estruturados e acessados em um banco de dados. Uma das opções de arquitetura é o formato YAML, que significa YAML Ain't Markup Language. O YAML é um formato de serialização de dados que usa uma sintaxe simples e legível para representar estruturas de dados como listas, mapas, sequências e escalares. O YAML é compatível com diversas linguagens de programação e pode ser usado para armazenar dados de forma hierárquica e flexível.\n", - "\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Importa os módulos necessários\n", - "import os # Módulo para lidar com funções do sistema operacional\n", - "import gc # Módulo para realizar coleta de lixo e gerenciamento de memória\n", - "import pickle # Módulo para serialização e desserialização de objetos em Python\n", - "import itertools # Módulo para criação de iteráveis\n", - "\n", - "import numpy as np # Módulo para trabalhar com matrizes e funções matemáticas\n", - "import pandas as pd # Módulo para trabalhar com dataframes e séries em Python\n", - "\n", - "import nltk # Módulo para processamento de linguagem natural\n", - "from nltk.corpus import stopwords # Módulo para importar lista de stopwords em inglês\n", - "from spellchecker import SpellChecker # Módulo para correção ortográfica de palavras\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "## Estruturação dos Arquivos\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Set the directory path to where the data is stored\n", - "doc_dir = '../data/emails/20_newsgroups/'\n", - "\n", - "# Create an empty list to hold the processed data\n", - "database = []\n", - "\n", - "# Iterate over each file in the directory and its subdirectories\n", - "def process_files(doc_dir): \n", - " \n", - " database = [] \n", - " \n", - " for filepath in os.listdir(doc_dir): \n", - " \n", - " for filename in os.listdir(f'{doc_dir}{filepath}'):\n", - "\n", - " # Open each file individually and read its contents\n", - " with open(os.path.join(doc_dir, filepath, filename), 'r') as f:\n", - " text_data = f.read().strip()\n", - "\n", - " # Split the header and body of the email\n", - " try:\n", - " header, body = text_data.split('\\n\\n', maxsplit=1)\n", - " except:\n", - " continue\n", - "\n", - " # Convert header to a dictionary\n", - " header_dict = {}\n", - " for line in header.split('\\n'):\n", - " try:\n", - " # Split the key and value in each header field and store them in a dictionary\n", - " key, value = line.strip().split(': ', maxsplit=1)\n", - " header_dict[key] = value\n", - " except:\n", - " # If a header field cannot be split properly, skip it and continue\n", - " continue\n", - "\n", - " # Append the processed data to the list\n", - " database.append({'filepath': filepath, \n", - " 'filename': filename,\n", - " 'body': body, \n", - " **header_dict,\n", - " 'text': text_data\n", - " })\n", - " return database\n", - "\n", - "database = process_files(doc_dir)\n", - "\n", - "# tranformation from dict -> dataframe\n", - "base_inicial = pd.DataFrame(database)\n", - "\n", - "# remove database from memory\n", - "del database\n", - "gc.collect()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "## Processamento de Texto\n", - "\n", - "
\n", - "\n", - "### Transformação de minúsculos\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Substituindo todos os caracteres que não são letras ou números por espaços em branco, exceto as barras invertidas (\\\\)\n", - "base_inicial['text'] = base_inicial['text'].replace(r'(\\\\[a-z])|([^\\w\\\\])|(\\S+\\d\\S+)', ' ', regex=True)\n", - "\n", - "# Aplicando as funções str.lower() e str.strip() simultaneamente\n", - "base_inicial['text'] = base_inicial['text'].apply(lambda x: x.lower().strip())\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "### Tokenização e Lemmatizer\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[nltk_data] Downloading package wordnet to\n", - "[nltk_data] C:\\Users\\kevin\\AppData\\Roaming\\nltk_data...\n", - "[nltk_data] Package wordnet is already up-to-date!\n", - "[nltk_data] Downloading package punkt to\n", - "[nltk_data] C:\\Users\\kevin\\AppData\\Roaming\\nltk_data...\n", - "[nltk_data] Package punkt is already up-to-date!\n" - ] - } - ], - "source": [ - "nltk.download('wordnet') # faz o download do recurso 'wordnet' do nltk\n", - "nltk.download('punkt') # faz o download do recurso 'punkt' do nltk\n", - "\n", - "# Cria um objeto 'w_tokenizer' da classe 'WhitespaceTokenizer' do nltk para tokenizar o texto por espaços em branco\n", - "w_tokenizer = nltk.tokenize.WhitespaceTokenizer()\n", - "\n", - "# Cria um objeto 'lemmatizer' da classe 'WordNetLemmatizer' do nltk para realizar a lematização das palavras\n", - "lemmatizer = nltk.WordNetLemmatizer()\n", - "\n", - "# Define a função 'lemmatizer_text' que recebe um texto como entrada, tokeniza o texto em palavras e lematiza cada palavra\n", - "def lemmatizer_text(text): \n", - " return [lemmatizer.lemmatize(w) for w in w_tokenizer.tokenize(text)]\n", - "\n", - "# Cria uma nova coluna 'tokens' na tabela 'base_inicial' que contém uma lista de tokens lematizados para cada texto\n", - "base_inicial['tokens'] = base_inicial['text'].map(lemmatizer_text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "### Remoção de Stopwords\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[nltk_data] Downloading package stopwords to\n", - "[nltk_data] C:\\Users\\kevin\\AppData\\Roaming\\nltk_data...\n", - "[nltk_data] Package stopwords is already up-to-date!\n" - ] - } - ], - "source": [ - "# Faz o download do recurso 'stopwords' do nltk\n", - "nltk.download('stopwords')\n", - "\n", - "# Define a lista de stopwords em inglês usando o módulo stopwords do nltk\n", - "stopwords = stopwords.words('english')\n", - "\n", - "# Aplica a função lambda em cada linha da coluna 'text' da tabela 'base_inicial'\n", - "# A função lambda realiza a tokenização do texto, transforma as palavras em minúsculas e remove as stopwords\n", - "base_inicial['text'] = base_inicial['text'].apply(lambda words: ' '.join(word.lower() for word in words.split() if word not in stopwords))\n", - "base_inicial['tokens'] = base_inicial['tokens'].apply(lambda words: [word.lower() for word in words if word not in stopwords])\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# # Cria uma lista de palavras a partir da lista de tokens\n", - "# w = [j for i in list(itertools.chain(base_inicial['tokens'])) for j in i]\n", - "\n", - "# # Instancia um objeto SpellChecker para correção ortográfica\n", - "# spell = SpellChecker()\n", - "\n", - "# if !os.path.isfile(): \n", - "# # Cria um dicionário com as palavras únicas da lista, faz a correção ortográfica e associa com a palavra original\n", - "# spell_checked = {word: spell.correction(word) for word in pd.Series(w).unique()}\n", - "\n", - "# # Define o caminho do arquivo que irá armazenar o dicionário serializado\n", - "# path = '../references/spellcheck.pickle'\n", - "\n", - "# # Abre o arquivo para gravação em modo binário e escreve o objeto serializado\n", - "# with open(path, 'wb') as file: \n", - "# pickle.dump(spell_checked, file)\n", - "# else: \n", - "# pass" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Export da base" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "path = '../data/processed/base_processed.parquet.gzip'\n", - "\n", - "if os.path.isfile(path): \n", - " answer = input('File already exists, do you want to overwrite? (y/n)')\n", - " if answer.lower() in ['s', 'y']:\n", - " base_inicial.to_parquet(path, compression='gzip')\n", - " else:\n", - " raise FileExistsError('File already exists')\n", - "else: \n", - " base_inicial.to_parquet(path, compression='gzip')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.1" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/01_exploratory_data.ipynb b/notebooks/01_exploratory_data.ipynb deleted file mode 100644 index 3a2118c..0000000 --- a/notebooks/01_exploratory_data.ipynb +++ /dev/null @@ -1,134 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Análise exploratória de dados\n", - "\n", - "A análise exploratória de dados é uma etapa fundamental para qualquer projeto que envolva dados. Ela consiste em examinar e estudar as características de um conjunto de dados, como sua distribuição, suas variáveis, suas relações e suas anomalias, antes de aplicar técnicas mais avançadas de estatística ou aprendizagem de máquina. O objetivo da análise exploratória de dados é obter uma compreensão mais profunda dos dados, identificar padrões, tendências e insights que possam orientar a tomada de decisão e a solução de problemas. A análise exploratória de dados pode ser realizada de forma visual, usando gráficos, tabelas e diagramas, ou de forma numérica, usando medidas de tendência central, dispersão, correlação e teste de hipóteses. A escolha dos métodos depende do tipo e da qualidade dos dados disponíveis, bem como da pergunta ou objetivo que se quer responder com a anál\n", - "\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Importa os módulos necessários\n", - "import numpy as np # Módulo para trabalhar com matrizes e funções matemáticas\n", - "import pandas as pd # Módulo para trabalhar com dataframes e séries em Python\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "## Estruturação dos Arquivos\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "base = pd.read_parquet('../data/processed/base_processed.parquet.gzip')" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[('edu', 72452),\n", - " ('ax', 47799),\n", - " ('c', 27223),\n", - " ('wa', 24454),\n", - " ('line', 23074),\n", - " ('com', 22785),\n", - " ('message', 22263),\n", - " ('subject', 21661),\n", - " ('news', 21081),\n", - " ('date', 20828),\n", - " ('id', 20598),\n", - " ('newsgroups', 20363),\n", - " ('path', 20353),\n", - " ('cmu', 20143),\n", - " ('organization', 20077),\n", - " ('apr', 19593),\n", - " ('gmt', 17725),\n", - " ('one', 16548),\n", - " ('would', 15997),\n", - " ('srv', 15120),\n", - " ('cantaloupe', 15069),\n", - " ('writes', 14685),\n", - " ('1', 13931),\n", - " ('article', 13867),\n", - " ('reference', 13740),\n", - " ('ha', 13217),\n", - " ('net', 13057),\n", - " ('x', 12252),\n", - " ('state', 11585),\n", - " ('sender', 11024)]" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import itertools\n", - "import collections\n", - "\n", - "counts_no = collections.Counter(list(itertools.chain(*base['tokens'])))\n", - "\n", - "counts_no.most_common(30)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.1" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/02_vector_model.ipynb b/notebooks/02_vector_model.ipynb deleted file mode 100644 index 9404e46..0000000 --- a/notebooks/02_vector_model.ipynb +++ /dev/null @@ -1,166 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modelo Vetorial para Recuperação da Informação\n", - "\n", - "O modelo vetorial é um dos métodos mais utilizados para recuperação de informação, que consiste em representar documentos e consultas como vetores em um espaço multidimensional. Cada dimensão corresponde a um termo do vocabulário da coleção de documentos, e o peso de cada termo é calculado com base na sua frequência e relevância. A recuperação de informação é feita comparando a similaridade entre os vetores de documentos e consultas, usando medidas como o produto escalar ou o cosseno do ângulo entre eles. O modelo vetorial permite recuperar documentos que satisfaçam parcialmente a consulta, definindo um limiar de similaridade mínimo aceitável.\n", - "\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# Importa os módulos necessários\n", - "import numpy as np # Módulo para trabalhar com matrizes e funções matemáticas\n", - "import pandas as pd # Módulo para trabalhar com dataframes e séries em Python\n", - "\n", - "from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer\n", - "from sklearn.metrics.pairwise import cosine_similarity" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- Carregue os documentos em uma lista, onde cada documento é uma string.\n", - "- Instancie o objeto CountVectorizer para transformar o texto em uma matriz de termos.\n", - "- Ajuste e transforme a matriz de termos usando os dados do conjunto de documentos." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "database = pd.read_parquet('../data/processed/base_processed.parquet.gzip')" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Calcule a similaridade entre as consultas e os documentos:\n", - "- Transforme a consulta em uma matriz de termos.\n", - "- Calcule a similaridade entre a matriz de termos da consulta e a matriz de termos dos documentos usando a função cosine_similarity." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "# Instancie o objeto CountVectorizer\n", - "vectorizer = CountVectorizer()\n", - "\n", - "# Transforme os documentos em uma matriz de termos\n", - "term_matrix = vectorizer.fit_transform(database['text'])\n" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "with open('../data/emails/mini_newsgroups/alt.atheism/51126', 'r') as file: \n", - " query = file.read()\n", - "\n", - "# Transforme a consulta em uma matriz de termos\n", - "query_vec = vectorizer.transform([query])\n", - "\n", - "# Calcule a similaridade entre a consulta e os documentos\n", - "similarity_scores = cosine_similarity(query_vec, term_matrix)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Recupere os documentos mais relevantes:\n", - "- Classifique os documentos com base em suas pontuações de similaridade.\n", - "- Recupere os documentos mais relevantes." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "53761 - Score: 0.7457440977032672\n", - "53194 - Score: 0.7438271604938276\n", - "51126 - Score: 0.7377253439645206\n", - "51273 - Score: 0.7142800526817401\n", - "53142 - Score: 0.7017401753421795\n", - "51160 - Score: 0.7001157311738098\n", - "54283 - Score: 0.6967342757044788\n", - "53175 - Score: 0.6939201375033768\n", - "53172 - Score: 0.6869464497590783\n", - "53143 - Score: 0.6855706297881646\n", - "39619 - Score: 0.660578259075816\n", - "53763 - Score: 0.6472975596607513\n", - "53144 - Score: 0.6401055804697865\n", - "101656 - Score: 0.6390178833325828\n", - "53762 - Score: 0.638752210144836\n", - "178451 - Score: 0.6386838743880491\n", - "178846 - Score: 0.6361329120147892\n", - "83796 - Score: 0.6361329120147892\n", - "105113 - Score: 0.6293008682666752\n", - "54241 - Score: 0.6264456087861376\n" - ] - } - ], - "source": [ - "# Classifique os documentos com base em suas pontuações de similaridade\n", - "ranked_docs = sorted(\n", - " list(enumerate(similarity_scores[0])),\n", - " key=lambda x: x[1],\n", - " reverse=True\n", - ")\n", - "\n", - "# Recupere os documentos mais relevantes\n", - "for i, score in ranked_docs[0:20]:\n", - " print(database.iloc[i]['filename'], \" - Score:\", score)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.1" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/03_tf_idf_model.ipynb b/notebooks/03_tf_idf_model.ipynb deleted file mode 100644 index 9f7cd1d..0000000 --- a/notebooks/03_tf_idf_model.ipynb +++ /dev/null @@ -1,130 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modelo Vetorial para Recuperação da Informação\n", - "\n", - "O modelo vetorial é um dos métodos mais utilizados para recuperação de informação, que consiste em representar documentos e consultas como vetores em um espaço multidimensional. Cada dimensão corresponde a um termo do vocabulário da coleção de documentos, e o peso de cada termo é calculado com base na sua frequência e relevância. A recuperação de informação é feita comparando a similaridade entre os vetores de documentos e consultas, usando medidas como o produto escalar ou o cosseno do ângulo entre eles. O modelo vetorial permite recuperar documentos que satisfaçam parcialmente a consulta, definindo um limiar de similaridade mínimo aceitável.\n", - "\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Importa os módulos necessários\n", - "import numpy as np # Módulo para trabalhar com matrizes e funções matemáticas\n", - "import pandas as pd # Módulo para trabalhar com dataframes e séries em Python\n", - "import os\n", - "\n", - "from sklearn.feature_extraction.text import CountVectorizer\n", - "from sklearn.metrics.pairwise import cosine_similarity" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- Carregue os documentos em uma lista, onde cada documento é uma string.\n", - "- Instancie o objeto CountVectorizer para transformar o texto em uma matriz de termos.\n", - "- Ajuste e transforme a matriz de termos usando os dados do conjunto de documentos." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Calcule a similaridade entre as consultas e os documentos:\n", - "- Transforme a consulta em uma matriz de termos.\n", - "- Calcule a similaridade entre a matriz de termos da consulta e a matriz de termos dos documentos usando a função cosine_similarity." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Instancie o objeto CountVectorizer\n", - "vectorizer = CountVectorizer()\n", - "\n", - "# Transforme os documentos em uma matriz de termos\n", - "term_matrix = vectorizer.fit_transform(database)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "query = \"graphics\"\n", - "\n", - "# Transforme a consulta em uma matriz de termos\n", - "query_vec = vectorizer.transform([query])\n", - "\n", - "# Calcule a similaridade entre a consulta e os documentos\n", - "similarity_scores = cosine_similarity(query_vec, term_matrix)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Recupere os documentos mais relevantes:\n", - "- Classifique os documentos com base em suas pontuações de similaridade.\n", - "- Recupere os documentos mais relevantes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Classifique os documentos com base em suas pontuações de similaridade\n", - "ranked_docs = sorted(\n", - " list(enumerate(similarity_scores[0])),\n", - " key=lambda x: x[1],\n", - " reverse=True\n", - ")\n", - "\n", - "# Recupere os documentos mais relevantes\n", - "for i, score in ranked_docs:\n", - " print(docs[i], \" - Score:\", score)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.1" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/backup/00_preprocessing.ipynb b/notebooks/backup/00_preprocessing.ipynb deleted file mode 100644 index d484ba9..0000000 --- a/notebooks/backup/00_preprocessing.ipynb +++ /dev/null @@ -1,303 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Pré-Processamento dos Documentos\n", - "\n", - "A limpeza dos dados é um processo essencial para garantir a qualidade e a confiabilidade das informações armazenadas em um banco de dados. A limpeza dos dados envolve a identificação e a correção de erros, inconsistências, duplicidades e valores ausentes nos dados. A arquitetura do armazenamento é a forma como os dados são organizados, estruturados e acessados em um banco de dados. Uma das opções de arquitetura é o formato YAML, que significa YAML Ain't Markup Language. O YAML é um formato de serialização de dados que usa uma sintaxe simples e legível para representar estruturas de dados como listas, mapas, sequências e escalares. O YAML é compatível com diversas linguagens de programação e pode ser usado para armazenar dados de forma hierárquica e flexível.\n", - "\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Importa os módulos necessários\n", - "import os # Módulo para lidar com funções do sistema operacional\n", - "import gc # Módulo para realizar coleta de lixo e gerenciamento de memória\n", - "import yaml # Módulo para trabalhar com arquivos YAML\n", - "import pickle # Módulo para serialização e desserialização de objetos em Python\n", - "import itertools # Módulo para criação de iteráveis\n", - "\n", - "import numpy as np # Módulo para trabalhar com matrizes e funções matemáticas\n", - "import pandas as pd # Módulo para trabalhar com dataframes e séries em Python\n", - "\n", - "import nltk # Módulo para processamento de linguagem natural\n", - "from nltk.corpus import stopwords # Módulo para importar lista de stopwords em inglês\n", - "from spellchecker import SpellChecker # Módulo para correção ortográfica de palavras\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "## Estruturação dos Arquivos\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Set the directory path to where the data is stored\n", - "doc_dir = '../data/emails/20_newsgroups/'\n", - "\n", - "# Create an empty list to hold the processed data\n", - "database = []\n", - "\n", - "# Iterate over each file in the directory and its subdirectories\n", - "for filepath in os.listdir(doc_dir): \n", - " for filename in os.listdir(f'{doc_dir}{filepath}'):\n", - "\n", - " # Open each file individually and read its contents\n", - " with open(os.path.join(doc_dir, filepath, filename), 'r') as f:\n", - " text_data = f.read().strip()\n", - " \n", - " # Split the header and body of the email\n", - " try:\n", - " header, body = text_data.split('\\n\\n', maxsplit=1)\n", - " except:\n", - " continue\n", - " \n", - " # Convert header to a dictionary\n", - " header_dict = {}\n", - " for line in header.split('\\n'):\n", - " try:\n", - " # Split the key and value in each header field and store them in a dictionary\n", - " key, value = line.strip().split(': ', maxsplit=1)\n", - " header_dict[key] = value\n", - " except:\n", - " # If a header field cannot be split properly, skip it and continue\n", - " continue\n", - " \n", - " # Append the processed data to the list\n", - " database.append({'filepath': filepath, \n", - " 'filename': filename,\n", - " 'text': body, \n", - " **header_dict\n", - " })\n", - "\n", - "# tranformation from dict -> dataframe\n", - "base_inicial = pd.DataFrame(database)\n", - "\n", - "# remove database from memory\n", - "del database\n", - "gc.collect()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "## Processamento de Texto\n", - "\n", - "
\n", - "\n", - "### Transformação de minúsculos\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Substituindo todos os caracteres que não são letras ou números por espaços em branco, exceto as barras invertidas (\\\\)\n", - "base_inicial['text'] = base_inicial['text'].replace(r'(\\\\[a-z])|([^\\w\\\\])', ' ', regex=True)\n", - "\n", - "# Aplicando as funções str.lower() e str.strip() simultaneamente\n", - "base_inicial['text'] = base_inicial['text'].apply(lambda x: x.lower().strip())\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "### Remoção de Stopwords\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[nltk_data] Downloading package stopwords to\n", - "[nltk_data] C:\\Users\\kevin\\AppData\\Roaming\\nltk_data...\n", - "[nltk_data] Package stopwords is already up-to-date!\n" - ] - } - ], - "source": [ - "# Faz o download do recurso 'stopwords' do nltk\n", - "nltk.download('stopwords')\n", - "\n", - "# Define a lista de stopwords em inglês usando o módulo stopwords do nltk\n", - "stopwords = stopwords.words('english')\n", - "\n", - "# Aplica a função lambda em cada linha da coluna 'text' da tabela 'base_inicial'\n", - "# A função lambda realiza a tokenização do texto, transforma as palavras em minúsculas e remove as stopwords\n", - "base_inicial['text'] = base_inicial['text'].apply(lambda words: ' '.join(word.lower() for word in words.split() if word not in stopwords))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "### Tokenização e Lemmatizer\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[nltk_data] Downloading package wordnet to\n", - "[nltk_data] C:\\Users\\kevin\\AppData\\Roaming\\nltk_data...\n", - "[nltk_data] Package wordnet is already up-to-date!\n", - "[nltk_data] Downloading package punkt to\n", - "[nltk_data] C:\\Users\\kevin\\AppData\\Roaming\\nltk_data...\n", - "[nltk_data] Package punkt is already up-to-date!\n" - ] - } - ], - "source": [ - "nltk.download('wordnet') # faz o download do recurso 'wordnet' do nltk\n", - "nltk.download('punkt') # faz o download do recurso 'punkt' do nltk\n", - "\n", - "# Cria um objeto 'w_tokenizer' da classe 'WhitespaceTokenizer' do nltk para tokenizar o texto por espaços em branco\n", - "w_tokenizer = nltk.tokenize.WhitespaceTokenizer()\n", - "\n", - "# Cria um objeto 'lemmatizer' da classe 'WordNetLemmatizer' do nltk para realizar a lematização das palavras\n", - "lemmatizer = nltk.WordNetLemmatizer()\n", - "\n", - "# Define a função 'lemmatizer_text' que recebe um texto como entrada, tokeniza o texto em palavras e lematiza cada palavra\n", - "def lemmatizer_text(text): \n", - " return [lemmatizer.lemmatize(w) for w in w_tokenizer.tokenize(text)]\n", - "\n", - "# Cria uma nova coluna 'tokens' na tabela 'base_inicial' que contém uma lista de tokens lematizados para cada texto\n", - "base_inicial['tokens'] = base_inicial['text'].map(lemmatizer_text)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# # Cria uma lista de palavras a partir da lista de tokens\n", - "# w = [j for i in list(itertools.chain(base_inicial['tokens'])) for j in i]\n", - "\n", - "# # Instancia um objeto SpellChecker para correção ortográfica\n", - "# spell = SpellChecker()\n", - "\n", - "# if !os.path.isfile(): \n", - "# # Cria um dicionário com as palavras únicas da lista, faz a correção ortográfica e associa com a palavra original\n", - "# spell_checked = {word: spell.correction(word) for word in pd.Series(w).unique()}\n", - "\n", - "# # Define o caminho do arquivo que irá armazenar o dicionário serializado\n", - "# path = '../references/spellcheck.pickle'\n", - "\n", - "# # Abre o arquivo para gravação em modo binário e escreve o objeto serializado\n", - "# with open(path, 'wb') as file: \n", - "# pickle.dump(spell_checked, file)\n", - "# else: \n", - "# pass" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Export da base" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "path = '../data/processed/base_processed.parquet.gzip'\n", - "\n", - "if os.path.isfile(path): \n", - " answer = input('File already exists, do you want to overwrite? (y/n)')\n", - " if answer.lower() in ['s', 'y']:\n", - " base_inicial.to_parquet(path, compression='gzip')\n", - " else:\n", - " raise FileExistsError('File already exists')\n", - "else: \n", - " base_inicial.to_parquet(path, compression='gzip')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.1" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/idf.ipynb b/notebooks/idf.ipynb deleted file mode 100644 index e723d01..0000000 --- a/notebooks/idf.ipynb +++ /dev/null @@ -1,138 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "# Conjunto de documentos\n", - "documents = [\n", - " \"to do is to be to be is to do\",\n", - " \"to be or not to be i am not what i am\",\n", - " \"i think therefore i am do be do be do\",\n", - " \"do do do da da da let it be let it be\"\n", - "]\n", - "\n", - "base = pd.DataFrame({'docs': documents})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "# Função que calcula o log na base 2 da frequência das palavras em um documento\n", - "def log_freq(doc):\n", - " \"\"\"\n", - " Calcula o log na base 2 da frequência das palavras em um documento.\n", - "\n", - " Args:\n", - " doc (str): O documento para o qual o log de frequência será calculado.\n", - "\n", - " Returns:\n", - " pandas.Series: A série de palavras e seus logs de frequência.\n", - "\n", - " \"\"\"\n", - " words = doc.split(' ') # Separar o documento em palavras\n", - " freq = pd.Series(words).value_counts() # Contar a frequência de cada palavra\n", - " log_freq = 1 + np.log2(freq) # Calcular o log na base 2 da frequência\n", - " return log_freq\n", - "\n", - "# Função que aplica a função log_freq a cada documento do dataframe\n", - "def ri_tf(dataframe: pd.DataFrame, column: str):\n", - " \"\"\"Calcula o term frequency relativo para cada documento em uma coluna do dataframe.\n", - " Args:\n", - " dataframe (pandas.DataFrame): O dataframe com a coluna desejada.\n", - " column (str): O nome da coluna que contém os documentos.\n", - "\n", - " Returns:\n", - " pandas.DataFrame: O dataframe com os logs de frequência para cada palavra em cada documento.\n", - "\n", - " \"\"\"\n", - " return dataframe[column].apply(log_freq).T\n", - "\n", - "# Função que calcula o IDF (inverse document frequency) de cada termo do corpus\n", - "def ri_idf(dataframe: pd.DataFrame, column: str): \n", - " \"\"\"Calcula o inverse document frequency relativo para cada palavra em uma coluna do dataframe.\n", - " Args:\n", - " dataframe (pandas.DataFrame): O dataframe com a coluna desejada.\n", - " column (str): O nome da coluna que contém os documentos.\n", - "\n", - " Returns:\n", - " dict: Um dicionário com as palavras e seus respectivos IDF relativos.\n", - "\n", - " \"\"\"\n", - " words = [phrase.split(' ') for phrase in dataframe[column]] # Separar cada frase em palavras\n", - "\n", - " df_t = pd.Series([i for j in words for i in j]).value_counts() # Contar a frequência de cada termo\n", - "\n", - " n_i = {} # Dicionário que armazenará a quantidade de documentos que contém cada termo\n", - " idf = {} # Dicionário que armazenará o IDF de cada termo\n", - "\n", - " # Calcular n_i e IDF para cada termo do corpus\n", - " for term in df_t.index: \n", - " n_i[term] = dataframe[column].map(lambda x: term in x).sum() # Quantidade de documentos que contém o termo\n", - " idf[term] = np.log2(dataframe.shape[0]/n_i[term]) # IDF do termo\n", - "\n", - " return idf\n", - "\n", - "# Função que calcula o TF-IDF de cada termo em cada documento do corpus\n", - "def tfidf(dataframe, column): \n", - " \"\"\"Calcula o TF-IDF relativo para cada palavra em cada documento de uma coluna do dataframe.\n", - " Args:\n", - " dataframe (pandas.DataFrame): O dataframe com a coluna desejada.\n", - " column (str): O nome da coluna que contém os documentos.\n", - "\n", - " Ret'urns:\n", - " pandas.DataFrame: O dataframe com os TF-IDFs relativos para cada palavra em cada documento.\n", - "\n", - " \"\"\"'\n", - " tf = ri_tf(dataframe=dataframe, column=column) # Calcular o TF de cada termo em cada documento\n", - " idf = ri_idf(dataframe=dataframe, column=column) # Calcular o IDF de cada termo\n", - "\n", - " df = {}\n", - " for word in tf.index: # Iterar por cada termo do corpus\n", - " df[word] = tf.loc[word]*idf[word] # Calcular o TF-IDF do termo em cada documento\n", - "\n", - " return pd.DataFrame.from_dict(df, orient='index') # Retornar os resultados em um DataFrame\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.1" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/information_retrieval_emails.ipynb b/notebooks/information_retrieval_emails.ipynb new file mode 100644 index 0000000..da918c0 --- /dev/null +++ b/notebooks/information_retrieval_emails.ipynb @@ -0,0 +1,861 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pré-Processamento dos Documentos\n", + "\n", + "A limpeza dos dados é um processo essencial para garantir a qualidade e a confiabilidade das informações armazenadas em um banco de dados. A limpeza dos dados envolve a identificação e a correção de erros, inconsistências, duplicidades e valores ausentes nos dados. A arquitetura do armazenamento é a forma como os dados são organizados, estruturados e acessados em um banco de dados. Uma das opções de arquitetura é o formato YAML, que significa YAML Ain't Markup Language. O YAML é um formato de serialização de dados que usa uma sintaxe simples e legível para representar estruturas de dados como listas, mapas, sequências e escalares. O YAML é compatível com diversas linguagens de programação e pode ser usado para armazenar dados de forma hierárquica e flexível.\n", + "\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Importa os módulos necessários\n", + "import os # Módulo para lidar com funções do sistema operacional\n", + "import gc # Módulo para realizar coleta de lixo e gerenciamento de memória\n", + "\n", + "import numpy as np # Módulo para trabalhar com matrizes e funções matemáticas\n", + "import pandas as pd # Módulo para trabalhar com dataframes e séries em Python\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "## Estruturação dos Arquivos\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# caminho das queries \n", + "query_path = '../data/emails/mini_newsgroups/'\n", + "\n", + "# caminho dos documentos\n", + "docs_path = '../data/emails/20_newsgroups/'\n", + "\n", + "# Iterate over each file in the directory and its subdirectories\n", + "def process_files(doc_dir: str): \n", + " \n", + " database = [] \n", + " \n", + " for filepath in os.listdir(doc_dir): \n", + " \n", + " for filename in os.listdir(f'{doc_dir}{filepath}'):\n", + "\n", + " # Open each file individually and read its contents\n", + " with open(os.path.join(doc_dir, filepath, filename), 'r') as f:\n", + " text_data = f.read().strip()\n", + "\n", + " # Split the header and body of the email\n", + " try:\n", + " header, body = text_data.split('\\n\\n', maxsplit=1)\n", + " except:\n", + " continue\n", + "\n", + " # Convert header to a dictionary\n", + " # header_dict = {}\n", + " # for line in header.split('\\n'):\n", + " # try:\n", + " # # Split the key and value in each header field and store them in a dictionary\n", + " # key, value = line.strip().split(': ', maxsplit=1)\n", + " # header_dict[key] = value\n", + " # except:\n", + " # # If a header field cannot be split properly, skip it and continue\n", + " # continue\n", + "\n", + " # Append the processed data to the list\n", + "\n", + " database.append({'filepath': filepath, \n", + " 'filename': filename,\n", + " 'body': body, \n", + " # **header_dict,\n", + " # 'text': text_data\n", + " })\n", + " return database\n", + "\n", + "# tranformation from dict -> dataframe\n", + "base_doc = pd.DataFrame(process_files(docs))\n", + "\n", + "base_doc = pd.DataFrame(process_files(doc_dir))\n", + "\n", + "# remove database from memory\n", + "gc.collect()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "ename": "PermissionError", + "evalue": "[Errno 13] Permission denied: '../data/emails/20_newsgroups/alt.atheism'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mPermissionError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[5], line 8\u001b[0m\n\u001b[0;32m 5\u001b[0m docs_path \u001b[39m=\u001b[39m \u001b[39m'\u001b[39m\u001b[39m../data/emails/20_newsgroups/\u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 7\u001b[0m \u001b[39m# Import das bases\u001b[39;00m\n\u001b[1;32m----> 8\u001b[0m database_docs \u001b[39m=\u001b[39m read_files(docs_path)\n\u001b[0;32m 9\u001b[0m database_query \u001b[39m=\u001b[39m read_files(query_path)\n\u001b[0;32m 11\u001b[0m base_docs \u001b[39m=\u001b[39m pd\u001b[39m.\u001b[39mDataFrame(database_docs)\n", + "Cell \u001b[1;32mIn[2], line 3\u001b[0m, in \u001b[0;36mread_files\u001b[1;34m(doc_dir)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mread_files\u001b[39m(doc_dir):\n\u001b[0;32m 2\u001b[0m \u001b[39m# Use a list comprehension to get a list of file paths\u001b[39;00m\n\u001b[1;32m----> 3\u001b[0m database \u001b[39m=\u001b[39m [{\u001b[39m'\u001b[39;49m\u001b[39mfilepath\u001b[39;49m\u001b[39m'\u001b[39;49m: doc_dir,\n\u001b[0;32m 4\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mfilename\u001b[39;49m\u001b[39m'\u001b[39;49m: filename,\n\u001b[0;32m 5\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mtext\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39mopen\u001b[39;49m(os\u001b[39m.\u001b[39;49mpath\u001b[39m.\u001b[39;49mjoin(doc_dir, filename), \u001b[39m'\u001b[39;49m\u001b[39mr\u001b[39;49m\u001b[39m'\u001b[39;49m)\u001b[39m.\u001b[39;49mread()\u001b[39m.\u001b[39;49mstrip()}\n\u001b[0;32m 6\u001b[0m \u001b[39mfor\u001b[39;49;00m filename \u001b[39min\u001b[39;49;00m os\u001b[39m.\u001b[39;49mlistdir(doc_dir)]\n\u001b[0;32m 8\u001b[0m \u001b[39mreturn\u001b[39;00m database\n", + "Cell \u001b[1;32mIn[2], line 5\u001b[0m, in \u001b[0;36m\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mread_files\u001b[39m(doc_dir):\n\u001b[0;32m 2\u001b[0m \u001b[39m# Use a list comprehension to get a list of file paths\u001b[39;00m\n\u001b[0;32m 3\u001b[0m database \u001b[39m=\u001b[39m [{\u001b[39m'\u001b[39m\u001b[39mfilepath\u001b[39m\u001b[39m'\u001b[39m: doc_dir,\n\u001b[0;32m 4\u001b[0m \u001b[39m'\u001b[39m\u001b[39mfilename\u001b[39m\u001b[39m'\u001b[39m: filename,\n\u001b[1;32m----> 5\u001b[0m \u001b[39m'\u001b[39m\u001b[39mtext\u001b[39m\u001b[39m'\u001b[39m: \u001b[39mopen\u001b[39;49m(os\u001b[39m.\u001b[39;49mpath\u001b[39m.\u001b[39;49mjoin(doc_dir, filename), \u001b[39m'\u001b[39;49m\u001b[39mr\u001b[39;49m\u001b[39m'\u001b[39;49m)\u001b[39m.\u001b[39mread()\u001b[39m.\u001b[39mstrip()}\n\u001b[0;32m 6\u001b[0m \u001b[39mfor\u001b[39;00m filename \u001b[39min\u001b[39;00m os\u001b[39m.\u001b[39mlistdir(doc_dir)]\n\u001b[0;32m 8\u001b[0m \u001b[39mreturn\u001b[39;00m database\n", + "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python311\\site-packages\\IPython\\core\\interactiveshell.py:282\u001b[0m, in \u001b[0;36m_modified_open\u001b[1;34m(file, *args, **kwargs)\u001b[0m\n\u001b[0;32m 275\u001b[0m \u001b[39mif\u001b[39;00m file \u001b[39min\u001b[39;00m {\u001b[39m0\u001b[39m, \u001b[39m1\u001b[39m, \u001b[39m2\u001b[39m}:\n\u001b[0;32m 276\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mValueError\u001b[39;00m(\n\u001b[0;32m 277\u001b[0m \u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mIPython won\u001b[39m\u001b[39m'\u001b[39m\u001b[39mt let you open fd=\u001b[39m\u001b[39m{\u001b[39;00mfile\u001b[39m}\u001b[39;00m\u001b[39m by default \u001b[39m\u001b[39m\"\u001b[39m\n\u001b[0;32m 278\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mas it is likely to crash IPython. If you know what you are doing, \u001b[39m\u001b[39m\"\u001b[39m\n\u001b[0;32m 279\u001b[0m \u001b[39m\"\u001b[39m\u001b[39myou can use builtins\u001b[39m\u001b[39m'\u001b[39m\u001b[39m open.\u001b[39m\u001b[39m\"\u001b[39m\n\u001b[0;32m 280\u001b[0m )\n\u001b[1;32m--> 282\u001b[0m \u001b[39mreturn\u001b[39;00m io_open(file, \u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n", + "\u001b[1;31mPermissionError\u001b[0m: [Errno 13] Permission denied: '../data/emails/20_newsgroups/alt.atheism'" + ] + } + ], + "source": [ + "# caminho das queries \n", + "query_path = '../data/emails/mini_newsgroups/'\n", + "\n", + "# caminho dos documentos\n", + "docs_path = '../data/emails/20_newsgroups/'\n", + "\n", + "# Import das bases\n", + "database_docs = read_files(docs_path)\n", + "database_query = read_files(query_path)\n", + "\n", + "base_docs = pd.DataFrame(database_docs)\n", + "base_query = pd.DataFrame(database_query)\n", + "\n", + "# Marcação das bases\n", + "base_docs['tag'] = 'doc'\n", + "base_query['tag'] = 'query'\n", + "\n", + "# junção das bases \n", + "base = pd.concat([base_docs, base_query])\n", + "base.reset_index(drop=True, inplace=True)\n", + "\n", + "del base_docs, base_query, database_docs, database_query\n", + "gc.collect()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
filepathfilenametexttag
0../data/emails/20_newsgroups/misc.forsale/70337Path: cantaloupe.srv.cs.cmu.edu!rochester!udel...doc
1../data/emails/20_newsgroups/misc.forsale/74150Path: cantaloupe.srv.cs.cmu.edu!crabapple.srv....doc
2../data/emails/20_newsgroups/misc.forsale/74720Path: cantaloupe.srv.cs.cmu.edu!das-news.harva...doc
3../data/emails/20_newsgroups/misc.forsale/74721Newsgroups: misc.forsale\\nPath: cantaloupe.srv...doc
4../data/emails/20_newsgroups/misc.forsale/74722Path: cantaloupe.srv.cs.cmu.edu!crabapple.srv....doc
...............
1095../data/emails/mini_newsgroups/misc.forsale/76927Xref: cantaloupe.srv.cs.cmu.edu misc.wanted:31...query
1096../data/emails/mini_newsgroups/misc.forsale/76936Newsgroups: misc.forsale\\nSubject: WANTED LCD ...query
1097../data/emails/mini_newsgroups/misc.forsale/76937Newsgroups: ingr.forsale,hsv.forsale,misc.fors...query
1098../data/emails/mini_newsgroups/misc.forsale/76940Newsgroups: misc.forsale\\nPath: cantaloupe.srv...query
1099../data/emails/mini_newsgroups/misc.forsale/76945Xref: cantaloupe.srv.cs.cmu.edu comp.sys.mac.h...query
\n", + "

1100 rows × 4 columns

\n", + "
" + ], + "text/plain": [ + " filepath filename \n", + "0 ../data/emails/20_newsgroups/misc.forsale/ 70337 \\\n", + "1 ../data/emails/20_newsgroups/misc.forsale/ 74150 \n", + "2 ../data/emails/20_newsgroups/misc.forsale/ 74720 \n", + "3 ../data/emails/20_newsgroups/misc.forsale/ 74721 \n", + "4 ../data/emails/20_newsgroups/misc.forsale/ 74722 \n", + "... ... ... \n", + "1095 ../data/emails/mini_newsgroups/misc.forsale/ 76927 \n", + "1096 ../data/emails/mini_newsgroups/misc.forsale/ 76936 \n", + "1097 ../data/emails/mini_newsgroups/misc.forsale/ 76937 \n", + "1098 ../data/emails/mini_newsgroups/misc.forsale/ 76940 \n", + "1099 ../data/emails/mini_newsgroups/misc.forsale/ 76945 \n", + "\n", + " text tag \n", + "0 Path: cantaloupe.srv.cs.cmu.edu!rochester!udel... doc \n", + "1 Path: cantaloupe.srv.cs.cmu.edu!crabapple.srv.... doc \n", + "2 Path: cantaloupe.srv.cs.cmu.edu!das-news.harva... doc \n", + "3 Newsgroups: misc.forsale\\nPath: cantaloupe.srv... doc \n", + "4 Path: cantaloupe.srv.cs.cmu.edu!crabapple.srv.... doc \n", + "... ... ... \n", + "1095 Xref: cantaloupe.srv.cs.cmu.edu misc.wanted:31... query \n", + "1096 Newsgroups: misc.forsale\\nSubject: WANTED LCD ... query \n", + "1097 Newsgroups: ingr.forsale,hsv.forsale,misc.fors... query \n", + "1098 Newsgroups: misc.forsale\\nPath: cantaloupe.srv... query \n", + "1099 Xref: cantaloupe.srv.cs.cmu.edu comp.sys.mac.h... query \n", + "\n", + "[1100 rows x 4 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "base" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "## Processamento de Texto\n", + "\n", + "
\n", + "\n", + "### Transformação de minúsculos\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# (\\[a-z]): para encontrar todos os caracteres que começam com uma barra invertida () seguida por uma letra minúscula (a-z);\n", + "# ([^\\w\\]): para encontrar todos os caracteres que não são letras, números ou barras invertidas ();\n", + "# (\\S+\\d\\S+): para encontrar todos os trechos de texto que contêm um ou mais caracteres não brancos (\\S), \n", + "# seguidos por um dígito (\\d), seguidos por mais um ou mais caracteres não brancos (\\S).\n", + "base['post'] = base['text'].replace(r'(\\\\[a-z])|([^\\w\\\\])|(\\S+\\d\\S+)', ' ', regex=True)\n", + "\n", + "\n", + "# Aplicando as funções str.lower() e str.strip() simultaneamente\n", + "base['post'] = base['post'].apply(lambda x: x.lower().strip())\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "### Tokenização e Lemmatizer\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 path cantaloupe srv c cmu edu rochester udel g...\n", + "1 path from myoakam ci ohio state edu micah r yo...\n", + "2 path from maureen l eagle newsgroup misc forsa...\n", + "3 newsgroup misc forsale path from mike diack mi...\n", + "4 path from jvinson xsoft xerox com jeffrey a vi...\n", + " ... \n", + "1095 xref cantaloupe srv c cmu edu newsgroup misc w...\n", + "1096 newsgroup misc forsale subject want lcd overhe...\n", + "1097 newsgroup ingr forsale hsv forsale misc forsal...\n", + "1098 newsgroup misc forsale path cantaloupe srv c c...\n", + "1099 xref cantaloupe srv c cmu edu path from scott ...\n", + "Name: post, Length: 1100, dtype: object" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ir.preprocessing import lemmatize_word\n", + "\n", + "base['post'].apply(lambda x: ' '.join([lemmatize_word(word.lower()) for word in x.split()]))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from ir import tf_idf\n", + "\n", + "weight = tf_idf.tfidf(base, 'post').iloc[1:]\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Identificação das query / docs" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "d_index = base.query('tag==\"doc\"').index\n", + "q_index = base.query('tag==\"query\"').index" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "\n", + "similarity = dict()\n", + "index_matrix = dict()\n", + "rank_matrix = dict()\n", + "\n", + "for j in q_index:\n", + " for i in d_index: \n", + " numerator = np.sum( weight.loc[:,i] * weight.loc[: , j])\n", + " denominator = np.linalg.norm(weight.loc[:,i])*np.linalg.norm(weight.loc[:,1019])\n", + " similarity[i] = numerator/denominator\n", + "\n", + "\n", + " rank_matrix[j] = pd.DataFrame(similarity.values(), columns=['rank']).sort_values(by='rank', ascending=False).head(10).values.tolist()\n", + " index_matrix[j] = pd.DataFrame(similarity.values(), columns=['rank']).sort_values(by='rank', ascending=False).head(10).index.to_list()\n", + "\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
1000100110021003100410051006100710081009...1090109110921093109410951096109710981099
0[0.6422584945103841][0.668743974268336][1.008422835773131][0.6661363151210968][0.8642602181331219][0.7417012424961686][0.7315442686011049][0.9460833202189584][0.9746037822064916][0.6869123604148133]...[1.1523833456270243][0.7956243694544436][1.7802942925566625][0.5158809769289813][0.5234239788184921][0.8038496213979783][0.6360279187397326][1.1259044435676868][1.0616162379284384][0.6784846202202287]
1[0.2873172889001637][0.17456679249116952][0.6332470445844255][0.09678526898308248][0.537824913541413][0.2820702175096787][0.6513139955405113][0.3597970619791958][0.24900044829809295][0.14368626326554504]...[0.42456045151374616][0.13839258426540738][0.1920094056436893][0.4608108432879023][0.46754863207460967][0.635942460180357][0.2673113797648495][0.09064837068978648][0.19426228087688888][0.17427577670397354]
2[0.19584221062188678][0.1319897299803032][0.08491363433325547][0.08737476357264361][0.47799964699339453][0.20691840972537448][0.0713300455225047][0.18078764107798664][0.11093369090167131][0.09580043962698802]...[0.39006994534248846][0.09497709201044909][0.17705994384084703][0.1911406606344065][0.19471247373710213][0.6126542869398587][0.06788233871189489][0.07823702305427702][0.17319361461646068][0.07447944169226609]
3[0.13903116434341475][0.10146093652625415][0.08181050400129704][0.07523129301857605][0.18045913119908524][0.20260530333012666][0.054813355262948726][0.16776960486431058][0.10031171790840715][0.07826399750895882]...[0.2509003350905366][0.08889171346193057][0.16005754765559338][0.053302833660593664][0.053302833660593664][0.2514441707424639][0.06610690581400588][0.07732220562999899][0.17148587761263784][0.04757751873987604]
4[0.1129173777348391][0.08606053698831793][0.07547569629425901][0.06970411933742698][0.17135272355687187][0.20091765863852995][0.05469328989879712][0.16743762657989103][0.09811418008672529][0.07488273104320195]...[0.1456676671253771][0.08031528425526144][0.1591723994058402][0.05187299077727413][0.04876597436921601][0.24540764374264745][0.064867860088572][0.07458882712067232][0.1268769662336863][0.04675221931850179]
5[0.11038217708080131][0.08212202043548315][0.07432402899651668][0.06957535331034335][0.11414112796082569][0.09766529639479365][0.05342228639423025][0.11293055713592527][0.09499508593239105][0.07351917900522721]...[0.1356468036645224][0.07846954905194828][0.15849239426755599][0.04462161696830591][0.04472544383210234][0.2051558258313172][0.06082789393903154][0.06855422701443233][0.08964124013628569][0.043706874358644336]
6[0.10833574384967115][0.08201334401786602][0.07412645164725896][0.06759708576024981][0.09130509649851562][0.08716787810262448][0.0497899252125642][0.09487498299853164][0.08477817264550953][0.06935710726422203]...[0.13395484290588885][0.07651073480529516][0.14400058142958222][0.04329534404644248][0.04462161696830591][0.10677052218571517][0.05830025911923057][0.06718300838215913][0.08292563259802246][0.04288337427490202]
7[0.09458970277908005][0.08147133735902357][0.07118734960890093][0.06755116098175724][0.08423578168208135][0.08564850087282809][0.04712198065394311][0.08900364470414873][0.0845410509094711][0.06696048135598477]...[0.12832106795670173][0.0742473891955823][0.1337598250032541][0.03987922316585672][0.04329534404644248][0.08913041300626738][0.05705502660231897][0.06310045914518085][0.07445114613146177][0.04255318648575154]
8[0.06683185055384143][0.08107329389309223][0.06992507760853946][0.0665144106585746][0.07732918671239193][0.07767329982049115][0.04496152399683175][0.07886393345966285][0.08159272098702083][0.0655222722939256]...[0.12560417304820823][0.07411198929338968][0.1331408833577327][0.03857993879037075][0.038711425869337984][0.08897777442428674][0.05560594182068467][0.0626845924656044][0.07237739511197822][0.041613779283231046]
9[0.06139453131422424][0.07948227337943198][0.06689020592169981][0.05955785852513209][0.07517277782794342][0.07765247541664737][0.04318953773752061][0.07531732464870161][0.08098184954929952][0.06458414852590263]...[0.12278663409161311][0.07365677105558269][0.13062404036287226][0.03855871951859559][0.03855871951859559][0.08461722594983144][0.053320319118708596][0.06258022212397257][0.07103356433858925][0.04145958334073781]
\n", + "

10 rows × 100 columns

\n", + "
" + ], + "text/plain": [ + " 1000 1001 1002 \n", + "0 [0.6422584945103841] [0.668743974268336] [1.008422835773131] \\\n", + "1 [0.2873172889001637] [0.17456679249116952] [0.6332470445844255] \n", + "2 [0.19584221062188678] [0.1319897299803032] [0.08491363433325547] \n", + "3 [0.13903116434341475] [0.10146093652625415] [0.08181050400129704] \n", + "4 [0.1129173777348391] [0.08606053698831793] [0.07547569629425901] \n", + "5 [0.11038217708080131] [0.08212202043548315] [0.07432402899651668] \n", + "6 [0.10833574384967115] [0.08201334401786602] [0.07412645164725896] \n", + "7 [0.09458970277908005] [0.08147133735902357] [0.07118734960890093] \n", + "8 [0.06683185055384143] [0.08107329389309223] [0.06992507760853946] \n", + "9 [0.06139453131422424] [0.07948227337943198] [0.06689020592169981] \n", + "\n", + " 1003 1004 1005 \n", + "0 [0.6661363151210968] [0.8642602181331219] [0.7417012424961686] \\\n", + "1 [0.09678526898308248] [0.537824913541413] [0.2820702175096787] \n", + "2 [0.08737476357264361] [0.47799964699339453] [0.20691840972537448] \n", + "3 [0.07523129301857605] [0.18045913119908524] [0.20260530333012666] \n", + "4 [0.06970411933742698] [0.17135272355687187] [0.20091765863852995] \n", + "5 [0.06957535331034335] [0.11414112796082569] [0.09766529639479365] \n", + "6 [0.06759708576024981] [0.09130509649851562] [0.08716787810262448] \n", + "7 [0.06755116098175724] [0.08423578168208135] [0.08564850087282809] \n", + "8 [0.0665144106585746] [0.07732918671239193] [0.07767329982049115] \n", + "9 [0.05955785852513209] [0.07517277782794342] [0.07765247541664737] \n", + "\n", + " 1006 1007 1008 \n", + "0 [0.7315442686011049] [0.9460833202189584] [0.9746037822064916] \\\n", + "1 [0.6513139955405113] [0.3597970619791958] [0.24900044829809295] \n", + "2 [0.0713300455225047] [0.18078764107798664] [0.11093369090167131] \n", + "3 [0.054813355262948726] [0.16776960486431058] [0.10031171790840715] \n", + "4 [0.05469328989879712] [0.16743762657989103] [0.09811418008672529] \n", + "5 [0.05342228639423025] [0.11293055713592527] [0.09499508593239105] \n", + "6 [0.0497899252125642] [0.09487498299853164] [0.08477817264550953] \n", + "7 [0.04712198065394311] [0.08900364470414873] [0.0845410509094711] \n", + "8 [0.04496152399683175] [0.07886393345966285] [0.08159272098702083] \n", + "9 [0.04318953773752061] [0.07531732464870161] [0.08098184954929952] \n", + "\n", + " 1009 ... 1090 1091 \n", + "0 [0.6869123604148133] ... [1.1523833456270243] [0.7956243694544436] \\\n", + "1 [0.14368626326554504] ... [0.42456045151374616] [0.13839258426540738] \n", + "2 [0.09580043962698802] ... [0.39006994534248846] [0.09497709201044909] \n", + "3 [0.07826399750895882] ... [0.2509003350905366] [0.08889171346193057] \n", + "4 [0.07488273104320195] ... [0.1456676671253771] [0.08031528425526144] \n", + "5 [0.07351917900522721] ... [0.1356468036645224] [0.07846954905194828] \n", + "6 [0.06935710726422203] ... [0.13395484290588885] [0.07651073480529516] \n", + "7 [0.06696048135598477] ... [0.12832106795670173] [0.0742473891955823] \n", + "8 [0.0655222722939256] ... [0.12560417304820823] [0.07411198929338968] \n", + "9 [0.06458414852590263] ... [0.12278663409161311] [0.07365677105558269] \n", + "\n", + " 1092 1093 1094 \n", + "0 [1.7802942925566625] [0.5158809769289813] [0.5234239788184921] \\\n", + "1 [0.1920094056436893] [0.4608108432879023] [0.46754863207460967] \n", + "2 [0.17705994384084703] [0.1911406606344065] [0.19471247373710213] \n", + "3 [0.16005754765559338] [0.053302833660593664] [0.053302833660593664] \n", + "4 [0.1591723994058402] [0.05187299077727413] [0.04876597436921601] \n", + "5 [0.15849239426755599] [0.04462161696830591] [0.04472544383210234] \n", + "6 [0.14400058142958222] [0.04329534404644248] [0.04462161696830591] \n", + "7 [0.1337598250032541] [0.03987922316585672] [0.04329534404644248] \n", + "8 [0.1331408833577327] [0.03857993879037075] [0.038711425869337984] \n", + "9 [0.13062404036287226] [0.03855871951859559] [0.03855871951859559] \n", + "\n", + " 1095 1096 1097 \n", + "0 [0.8038496213979783] [0.6360279187397326] [1.1259044435676868] \\\n", + "1 [0.635942460180357] [0.2673113797648495] [0.09064837068978648] \n", + "2 [0.6126542869398587] [0.06788233871189489] [0.07823702305427702] \n", + "3 [0.2514441707424639] [0.06610690581400588] [0.07732220562999899] \n", + "4 [0.24540764374264745] [0.064867860088572] [0.07458882712067232] \n", + "5 [0.2051558258313172] [0.06082789393903154] [0.06855422701443233] \n", + "6 [0.10677052218571517] [0.05830025911923057] [0.06718300838215913] \n", + "7 [0.08913041300626738] [0.05705502660231897] [0.06310045914518085] \n", + "8 [0.08897777442428674] [0.05560594182068467] [0.0626845924656044] \n", + "9 [0.08461722594983144] [0.053320319118708596] [0.06258022212397257] \n", + "\n", + " 1098 1099 \n", + "0 [1.0616162379284384] [0.6784846202202287] \n", + "1 [0.19426228087688888] [0.17427577670397354] \n", + "2 [0.17319361461646068] [0.07447944169226609] \n", + "3 [0.17148587761263784] [0.04757751873987604] \n", + "4 [0.1268769662336863] [0.04675221931850179] \n", + "5 [0.08964124013628569] [0.043706874358644336] \n", + "6 [0.08292563259802246] [0.04288337427490202] \n", + "7 [0.07445114613146177] [0.04255318648575154] \n", + "8 [0.07237739511197822] [0.041613779283231046] \n", + "9 [0.07103356433858925] [0.04145958334073781] \n", + "\n", + "[10 rows x 100 columns]" + ] + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.DataFrame(rank_matrix)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.1" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/information_retrieval_movies.ipynb b/notebooks/information_retrieval_movies.ipynb new file mode 100644 index 0000000..45e5166 --- /dev/null +++ b/notebooks/information_retrieval_movies.ipynb @@ -0,0 +1,444 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pré-Processamento dos Documentos\n", + "\n", + "A limpeza dos dados é um processo essencial para garantir a qualidade e a confiabilidade das informações armazenadas em um banco de dados. A limpeza dos dados envolve a identificação e a correção de erros, inconsistências, duplicidades e valores ausentes nos dados. A arquitetura do armazenamento é a forma como os dados são organizados, estruturados e acessados em um banco de dados. Uma das opções de arquitetura é o formato YAML, que significa YAML Ain't Markup Language. O YAML é um formato de serialização de dados que usa uma sintaxe simples e legível para representar estruturas de dados como listas, mapas, sequências e escalares. O YAML é compatível com diversas linguagens de programação e pode ser usado para armazenar dados de forma hierárquica e flexível.\n", + "\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Importa os módulos necessários\n", + "import os # Módulo para lidar com funções do sistema operacional\n", + "import gc # Módulo para realizar coleta de lixo e gerenciamento de memória\n", + "import sys\n", + "\n", + "import numpy as np # Módulo para trabalhar com matrizes e funções matemáticas\n", + "import pandas as pd # Módulo para trabalhar com dataframes e séries em Python\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "## Estruturação dos Arquivos\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "disney = pd.read_csv('../data/movies/disney_titles.csv')\n", + "prime = pd.read_csv('../data/movies/prime_titles.csv')\n", + "hbo = pd.read_csv('../data/movies/hbo_titles.csv')\n", + "netflix = pd.read_csv('../data/movies/netflix_titles.csv')\n", + "\n", + "disney['provider'] = 'disney'\n", + "prime['provider'] = 'prime'\n", + "hbo['provider'] = 'hbo'\n", + "netflix['provider'] = 'netflix'\n", + "\n", + "base = pd.concat([disney, prime, hbo, netflix])\n", + "\n", + "del disney, prime, hbo, netflix\n", + "\n", + "base.dropna(ignore_index=True, inplace=True)\n", + "base.reset_index(drop=True, inplace=True)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "## Processamento de Texto\n", + "\n", + "
\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tratamento dos gêneros" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from ast import literal_eval\n", + "\n", + "base['genres'] = base['genres'].fillna('[]').apply(literal_eval).apply(lambda x: [i for i in x] if isinstance(x, list) else [])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Transformação de minúsculos\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# (\\[a-z]): para encontrar todos os caracteres que começam com uma barra invertida () seguida por uma letra minúscula (a-z);\n", + "# ([^\\w\\]): para encontrar todos os caracteres que não são letras, números ou barras invertidas ();\n", + "# (\\S+\\d\\S+): para encontrar todos os trechos de texto que contêm um ou mais caracteres não brancos (\\S), \n", + "# seguidos por um dígito (\\d), seguidos por mais um ou mais caracteres não brancos (\\S).\n", + "base['post'] = base['description'].replace(r'(\\\\[a-z])|([^\\w\\\\])|(\\S+\\d\\S+)', ' ', regex=True)\n", + "\n", + "# Aplicando as funções str.lower() e str.strip() simultaneamente\n", + "base['post'] = base['post'].apply(lambda x: x.lower().strip() if isinstance(x, str) else x)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "### Tokenização e Lemmatizer\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "from ir.preprocessing import lemmatize_word\n", + "\n", + "base['post'] = base['post'].apply(lambda x: ' '.join([lemmatize_word(word.lower()) for word in x.split()]))\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Identificação das query / docs" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(42)\n", + "rand = np.random.random(base.shape[0])\n", + "\n", + "d_index = rand < 0.7\n", + "q_index = rand >= 0.7" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### TF IDF" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "from ir.tf_idf import tfidf" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "weights = tfidf(base, 'post').T" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.metrics.pairwise import linear_kernel" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ranqueamento" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "q_index = base[q_index].index\n", + "d_index = base[d_index].index" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "rank_geral = linear_kernel(weights.iloc[d_index], weights.iloc[q_index])\n", + "rank_geral = pd.DataFrame(rank_geral, index=d_index, columns=q_index)" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Query: 3093/3093 - Doc: 1730/3092" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 3 function calls in 0.000 seconds\n", + "\n", + " Ordered by: internal time\n", + "\n", + " ncalls tottime percall cumtime percall filename:lineno(function)\n", + " 1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}\n", + " 1 0.000 0.000 0.000 0.000 :1()\n", + " 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}" + ] + } + ], + "source": [ + "import sys\n", + "import numba\n", + "\n", + "def calcular_resultados_relevantes(q_index: list, base: pd.DataFrame) -> 'resultados_relevantes[dict], resultados_sistema[dict]':\n", + " resultados_sistema = {}\n", + "\n", + " for q in q_index: \n", + " resultados_sistema[q] = rank_geral[q].sort_values(ascending=False).index\n", + "\n", + " resultados_relevantes = {}\n", + "\n", + " for q in q_index:\n", + " q_genre = base.iloc[q]['genres']\n", + "\n", + " k = []\n", + "\n", + " for d in resultados_sistema[q]:\n", + " d_genre = base.iloc[d]['genres']\n", + " \n", + " # Verifica qual lista de gêneros é menor para otimizar a comparação\n", + " if len(d_genre) > len(q_genre):\n", + " comparativo_menor = q_genre\n", + " comparativo_maior = d_genre\n", + " else:\n", + " comparativo_menor = d_genre\n", + " comparativo_maior = q_genre\n", + " \n", + " # Verifica se há pelo menos um gênero em comum entre as listas\n", + " partial_relevance = any(i in comparativo_maior for i in comparativo_menor)\n", + " \n", + " if partial_relevance:\n", + " k.append(d)\n", + " \n", + " print(f'\\rQuery: {q}/{q_index.max()} - Doc: {d}/{d_index.max()}', end='')\n", + " sys.stdout.flush()\n", + "\n", + " resultados_relevantes[q] = k\n", + " \n", + " return resultados_relevantes, resultados_sistema\n", + "\n", + "resultados_relevantes, resultados_sistema = calcular_resultados_relevantes(q_index, base)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Métricas" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Média do P@: 0.719268030139934\n" + ] + } + ], + "source": [ + "def calcular_p_n_media(resultados_relevantes, resultados_sistema, n):\n", + " def calcular_p_n(resultados, relevantes):\n", + " if len(resultados) > n:\n", + " resultados = resultados[:n] # Considerar apenas os primeiros n resultados\n", + " num_relevantes = len(set(resultados) & set(relevantes)) # Contar quantos resultados relevantes foram encontrados\n", + " p_n = num_relevantes / n # Calcular a precisão P@n\n", + " return p_n\n", + "\n", + " p_n_total = 0\n", + " for consulta, relevantes in resultados_relevantes.items():\n", + " resultados = resultados_sistema.get(consulta, []) # Obtém os resultados retornados pelo sistema para a consulta\n", + " p_n = calcular_p_n(resultados, relevantes)\n", + " p_n_total += p_n\n", + "\n", + " p_n_media = p_n_total / len(resultados_relevantes)\n", + " return p_n_media\n", + "\n", + "# Utilizando a função para calcular a média do P@n\n", + "p_n_media = calcular_p_n_media(resultados_relevantes, resultados_sistema, n=10)\n", + "print(f\"Média do P@: {p_n_media}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Média do P@10: 0.719268030139934\n", + "Média do P@20: 0.6967168998923577\n", + "Média do P@50: 0.6745748116254034\n", + "Média do P@100: 0.658902045209902\n" + ] + } + ], + "source": [ + "for x in [10, 20, 50, 100]: \n", + " print(f\"Média do P@{x}: {calcular_p_n_media(resultados_relevantes, resultados_sistema, n=x)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.5882758350175267" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def average_precision(relevantes, recomendados):\n", + " relevancia_cumulativa = 0\n", + " precision_cumulativa = 0\n", + " num_relevantes = len(relevantes)\n", + " ap = 0\n", + "\n", + " for i, rec in enumerate(recomendados):\n", + " if rec in relevantes:\n", + " relevancia_cumulativa += 1\n", + " precision_cumulativa += relevancia_cumulativa / (i + 1)\n", + "\n", + " if num_relevantes > 0:\n", + " ap = precision_cumulativa / num_relevantes\n", + "\n", + " return ap\n", + "\n", + "def mean_average_precision(resultados_relevantes, resultados_sistema):\n", + " map = 0\n", + " num_consultas = len(resultados_relevantes)\n", + "\n", + " for q in resultados_relevantes:\n", + " relevantes = resultados_relevantes[q]\n", + " recomendados = resultados_sistema[q]\n", + " ap = average_precision(relevantes, recomendados)\n", + " map += ap\n", + "\n", + " if num_consultas > 0:\n", + " map /= num_consultas\n", + "\n", + " return map\n", + "\n", + "# Aplicar o MAP nas consultas\n", + "map_result = mean_average_precision(resultados_relevantes, resultados_sistema)\n", + "map_result" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.1" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/ir/__init__.py b/notebooks/ir/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notebooks/ir/io_file.py b/notebooks/ir/io_file.py new file mode 100644 index 0000000..ccbd73e --- /dev/null +++ b/notebooks/ir/io_file.py @@ -0,0 +1,47 @@ +import os + +def read_files(doc_dir) -> dict: + # Use a list comprehension to get a list of file paths + database = [{'filepath': doc_dir, + 'filename': filename, + 'text': open(os.path.join(doc_dir, filename), 'r').read().strip()} + for filename in os.listdir(doc_dir)] + + return database + +def process_files(doc_dir: str) -> dict: + """ + Processa os arquivos em um diretório e seus subdiretórios. + + Args: + doc_dir (str): Caminho para o diretório que contém os arquivos. + + Returns: + list: Uma lista de dicionários contendo os dados processados de cada arquivo. + + """ + database = [] # Lista para armazenar os dados processados + + for filepath in os.listdir(doc_dir): # Percorre os arquivos no diretório + for filename in os.listdir(f'{doc_dir}{filepath}'): # Percorre os arquivos nos subdiretórios + + # Abre o arquivo e lê seu conteúdo + with open(os.path.join(doc_dir, filepath, filename), 'r') as f: + text_data = f.read().strip() + + try: + # Divide o conteúdo do arquivo em header e body + header, body = text_data.split('\n\n', maxsplit=1) + + # Adiciona os dados processados à lista database + database.append({ + 'filepath': filepath, + 'filename': filename, + 'body': body, + }) + except ValueError: + # Se ocorrer uma exceção ao dividir o conteúdo, continua para o próximo arquivo + continue + + return database + diff --git a/notebooks/ir/preprocessing.py b/notebooks/ir/preprocessing.py new file mode 100644 index 0000000..eba28fd --- /dev/null +++ b/notebooks/ir/preprocessing.py @@ -0,0 +1,55 @@ +import pandas as pd +import numpy as np + +def lemmatize_word(word): + ''' + Uma palavra lematizada é a forma básica ou canônica de uma palavra, sem os sufixos que indicam flexão de número, gênero, tempo, modo ou aspecto. Por exemplo, as palavras 'cantando', 'cantou' e 'cantaria' são lematizadas como 'cantar'. + + Esta função aplica algumas regras simples para remover os sufixos mais comuns em português, como 's', 'ns', 'ing', 'ly' e 'ed'. Ela não usa um dicionário ou um algoritmo sofisticado de análise morfológica, portanto pode não funcionar para todas as palavras ou casos especiais. + + Parâmetros + ---------- + word : str + A palavra a ser lematizada. + + Retorna + ------- + str: A palavra lematizada. + ''' + + vowels = ['a', 'e', 'i', 'o', 'u'] + # Se a palavra termina com 'ns', remove os dois últimos caracteres + if word.endswith('ns'): + return word[:-2] + + # Se a palavra termina com 's', remove o último caractere + if word.endswith('s'): + return word[:-1] + + # Se a palavra termina com 'ing' e tem mais de cinco caracteres, aplica algumas regras para remover o sufixo + if word.endswith('ing') and len(word) > 5: + # Se os dois últimos caracteres antes de 'ing' são iguais e não são vogais, remove os quatro últimos caracteres e adiciona o terceiro último caractere + if word[-4] == word[-5] and word[-5] not in vowels: + return word[:-4] + word[-3:] + # Se o terceiro último caractere é uma vogal, remove os três últimos caracteres + elif word[-3] in vowels: + return word[:-3] + # Caso contrário, remove os dois últimos caracteres + else: + return word[:-2] + + # Se a palavra termina com 'ly' e tem mais de quatro caracteres, remove os dois últimos caracteres + if word.endswith('ly') and len(word) > 4: + return word[:-2] + + # Se a palavra termina com 'ed' e tem mais de três caracteres, aplica algumas regras para remover o sufixo + if word.endswith('ed') and len(word) > 3: + # Se os dois últimos caracteres antes de 'ed' são iguais e não são vogais, remove os três últimos caracteres e adiciona o segundo último caractere + if word[-3] == word[-4] and word[-4] not in vowels: + return word[:-3] + word[-2:] + # Caso contrário, remove os dois últimos caracteres + else: + return word[:-2] + + # Se nenhuma das regras anteriores se aplica, retorna a palavra sem alteração + return word diff --git a/notebooks/ir/tf_idf.py b/notebooks/ir/tf_idf.py index 7d39952..5473a4b 100644 --- a/notebooks/ir/tf_idf.py +++ b/notebooks/ir/tf_idf.py @@ -15,7 +15,7 @@ def log_freq(doc): """ words = doc.split(' ') # Separar o documento em palavras freq = pd.Series(words).value_counts() # Contar a frequência de cada palavra - log_freq = 1 + np.log2(freq) # Calcular o log na base 2 da frequência + log_freq = 1 + np.log10(freq) # Calcular o log na base 2 da frequência return log_freq # Função que aplica a função log_freq a cada documento do dataframe @@ -66,7 +66,7 @@ def tfidf(dataframe, column): Ret'urns: pandas.DataFrame: O dataframe com os TF-IDFs relativos para cada palavra em cada documento. - """' + """ tf = ri_tf(dataframe=dataframe, column=column) # Calcular o TF de cada termo em cada documento idf = ri_idf(dataframe=dataframe, column=column) # Calcular o IDF de cada termo @@ -74,4 +74,4 @@ def tfidf(dataframe, column): for word in tf.index: # Iterar por cada termo do corpus df[word] = tf.loc[word]*idf[word] # Calcular o TF-IDF do termo em cada documento - return pd.DataFrame.from_dict(df, orient='index') # Retornar os resultados em um DataFrame + return pd.DataFrame.from_dict(df, orient='index').fillna(0) # Retornar os resultados em um DataFrame