Neste trabalho, faremos uma análise de sentimentos no twitter sobre a Série “Rick and Morty”, utilizando uma massa de dados de 10.000 tweets. Analisaremos os dados segundo o modelo “Naive Bayes” para criação dos dados de polarização e de emoções. Também criaremos aqui uma Wordcloud e um dendograma para identificação de associações de palavras ao nosso tema principal (“Rick and Morty”).
## [1] "/home/sandro/Documents/3.Projetos_ds/machine_learning/1.Concluídos/analise_redes_sociais"
setup_twitter_oauth(key, secret, token, tokensecret)
## [1] "Using direct authentication"
Vamos capturar uma massa de dados dos últimos 10.000 Tweets com o tema “Rick and Morty”.
tema <- "Rick and Morty"
qtd_tweets <- 10000
lingua <- "pt"
tweetdata = searchTwitter(tema, n = qtd_tweets, lang = lingua)
## Warning in doRppAPICall("search/tweets", n, params = params, retryOnRateLimit =
## retryOnRateLimit, : 10000 tweets were requested but the API can only return 3838
Aqui faremos o tratamento dos dados coletados anteriormente.
Vamos converter os dados coletados em um objeto do tipo ‘Corpus’ e em seguida faremos um processo de limpeza,excluíremos as stopwords, ajustaremos o encoding, removeremos acentuação e converteremos as letras para minúsculas.
options(warn=-1)
tweetlist <- sapply(tweetdata, function(x) x$getText())
tweetlist <- iconv(tweetlist, to = "utf-8", sub="")
tweetlist <- limpaTweets(tweetlist)
tweetcorpus <- Corpus(VectorSource(tweetlist))
tweetcorpus <- tm_map(tweetcorpus, removePunctuation)
tweetcorpus <- tm_map(tweetcorpus, content_transformer(tolower))
tweetcorpus <- tm_map(tweetcorpus, function(x)removeWords(x, stopwords()))
termo_por_documento = as.matrix(TermDocumentMatrix(tweetcorpus), control = list(stopwords = c(stopwords("portuguese"))))
Após as fases de coleta e tratamento dos dados, vizualizaremos as palavras mais associadas a ‘Rick and Morty’. Também construíremos um dendograma. Isso nos auxiliará em nossa análise inicial de associação entre palavras e o tema principal.
pal2 <- brewer.pal(8,"Dark2")
wordcloud(tweetcorpus,
min.freq = 2,
scale = c(5,1),
random.color = F,
max.word = 60,
random.order = F,
colors = pal2)
tweettdm <- TermDocumentMatrix(tweetcorpus)
tweettdm
## <<TermDocumentMatrix (terms: 4223, documents: 3838)>>
## Non-/sparse entries: 32316/16175558
## Sparsity : 100%
## Maximal term length: 57
## Weighting : term frequency (tf)
findFreqTerms(tweettdm, lowfreq = 11)
## [1] "consigo" "falar" "morty" "nao" "que"
## [6] "rick" "vou" "cara" "ele" "faz"
## [11] "tbm" "desenho" "essa" "estao" "mas"
## [16] "melhor" "mundo" "para" "voces" "dnv"
## [21] "temporada" "uma" "black" "com" "isso"
## [26] "mirror" "quarta" "terminei" "por" "assistir"
## [31] "kkkkkk" "muito" "ruim" "big" "esse"
## [36] "pessoal" "quem" "vejo" "ver" "meu"
## [41] "minha" "tempo" "vida" "anime" "favorito"
## [46] "bad" "breaking" "dead" "gosto" "himym"
## [51] "nosso" "parecido" "pra" "quao" "serie"
## [56] "vikings" "walking" "acabou" "brasil" "netflix"
## [61] "parte" "quando" "segunda" "vai" "dias"
## [66] "pqp" "quase" "caralho" "eles" "episodio"
## [71] "filme" "como" "das" "incrivel" "menos"
## [76] "pelo" "final" "pela" "temp" "aqui"
## [81] "assiste" "bem" "coisa" "dos" "qualquer"
## [86] "amo" "pessoas" "queria" "alguem" "cmg"
## [91] "foi" "certo" "meio" "nada" "assistindo"
## [96] "ate" "gente" "manha" "esta" "era"
## [101] "mesmo" "viu" "voce" "amor" "star"
## [106] "fez" "office" "sei" "sempre" "todos"
## [111] "tudo" "dia" "passar" "vendo" "aventura"
## [116] "hora" "midnight" "tipo" "espero" "fiz"
## [121] "foda" "futuro" "pro" "volta" "existe"
## [126] "todo" "mais" "todas" "agora" "mano"
## [131] "pos" "sou" "tem" "dark" "sai"
## [136] "vontade" "acabei" "tenho" "toda" "noite"
## [141] "sim" "falando" "mal" "dormir" "bom"
## [146] "maluco" "eps" "esses" "assisti" "treco"
## [151] "resto" "family" "ficar" "mouth" "uns"
## [156] "ela" "meus" "tinha" "pouco" "sobre"
## [161] "ultimo" "chegando" "duas" "chega" "nunca"
## [166] "merda" "season" "assisto" "outra" "pedaco"
## [171] "diz" "ainda" "hoje" "nova" "preciso"
## [176] "casa" "sem" "tao" "fala" "kkkkk"
## [181] "tanto" "boa" "quero" "vezes" "acho"
## [186] "gostar" "mim" "altura" "coisas" "inuteis"
## [191] "signo" "tamanho" "tatuagens" "episodios" "porra"
## [196] "nem" "nossa" "series" "sherlock" "vamos"
## [201] "kkkk" "semana" "apenas" "gosta" "pessoa"
## [206] "friends" "met" "demais" "ultima" "brooklyn"
## [211] "show" "entao" "assistido" "tivesse" "ter"
## [216] "banks" "elite" "outer" "gospel" "quanto"
## [221] "rir" "vez" "fiquei" "tava" "education"
## [226] "lucifer" "sex" "stranger" "things" "witcher"
## [231] "dar" "humor" "tambem" "versao" "irmao"
## [236] "novo" "seja" "nos" "estou" "greys"
## [241] "house" "motel" "naruto" "aquele" "quer"
## [246] "terminar" "cabeca" "algum" "nome" "qual"
## [251] "seu" "inteligente" "jogo" "cada" "bagulho"
## [256] "comecei" "odeia" "amei" "porque" "novos"
## [261] "num" "sera" "triste" "fazer" "girl"
## [266] "gossip" "papel" "sao" "pensando" "bojack"
## [271] "horseman" "fico" "ser" "msm" "comigo"
## [276] "parar" "chapado" "cor" "sabe" "melhores"
## [281] "musica" "fandom" "cade" "supernatural" "fui"
## [286] "vcs" "transar" "parece" "depois" "mto"
## [291] "modern" "bue" "kkk" "lembra" "planeta"
## [296] "dps" "dizer" "criador" "animacao" "agr"
## [301] "ponto" "comecar" "maratonar" "logo" "namorados"
## [306] "primeiro" "pode" "assim" "acredito" "temporadas"
## [311] "deus" "umas" "assistiu" "quarentena" "fav"
## [316] "piercing" "mae" "camiseta" "fas" "teen"
## [321] "wolf" "galera" "olha" "scream" "blinders"
## [326] "peaky" "aquela" "outro" "maldicao" "residencia"
## [331] "fosse" "animacoes" "nessa" "min" "universo"
## [336] "fumar" "concorda" "respira" "break" "prison"
## [341] "vamo" "atypical" "anne" "got" "desencanto"
## [346] "nine" "estar" "kkkkkkk" "mil" "game"
## [351] "thrones" "piada" "vis" "ahs" "moral"
## [356] "nois" "twd" "desenhei" "estilo" "mother"
## [361] "desenhos" "certeza" "flash" "euphoria" "suits"
## [366] "grey" "narcos" "anatomy" "control" "minutos"
## [371] "good" "malvado" "ama" "skins" "place"
## [376] "ouca" "sexo" "aguentarias" "mentir" "perguntas"
## [381] "poderia" "preparar"
findAssocs(tweettdm, 'netflix', 0.60)
## $netflix
## numeric(0)
tweet2tdm <- removeSparseTerms(tweettdm, sparse = 0.9)
tweet2tdmscale <- scale(tweet2tdm)
tweetdist <- dist(tweet2tdmscale, method = "euclidean")
tweetfit <- hclust(tweetdist)
plot(tweetfit)
cutree(tweetfit, k = 4)
## morty que rick gosto nosso parecido pra quao
## 1 2 1 3 3 3 3 3
## serie desenhei estilo
## 3 4 4
rect.hclust(tweetfit, k = 3, border = "red")
Aqui construíremos a etapa de análise de sentimentos dos tweets coletados, associando os termos à palavras negativas, positivas e palavras neutras.
sentimento.score = function(sentences, pos.words, neg.words, .progress = 'none')
{
# Criando um array de scores com lapply
scores = laply(sentences,
function(sentence, pos.words, neg.words)
{
sentence = gsub("[[:punct:]]", "", sentence)
sentence = gsub("[[:cntrl:]]", "", sentence)
sentence =gsub('\\d+', '', sentence)
tryTolower = function(x)
{
y = NA
try_error = tryCatch(tolower(x), error=function(e) e)
if (!inherits(try_error, "error"))
y = tolower(x)
return(y)
}
sentence = sapply(sentence, tryTolower)
word.list = str_split(sentence, "\\s+")
words = unlist(word.list)
pos.matches = match(words, pos.words)
neg.matches = match(words, neg.words)
pos.matches = !is.na(pos.matches)
neg.matches = !is.na(neg.matches)
score = sum(pos.matches) - sum(neg.matches)
return(score)
}, pos.words, neg.words, .progress = .progress )
scores.df = data.frame(text = sentences, score = scores)
return(scores.df)
}
Nesta etapa, vamos carregar dois arquivos txt contendo nossas palavras positivas e negativas que deverão ser associadas aos sentimentos de nossos tweets coletados. Também criaremos um teste de massa de dados para analisarmos se o modelo está identificando os termos positivos, negativos e neutros adequadamente.
pos = readLines("pos_words.txt")
neg = readLines("neg_words.txt")
teste = c("rick and morty is just awesome", "i dont know what to think about rick and morty",
"rick and morty is awful")
testesentimento = sentimento.score(teste, pos, neg)
class(testesentimento)
## [1] "data.frame"
testesentimento$score
## [1] 1 0 -1
Aqui vamos utilizar o modelo ‘Naive Bayes’ para analisarmos os sentimentos de uma massa de dados de 10000 tweets, também como tema principal ‘Rick and Morty’.
A vantagem de se utilizar esse modelo é que, aqui não precisamos indicar as palavras positvas ou negativas.
Este modelo também nos permitirá analisar os indicadores de emoções e polaridade.
tweetpt = searchTwitter("Rick and Morty", n = 10000, lang = "pt")
## [1] "Rate limited .... blocking for a minute and retrying up to 119 times ..."
tweetpt = sapply(tweetpt, function(x) x$getText())
# Removendo caracteres especiais
tweetpt = gsub("(RT|via)((?:\\b\\W*@\\w+)+)", "", tweetpt)
# Removendo @
tweetpt = gsub("@\\w+", "", tweetpt)
# Removendo pontuação
tweetpt = gsub("[[:punct:]]", "", tweetpt)
# Removendo digitos
tweetpt = gsub("[[:digit:]]", "", tweetpt)
# Removendo links html
tweetpt = gsub("http\\w+", "", tweetpt)
# Removendo espacos desnecessários
tweetpt = gsub("[ \t]{2,}", "", tweetpt)
tweetpt = gsub("^\\s+|\\s+$", "", tweetpt)
# Criando função para tolower
try.error = function(x)
{
# Criando missing value
y = NA
try_error = tryCatch(tolower(x), error=function(e) e)
if (!inherits(try_error, "error"))
y = tolower(x)
return(y)
}
# Lower case
tweetpt = sapply(tweetpt, try.error)
# Removendo os NAs
tweetpt = tweetpt[!is.na(tweetpt)]
names(tweetpt) = NULL
# Classificando emocao
class_emo = classify_emotion(tweetpt, algorithm = "bayes", prior = 1.0)
emotion = class_emo[,7]
# Substituindo NA's por "Neutro"
emotion[is.na(emotion)] = "Neutro"
# Classificando polaridade
class_pol = classify_polarity(tweetpt, algorithm = "bayes")
polarity = class_pol[,4]
# Gerando um dataframe com o resultado
sent_df = data.frame(text = tweetpt, emotion = emotion,
polarity = polarity, stringsAsFactors = FALSE)
sent_df = within(sent_df,
emotion <- factor(emotion, levels = names(sort(table(emotion),
decreasing=TRUE))))
# Emoções encontradas
ggplot(sent_df, aes(x = emotion)) +
geom_bar(aes(y = ..count.., fill = emotion)) +
scale_fill_brewer(palette = "Dark2") +
labs(x = "Categorias", y = "Numero de Tweets")
# Polaridade
ggplot(sent_df, aes(x=polarity)) +
geom_bar(aes(y=..count.., fill=polarity)) +
scale_fill_brewer(palette="RdGy") +
labs(x = "Categorias de Sentimento", y = "Numero de Tweets")