Nesse projeto vamos utilizar tweets relacionados a última eleição presidencial dos Estados Unidos, onde Hillary Clinton e Donald Trump dispuram o pleito final. A proposta é utilizar os métodos de aprendizados supervisionados estudados para classificar tweets entre duas categorias: Hillary e Trump.
O primeiro passo foi obter um conjunto de tweets que foi publicado pelas contas oficiais do tweet dos dois candidatos. Para isso, vamos utilizar este dataset disponibilizado pelo Kaglle. Com este conjunto de dados, vamos construir um modelo capaz de aprender, a partir de um conjunto de palavras, se o texto foi digitado pela conta da Hillary ou do Trump.
Uma vez que este modelo foi construído, vamos classificar um conjunto novo de dados relacionados às eleições americadas e classifica-los em um dos discursos. A proposta é tentar classificar tweets que tenham um direcionamento mais próximo do discurso da Hillary e aqueles que são mais próximos do discurso do Trump. Lê-se como discurso os tweets publicados.
Para essa base de teste, vamos utilizar um subconjunto de tweets deste dataset que consta com tweets que foram postados no dia da eleição americana.
Para exibir os resultados, vamos construir uma página html. Além da análise automática, esta página terá informações sobre os termos mais citados pelas contas dos candidatos.
Sendo assim, o primeiro passo é gerar a base de tweets dos candidatos e extrair as informações mais relevantes. Vamos trabalhar primeiro aqui no Jupyter Notebook para testar os métodos. Ao final será gerado um JSON que será lido pela página HTML. Um exemplo da página já alimentada pode ser encontrada neste link.
Vamos começar ;)
In [1]:
import pandas as pd
import nltk
df = pd.read_csv("https://www.data2learning.com/machinelearning/datasets/tweets.csv")
dataset = df[['text','handle']]
dict_ = dataset.T.to_dict("list")
O objeto dict_ representa todos os textos associados a classe correspondente.
Na etapa de pré-processamento vamos fazer algumas operações:
Essas etapas de pré-processamento dependem do objetivo do trabalho. Pode ser de interessa, a depender da tarefa de classificação, manter tais símbolos. Para o nosso trabalho, só é de interesse as palavras em si.
Para esta tarefa, vamos utilizar também o NLTK, conjunto de ferramentas voltadas para o processamento de linguagem natural.
vamos criar um método para isso, já que iremos utiliza-lo mais adiante com a base de teste:
In [2]:
from unicodedata import normalize, category
from nltk.tokenize import regexp_tokenize
from collections import Counter, Set
from nltk.corpus import stopwords
import re
def pre_process_text(text):
# Expressão regular para extrair padrões do texto. São reconhecidos (na ordem, o símbolo | separa um padrão):
# - links com https, links com http, links com www, palavras, nome de usuários (começa com @), hashtags (começa com #)
pattern = r'(https://[^"\' ]+|www.[^"\' ]+|http://[^"\' ]+|[a-zA-Z]+|\@\w+|\#\w+)'
#Cria a lista de stopwords
english_stop = stopwords.words(['english'])
users_cited = []
hash_tags = []
tokens = []
text = text.lower()
patterns = regexp_tokenize(text, pattern)
users_cited = [e for e in patterns if e[0] == '@']
hashtags = [e for e in patterns if e[0] == '#']
tokens = [e for e in patterns if e[:4] != 'http']
tokens = [e for e in tokens if e[:4] != 'www.']
tokens = [e for e in tokens if e[0] != '#']
tokens = [e for e in tokens if e[0] != '@']
tokens = [e for e in tokens if e not in english_stop]
tokens = [e for e in tokens if len(e) > 3]
return users_cited, hashtags, tokens
In [3]:
users_cited_h = [] # armazena os usuários citatdos por hillary
users_cited_t = [] # armazena os usuários citados por trump
hashtags_h = [] # armazena as hashtags de hillary
hashtags_t = [] # armazena as hashtags de trump
words_h = [] # lista de palavras que apareceram no discurso de hillary
words_t = [] # lista de palavras que apareceram no discurso de trump
all_tokens = [] # armazena todos os tokens, para gerar o vocabulário final
all_texts = [] # armazena todos
for d in dict_:
text_ = dict_[d][0]
class_ = dict_[d][1]
users_, hash_, tokens_ = pre_process_text(text_)
if class_ == "HillaryClinton":
class_ = "hillary"
users_cited_h += users_
hashtags_h += hash_
words_h += tokens_
elif class_ == "realDonaldTrump":
class_ = "trump"
users_cited_t += users_
hashtags_t += hash_
words_t += tokens_
temp_dict = {
'text': " ".join(tokens_),
'class_': class_
}
all_tokens += tokens_
all_texts.append(temp_dict)
In [4]:
print("Termos mais frequentes ditos por Hillary:")
print()
hillary_frequent_terms = nltk.FreqDist(words_h).most_common(10)
for word in hillary_frequent_terms:
print(word[0])
In [5]:
print("Termos mais frequentes ditos por Trump:")
print()
trump_frequent_terms = nltk.FreqDist(words_t).most_common(10)
for word in trump_frequent_terms:
print(word[0])
Bigrams e Trigram mais frequentes da Hillary
In [6]:
#Pegando os bigram e trigram mais frequentes
from nltk.collocations import BigramCollocationFinder, TrigramCollocationFinder
from nltk.metrics import BigramAssocMeasures, TrigramAssocMeasures
bcf = BigramCollocationFinder.from_words(words_h)
tcf = TrigramCollocationFinder.from_words(words_h)
bcf.apply_freq_filter(3)
tcf.apply_freq_filter(3)
result_bi = bcf.nbest(BigramAssocMeasures.raw_freq, 5)
result_tri = tcf.nbest(TrigramAssocMeasures.raw_freq, 5)
hillary_frequent_bitrigram = []
for r in result_bi:
w_ = " ".join(r)
print(w_)
hillary_frequent_bitrigram.append(w_)
print
for r in result_tri:
w_ = " ".join(r)
print(w_)
hillary_frequent_bitrigram.append(w_)
Bigrams e Trigram mais frequentes do Trump
In [7]:
bcf = BigramCollocationFinder.from_words(words_t)
tcf = TrigramCollocationFinder.from_words(words_t)
bcf.apply_freq_filter(3)
tcf.apply_freq_filter(3)
result_bi = bcf.nbest(BigramAssocMeasures.raw_freq, 5)
result_tri = tcf.nbest(TrigramAssocMeasures.raw_freq, 5)
trump_frequent_bitrigram = []
for r in result_bi:
w_ = " ".join(r)
print(w_)
trump_frequent_bitrigram.append(w_)
print
for r in result_tri:
w_ = " ".join(r)
print(w_)
trump_frequent_bitrigram.append(w_)
In [8]:
# Cada token é concatenado em uma única string que representa um tweet
# Cada classe é atribuída a um vetor (hillary, trump)
# Instâncias: [t1, t2, t3, t4]
# Classes: [c1, c2, c3, c4]
all_tweets = []
all_class = []
for t in all_texts:
all_tweets.append(t['text'])
all_class.append(t['class_'])
In [9]:
print("Criar o bag of words...\n")
#Número de features, coluna da tabela
max_features = 2000
from sklearn.feature_extraction.text import CountVectorizer
# Initialize the "CountVectorizer" object, which is scikit-learn's
# bag of words tool.
vectorizer = CountVectorizer(analyzer = "word", \
tokenizer = None, \
preprocessor = None, \
stop_words = None, \
max_features = max_features)
# fit_transform() does two functions: First, it fits the model
# and learns the vocabulary; second, it transforms our training data
# into feature vectors. The input to fit_transform should be a list of
# strings.
X = vectorizer.fit_transform(all_tweets)
# Numpy arrays are easy to work with, so convert the result to an
# array
X = X.toarray()
y = all_class
print("Train data: OK!")
In [10]:
X.shape
Out[10]:
Ao final deste processo já temos nossa base de dados dividido em duas variáveis: X e y.
X corresponde ao bag of words, ou seja, cada linha consiste de um twitter e cada coluna de uma palavra presente no vocabulário da base dados. Para cada linha/coluna é atribuído um valor que corresponde a quantidade de vezes que aquela palavra aparece no respectivo tweet. Se a palavra não está presente, o valor de 0 é atribuído.
y corresponde a classe de cada tweet: hillary, tweet do perfil @HillaryClinton e trump, tweet do perfil @realDonaldTrump.
Vamos testar os diferentes modelos estudados com a base de dados criada e escolher aquele que melhor generaliza o conjunto de dados. Para testar os modelos, vamos utilizar validação cruzada de 10 folds. Aplique os modelos estudados:
Além disso, teste dois outros modelos:
Para estes dois, pesquise no Google como utiliza-los no Scikit-Learn.
Para cada modelo imprima a acurácia no treino e na média dos 10 folds da validação cruzada. A escolha do melhor deve ser feita a partir do valor da média da validação cruzada.
O melhor modelo será utilizado para classificar outros textos extraídos do twitter e na implementação da página web.
Atenção: dada a quantidade de dados, alguns modelos pode demorar alguns minutos para executar
In [ ]:
# Teste os modelos a partir daqui
Vamos executar o melhor clasificador em um conjunto de textos novos. Esses textos não tem classificação. Eles foram postados durante o dia da eleição americana. A idéia é identificar de forma automática os tweets que estão mais próximos dos discursos da Hillary Clinton e de Donald Trump.
Essa tarefa será realizada em sala após a entrega da atividade do melhor modelo
In [ ]:
hillary_frequent_hashtags = nltk.FreqDist(hashtags_h).most_common(10)
trump_frequent_hashtags = nltk.FreqDist(hashtags_t).most_common(10)
dict_web = {
'hillary_information': {
'frequent_terms': hillary_frequent_terms,
'frequent_bitrigram': hillary_frequent_bitrigram,
'frequent_hashtags': hillary_frequent_hashtags
},
'trump_information': {
'frequent_terms': trump_frequent_terms,
'frequent_bitrigram': trump_frequent_bitrigram,
'frequent_hashtags': trump_frequent_hashtags
},
'classified_information': {
'hillary_terms': hillary_classified_frequent_terms,
'hillary_bigram': hillary_classified_bitrigram,
'trump_terms': trump_classified_frequent_terms,
'trump_bigram': trump_classified_bitrigram,
'texts_classified': all_classified
}
}
with open('data.json', 'w') as outfile:
json.dump(dict_web, outfile)