Medindo a popularidade de políticos no G1

G1
Esse post é um oferecimento de uma divagação

Imagine que um político conhecido nacionalmente contratou você para ajudar a responder a seguinte pergunta:

“Quero saber como está a minha popularidade na imprensa e a de meus concorrentes”

Voilá cherie, temos um problema que precisa ser resolvido.

Qual o plano? Bem a principio vamos definir um jornal que será utilizado como base para nossa solução, depois, se quisermos, poderemos generalizar para demais jornais. Vamos então usar o G1 que é um site bem conhecido nacionalmente e que tem muitas publicações diarias, vamos focar somente na categoria política, que é o lugar que esperamos encontrar os acontecimentos do nosso “chefe” e de seus comparsas.

Definido a “fonte de dados” vamos então definir nossa variável resposta que neste caso é a popularidade. Bem, nesse sentido pensei em dividir essa analise em três partes:

Parte I - Para esse primeiro post seria isso - extração de dados e essa análise gráfica
  1. Medir a popularidade da nossa entidade, nosso político, definido um número finito de políticos sendo analisados. O que quero dizer com isso é, vamos selecionar os 50 nomes mais citados semanalmente e ver como varia proporção da citação do nosso político ao longo do tempo.
Parte II - Análise do que sentimento da popularidade
  1. Não satisfeito somente com a proporção de vezes que é citado ao longo da semana vamos agora fazer uma análise de sentimento e verificar se essas citações são boas ou ruins.
Parte III - Possibilidade de escolher outros políticos e fazer benchmarks
  1. Por último vamos juntar tudo isso numa ferramenta selfservice.

Essa é o plano, divagação, agora o que está em jogo é, isso vai dar certo?

Confira nos próximos capítulos.

Um resumo dos resultados

Para os apressadinhos em cada post já vou deixar um resumo dos resultados obtidos, para os que gostam de ver o desenvolvimento código por código, segue a análise depois desse seção.

Assumindo que quem nos contratou foi o Jair Messias Bolsonaro e ele pediu para compararmos sua popularidade com a do Luiz Inácio Lula da Silva. O resultado foi este:

Podemos ver que na véspera das eleições de 2018 a popularidade do Lula era maior do que a do Bolsonaro, ou seja, o percentual de aparições do nome Lula nas noticias daquelas semana era maior que a do Bolsonaro.

Após esse período, Bolsonaro é a pessoa mais citada no G1 na categoria política.

Agora resta a gente saber se essa popularidade é para o lado do bem ou para o lado do mal…

O ferramental

library(tidyverse)

library(pbmcapply)
library(lubridate)

library(tictoc)
library(httr)
library(XML)

library(spacyr)
library(RecordLinkage)
library(directlabels)

Uma breve descrição dos porquês de cada pacote

  • tidyverse: Fundamental para análise de dados, i.e, manipulação :D
  • pbmcapply: Bom para agilizar o processo.
  • lubridate: Essencial para trabalhar com data no .
  • tictoc: Função amigável que substitui o Sys.time()
  • httr: Fazer requisição de páginas “web”.
  • XML: Extrair informação de XML e HTML
  • spacyr: Principal pacote desse post. É um wrapper do pacote spacy do Python. Foi desenvolvido para mineração de texto e facilitou muito o trabalho de identificar entidades nas notícias.
  • RecordLinkage: Util para trabalhar com similaridade de palavras.
  • directlabels: Parar dar um tchã nos gráficos

Coletando notícias do jornal G1

A primeira parte é coletar o máximo de notícias possíveis do G1. Neste caso basta identificarmos o padrão na URL e começar a pagina-la.

Primeira página da categoria política

A URL dessa primeira página é a seguinte: https://g1.globo.com/politica/

Se rolarmos a página até o final vamos encontrar um botão escrito Veja Mais.

Veja mais

Se verificarmos o que está por trás desse botão através da ferramenta do desenvolvedor do nosso browser obteremos o seguinte resultado:

<div class="load-more gui-color-primary-bg">
    <a href="https://g1.globo.com/politica/index/feed/pagina-4.ghtml">Veja mais</a>
</div>

Onde o 4 é a página atual, ou seja, para avançamos para a próxima página basta alterar o 4 por 5, depois o 5 por 6 e assim sucessivamente até atingirmos um número de notícias ou não encontrarmos mais nenhum link.

Em código fica assim.

url <- "https://g1.globo.com/politica/index/feed/pagina-%s.ghtml"

i <- 1
j <- 1
links <- c()
tic()
while (TRUE) {
    page <- GET(sprintf(url, i))
    if (status_code(page) != 200) Sys.sleep(15)
    content <- content(page)
    h <- htmlParse(content)
    # Extrai os links das notícias
    links_new <- xpathSApply(h, "//div[@id='feed-placeholder']//div[@class='bastian-page']//a", xmlGetAttr, "href")
    
    # Verifica se extraiu pelo menos um link
    if (length(links_new) == 0 | is.null(links_new)) {
        j <- j + 1
    } else {
        j <- 1
    }

    if (j > 10) {
        break
    }
    
    # Caso a quantidade de links + links novos seja igual ao total de
    # links da última iteração STOP
    if (lenght(unique(c(links, links_new))) == length(links)) break
    
    links <- unique(c(links, links_new))
    
    # Se coletar mais de vinte mil notícias STOP
    if (length(unique(links)) >= 20000) break

    i <- i + 1
    Sys.sleep(runif(1, 1, 3))
}
toc()

links <- unique(links)

Após coletar os links das notícias começamos a baixar cada uma delas e salvar em uma pasta local.

dir.create("data")
i <- 1
pb <- progress_bar$new(format = "[:bar] :current/:total (:percent) eta: :eta", total = length(links))
pb$tick(i - 1)
tic()
for (i in i:length(links)) {
    page <- GET(links[i])
    cat(content(page, "text"), file = paste0("data/", i, ".html"))
    pb$tick(1)
    Sys.sleep(runif(1, 0.5, 1.5))
}
toc()
# Tempo estimado: 22770.375 sec elapsed

Agora vamos processar as notícias extraindo de cada uma delas: data, texto e título.

Seguir dessa forma, armazenando os HTML das notícias, nos permite uma maior flexibilidade na hora de extrair informação, pode acontecer que somente uma regra de XPATH não colete toda a informação desejamos, podendo existir algumas exceções, com os HTML salvos localmente é possível ir e vir nesse processo de extração de informação sem ter que ficar fazendo novas requisições.
i <- 1
n <- length(list.files("data/"))
dfl <- pbmclapply(1:n, function(i) {
    h <- htmlParse(paste0("data/", i, ".html"), encoding = "utf-8")

    title <- xpathSApply(h, "//h1[@class='content-head__title']", xmlValue)
    if (is.null(title) | length(title) == 0) {
        title <- NA
    }

    data <- xpathSApply(h, "//time[@itemprop='datePublished']", xmlGetAttr, "datetime")
    if (is.null(data) | length(data) == 0) {
        data <- NA
    }

    text <- xpathSApply(h, "//div[@class='mc-article-body']//p[@class='content-text__container ']", xmlValue)
    text <- paste0(text, collapse = "\n")
    if (is.null(text) | length(text) == 0) {
        text <- NA
    }

   tibble(
        title = title,
        data = data,
        text = text
    )
}, mc.cores = 4)

df <- df %>%
    bind_rows()
head(df)
## # A tibble: 6 x 5
##   title                                                                                                data               
##   <chr>                                                                                                <dttm>             
## 1 Ministro de Minas e Energia diz que eleições no Amapá estão asseguradas neste domingo                2020-11-09 19:20:49
## 2 Conselho da Justiça Federal discute execução do projeto Juízo 100% Digital                           2020-11-09 18:44:19
## 3 Justiça Eleitoral do Amapá diz ao TSE que tem condições de manter eleição neste domingo              2020-11-09 18:41:22
## 4 Após ataque hacker ao STJ, Fux anuncia comitê de proteção à Justiça digital                          2020-11-09 18:22:22
## 5 Balança comercial registra superávit de US$ 1,55 bilhão na primeira semana de novembro               2020-11-09 18:09:21
## 6 Amazônia: governo quer controlar ONGs e permitir as que atendam a 'interesses nacionais', diz jornal 2020-11-09 17:15:27
##   text                                                                                                                                                                                                                                                                                                                                                                                                                                                          
##   <chr>                                                                                                                                                                                                                                                                                                                                                                                                                                                         
## 1 "O ministro de Minas e Energia, Bento Albuquerque, afirmou nesta segunda-feira (9) que, apesar do blecaute parcial que atinge o Amapa ha sete dias, \"todas as garantias\" para as eleicoes municipais no proximo domingo (15) estao asseguradas no estado. \n Quase 90% da populacao do Amapa ficou sem energia eletrica na ultima terca-feira (3), quando um incendio atingiu a principal subestacao do estado. O apagao atingiu 13 das 16 cidades do Amapa…
## 2 "O Conselho da Justica Federal discutiu nesta segunda-feira (9), em sessao por videoconferencia, o projeto Juizo 100% Digital, que preve a realizacao de todos os atos processuais exclusivamente por meio eletronico e de maneira remota. \n Em outubro, o Conselho Nacional de Justica (CNJ) aprovou uma resolucao que institui o projeto. Isso vale tambem para as audiencias e sessoes de julgamento, que vao ocorrer exclusivamente por videoconferencia…
## 3 "O presidente do Tribunal Regional Eleitoral do Amapa (TRE-AP), desembargador Rommel Araujo, afirmou nesta segunda-feira (9) que o estado tem condicoes de realizar o primeiro turno das eleicoes municipais no proximo domingo (15) - mesmo em meio ao apagao que atinge a regiao ha uma semana. \n A garantia foi dada em reuniao com o presidente do Tribunal Superior Eleitoral (TSE), ministro Luis Roberto Barroso. Araujo afirmou que o TRE identifico…
## 4 "O presidente do Supremo Tribunal Federal (STF), Luiz Fux, anunciou nesta segunda-feira (9) a criacao pelo Conselho Nacional de Justica (CNJ) de um comite cibernetico para monitorar e discutir medidas que permitam a seguranca dos sistemas eletronicos dos tribunais. \n A medida e uma resposta do CNJ, tambem presidido por Fux, ao ataque hacker da ultima terca-feira (3) que tirou do ar a rede de tecnologia do Superior Tribunal de Justica e para…
## 5 "O superavit e registrado quando as exportacoes superam as importacoes. Quando ocorre o contrario, e registrado deficit comercial. \n Na primeira semana do mes, as exportacoes somaram US$ 4,464 bilhoes e as importacoes, US$ 2,914 bilhoes. \n A media diaria do mes, que considera o valor medio por dia util, a primeira semana registrou alta de 25,8% nas exportacoes - a comparacao e feita com todo o mes de novembro de 2019. Ja o valor importado …
## 6 "O governo federal quer aprovar um marco regulatorio com regras para controlar as organizacoes nao governamentais (ONGs) na regiao da Amazonia e permitir a atuacao somente daquelas que atenderem aos \"interesses nacionais\". \n A informacao foi publicada nesta segunda-feira (9) pelo jornal \"O Estado de S.Paulo\". O jornal obteve documentos do Conselho da Amazonia no qual estao listadas metas do orgao. Uma delas e a aprovacao do marco legal …
##      id yw        
##   <int> <date>    
## 1     1 2020-11-08
## 2     2 2020-11-08
## 3     3 2020-11-08
## 4     4 2020-11-08
## 5     5 2020-11-08
## 6     6 2020-11-08

Dando um tapa nos dados

Nesta etapa adicionamos uma variável ID para que cada notícia seja identificada com um valor único, formatamos a variável data, fazemos um pequeno processo no texto, removendo acentos, e por último criamos uma variável a partir da data, porém, referindo-se a semana.

df <- df %>%
    mutate(
        id = 1:n(),
        data = ymd_hms(data),
        text = iconv(text, to = "ASCII//TRANSLIT"),
        text = trimws(text),
        yw = floor_date(ymd(format(data, "%Y-%m-%d")), "week")
    ) %>%
		drop_na()
summary(df$data)
##                  Min.               1st Qu.                Median                  Mean               3rd Qu.                  Max. 
## "2018-04-13 08:00:10" "2018-11-14 16:53:05" "2019-07-06 00:30:15" "2019-07-25 02:25:19" "2020-03-30 12:21:33" "2020-11-14 09:00:51"
df %>%
    group_by(yw) %>%
    count() %>%
    ggplot(aes(x = yw, y = n, group = 1)) +
    geom_line(size = 1.5) +
    theme_minimal() +
    geom_smooth(se = FALSE) +
    labs(x = "Semana", y = "Volume de notícias") +
    ggtitle("Volume de notícias por semana da categoria política do jornal g1")

Identificando entidades nas notícias de política do G1

Agora é que a magia acontece, com os dados nas mãos, ou melhor, em memória basta aplicarmos uma única função do pacote spacyr e BUMM temos nossas entidades.

spaCy é uma biblioteca pronta feita em python para processamento de linguagem natural! Caso tenha interesse de uma olhada no github, ou no site ou caso seja o Reriano no tutorial do spaCyR.

Aqui vou me ater somente em extrair as entidades do texto.

Em código ficou assim:

spacy_initialize(model = "pt")

ent <- split(df$text, df$id) %>% 
    pbmcmapply(
        x = .,
        y = df$id,
        function(x, y) {
            suppressMessages(
                tb <- spacy_extract_entity(x)
            )
            if (!is.null(tb) && nrow(tb) > 0) {
                tb <- tb %>% 
                    mutate(
                        doc_id = y
                    )
            } 
            return(tb)
        },
        mc.cores = 4,
        SIMPLIFY = FALSE
    )
toc()

ent <- ent %>%
    bind_rows() %>%
    as_tibble()

ent2 <- ent %>%
    filter(str_detect(ent_type, "PER"))
head(ent2)
## # A tibble: 6 x 5
##   doc_id text               ent_type start_id length
##    <int> <chr>              <chr>       <dbl>  <int>
## 1      1 Energia            PER             6      1
## 2      1 Bento Albuquerque  PER             8      2
## 3      1 Bento              PER           140      1
## 4      1 Randolfe Rodrigues PER           210      2
## 5      1 Bento Albuquerque  PER           268      2
## 6      1 Rommel Araujo      PER           326      2

O mesmo nome só que diferente

Com as entidades identificadas e filtradas por pessoas temos que encontrar aquelas que fazem referência a mesma pessoa. Por exemplo, Jair Messias Bolsonaro é a mesma pessoa que Jair Bolsonaro e provavelmente, na maioria das notícias, é a mesma que Bolsonaro, no entanto difere de Eduardo Bolsonaro. Outro exemplo pode ser dado por Ciro que, na maioria das notícias, esta fazendo referência a Ciro Gomes, no entanto existe uma outra entidade que pode confudir que é o Ciro Nogueira.

Como dito anteriormente “O mesmo nome só que diferente”, nosso objetivo é então encontrar essas relações. A forma que pensei para solucionar isso foi:

  • Pensar no especifico - Primeiramente vamos trabalhar por notícia, identificando em cada uma delas o nome mais simples e atribuindo ao mais complexo (entenda complexo como nome composto). Por exemplo, Bolsonaro seria transformado, dependendo da noticia, em Jair Messias Bolsonaro ou somente Jair Bolsonaro.

  • Atribuir do mais simples ao mais complexo - Depois selecionar os nomes mais complexos, isto é, nomes com 3 palavras, por exemplo, e encontrar nos nomes com 4 palavras aqueles que tem a maior similaridade com os nomes de 3 palavras e atribuir o nome de 4 palavras ao de três. Por exemplo, Jair Bolsonaro -> Jair Messias Bolsonaro.

  • Generalizar o especifico - Por último repetir o primeiro passo porém dessa vez sem restrição. Repetir para o todo.

Em código fica:

## Agrega os dados e remove entidades com uma frequência total menor que 50
ent3 <- ent2 %>%
    rename(name = text) %>% 
    group_by(doc_id, name) %>%
    count(name = "freqDoc") %>%
    ungroup() %>%
    group_by(name) %>%
    mutate(freqTotal = sum(freqDoc)) %>%
    arrange(desc(freqTotal)) %>%
    ungroup() %>%
    filter(str_detect(name, "[[:lower:]]")) %>%
    filter(freqTotal >= 50)

# Funcao para calcular a quantidade de palavras
nwords <- function(x) {
    length(strsplit(x, "\\s+")[[1]])
}
nwords <- Vectorize(nwords, "x")

ent4 <- ent3 %>%
    mutate(nwords = nwords(name)) %>%
    ## constroi uma lista por noticia
    group_by(doc_id) %>%
    group_split()
## Código para uma noticia
set.seed(12345)
x <- ent4[[round(runif(1, 1, length(ent4)))]]
x
## # A tibble: 13 x 5
##    doc_id name                      freqDoc freqTotal nwords
##     <int> <chr>                       <int>     <int>  <int>
##  1  13476 Bolsonaro                       5     13123      1
##  2  13476 Jair Bolsonaro                  1      9241      2
##  3  13476 Sergio Moro                     1      2538      2
##  4  13476 Luiz Inacio Lula da Silva       1      1115      5
##  5  13476 Apos                            1       886      1
##  6  13476 Onyx Lorenzoni                  1       663      2
##  7  13476 Casa Civil                      1       505      2
##  8  13476 Presidencia da Republica        1       446      3
##  9  13476 Familia                         2       196      1
## 10  13476 Direitos Humanos                3       174      2
## 11  13476 Damares Alves                   3       122      2
## 12  13476 Damares                         1        62      1
## 13  13476 Magno Malta                     3        50      2
# Seleciona todas as palavras simples - no caso somente Bolsonaro
oneName <- x$name[do.call(c, map(strsplit(x$name, "\\s+"), length)) == 1]
oneName <- unique(oneName)

if (length(oneName) > 0) {
    for (j in 1:length(oneName)) {
        ## filtra todos os nomes que contenham o nome simples e atribui o nome
        ## complexo ao nome mais simples
        newName <- x %>%
            filter(str_detect(name, !!oneName[j])) %>%
            arrange(desc(freqTotal)) %>%
            filter(nwords > 1) %>%
            slice(1) %>%
            pull(name)

        if (length(newName) == 0) next
				
        ## ^[a-z] serve para encontrar algumas relações especificas como 'presidente Bolsonaro', 'ex-presidente Lula' etc...
        x$name[str_detect(x$name, str_c("^", oneName[j], "$|^[a-z]+.+", oneName[j]))] <- newName
    }
		
    # agrega resultados
    x <- x %>%
        group_by(doc_id, name) %>%
        summarise(
            freqDoc = sum(freqDoc),
            freqTotal = sum(unique(freqTotal)),
            nwords = max(nwords)
        ) %>%
        ungroup()
}
x
## # A tibble: 11 x 5
##    doc_id name                      freqDoc freqTotal nwords
##     <int> <chr>                       <int>     <int>  <int>
##  1  13476 Apos                            1       886      1
##  2  13476 Casa Civil                      1       505      2
##  3  13476 Damares Alves                   4       184      2
##  4  13476 Direitos Humanos                3       174      2
##  5  13476 Familia                         2       196      1
##  6  13476 Jair Bolsonaro                  6     22364      2
##  7  13476 Luiz Inacio Lula da Silva       1      1115      5
##  8  13476 Magno Malta                     3        50      2
##  9  13476 Onyx Lorenzoni                  1       663      2
## 10  13476 Presidencia da Republica        1       446      3
## 11  13476 Sergio Moro                     1      2538      2

Agora envelopamos para aplicar essa mesma logica a todas as noticias.

ent5 <- pbmclapply(ent4, function(x) {
    oneName <- x$name[do.call(c, map(strsplit(x$name, "\\s+"), length)) == 1]
    oneName <- unique(oneName)

    if (length(oneName) > 0) {
        for (j in 1:length(oneName)) {
            newName <- x %>%
                filter(str_detect(name, !!oneName[j])) %>%
                arrange(desc(freqTotal)) %>%
                filter(nwords == max(nwords)) %>%
                slice(1) %>%
                pull(name)

            if (length(newName) == 0) next

            x$name[str_detect(x$name, str_c("^", oneName[j], "|^[a-z]+.+", oneName[j]))] <- newName
        }

        x <- x %>%
            group_by(doc_id, name, freqDoc) %>%
            summarise(
                freqTotal = sum(freqTotal),
                nwords = max(nwords)
            ) %>%
            ungroup()
    }

    return(x)
}, mc.cores = 4)

Agora aplicamos a segunda parte que é identificar nomes com 2 palavras em nomes com 3 palavras, nomes com 3 palavras em 4 palavras etc… Nesse caso vamos usar uma função do pacote RecordLinkage que calcula a similaridade entre duas palavras. A função que eu utilização é a jarowinkler. Essa função é muito utilizada para fazer depara de bases de dados através de similaridade dos campos.

ent6 <- ent5 %>%
    bind_rows() %>% 
    mutate(nwords = nwords(name))

for (i in 2:4) {
    names <- ent6 %>%
        filter(nwords == !!i) %>%
        pull(name) %>%
        unique()

    names2 <- ent6 %>%
        filter(nwords == (!!i + 1)) %>%
        pull(name) %>%
        unique()

    namesc <- expand.grid(
        namec = names2,
        names = names,
        stringsAsFactors = FALSE
    )

    namesc <- namesc %>%
        mutate(sim = jarowinkler(namec, names))

    namesc <- namesc %>%
        filter(sim >= 0.88) ## Corte

    if (nrow(namesc) > 0) {
        for (r in 1:nrow(namesc)) {
            ent6$name[str_detect(ent6$name, namesc$names[r])] <- namesc$namec[r]
        }
    }
}

ent6 <- ent6 %>%
    group_by(doc_id, name) %>%
    summarise(
        freqDoc = sum(freqDoc),
        freqTotal = max(freqTotal) + min(freqTotal),
        nwords = max(nwords)
    ) %>%
    ungroup()

Em teoria agora todos os nomes com duas ou três palavras estarão sendo referenciados pelos mais complexos, mais de duas ou mais de três palavras. O que quero dizer é pelo similaridade o senhor Jair Bolsonaro passou a ser Jair Messias Bolsonaro.

Por último rodamos a primeira parte so que dessa vez para todas as notícias ao mesmo tempo. Ou seja, os nomes mais simples, com apenas uma palavra, vão ser substituidos pelos seus respectivos mais complexos, mais de uma palavra.

oneName <- ent6 %>%
    mutate(nwords = nwords(name)) %>% 
    filter(nwords == 1) %>%
    pull(name)
oneName <- unique(oneName)

for (j in 1:length(oneName)) {
    newName <- ent6 %>%
        filter(str_detect(name, !!oneName[j])) %>%
        arrange(desc(freqTotal)) %>%
        filter(freqTotal == max(freqTotal)) %>%
        pull(name) %>% 
        unique()

    if (length(newName) > 1) {
        newName <- ent6 %>%
            filter(name %in% !!newName) %>%
            distinct(name, .keep_all = TRUE) %>%
            filter(freqTotal == max(freqTotal)) %>%
            pull(name)
    }
    ent6$name[str_detect(ent6$name, str_c("^", oneName[j], "$|^[a-z]+.+", oneName[j]))] <- newName
}

ent6 <- ent6 %>%
    group_by(doc_id, name) %>%
    summarise(
        freqDoc = sum(freqDoc),
        freqTotal = sum(unique(freqTotal)),
        nwords = max(nwords)
    ) %>%
    ungroup()
ent6
## # A tibble: 69,093 x 5
##    doc_id name                 freqDoc freqTotal nwords
##     <int> <chr>                  <int>     <int>  <int>
##  1      1 Bento Albuquerque          3       250      2
##  2      1 Energia                    1       152      1
##  3      1 Randolfe Rodrigues         1       384      2
##  4      2 Luiz Fux                   1      2636      2
##  5      3 Bento Albuquerque          1       250      2
##  6      3 Energia                    1       152      1
##  7      3 Luis Roberto Barroso       1      1550      3
##  8      3 Randolfe Rodrigues         1       384      2
##  9      4 Humberto Martins           2       210      2
## 10      4 Luiz Fux                   4      1318      2
## # … with 69,083 more rows

Um último passo

Para finalizar vamos fazer uma limpeza manual somente para as 100 primeiras entidades mais frequentes.

ent6 %>%
    arrange(desc(freqTotal)) %>%
    distinct(name) %>%
    slice(1:100) %>% 
    pull(name)
##   [1] "Jair Messias Bolsonaro"              "Flavio Bolsonaro"                    "Eduardo Bolsonaro"                   "Carlos Bolsonaro"                    "Luiz Eduardo Ramos"                  "Michelle Bolsonaro"                  "Sergio Moro"                         "Luiz Inacio Lula da Silva"           "Michel Temer"                        "Nao"                                 "Paulo Guedes"                       
##  [12] "Fernando Haddad"                     "Rodrigo Maia"                        "Alexandre de Moraes"                 "Tambem"                              "Edson Fachin"                        "Gilmar Mendes"                       "Dias Toffoli"                        "Geraldo Alckmin"                     "Celso de Mello"                      "Ciro Gomes"                          "Luiz Edson Fachin"                  
##  [23] "Luiz Fux"                            "Hamilton Mourao"                     "Raquel Dodge"                        "Dilma Rousseff"                      "Lava Jato"                           "Donald Trump"                        "Casa Civil"                          "Davi Alcolumbre"                     "Leia"                                "Augusto Aras"                        "Apos"                               
##  [34] "Eduardo Pazuello"                    "Ricardo Salles"                      "Ricardo Lewandowski"                 "Luis Roberto Barroso"                "Onyx Lorenzoni"                      "Presidencia da Republica"            "Eduardo Cunha"                       "Rosa Weber"                          "Marco Aurelio Mello"                 "Augusto Heleno"                      "Saude"                              
##  [45] "Marina Silva"                        "Ciro Nogueira"                       "Carmen Lucia"                        "Eduardo Gomes"                       "Rogerio Marinho"                     "Fabricio Queiroz"                    "Wilson Witzel"                       "Eduardo Paes"                        "Renan Calheiros"                     "Seguranca Publica"                   "Alem"                               
##  [56] "Comissao de Constituicao"            "Procuradoria Geral da Republica"     "Estadao"                             "Ernesto Araujo"                      "Luiz Henrique Mandetta"              "Acho"                                "Carlos Alberto dos Santos Cruz"      "Gabinete de Seguranca Institucional" "Joao Doria"                          "Aecio Neves"                         "Alvaro Dias"                        
##  [67] "Voce"                                "Abraham Weintraub"                   "Henrique Meirelles"                  "Alexandre Ramagem"                   "Jose Serra"                          "Adelio Bispo de Oliveira"            "Randolfe Rodrigues"                  "Paulo Marinho"                       "Paulo Camara"                        "Joesley Batista"                     "Cesare Battisti"                    
##  [78] "Deltan Dallagnol"                    "Chico Rodrigues"                     "Raul Jungmann"                       "Deus"                                "Adelio Bispo"                        "Marcelo Ramos"                       "Quero"                               "Sergio Cabral"                       "Geddel Vieira Lima"                  "Samuel Moreira"                      "Carla Zambelli"                     
##  [89] "Marielle Franco"                     "Nicolas Maduro"                      "Fernando Henrique Cardoso"           "Joice Hasselmann"                    "Nelson Teich"                        "Rodrigo Janot"                       "Romero Juca"                         "Moreira Franco"                      "Mauricio Valeixo"                    "Pesquisa Ibope"                      "Previdencia Social"                 
## [100] "Gustavo Bebianno"

Observamos que existem algumas entidades que não são na realidade nomes de pessoas, como por exemplo Nao, Tambem etc… Aqui vamos remove-las na base do olhometro…

ent7 <- ent6 %>%
    filter(!(name %in% c(
        "Nao", "Tambem", "Lava Jato", "Casa Civil", "Leia", "Apos",
        "Presidencia da Republica", "Saude", "Seguranca Publica", "Alem",
        "Comissao de Constituicao", "Acho", "Gabinete de Seguranca Institucional",
        "Voce", "Deus", "Quero", "Pesquisa Ibope", "Previdencia Social", "Estadao"
    )))

Repetimos o processo mais uma vez…

ent7 %>%
    arrange(desc(freqTotal)) %>%
    distinct(name) %>%
    slice(1:100) %>% 
    pull(name)
##   [1] "Jair Messias Bolsonaro"          "Flavio Bolsonaro"                "Eduardo Bolsonaro"               "Carlos Bolsonaro"                "Luiz Eduardo Ramos"              "Michelle Bolsonaro"              "Sergio Moro"                     "Luiz Inacio Lula da Silva"       "Michel Temer"                    "Paulo Guedes"                    "Fernando Haddad"                 "Rodrigo Maia"                    "Alexandre de Moraes"            
##  [14] "Edson Fachin"                    "Gilmar Mendes"                   "Dias Toffoli"                    "Geraldo Alckmin"                 "Celso de Mello"                  "Ciro Gomes"                      "Luiz Edson Fachin"               "Luiz Fux"                        "Hamilton Mourao"                 "Raquel Dodge"                    "Dilma Rousseff"                  "Donald Trump"                    "Davi Alcolumbre"                
##  [27] "Augusto Aras"                    "Eduardo Pazuello"                "Ricardo Salles"                  "Ricardo Lewandowski"             "Luis Roberto Barroso"            "Onyx Lorenzoni"                  "Eduardo Cunha"                   "Rosa Weber"                      "Marco Aurelio Mello"             "Augusto Heleno"                  "Marina Silva"                    "Ciro Nogueira"                   "Carmen Lucia"                   
##  [40] "Eduardo Gomes"                   "Rogerio Marinho"                 "Fabricio Queiroz"                "Wilson Witzel"                   "Eduardo Paes"                    "Renan Calheiros"                 "Procuradoria Geral da Republica" "Ernesto Araujo"                  "Luiz Henrique Mandetta"          "Carlos Alberto dos Santos Cruz"  "Joao Doria"                      "Aecio Neves"                     "Alvaro Dias"                    
##  [53] "Abraham Weintraub"               "Henrique Meirelles"              "Alexandre Ramagem"               "Jose Serra"                      "Adelio Bispo de Oliveira"        "Randolfe Rodrigues"              "Paulo Marinho"                   "Paulo Camara"                    "Joesley Batista"                 "Cesare Battisti"                 "Deltan Dallagnol"                "Chico Rodrigues"                 "Raul Jungmann"                  
##  [66] "Adelio Bispo"                    "Marcelo Ramos"                   "Sergio Cabral"                   "Geddel Vieira Lima"              "Samuel Moreira"                  "Carla Zambelli"                  "Marielle Franco"                 "Nicolas Maduro"                  "Fernando Henrique Cardoso"       "Joice Hasselmann"                "Nelson Teich"                    "Rodrigo Janot"                   "Romero Juca"                    
##  [79] "Moreira Franco"                  "Mauricio Valeixo"                "Gustavo Bebianno"                "Fernando Bezerra Coelho"         "Frederick Wassef"                "Waldery Rodrigues"               "Eunicio Oliveira"                "Otavio do Rego Barros"           "Andre Mendonca"                  "Jose Dirceu"                     "Emmanuel Macron"                 "Luciano Bivar"                   "Fernando Azevedo e Silva"       
##  [92] "Braga Netto"                     "Roberto Alvim"                   "Tasso Jereissati"                "Anvisa"                          "Gleisi Hoffmann"                 "Felix Fischer"                   "Marcelo Alvaro Antonio"          "Paulo Roberto Costa"             "Turismo"
ent7 <- ent7 %>%
    filter(!(name %in% c(
        "Anvisa", "Turismo", "Sao Paulo", "Familia", "Lei da Ficha Limpa", "Suica",
        "Procuradoria Geral da Republica", "Centrao", "Poder Judiciario", 
        "Direitos Humanos", "Perguntado"
    )))

Adicionando a semana da publicação…

dff <- ent7 %>%
    left_join(
        df %>%
            select(id, yw) %>%
            rename(doc_id = id),
        by = "doc_id"
    )

Adicionando quantidade de noticias relacionadas a entidade na semana.

dff <- dff %>%
    group_by(name, yw) %>%
    mutate(freqWeek = n()) %>%
    select(doc_id, yw, name, nwords, everything()) %>% 
    ungroup()

Pronto!! Agora temos os dados prontos para análise, bora lá!

Respondendo a divagação

Vamos selecionar então os 50 individuos mais frequentes para o periodo que vai de 2018-04-08 até 2020-11-08.

Entenda aqui popularidade da seguinte maneira:

Das 50 pessoas mais citadas na semana o nosso político representa x% dessas citações!

Digamos que nosso contratante seja o senhor presidente da republica, sua popularidade, como definida no começo do post, por semana seria dada da seguinte maneira:

totWeek <- dff %>%
    group_by(yw) %>%
    distinct(name, .keep_all = TRUE) %>% 
    select(yw, name, freqWeek) %>% 
    arrange(desc(freqWeek)) %>%
    slice(1:50) %>%
    arrange(desc(freqWeek)) %>% 
    summarise(totalCit = sum(freqWeek))
    
dffWeek <- dff %>%
    group_by(yw) %>% 
    distinct(name, .keep_all = TRUE) %>% 
    select(yw, name, freqWeek)
dffWeek %>%
    filter(name == "Jair Messias Bolsonaro") %>%
    left_join(
        totWeek
    ) %>% 
    mutate(
        pop = freqWeek / totalCit
    ) %>% 
    ggplot(aes(x = yw, y = pop, group = 1)) +
    geom_line(size = 1.2) +
    scale_y_continuous(label = scales::percent) +
    theme_classic() +
    labs(x = "Semana", y = "Indice de popularidade") +
    ggtitle("Popularidade de Jair Messias Bolsonaro por Semana")

Podemos observar que a 1/4 de todas as citações das 50 pessoas mais comentadas na semana são referentes ao Jair Messias Bolsonaro.

Agora vamos mudar nosso contratante para Ciro Gomes.

dffWeek %>%
    filter(name == "Ciro Gomes") %>%
    left_join(
        totWeek
    ) %>%
    mutate(
        pop = freqWeek / totalCit
    ) %>% 
    ggplot(aes(x = yw, y = pop, group = 1)) +
    geom_line(size = 1.2) +
    scale_y_continuous(label = scales::percent, limits = c(0,0.3)) +
    theme_classic() +
    labs(x = "Semana", y = "Indice de popularidade") +
    ggtitle("Popularidade de Ciro Gomes por Semana")

Observamos que a época de maior citação do Ciro foi nas vésperas da eleição.

Vamos comparar agora o Lula com o Bolsonaro

dffWeek %>%
    filter(name %in% c("Jair Messias Bolsonaro", "Luiz Inácio Lula da Silva")) %>%
    left_join(
        totWeek
    ) %>%
    mutate(
        pop = freqWeek / totalCit
    ) %>% 
    ggplot(aes(x = yw, y = pop, colour = name, group = name)) +
    geom_line(size = 1.2) +
    scale_y_continuous(label = scales::percent) +
    theme_classic() +
    labs(x = "Semana", y = "Indice de popularidade") +
    ggtitle("Popularidade de Jair Messias Bolsonaro vs Luiz Inácio Lula da Silva por Semana") +
    ggplot2::scale_x_date(expand = c(0.25, 0.25)) +
    geom_dl(aes(label = name), method = list(dl.combine("last.points")), cex = 0.5) +
    theme(legend.position = "none")

Agora vamos comparar Haddad vs Bolsonaro.

dffWeek %>%
    filter(name %in% c("Jair Messias Bolsonaro", "Fernando Haddad")) %>%
    left_join(
        totWeek
    ) %>%
    mutate(
        pop = freqWeek / totalCit
    ) %>% 
    ggplot(aes(x = yw, y = pop, colour = name, group = name)) +
    geom_line(size = 1.2) +
    scale_y_continuous(label = scales::percent) +
    theme_classic() +
    labs(x = "Semana", y = "Indice de popularidade") +
    ggtitle("Popularidade de Jair Messias Bolsonaro vs Luiz Inácio Lula da Silva por Semana") +
    ggplot2::scale_x_date(expand = c(0.25, 0.25)) +
    geom_dl(aes(label = name), method = list(dl.combine("last.points")), cex = 0.5) +
    theme(legend.position = "none")

De modo geral, o a pessoa mais comentada é o Jair Messias Bolsonaro, no entanto, mesmo assim, conseguimos identificar a popularidade de outras pessoas. Agora um proximo passo é identificar se essa popularidade ela é positiva ou negativa, mas isso vai ficar para um próximo post.

Andryas Waurzenczak
Andryas Waurzenczak
Estatístico PL.

Criando conteudo de ciência de dados para o mundo.

comments powered by Disqus
Próximo

Relacionados