Projeto: Análise de Sentimentos em Discursos Políticos

Parte 1: Coleta de dados
Parte 2: Limpeza de dados
Parte 3: Modelagem e Análise de Sentimentos

Esta é a terceira e última parte deste projeto, no qual faremos alguma limpeza, se necessário, dos dados, a modelagem dos tópicos e a análise de sentimentos.

Importando as bibliotecas

Iniciaremos importando as bibliotecas que serão utilizadas. As principais delas são:

  • SpaCy: é uma biblioteca em Python para PLN em escala industrial, vamos utiliza-la para algumas transformações nos textos
  • scikit_learn: para extrair informações dos textos
  • NMF: para extração dos tópicos mais importantes
  • WordCloud: para criação de núvem de palavras
In [1]:
# Manipulação de dados
import os
import re
import sys
import time
import string
import pickle
import requests
import datetime
import numpy as np
import pandas as pd

# Visualização de dados
import matplotlib
import seaborn as sns
import matplotlib.pyplot as plt

# Processamento de Linguagem Natural
import spacy
from spacy.symbols import amod
from collections import Counter
from nltk.corpus import stopwords

# Topic Modeling
import sklearn
from sklearn.feature_extraction import text
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.decomposition import NMF

#importando as libraries necessárias para o wordcloud
from wordcloud import WordCloud, ImageColorGenerator
from PIL import Image

Parte 3 - Topic Modeling

Modelagem de Tópicos é uma forma de mineração de texto, uma forma de identificar padrões. Construindo um corpus e executando uma ferramenta que gera grupos de palavras a respeito do corpus distribuídas em “tópicos”.

Modelagem de tópicos é um método para achar e traçar clusters de palavras (chamado “tópicos” de forma abreviada) em grandes conjuntos de texto.

Aqui tem uma definição completa sobre o tema:

http://journalofdigitalhumanities.org/2-1/topic-modeling-a-basic-introduction-by-megan-r-brett/#topic-modeling-a-basic-introduction-by-megan-r-brett-n-1

Inicialmente carregamos o modelo pré-treinado em português do SpaCy. Esse modelo é de uso geral para prever entidades nomeadas, tags de classes gramaticais e dependências sintáticas e pode ser usado fora da caixa e ajustado em dados mais específicos.

In [2]:
# importando o modelo
import pt_core_news_sm

# instanciando o modelo pré-treinado
sp = pt_core_news_sm.load()

Carregando os discursos para comerçarmos a trabalhar e já vamos dar uma olhada nas primeiras linhas.

In [3]:
# carregando o banco de dados completo
with open('data/db_discurso_limpo.pickle', 'rb') as read_file:
    db_final = pickle.load(read_file)
    
# visualizando
db_final.head()
Out[3]:
data hora link text monologue
0 2020-11-22 11:16:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Primeiramente parabenizo ... senhoras e senhores primeiramente parabenizo a...
1 2020-11-21 11:20:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Antes de adentrarmos o te... senhoras e senhores antes de adentrarmos o tem...
2 2020-11-18 14:52:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Mais que emocionado eu estou muito feliz em p... mais que emocionado eu estou muito feliz em po...
3 2020-11-17 18:12:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Brasília/DF 17 de novembro de 2020 Sua Excelê... brasília/df 17 de novembro de 2020 sua excelên...
4 2020-10-11 20:49:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [ Boa tarde. Senhores e senhoras se me permite... boa tarde. senhores e senhoras se me permitem ...

Faremos algumas funções anônimas que extraem alguns padrões como caracteres alphanuméricos, pontuações e espaços a mais, isso tudo com o uso de regex.

In [4]:
# remover caracteres alphanuméricos
alphanumeric = lambda x: re.sub('\w*\d\w*', '', x)

# remover pontuações
punc = lambda x: re.sub('[%s]' % re.escape(string.punctuation), ' ', x)

# remover espaços duplos
remove_space = lambda x: x.replace('  ', ' ')

# aplicando as funções
db_final['for_spacy'] = (db_final['monologue'].map(alphanumeric).map(punc).map(remove_space))

Vamos dar uma olhada em como ficou.

In [5]:
# visualizando
db_final.head(2)
Out[5]:
data hora link text monologue for_spacy
0 2020-11-22 11:16:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Primeiramente parabenizo ... senhoras e senhores primeiramente parabenizo a... senhoras e senhores primeiramente parabenizo a...
1 2020-11-21 11:20:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Antes de adentrarmos o te... senhoras e senhores antes de adentrarmos o tem... senhoras e senhores antes de adentrarmos o tem...

Passamos o modelo pré-treinado em cada discurso e agora com o modelo aprendido, podemos usufruir dos métodos do SpaCy.

In [6]:
# aplicando o modelo de linguagem (sp) do SpaCy
db_final['spacy_monologue'] = db_final['for_spacy'].map(lambda x: sp(x))

# visualizando
db_final.head(2)
Out[6]:
data hora link text monologue for_spacy spacy_monologue
0 2020-11-22 11:16:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Primeiramente parabenizo ... senhoras e senhores primeiramente parabenizo a... senhoras e senhores primeiramente parabenizo a... (senhoras, e, senhores, primeiramente, paraben...
1 2020-11-21 11:20:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Antes de adentrarmos o te... senhoras e senhores antes de adentrarmos o tem... senhoras e senhores antes de adentrarmos o tem... (senhoras, e, senhores, antes, de, adentrarmos...

Lematização é o processo de extrair o lema de cada palavra, tarefa fundamental em PLN. Não precisamos da palavra inteira, somente do seu lema.

Vamos utilizar o método do SpaCy e vamos informar para utilizar em palavras diferentes de pronomes.

In [7]:
# aplicando lematização
db_final['lemmatized'] = (db_final['spacy_monologue']
                          .map(lambda x: [' '.join(word.lemma_ if word.lemma_ != '-PRON-' else word.text for word in x)][0]))

# visualizando
db_final.head()
Out[7]:
data hora link text monologue for_spacy spacy_monologue lemmatized
0 2020-11-22 11:16:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Primeiramente parabenizo ... senhoras e senhores primeiramente parabenizo a... senhoras e senhores primeiramente parabenizo a... (senhoras, e, senhores, primeiramente, paraben... senhor e senhor primeiramente parabenizar o ar...
1 2020-11-21 11:20:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Antes de adentrarmos o te... senhoras e senhores antes de adentrarmos o tem... senhoras e senhores antes de adentrarmos o tem... (senhoras, e, senhores, antes, de, adentrarmos... senhor e senhor antar de adentrar o temer prin...
2 2020-11-18 14:52:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Mais que emocionado eu estou muito feliz em p... mais que emocionado eu estou muito feliz em po... mais que emocionado eu estou muito feliz em po... (mais, que, emocionado, eu, estou, muito, feli... mais que emocionar eu estar muito feliz em pod...
3 2020-11-17 18:12:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Brasília/DF 17 de novembro de 2020 Sua Excelê... brasília/df 17 de novembro de 2020 sua excelên... brasília df de novembro de sua excelência vlad... (brasília, df, de, novembro, de, sua, excelênc... brasília df de novembro de suar excelência vla...
4 2020-10-11 20:49:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [ Boa tarde. Senhores e senhoras se me permite... boa tarde. senhores e senhoras se me permitem ... boa tarde senhores e senhoras se me permitem e... (boa, tarde, senhores, e, senhoras, se, me, pe... bom tardar senhor e senhor se me permitir eu q...

Aqui nós criamos uma lista de stop words palavras que não são relevantes para esta análise e também vamos utilizar a função CountVectorizer, no qual nos apresenta as palavras e as suas respectivas frequencias nos textos.

Podemos passar alguns parâmetros para ajudar com as análises das palavras certas:

  • max_df: ignora palavras que são comuns entre os documentos e acabam se tornando stopwords, podemos passar em números absolutos ou proporção dos documentos, neste caso passaremos 0.95, ou seja, as palavras que aparecem em mais de 95% dos documentos.
  • min_df: ignora os documentos que aparecem menos que o hiperparâmetro passado, neste caso vamos passar o número 2 e isso significa que serão ignoradas as palavras que aparecem em menos de 2 documentos.
  • ngram_range: é o limite inferior e superior de palavras a serem extraidas, no nosso caso utilizaremos somente bigrams, ou seja, (2,2).
  • max_features: Com esse parâmetro podemos limitar a quantidade de palavras extraidas, isso ajuda porque dependendo do tamanho do texto, podemos ter muitas palavras e como nós já teremos uma matriz esparsa, essa quantidade de zeros pode ser muito maior e isso pode ser ruim para o nosso modelo, depende do seu objetivo e qual fica melhor, no nosso caso utilizarei como 5000.
In [8]:
# instanciando a lista de stopwords
stopwords = set(stopwords.words('portuguese'))

# Configurando max_df para 0,5 e min_df = 2, porque estes forneceram os melhores tópicos
cv = CountVectorizer(stop_words=stopwords, max_df=0.95, min_df=2, ngram_range=(2,2), max_features=5000)

# Cria a matriz de documentos, que basicamente contém palavras em representações numéricas
docterm_matrix = cv.fit_transform(db_final.loc[:, 'lemmatized'])

Se desejarmos podemos ver as palavras ignoradas internamente com os parâmetros informados, podemos utilizar o método stop_words_.

Assim como também podemos checar o vocabulário que restou com suas respectivas frequencias com o método vocabulary_.

In [9]:
## palavras removidas
# cv.stop_words_

# vocabulario com as respectivas frequencias
cv.vocabulary_
Out[9]:
{'senhor senhor': 3613,
 'arábio saúde': 274,
 'ter escolher': 4304,
 'parir todo': 2636,
 'longo ano': 1941,
 'melhorar prático': 2046,
 'bem comer': 331,
 'senhor brasil': 3583,
 'brasil ser': 458,
 'ser país': 3836,
 'desenvolvimento sustentável': 960,
 'oportunidade parir': 2422,
 'parir população': 2599,
 'governar ter': 1669,
 'umar maior': 4613,
 'comércio investimento': 745,
 'mundial ser': 2193,
 'acordo comerciar': 35,
 'mercosul união': 2053,
 'união europeu': 4673,
 'europeu associação': 1397,
 'associação europeu': 284,
 'europeu livrar': 1398,
 'livrar comércio': 1935,
 'coreia sul': 831,
 'entrar brasil': 1197,
 'brasil eua': 418,
 'facilitação comércio': 1433,
 'combater corrupção': 666,
 'corrupção estar': 834,
 'estar construir': 1304,
 'aberto parir': 2,
 'parir mundo': 2576,
 'cada vez': 520,
 'último ano': 4985,
 'ano brasil': 154,
 'brasil passar': 443,
 'quase bilião': 3327,
 'bilião mear': 366,
 'garantir segurança': 1613,
 'segurança alimentar': 3557,
 'ser realizar': 3888,
 'terra parir': 4422,
 'parir agricultura': 2497,
 'parir pecuário': 2592,
 'preservar vegetação': 3124,
 'vegetação nativo': 4686,
 'ter orgulhar': 4360,
 'trabalhar sempre': 4514,
 'sempre parir': 3570,
 'parir manter': 2566,
 'emissão carbono': 1167,
 'dever ser': 1020,
 'primeiro ser': 3247,
 'cenário mundial': 565,
 'ser responsável': 3901,
 'ser umar': 3959,
 'maior economia': 1977,
 'economia mundo': 1139,
 'umar vez': 4666,
 'vez ter': 4774,
 'orgulhar dizer': 2428,
 'dizer brasil': 1084,
 'matriz energético': 2017,
 'entrar país': 1206,
 'manter firmar': 2001,
 'firmar compromisso': 1560,
 'suar plenitude': 4130,
 'aqui ser': 259,
 'ser dar': 3690,
 'estar certo': 1298,
 'mudar ir': 2183,
 'ir continuar': 1825,
 'país povo': 2730,
 'povo parir': 3069,
 'parir tornar': 2638,
 'mundo realmente': 2204,
 'obrigar todo': 2374,
 'querer fazer': 3359,
 'fazer umar': 1520,
 'história brasil': 1729,
 'brasil ter': 461,
 'ter umar': 4412,
 'umar cultura': 4588,
 'entrar nação': 1203,
 'nação ser': 2241,
 'ser povo': 3859,
 'família brasileiro': 1460,
 'brasileiro poder': 479,
 'dessar povo': 976,
 'colocar lugar': 658,
 'poder ser': 2886,
 'ter sim': 4402,
 'parir criar': 2521,
 'entrar nó': 1204,
 'unir ser': 4672,
 'liberdade ser': 1929,
 'comer homem': 699,
 'homem comer': 1743,
 'comer presidente': 715,
 'ver amarelar': 4703,
 'existir umar': 1425,
 'melhor outro': 2036,
 'existir ser': 1423,
 'ser homem': 3755,
 'ser escolher': 3718,
 'nó ser': 2349,
 'umar satisfação': 4653,
 'poder umar': 2900,
 'população mundial': 2950,
 'comércio internacional': 744,
 'crise sanitário': 846,
 'ser possível': 3854,
 'buscar resultar': 503,
 'bemestar prosperidade': 361,
 'prosperidade parir': 3292,
 'parir país': 2591,
 'último reunião': 4995,
 'todo medir': 4448,
 'necessário parir': 2244,
 'parir combater': 2513,
 'buscar promover': 501,
 'umar grave': 4602,
 'grave crise': 1697,
 'estar vencer': 1386,
 'somar esforço': 4081,
 'parir buscar': 2509,
 'tratamento precoce': 4521,
 'ser objetivo': 3811,
 'ser precisar': 3862,
 'precisar ressaltar': 3096,
 'parir decidir': 2526,
 'tomar vacinar': 4495,
 'poder servir': 2887,
 'economia mundial': 1138,
 'presidente banco': 3128,
 'contribuir parir': 808,
 'dessar maneiro': 969,
 'pandemia ser': 2479,
 'ser ainda': 3628,
 'medir tomar': 2030,
 'tomar pelar': 4490,
 'pelar governar': 2767,
 'milhão brasileiro': 2061,
 'estar município': 1344,
 'ser superar': 3933,
 'vidar pessoa': 4831,
 'querer dar': 3352,
 'dar continuidade': 875,
 'reformar estruturar': 3427,
 'brasil senhor': 457,
 'reformar omc': 3428,
 'parir recuperação': 2614,
 'rápido possível': 3499,
 'subsídio parir': 4151,
 'parir bem': 2507,
 'algum país': 107,
 'país buscar': 2716,
 'sobrar comer': 4053,
 'internacional ter': 1803,
 'ter certeza': 4269,
 'fundamental parir': 1593,
 'haver tempo': 1721,
 'contar apoiar': 785,
 'vosso excelência': 4916,
 'parir dar': 2525,
 'dar início': 884,
 'estar feliz': 1318,
 'ter preço': 4386,
 'assim comer': 276,
 'comer ter': 724,
 'formar comer': 1570,
 'ter receber': 4392,
 'qualquer lugar': 3320,
 'lugar brasil': 1945,
 'ter ir': 4328,
 'ir obrigar': 1841,
 'obrigar deus': 2369,
 'oportunidade ter': 2425,
 'umar passagem': 4632,
 'passagem bíblico': 2687,
 'suar forçar': 4111,
 'ser pequeno': 3841,
 'povo brasileiro': 3057,
 'brasileiro ser': 482,
 'ser cristão': 3685,
 'ser temente': 3938,
 'temente deus': 4206,
 'deus sempre': 1008,
 'enfrentar adversidade': 1180,
 'passar momento': 2694,
 'momento difícil': 2144,
 'passar ser': 2703,
 'ser usar': 3960,
 'virar ter': 4884,
 'ter comer': 4273,
 'atingir objetivo': 289,
 'ser algo': 3629,
 'algo parecer': 94,
 'viver aqui': 4890,
 'aqui trabalhar': 265,
 'pedaço terra': 2739,
 'terra ser': 4424,
 'ser filho': 3739,
 'passar aqui': 2690,
 'ser ser': 3921,
 'existir satisfação': 1422,
 'satisfação maior': 3536,
 'parir umar': 2644,
 'umar mãe': 4622,
 'parecer comer': 2488,
 'ir ser': 1847,
 'ser reverter': 3904,
 'parir suceder': 2630,
 'aqui terra': 263,
 'vez obrigar': 4768,
 'deus momento': 999,
 'momento estar': 2146,
 'estar lado': 1333,
 'ronaldo caiar': 3496,
 'caiar ser': 524,
 'vez estar': 4761,
 'estar aqui': 1284,
 'vinte quatro': 4848,
 'horar dia': 1755,
 'parecer ser': 2491,
 'ter algum': 4246,
 'ministério economia': 2116,
 'entrar tanto': 1211,
 'tanto obrar': 4185,
 'porque conseguir': 2964,
 'coisa parir': 643,
 'bem povo': 351,
 'povo ser': 3075,
 'ser trabalhar': 3947,
 'general ramo': 1626,
 'ser dono': 3707,
 'ir todo': 1851,
 'todo certeza': 4440,
 'ir dar': 1827,
 'recurso parir': 3420,
 'parir fazer': 2546,
 'aqui goiá': 230,
 'poder ter': 2891,
 'certeza ter': 588,
 'umar pessoa': 4634,
 'pessoa aqui': 2813,
 'horário ver': 1758,
 'levantar aí': 1916,
 'joão campo': 1877,
 'parlamentar ter': 2658,
 'ter idear': 4325,
 'ser lá': 3780,
 'tomar café': 4486,
 'comer ser': 723,
 'umar caneta': 4578,
 'ser nó': 3809,
 'ministério minar': 2118,
 'minar energia': 2084,
 'ser bom': 3651,
 'bom parir': 376,
 'pessoa ser': 2831,
 'parir economia': 2534,
 'botar pontar': 388,
 'pontar final': 2939,
 'dizer ser': 1100,
 'ser aqui': 3639,
 'ficar casar': 1543,
 'casar economia': 553,
 'economia gente': 1137,
 'gente ver': 1641,
 'ver ser': 4728,
 'ser aplicar': 3637,
 'campar ter': 535,
 'problema social': 3270,
 'comer dizer': 687,
 'dizer aqui': 1083,
 'umar coisa': 4582,
 'respeitar todo': 3470,
 'saber ser': 3513,
 'ter problema': 4387,
 'problema ir': 3264,
 'lá ir': 1962,
 'umar palavra': 4629,
 'suar família': 4109,
 'atingir objetivos': 290,
 'ir parir': 1842,
 'caiar falar': 523,
 'tudo aqui': 4544,
 'quase tudo': 3335,
 'tudo prezar': 4559,
 'prezar caiar': 3177,
 'prezar senador': 3211,
 'senador deputar': 3577,
 'deputar federar': 942,
 'dois ano': 1107,
 'ano governar': 162,
 'nunca ver': 2289,
 'ver poder': 4722,
 'político continuar': 2917,
 'porque saber': 2992,
 'pontar linha': 2940,
 'ser bem': 3649,
 'trazer aqui': 4529,
 'aqui ir': 234,
 'ir passar': 1843,
 'prezar pedrar': 3203,
 'pedrar guimarães': 2743,
 'presidente caixa': 3131,
 'caixa econômica': 525,
 'econômica federal': 1148,
 'agora ter': 67,
 'título propriedade': 4572,
 'agora poder': 64,
 'ser casar': 3662,
 'parir querer': 2611,
 'fazer lá': 1493,
 'estar frente': 1320,
 'apenas umar': 191,
 'coisa todo': 650,
 'todo vir': 4484,
 'vir aqui': 4852,
 'ser bemvindo': 3650,
 'saber disso': 3504,
 'estar junto': 1332,
 'algum coisa': 97,
 'passagem aqui': 2686,
 'ser ter': 3941,
 'ter aqui': 4255,
 'assinar umar': 283,
 'umar empresar': 4593,
 'sair lá': 3521,
 'ser paulo': 3835,
 'portar santo': 2999,
 'progresso parir': 3280,
 'parir região': 2615,
 'produzir ser': 3277,
 'parir norte': 2580,
 'norte sul': 2278,
 'sul brasil': 4161,
 'brasil obviamente': 437,
 'obviamente ser': 2383,
 'dar governar': 881,
 'iniciativo privar': 1793,
 'obrigar pelar': 2371,
 'pelar oportunidade': 2780,
 'deus pelar': 1003,
 'pelar missão': 2776,
 'missão ter': 2132,
 'certeza junto': 582,
 'brasil melhor': 430,
 'melhor parir': 2037,
 'todo nó': 4454,
 'nó obrigar': 2329,
 'brasília df': 487,
 'df novembro': 1031,
 'vladimir putin': 4899,
 'putin presidente': 3309,
 'presidente federação': 3139,
 'cyril ramaphosa': 861,
 'ramaphosa presidente': 3397,
 'presidente repúblico': 3147,
 'áfrico sul': 4947,
 'xi jinping': 4940,
 'jinping presidente': 1870,
 'repúblico popular': 3452,
 'popular chino': 2944,
 'narendra modi': 2237,
 'modi primeiroministro': 2134,
 'primeiroministro repúblico': 3254,
 'repúblico índio': 3455,
 'tudo ser': 4561,
 'ser tratar': 3950,
 'tratar aqui': 4522,
 'aqui estar': 227,
 'buscar umar': 505,
 'umar vacinar': 4663,
 'contra brasil': 804,
 'trabalhar parir': 4511,
 'ter visto': 4417,
 'região amazônica': 3434,
 'umar coincidência': 4581,
 'falar sobrar': 1453,
 'sobrar amazônia': 4050,
 'dizer senhor': 1099,
 'senhor amigo': 3579,
 'polícia federal': 2908,
 'ser importante': 3761,
 'então estar': 1221,
 'próximo dia': 3304,
 'país ter': 2736,
 'dessar país': 974,
 'país ser': 2735,
 'interessar todo': 1800,
 'todo porque': 4465,
 'porque dizer': 2968,
 'nessa região': 2255,
 'região ser': 3441,
 'umar honrar': 4605,
 'honrar estar': 1749,
 'haver ser': 1720,
 'país querer': 2732,
 'presidência turno': 3161,
 'diverso área': 1080,
 'querer dizer': 3355,
 'orgulhar ter': 2434,
 'ter conduzir': 4277,
 'ano passar': 171,
 'passar presidência': 2700,
 'resultar concreto': 3474,
 'população ser': 2954,
 'ser dever': 3697,
 'ser cobrar': 3667,
 'suar vontade': 4147,
 'vontade trabalhar': 4914,
 'brics ser': 493,
 'empregar render': 1169,
 'povo primeiro': 3072,
 'primeiro cúpula': 3225,
 'mear umar': 2024,
 'enfrentar umar': 1183,
 'umar crise': 4587,
 'caminhar parir': 528,
 'crescimento econômico': 840,
 'soberania nacional': 4048,
 'estar certar': 1297,
 'espaçar parir': 1255,
 'parir ampliar': 2501,
 'setores privar': 4006,
 'privar país': 3256,
 'desde início': 952,
 'início pandemia': 1808,
 'tratar simultaneamente': 4527,
 'simultaneamente responsabilidade': 4029,
 'nenhum país': 2250,
 'país poder': 2729,
 'situação excepcional': 4039,
 'brasil desde': 409,
 'reformar ser': 3430,
 'solução problema': 4080,
 'mundo ter': 2206,
 'realidade ser': 3402,
 'superar desafio': 4169,
 'país brasil': 2715,
 'ser instituição': 3764,
 'combater vírus': 668,
 'senhor líder': 3598,
 'internacional brasil': 1801,
 'lutar parir': 1951,
 'sistema internacional': 4034,
 'pelar liberdade': 2771,
 'país parir': 2728,
 'termo umar': 4419,
 'ser fundamental': 3743,
 'retomar crescimento': 3475,
 'ser necessário': 3805,
 'nação unir': 2243,
 'temer ser': 4207,
 'brasil índio': 467,
 'índio áfrico': 4981,
 'passar ter': 2704,
 'ter brasil': 4262,
 'neste ano': 2263,
 'índio ser': 4979,
 'bom tardar': 382,
 'turismo ser': 4563,
 'brasil estar': 417,
 'ricardo salles': 3484,
 'ministrar mear': 2095,
 'mear ambientar': 2018,
 'ambientar estar': 135,
 'querer ir': 3362,
 'parir lá': 2564,
 'lá ter': 1968,
 'umar taxar': 4659,
 'dia umar': 1053,
 'praia ser': 3081,
 'passar parir': 2697,
 'parir ir': 2560,
 'querer falar': 3358,
 'aqui porque': 250,
 'porque ser': 2993,
 'comer ir': 700,
 'parir frente': 2550,
 'ter agora': 4241,
 'agora ir': 59,
 'ir tentar': 1849,
 'prezar ricardo': 3209,
 'marcelo antônio': 2010,
 'sobrar futurar': 4055,
 'democracia liberdade': 926,
 'ter ninguém': 4351,
 'ficar preocupar': 1547,
 'vencer obstáculo': 4691,
 'brasil ir': 424,
 'ir buscar': 1819,
 'buscar fazer': 498,
 'fazer partir': 1503,
 'depender mim': 938,
 'depender parlamentar': 939,
 'dificuldade parir': 1059,
 'parir vender': 2646,
 'vender facilidade': 4697,
 'talvez parir': 4178,
 'brasil umar': 463,
 'umar outro': 4628,
 'outro coisa': 2442,
 'coisa simples': 648,
 'simples ter': 4028,
 'tempo atrás': 4212,
 'jet ski': 1869,
 'porque ter': 2995,
 'lá atrás': 1954,
 'parir tirar': 2635,
 'umar provar': 4640,
 'corrente ano': 832,
 'comandante marinho': 664,
 'estar longe': 1334,
 'valer ribeira': 4681,
 'época ser': 4970,
 'ser rir': 3906,
 'naquela região': 2232,
 'região pobre': 3439,
 'pobre estar': 2845,
 'estar ser': 1375,
 'paulo ser': 2707,
 'parir explorar': 2543,
 'poder fazer': 2866,
 'chegar lá': 608,
 'ter país': 4371,
 'país mundo': 2724,
 'ter responsabilidade': 4395,
 'parecer estar': 2489,
 'tempo todo': 4227,
 'todo tomar': 4480,
 'parir nado': 2577,
 'nado ter': 2230,
 'ano vir': 180,
 'vir nó': 4871,
 'nó ir': 2322,
 'ir resolver': 1845,
 'resolver questão': 3461,
 'porque daí': 2966,
 'porque ir': 2979,
 'ser brasil': 3653,
 'chefe estar': 597,
 'estar falar': 1316,
 'falar poder': 1449,
 'poder gastar': 2869,
 'bilião real': 369,
 'real parir': 3400,
 'parir investir': 2559,
 'baía angra': 327,
 'algo parir': 95,
 'todo respeitar': 4472,
 'vez ser': 4773,
 'ser melhor': 3790,
 'angra ter': 150,
 'umar multar': 4621,
 'mil real': 2058,
 'dia estar': 1036,
 'estar lá': 1336,
 'lá ser': 1967,
 'dizer ir': 1087,
 'melhorar bastante': 2044,
 'ser melhorar': 3791,
 'homem campar': 1742,
 'ibama icmbio': 1763,
 'dinheiro ser': 1067,
 'ser parir': 3825,
 'revogar decretar': 3481,
 'poder perguntar': 2883,
 'decretar ambiental': 905,
 'ser presidente': 3866,
 'presidente ser': 3150,
 'mandar projeto': 1991,
 'projeto parir': 3283,
 'ver ter': 4730,
 'parir gente': 2553,
 'ainda ter': 85,
 'gente poder': 1636,
 'poder estar': 2862,
 'nó ter': 2350,
 'ter acontecer': 4239,
 'ter interessar': 4327,
 'ser verdade': 3964,
 'verdade comer': 4733,
 'comer goiá': 696,
 'ambientar parir': 137,
 'parir turismo': 2643,
 'país estar': 2721,
 'parir nó': 2581,
 'ser dinheiro': 3701,
 'vezar umar': 4792,
 'parir mim': 2570,
 'mim ser': 2082,
 'dinheiro parir': 1066,
 'cartão corporativo': 550,
 'ser três': 3953,
 'ter nado': 4348,
 'nado ver': 2231,
 'parir alimentar': 2500,
 'energia elétrica': 1177,
 'lá alvorar': 1953,
 'parir ser': 2624,
 'onde querer': 2409,
 'querer querer': 3372,
 'sacar mil': 3518,
 'pra mim': 3080,
 'ser caro': 3661,
 'caro demais': 544,
 'dois mil': 1112,
 'poder continuar': 2856,
 'assim ser': 279,
 'quase ninguém': 3332,
 'ninguém poder': 2267,
 'poder ir': 2871,
 'maneiro ter': 1999,
 'umar pequeno': 4633,
 'caro ir': 545,
 'ir lá': 1836,
 'bilião dólar': 364,
 'dólar ano': 1133,
 'ano ter': 178,
 'certeza achar': 575,
 'achar ser': 18,
 'ser aí': 3646,
 'rir janeiro': 3489,
 'atrás ter': 298,
 'então nó': 1230,
 'próprio vidar': 3301,
 'cabra peste': 508,
 'ser ministrar': 3797,
 'ministrar ser': 2100,
 'ser onyx': 3817,
 'isenção visto': 1858,
 'coisa ser': 647,
 'ser feito': 3736,
 'ser super': 3932,
 'ter caro': 4266,
 'caro ter': 547,
 'ter sentimento': 4400,
 'cão sarnento': 867,
 'mulher praia': 2190,
 'economia ser': 1144,
 'ser vital': 3970,
 'parir vidar': 2648,
 'dizer lá': 1088,
 'imprensar brasil': 1776,
 'ser maior': 3782,
 'maior próprio': 1979,
 'comer novo': 705,
 'ter mostrar': 4344,
 'ser outro': 3819,
 'nó aqui': 2292,
 'aqui umar': 266,
 'fechar tudo': 1523,
 'pelar ter': 2793,
 'ter conhecimento': 4279,
 'lá estar': 1960,
 'ser diferente': 3699,
 'brasileiro umar': 485,
 'umar segundo': 4654,
 'ter enfrentar': 4298,
 'ser vidar': 3966,
 'vidar ter': 4839,
 'comer chefe': 680,
 'estar ter': 1378,
 'ter tomar': 4408,
 'tomar decisão': 4488,
 'ter decidir': 4287,
 'nó poder': 2336,
 'ser líder': 3781,
 'trabalhar ser': 4516,
 'ser eleito': 3712,
 'lugar ter': 1949,
 'ter governador': 4316,
 'governador ser': 1647,
 'ser estar': 3722,
 'estar brasil': 1290,
 'aqui sul': 261,
 'rir grande': 3488,
 'grande sul': 1694,
 'sul ser': 4166,
 'sul ir': 4162,
 'parir brasil': 2508,
 'brasil nó': 435,
 'nó querer': 2342,
 'nado além': 2223,
 'além disso': 123,
 'falto parir': 1459,
 'ser empregar': 3713,
 'empregar ter': 1171,
 'ter ter': 4406,
 'parir conversar': 2520,
 'conversar comigo': 812,
 'conversar ministrar': 816,
 'ministrar ter': 2103,
 'ter buscar': 4263,
 'buscar solução': 504,
 'coragem parir': 825,
 'saber comer': 3501,
 'comer funcionar': 695,
 'ali ter': 116,
 'umar corrente': 4586,
 'forte esquerdo': 1575,
 'parecer brasil': 2487,
 'ser passível': 3831,
 'tudo poder': 4558,
 'poder mudar': 2876,
 'eleger presidente': 1159,
 'presidente porque': 3146,
 'milhão real': 2067,
 'ter todo': 4407,
 'vidar aqui': 4804,
 'ser problema': 3874,
 'todo ter': 4479,
 'ter paz': 4370,
 'nado poder': 2228,
 'poder tomar': 2894,
 'pegar umar': 2745,
 'parir tentar': 2633,
 'então pessoal': 1236,
 'ter outro': 4361,
 'aí vir': 315,
 'vir umar': 4880,
 'parir cá': 2524,
 'ser coisa': 3668,
 'escola ser': 1251,
 'comer mudar': 703,
 'mudar destinar': 2181,
 'destinar brasil': 985,
 'macri argentino': 1973,
 'suar político': 4132,
 'umar horar': 4606,
 'agora comer': 55,
 'ser ficar': 3738,
 'ter bem': 4258,
 'taxar juro': 4200,
 'brasil poder': 446,
 'parir lado': 2562,
 'deus céu': 995,
 'estar disposição': 1310,
 'ver pessoa': 4721,
 'parir chegar': 2512,
 'criticar falar': 848,
 'todo querer': 4471,
 'querer chegar': 3348,
 'sim parir': 4020,
 'achar ter': 19,
 'precisar ajudar': 3089,
 'ajudar parir': 89,
 'auxílio emergencial': 305,
 'quase milhão': 3330,
 'perder tudo': 2806,
 'vender biscoitar': 4696,
 'vender matar': 4698,
 'agora ser': 66,
 'ir morrer': 1838,
 'aqui todo': 264,
 'todo mundo': 4452,
 'mundo ir': 2200,
 'dia ser': 1049,
 'realidade ter': 3403,
 'deixar ser': 917,
 'parir imprensar': 2555,
 'estar ali': 1280,
 'peitar aberto': 2747,
 'geração ser': 1643,
 'ano idade': 164,
 'hoje dia': 1734,
 'ser todo': 3945,
 'umar realidade': 4644,
 'pessoal saber': 2836,
 'ter riqueza': 4396,
 'poder dizer': 2860,
 'ser feliz': 3737,
 'feliz ter': 1537,
 'país rico': 2733,
 'haver pouco': 1719,
 'chefiar estar': 600,
 'estar dizer': 1311,
 'fogo amazônia': 1566,
 'brasil comer': 402,
 'dar ser': 893,
 'saber ter': 3516,
 'ter ser': 4401,
 'ninguém ter': 2270,
 'ter nó': 4354,
 'riqueza minerar': 3487,
 'minerar biodiversidade': 2086,
 'dar liberdade': 885,
 'liberdade parir': 1927,
 'querer produzir': 3371,
 'daí ser': 898,
 'ter tentar': 4405,
 'tentar mudar': 4233,
 'ano ir': 165,
 'ir aparecer': 1813,
 'aparecer ser': 187,
 'curtir espaçar': 859,
 'espaçar tempo': 1256,
 'mudar ser': 2187,
 'aqui poder': 249,
 'ir sofrer': 1848,
 'sofrer umar': 4073,
 'porque estar': 2970,
 'falar aqui': 1437,
 'verdade ser': 4745,
 'fake news': 1434,
 'porque passar': 2989,
 'ano dentro': 157,
 'dentro câmara': 933,
 'pedir deus': 2740,
 'deus parir': 1002,
 'parir ter': 2634,
 'umar oportunidade': 4626,
 'mudar brasil': 2180,
 'estar preocupar': 1360,
 'tardar ser': 4195,
 'umar grande': 4601,
 'grande satisfação': 1691,
 'satisfação estar': 3534,
 'aqui realmente': 257,
 'nó apresentar': 2291,
 'especial pelar': 1260,
 'relacionamento ter': 3445,
 'prezar parlamentar': 3199,
 'jorginho mello': 1871,
 'santo catarino': 3531,
 'saudar todo': 3547,
 'todo prezar': 4469,
 'prezar ministro': 3195,
 'satisfação vêlos': 3543,
 'resolver disputar': 3458,
 'disputar presidência': 1075,
 'começar andar': 732,
 'andar pelar': 148,
 'pelar brasil': 2749,
 'minar gerar': 2085,
 'levar parir': 1920,
 'falar ter': 1454,
 'ter ano': 4251,
 'suar propriedade': 4136,
 'pouco ser': 3045,
 'parir ver': 2647,
 'ver umar': 4731,
 'ficar feliz': 1544,
 'efeito colateral': 1156,
 'poder falar': 2864,
 'falar brasil': 1439,
 'parir povo': 2602,
 'povo ainda': 3053,
 'ter coisa': 4271,
 'coisa comum': 635,
 'então ser': 1240,
 'ser pouco': 3858,
 'pouco paranaense': 3037,
 'trabalhar lá': 4508,
 'sete filho': 4001,
 'dentista prático': 930,
 'ter algo': 4245,
 'verdade ter': 4746,
 'ser secretário': 3913,
 'general silvar': 1627,
 'silvar luna': 4010,
 'chave sucesso': 595,
 'ser fácil': 3745,
 'alguém falar': 113,
 'passar saber': 2702,
 'ser dizer': 3705,
 'parir tudo': 2642,
 'tudo parir': 4556,
 'itaipu binacional': 1859,
 'lá umar': 1970,
 'parir escolher': 2539,
 'ministério banco': 2115,
 'banco oficiar': 320,
 'bom ter': 383,
 'ter trabalhar': 4409,
 'trabalhar dessar': 4502,
 'dessar empresar': 965,
 'dizer estar': 1085,
 'estar equivocar': 1313,
 'então fazer': 1223,
 'fazer realmente': 1511,
 'comer fazer': 694,
 'fazer diferençar': 1483,
 'aqui ministrar': 236,
 'ministrar tarcísio': 2102,
 'tarcísio infraestrutura': 4191,
 'respeitar demais': 3465,
 'quase todo': 3334,
 'quase nado': 3331,
 'nado fazer': 2225,
 'fazer fazer': 1488,
 'fazer ter': 1516,
 'graça deus': 1698,
 'governar dar': 1654,
 'tarcísio ser': 4194,
 'ser capitão': 3660,
 'capitão exército': 539,
 'formar pelar': 1572,
 'pelar ime': 2768,
 'participar dessar': 2659,
 'dessar projeto': 977,
 'coração todo': 830,
 'ministério parir': 2119,
 'pelar tudo': 2795,
 'tudo indicar': 4552,
 'bom ser': 380,
 'afinal contar': 52,
 'contar brasil': 786,
 'brasil crescer': 407,
 'milhão habitante': 2063,
 'habitante ano': 1708,
 'lá frente': 1961,
 'energia brasil': 1176,
 'ter nessa': 4350,
 'nessa área': 2257,
 'área outro': 4960,
 'outro país': 2455,
 'comer bem': 677,
 'bem dizer': 336,
 'agora haver': 58,
 'vir ser': 4876,
 'ser ninguém': 3806,
 'comer nó': 707,
 'amazônica ser': 132,
 'país preservar': 2731,
 'preservar mear': 3123,
 'todo aqui': 4436,
 'poder bem': 2853,
 'ter dar': 4286,
 'dar umar': 894,
 'acusar ser': 47,
 'grande partir': 1688,
 'potencializar porque': 3019,
 'estar jogar': 1330,
 'sim umar': 4027,
 'mundo melhor': 2201,
 'fazer sentir': 1513,
 'satisfação ser': 3538,
 'satisfação ter': 3539,
 'ter governar': 4317,
 'governar ser': 1668,
 'ministério técnico': 2123,
 'realmente ter': 3411,
 'ter coragem': 4283,
 'comer ministrar': 702,
 'ministrar bento': 2089,
 'umar maneiro': 4614,
 'parlamentar ser': 2657,
 'modo nó': 2135,
 'poder sim': 2888,
 'sim ser': 4025,
 'ser bastante': 3648,
 'bastante rápido': 325,
 'ser obviamente': 3814,
 'continuar ser': 802,
 'oportunidade ser': 2424,
 'aqui obrigar': 242,
 'obrigar ter': 2373,
 'confiar mim': 764,
 'presidência repúblico': 3160,
 'abraçar todo': 4,
 'querer estar': 3357,
 'ser sinal': 3926,
 'pelar menos': 2773,
 'menos ano': 2047,
 'tudo passar': 4557,
 'passar rápido': 2701,
 'chegar aqui': 601,
 'comer umar': 728,
 'umar tremendo': 4661,
 'estar parabém': 1351,
 'porque deus': 2967,
 'parir cada': 2510,
 'cada nó': 515,
 'vidar fazer': 4816,
 'fazer momento': 1495,
 'momento aqui': 2141,
 'estar sentir': 1374,
 'formar ser': 1573,
 'marcar parir': 2008,
 'parir sempre': 2622,
 'resolver problema': 3460,
 'vidar ser': 4838,
 'ser assim': 3642,
 'passar passar': 2698,
 'nó atingir': 2293,
 'brasil ainda': 394,
 'ainda ser': 84,
 'ser difícil': 3700,
 'político externo': 2924,
 'acontecer lá': 26,
 'ser interessar': 3766,
 'interessar parir': 1799,
 'aqui dentro': 221,
 'ser certo': 3664,
 ...}

Bigrams mostram duas palavras que aparecem na sequencia, no nosso caso foi "senhor senhor", analizando os discursos podemos perceber que praticamentes todos começam com "senhoras e senhores", no resultado apareceu assim em função do lemma que extraimos anteriormente.

Se o "senhor senhor" ou outras palavras não agregarem valor para o que nós precisamos podemos ignora-las customizando a lista de stopwords.

Vamos dar uma olhada na resultante da nossa matriz esparsa, a quantidade de colunas foi deve ser 5000, que foi o limite que especificamos ao instanciar o CountVectorizer, enquanto a quantidade de linhas, no nosso caso, é a quantidade de discursos.

In [10]:
# dimensão da matriz formada
docterm_matrix.shape
Out[10]:
(150, 5000)

Em seguida, classificamos as palavras no vetor em ordem decrescente dos valores obtidos pelo CountVectorizer e, em seguida, iteramos para extrair as n palavras-chave principais.

Criaremos uma função com o método sort_coo que, essencialmente classifica os valores no vetor enquanto preserva o índice da coluna. Depois de ter o índice da coluna, é realmente fácil procurar o valor da palavra correspondente.

In [11]:
def sort_coo(coo_matrix):
    tuples = zip(coo_matrix.col, coo_matrix.data)
    return sorted(tuples, key=lambda x: (x[1], x[0]), reverse=True)
 
def extract_topn_from_vector(feature_names, sorted_items, topn=10):
    """return n-gram counts in descending order of counts"""
    
    #use only topn items from vector
    sorted_items = sorted_items[:topn]

    results=[]
    
    # word index, count i
    for idx, count in sorted_items:
        
        # get the ngram name
        n_gram=feature_names[idx]
        
        # collect as a list of tuples
        results.append((n_gram,count))

    return results

Executamos as funções para obter os top 10 n_grams de acordo com os hiperparâmetros inseridos no CountVectorizer.

In [12]:
# classificando a contagem
sorted_items=sort_coo(docterm_matrix[0].tocoo())

# relacionando as palavras, classificado pela posição na matriz
feature_names=cv.get_feature_names()
n_grams=extract_topn_from_vector(feature_names,sorted_items,10)

# visualizando o resultado
n_grams
Out[12]:
[('emissão carbono', 3),
 ('desenvolvimento sustentável', 3),
 ('brasil ser', 3),
 ('acordo comerciar', 3),
 ('umar maior', 2),
 ('ter orgulhar', 2),
 ('senhor senhor', 2),
 ('mundial ser', 2),
 ('dizer brasil', 2),
 ('bem comer', 2)]

Agora faremos a mesma coisa, porém ao invés de termos a quantidade, teremos a pontuação de cada palavra que aparecem nos textos, essa pontuação é definida pelo tf-idf, termo em inglês que siginifica term frequency–inverse document frequency, ou em português frequência do termo–inverso da frequência nos documentos.

O valor tf–idf é uma medida estatística que tem o intuito de indicar a importância de uma palavra de um documento em relação a uma coleção de documentos ou em um corpus linguístico. Ela é frequentemente utilizada como fator de ponderação na recuperação de informações e na mineração de dados.

O valor de uma palavra aumenta proporcionalmente à medida que aumenta o número de ocorrências dela em um documento, no entanto, esse valor é equilibrado pela frequência da palavra no corpus. Isso auxilia a distinguir o fato da ocorrência de algumas palavras serem geralmente mais comuns que outras.

Passaremos os mesmos argumentos que passamos com o CountVectorizer.

In [13]:
# instanciando o tf-idf
tfidf_vectorizer = TfidfVectorizer(stop_words=stopwords, max_df=0.95, min_df=2, ngram_range=(2,2), max_features=5000)

# executando o tf-idf
tfidf = tfidf_vectorizer.fit_transform(db_final.loc[:, 'lemmatized'])
In [14]:
# classicando a pontuação
sorted_items=sort_coo(tfidf[0].tocoo())

# relacionando as palavras, classificado pela posição na matriz
feature_names_tfidf=tfidf_vectorizer.get_feature_names()
n_grams_tfidf=extract_topn_from_vector(feature_names,sorted_items,10)

# visualizando o resultado
n_grams_tfidf
Out[14]:
[('acordo comerciar', 0.32280380352377624),
 ('emissão carbono', 0.3039237173451473),
 ('desenvolvimento sustentável', 0.2892791847779546),
 ('mundial ser', 0.21520253568251752),
 ('umar maior', 0.1848758202870261),
 ('ter orgulhar', 0.1848758202870261),
 ('dizer brasil', 0.1848758202870261),
 ('brasil ser', 0.12730200123472807),
 ('sempre parir', 0.10760126784125876),
 ('preservar vegetação', 0.10760126784125876)]

Agora vamos definir os tópicos e as 10 palavras mais importantes por tópico.

In [15]:
# definindo a função para extrair os tópicos
def print_top_words(model, feature_names, n_top_words):
    for topic_idx, topic in enumerate(model.components_):
        message = "Topic #%d: " % topic_idx
        message += " ".join([feature_names[i]
                             for i in topic.argsort()[:-n_top_words - 1:-1]])
        print(message)
    print()

Vamos utilizar o framework NMF do scikit-learn, Fatoração de Matriz Não Negativa e Alocação de Dirichlet Latente (tradução em português) em um corpus de documentos para extrair modelos aditivos da estrutura de tópicos do corpus. A saída é uma lista de tópicos, cada um representado como uma lista de termos (pesos não são mostrados).

Vamos utilizar o modelo para extrair os tópicos

In [16]:
# instanciando o NMF
nmf_tf = NMF(10)

# treinando o tfidf para NMF
nmf_topics1 = nmf_tf.fit_transform(tfidf)
C:\Users\carlo\anaconda3\lib\site-packages\sklearn\decomposition\_nmf.py:1077: ConvergenceWarning: Maximum number of iterations 200 reached. Increase it to improve convergence.
  " improve convergence." % max_iter, ConvergenceWarning)
In [17]:
# Função para encontrar as palavras mais importantes por tópico
def top_words_per_topic(model, terms, topic_names = None):
    for ix, topic in enumerate(model.components_):
        if not topic_names or not topic_names[ix]:
            print("\nTópico ", ix)
        else:
            print("\nTópico '",topic_names[ix],"'")
        print(", ".join([tfidf_vectorizer.get_feature_names()[i] for i in topic.argsort()[:-10 - 1:-1]]))
In [18]:
top_words_per_topic(nmf_tf, tfidf)
Tópico  0
nó ter, ser umar, ter umar, haver pouco, estar aqui, comer ser, dar certar, cada vez, brasil ter, ser ser

Tópico  1
desenvolvimento sustentável, senhor senhor, entrar brasil, arábio saúde, mario abdo, senhor presidente, cada vez, estar unir, união europeu, brasil ser

Tópico  2
acima tudo, brasil acima, deus acima, estar mudar, mudar destinar, tudo deus, acima todo, destinar brasil, estar aqui, dever lealdade

Tópico  3
oferecer parir, narendra modi, ter oferecer, brasil índio, véspera dessar, potencial nação, potencializar fazer, povo assinar, ver horar, índio estar

Tópico  4
aéreo brasileiro, forçar aéreo, forçar armar, todo nó, exército brasileiro, prezar general, evento comer, fernando azevedo, militar agulhar, academiar militar

Tópico  5
ser médico, comprovação científico, naquele momento, umar decisão, ter ser, ser parir, ministrar saudar, salvar vidar, ser umar, ser ser

Tópico  6
paulo guedes, todo nó, câmara senado, auxílio emergencial, senhor governador, deus querer, assim ser, bem comer, atender necessitar, guedes ter

Tópico  7
polícia federal, sergio morar, andré mendonça, ser umar, valer ribeira, vosso excelência, haver pouco, ministrar justiçar, vir cá, ter certeza

Tópico  8
conselho empresarial, empresarial brics, banco desenvolvimento, novo banco, declaração brasília, palácio itamaraty, itamaraty novembro, presidente turno, presidente ndb, chefe estar

Tópico  9
pirar gonçalves, leônidas pirar, calhar norte, amazônia azul, realmente poder, zonar franco, falir amazônia, projeto calhar, franco manaus, josé sarney

Parte 4 - Análise de Sentimentos

Esta é a última parte do trabalho.

Pesquisei alguns frameworks e tive dificuldades de encontrar bibliotecas mais robustas no idioma em português, entenda como um MVP este trabalho e que podemos melhorar mais a frente.

Utilizarei um dicionário de sentimentos, o Sentilex-PT02 que é um léxico de sentimentos para o idioma em português. Ele é útil especialmente para aplicações de mineração de opinião, em particular para detectar sentimentos e classificá-los.

Primeiro vamos carregar o arquivo que contém as palavras e suas polaridades:

  • +1 sentimento positivo
  • 0 neutro
  • -1 sentimento negativo
In [19]:
sentilexpt = open('SentiLex/SentiLex-lem-PT01.txt','r')

Depois de colocar em um objeto, vamos criar o dicionário, porque contém outras informações no arquivo.

In [20]:
#Criando um dicionário de palavras com a respectiva polaridade.
dic_palavra_polaridade = {}
for i in sentilexpt.readlines():
    pos_ponto = i.find('.')
    palavra = (i[:pos_ponto])
    pol_pos = i.find('POL')
    polaridade = (i[pol_pos+4:pol_pos+6]).replace(';','')
    dic_palavra_polaridade[palavra] = polaridade


#Verificando o dicionário
dic_palavra_polaridade
Out[20]:
{'abafado': '-1',
 'abafante': '-1',
 'abaixado': '-1',
 'abalado': '-1',
 'abalizado': '1',
 'abandalhado': '-1',
 'abandonado': '-1',
 'abarcante': '-1',
 'abarrotado': '-1',
 'abastado': '1',
 'abastecido': '0',
 'abatido': '-1',
 'abelhudo': '-1',
 'abençoado': '1',
 'aberrante': '-1',
 'aberrativo': '-1',
 'aberto': '1',
 'abespinhado': '-1',
 'abestalhado': '-1',
 'abilolado': '-1',
 'abismado': '-1',
 'abismal': '-1',
 'abjecto': '-1',
 'abjeto': '-1',
 'abnegado': '1',
 'abobado': '-1',
 'abobalhado': '-1',
 'abolicionista': '0',
 'abolido': '-1',
 'abominador': '-1',
 'abominando': '-1',
 'abominável': '-1',
 'abonado': '1',
 'abonatório': '1',
 'abonecado': '0',
 'aborÃ\xadgene': '-1',
 'aborrecido': '-1',
 'abortado': '-1',
 'abraçado': '1',
 'abrangente': '-1',
 'abrangido': '-1',
 'abrasileirado': '0',
 'abrigado': '0',
 'abrilhantado': '1',
 'abrupto': '-1',
 'abrutalhado': '-1',
 'absentista': '-1',
 'absolutista': '0',
 'absoluto': '0',
 'absolvido': '0',
 'absorto': '-1',
 'absorvente': '1',
 'abstémico': '1',
 'abstémio': '1',
 'abstencionista': '0',
 'abstinente': '0',
 'abstraccionista': '0',
 'abstracto': '-1',
 'abstraÃ\xaddo': '-1',
 'abstrato': '-1',
 'abstruso': '-1',
 'absurdo': '-1',
 'abúlico': '-1',
 'aburguesado': '0',
 'abusado': '-1',
 'abusador': '-1',
 'abusivo': '-1',
 'acabado': '-1',
 'acabadote': '-1',
 'acabrunhado': '-1',
 'acaciano': '-1',
 'académico': '-1',
 'acadêmico': '0',
 'acalorado': '0',
 'açambarcador': '-1',
 'acanhado': '-1',
 'acarinhado': '-1',
 'acatado': '1',
 'acatador': '0',
 'acautelado': '0',
 'accionado': '-1',
 'acéfalo': '-1',
 'aceitável': '1',
 'aceite': '0',
 'aceito': '0',
 'acelerado': '0',
 'acentuado': '0',
 'acerado': '-1',
 'acerbo': '-1',
 'acérrimo': '-1',
 'acertado': '1',
 'aceso': '0',
 'acessÃ\xadvel': '1',
 'achacado': '-1',
 'achado': '0',
 'achatado': '-1',
 'achegado': '1',
 'achinesado': '0',
 'acidentado': '-1',
 'aciganado': '-1',
 'acintoso': '-1',
 'acirrado': '-1',
 'aclamado': '0',
 'acobardado': '-1',
 'acobertado': '-1',
 'acobreado': '0',
 'acocado': '-1',
 'açoitado': '-1',
 'acolhedor': '1',
 'acolhido': '0',
 'acomodadiço': '-1',
 'acomodado': '-1',
 'acomodatÃ\xadcio': '0',
 'acompanhado': '0',
 'acompanhador': '0',
 'acompanhante': '0',
 'aconchegado': '1',
 'acoplado': '-1',
 'acordado': '0',
 'acorde': '1',
 'acorrentado': '-1',
 'acossado': '-1',
 'acostumado': '0',
 'açougueiro': '-1',
 'acre': '-1',
 'acreditado': '1',
 'acreditável': '0',
 'acrescentado': '1',
 'acriançado': '-1',
 'acrisolado': '0',
 'acrÃ\xadtico': '-1',
 'activo': '1',
 'actualista': '0',
 'açucarado': '-1',
 'aculturado': '-1',
 'acurado': '1',
 'acusado': '-1',
 'acutilante': '-1',
 'agudo': '0',
 'adâmico': '-1',
 'adaptado': '0',
 'adaptável': '1',
 'adepto': '1',
 'adequado': '1',
 'aderente': '1',
 'adestrado': '1',
 'adicional': '0',
 'adicto': '1',
 'adido': '-1',
 'adiposo': '-1',
 'adjacente': '0',
 'adjunto': '-1',
 'administrado': '0',
 'administrador': '1',
 'admirado': '-1',
 'admirando': '-1',
 'admirável': '1',
 'admitido': '0',
 'admoestador': '-1',
 'adoentado': '-1',
 'adorado': '1',
 'adorando': '1',
 'adorável': '1',
 'adormecido': '-1',
 'adornado': '-1',
 'adotado': '-1',
 'adstrito': '-1',
 'adubado': '1',
 'adulador': '-1',
 'adulterado': '0',
 'adulterador': '-1',
 'adulterino': '-1',
 'adultero': '-1',
 'adúltero': '-1',
 'adulto': '0',
 'adventÃ\xadcio': '-1',
 'adventista': '0',
 'adversário': '-1',
 'adverso': '-1',
 'advertido': '1',
 'aéreo': '-1',
 'afamado': '1',
 'afanado': '-1',
 'afásico': '-1',
 'afastado': '-1',
 'afável': '1',
 'afectado': '-1',
 'afectivo': '1',
 'afecto': '0',
 'afectuoso': '1',
 'afeiçoado': '1',
 'afeito': '0',
 'afeminado': '-1',
 'aferido': '0',
 'aferrado': '0',
 'afetado': '-1',
 'afetuoso': '1',
 'afiado': '-1',
 'afidalgado': '0',
 'afilado': '0',
 'afilhado': '0',
 'afim': '-1',
 'afinado': '1',
 'afincado': '0',
 'afirmativo': '1',
 'afixo': '1',
 'afligido': '-1',
 'aflitivo': '-1',
 'aflito': '-1',
 'afluente': '1',
 'afobado': '-1',
 'afogado': '-1',
 'afogueado': '0',
 'afoito': '1',
 'afortunado': '1',
 'afrancesado': '0',
 'afrontado': '-1',
 'afrontoso': '-1',
 'afundado': '-1',
 'agachado': '-1',
 'agarotado': '-1',
 'agarrado': '-1',
 'agasalhado': '0',
 'agastadiço': '-1',
 'agastado': '-1',
 'agigantado': '-1',
 'ágil': '1',
 'agiota': '-1',
 'agitado': '-1',
 'agitador': '-1',
 'aglomerado': '0',
 'aglutinante': '-1',
 'agnóstico': '0',
 'agoirento': '-1',
 'agoniado': '-1',
 'agoniante': '-1',
 'agonizante': '-1',
 'agoureiro': '-1',
 'agourento': '-1',
 'agraciado': '0',
 'agradado': '1',
 'agradável': '1',
 'agradecido': '-1',
 'agravado': '-1',
 'agre': '-1',
 'agregado': '0',
 'agressivo': '-1',
 'agressor': '-1',
 'agreste': '-1',
 'agricultor': '-1',
 'agrilhoado': '-1',
 'agrosseirado': '-1',
 'agrupado': '-1',
 'aguado': '-1',
 'aguardado': '0',
 'aguçado': '-1',
 'aguerrido': '0',
 'airoso': '1',
 'ajanotado': '0',
 'ajoelhado': '-1',
 'ajoujado': '-1',
 'ajudado': '1',
 'ajudante': '-1',
 'ajuizado': '1',
 'ajustado': '-1',
 'alado': '1',
 'alapado': '-1',
 'alarmante': '-1',
 'alarmista': '-1',
 'alarve': '-1',
 'albigense': '0',
 'albino': '-1',
 'alcandorado': '0',
 'alcoólatra': '-1',
 'alcoólico': '-1',
 'alcoolizado': '-1',
 'alcunhado': '0',
 'aldeão': '-1',
 'aldrabão': '-1',
 'alegado': '-1',
 'alegre': '1',
 'alegrete': '0',
 'alegrote': '0',
 'aleijado': '-1',
 'aleivoso': '-1',
 'alentado': '1',
 'alentador': '1',
 'alérgico': '0',
 'alerta': '1',
 'alfabetizado': '0',
 'algarvio': '-1',
 'algemado': '-1',
 'alheado': '-1',
 'alheio': '-1',
 'aliado': '0',
 'alicerçado': '1',
 'aliciado': '-1',
 'alienado': '-1',
 'alienÃ\xadgena': '-1',
 'aligeirado': '0',
 'alimentado': '0',
 'alinhado': '1',
 'alinhavado': '-1',
 'alistado': '-1',
 'aliviado': '0',
 'almo': '1',
 'almóada': '0',
 'almorávida': '0',
 'aloirado': '0',
 'alourado': '0',
 'alquebrado': '-1',
 'altamente': '1',
 'altaneiro': '-1',
 'alteroso': '0',
 'altivo': '-1',
 'alto': '0',
 'altruÃ\xadsta': '1',
 'altruÃ\xadstico': '1',
 'aluado': '-1',
 'alucinado': '-1',
 'alucinante': '1',
 'alumbrado': '-1',
 'alumiado': '1',
 'alvejado': '0',
 'alvissareiro': '0',
 'alvo': '1',
 'alvoraçado': '-1',
 'alvoroçado': '-1',
 'amachucado': '-1',
 'amado': '1',
 'amador': '0',
 'amadurecido': '1',
 'amaldiçoado': '-1',
 'amalucado': '-1',
 'amamentado': '0',
 'amaneirado': '-1',
 'amante': '1',
 'amarantino': '-1',
 'amarelado': '-1',
 'amarelo': '-1',
 'amargado': '-1',
 'amargo': '-1',
 'amargurado': '-1',
 'amaricado': '-1',
 'amarrado': '-1',
 'amassado': '-1',
 'amável': '1',
 'ambicioso': '0',
 'ambidestro': '0',
 'ambidextro': '0',
 'ambÃ\xadguo': '-1',
 'ambrosiano': '0',
 'ambulante': '-1',
 'ameaçador': '-1',
 'ameaçante': '-1',
 'amedrontado': '-1',
 'amedrontador': '-1',
 'ameno': '1',
 'americanista': '0',
 'americanófilo': '0',
 'americanófobo': '-1',
 'amestrado': '1',
 'amigalhaço': '1',
 'amigalhão': '1',
 'amigável': '1',
 'amigo': '1',
 'amigo da onça': '-1',
 'amigo da pinga': '-1',
 'amigo dos copos': '-1',
 'amistoso': '1',
 'amnésico': '-1',
 'amontado': '-1',
 'amontoado': '-1',
 'amoral': '-1',
 'amoralista': '-1',
 'amorável': '1',
 'amorfo': '-1',
 'amoroso': '1',
 'amortecido': '-1',
 'amotinado': '-1',
 'amparado': '-1',
 'amputado': '-1',
 'amuado': '-1',
 'anabaptista': '0',
 'anacrónico': '-1',
 'anacrônico': '-1',
 'anafado': '-1',
 'analfabeto': '-1',
 'analisador': '0',
 'analista': '0',
 'análogo': '-1',
 'anão': '-1',
 'anárquico': '-1',
 'anarquista': '-1',
 'ancestral': '-1',
 'ancião': '0',
 'ancorado': '-1',
 'andado': '-1',
 'andante': '-1',
 'andarilho': '0',
 'andejo': '-1',
 'andrajoso': '-1',
 'anedótico': '-1',
 'anémico': '-1',
 'anêmico': '-1',
 'anestesiado': '-1',
 'anexado': '0',
 'anexionista': '0',
 'anexo': '-1',
 'angelical': '1',
 'angélico': '1',
 'angelita': '0',
 'anglicano': '0',
 'anglófilo': '0',
 'anglófobo': '-1',
 'anguloso': '-1',
 'angustiado': '-1',
 'angustiante': '-1',
 'animado': '1',
 'animador': '1',
 'animal': '-1',
 'animista': '0',
 'animoso': '1',
 'aniquilado': '-1',
 'anjinho': '-1',
 'anódino': '-1',
 'anómalo': '-1',
 'anónimo': '-1',
 'anoréctico': '-1',
 'anormal': '-1',
 'ansioso': '-1',
 'antagónico': '-1',
 'antagonista': '-1',
 'antepassado': '0',
 'anticonformista': '-1',
 'antidemocrata': '-1',
 'antidesportista': '-1',
 'antidogmático': '1',
 'antifascista': '1',
 'antifederalista': '0',
 'antigo': '-1',
 'antiliberal': '-1',
 'antiliberalista': '-1',
 'antimilitarista': '0',
 'antinatural': '-1',
 'antinazista': '1',
 'antinómico': '-1',
 'antipapista': '0',
 'antipartidário': '0',
 'antipático': '-1',
 'antipatriota': '-1',
 'antipolÃ\xadtico': '-1',
 'antiquado': '-1',
 'anti-republicano': '-1',
 'antisocial': '-1',
 'antoniano': '0',
 'antonino': '0',
 'antropófago': '-1',
 'anunciador': '-1',
 'apadrinhado': '0',
 'apagado': '-1',
 'apaixonado': '0',
 'apaixonante': '1',
 'apalavrado': '0',
 'apalermado': '-1',
 'apalhaçado': '-1',
 'apalpado': '-1',
 'apancado': '-1',
 'apanhado': '-1',
 'apaniguado': '-1',
 'aparentado': '-1',
 'aparente': '-1',
 'aparolado': '-1',
 'apartado': '-1',
 'aparvalhado': '-1',
 'apatetado': '-1',
 'apático': '-1',
 'apatriota': '-1',
 'apavorado': '-1',
 'apavorante': '-1',
 'apaziguador': '1',
 'apeado': '-1',
 'apedantado': '-1',
 'apedrejado': '-1',
 'apegado': '-1',
 'apelativo': '1',
 'apele': '0',
 'apelidado': '0',
 'aperaltado': '-1',
 'aperfeiçoado': '0',
 'aperreado': '-1',
 'apessoado': '1',
 'apetecÃ\xadvel': '-1',
 'apetitoso': '0',
 'aplaudido': '-1',
 'aplicado': '1',
 'apocalÃ\xadptico': '-1',
 'apócrifo': '-1',
 'apoderado': '1',
 'apoiado': '-1',
 'apolÃ\xadneo': '1',
 'apologético': '0',
 'apologista': '1',
 'apontado': '-1',
 'apopléctico': '-1',
 'apoquentado': '-1',
 'apoquentador': '-1',
 'aposentado': '0',
 'apostado': '1',
 'apóstata': '-1',
 'aposto': '1',
 'apoteótico': '0',
 'aprazÃ\xadvel': '1',
 'apreciado': '1',
 'apreciador': '1',
 'apreciável': '1',
 'apreendido': '-1',
 'apreensivo': '-1',
 'apresentado': '-1',
 'apresentável': '1',
 'apressado': '0',
 'aprimorado': '1',
 'apriorista': '-1',
 'aprisionado': '-1',
 'aprofundado': '1',
 'aprontado': '0',
 'apropriado': '1',
 'aprovado': '1',
 'aproveitador': '-1',
 'aproveitável': '1',
 'aprumado': '1',
 'apto': '1',
 'apunhalado': '-1',
 'apupado': '0',
 'apurado': '1',
 'aquietador': '-1',
 'árabe': '0',
 'arbitrário': '-1',
 'arcaico': '-1',
 'ardente': '1',
 'ardido': '1',
 'ardiloso': '-1',
 'ardoroso': '0',
 'árduo': '-1',
 'arfante': '-1',
 'argivo': '-1',
 'arguido': '0',
 'arguto': '0',
 'ariano': '0',
 'árido': '-1',
 'arisco': '-1',
 'aristocrata': '0',
 'aristocrático': '1',
 'aristotélico': '0',
 'armado': '0',
 'arnaz': '1',
 'arquejante': '-1',
 'arqueológico': '-1',
 'arraigado': '-1',
 'arrancado': '0',
 'arranhado': '-1',
 'arranjado': '1',
 'arrasado': '-1',
 'arrasador': '0',
 'arrasante': '0',
 'arrastadeiro': '-1',
 'arrastado': '-1',
 'arrazoado': '1',
 'arrebatado': '0',
 'arrebatador': '0',
 'arrebentado': '-1',
 'arrebitado': '-1',
 'arrecadado': '-1',
 'arredado': '-1',
 'arredio': '-1',
 'arredondado': '-1',
 'arreliado': '-1',
 'arreliador': '-1',
 'arremessado': '0',
 'arrenegado': '-1',
 'arrependido': '-1',
 'arrepiado': '-1',
 'arrepiante': '-1',
 'arretado': '1',
 'arrevesado': '-1',
 'arriscado': '-1',
 'arrivista': '-1',
 'arrogante': '-1',
 'arrojado': '1',
 'arrolado': '-1',
 'arrozeiro': '0',
 'arruaceiro': '-1',
 'arrufado': '-1',
 'arruinado': '-1',
 'arruivado': '0',
 'arrumado': '1',
 'arteiro': '-1',
 'arteriosclerótico': '-1',
 'articulado': '-1',
 'artificial': '-1',
 'artificioso': '-1',
 'artista': '0',
 'artÃ\xadstico': '1',
 'ascético': '0',
 'aselha': '-1',
 'asfixiante': '-1',
 'asiático': '-1',
 'asilado': '-1',
 'asneirento': '-1',
 'asno': '-1',
 'áspero': '-1',
 'aspirante': '0',
 'asqueroso': '-1',
 'assalariado': '-1',
 'assaloiado': '-1',
 'assaltado': '0',
 'assaltante': '-1',
 'assanhadiço': '-1',
 'assanhado': '-1',
 'assarapantado': '-1',
 'assassinado': '1',
 'assassino': '-1',
 'assaz': '0',
 'asseado': '1',
 'assediado': '-1',
 'assediador': '-1',
 'assegurado': '1',
 'assentado': '1',
 'assente': '1',
 'assertivo': '1',
 'assessor': '-1',
 'assexuado': '0',
 'assexual': '0',
 'assÃ\xadduo': '1',
 'assimétrico': '-1',
 'assimilado': '0',
 'assinado': '0',
 'assinalado': '1',
 'assinalável': '1',
 'assinante': '1',
 'assisado': '1',
 'assistente': '-1',
 'assistido': '-1',
 'assoberbado': '-1',
 'assobiado': '0',
 'associado': '-1',
 'assolado': '-1',
 'assolador': '-1',
 'assomadiço': '-1',
 'assombrado': '-1',
 'assombroso': '1',
 'assumido': '-1',
 'assustadiço': '-1',
 'assustado': '-1',
 'assustador': '-1',
 'asténico': '-1',
 'astucioso': '-1',
 'astuto': '-1',
 'atabalhoado': '-1',
 'atacado': '0',
 'atacante': '-1',
 'atado': '-1',
 'atafulhado': '-1',
 'atamancado': '-1',
 'atarantado': '-1',
 'atarefado': '0',
 'atarracado': '-1',
 'atascado': '0',
 'ateado': '-1',
 'atemorizado': '-1',
 'atencioso': '1',
 'atento': '1',
 'aterrado': '-1',
 'aterrador': '-1',
 'aterrorizado': '-1',
 'aterrorizador': '-1',
 'ateu': '-1',
 'atiçado': '0',
 'atilado': '1',
 'atinado': '1',
 'atingido': '-1',
 'atiradiço': '0',
 'ativado': '0',
 'ativo': '1',
 'atleta': '1',
 'atlético': '1',
 'atolado': '0',
 'atoleimado': '-1',
 'atónito': '-1',
 'atônito': '-1',
 'atordoado': '-1',
 'atordoador': '-1',
 'atormentado': '-1',
 'atormentador': '-1',
 'atracado': '-1',
 'atractivo': '1',
 'atraente': '1',
 'atraiçoado': '-1',
 'atraÃ\xaddo': '-1',
 'atrapalhado': '-1',
 'atrasado': '-1',
 'atrasado mental': '-1',
 'atrativo': '1',
 'atravessadiço': '-1',
 'atravessado': '-1',
 'atreito': '-1',
 'atrelado': '1',
 'atrevido': '0',
 'atrevidote': '0',
 'atribulado': '-1',
 'atroador': '-1',
 'atrofiado': '-1',
 'atrofiante': '-1',
 'atropelado': '-1',
 'atroz': '-1',
 'atulhado': '-1',
 'aturdido': '-1',
 'audacioso': '0',
 'audaz': '0',
 'áureo': '1',
 'ausente': '-1',
 'auspicioso': '1',
 'austero': '-1',
 'autarca': '0',
 'autêntico': '1',
 'autista': '-1',
 'autoconfiante': '1',
 'autocrata': '0',
 'autóctone': '0',
 'autodestrutivo': '-1',
 'autodidacta': '1',
 'autonomista': '1',
 'autónomo': '1',
 'autônomo': '0',
 'autoritário': '-1',
 'autorizado': '1',
 'autosuficiente': '1',
 'auxiliado': '0',
 'auxiliar': '-1',
 'avançado': '1',
 'avantajado': '0',
 'avarento': '-1',
 'avariado': '-1',
 'avaro': '-1',
 'avassalador': '-1',
 'avassalante': '-1',
 'aveludado': '1',
 'avençado': '0',
 'aventurado': '0',
 'aventureiro': '0',
 'aventuroso': '0',
 'averiguador': '0',
 'avermelhado': '0',
 'averroÃ\xadsta': '0',
 'averso': '-1',
 'avesso': '-1',
 'ávido': '-1',
 'aviltado': '-1',
 'aviltador': '-1',
 'aviltante': '-1',
 'avinagrado': '-1',
 'avinhado': '-1',
 'avisado': '1',
 'avulso': '-1',
 'axiomático': '1',
 'azafamado': '-1',
 'azambrado': '-1',
 'azarado': '-1',
 'azarento': '-1',
 'azedo': '-1',
 'azeiteiro': '-1',
 'azelha': '-1',
 'azoinante': '-1',
 'azougado': '-1',
 'azul': '-1',
 'babaca': '-1',
 'babado': '-1',
 'babão': '-1',
 'babilónico': '1',
 'baboso': '-1',
 'bacano': '1',
 'baço para espelho': '-1',
 'bacoco': '-1',
 'baconiano': '0',
 'badalhoco': '-1',
 'baderneiro': '-1',
 'bagaceiro': '-1',
 'bagunceiro': '-1',
 'bairrista': '0',
 'baita': '1',
 'baixote': '-1',
 'bajulador': '-1',
 'balbuciante': '-1',
 'balofo': '-1',
 'bambo': '-1',
 'banal': '-1',
 'banana': '-1',
 'bandido': '-1',
 'banhado': '0',
 'banido': '-1',
 'banzado': '-1',
 'baralhado': '-1',
 'barateiro': '0',
 'barato': '-1',
 'barbado': '0',
 'bárbaro': '-1',
 'barbeado': '0',
 'barbilongo': '0',
 'barbudo': '0',
 'barrado': '-1',
 'barraqueiro': '-1',
 'barrigana': '-1',
 'barrigudo': '-1',
 'barroco': '-1',
 'barulhento': '-1',
 'baselga': '-1',
 'básico': '-1',
 'bastardo': '-1',
 'basto': '0',
 'batalhador': '1',
 'batalhante': '1',
 'batateiro': '0',
 'batatudo': '-1',
 'batido': '-1',
 'batoteiro': '-1',
 'batuta': '1',
 'bazófio': '-1',
 'beatificado': '-1',
 'beato': '-1',
 'bêbado': '-1',
 'bêbedo': '-1',
 'bebedor': '-1',
 'beberrão': '-1',
 'bebido': '-1',
 'bebum': '-1',
 'beiçudo': '-1',
 'beijado': '-1',
 'beijoqueiro': '0',
 'belfo': '-1',
 'belicista': '-1',
 'bélico': '-1',
 'belicoso': '-1',
 'beligerante': '0',
 'belo': '1',
 'bem ataviado': '0',
 'bem constituÃ\xaddo': '0',
 'bem disposto': '1',
 'bem vestido': '1',
 'bem-amado': '1',
 'bem-apessoado': '1',
 'bem-aventurado': '1',
 'bem-colocado': '1',
 'bem-comportado': '1',
 'bem-criado': '1',
 'bem-educado': '1',
 'bem-encarado': '1',
 'bem-falante': '1',
 'bem-formado': '1',
 'bem-humorado': '1',
 'bem-intencionado': '1',
 'bem-mandado': '0',
 'bem-pago': '0',
 'bem-parecido': '1',
 'bem-pensante': '1',
 'bem-posto': '1',
 'bem-sucedido': '1',
 'bem-vindo': '1',
 'bendito': '1',
 'beneditino': '0',
 'beneficiado': '0',
 'beneficiário': '0',
 'benéfico': '1',
 'benemérito': '1',
 'benevolente': '1',
 'benévolo': '1',
 'benêvolo': '1',
 'benfazejo': '1',
 'benfeitor': '1',
 'benfiquista': '0',
 'benigno': '1',
 'benquisto': '1',
 'bera': '-1',
 'bernardo': '-1',
 'besta': '-1',
 'bestial': '0',
 'bestificado': '-1',
 'bexiguento': '-1',
 'bibliómano': '0',
 'bÃ\xadgamo': '0',
 'bilingue': '0',
 'bilioso': '-1',
 'biltre': '-1',
 'bimbo': '-1',
 'biqueiro': '-1',
 'birrento': '-1',
 'biruta': '-1',
 'bisbilhoteiro': '-1',
 'bisonho': '-1',
 'bissexual': '0',
 'bizarro': '-1',
 'blasfemador': '-1',
 'blasfemo': '-1',
 'blindado': '0',
 'bloqueado': '-1',
 'boateiro': '-1',
 'bobo': '-1',
 'boboca': '-1',
 'boçal': '-1',
 'bochechudo': '-1',
 'bocó': '-1',
 'boémio': '-1',
 'boêmio': '-1',
 'bojudo': '-1',
 'bolachudo': '-1',
 'bolchevique': '0',
 'bolchevista': '0',
 'bolorento': '-1',
 'bom': '1',
 'bom aluno': '1',
 'bombástico': '-1',
 'bombeado': '-1',
 'bom-samaritano': '1',
 'bonachão': '-1',
 'bonacheirão': '0',
 'bonacheiro': '0',
 'bonapartista': '0',
 'bondoso': '1',
 'bonito': '1',
 'boquiaberto': '-1',
 'borbulhento': '-1',
 'borguista': '-1',
 'borracho': '-1',
 'borrachudo': '-1',
 'borrado': '-1',
 'borralheiro': '0',
 'boto': '-1',
 'botocudo': '-1',
 'brabo': '0',
 'braçal': '0',
 'braceiro': '1',
 'branco': '-1',
 'brando': '0',
 'bravateador': '-1',
 'bravo': '0',
 'brega': '-1',
 'brejeiro': '-1',
 'brejeirote': '-1',
 'breve': '1',
 'brigador': '-1',
 'brigante': '-1',
 'brigão': '-1',
 'briguento': '-1',
 'brilhante': '1',
 'brincalhão': '0',
 'brioso': '1',
 'britânico': '0',
 'broeiro': '0',
 'bronco': '-1',
 'bronzeado': '0',
 'brônzeo': '0',
 'brotado': '0',
 'brunido': '0',
 'bruno': '-1',
 'brusco': '-1',
 'brutal': '-1',
 'bruto': '-1',
 'bucha': '-1',
 'bucólico': '0',
 'budista': '0',
 'bufo': '-1',
 'bulhão': '-1',
 'bulhento': '-1',
 'buliçoso': '-1',
 'burguês': '-1',
 'burlado': '-1',
 'burlador': '-1',
 'burlão': '-1',
 'burlesco': '-1',
 'burlista': '-1',
 'burocrático': '0',
 'burro': '-1',
 'cabeça dura': '-1',
 'cabeçudo': '-1',
 'cabeludo': '0',
 'cabisbaixo': '-1',
 'caboclo': '-1',
 'cabotino': '-1',
 'cabralino': '0',
 'cabrão': '-1',
 'cabrito': '0',
 'cábula': '-1',
 'caçado': '-1',
 'cacete': '-1',
 'caceteiro': '-1',
 'cachaceiro': '-1',
 'cadastrado': '-1',
 'cadavérico': '-1',
 'cadaveroso': '-1',
 'cadelo': '-1',
 'cadente': '-1',
 ...}

Podemos observar como ficou o dicionário, somente com as polaridades que indicam os sentimentos.

Vamos criar um função que retorna a polaridade, positivo, neutro ou negativo.

In [21]:
#Criando uma função chamada "Score de Sentimento" para determinar os #sentimentos associados
def Score_sentimento(frase):
    frase = frase.lower()
    l_sentimento = []
    for p in frase.split():
        l_sentimento.append(float(dic_palavra_polaridade.get(p, 0)))
    score = sum(l_sentimento)
    if score > 0:
        return 'Positivo: {} '.format(score)
#         return '{}'.format(score)
    elif score == 0:
        return 'Neutro: {} '.format(score)
#         return '{}'.format(score)
    else:
#         return '{}'.format(score)
        return 'Negativo: {}'.format(score)

A função acima retornará nesse formato, por exemplo, "Positivo: 2.0", então na sequencia eu vou separar mantendo a polaridade em uma coluna e o score em outra.

In [22]:
# aplicando a função para entender os sentimentos de cada discurso
db_final['sentimento'] = db_final.apply(lambda row: Score_sentimento(row['lemmatized']), axis=1)

# separando a polaridade do score
db_final[['polaridade', 'score']] = db_final['sentimento'].str.split(':', expand=True)

# removendo a coluna original
db_final = db_final.drop(['sentimento'], axis=1)

# visualizando o resultado
db_final.head()
Out[22]:
data hora link text monologue for_spacy spacy_monologue lemmatized polaridade score
0 2020-11-22 11:16:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Primeiramente parabenizo ... senhoras e senhores primeiramente parabenizo a... senhoras e senhores primeiramente parabenizo a... (senhoras, e, senhores, primeiramente, paraben... senhor e senhor primeiramente parabenizar o ar... Positivo 8.0
1 2020-11-21 11:20:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Senhoras e Senhores Antes de adentrarmos o te... senhoras e senhores antes de adentrarmos o tem... senhoras e senhores antes de adentrarmos o tem... (senhoras, e, senhores, antes, de, adentrarmos... senhor e senhor antar de adentrar o temer prin... Positivo 9.0
2 2020-11-18 14:52:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Mais que emocionado eu estou muito feliz em p... mais que emocionado eu estou muito feliz em po... mais que emocionado eu estou muito feliz em po... (mais, que, emocionado, eu, estou, muito, feli... mais que emocionar eu estar muito feliz em pod... Positivo 3.0
3 2020-11-17 18:12:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [Brasília/DF 17 de novembro de 2020 Sua Excelê... brasília/df 17 de novembro de 2020 sua excelên... brasília df de novembro de sua excelência vlad... (brasília, df, de, novembro, de, sua, excelênc... brasília df de novembro de suar excelência vla... Positivo 14.0
4 2020-10-11 20:49:00 https://www.gov.br/planalto/pt-br/acompanhe-o-... [ Boa tarde. Senhores e senhoras se me permite... boa tarde. senhores e senhoras se me permitem ... boa tarde senhores e senhoras se me permitem e... (boa, tarde, senhores, e, senhoras, se, me, pe... bom tardar senhor e senhor se me permitir eu q... Positivo 14.0

Depois dos sentimentos já encontrados em cada discurso, vamos fazer algumas análises.

Primeiramente, vou medir os discursos e colocar em uma coluna a quantidade de palavras.

In [23]:
# medindo os textos
db_final['lenght'] = db_final['lemmatized'].apply(lambda x: len(x))

A seguir vou fazer 3 histogramas e analisar a distribuição do tamanho dos textos por polaridade, será que tem alguma relação?

In [24]:
# colocando a polaridade em uma lista dos unicos existentes
polarity = db_final.polaridade.unique().tolist()

# definindo a área de plotagem
fig, ax = plt.subplots(nrows=len(polarity), figsize=(8,8))

# loop para criar os histogramas com seus respectivos parâmetros
for i in range(len(polarity)):
    sns.distplot(db_final[db_final.polaridade == polarity[i]]['lenght'], ax=ax[i], kde=False, bins=15)
    ax[i].set_xlim(0, 20000)
    ax[i].set_title(f'Distribuição do tamanho do discurso por polaridade: {polarity[i]}', fontsize=14, fontweight='bold')
    ax[i].set_xlabel(None)
    print(f"Média de palavras nos discursos {polarity[i]}: {db_final[db_final.polaridade == polarity[i]]['lenght'].mean():.2f} e desvio padrão: {db_final[db_final.polaridade == polarity[i]]['lenght'].std():.2f}")

# ajusta o espaçamento entre os plots
plt.tight_layout(pad=3)
Média de palavras nos discursos Positivo: 5114.86 e desvio padrão: 3857.22
Média de palavras nos discursos Neutro: 1968.00 e desvio padrão: 1670.96
Média de palavras nos discursos Negativo: 2777.27 e desvio padrão: 1789.53

Podemos observar que os discursos classificados como positivos tem uma variação maior que os outros dois sentimentos, tendo praticamente o dobro de desvio-padrão e uma distribuição bem diferente entre eles.

Podemos contruir uma wordcloud, ou seja, uma núvem de palavras, assim conseguimos ver visualmente, com base no tamanho das palavras, que quanto maior, mais ela é frequente nos discursos.

Temos que juntar todas as palavras como se fosse um só texto.

In [25]:
# juntando todos eles para construir a wordcloud - ela tem que estar todo contido numa string
all_content = "".join(c for c in db_final['lemmatized'])

Se quiser podemos customizar uma lista de stopwords e ir ajustando a nuvem de palavras.

In [26]:
# customizando stopwords
stopwords.update(['ser', 'não', 'ter', 'parir', 'um', 'aí', 'ma', 'pelar', 'então'])

Uma outra coisa que vamos fazer é colocar a núvem de palavras em um formato que também represente, ou próximo disso, do que se trata o projeto. Como estamos falando de sentimentos em discursos políticos, vamos colcar a wordcloud em formato da bandeira do brasil.

In [27]:
# definindo uma imagem como máscara
mask = np.array(Image.open("images/brasil.jpg"))

A imagem como máscara deve ser criada em branco e preto, fiz em power-point mesmo, a parte que não terá palavras deve ser branco e o restante preto, então exportamos a imagem com o método Image.open da biblioteca PIL e convertemos os pixels em array numpy como a função np.array, armazenando em um objeto mask.

Esse objeto servirá como argumento do parâmetro mask da função WordCloud, além de ajustar também outros parâmetros.

In [28]:
# Criando o objeto wordcloud com as configs necessárias
wordcloud = WordCloud(stopwords=stopwords,
                      background_color='black', width=1600,
                      height=800, max_words=1000, mask=mask, max_font_size=500,
                      min_font_size=1).generate(all_content)


#configurando forma de apresentação do gráfico e apresentando no notebook.
fig, ax = plt.subplots(figsize=(16,8))            
ax.imshow(wordcloud, interpolation='bilinear')
ax.set_axis_off()
plt.imshow(wordcloud);

Vamos criar um countplot para ver a quantidade por cada tipo de polaridade.

In [29]:
# definindo a área de plotagem
plt.figure(figsize=(8,4))

# criando o plot
sns.countplot(db_final['polaridade'])

# definindo o título
plt.title('Quantidade de tipo de polaridade');

Vemos que discursos com sentimentos positivos prevalecem entre todos os discursos realizados.

Iremos fazer uma análise da dimensão temporal pra ver o quanto oscila os sentimentos entre os discursos, utilizaremos um agrupamento por data, com as médias do score. Vamos utilizar a média porque constatamos que há dias que tiveram mais de um discurso.

Vamos criar um dataframe somente para a dimensão temporal, uma coluna para os scores e uma com uma média móvel de 7 dias, ou seja, a granularidade será semanal para visualizarmos.

In [30]:
# convertendo a coluna score em float
db_final['score'] = db_final['score'].astype(float)

# criando o dataframe
db_final_temporal = pd.DataFrame(db_final.groupby(['data'])['score'].mean())

# criando a janela temporal com a média
db_final_temporal['score_roll_avg'] = db_final_temporal.rolling(window=7).mean()

# visualizando o resultado
db_final_temporal.head(10)
Out[30]:
score score_roll_avg
data
2019-02-12 9.5 NaN
2019-03-12 5.0 NaN
2019-04-11 -1.0 NaN
2019-05-11 19.0 NaN
2019-05-12 4.0 NaN
2019-06-12 12.0 NaN
2019-07-12 7.0 7.928571
2019-08-11 12.0 8.285714
2019-09-12 2.5 7.928571
2019-10-12 15.0 10.214286

Plotaremos dois tipos de gráficos:

  1. Primeiro será somente com os scores sem nenhum tipo de transformação, para analisar o comportamento ao longo do tempo.
  2. Segundo será o plot com a média móvel.
In [31]:
# definindo a área de plotagem
fig = plt.figure(figsize=(15,8))
ax = plt.axes()

# criando o plot
sns.lineplot(x = db_final_temporal.index, y = db_final_temporal.score)

# definindo o título
plt.title('Visualização do score ao longo do tempo')

# configurando eixos
plt.xlabel('data do discurso')
plt.ylabel('Score')
plt.ylim(db_final_temporal.score.min(), db_final_temporal.score.max())
plt.axhline(y = 0, color = 'red', linestyle = ':')
plt.axvline(x = db_final_temporal.index.min(), color = 'black', linestyle = 'dashed')
plt.xticks(rotation = 30);

A linha vermelha corresponde o neutro, podemos obervar que em outubro de 2019 houve um discurso com sentido bem negativo, embora os discursos nessa amostragem seja, em sua grande maioria positivo. Vemos que a partir de agosto de 2020 os discursos começaram oscilar mais, sendo realizados mais discursos em tons negativos. Poderíamos averiguar o que aconteceu a partir desta data que alterou esse comportamento.

In [32]:
# definindo a área de plotagem
fig = plt.figure(figsize=(15,8))
ax = plt.axes()

# criando o plot
sns.lineplot(x = db_final_temporal.index, y = db_final_temporal.score_roll_avg)

# definindo o título
plt.title('Score - Média móvel de 7 dias')

# configurando eixos
plt.xlabel('data do discurso')
plt.ylabel('Score')
plt.ylim(-5, db_final_temporal.score_roll_avg.max())
plt.axhline(y = 0, color = 'red', linestyle = ':')
plt.axvline(x = db_final_temporal.index.min(), color = 'black', linestyle = 'dashed')
plt.xticks(rotation = 30);

Podemos observar no gráfico que de julho/2020 à agosto/2020, a pontuação começou a decrescer muito, mesmo que na média desse período tenha ficado positivo, dá pra constatar que o tom dos discursos foram mais negativos e a partir daí os discursos deram um pico de positividade, mas começaram a oscilar mais que anterior à esta data.

Conclusão

Nosso trabalho termina aqui por enquanto, fomos desde a definição do problema até a análise dos sentimentos passando pelos seguintes passos: Coleta de dados, fizemos web scraping para obtenção dos dados, limpeza dos dados, no qual extraimos todos os textos do código fonte da página html, modelagem dos tópicos no qual definimos os tópicos mais relevantes com as 10 palavras mais importantes e análise de sentimentos feito com um léxico de sentimentos, por fim, para fecharmos as análises vimos como são os discursos ao longo do tempo.

Referências

Data Science Academy
https://www.datascienceacademy.com.br/

spaCy em Português (Aula)
https://www.youtube.com/watch?v=1pGe5OSbbDg&list=PL4OAe-tL47saZwtt9fLHmT5cas57rjCCW

analise-de-sentimentos-de-uma-forma-diferente
https://minerandodados.com.br/analise-de-sentimentos-de-uma-forma-diferente/

Analisando Sentimento em notas de app (Python) – Parte 1
https://escoladeia.com.br/analisando-sentimento-em-notas-de-app-python-parte-1/

Brincando de Processamento Natural de Linguagem com spaCy
https://leportella.com/pt-br/npl-com-spacy/