<jupyter_start><jupyter_text>>   **Natural Language Processing** **Processamento de Linguagem Natural é um campo da inteligência artificial que estuda as interações entre computadores e linguagens humanas, em particular como programar computadores para processar e analisar grandes quantidades de dados de linguagem natural. NLP  aplicada para a classificação do texto atribui categorias aos dados do texto de acordo com seu conteúdo. Nesta aplicação será explicado métodos para analisar o texto e extrair recursos que podem ser usados para construir um modelo de classificação.** **DADOS*** A base de dados foi coletada a partir do Portal da Transparência do Tribunal de Contas de São Paulo, que pode ser acessado através do link: http://www4.tce.sp.gov.br/licitacao/contratos* O dataset contém informações relacionadas a base de dados de contratos enviadas pelos municípios do Estado de São Paulo  **Objetivo*** Implementar técnicas de pré-processamento de nlp e o algoritmo de Naive Bayes multinomial para a classificação de contratos a partir da descrição de seus objetos  **Importar bibliotecas**<jupyter_code>#Biblioteca para a importação dos dados
import pandas as pd

#Biblioteca para gerar gráficos e visualizações
import matplotlib.pyplot as plt

#Biblioteca para remover pontuações
from string import punctuation

#Biblioteca para o processamento de linguagem natural
import nltk
#Tokenization e distribuições de frequência
from nltk import tokenize, FreqDist

#Biblioteca para gerar a WordCloud
from wordcloud.wordcloud import WordCloud

#Biblioteca importada SKLEARN para análise dos dados
#Vetorização dos dados
from sklearn.feature_extraction.text import CountVectorizer
#Modelo de Naive Bayes
from sklearn.naive_bayes import MultinomialNB
#Métricas 
from sklearn import metrics
#Validação cruzada
from sklearn.model_selection import cross_val_predict<jupyter_output><empty_output><jupyter_text>**Ferramenta NLTK*** Recurso para o processamento de textos, a NLTK será útil para remoção de stopwords, separar o texto em sentenças, separar as palavras dentro de cada sentença, para representar e processar informações probabilísticasDocumentação: https://www.nltk.org<jupyter_code>nltk.download()
#d all q<jupyter_output><empty_output><jupyter_text>**Importação da base de dados do TCE** **Após a importação do Pandas é feita a importação e a leitura do arquivo. O comando read_csv lê o CSV e o armazena em um Dataframe**<jupyter_code>#Importando o arquivo direto do Google Drive
from google_drive_downloader import GoogleDriveDownloader as gdd
google_id = '1u7B93IFnCob1KebVQ0y_D2aHTRnwd-nD'
gdd.download_file_from_google_drive(file_id=google_id, 
                                    dest_path = '/content/terms-3.csv', 
                                    showsize = True)
df = pd.read_csv('/content/terms-3.csv' , delimiter=';', encoding='latin_1')<jupyter_output>Downloading 1u7B93IFnCob1KebVQ0y_D2aHTRnwd-nD into /content/terms-3.csv... 
1.5 MiB Done.<jupyter_text>**Head(): permite a visualização das primeiras linhas do DataFrame, o nome das colunas se encontra na primeira linha, a partir daí, podemos escolher quais colunas queremos trabalhar**<jupyter_code>#Exibir o dataset
df.head()<jupyter_output><empty_output><jupyter_text>**Exploração dos dados** **A função df.drop ([ ], axis = 1, inplace = True)remove permanentemente as colunas**<jupyter_code>#Remover as colunas
df.drop(df.columns[[1, 2, 3, 4, 5, 7, 10, 11, 12, 13, 14, 15, 16, 17]], axis=1, inplace = True)<jupyter_output><empty_output><jupyter_text>**A função df.columns retorna uma lista com o nome de todas as colunas do arquivo de dados**<jupyter_code>#Exibir o nome das colunas
df.columns
#Exibir o dataset
df.head()<jupyter_output><empty_output><jupyter_text>**Distribuição do dataset***   **%matplotlib inline: gera gráficos em imagens estáticas no notebook.***   **value_counts: conta a quantidade de linhas de cada classificação***   **plot(kind='bar'): gera um gráfico de barras**<jupyter_code>#Gráficos de barras
%matplotlib inline
df.Classificação.value_counts().plot(kind='bar')<jupyter_output><empty_output><jupyter_text>**Com o método info() conseguimos facilmente saber o nome de cada coluna, o número de linhas por coluna, se há valores nulos e o tipo de dado de cada uma dessas colunas. Por padrão ele tenta identificar o tipo de dado como int, float, datetime, ou object caso ele não consiga identificar o tipo exato. No final podemos observar a quantidade de memória utilizada para ler o arquivo.**<jupyter_code>#Informações do conjunto de dados
df.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 2211 entries, 0 to 2210
Data columns (total 4 columns):
 #   Column                     Non-Null Count  Dtype 
---  ------                     --------------  ----- 
 0   Classificação              2211 non-null   object
 1   Modalidade da contratação  2211 non-null   object
 2   Objeto                     2211 non-null   object
 3   Interessado                2211 non-null   object
dtypes: object(4)
memory usage: 69.2+ KB<jupyter_text>**A função df.nunique retorna o número de valores únicos em cada coluna**<jupyter_code>#Valores únicos
df.nunique()<jupyter_output><empty_output><jupyter_text>**Pré-processamento****Técnicas de pré-processamento são aplicadas para formatar e estruturar os textos, sem remover suas características naturais***  Etapas de pré processamento a serem realizadas de acordo com a análise do texto:1. Remover acentos2. Remover números, caracteres e pontuações3. Remover stopwords<jupyter_code>#Função para remover acentos
def remover_acentos(txt):
        
      dicionario_acentos = {
      'á' : 'a', 'Á' : 'A', 'â' : 'a', 'Â' : 'A', 'à' : 'a', 'À' : 'A', 'ã' : 'a', 'Ã' : 'A',
      'é' : 'e', 'É' : 'E', 'ê' : 'e', 'Ê' : 'E', 'è' : 'e', 'È' : 'E',
      'í' : 'i', 'Í' : 'I', 'î' : 'i', 'Î' : 'I', 'ì' : 'i', 'Ì' : 'I',
      'ó' : 'o', 'Ó' : 'O', 'ô' : 'o', 'Ô' : 'O', 'ò' : 'o', 'Ò' : 'O', 'õ' : 'o', 'Õ' : 'O',
      'ú' : 'u', 'Ú' : 'U', 'û' : 'u', 'Û' : 'U', 'ù' : 'u', 'Ù' : 'U', 'ü' : 'u',
      'ç' : 'c','Ç' : 'C', 'ñ' : 'n','Ñ' : 'N'
      }
    
      for key, value in dicionario_acentos.items():

          if key in txt:
                txt = txt.replace(key, value)

      return txt
#Aplicar a função no dataset
df['Objeto']=df['Objeto'].apply(remover_acentos)
df['Objeto'].head()<jupyter_output><empty_output><jupyter_text>**O método apply() permite que a função criada seja aplicada em todas as linhas da coluna indicada.**<jupyter_code>#Função para remover números, caracteres e pontuações
#print(list(punctuation))
def remover_numeros_pontuacao(txt):
        
        pontuacao = list(punctuation)
        
        dicionario_caracteres =['º', '¿','¾','¾','´','`','“','”','•','ª','–','ö','»','°','«','¬','®','¼','§', '½', '‘', '’', 'ø', '±', '—']
        for item in dicionario_caracteres:
            pontuacao.append(item)

        txt_r = ''.join([char for char in txt if not char.isdigit() and char not in pontuacao])

        return txt_r
#Aplicar a função no dataset
df['Objeto']=df['Objeto'].apply(remover_numeros_pontuacao)<jupyter_output><empty_output><jupyter_text>**Stopwords são palavras utilizadas com muita frequência como: artigos, preposições, conjunções e  outras palavras auxiliares que não têm potencial para contribuir para a caracterização do conteúdo presente no texto**<jupyter_code>#Exibir as stopwords da nltk
stop= setstopwords = set(nltk.corpus.stopwords.words('portuguese'))
print(stop) 
#Função para remover stopwords
def RemoveStopWords(instancia):
    stopwords = set(nltk.corpus.stopwords.words('portuguese'))
    palavras = [i for i in instancia.split() if not i in stopwords]
    return (" ".join(palavras))
#Aplicar a função no dataset
df['Objeto']=df['Objeto'].apply(RemoveStopWords)
#Verificar se todas as funções foram aplicadas
df['Objeto'].head()<jupyter_output><empty_output><jupyter_text>**Feature Engineering** **Distribuição de frequência: retorna as palavras que ocorrem com a maior frequência no texto****Tokenization: separa o texto em tokens, segmentando as palavras**<jupyter_code># Junta todos os textos em uma string
todas_palavras = ' '.join([texto for texto in df['Objeto']])
# Tokenizar para verificar a frequência de cada palavra
token_espaco = tokenize.WhitespaceTokenizer()
token_frase = token_espaco.tokenize(todas_palavras)
# Frequência das palavras
frequencia = nltk.FreqDist(token_frase)
df_frequencia = pd.DataFrame({"Palavra": list(frequencia.keys()),
                              "Frequência": list(frequencia.values())})
#  20 Palavras mais comuns na base de dados
df_frequencia.sort_values(by='Frequência', inplace=True, ascending=False)
df_frequencia.head(20)<jupyter_output><empty_output><jupyter_text>**Wordcloud (nuvem de palavras): visualização dos dados onde o tamanho de cada palavra é determinado pela sua frequência em um texto, ou seja, quanto maior for a ocorrência daquela palavra maior será sua disposição na nuvem**<jupyter_code>def nuvem_palavras(texto, coluna_texto,sentimento):
    # Separar nuvem por sentimento
    texto = texto.query(f"Classificação == '{sentimento}'")
    # Junta todos os textos em uma string
    todas_palavras = ' '.join([texto for texto in texto[coluna_texto]])
    # Gerar a nuvem de palavras
    nuvem_palvras = WordCloud(width= 800, height= 500,
                              max_font_size = 110,
                              collocations = False).generate(todas_palavras)
    # Plotar a nuvem de palavras
    plt.figure(figsize=(24,12))
    plt.imshow(nuvem_palvras, interpolation='none')
    plt.axis("off")
    plt.show()
#A função abaixo retorna uma lista ordenada com o nome de cada tipo de classificação
sorted(set(df['Classificação']))
#Exibir a nuvem de palavras de acordo com a classificação
nuvem_palavras(df, "Objeto", "Obras")<jupyter_output><empty_output><jupyter_text>**Modelo de Naive Bayes****É um classificador probabilístico baseado no Teorema de Bayes que será utilizado para a classificação dos textos. O modelo produz estimativas de probabilidade para cada classe. O algoritmo foi escolhido por apresentar melhor resultado em situações de classes múltiplas, regra de independência e por sua taxa de sucesso em comparação com outros algoritmos. O modelo multinomial trabalha com a frequência que cada termo ocorre no documento** **Variáveis**<jupyter_code>#Declarar as variáveis
obj_contrato = df['Objeto'].values
clas_contrato = df['Classificação'].values<jupyter_output><empty_output><jupyter_text>**Vetorização dos dados: converte cada palavra em vetores***   **O CountVectorizer é responsável pela vetorização dos dados, por transformar o texto em um vetor com base na frequência (contagem) de cada palavra que ocorre no texto e pela transformação dos dados em uma matriz de frequência**<jupyter_code>#Definição do vetorizador
vectorizer = CountVectorizer(analyzer= 'word')
from sklearn.feature_extraction.text import CountVectorizer<jupyter_output><empty_output><jupyter_text>*   **O vectorizer.fit_transform aplica o vetorizador transformando os dados em uma matriz esparsa**<jupyter_code>#Aplicar o vetorizador
vect_obj = vectorizer.fit_transform(obj_contrato)<jupyter_output><empty_output><jupyter_text>**Aplicar o modelo**<jupyter_code>#Naive Bayes Multinomial
modelo = MultinomialNB() 
modelo.fit(vect_obj, clas_contrato)<jupyter_output><empty_output><jupyter_text>**Testar o modelo**<jupyter_code>teste = ['Água 98', 'Acrécimo microcomputador contrato Aquisição', 'Prestação serviços limpeza asseio conservação']
#transforma os dados de teste em vetores de palavras
vect_teste = vectorizer.transform(teste)
#classificação com o modelo treinado
for a, b in zip(teste,modelo.predict(vect_teste)):
    print( a +', '+ b)
#probabilidade de classificação em cada conjunto de dados de teste
print(modelo.classes_)
modelo.predict_proba(vect_teste).round(2)<jupyter_output>[' Registro de Preços' 'Aquisição' 'Assinaturas e Publicações'
 'Contratos de Aquisição' 'Convênios' 'Cursos' 'Fornecimento Parcelado'
 'Fornecimento contínuo' 'Informática' 'Limpeza' 'Locação'
 'Locação de Equipamentos' 'Locação de Imóveis' 'Manutenção' 'Obras'
 'Obras ' 'Outros' 'Projetos' 'Registro de Preços' 'Serviços'
 'Serviços Contínuos' 'Serviços contínuos' 'Vigilância']<jupyter_text>**Validação e métricas do modelo** **Validação Cruzada: técnica que visa entender como o modelo generaliza, ou seja, como ele se comporta ao prever um dado que nunca viu. Podemos fazer k combinações,  cada cruzamento retorna um valor que é a taxa de acerto daquela combinação.****Passamos como parâmetro dessa função o nosso modelo, os valores dos dados para treino e quantas combinações (cv) queremos que ela faça**<jupyter_code>#Validação cruzada 
resultados = cross_val_predict(modelo, vect_obj, clas_contrato, cv=10)<jupyter_output>/usr/local/lib/python3.6/dist-packages/sklearn/model_selection/_split.py:667: UserWarning: The least populated class in y has only 1 members, which is less than n_splits=10.
  % (min_groups, self.n_splits)), UserWarning)<jupyter_text>**A acurácia é a proporção de casos que foram corretamente previstos, sejam eles verdadeiro positivo ou verdadeiro negativo**<jupyter_code>#Métrica de acurácia do modelo
metrics.accuracy_score(clas_contrato,resultados)<jupyter_output><empty_output><jupyter_text>**A função metrics.classification_report retorna as seguintes métricas:*** **Precisão: indica a relação entre as previsões positivas realizadas corretamente e todas as previsões positivas (incluindo as falsas).  A principal utilização desta métrica para o modelo é minimizar os falsos positivos, neste caso, quanto mais perto dos 100% chegarmos melhor.*** **Recall: indica a relação entre as previsões positivas realizadas corretamente e todas as previsões que realmente são positivas (True Positives e False Negatives), indicando o quanto nosso modelo está identificando os casos positivos corretamente***  **O F1 Score: média harmônica entre precisão (que, apesar de ter o mesmo nome, não é a mesma citada acima) e recall**<jupyter_code>#Métrica de precisão do modelo
print(metrics.classification_report(clas_contrato,resultados))<jupyter_output>precision    recall  f1-score   support

       Registro de Preços       0.00      0.00      0.00        17
                Aquisição       0.00      0.00      0.00         1
Assinaturas e Publicações       0.87      0.74      0.80        62
   Contratos de Aquisição       0.95      0.81      0.88       646
                Convênios       0.89      0.74      0.81        34
                   Cursos       0.00      0.00      0.00         8
   Fornecimento Parcelado       0.79      0.94      0.86        87
    Fornecimento contínuo       0.00      0.00      0.00         1
              Informática       0.70      0.86      0.77       206
                  Limpeza       0.90      0.96      0.93       162
                  Locação       0.00      0.00      0.00         1
  Locação de Equipamentos       0.70      0.78      0.74         9
       Locação de Imóveis       0.67      0.52      0.59        23
               Manutenção       0.78      0.76      0.77     [...]
{'fraction_non_alphanumeric': 0.09813114970613485, 'fraction_numerical': 0.017631909132932708, 'mean_word_length': 4.180978446801231, 'pattern_counts': {'":': 2, '<': 85, '<?xml version=': 0, '>': 86, 'https://': 1, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '314672', 'license': 'Apache-2.0', 'n_tokens_mistral': 5420, 'n_tokens_neox': 5240, 'n_words': 1585, 'path': 'CODIGO_NLP.ipynb', 'repo_name': 'millenacastro/Natural-Language-Processing-An-lise-de-Contratos', 'size': '15143'}