<jupyter_start><jupyter_text>Representação numérica de palavras e textos Neste notebook iremos apresentação formas de representar valores textuais por meio de representação numérica. Iremos usar pandas, caso queira entender um pouco sobre pandas, [veja este notebook](pandas.ipynb). Por isso, não esqueça de instalar o módulo pandas:``pip3 install pandas``Em aprendizado de máquina, muitas vezes, precisamos da representação numérica de um determinado valor. Por exemplo:<jupyter_code>import pandas as pd
df_jogos = pd.DataFrame([   ["boa","nublado","não"],
                            ["boa","chuvoso","não"],
                           ["média","nublado","sim"],
                         ["fraca","chuvoso","não"]],
                        columns=["disposição","tempo","jogar volei?"])
df_jogos<jupyter_output><empty_output><jupyter_text>Caso quisermos maperar cada coluna (agora chamada de atributo) para um valor, forma mais simples de se fazer a transformação é simplesmente mapear esse atributo para um valor numérico. Veja o exemplo abaixo:  Nesse exemplo, temos dois atributos disposição do jogador e tempo e queremos prever se o jogar irá jogar volei ou não. Tanto os atributos quanto a classe podem ser mapeados como número. Além disso, o atributo `disposicao` é um atributo que representa uma escala - o que deixa essa forma de tranformação bem adequada para esse atributo.<jupyter_code>from typing import Dict
def mapeia_atributo_para_int(df_data:pd.DataFrame, coluna:str, dic_nom_to_int: Dict[int,str]):
    for i,valor in enumerate(df_data[coluna]):
        valor_int = dic_nom_to_int[valor]
        df_data[coluna].iat[i] = valor_int

        
df_jogos = pd.DataFrame([   ["boa","nublado","sim"],
                            ["boa","chuvoso","não"],
                           ["média","ensolarado","sim"],
                         ["fraca","chuvoso","não"]],
                        columns=["disposição","tempo","jogar volei?"])
dic_disposicao = {"boa":3,"média":2,"fraca":1}
mapeia_atributo_para_int(df_jogos, "disposição", dic_disposicao)

dic_tempo = {"ensolarado":3,"nublado":2,"chuvoso":1}
mapeia_atributo_para_int(df_jogos, "tempo", dic_tempo)

dic_volei = {"sim":1, "não":0}
mapeia_atributo_para_int(df_jogos, "jogar volei?", dic_volei)
df_jogos<jupyter_output><empty_output><jupyter_text>Binarização dos atributos categóricos Podemos fazer a binarização dos atributos categóricos em que, cada valor de atributo transforma-se em uma coluna que recebe `0` caso esse atributo não exista e `1`, caso contrário. Em nosso exemplo:<jupyter_code>from preprocessamento_atributos import BagOfItems
df_jogos = pd.DataFrame([   [4, "boa","nublado","sim"],
                            [3,"boa","chuvoso","não"],
                           [2,"média","ensolarado","sim"],
                         [1,"fraca","chuvoso","não"]],
                        columns=["id","disposição","tempo","jogar volei?"])
dic_disposicao = {"boa":3,"média":2,"fraca":1}


bag_of_tempo = BagOfItems(0)
#veja a implementação do método em preprocesamento_atributos.py
df_jogos_bot = bag_of_tempo.cria_bag_of_items(df_jogos,["tempo"])
df_jogos_bot<jupyter_output>0/4<jupyter_text>Como existem vários valores no teste que você desconhece, se fizermos dessa forma, atributos que estão no teste poderiam estar completamente zerados no treino, sendo desnecessário, por exemplo:<jupyter_code>df_jogos_treino = df_jogos[:2]
df_jogos_treino
df_jogos_teste = df_jogos[2:]
df_jogos_teste<jupyter_output><empty_output><jupyter_text>Exemplo Real Considere este exemplo real de filmes e seus atores ([obtidos no kaggle](https://www.kaggle.com/rounakbanik/the-movies-dataset)):<jupyter_code>import pandas as pd
df_amostra = pd.read_csv("movies_amostra.csv")
df_amostra<jupyter_output><empty_output><jupyter_text>Nesse exemplo, as colunas que representam os atores principais podem ser binarizadas. Em nosso caso, podemos colocar os atores todos em um "Bag of Items". Os atores são representados por as colunas `ator_1`, `ator_2`,..., `ator_5`. Abaixo, veja um sugestão de como fazer em dataset:<jupyter_code>import pandas as pd
from preprocessamento_atributos import BagOfItems




obj_bag_of_actors = BagOfItems(min_occur=3)

#boa=bag of actors ;)
df_amostra_boa = obj_bag_of_actors.cria_bag_of_items(df_amostra,["ator_1","ator_2","ator_3","ator_4","ator_5"])
df_amostra_boa<jupyter_output><empty_output><jupyter_text>Veja que temos bastante atributos um para cada ator. Mesmo sendo melhor possuirmos poucos atributos e mais informativos, um método de aprendizado de máquina pode ser capaz de usar essa quantidade de forma eficaz. Particularmente, o [SVM linear](https://scikit-learn.org/stable/modules/generated/sklearn.svm.LinearSVC.html) e o [RandomForest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html) são métodos que conseguem ir bem nesse tipo de dado. Essa é a forma mais prática de fazer, porém, em aprendizado de máquina, geralmente dividimos nossos dados em, pelo menos, treino e teste em que treino é o dado que você terá todo o acesso e, o teste, deve reproduzir uma amostra do mundo real. Vamos supor que no treino há atores raros que não ocorrem no teste, nesse caso tais atributos seriam inúteis para o teste. Isso pode fazer com que o resultado reproduza menos o mundo real - neste caso, é muito possível que a diferença seja quase insignificante. Mas, caso queiramos fazer da forma "mais correta", temos que considerar apenas o treino para isso:<jupyter_code>#supondo que 80% da amostra é treino
df_treino_amostra = df_amostra.sample(frac=0.8, random_state = 2)
df_teste_amostra = df_amostra.drop(df_treino_amostra.index)

#min_occur=3 definie o minimo de ocorrencias desse ator para ser considerado
#pois, um ator que apareceu em poucos filmes, pode ser menos relevante para a predição do genero
obj_bag_of_actors = BagOfItems(min_occur=3)
df_treino_amostra_boa = obj_bag_of_actors.cria_bag_of_items(df_treino_amostra,["ator_1","ator_2","ator_3","ator_4","ator_5"])
df_teste_amostra_boa = obj_bag_of_actors.aplica_bag_of_items(df_teste_amostra,["ator_1","ator_2","ator_3","ator_4","ator_5"])<jupyter_output>0/2400
1000/2400
2000/2400
0/600<jupyter_text>Representação Bag of Words Muitas vezes, temos textos que podem ser relevantes para uma determinada tarefa de aprendizado d máquina. Por isso, temos que representar tais elementos para nosso método de aprendizado de máquina. A forma mais usual para isso, é a `Bag of Words` em que cada palavra é um atributo e, o valor dela, é a frequencia dele no texto (ou algum outro valor que indique a importancia dessa palavra no texto).Por exemplo, caso temos as frases `A casa é grande`, `A casa é verde verde` em que cada frase é uma instancia diferente. A representação seria da seguinte forma:<jupyter_code>dic_bow = {"a":[1,1],
         "casa":[1,1],
         "é":[1,1],
         "verde":[0,2]
        }
df_bow = pd.DataFrame.from_dict(dic_bow)
df_bow<jupyter_output><empty_output><jupyter_text>Da forma que fizemos acima, usamos a frequencia de um termo para definir sua importancia no texto, porém, existem termos que possuem uma frequencia muito alta e importancia baixa: são os casos dos artigos e preposições por exemplo, pois, eles não discriminam o texto. Uma forma de mensurar o porder discriminativo das palavras é usando a métrica `TF-IDF`. Para calcularmos essa métrica, primeiramente calculamos a frequencia de um termo no documento (TF) e, logo após multiplamos pelo IDF. A fórmula para calcular o TF-IDF do termo $i$ no documento (ou instancia) $j$ é a seguinte:\begin{equation}    TFIDF_{ij} = TF_{ij} \times IDF_i\end{equation}\begin{equation}    TF_{ij} = log(f_{ij})\end{equation}em que $f_{ij}$ é a frequencia de um termo $i$ no documento $j$. Usa-se o `log` para suavizar valores muito altos e o $IDF$ (do inglês, _Inverse Document Frequency_) do termo $i$ é calculado da seguinte forma:\begin{equation}    IDF_i = log(\frac{N}{n_i})\end{equation}em que $N$ é o número de documentos da coleção e $n_i$ é o número de documentos em que esse termo $i$ ocorre. Espera-se que, quanto mais discriminativo o termo, em menos documentos esse termo irá ocorrer e, consequentemente, o $IDF$ deste termo será mais alto. Por exemplo, considere as palavras `de`, `bebida` e `cerveja`. `cerveja` é uma palavra mais discriminativa do que `bebida`; e `bebibda` é mais discriminativo do que a preposição `de`. Muito provavelmente teremos mais frequentemente termos menos discriminativos. Por exemplo, se tivermos uma coleção de 1000 documentos,   `de` poderia ocorrer em 900 documentos,  `bebida` em 500 e `cerveja` em 100 documentos. Se fizermos o calculo, veremos que quanto mais discriminativo um termo, mais alto é seu IDF:<jupyter_code>import math
N = 1000
n_de = 900
n_bebida = 500
n_cerveja = 100

IDF_de = math.log(N/n_de)
IDF_bebida = math.log(N/n_bebida)
IDF_cerveja = math.log(N/n_cerveja)

print(f"IDF_de: {IDF_de}\tIDF_bebida:{IDF_bebida}\tIDF_cerveja:{IDF_cerveja}")<jupyter_output><empty_output><jupyter_text>A biblioteca `scikitlearn`também já possui uma classe [TFIDFVectorizer](https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html) que transforma um texto em um vetor de atributos usando o TF-IDF para o valor referente a relevancia deste termo. Veja um exemplo na coluna `resumo` do nosso dataset de filme:<jupyter_code>import pandas as pd
from preprocessamento_atributos import BagOfWords

df_amostra = pd.read_csv("datasets/movies_amostra.csv")
bow_amostra = BagOfWords()
df_bow_amostra = bow_amostra.cria_bow(df_amostra,"resumo")
df_bow_amostra<jupyter_output><empty_output><jupyter_text>Como são muitos atributos, pode parecer que não ficou corretamente gerado. Mas, filtrando as palavras de um determinado resumo você verificará que está ok:<jupyter_code>df_bow_amostra[["in","lake", "high"]]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09322548028311425, 'fraction_numerical': 0.010920121334681496, 'mean_word_length': 5.124458204334365, 'pattern_counts': {'":': 15, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 4, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '3312', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 3703, 'n_tokens_neox': 3579, 'n_words': 1035, 'path': 'pln/binarizacao_e_tf_idf.ipynb', 'repo_name': 'IagorSs/roteiro-projetos', 'size': '9890'}