<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Paixão por Dados</title>
    <link>http://www.sillasgonzaga.com/index.xml</link>
    <description>Recent content on Paixão por Dados</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <copyright>&amp;copy; 2016. All rights reserved.</copyright>
    <lastBuildDate>Thu, 04 Jul 2019 00:00:00 +0000</lastBuildDate>
    <atom:link href="http://www.sillasgonzaga.com/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Anunciando um &#34;novo&#34; dataset no Kaggle: Pesquisa Origem Destino do Metrô SP</title>
      <link>http://www.sillasgonzaga.com/post/anunciando-um-novo-dataset-no-kaggle-pesquisa-origem-destino-do-metr%C3%B4-sp/</link>
      <pubDate>Thu, 04 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/anunciando-um-novo-dataset-no-kaggle-pesquisa-origem-destino-do-metr%C3%B4-sp/</guid>
      <description>&lt;div id=&#34;motivacao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Motivação&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www1.folha.uol.com.br/cotidiano/2019/07/sao-paulo-ganha-nova-hora-do-rush-com-aumento-de-viagens-ao-meio-dia.shtml&#34;&gt;Esta matéria da Folha de São Paulo&lt;/a&gt; me motivou a fazer algo que sempre tive muito interesse e que foi responsável por ajudar a desenvolver meu raciocínio analítico: baixar um conjunto de dados público, fazer minhas próprias análises e tirar conclusões que considero interessantes.&lt;/p&gt;
&lt;p&gt;Ao baixar o dataset &lt;a href=&#34;http://www.metro.sp.gov.br/pesquisa-od/&#34;&gt;neste link&lt;/a&gt;, contudo, meu primeiro obstáculo foi o formato em que os dados foram disponbilizados. Problemas como células mescladas, problemas de encoding, formato matricial (e não tidy) de tabelas, etc. Além disso, o próprio nome dos arquivos não esclarece seu conteúdo e não facilita o trabalho de quem deseja juntar as diferentes tabelas para tirar conclusões criativas.&lt;/p&gt;
&lt;p&gt;Por exemplo, existe relação entre renda familiar e a “popularidade” de uma zona de destino de viagens? Bairros mais ricos recebem mais trabalhadores da indústria, de serviços ou de comércio? Quais distritos de São Paulo mais “exportam” pessoas que pegam metrô para buscar emprego? As possibilidades de análises e modelagens são muitas.&lt;/p&gt;
&lt;p&gt;Com esta motivação, ao perceber que o trabalho de limpeza de dados, realizado &lt;a href=&#34;https://gist.github.com/sillasgonzaga/5e3282d160ea87a97c0be0b46d00df8a&#34;&gt;neste código&lt;/a&gt;, poderia ser trabalhoso para mais gente além de mim, decidi exportar os arquivos &lt;a href=&#34;https://www.kaggle.com/sillas/pesquisa-origem-destino-2017-metr-so-paulo&#34;&gt;no Kaggle&lt;/a&gt; em um arquivo SQLite que contem as tabelas, além de salvar as tabelas individualmente em arquivos csv após passar pelo processo de limpeza e padronização de dados.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;como-usar-os-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Como usar os dados&lt;/h2&gt;
&lt;p&gt;O &lt;code&gt;dplyr&lt;/code&gt; tem um recurso muito legal: pode interagir diretamente com arquivos SQL, sejam eles como arquivo local ou como um sistema de banco de dados hospedado em algum servidor. O &lt;a href=&#34;https://db.rstudio.com/&#34;&gt;Rstudio&lt;/a&gt; possui um site com vários tutoriais sobre trabalhar com bancos de dados SQL.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(RSQLite)
library(tidyverse)
# apos baixar o arquivo SQLITE chamado DB_ORIGEM_DESTINO_SP do Kaggle,
# indique abaixo o caminho para o arquivo
arquivo_db &amp;lt;- &amp;quot;/home/sillas/R/Projetos/pesquisa_origem_destino/data/DB_ORIGEM_DESTINO_SP&amp;quot;
# criar conexao com o banco
con &amp;lt;- dbConnect(RSQLite::SQLite(), dbname = arquivo_db)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A função &lt;code&gt;dbListTables&lt;/code&gt; exibe quais tabelas estão disponíveis:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;dbListTables(con)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##  [1] &amp;quot;dados_geograficos_das_zonas&amp;quot;                                                                           
##  [2] &amp;quot;dados_gerais&amp;quot;                                                                                          
##  [3] &amp;quot;dados_gerais_por_zona_de_pesquisa_2017&amp;quot;                                                                
##  [4] &amp;quot;dicionario_das_variaveis_da_tabela_dados_gerais&amp;quot;                                                       
##  [5] &amp;quot;dicionarios_de_variaveis_da_tabela_de_dados_gerais_encodificadas&amp;quot;                                      
##  [6] &amp;quot;empregos_com_trabalho_externo_ou_interno_por_zona_de_emprego_2017&amp;quot;                                     
##  [7] &amp;quot;empregos_em_endereco_fixo_na_propria_residencia_e_sem_endereco_fixo_por_zona_de_emprego_2017&amp;quot;          
##  [8] &amp;quot;empregos_por_classe_de_atividade_e_zona_de_emprego_2017&amp;quot;                                               
##  [9] &amp;quot;empregos_por_setor_de_atividade_e_zona_de_emprego_2017&amp;quot;                                                
## [10] &amp;quot;empregos_por_vinculo_empregaticio_e_zona_de_emprego_2017&amp;quot;                                              
## [11] &amp;quot;familias_por_numero_de_automoveis_particulares_e_zona_de_residencia_2017&amp;quot;                              
## [12] &amp;quot;matriculas_escolares_por_tipo_de_estabelecimento_e_zona_de_escola_2017&amp;quot;                                
## [13] &amp;quot;populacao_por_condicao_de_atividade_e_zona_de_residencia_2017&amp;quot;                                         
## [14] &amp;quot;populacao_por_faixa_de_renda_familiar_mensal_e_zona_de_residencia_2017&amp;quot;                                
## [15] &amp;quot;populacao_por_faixa_etaria_e_zona_de_residencia_2017&amp;quot;                                                  
## [16] &amp;quot;populacao_por_genero_e_zona_de_residencia_2017&amp;quot;                                                        
## [17] &amp;quot;populacao_por_grau_de_instrucao_e_zona_de_residencia_2017&amp;quot;                                             
## [18] &amp;quot;populacao_que_trabalha_por_vinculo_empregaticio_do_primeiro_trabalho_e_zona_de_residencia_2017&amp;quot;        
## [19] &amp;quot;renda_total_renda_media_familiar_renda_per_capita_e_renda_mediana_familiar_por_zona_de_residencia_2017&amp;quot;
## [20] &amp;quot;tempo_medio_das_viagem_produzidas_por_tipo_de_viagem_e_zona_de_origem_2017&amp;quot;                            
## [21] &amp;quot;viagens_diarias_a_pe_por_zonas_de_origem_e_destino_2017&amp;quot;                                               
## [22] &amp;quot;viagens_diarias_atraidas_por_modo_principal_e_zona_de_destino_2017&amp;quot;                                    
## [23] &amp;quot;viagens_diarias_atraidas_por_motivo_e_zona_de_destino_2017&amp;quot;                                            
## [24] &amp;quot;viagens_diarias_atraidas_por_tipo_e_zona_de_destino_2017&amp;quot;                                              
## [25] &amp;quot;viagens_diarias_motorizadas_por_zonas_de_origem_e_destino_2017&amp;quot;                                        
## [26] &amp;quot;viagens_diarias_nao_motorizadas_por_zonas_de_origem_e_destino_2017&amp;quot;                                    
## [27] &amp;quot;viagens_diarias_por_bicicleta_e_zonas_de_origem_e_destino_2017&amp;quot;                                        
## [28] &amp;quot;viagens_diarias_por_modo_coletivo_e_zonas_de_origem_e_destino_2017&amp;quot;                                    
## [29] &amp;quot;viagens_diarias_por_modo_individual_e_zonas_de_origem_e_destino_2017&amp;quot;                                  
## [30] &amp;quot;viagens_diarias_produzidas_a_pe_por_razao_de_escolha_do_modo_e_zona_de_origem_2017&amp;quot;                    
## [31] &amp;quot;viagens_diarias_produzidas_por_modo_principal_e_zona_de_origem_2017&amp;quot;                                   
## [32] &amp;quot;viagens_diarias_produzidas_por_motivo_e_zona_de_origem_2017&amp;quot;                                           
## [33] &amp;quot;viagens_diarias_produzidas_por_tipo_e_zona_de_origem_2017&amp;quot;                                             
## [34] &amp;quot;viagens_diarias_totais_por_zonas_de_origem_e_destino_2017&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para iniciantes nesse dataset, as tabelas mais importantes são estas:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;dbListTables(con)[c(1, 4, 5)]&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;dados_geograficos_das_zonas&amp;quot;                                     
## [2] &amp;quot;dicionario_das_variaveis_da_tabela_dados_gerais&amp;quot;                 
## [3] &amp;quot;dicionarios_de_variaveis_da_tabela_de_dados_gerais_encodificadas&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para interagir com uma tabela desse banco de dados, usa-se a função &lt;code&gt;tbl()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tbl(con, # arquivo de conexao com o banco
    &amp;quot;dicionario_das_variaveis_da_tabela_dados_gerais&amp;quot;) # nome da tabela)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # Source:   table&amp;lt;dicionario_das_variaveis_da_tabela_dados_gerais&amp;gt; [?? x
## #   2]
## # Database: sqlite 3.22.0
## #   [/home/sillas/R/Projetos/pesquisa_origem_destino/data/DB_ORIGEM_DESTINO_SP]
##    VAR_NOME VAR_CONTEUDO                             
##    &amp;lt;chr&amp;gt;    &amp;lt;chr&amp;gt;                                    
##  1 ZONA     Zona do Domicílio                        
##  2 MUNI_DOM Município de Domicílio                   
##  3 CO_DOM_X Coordenada X Domicílio                   
##  4 CO_DOM_Y Coordenada Y Domicílio                   
##  5 ID_DOM   Identifica Domicílio                     
##  6 F_DOM    Identifica Primeiro Registro do Domicílio
##  7 FE_DOM   Fator de Expansão do Domicílio           
##  8 DOM      Número do Domicílio                      
##  9 CD_ENTRE Código de Entrevista                     
## 10 DATA     Data da Entrevista                       
## # … with more rows&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O &lt;code&gt;dplyr&lt;/code&gt; faz, então, algo que se chama de consulta lazy: o output acima não é um dataframe ou tibble, mas sim uma breve exibição das primeiras linhas do resultado da consulta à tabela. O benefício disso é que o resultado da consulta não foi carregado para a memória RAM, o que é muito útil ao lidar com bancos de dados muito grandes e queries complexas.&lt;/p&gt;
&lt;p&gt;No código abaixo, por exemplo, eu junto duas tabelas diferentes e filtro as zonas da cidade de São Paulo para só então “baixar” ou trazer os dados para a memória RAM com a função &lt;code&gt;collect()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_zonas_sp &amp;lt;- tbl(con, &amp;quot;dados_gerais&amp;quot;) %&amp;gt;% 
  left_join(tbl(con, &amp;quot;dados_geograficos_das_zonas&amp;quot;),
            by = c(&amp;quot;ZONA&amp;quot; = c(&amp;quot;COD_ZONA&amp;quot;))) %&amp;gt;% 
  # filtrar zonas de SP
  filter(NOME_MUNICIPIO == &amp;quot;São Paulo&amp;quot;) %&amp;gt;% 
  collect()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir dos dados obtidos, podemos fazer qualquer tipo de análise desejada. Por exemplo, um gráfico contendo a renda familiar mediana por distrito na cidade de São Paulo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# distritos por renda familiar
renda_por_distrito &amp;lt;- df_zonas_sp %&amp;gt;% 
  filter(!is.na(RENDA_FA)) %&amp;gt;% 
  group_by(NOME_AREA_SP_CAPITAL, NOME_DISTRITO) %&amp;gt;% 
  summarise(RENDA_MEDIANA = median(RENDA_FA))

quartis &amp;lt;- quantile(renda_por_distrito$RENDA_MEDIANA,
                    prob = c(.25, .50, .75))

renda_por_distrito %&amp;gt;% 
  ggplot(aes(x = fct_reorder(NOME_DISTRITO, RENDA_MEDIANA),
             y = RENDA_MEDIANA)) +
  geom_col(fill = &amp;quot;#A82B2EFE&amp;quot;) +
  geom_hline(yintercept = quartis, linetype = &amp;quot;dashed&amp;quot;) +
  facet_wrap(vars(NOME_AREA_SP_CAPITAL), scales = &amp;quot;free_y&amp;quot;) +
  labs(x = NULL, y = NULL,
       title = &amp;quot;Mediana da renda familiar por zona na cidade de SP&amp;quot;) +
  scale_y_continuous(breaks = seq(0, 10000, 2000),
                     minor_breaks = NULL) +
  coord_flip()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2019-07-04-anunciando-um-novo-dataset-no-kaggle-pesquisa-origem-destino-do-metr%C3%B4-sp_files/figure-html/unnamed-chunk-6-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Outro tipo de análise possível pelo cruzamento de tabelas: Para onde vão as pessoas que partem da Luz?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# para onde as pessoas que moram na luz vão?
tb_zona_de_para &amp;lt;- tbl(con, &amp;quot;dados_geograficos_das_zonas&amp;quot;) %&amp;gt;% 
  select(COD_ZONA, NOME_ZONA)


tbl(con, &amp;quot;viagens_diarias_totais_por_zonas_de_origem_e_destino_2017&amp;quot;) %&amp;gt;% 
  left_join(tb_zona_de_para, by = c(&amp;quot;COD_ZONA_ORIGEM&amp;quot; = &amp;quot;COD_ZONA&amp;quot;)) %&amp;gt;% 
  rename(NOME_ZONA_ORIGEM = NOME_ZONA) %&amp;gt;% 
  left_join(tb_zona_de_para, by = c(&amp;quot;COD_ZONA_DESTINO&amp;quot; = &amp;quot;COD_ZONA&amp;quot;)) %&amp;gt;% 
  rename(NOME_ZONA_DESTINO = NOME_ZONA) %&amp;gt;% 
  filter(NOME_ZONA_ORIGEM == &amp;quot;Luz&amp;quot;) %&amp;gt;% 
  select(NOME_ZONA_DESTINO, QTD) %&amp;gt;% 
  arrange(desc(QTD))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # Source:     lazy query [?? x 2]
## # Database:   sqlite 3.22.0
## #   [/home/sillas/R/Projetos/pesquisa_origem_destino/data/DB_ORIGEM_DESTINO_SP]
## # Ordered by: desc(QTD)
##    NOME_ZONA_DESTINO     QTD
##    &amp;lt;chr&amp;gt;               &amp;lt;dbl&amp;gt;
##  1 Luz                18425.
##  2 Bom Retiro          3702.
##  3 Parque Dom Pedro    2752.
##  4 Santa Ifigênia      2550.
##  5 Santa Cecília       1945.
##  6 Ponte Pequena       1712.
##  7 Saúde               1392.
##  8 Vila Formosa        1368.
##  9 Pari                1304.
## 10 Ermelino Matarazzo  1261.
## # … with more rows&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;e-python&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;E Python?&lt;/h2&gt;
&lt;p&gt;Também é bem fácil importar um arquivo SQLite no Python combinando os módulos &lt;code&gt;sqlite3&lt;/code&gt; e &lt;code&gt;pandas&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;python&#34;&gt;&lt;code&gt;import sqlite3
import pandas as pd


x = &amp;#39;/home/sillas/R/Projetos/pesquisa_origem_destino/data/DB_ORIGEM_DESTINO_SP&amp;#39;
con = sqlite3.connect(x)
# importar tabela como um dataframe no pandas
tb = pd.read_sql_query(&amp;#39;SELECT * FROM dicionario_das_variaveis_da_tabela_dados_gerais&amp;#39;, con)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Qual o melhor serviço de streaming de filmes e séries no Brasil? O R responde.</title>
      <link>http://www.sillasgonzaga.com/post/qual-o-melhor-servico-de-streaming/</link>
      <pubDate>Sat, 23 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/qual-o-melhor-servico-de-streaming/</guid>
      <description>&lt;div id=&#34;introducao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Introdução&lt;/h2&gt;
&lt;p&gt;Recentemente, li um artigo sobre a &lt;a href=&#34;https://meiobit.com/397799/balcanizacao-streaming-aumento-pirataria/&#34;&gt;Balcanização dos serviços de streaming&lt;/a&gt; que me fez refletir sobre essa indústria. Dado que assinar mais de um serviço de streaming pode ser um desperdício, devido a falta de tempo para consumir tanto material, as pessoas costumam optar por apenas um dos existentes: Netflix, Amazon Prime Video ou algum outro. Como saber qual escolher?&lt;/p&gt;
&lt;p&gt;O serviço &lt;a href=&#34;https://www.justwatch.com/br/&#34;&gt;JustWatch&lt;/a&gt; ajuda um pouco. Nele, é possível pesquisar por um filme ou seriado e descobrir em quais serviços ele está disponível. Assim, se temos em mão os dados dos catálogos dos serviços, podemos avaliar a quantidade e qualidade dos filmes em cada um para decidir o melhor.&lt;/p&gt;
&lt;p&gt;Existe um &lt;a href=&#34;https://github.com/dawoudt/JustWatchAPI&#34;&gt;módulo Python&lt;/a&gt; que permite extrair os dados do JustWatch em json, tornando os dados muito fáceis para trabalhar. Por isso, este projeto usará código não só em R como em Python.&lt;/p&gt;
&lt;p&gt;Este projeto consiste em coletar os 5000 melhores filmes e seriados do IMDB, coletar dados de cada usando a API do Justwatch e analisar os resultados para concluir sobre o melhor serviço de streaming no Brasil.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;primeiro-passo-coletando-nomes-de-filmes-e-seriados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Primeiro passo: Coletando nomes de filmes e seriados&lt;/h2&gt;
&lt;p&gt;Infelizmente, a API do Justwatch não nos permite coletar todo o catálogo do site, existindo um limite dos 1000 primeiros resultados, &lt;a href=&#34;https://github.com/dawoudt/JustWatchAPI/issues/18&#34;&gt;mesmo que se altere os parâmetros &lt;code&gt;page&lt;/code&gt; e &lt;code&gt;page_size&lt;/code&gt; da função&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Por outro lado, a função não impõe um limite de requisições feitas sequencialmente. Por isso, a melhor estratégia é iterar a função sobre uma lista de filmes a pesquisar.&lt;/p&gt;
&lt;p&gt;Onde coletar essa lista de filmes? A ideia que eu tive foi usar o IMDB para isso. Eu coletei os 5000 melhores filmes e seriados (sem distinção) para ter os resultados. Como o código é relativamente simples e os dados bem grandes, não vou descrever essa parte do projeto aqui. Os interessados podem consultar como eu fiz para coletar os dados do IMDB &lt;a href=&#34;https://gist.github.com/sillasgonzaga/565d69234f53b3aeac9e22ea7ed692b4&#34;&gt;neste gist&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;usar-o-modulo-python-para-coletar-dados-da-api-do-justwatch&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Usar o módulo Python para coletar dados da API do JustWatch&lt;/h2&gt;
&lt;p&gt;O código é bem simples: eu leio o arquivo com a lista de filmes salvos na parte anterior, itero sobre o dataframe, junto todos os resultados em um dicionário e salvo os dados em formato json.&lt;/p&gt;
&lt;p&gt;O comportamento da função &lt;code&gt;just_watch.search_for_item()&lt;/code&gt; é o mesmo de você pesquisar manualmente no site. Isto é, ao digitar na caixa de busca o título desejado, aparecerá uma lista com os resultados relacionados, sendo que provavelmente apenas o primeiro resultado é o que interessa. Por isso, no código abaixo, eu extraio o primeiro resultado da busca. Funciona na maioria das vezes.&lt;/p&gt;
&lt;pre class=&#34;python&#34;&gt;&lt;code&gt;from justwatch import JustWatch
import csv
import json
import pandas as pd
just_watch = JustWatch(country=&amp;#39;BR&amp;#39;)

# ler lista de filmes
arquivo = &amp;#39;/home/sillas/R/data/imdb/lista_filmes_imdb.csv&amp;#39;
titulos = pd.read_csv(arquivo)

out = list()

for index, row in titulos.iterrows():
    # imprimir status do loop
    if index % 100 == 0:
        print(index)
    # consultar api
    api_data = just_watch.search_for_item(query = row[&amp;quot;full_title&amp;quot;])[&amp;quot;items&amp;quot;]
    
    # salvar dados apenas se houver resultado
    if len(api_data) &amp;gt; 0:
        first_item_data = api_data[0]
        first_item_data[&amp;quot;tconst&amp;quot;] = row[&amp;quot;tconst&amp;quot;]
        out.append(first_item_data)
    
# salvar resultados em json
with open(&amp;#39;/home/sillas/R/data/imdb/justwatchdata.json&amp;#39;, &amp;#39;w&amp;#39;) as fp:
    json.dump(out, fp)
    
# salvar dados de sites de streaming
provider_details = just_watch.get_providers()

with open(&amp;#39;/home/sillas/R/data/imdb/justwatchdata_providers.json&amp;#39;, &amp;#39;w&amp;#39;) as fp:
    json.dump(provider_details, fp)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;juntando-os-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Juntando os dados&lt;/h2&gt;
&lt;p&gt;Finalmente, chegamos à parte divertida. Abaixo eu importo os pacotes usados e o arquivo json salvo acima.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(tidyverse)
library(jsonlite)
library(UpSetR)
library(ggridges)

# gist com o json: 
jw &amp;lt;- jsonlite::fromJSON(&amp;quot;/home/sillas/R/data/imdb/justwatchdata.json&amp;quot;) %&amp;gt;% 
  as_tibble() %&amp;gt;% 
  # selecionar colunas importantes
  select(tconst, original_title, original_release_year,
         tmdb_popularity, object_type, offers, scoring) 

glimpse(jw)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 4,442
## Variables: 7
## $ tconst                &amp;lt;chr&amp;gt; &amp;quot;tt0903747&amp;quot;, &amp;quot;tt0111161&amp;quot;, &amp;quot;tt0068646&amp;quot;, &amp;quot;tt…
## $ original_title        &amp;lt;chr&amp;gt; &amp;quot;Breaking Bad&amp;quot;, &amp;quot;The Shawshank Redemption&amp;quot;…
## $ original_release_year &amp;lt;int&amp;gt; 2008, 1994, 1972, 2013, 1974, 1994, 1993, …
## $ tmdb_popularity       &amp;lt;dbl&amp;gt; 76.613, 30.009, 26.993, 82.621, 19.567, 32…
## $ object_type           &amp;lt;chr&amp;gt; &amp;quot;show&amp;quot;, &amp;quot;movie&amp;quot;, &amp;quot;movie&amp;quot;, &amp;quot;show&amp;quot;, &amp;quot;movie&amp;quot;,…
## $ offers                &amp;lt;list&amp;gt; [&amp;lt;data.frame[5 x 14]&amp;gt;, &amp;lt;data.frame[13 x 1…
## $ scoring               &amp;lt;list&amp;gt; [&amp;lt;data.frame[4 x 2]&amp;gt;, &amp;lt;data.frame[10 x 2]…&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A estrutura deste dataframe não é necessariamente tabular, como se costuma ver. As colunas &lt;code&gt;offers&lt;/code&gt; e &lt;code&gt;scoring&lt;/code&gt; são da classe &lt;code&gt;list&lt;/code&gt;, pois cada valor dessas duas variáveis é um dataframe. Ou seja, no R é possível termos dataframes dentro de um dataframe.&lt;/p&gt;
&lt;p&gt;Para tornar o dataframe mais simples de ser usado, vamos transformar essas duas colunas em dataframes separados.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# criar funcao para extrair apenas as colunas importantes de offers
extract_offers_data &amp;lt;- function(offers){
  if(!is.null(offers)){
    offers %&amp;gt;% 
      select(monetization_type, provider_id,
             date_created, presentation_type)
  }
}

tbl_offers &amp;lt;- jw %&amp;gt;% 
  select(tconst, offers) %&amp;gt;% 
  # remover filmes sem dados de streaming
  filter(!map_lgl(offers, is.null)) %&amp;gt;% 
  # usar funcao criada acima para extrair colunas
  mutate(offers = map(offers, extract_offers_data)) %&amp;gt;% 
  unnest(offers) %&amp;gt;% 
  # filtrar apenas filmes de serviços onde se paga uma taxa mensal para ter
  # acesso aos filmes, ao inves de um preço para cada filme, como se fosse
  # uma loja.
  filter(monetization_type == &amp;quot;flatrate&amp;quot;) %&amp;gt;% 
  distinct(tconst, provider_id)

tbl_scoring &amp;lt;- jw %&amp;gt;% 
  select(tconst, scoring) %&amp;gt;% 
  filter(!map_lgl(scoring, is.null)) %&amp;gt;% 
  unnest(scoring) %&amp;gt;% 
  rename(scoring_provider_type = provider_type)

# no dataframe principal, remover essas duas colunas
tbl_jw &amp;lt;- jw %&amp;gt;% 
  select(-c(offers, scoring))

# importar arquivos com os metadados dos sites de streaming
tbl_providers &amp;lt;- fromJSON(&amp;quot;/home/sillas/R/data/imdb/justwatchdata_providers.json&amp;quot;) %&amp;gt;% 
  as_tibble() %&amp;gt;%
  select(provider_id = id, provider_name = slug) %&amp;gt;% 
  mutate(provider_name = str_replace_all(provider_name, &amp;quot;-&amp;quot;, &amp;quot;_&amp;quot;))

tbl_offers_prov &amp;lt;- left_join(tbl_offers, tbl_providers, by = &amp;quot;provider_id&amp;quot;)

# criar dataset das analises
tbl_jw &amp;lt;- tbl_jw %&amp;gt;% 
  left_join(tbl_offers_prov, by = &amp;quot;tconst&amp;quot;) %&amp;gt;% 
  left_join(tbl_scoring, by = &amp;quot;tconst&amp;quot;) %&amp;gt;% 
  select(-provider_id)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este é o dataset das nossas análises:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# dataset das analises
tbl_jw&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 41,085 x 8
##    tconst original_title original_releas… tmdb_popularity object_type
##    &amp;lt;chr&amp;gt;  &amp;lt;chr&amp;gt;                     &amp;lt;int&amp;gt;           &amp;lt;dbl&amp;gt; &amp;lt;chr&amp;gt;      
##  1 tt090… Breaking Bad               2008            76.6 show       
##  2 tt090… Breaking Bad               2008            76.6 show       
##  3 tt090… Breaking Bad               2008            76.6 show       
##  4 tt090… Breaking Bad               2008            76.6 show       
##  5 tt011… The Shawshank…             1994            30.0 movie      
##  6 tt011… The Shawshank…             1994            30.0 movie      
##  7 tt011… The Shawshank…             1994            30.0 movie      
##  8 tt011… The Shawshank…             1994            30.0 movie      
##  9 tt011… The Shawshank…             1994            30.0 movie      
## 10 tt011… The Shawshank…             1994            30.0 movie      
## # … with 41,075 more rows, and 3 more variables: provider_name &amp;lt;chr&amp;gt;,
## #   scoring_provider_type &amp;lt;chr&amp;gt;, value &amp;lt;dbl&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;resultados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Resultados&lt;/h2&gt;
&lt;div id=&#34;quais-sites-possuem-mais-filmes-e-seriados&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Quais sites possuem mais filmes e seriados?&lt;/h3&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tbl_jw %&amp;gt;% 
  distinct(tconst, object_type, provider_name) %&amp;gt;% 
  count(provider_name, object_type) %&amp;gt;% 
  group_by(object_type) %&amp;gt;% 
  mutate(pct = 100 * n/sum(n)) %&amp;gt;% 
  ggplot(aes(x = fct_rev(provider_name), y = pct)) +
  geom_col() +
  coord_flip() +
  facet_wrap(~ object_type, ncol = 1) +
  labs(x = NULL, y = &amp;quot;%&amp;quot;,
       title = &amp;quot;Presença dos melhores filmes e seriados \nnos sites de streaming no Brasil&amp;quot;) +
  theme_bw()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2019-02-23-qual-o-melhor-servico-de-streaming_files/figure-html/unnamed-chunk-5-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Os resultados são bem interessantes. Para quem adora seriados, a Netflix reina absoluta tendo em seu portfolio 40% dos melhores seriados, enquanto que o segundo melhor neste quesito, o Amazon Prime Video, possui apenas 10%. Em termos de filmes, a Netflix também é a melhor, sendo seguida de perto pelo Telecine Play. O portfolio do Amazon Prime Video para filmes deixa bastante a desejar.&lt;/p&gt;
&lt;p&gt;Ainda sobre filmes, é bastante importante mencionar que mais da metade das produções não está disponível em nenhum serviço de streaming de vídeo por assinatura no Brasil, um resultado bem lamentável para cinéfilos.&lt;/p&gt;
&lt;p&gt;Para o restante deste post, vamos focar apenas nesses 3 principais streamings:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;prvs &amp;lt;- c(&amp;quot;netflix&amp;quot;, &amp;quot;amazon_prime_video&amp;quot;, &amp;quot;telecine_play&amp;quot;)

tbl_jw_principais &amp;lt;- tbl_jw %&amp;gt;% 
  filter(is.na(provider_name) | provider_name %in% prvs) %&amp;gt;% 
  mutate(provider_name = tidyr::replace_na(provider_name, &amp;quot;nenhum&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;quais-sites-possuem-os-filmes-mais-recentes&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Quais sites possuem os filmes mais recentes?&lt;/h3&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;range(tbl_jw_principais$original_release_year)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 1920 2019&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tbl_jw_principais %&amp;gt;% 
  ggplot(aes(x = original_release_year, color = provider_name)) +
  stat_ecdf() +
  theme_bw() +
  scale_x_continuous(breaks = seq(1920, 2020, by  = 10), 
                     minor_breaks = NULL) +
  scale_y_continuous(breaks = seq(0, 1, .1), 
                     minor_breaks = NULL,
                     labels = scales::percent) +
  theme(legend.position = &amp;quot;bottom&amp;quot;) +
  facet_wrap(~ object_type, ncol = 2) +
  labs(title = &amp;quot;Distribuição da idade de títulos por serviço de streaming&amp;quot;,
       x = NULL, 
       y = NULL)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2019-02-23-qual-o-melhor-servico-de-streaming_files/figure-html/unnamed-chunk-7-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;O formato da curva referente à Netflix é muito interessante. Note como ela cresce numa velocidade muito maior a partir de 2010. Olhando para este ponto no eixo vertical, nota-se que 70% dos filmes e incríveis 80% dos seriados foram lançados a partir de 2010. De todos os 3 serviços de streaming, a Netflix é a mais enviesada para produções recentes. Note também como 70% dos filmes que não estão presentes em nenhum serviço por assinatura foram lançados antes de 2010. Na verdade, Netflix e Amazon Prime Video não possuem nenhum filme lançado antes de 1960.&lt;/p&gt;
&lt;p&gt;Outra maneira de visualizar os resultados acima é separando os anos por década:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tbl_jw_principais %&amp;gt;% 
  mutate(decada = as.character(round(original_release_year %/% 10) * 10)) %&amp;gt;% 
  count(decada, object_type, provider_name) %&amp;gt;% 
  ggplot(aes(x = decada, y = n, fill = provider_name)) +
  geom_col(position = position_dodge()) +
  theme_bw() +
  labs(title = &amp;quot;Quantidade de títulos por década&amp;quot;,
       x = NULL, 
       y = NULL) +
  facet_wrap(~ object_type, scales = &amp;quot;free_y&amp;quot;, ncol = 1) +
  theme(legend.position = &amp;quot;bottom&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2019-02-23-qual-o-melhor-servico-de-streaming_files/figure-html/unnamed-chunk-8-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;qualidade-dos-filmes&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Qualidade dos filmes&lt;/h3&gt;
&lt;p&gt;Como parâmetro para qualidade dos filmes, usei a nota do IMDB, que também é fornecida no dataset do JustWatch.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tbl_jw_principais %&amp;gt;% 
  # filtrar filmes com nota IMDB acima de 7 para eliminar entradas erradas
  filter(scoring_provider_type == &amp;quot;imdb:score&amp;quot;, value &amp;gt;= 7) %&amp;gt;% 
  mutate(value = cut(value, breaks = 0:10)) %&amp;gt;% 
  count(object_type, provider_name, value) %&amp;gt;% 
  ggplot(aes(x = value, y = n, fill = provider_name)) +
  geom_col(position = position_fill()) +
  theme_bw() +
  scale_y_continuous(breaks = seq(0, 1, .1),
                     labels = scales::percent) +
  facet_wrap(~ object_type) +
  labs(x = NULL,
       y = NULL,
       title = &amp;quot;Percentual de títulos por faixa de nota no IMDB&amp;quot;) +
  theme(legend.position = &amp;quot;bottom&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2019-02-23-qual-o-melhor-servico-de-streaming_files/figure-html/unnamed-chunk-9-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Não aparenta haver diferença em relação à nota dos filmes e seriados de acordo com o streaming.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;exclusividade-de-conteudo&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Exclusividade de conteúdo&lt;/h3&gt;
&lt;p&gt;Por último, mas não menos importante, existe o critério de exclusividade entre as plataformas, algo comum nesta indústria. Quantos filmes estão presentes apenas, por exemplo no Telecine Play? Quantos são possíveis de encontrar em mais de uma plataforma?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# criar matriz binaria
tbl_jw_principais_wide &amp;lt;- tbl_jw_principais %&amp;gt;% 
  distinct(tconst, provider_name) %&amp;gt;%
  mutate(provider_name = replace_na(provider_name, &amp;quot;nenhum&amp;quot;)) %&amp;gt;% 
  mutate(tem = 1) %&amp;gt;% 
  spread(provider_name, tem) %&amp;gt;% 
  mutate_at(vars(-tconst), replace_na, 0) %&amp;gt;% 
  as.data.frame()

# grafico upset
upset(tbl_jw_principais_wide, nsets = 10,
      mainbar.y.label = NULL)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2019-02-23-qual-o-melhor-servico-de-streaming_files/figure-html/unnamed-chunk-10-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Também neste quesito a Netflix é disparado o melhor dentre os 3. 979 dos títulos são exclusivos da Netflix, muito mais que a soma dos outros dois serviços. É curioso que exista pouquíssima interseção entre as plataformas: apenas 17 títulos estão presentes tanto na Netflix como no Amazon Prime Video.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;bonus-diagrama-de-venn&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Bônus: Diagrama de Venn&lt;/h3&gt;
&lt;p&gt;Outra forma de visualizar os resultados acima é por meio de um diagrama de Venn:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;lst &amp;lt;- tbl_jw_principais %&amp;gt;% 
  distinct(tconst, provider_name) %&amp;gt;% 
  split(.$provider_name) %&amp;gt;% 
  map(&amp;quot;tconst&amp;quot;)

gplots::venn(lst)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2019-02-23-qual-o-melhor-servico-de-streaming_files/figure-html/unnamed-chunk-11-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Conclusão&lt;/h2&gt;
&lt;p&gt;Usando o universo dos 5000 melhores títulos de filmes e seriados de acordo com o IMDB, a Netflix é bem superior às concorrentes no Brasil.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>A equação mais perigosa do mundo e o efeito do tamanho da amostra nos resultados</title>
      <link>http://www.sillasgonzaga.com/post/a-equacao-mais-perigosa-do-mundo/</link>
      <pubDate>Sun, 01 Jul 2018 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/a-equacao-mais-perigosa-do-mundo/</guid>
      <description>&lt;div id=&#34;introducao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Introdução&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://old.reddit.com/r/datascience/comments/8r16mi/whats_the_dumbest_thing_a_manager_or_executive/e0np7vm/&#34;&gt;Nesta thread&lt;/a&gt; no subreddit de Data Science, um usuário fez o seguinte comentário:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So basically, I was asked to make inference on 10 people and expect those to generalize to the entire study population. I said the study was poorly designed, and that if I made up random numbers we would do a better job of understanding the customer base.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;É muito comum pessoas que não são muito familiares com conceitos de inferência estatística ignorar o fato de que tomar conclusões a partir de amostras muito pequenas pode ser bastante perigoso.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.504.3301&amp;amp;rep=rep1&amp;amp;type=pdf&#34;&gt;Neste artigo escrito por Howard Wainer&lt;/a&gt; (Aviso: Link para PDF), são citados dois motivos para que uma equação possa ser perigosa: o fato de elas serem conhecidas e o fato de elas serem desconhecidas. A equação de Moivre, que descreve o desvio padrão da distribuição amostral da média, se encontra no segundo caso:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math inline&#34;&gt;\(\sigma_{\bar{x}}= \frac{\sigma}{\sqrt{n}}\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Onde &lt;span class=&#34;math inline&#34;&gt;\(\sigma_{\bar{x}}\)&lt;/span&gt; é o erro padrão da média, &lt;span class=&#34;math inline&#34;&gt;\(\sigma\)&lt;/span&gt; o desvio padrão da amostra e &lt;span class=&#34;math inline&#34;&gt;\(n\)&lt;/span&gt; o tamanho da amostra. Em outras palavras, dado que &lt;span class=&#34;math inline&#34;&gt;\(\sigma\)&lt;/span&gt; é um parâmetro conhecido, quanto menor o tamanho da amost, maior será erro da estimativa, que corresponde a variação da média amostral em relação à média da população. O paper inteiro é muito bom e fácil de ler, então recomendo a leitura.&lt;/p&gt;
&lt;p&gt;O objetivo deste post é um exercício simples para mostrar o efeito do tamanho da amostra na variabilidade dos resultados, mesmo quando se sabem os valores verdadeiros dos parâmetros de toda a população.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;geracao-dos-dados-por-simulacao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Geração dos dados por simulação&lt;/h2&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(tidyverse)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O primeiro passo é gerar uma população de dados cujos parâmetros são conhecidos. Aqui, duas variáveis aleatórias ´x´ e ´y´ são definidas, em que x é uma variável aleatória e ´y´ deriva de x, com algum ruído:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;n &amp;lt;- 1e5
set.seed(123)
x &amp;lt;- rnorm(n, mean = 0, sd = 0.5)
y &amp;lt;- 2 * x + 5 + rnorm(n, mean = 0, sd = 0.25)

df &amp;lt;- data.frame(x = x, y = y)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mod_populacao &amp;lt;- lm(y ~ x, data = df)
summary(mod_populacao)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## 
## Call:
## lm(formula = y ~ x, data = df)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.0972 -0.1685 -0.0002  0.1683  1.0506 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(&amp;gt;|t|)    
## (Intercept) 5.0013045  0.0007937    6302   &amp;lt;2e-16 ***
## x           1.9986648  0.0015877    1259   &amp;lt;2e-16 ***
## ---
## Signif. codes:  0 &amp;#39;***&amp;#39; 0.001 &amp;#39;**&amp;#39; 0.01 &amp;#39;*&amp;#39; 0.05 &amp;#39;.&amp;#39; 0.1 &amp;#39; &amp;#39; 1
## 
## Residual standard error: 0.251 on 99998 degrees of freedom
## Multiple R-squared:  0.9406, Adjusted R-squared:  0.9406 
## F-statistic: 1.585e+06 on 1 and 99998 DF,  p-value: &amp;lt; 2.2e-16&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como esperado, a partir do modelo de regressão obtido acima a partir da população de parâmetros são conhecidos que o processo de geração da variável resposta ´y´ pode ser representado pela equação abaixo:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math inline&#34;&gt;\(y = 2x + 5 + \epsilon\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;O histograma dos resíduos do modelo é mais uma evidência de que o modelo obtido possui boas propriedades:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;hist(resid(mod_populacao),
     main = &amp;quot;Distribuição dos resíduos do modelo da população&amp;quot;,
     xlab = NULL)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-07-01-a-equacao-mais-perigosa-do-mundo_files/figure-html/unnamed-chunk-4-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Assim, se tirarmos uma amostra da população e criarmos um modelo &lt;span class=&#34;math inline&#34;&gt;\(y = f(x)\)&lt;/span&gt;, os parâmetros do modelo dessa amostra serão os mesmos ou próximos ao da população, correto? Depende de seu tamanho.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;geracao-das-amostras&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Geração das amostras&lt;/h2&gt;
&lt;p&gt;A função abaixo cria 1000 amostras de tamanho &lt;strong&gt;&lt;code&gt;sample_size&lt;/code&gt;&lt;/strong&gt; do dataframe &lt;strong&gt;&lt;code&gt;df&lt;/code&gt;&lt;/strong&gt; dos dados da população e, para cada uma das amostras, ajusta um modelo de regressão linear, extrai o coeficiente de &lt;code&gt;x&lt;/code&gt; da regressão e retorna um dataframe com os resultados:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;coef_x_sample &amp;lt;- function(sample_size){
  repl &amp;lt;- purrr::rerun(1000, sample_n(df, sample_size))
  
  vec_coef_x &amp;lt;- repl %&amp;gt;% 
    map(~ lm(y ~ x, data = .)) %&amp;gt;% 
    map_dbl(~ coef(.)[&amp;quot;x&amp;quot;])
  
  data.frame(size = sample_size, coef_x = vec_coef_x)
}

# exemplo
set.seed(123)
head(coef_x_sample(10))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##   size   coef_x
## 1   10 2.065196
## 2   10 2.320681
## 3   10 2.142100
## 4   10 2.069578
## 5   10 2.036510
## 6   10 1.704862&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nas 6 primeiras amostras de tamanho 10, o coeficiente de x foi desde 1,70 a 2,32, mostrando uma certa variabilidade.&lt;/p&gt;
&lt;p&gt;O código abaixo repete o processo acima para valores diferentes do tamanho da amostra:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;vec_samp_size &amp;lt;- c(5, 10, 15, 20, 50, 75, 100, 150, 200, 250, 500, 750, 1000, 5000)

df_result &amp;lt;- vec_samp_size  %&amp;gt;% map_dfr(coef_x_sample)

df_result &amp;lt;- df_result %&amp;gt;% 
  mutate(size = factor(size, levels = vec_samp_size))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;analise-dos-resultados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Análise dos resultados&lt;/h2&gt;
&lt;p&gt;Vários gráficos podem ser feitos para ilustrar os resultados da variabilidade do parâmetro da regressão em funcão do tamanho da amostra, como o boxplot abaixo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_result %&amp;gt;% 
  ggplot(aes(x = size, y = coef_x)) + 
  geom_boxplot() + 
  scale_y_continuous(breaks = seq(-4, 4, by = 1)) + 
  labs(x = NULL, y = NULL,
       title = &amp;quot;Variabilidade do coeficiente de x em função do tamanho da amostra&amp;quot;) + 
  theme_minimal()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-07-01-a-equacao-mais-perigosa-do-mundo_files/figure-html/unnamed-chunk-7-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;O gráfico acima mostra que, apesar de a mediana ser aproximadamente 2 em todos os tamanhos amostrais testados, a variabilidade é muito maior nas menores amostras. A medida que se aumenta o número de indivíduos na amostra, a distribuição dos resultados converge para o valor verdadeiro. Para amostras de 5 indivíduos, foram observados resultados no seguinte intervalo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# intervalo para menor tamanho amostral
range(df_result$coef_x[df_result$size == &amp;quot;5&amp;quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.3693973 3.6749739&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# intervalo para maior tamanho amostral
range(df_result$coef_x[df_result$size == &amp;quot;5000&amp;quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 1.973962 2.022880&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou seja, mesmo que uma amostra seja oriunda de uma população descrita por parâmetros já determinados, usar resultados de uma amostra pequena pode levar a conclusões erradas.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Análise e simulação de investimentos com o pacote calcCidadao</title>
      <link>http://www.sillasgonzaga.com/post/analise-e-simulacao-de-investimentos-com-o-pacote-calccidadao/</link>
      <pubDate>Sat, 09 Jun 2018 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/analise-e-simulacao-de-investimentos-com-o-pacote-calccidadao/</guid>
      <description>&lt;div id=&#34;calculadora-do-cidadao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Calculadora do Cidadão&lt;/h2&gt;
&lt;p&gt;Devido a um fenômeno econômico chamado de Inflação, o valor do dinheiro muda com o tempo. R\$100,00 hoje não possuem o mesmo valor monetário ou poder de compra que R\$100,00 daqui a 10 anos, ou mesmo 10 anos atrás. Uma técnica que pode ser usada para corrigir esse efeito é a de deflacionar esses valores, como mostrado neste post do blog &lt;a href=&#34;https://analisemacro.com.br/economia/macroeconometria/obtendo-valores-deflacionados-com-o-r/&#34;&gt;Análise Macro&lt;/a&gt;. Um outro serviço que pode ser usado é a &lt;a href=&#34;https://www3.bcb.gov.br/CALCIDADAO/jsp/index.jsp&#34;&gt;&lt;strong&gt;Calculadora do Cidadão&lt;/strong&gt;&lt;/a&gt;, um produto desenvolvido pelo Banco Central do Brasil para facilitar o trabalho das pessoas que desejam corrigir valores pela inflação de maneira muito simples: Basta adicionar os dados &lt;a href=&#34;https://www3.bcb.gov.br/CALCIDADAO/publico/exibirFormCorrecaoValores.do?method=exibirFormCorrecaoValores&#34;&gt;nesta página&lt;/a&gt; de data inicial, data final e valor a ser corrigido. A ferramenta então informará o índice de correção no período, o valor percentual correspondente e o valor corrigido na data final. Assim, descobrimos que, por exemplo, R\$1,00 em Janeiro de 2008 equivale a R\$1,81 em Janeiro de 2018, de acordo com o IPCA-E, índice desenvolvido pelo IBGE.&lt;/p&gt;
&lt;p&gt;Além da correção de valores pela inflação, a Calculadora do Cidadão fornece outros serviços de atualização de valores monetários, como:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TR&lt;/strong&gt;: Taxa Referencial. Taxa obtida a partir das médias dos CDBs de 30 dias a taxas pré-fixadas praticadas por bancos comerciais.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Poupança&lt;/strong&gt;: Os rendimentos da poupança são creditados mensalmente, na data equivalente à data de aplicação (data-base). Dessa forma, se uma aplicação na poupança for resgatada antes de chegar à sua primeira data-base, não fará jus a qualquer correção, e o valor final será igual ao inicial.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selic&lt;/strong&gt;: Utiliza para correção a taxa apurada no Selic. Esta taxa é obtida mediante o cálculo da taxa média ponderada e ajustada das operações de financiamento por um dia, lastreadas em títulos públicos federais e cursadas no referido sistema ou em câmaras de compensação e liquidação de ativos, na forma de operações compromissadas.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDI&lt;/strong&gt;: Depósito Interfinanceiro. É uma operação realizada exclusivamente entre instituições financeiras, para permitir a troca de reservas bancárias entre elas. Tem registro no CETIP - Câmara de Custódia e Liquidação. Foi autorizada pelo Decreto-Lei nº 2.284/86 e regulamentada pela Resolução nº 1.647/89. A taxa do CDI é calculada como a média das taxas das operações registradas, ponderadas pelos volumes negociados, em determinado dia útil.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Uma das utilizações dessas ferramentas é poder saber, por exemplo, quanto uma aplicação de R\$1000,00 teria rendido nessas diferentes modalidades de renda fixa em um determinado período no passado.&lt;/p&gt;
&lt;p&gt;Pensando em todas essas utilidades da Calculadora do Cidadão, eu criei há um tempo o pacote &lt;a href=&#34;https://github.com/sillasgonzaga/calcCidadao&#34;&gt;&lt;code&gt;calcCidadao&lt;/code&gt;&lt;/a&gt;, cujo objetivo é automatizar as consultas de tais informações na plataforma do BCB. Um ponto negativo do pacote é que, como o Banco Central não fornece uma API para a Calculadora do Cidadão, as funções do &lt;code&gt;calcCidadao&lt;/code&gt; funcionam por meio de Web Scraping, o que pode tornar o código lento para muitas requisições (não é a toa que eu paralelizei as iterações neste post). Neste post, darei uma sugestão de como o pacote pode ser usado na análise de investimentos.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;funcoes-basicas-do-pacote&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Funções básicas do pacote&lt;/h2&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(tidyverse)
library(calcCidadao)
library(lubridate)
library(quantmod)
library(furrr)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O pacote possui uma função para cada tipo de correção fornecida pela Calculadora do Cidadão:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ls(&amp;quot;package:calcCidadao&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;inflation_indices&amp;quot; &amp;quot;return_cdi&amp;quot;        &amp;quot;return_inflation&amp;quot; 
## [4] &amp;quot;return_poupanca&amp;quot;   &amp;quot;return_selic&amp;quot;      &amp;quot;return_tr&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;inflation_indices&lt;/code&gt; é uma função que retorna os nomes e códigos dos índices de inflação que podem ser usados como input na função &lt;code&gt;return_inflation&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;print(inflation_indices())&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##             IGP-M            IGP-DI              INPC             IPC-A 
##      &amp;quot;00189IGP-M&amp;quot;     &amp;quot;00190IGP-DI&amp;quot;       &amp;quot;00188INPC&amp;quot;      &amp;quot;00433IPC-A&amp;quot; 
##            IPCA-E        IPC-BRASIL            IPC-SP 
##      &amp;quot;10764IPC-E&amp;quot; &amp;quot;00191IPC-BRASIL&amp;quot;     &amp;quot;00193IPC-SP&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para corrigir um valor de 10 anos atrás de acordo com o IPCA-E, fazemos assim:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;d1 &amp;lt;- as.Date(&amp;quot;2008-01-01&amp;quot;)
d2 &amp;lt;- as.Date(&amp;quot;2018-01-01&amp;quot;)

return_inflation(&amp;quot;IPCA-E&amp;quot;, start_date = d1, end_date = d2)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 1.811145&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O fator de correção do IPCA-E no período é de 1,811145. Assim, R\$1000 em 01/01/2008 valiam o equivalente a R\$1811,45 10 anos depois.&lt;/p&gt;
&lt;p&gt;O mesmo pode ser feito com os índices de renda fixa. Quanto o CDI e a Selic renderam a mais que a Poupança nesse mesmo período?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;renda_fixa_10_anos &amp;lt;- c(
  return_cdi(d1, d2),
  return_selic(d1, d2),
  return_poupanca(d1, d2)
  )

renda_fixa_10_anos&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 2.785752 2.801270 2.009103&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Os resultados acima mostram algo que já é sabido: A poupança é um investimento muito pior que investimentos atrelados a CDI e Selic (a não ser que a taxa seja muito ruim).&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;estudo-de-caso-para-o-pacote-analise-de-um-titulo-pre-fixado&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Estudo de caso para o pacote: Análise de um Título Pré-Fixado&lt;/h2&gt;
&lt;p&gt;Em um fórum de investimentos que participo, havia uma discussão sobre como avaliar a “qualidade” de um título CDB pré-fixado de um prazo relativamente longo, como 6 anos. Mesmo que a taxa seja atrativa (um rendimento de 14% ao ano pode fazer o capital investido dobrar no período), 6 anos é um prazo muito longo em um país onde até o passado é imprevisível. É natural surgir perguntas como &lt;em&gt;E se a inflação acumulada no período for maior?&lt;/em&gt; ou &lt;em&gt;E se um título pós-fixado a 100% do CDI render mais, me fazendo ter um custo de oportunidade?&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;De fato, a não ser que você trabalhe na Empiricus, não dá para saber como estará a Selic e a inflação ao longo dos próximos 6 anos. É simplesmente impossível prever variáveis tão complexas em um período tão distante no período no futuro. Contudo, é possível ter alguma noção desses valores a partir de simulação de rendimentos passados do mesmo intervalo de tempo.&lt;/p&gt;
&lt;p&gt;Vamos então tomar como exemplo um produto que eu recebi por e-mail da Easynvest: um CDB prefixado de 15,07% ao ano do Banco Pine com prazo de 6 anos. Para calcular seu retorno líquido ao final do período, basta usar uma fórmula de juros compostos:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;juros_compostos &amp;lt;- function(i, t, imposto){
  bruto &amp;lt;- (1 + i)^t - 1
  liquido &amp;lt;- bruto * (1 - imposto)
  liquido
  }
# taxa de 15.07%aa para 6 anos, considerando 15% de IR
retorno_cdb &amp;lt;- juros_compostos(0.1507, 6, 0.15)
retorno_cdb&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 1.123293&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# montante liquido no resgate para um capital de 1000 reais
valor_liquido_cdb &amp;lt;- round(1000 * (1 + retorno_cdb), 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como vimos, o CDB mais que dobra o dinheiro investido no período. Mas será que isso é suficiente para bater a inflação e o CDI? Isso pode ser respondido por meio de simulação.&lt;/p&gt;
&lt;p&gt;O método da simulação consiste em obter o retorno desses indicadores para cerca de 200 pares de datas com 6 anos de diferença entre elas.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# data para gerar vetor de datas para tras
data_base &amp;lt;- floor_date(today() - 31, &amp;quot;month&amp;quot;)

qtd_sim &amp;lt;- 200

# gerar vetor de datas começando  no primeiro dia de cada mes
d1 &amp;lt;- d2 &amp;lt;- sort(unique(floor_date(data_base - ddays(seq_len(qtd_sim) * 30), &amp;quot;month&amp;quot;)))
# descontar para a data inicial 6 anos
year(d1) &amp;lt;- year(d1) - 6 
# exemplo de como ficou
head(tibble(d1, d2))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 6 x 2
##   d1         d2        
##   &amp;lt;date&amp;gt;     &amp;lt;date&amp;gt;    
## 1 1995-11-01 2001-11-01
## 2 1995-12-01 2001-12-01
## 3 1996-01-01 2002-01-01
## 4 1996-02-01 2002-02-01
## 5 1996-03-01 2002-03-01
## 6 1996-04-01 2002-04-01&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Temos então dois vetores de datas &lt;code&gt;d1&lt;/code&gt; e &lt;code&gt;d2&lt;/code&gt; em que &lt;span class=&#34;math inline&#34;&gt;\(d{_2}_t \simeq (365 * 5) + d{_1}_t\)&lt;/span&gt;. Esses serão usados como inputs das funções do &lt;code&gt;calcCidadao&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Como são muitas requisições, o processo pode ser bem lento. Por isso, eu uso o pacote &lt;code&gt;furrr&lt;/code&gt; para paralelizar as iterações:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plan(multiprocess)
inflacao_6 &amp;lt;-  furrr::future_map2_dbl(
  d1, d2, ~return_inflation(&amp;quot;IPCA-E&amp;quot;, start_date = .x, end_date = .y),
  .progress = FALSE)

cdi_6 = furrr::future_map2_dbl(
  d1, d2, ~return_cdi(start_date = .x, end_date = .y),
  .progress = FALSE)

# salvar resultados em um tibble
df_results &amp;lt;- tibble(
  data_aplicacao = d1,
  data_resgate = d2,
  # no caso dos valores extraidos pelas funcoes do calcCidadao,
  # nao eh necessario somar 1, visto que as funcoes ja retornam
  # o indice de correcao
  valor_corrigido_ipca = round(1000 * inflacao_6, 2),
  valor_corrigido_cdi =  round(1000 * cdi_6, 2)
)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;head(df_results)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 6 x 4
##   data_aplicacao data_resgate valor_corrigido_ipca valor_corrigido_cdi
##   &amp;lt;date&amp;gt;         &amp;lt;date&amp;gt;                      &amp;lt;dbl&amp;gt;               &amp;lt;dbl&amp;gt;
## 1 1995-11-01     2001-11-01                  1498.               3603.
## 2 1995-12-01     2001-12-01                  1484.               3552.
## 3 1996-01-01     2002-01-01                  1473.               3506.
## 4 1996-02-01     2002-02-01                  1456.               3471.
## 5 1996-03-01     2002-03-01                  1445.               3435.
## 6 1996-04-01     2002-04-01                  1447.               3407.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Já que estamos falando de comparação de investimentos, por que não incluir também a renda variável no estudo? O código abaixo extrai preços de fechamento ajustados do IBOVESPA, agrega os dados pelo primeiro dia de operação de cada mês, e calcula o “valor corrigido” usando o preço de fechamento ajustado após 72 meses.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ibov &amp;lt;- quantmod::getSymbols(&amp;quot;^BVSP&amp;quot;, from = min(d1), auto.assign = FALSE)
head(Ad(ibov))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##            BVSP.Adjusted
## 1995-11-01        4099.8
## 1995-11-02            NA
## 1995-11-03        4158.8
## 1995-11-06        4094.3
## 1995-11-07        4111.2
## 1995-11-08        4117.1&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_ibov &amp;lt;- tibble(date = index(ibov), price = as.numeric(Ad(ibov))) %&amp;gt;% 
  na.omit() %&amp;gt;% 
  group_by(mes = floor_date(date, &amp;quot;month&amp;quot;)) %&amp;gt;% 
  filter(date == min(date)) %&amp;gt;% 
  ungroup() %&amp;gt;% 
  mutate(fator_ibov = dplyr::lead(price, 72)/price,
         valor_corrigido_ibov = round(1000 * fator_ibov, 2)) %&amp;gt;% 
  select(data_aplicacao = mes, valor_corrigido_ibov)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# juntar em um dataframe so
df_completo &amp;lt;- left_join(df_results, df_ibov, by = &amp;quot;data_aplicacao&amp;quot;)
names(df_completo)[3:5] &amp;lt;- str_remove_all(names(df_completo)[3:5], &amp;quot;valor_corrigido_&amp;quot;)

df_completo[1,]&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 1 x 5
##   data_aplicacao data_resgate  ipca   cdi  ibov
##   &amp;lt;date&amp;gt;         &amp;lt;date&amp;gt;       &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;
## 1 1995-11-01     2001-11-01   1498. 3603. 2778.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No exemplo acima, apenas a inflação não foi superior ao nosso CDB prefixado de exemplo: Tanto o CDI como o IBOV eram opções melhores (partindo do pressuposto que em 01/11/1995 você era capaz de prever tais índices 6 anos depois).&lt;/p&gt;
&lt;p&gt;O gráfico abaixo ilustra a distribuição dos resultados de cada índice comparado com o CDB de interesse:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_completo %&amp;gt;% 
  gather(indice, novo_valor, ipca:ibov) %&amp;gt;% 
  ggplot(aes(x = novo_valor)) + 
  geom_histogram(binwidth = 100) + 
  geom_vline(xintercept = valor_liquido_cdb, color =  &amp;quot;red&amp;quot;, linetype = &amp;quot;dashed&amp;quot;) + 
  facet_wrap(~ indice, scales = &amp;quot;fixed&amp;quot;, ncol = 1) + 
  theme_minimal() + 
  labs(x = &amp;quot;Valor corrigido líquido (R$)&amp;quot;, y = NULL,
       title = &amp;quot;CDI, IBOV e Inflação vs. CDB Prefixado de 15,07% a.a.&amp;quot;,
       subtitle = &amp;quot;Prazo de 6 anos&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-06-09-analise-e-simulacao-de-investimentos-com-o-pacote-calccidadao_files/figure-html/unnamed-chunk-11-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;O gráfico acima mostra que esse CDB prefixado pode não ser um investimento tão bom assim. Muitas vezes o CDI e o IBOV trouxeram resultados melhores para o mesmo intervalo de 6 anos. Numericamente, os resultados são:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_completo %&amp;gt;% 
  summarise_at(vars(ipca:ibov), ~ mean(.x &amp;gt;= valor_liquido_cdb, na.rm = TRUE))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 1 x 3
##    ipca   cdi  ibov
##   &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;
## 1    0. 0.533 0.487&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou seja: o CDI e o IBOV foram melhores que o CDB em 53,3% e 48,7% das vezes, respectivamente. Levando em conta que o Ibovespa se trata de renda variável e apresenta um risco maior (levando em conta que o CDB possui proteção do FGC), pode-se dizer que o resultado do CDB é aceitável em comparação com a bolsa de valores.&lt;/p&gt;
&lt;p&gt;Será que a distribuição desses resultados é uniforme com o tempo? Certamente não, visto que tanto o Ibovespa quanto a taxa Selic (que apresenta resultados muito parecidos com o CDI) variaram muito desde 1995. Por isso a utilidade do gráfico abaixo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_completo %&amp;gt;% 
  gather(indice, novo_valor, ipca:ibov) %&amp;gt;% 
  ggplot(aes(x = data_aplicacao, y = novo_valor)) + 
  geom_line(aes(color = indice)) + 
  geom_hline(yintercept = valor_liquido_cdb, linetype = &amp;quot;dashed&amp;quot;) +
  geom_hline(yintercept = 1000, linetype = &amp;quot;dotted&amp;quot;) +
  theme_minimal()  +  
  scale_x_date(date_breaks = &amp;quot;1 year&amp;quot;, date_labels = &amp;quot;%Y&amp;quot;) +
  scale_y_continuous(breaks = scales::pretty_breaks(n = 6)) +
  annotate(&amp;quot;rect&amp;quot;, fill = &amp;quot;red&amp;quot;, alpha = 0.15,
           xmin = as.Date(-Inf), xmax = as.Date(Inf),
           ymin = -Inf, ymax = 1000) +
  labs(x = NULL, y = &amp;quot;R$&amp;quot;,
       title = &amp;quot;Valor de R$ 1000 corrigido por diferentes índices&amp;quot;,
       subtitle = &amp;quot;Resultados em função da data de aplicação apos um período de 6 anos&amp;quot;) + 
  theme(panel.grid.minor.x = element_blank())&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-06-09-analise-e-simulacao-de-investimentos-com-o-pacote-calccidadao_files/figure-html/unnamed-chunk-13-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;O que o gráfico acima mostra é que observar os resultados levando em conta o fator tempo muda completamente a interpretação: a partir do fim de 2005, nenhum investimento tem proporcionado um rendimento líquido superior ao CDB préfixado de 15,07% ao ano. Outra observação interessante a partir do gráfico é comprovar que a bolsa de valores é muito mais volátil que a renda fixa, chegando em alguns momentos a ficar abaixo da inflação ou até a dar prejuízo ao investidor.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Conclusão&lt;/h2&gt;
&lt;p&gt;Espero que este post tenha sido útil não só para mostrar o potencial de aplicação do pacote &lt;code&gt;calcCidadao&lt;/code&gt; mas também para propor uma nova abordagem na análise de investimentos. De fato, retornos passados não indicam retornos futuros, ainda mais se tratando de indicadores financeiros. Contudo, uma simples análise probabilística pode ajudar a dirimir algumas incertezas na consideração de investimentos.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Mapeando a abertura de escolas municipais em São Paulo ao longo dos anos com um GIF</title>
      <link>http://www.sillasgonzaga.com/post/mapeando-a-abertura-de-escolas-municipais-em-sao-paulo-ao-longo-dos-anos/</link>
      <pubDate>Mon, 14 May 2018 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/mapeando-a-abertura-de-escolas-municipais-em-sao-paulo-ao-longo-dos-anos/</guid>
      <description>&lt;p&gt;Pessoas adoram mapas. Sempre que puder fazer mapas para representar visualmente uma determinada informação, faça!&lt;/p&gt;
&lt;p&gt;Suponha que você deseja fazer uma visualização da taxa de homicídio por estados brasileiros. Nada te impede de fazer um gráfico de barras, onde cada UF seria representado por uma barra cujo tamanho seria dependente do valor da taxa, mas teria um impacto visual menor em que cada estado estaria colorido de acordo com essa variável.&lt;/p&gt;
&lt;p&gt;Contudo, em algumas situações, um mapa é a única maneira possível de transmitir com clareza uma ideia. Esta é a ideia deste post: mostrar como um mapa pode ser útil para mostrar a evolução das abertudas de escolas municipais na cidade de São Paulo.&lt;/p&gt;
&lt;div id=&#34;coleta-e-limpeza-dos-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Coleta e limpeza dos dados&lt;/h2&gt;
&lt;p&gt;Eu já tive a oportunidade de participar de uma palestra do pessoal da Secretaria Municipal de Educação de São Paulo, onde conheci suas iniciativas de &lt;a href=&#34;http://dados.prefeitura.sp.gov.br/group/educacao&#34;&gt;dados abertos&lt;/a&gt;. Esses projetos são benéficos não só para a população como um todo, por toda a questão da transparência, mas especialmente para quem deseja desenvolver projetos para praticar análise de dados, ganhando assim experiência real para lidar com tarefas de limpeza, manuseio e visualização de dados.&lt;/p&gt;
&lt;p&gt;O dataset de interesse deste post é o &lt;a href=&#34;http://dados.prefeitura.sp.gov.br/dataset/cadastro-de-escolas-municipais-conveniadas-e-privadas&#34;&gt;Cadastro de escolas municipais, conveniadas e privadas&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# pacotes
library(tidyverse)
library(ggmap)
library(gganimate)
library(lubridate)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# definir locale para lidar com caracteres especiais
lcl &amp;lt;- locale(encoding = &amp;quot;ISO-8859-1&amp;quot;)
df &amp;lt;- read_csv2(&amp;quot;/home/sillas/R/Projetos/paixaopordados-blogdown/data/escolasr34dez2017.csv&amp;quot;,
                locale = lcl)

# dimensoes do dataset
dim(df)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 6878   53&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O dataset possui 53 colunas, mas só precisamos de realmente 3: as colunas de coordenadas geográficas e a de data de fundação das escolas.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df &amp;lt;- df %&amp;gt;% 
  select(DATA = DT_CRIACAO, LAT = LATITUDE, LON = LONGITUDE)

knitr::kable(head(df))&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;DATA&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;LAT&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;LON&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;13-jun-88&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-23553905&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-46398452&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;04-jul-88&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-23489728&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-46670198&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;05-jul-88&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-23478312&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-46427344&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;27-mai-88&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-23612237&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-46749888&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;22-jun-88&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-23486142&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-46733901&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;07-jun-88&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-23611929&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-46750176&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;O output acima revela a necessidade de alguns ajustes de limpeza: converter a coluna &lt;code&gt;DATA&lt;/code&gt; para a classe &lt;code&gt;Date&lt;/code&gt; e dividir as colunas de latitude e longitude por um milhão para obter os valores corretos.&lt;/p&gt;
&lt;p&gt;A transformação da coluna &lt;code&gt;DATA&lt;/code&gt; poderia ser feita por funções automáticas, como a &lt;code&gt;strptime&lt;/code&gt;, mas isso dependeria de algumas configurações internas do seu sistema operacional. Por isso, eu uso uma solução mais manual:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;converter_mes &amp;lt;- function(x){
  nomes &amp;lt;- c(&amp;quot;jan&amp;quot;, &amp;quot;fev&amp;quot;, &amp;quot;mar&amp;quot;, &amp;quot;abr&amp;quot;, &amp;quot;mai&amp;quot;, &amp;quot;jun&amp;quot;,
             &amp;quot;jul&amp;quot;, &amp;quot;ago&amp;quot;, &amp;quot;set&amp;quot;, &amp;quot;out&amp;quot;, &amp;quot;nov&amp;quot;, &amp;quot;dez&amp;quot;)
  
  numeros &amp;lt;- str_pad(1:12, width = 2, pad = &amp;quot;0&amp;quot;)
  
  x &amp;lt;- str_replace_all(x, nomes[1], numeros[1])
  x &amp;lt;- str_replace_all(x, nomes[2], numeros[2])
  x &amp;lt;- str_replace_all(x, nomes[3], numeros[3])
  x &amp;lt;- str_replace_all(x, nomes[4], numeros[4])
  x &amp;lt;- str_replace_all(x, nomes[5], numeros[5])
  x &amp;lt;- str_replace_all(x, nomes[6], numeros[6])
  x &amp;lt;- str_replace_all(x, nomes[7], numeros[7])
  x &amp;lt;- str_replace_all(x, nomes[8], numeros[8])
  x &amp;lt;- str_replace_all(x, nomes[9], numeros[9])
  x &amp;lt;- str_replace_all(x, nomes[10], numeros[10])
  x &amp;lt;- str_replace_all(x, nomes[11], numeros[11])
  x &amp;lt;- str_replace_all(x, nomes[12], numeros[12])
  
  x
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Escrita a função, passo para a transformação das colunas:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df &amp;lt;- df %&amp;gt;% 
  mutate(DATA_CLEAN = dmy(converter_mes(DATA)),
         LAT = LAT/1e6,
         LON = LON/1e6
         ) %&amp;gt;% 
  mutate(ANO = year(DATA_CLEAN)) %&amp;gt;% 
  # remover linhas onde LAT ou LON é NA
  na.omit()

summary(df)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##      DATA                LAT              LON           DATA_CLEAN        
##  Length:4576        Min.   :-23.89   Min.   :-47.05   Min.   :1969-01-30  
##  Class :character   1st Qu.:-23.63   1st Qu.:-46.71   1st Qu.:2004-10-24  
##  Mode  :character   Median :-23.57   Median :-46.63   Median :2011-10-23  
##                     Mean   :-23.57   Mean   :-46.60   Mean   :2008-10-17  
##                     3rd Qu.:-23.51   3rd Qu.:-46.48   3rd Qu.:2015-05-08  
##                     Max.   :-22.89   Max.   :-46.37   Max.   :2068-12-12  
##       ANO      
##  Min.   :1969  
##  1st Qu.:2004  
##  Median :2011  
##  Mean   :2008  
##  3rd Qu.:2015  
##  Max.   :2068&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Surgiu um novo erro: a coluna &lt;code&gt;ANO&lt;/code&gt; possui valores acima do ano atual (2018). Isso provavelmente foi causado na conversão de datas como &lt;code&gt;30/08/68&lt;/code&gt;, que o R retornou 2068 ao invés de 1968. Sinceramente, não parei para investigar o motivo disso, até porque é facilmente consertado:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df &amp;lt;- df %&amp;gt;% 
  mutate(ANO = if_else(ANO &amp;gt; year(today()), ANO - 100, ANO))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;apresentacao-dos-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Apresentação dos dados&lt;/h2&gt;
&lt;p&gt;Primeiramente, qual a distribuição da abertura de novas escolas por ano?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df %&amp;gt;% 
  count(ANO) %&amp;gt;% 
  ggplot(aes(x = ANO, y = n)) + 
  geom_col(fill = &amp;quot;darkorange1&amp;quot;) + 
  theme_minimal() +
  labs(x = NULL, y = NULL,
       title = &amp;quot;Quantidade de escolas fundadas por ano em São Paulo&amp;quot;) +
  scale_x_continuous(breaks = scales::pretty_breaks(n = 10))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-05-14-mapeando-a-abertura-de-escolas-municipais-em-sao-paulo-ao-longo-dos-anos_files/figure-html/unnamed-chunk-6-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;A grande maioria das escolas foi criada a partir do ano de 2005. Confesso que esperava uma distribuição mais uniforme.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;criando-o-mapa&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Criando o mapa&lt;/h2&gt;
&lt;p&gt;Existem diversas maneiras de criar um mapa no R. O melhor método depende basicamente do tipo de dados que se tem em mãos. Caso seja necessário, por exemplo, plotar polígonos, áreas e fronteiras, o indicado é o combo do pacote &lt;code&gt;sf&lt;/code&gt; e da função &lt;code&gt;ggplot2::geom_sf&lt;/code&gt;. No nosso caso, como estamos interessados em plotar pontos e já possuímos os dados das coordenadas geográficas, uma das melhores opções é usar o pacote &lt;code&gt;ggmap&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Para criar um mapa, são necessários dois parâmetros iniciais: um ponto central e um nível de zoom, que define a escala do gráfico.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# para o centro de sp, usei as coordenadas da praca da se, que peguei no google maps
praca_se &amp;lt;- c(lon = -46.634123, lat = -23.548408)
# o zoom é calculado pela funcao calc_zoom do ggmap
zoom_sp &amp;lt;- calc_zoom(lon = LON, lat = LAT, data = df)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apenas com esses dois parâmetros, já é possível plotar um mapa base:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mapa_sp &amp;lt;- get_map(location = praca_se,
                   zoom = zoom_sp,
                   maptype = &amp;quot;toner-lite&amp;quot;)

ggmap(mapa_sp) +
  # plotar praça da sé
  geom_point(x = praca_se[1], y = praca_se[2], color = &amp;quot;red&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-05-14-mapeando-a-abertura-de-escolas-municipais-em-sao-paulo-ao-longo-dos-anos_files/figure-html/unnamed-chunk-8-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Vamos então adicionar um pouco de vida ao gráfico e plotar todas as escolas presentes no dataset:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggmap(mapa_sp) +
  geom_point(data = df, aes(x = LON, y = LAT),
             color = &amp;quot;red&amp;quot;, alpha = 0.1)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-05-14-mapeando-a-abertura-de-escolas-municipais-em-sao-paulo-ao-longo-dos-anos_files/figure-html/unnamed-chunk-9-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Aparantemente existe uma concentração de escolas perto, entre outras, da área de Itaquaquecetuba. Uma maneira de visualizar densidade de pontos é por meio do &lt;code&gt;geom_density2d&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggmap(mapa_sp) +
  geom_density2d(data = df, aes(x = LON, y = LAT),
                 color = &amp;quot;red&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-05-14-mapeando-a-abertura-de-escolas-municipais-em-sao-paulo-ao-longo-dos-anos_files/figure-html/unnamed-chunk-10-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;acrescentando-o-elemento-tempo-no-mapa&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Acrescentando o elemento tempo no mapa&lt;/h2&gt;
&lt;p&gt;Ainda não mostrei como representar o fator tempo na visualização. Penso que isto pode ser feito de três maneiras: colorir as escolas de acordo com o ano de fundação, separar o gráfico em &lt;em&gt;facets&lt;/em&gt; por ano ou, minha preferida, criar um gif composto por uma série de gráficos sobrepostos. Fazer isso é muito fácil com o auxílio do pacote &lt;code&gt;gganimate&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;Para criar uma sobreposição de gráficos ggplot com o pacote &lt;code&gt;gganimate&lt;/code&gt;, basta setar a &lt;em&gt;aesthetic&lt;/em&gt; especial &lt;code&gt;frame&lt;/code&gt; com o nome da variável que você deseja usar para separar os gráficos em unidades individuais. O argumento &lt;code&gt;cumulative&lt;/code&gt; é usado para que as escolas de anos mais recentes permaneçam no gráfico ao longo dos anos:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p &amp;lt;- ggmap(mapa_sp) +
  geom_point(data = df,
             aes(x = LON, y = LAT, frame = ANO,
                 cumulative  = TRUE), color = &amp;quot;red&amp;quot;, alpha = 0.1) + 
  labs(x = NULL, y = NULL, title = &amp;quot;Escolas em São Paulo em &amp;quot;)

# o argumento interval define o intervalo de transição do gif em segundos
gganimate(p = p, interval = .075)&lt;/code&gt;&lt;/pre&gt;
&lt;video width=&#34;864&#34;  controls loop&gt;
&lt;source src=&#34;http://www.sillasgonzaga.com/post/2018-05-14-mapeando-a-abertura-de-escolas-municipais-em-sao-paulo-ao-longo-dos-anos_files/figure-html/unnamed-chunk-11.webm&#34; /&gt;
&lt;/video&gt;
&lt;p&gt;Taí! Com o auxílio do gráfico, é possível perceber (pelo menos foi o que vi, vai que é uma miragem) que, de acordo com o dataset, nas primeiras décadas foram priorizadas as áreas mais periféricas de São Paulo. A região mais central aparenta ter recebido mais escolas apenas a partir das últimas duas décadas.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;publicidade&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Publicidade&lt;/h2&gt;
&lt;p&gt;Sou um dos intrutores do curso de &lt;a href=&#34;https://www.ibpad.com.br/produto/ciencia-de-dados-com-r-sp/&#34;&gt;Ciência de Dados do IBPAD&lt;/a&gt;. Temos um dia do curso voltado exclusivamente para visualização de dados, dentre eles mapas! Mais informações de novas turmas &lt;a href=&#34;https://www.ibpad.com.br/produto/ciencia-de-dados-com-r-sp/&#34;&gt;neste link&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Topic Modeling: Um algoritmo consegue entender sobre o que fala a youtuber Nathalia Arcuri?</title>
      <link>http://www.sillasgonzaga.com/post/topic-modeling-nathalia-arcuri/</link>
      <pubDate>Sat, 14 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/topic-modeling-nathalia-arcuri/</guid>
      <description>&lt;p&gt;No meu &lt;a href=&#34;http://www.sillasgonzaga.com/post/literaturaBR-01/&#34;&gt;último post&lt;/a&gt; sobre Mineração de Texto, usei algumas ferramentas do R para analisar textos clássicos da literatura brasileira. Desta vez, o foco da análise será algo mais contemporâneo: uma youtuber. Mais precisamente, a &lt;a href=&#34;http://mepoupenaweb.uol.com.br/sobre-a-nath/&#34;&gt;Nathalia Arcuri&lt;/a&gt;, responsável por um dos principais canais de educação financeira, o &lt;a href=&#34;https://www.youtube.com/channel/UC8mDF5mWNGE-Kpfcvnn0bUg/&#34;&gt;Me Poupe&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Além do objeto da análise, a abordagem aqui também é diferente: vou mostrar como Topic Modeling pode ser usado para descobrir temas gerais em um conjunto de dados textuais.&lt;/p&gt;
&lt;p&gt;Assim, este post se dedica ao seguinte problema de pesquisa: é possível identificar, por meio de um algoritmo de inteligência artificial, temas gerais que uma youtuber com mais de 300 vídeos publicados fala sobre?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(reticulate)
reticulate::use_python(&amp;quot;/home/sillas/anaconda3/bin/python&amp;quot;, required = TRUE)
library(lexiconPT)
library(tidytext)
library(tidyverse)
library(magrittr)
library(stm)
library(tm)
library(ggridges)
library(formattable)
options(scipen = 999)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;coleta-dos-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Coleta dos dados&lt;/h2&gt;
&lt;p&gt;Para analisar o conteúdo de vídeos de youtube, precisamos das legendas dos vídeos. Alguns (bem poucos) canais produzem suas próprias legendas manualmente, mas a grande maioria, como o Me Poupe, o canal da Nathalia Arcuri, não o faz. Sendo assim, precisamos nós mesmo produzir essas legendas.&lt;/p&gt;
&lt;p&gt;Isso seria uma tarefa muito árdua, mas felizmente o próprio Youtube tem seu próprio serviço de inteligência artifical de reconhecimento de fala, que cria automaticamente legendas para um vídeo. Apesar de algumas vezes as legendas produzidas pelo algoritmo do Youtube não serem muito fiéis, elas serão usadas como dados brutos para a modelagem por tópicos. Os resultados apresentados no post mostram que essas legendas automáticas podem sim serem usadas para fins de estudo.&lt;/p&gt;
&lt;p&gt;Para coletar as legendas dos vídeo, eu uso um utilitário de linha de comando chamado &lt;a href=&#34;https://rg3.github.io/youtube-dl/&#34;&gt;&lt;code&gt;youtube-dl&lt;/code&gt;&lt;/a&gt;, que é bem simples de usar. No código abaixo, que mistura R com shell script, eu mostro como montar uma query para baixar as legendas do vídeo em arquivos de texto cujos nomes contem alguns metadados do vídeo, descritos na variável &lt;code&gt;fields_raw&lt;/code&gt;. Caso você deseje realizar este mesmo estudo com outro youtuber, basta atribuir a url do canal na variável &lt;code&gt;channel_url&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;fields_raw &amp;lt;- c(&amp;quot;id&amp;quot;, &amp;quot;title&amp;quot;, &amp;quot;alt_title&amp;quot;, &amp;quot;creator&amp;quot;, &amp;quot;release_date&amp;quot;,
                &amp;quot;timestamp&amp;quot;, &amp;quot;upload_date&amp;quot;, &amp;quot;duration&amp;quot;, &amp;quot;view_count&amp;quot;,
                &amp;quot;like_count&amp;quot;, &amp;quot;dislike_count&amp;quot;, &amp;quot;comment_count&amp;quot;)

fields &amp;lt;- fields_raw %&amp;gt;% 
  map_chr(~paste0(&amp;quot;%(&amp;quot;, ., &amp;quot;)s&amp;quot;)) %&amp;gt;% 
  # usar &amp;amp;&amp;amp;&amp;amp; como separador de fields
  paste0(collapse = &amp;quot;&amp;amp;&amp;amp;&amp;amp;&amp;quot;) %&amp;gt;% 
  # acrescentar aspas no inicio e no final do string
  paste0(&amp;#39;&amp;quot;&amp;#39;, ., &amp;#39;&amp;quot;&amp;#39;)

# canal do me poupe
channel_url &amp;lt;- &amp;quot;https://www.youtube.com/channel/UC8mDF5mWNGE-Kpfcvnn0bUg&amp;quot;

# montar query (comando) do youtube-dl
cmd_ytdl &amp;lt;- str_glue(&amp;quot;youtube-dl -o {fields} -i -v -w --skip-download --write-auto-sub --sub-lang pt {channel_url}&amp;quot;)

# acrescentar diretorio
pasta_captions &amp;lt;- &amp;quot;/home/sillas/R/Projetos/paixaopordados-blogdown/data/mepoupe&amp;quot;
fs::dir_create(pasta_captions)
cmd &amp;lt;- str_glue(&amp;quot;cd {pasta_captions} &amp;amp;&amp;amp; {cmd_ytdl}&amp;quot;)

# imprimir comando para ver como ficou
cat(cmd)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## cd /home/sillas/R/Projetos/paixaopordados-blogdown/data/mepoupe &amp;amp;&amp;amp; youtube-dl -o &amp;quot;%(id)s&amp;amp;&amp;amp;&amp;amp;%(title)s&amp;amp;&amp;amp;&amp;amp;%(alt_title)s&amp;amp;&amp;amp;&amp;amp;%(creator)s&amp;amp;&amp;amp;&amp;amp;%(release_date)s&amp;amp;&amp;amp;&amp;amp;%(timestamp)s&amp;amp;&amp;amp;&amp;amp;%(upload_date)s&amp;amp;&amp;amp;&amp;amp;%(duration)s&amp;amp;&amp;amp;&amp;amp;%(view_count)s&amp;amp;&amp;amp;&amp;amp;%(like_count)s&amp;amp;&amp;amp;&amp;amp;%(dislike_count)s&amp;amp;&amp;amp;&amp;amp;%(comment_count)s&amp;quot; -i -v -w --skip-download --write-auto-sub --sub-lang pt https://www.youtube.com/channel/UC8mDF5mWNGE-Kpfcvnn0bUg&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Portanto, temos o comando para baixar as legendas dos vídeos na pasta indicada. Para rodar o comando, basta a variável &lt;code&gt;cmd&lt;/code&gt; como argumento da função &lt;code&gt;system()&lt;/code&gt; ou o copiar e colar no terminal.&lt;/p&gt;
&lt;p&gt;Uma amostra dos arquivos baixados:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;dir(pasta_captions)[1:3]&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;039orzgCUt0&amp;amp;&amp;amp;&amp;amp;10 DILEMAS MAIS FREQUENTES SOBRE TESOURO DIRETO! Tire as dúvidas e invista!&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;20170424&amp;amp;&amp;amp;&amp;amp;642&amp;amp;&amp;amp;&amp;amp;272848&amp;amp;&amp;amp;&amp;amp;33970&amp;amp;&amp;amp;&amp;amp;137&amp;amp;&amp;amp;&amp;amp;NA.pt.vtt&amp;quot;                 
## [2] &amp;quot;0bqZrSaitDo&amp;amp;&amp;amp;&amp;amp;PLANILHA DE INDEPENDÊNCIA FINANCEIRA! Aprenda a usar! Série especial_ Office 365 #AjudaaNath&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;20170328&amp;amp;&amp;amp;&amp;amp;988&amp;amp;&amp;amp;&amp;amp;260474&amp;amp;&amp;amp;&amp;amp;18439&amp;amp;&amp;amp;&amp;amp;206&amp;amp;&amp;amp;&amp;amp;NA.pt.vtt&amp;quot;
## [3] &amp;quot;0eSSqsHSr2A&amp;amp;&amp;amp;&amp;amp;Série empreendedorismo na veia Ep 3_ EXPERIENTES TAMBÉM FALHAM!&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;NA&amp;amp;&amp;amp;&amp;amp;20161225&amp;amp;&amp;amp;&amp;amp;282&amp;amp;&amp;amp;&amp;amp;32361&amp;amp;&amp;amp;&amp;amp;2307&amp;amp;&amp;amp;&amp;amp;21&amp;amp;&amp;amp;&amp;amp;NA.pt.vtt&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;limpeza-dos-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Limpeza dos dados&lt;/h2&gt;
&lt;p&gt;O Youtube tem um formato próprio de arquivos para legendas chamado vtt. Veja como são os arquivos de texto baixados na etapa anterior:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;arquivos_captions &amp;lt;- dir(pasta_captions, pattern = &amp;#39;*.vtt&amp;#39;, full.names = TRUE)
amostra &amp;lt;- arquivos_captions[1]

read_lines(amostra)[1:30]&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##  [1] &amp;quot;WEBVTT&amp;quot;                                                                                                                                                                                                    
##  [2] &amp;quot;Kind: captions&amp;quot;                                                                                                                                                                                            
##  [3] &amp;quot;Language: pt&amp;quot;                                                                                                                                                                                              
##  [4] &amp;quot;Style:&amp;quot;                                                                                                                                                                                                    
##  [5] &amp;quot;::cue(c.colorCCCCCC) { color: rgb(204,204,204);&amp;quot;                                                                                                                                                           
##  [6] &amp;quot; }&amp;quot;                                                                                                                                                                                                        
##  [7] &amp;quot;::cue(c.colorE5E5E5) { color: rgb(229,229,229);&amp;quot;                                                                                                                                                           
##  [8] &amp;quot; }&amp;quot;                                                                                                                                                                                                        
##  [9] &amp;quot;##&amp;quot;                                                                                                                                                                                                        
## [10] &amp;quot;&amp;quot;                                                                                                                                                                                                          
## [11] &amp;quot;00:00:00.000 --&amp;gt; 00:00:02.419 align:start position:0%&amp;quot;                                                                                                                                                     
## [12] &amp;quot; &amp;quot;                                                                                                                                                                                                         
## [13] &amp;quot;olha&amp;lt;00:00:00.329&amp;gt;&amp;lt;c&amp;gt; o&amp;lt;/c&amp;gt;&amp;lt;00:00:00.420&amp;gt;&amp;lt;c&amp;gt; data&amp;lt;/c&amp;gt;&amp;lt;c.colorE5E5E5&amp;gt;&amp;lt;00:00:00.840&amp;gt;&amp;lt;c&amp;gt; mtow&amp;lt;/c&amp;gt;&amp;lt;00:00:01.260&amp;gt;&amp;lt;c&amp;gt; que&amp;lt;/c&amp;gt;&amp;lt;00:00:01.350&amp;gt;&amp;lt;c&amp;gt; apurou&amp;lt;/c&amp;gt;&amp;lt;00:00:01.709&amp;gt;&amp;lt;c&amp;gt; que&amp;lt;/c&amp;gt;&amp;lt;00:00:01.979&amp;gt;&amp;lt;c&amp;gt; esta&amp;lt;/c&amp;gt;&amp;lt;/c&amp;gt;&amp;quot;
## [14] &amp;quot;&amp;quot;                                                                                                                                                                                                          
## [15] &amp;quot;00:00:02.419 --&amp;gt; 00:00:02.429 align:start position:0%&amp;quot;                                                                                                                                                     
## [16] &amp;quot;olha o data&amp;lt;c.colorE5E5E5&amp;gt; mtow que apurou que esta&amp;quot;                                                                                                                                                       
## [17] &amp;quot; &amp;lt;/c&amp;gt;&amp;quot;                                                                                                                                                                                                     
## [18] &amp;quot;&amp;quot;                                                                                                                                                                                                          
## [19] &amp;quot;00:00:02.429 --&amp;gt; 00:00:04.640 align:start position:0%&amp;quot;                                                                                                                                                     
## [20] &amp;quot;olha o data&amp;lt;c.colorE5E5E5&amp;gt; mtow que apurou que esta&amp;lt;/c&amp;gt;&amp;quot;                                                                                                                                                   
## [21] &amp;quot;&amp;lt;c.colorCCCCCC&amp;gt;pergunta&amp;lt;/c&amp;gt;&amp;lt;c.colorE5E5E5&amp;gt;&amp;lt;00:00:03.210&amp;gt;&amp;lt;c&amp;gt; este&amp;lt;/c&amp;gt;&amp;lt;00:00:03.389&amp;gt;&amp;lt;c&amp;gt; dilema&amp;lt;/c&amp;gt;&amp;lt;00:00:03.780&amp;gt;&amp;lt;c&amp;gt; é&amp;lt;/c&amp;gt;&amp;lt;00:00:04.170&amp;gt;&amp;lt;c&amp;gt; o&amp;lt;/c&amp;gt;&amp;lt;00:00:04.259&amp;gt;&amp;lt;c&amp;gt; mais&amp;lt;/c&amp;gt;&amp;lt;/c&amp;gt;&amp;quot;                              
## [22] &amp;quot;&amp;quot;                                                                                                                                                                                                          
## [23] &amp;quot;00:00:04.640 --&amp;gt; 00:00:04.650 align:start position:0%&amp;quot;                                                                                                                                                     
## [24] &amp;quot;&amp;lt;c.colorCCCCCC&amp;gt;pergunta&amp;lt;/c&amp;gt;&amp;lt;c.colorE5E5E5&amp;gt; este dilema é o mais&amp;quot;                                                                                                                                           
## [25] &amp;quot; &amp;lt;/c&amp;gt;&amp;quot;                                                                                                                                                                                                     
## [26] &amp;quot;&amp;quot;                                                                                                                                                                                                          
## [27] &amp;quot;00:00:04.650 --&amp;gt; 00:00:06.650 align:start position:0%&amp;quot;                                                                                                                                                     
## [28] &amp;quot;&amp;lt;c.colorCCCCCC&amp;gt;pergunta&amp;lt;/c&amp;gt;&amp;lt;c.colorE5E5E5&amp;gt; este dilema é o mais&amp;lt;/c&amp;gt;&amp;quot;                                                                                                                                       
## [29] &amp;quot;&amp;lt;c.colorE5E5E5&amp;gt;freqüente&amp;lt;00:00:05.190&amp;gt;&amp;lt;c&amp;gt; de&amp;lt;/c&amp;gt;&amp;lt;00:00:05.310&amp;gt;&amp;lt;c&amp;gt; tudo&amp;lt;/c&amp;gt;&amp;lt;00:00:05.700&amp;gt;&amp;lt;c&amp;gt; o&amp;lt;/c&amp;gt;&amp;lt;00:00:05.790&amp;gt;&amp;lt;c&amp;gt; que&amp;lt;/c&amp;gt;&amp;lt;/c&amp;gt;&amp;lt;c.colorCCCCCC&amp;gt;&amp;lt;00:00:05.970&amp;gt;&amp;lt;c&amp;gt; investiu&amp;lt;/c&amp;gt;&amp;lt;00:00:06.509&amp;gt;&amp;lt;c&amp;gt; uma&amp;lt;/c&amp;gt;&amp;lt;/c&amp;gt;&amp;quot;  
## [30] &amp;quot;&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Temos vários problemas de dados não estruturados aí, como timestamps, formatação de estilo como cor e alinhamento e repetição de frases em diferentes linhas (note como as frases nas linhas 24 e 28 são as mesmas). Entretanto, limpar esses dados é mais simples que se imagina. Segue o passo-a-passo:&lt;/p&gt;
&lt;p&gt;Começando pelo mais crítico, vamos remover toda a formatação de legendas do texto, deixando apenas as frases. Para isso, uso um módulo Python (sim, eu me rendi, esta é a primeira vez que uso Python no blog) chamado &lt;code&gt;webvtt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;python&#34;&gt;&lt;code&gt;from webvtt import WebVTT
def caption_to_vector(file):
  x = WebVTT().read(file)
  txt = [caption.text for caption in x]
  return(txt)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vejam com essa função faz todo o trabalho bruto de limpar os metadados da legenda:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;x &amp;lt;- amostra %&amp;gt;% caption_to_vector()
x[1:20]&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##  [1] &amp;quot; \nolha o data mtow que apurou que esta&amp;quot;                                      
##  [2] &amp;quot;olha o data mtow que apurou que esta\n &amp;quot;                                      
##  [3] &amp;quot;olha o data mtow que apurou que esta\npergunta este dilema é o mais&amp;quot;          
##  [4] &amp;quot;pergunta este dilema é o mais\n &amp;quot;                                             
##  [5] &amp;quot;pergunta este dilema é o mais\nfreqüente de tudo o que investiu uma&amp;quot;          
##  [6] &amp;quot;freqüente de tudo o que investiu uma\n &amp;quot;                                      
##  [7] &amp;quot;freqüente de tudo o que investiu uma\nvez só ou possa investir todo mês&amp;quot;      
##  [8] &amp;quot;vez só ou possa investir todo mês\n &amp;quot;                                         
##  [9] &amp;quot;vez só ou possa investir todo mês\nfilho olha esse aqui se não ficar nos&amp;quot;     
## [10] &amp;quot;filho olha esse aqui se não ficar nos\n &amp;quot;                                     
## [11] &amp;quot;filho olha esse aqui se não ficar nos\nvídeos em alta do yuan subir é&amp;quot;        
## [12] &amp;quot;vídeos em alta do yuan subir é\n &amp;quot;                                            
## [13] &amp;quot;vídeos em alta do yuan subir é\nmarmelada hoje vou falar para você&amp;quot;           
## [14] &amp;quot;marmelada hoje vou falar para você\n &amp;quot;                                        
## [15] &amp;quot;marmelada hoje vou falar para você\nquais são os dez maiores dilemas de&amp;quot;      
## [16] &amp;quot;quais são os dez maiores dilemas de\n &amp;quot;                                       
## [17] &amp;quot;quais são os dez maiores dilemas de\nquem quer investir no tesouro direto mas&amp;quot;
## [18] &amp;quot;quem quer investir no tesouro direto mas\n &amp;quot;                                  
## [19] &amp;quot;quem quer investir no tesouro direto mas\nainda não sabe muito bem como fazer&amp;quot;
## [20] &amp;quot;ainda não sabe muito bem como fazer\n &amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ainda temos alguns problemas, como as linhas repetidas, mas isso é o de menos. Resolvemos todos os problemas de limpeza de dados com a função abaixo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;limpar_caption &amp;lt;- function(arquivo){
  caption_raw &amp;lt;- caption_to_vector(arquivo)
  n &amp;lt;- length(caption_raw)
  # remover \n das linhas com exceção da ultima
  caption &amp;lt;- c(stringr::str_remove_all(caption_raw[-n], &amp;quot;[\n].*&amp;quot;),
               caption_raw[n])
  # remover duplicatas
  caption &amp;lt;- unique(caption)
  # remover acentos
  caption &amp;lt;- iconv(caption, from = &amp;quot;UTF-8&amp;quot;, to = &amp;quot;ASCII//TRANSLIT&amp;quot;)
  # juntar todo o vector em um so
  caption &amp;lt;- paste0(caption, collapse = &amp;quot;\n&amp;quot;)
  caption
}

# exemplo
# amostra %&amp;gt;% limpar_caption()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Além disso, precisamos também extrair os metadados dos vídeos salvos nos nomes dos arquivos:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# funcao para extrair metadados do video baseado no titulo
extrair_metadados &amp;lt;- function(arquivo, pasta = pasta_captions,
                              fields = fields_raw){
  mat &amp;lt;- str_split(arquivo, &amp;quot;&amp;amp;{3}&amp;quot;, simplify = TRUE)
  # substituir elemento da primeira coluna por id (remover pasta do nome)
  mat[1,1] &amp;lt;- mat[1,1] %&amp;gt;% str_remove_all(pasta) %&amp;gt;% str_remove_all(&amp;quot;/&amp;quot;)
  
  # renomear colunas
  cols &amp;lt;- fields[1:ncol(mat)]
  colnames(mat) &amp;lt;- cols
  as.tibble(mat)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente, a função abaixo cria um dataframe com as colunas de metadados e de legenda, que chamo de caption:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# funcao para juntar tudo num dataframe so
caption_to_df &amp;lt;- function(arquivo, ...){
  
  caption &amp;lt;- limpar_caption(arquivo)
  meta &amp;lt;- extrair_metadados(arquivo, ...)
  meta &amp;lt;- meta %&amp;gt;% mutate(caption = caption)
  
  meta
}

### gerar dataframe para todos os videos
df &amp;lt;- arquivos_captions %&amp;gt;% 
  map_df(caption_to_df) %&amp;gt;% 
  # remover coluna que nao uso
  select(-comment_count) %&amp;gt;% 
  # converter a classe de algumas colunas
  mutate(upload_date = lubridate::ymd(upload_date)) %&amp;gt;% 
  mutate_at(vars(duration:dislike_count), as.numeric)

# vendo como ficou
glimpse(df)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 319
## Variables: 12
## $ id            &amp;lt;chr&amp;gt; &amp;quot;039orzgCUt0&amp;quot;, &amp;quot;0bqZrSaitDo&amp;quot;, &amp;quot;0eSSqsHSr2A&amp;quot;, &amp;quot;0G...
## $ title         &amp;lt;chr&amp;gt; &amp;quot;10 DILEMAS MAIS FREQUENTES SOBRE TESOURO DIRETO...
## $ alt_title     &amp;lt;chr&amp;gt; &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, ...
## $ creator       &amp;lt;chr&amp;gt; &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, ...
## $ release_date  &amp;lt;chr&amp;gt; &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, ...
## $ timestamp     &amp;lt;chr&amp;gt; &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, &amp;quot;NA&amp;quot;, ...
## $ upload_date   &amp;lt;date&amp;gt; 2017-04-24, 2017-03-28, 2016-12-25, 2016-02-15,...
## $ duration      &amp;lt;dbl&amp;gt; 642, 988, 282, 440, 670, 647, 574, 396, 615, 614...
## $ view_count    &amp;lt;dbl&amp;gt; 272848, 260474, 32361, 242366, 262462, 53146, 11...
## $ like_count    &amp;lt;dbl&amp;gt; 33970, 18439, 2307, 13163, 14439, 3868, 9369, 17...
## $ dislike_count &amp;lt;dbl&amp;gt; 137, 206, 21, 371, 453, 89, 110, 176, 87, 149, 1...
## $ caption       &amp;lt;chr&amp;gt; &amp;quot; \nolha o data mtow que apurou que esta\npergun...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;topic-modeling&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Topic Modeling&lt;/h2&gt;
&lt;p&gt;Antes de partir para o código aplicado a Topic Modeling, uma brevíssima introdução teórica.&lt;/p&gt;
&lt;p&gt;No contexto de &lt;a href=&#34;https://en.wikipedia.org/wiki/Text_mining&#34;&gt;Text Mining&lt;/a&gt;, um &lt;strong&gt;tópico&lt;/strong&gt; &lt;span class=&#34;math inline&#34;&gt;\(T\)&lt;/span&gt; é definido como um conjunto de &lt;strong&gt;palavras&lt;/strong&gt; ou tokens (&lt;span class=&#34;math inline&#34;&gt;\(w_1, w_2, ... w_n\)&lt;/span&gt;) onde cada palavra possui uma probabilidade de pertencer a um tópico, e um &lt;strong&gt;documento&lt;/strong&gt; é definido como um conjunto de tópicos, onde cada um possui uma proporção de presença dentro do documento. A soma da proporção de cada tópico dentro de um documento é igual a 1, assim como a soma das probabilidades de cada palavra para um dado tópico, que também é igual a 1. Nesta análise, um documento corresponde a cada vídeo lançado no canal Me Poupe.&lt;/p&gt;
&lt;p&gt;Topic Modeling, portanto, é definido como uma ténica não-supervisionada de Machine Learning que busca que identica clusteres ou grupos de palavras que ocorrem juntas, descobrindo assim tópicos abstratos que ocorrem em um conjunto de documentos. O objetivo é descobrir subestruturas semânticas dentro de um conjunto de textos.&lt;/p&gt;
&lt;p&gt;Existem alguns algoritmos de Topic Modeling, muitos deles presentes no pacote &lt;code&gt;topicmodels&lt;/code&gt;. Apenas devido a preferência pessoal, vou usar neste post o algoritmo &lt;strong&gt;Structural Topic Models&lt;/strong&gt; (STM), presente no pacote &lt;code&gt;stm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A qualidade de um tópico encontrado por um algoritmo pode ser medida por algumas métricas, como &lt;strong&gt;exclusividade&lt;/strong&gt; e &lt;strong&gt;coerência semântica&lt;/strong&gt;, cuja ideia é que, em um modelo de tópicos que é semanticamente coerente, as palavras que são mais prováveis de pertencer a um tópico devem ocorrer dentro de um mesmo documento. A formulação matemática dessas métricas é razoavelmente mais complicadas que essas explicações. Caso você deseja conhecer essas métricas com mais detalhes, confira as referências no final do post.&lt;/p&gt;
&lt;div id=&#34;pre-processamento&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Pré-processamento&lt;/h3&gt;
&lt;p&gt;Mesmo após o processo de limpeza, é necessário realizar alguns pré-processamentos antes de aplicar o algoritmo de Topic Modeling.&lt;/p&gt;
&lt;p&gt;Um dos procedimentos necessários é a remoção de stopwords, que são palavras que ocorrem tão frequentemente que não acrescentam nenhum valor semântico a um texto, como “e”, “vai”, “lá”, etc. O pacote &lt;code&gt;tm&lt;/code&gt; fornece uma lista de stopwords em vários idiomas, incluindo Português.&lt;/p&gt;
&lt;p&gt;O código abaixo retorna as palavras mais faladas nos vídeos do Me Poupe, após a remoção de stopwords:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# stopwords da lingua portuguesa sem acento
sw_pt_tm &amp;lt;- tm::stopwords(&amp;quot;pt&amp;quot;) %&amp;gt;% iconv(from = &amp;quot;UTF-8&amp;quot;, to = &amp;quot;ASCII//TRANSLIT&amp;quot;)

# criar dataframe com uma linha por palavra
df_palavra &amp;lt;- df %&amp;gt;% 
  unnest_tokens(palavra, caption) %&amp;gt;% 
  # filtrar fora stopword
  filter(!palavra %in% sw_pt_tm)

df_palavra %&amp;gt;% 
  count(palavra) %&amp;gt;% 
  arrange(desc(n)) %&amp;gt;% 
  head(20) %&amp;gt;% 
  formattable()&lt;/code&gt;&lt;/pre&gt;
&lt;table class=&#34;table table-condensed&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:right;&#34;&gt;
palavra
&lt;/th&gt;
&lt;th style=&#34;text-align:right;&#34;&gt;
n
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
vai
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
5467
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
gente
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
3297
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
porque
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
3077
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
aqui
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
3047
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
pra
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
3001
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
dinheiro
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
2731
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
fazer
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
2373
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
entao
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
2256
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
pode
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
2119
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
la
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
2097
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
ter
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1645
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
ai
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1614
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
agora
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1520
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
ser
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1516
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
pessoas
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1354
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
canal
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1334
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
vou
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1334
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
hoje
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1319
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
video
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1267
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
tudo
&lt;/td&gt;
&lt;td style=&#34;text-align:right;&#34;&gt;
1243
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Como muitas dessas palavras não possuem um grande valor semântico para a separação de tópicos, podemos as acrescentar à lista de stopwords.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;minhas_sw &amp;lt;- c(&amp;quot;vai&amp;quot;, &amp;quot;porque&amp;quot;, &amp;quot;vou&amp;quot;, &amp;quot;ai&amp;quot;, &amp;quot;pra&amp;quot;, &amp;quot;entao&amp;quot;)
sw_pt &amp;lt;- c(minhas_sw, sw_pt_tm)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Partimos então para o processamento de textos com as funções do pacote &lt;code&gt;stm&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;proc &amp;lt;- stm::textProcessor(df$caption, metadata = df, language = &amp;quot;portuguese&amp;quot;,
                           customstopwords = sw_pt)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Building corpus... 
## Converting to Lower Case... 
## Removing punctuation... 
## Removing stopwords... 
## Remove Custom Stopwords...
## Removing numbers... 
## Stemming... 
## Creating Output...&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;out &amp;lt;- stm::prepDocuments(proc$documents, proc$vocab, proc$meta,
                          lower.thresh = 10)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Removing 14665 of 16857 terms (33357 of 129408 tokens) due to frequency 
## Your corpus now has 319 documents, 2192 terms and 96051 tokens.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;criacao-do-modelo-de-topicos-e-interpretacao-dos-resultados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Criação do modelo de tópicos e interpretação dos resultados&lt;/h2&gt;
&lt;p&gt;A quantidade de tópicos &lt;span class=&#34;math inline&#34;&gt;\(K\)&lt;/span&gt; que desejamos extrair dos textos é, na verdade, escolhida pelo ser humano. Não há uma regra precisa que defina o número ótimo de clusteres. No entanto, é possível usar a função &lt;code&gt;stm::searchK&lt;/code&gt; para rodar o STM para diferentes valores do parâmetro &lt;code&gt;K&lt;/code&gt; para encontrar o valor que otimiza a exclusividade e a coerência semântica do modelo.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;storage &amp;lt;- stm::searchK(out$documents, out$vocab, K = c(3:15),
                          data = out$meta)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Após uma inspeção manual, decidi usar &lt;span class=&#34;math inline&#34;&gt;\(K = 10\)&lt;/span&gt; para encontrar 10 tópicos sobre os quais a Nathalia Arcuri mais fala.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;fit &amp;lt;- stm(
  documents = out$documents, vocab = out$vocab, data = out$meta,  K = 10,
  max.em.its = 75, init.type = &amp;quot;Spectral&amp;quot;, verbose = FALSE
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Após a construção do modelo, a melhor forma de visualizar os resultados é por meio de um gráfico:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot(fit, &amp;quot;summary&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-04-14-topic-modeling-nathalia-arcuri_files/figure-html/unnamed-chunk-10-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Ou mesmo por texto:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;stm::labelTopics(fit)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Topic 1 Top Words:
##       Highest Prob: gent, aqui, coisa, fazer, pode, assim, casa 
##       FREX: roupa, cabelo, peca, barato, economizar, comer, loja 
##       Lift: energia, cor, tratamento, sapato, ovo, armario, compro 
##       Score: energia, tratamento, ovo, cabelo, pared, loja, dica 
## Topic 2 Top Words:
##       Highest Prob: gent, aqui, pessoa, pergunta, agora, vamo, fazer 
##       FREX: milhao, hashtag, galera, pergunta, poup, job, inscrito 
##       Lift: bilhao, premio, jornalista, seguidor, contrata, hashtag, orgulho 
##       Score: bilhao, live, job, milhao, premio, hashtag, conteudo 
## Topic 3 Top Words:
##       Highest Prob: dinheiro, reai, ano, aqui, fazer, video, mil 
##       FREX: meta, planilha, tarefa, viver, aposentadoria, reai, poupar 
##       Lift: vitoria, offic, aposentar, caderno, planilha, metodo, produtividad 
##       Score: vitoria, caderno, meta, offic, planilha, joaquina, metodo 
## Topic 4 Top Words:
##       Highest Prob: dinheiro, coisa, mulher, vida, pessoa, pode, ser 
##       FREX: mulher, mae, present, crianca, casamento, maravilha, pai 
##       Lift: gostam, casamento, adulto, maravilha, honesto, casar, present 
##       Score: gostam, casamento, natal, present, maravilha, adulto, objetivo 
## Topic 5 Top Words:
##       Highest Prob: gent, cidad, aqui, rio, janeiro, fazer, candidato 
##       FREX: cidad, candidato, prefeitura, janeiro, rio, prefeito, municipio 
##       Lift: arrecadou, evasiva, habitacao, municipio, prefeitura, promovida, tocar 
##       Score: municipio, tocar, candidato, prefeitura, habitacao, corrupcao, prefeito 
## Topic 6 Top Words:
##       Highest Prob: tesouro, dinheiro, taxa, fundo, direto, aqui, investimento 
##       FREX: tesouro, selic, fundo, direto, taxa, titulo, corretora 
##       Lift: digit, garantidor, imobiliario, ipc, ipc-, cdi, tesouro 
##       Score: digit, tesouro, selic, imobiliario, corretora, taxa, rentabilidad 
## Topic 7 Top Words:
##       Highest Prob: empresa, pessoa, aqui, pode, fazer, dinheiro, valor 
##       FREX: aco, aplicativo, empresa, aluguel, mercado, bolsa, acao 
##       Lift: placa, aco, lucro, bolo, alugar, documento, moeda 
##       Score: placa, aco, investidor, alugar, bolo, empresa, lucro 
## Topic 8 Top Words:
##       Highest Prob: ponto, cartao, gent, credito, aqui, pode, fazer 
##       FREX: multiplus, ponto, acumular, multiplo, cartao, black, site 
##       Lift: milha, anuidad, multiplus, multiplo, passagen, acumular, acumula 
##       Score: multiplus, anuidad, multiplo, ponto, black, acumular, cartao 
## Topic 9 Top Words:
##       Highest Prob: divida, dinheiro, pagar, carro, credito, cartao, pode 
##       FREX: divida, carro, parcela, pagar, juro, credito, ajuda 
##       Lift: chequ, financiado, quitar, devendo, divida, parcelar, parcela 
##       Score: chequ, divida, credito, cartao, financiamento, parcela, consorcio 
## Topic 10 Top Words:
##       Highest Prob: pessoa, gent, fazer, ser, aqui, dinheiro, ter 
##       FREX: ingl, empreendedor, curso, faculdad, sucesso, segredo, negocio 
##       Lift: encontrei, mestr, ingl, atividad, consequencia, tecnico, milionaria 
##       Score: encontrei, live, faculdad, empreendedor, conteudo, ingl, chefe&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na tabela acima, existe para cada tópico uma lista de palavras-chave, de acordo com uma métrica de associação. As métricas mais interessantes a serem olhadas são &lt;strong&gt;Highest prob&lt;/strong&gt;, que é basicamente uma contagem de cada palavra no tópico, e &lt;strong&gt;FREX&lt;/strong&gt;, que é combina frequência e exclusividade para identificar palavras que mais representam o tópico.&lt;/p&gt;
&lt;p&gt;Assim, a interpretação dos tópicos pode ser feita desta maneira:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tópico 1: Dicas de economia doméstica, como que para reduzir despesas em casa;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 2: Genérico;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 3: Planejamento Financeiro, com temas como planilhas e aposentadoria;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 4: Vídeos focados para mulheres ou famílias;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 5: Política;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 6: Renda Fixa (este ficou bem claro);&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 7: Dificil saber. Talvez renda variável;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 8: Cartão de cŕedito e temas afins, como programas de milhas;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Tópico 9: Dívidas e despesas;&lt;/li&gt;
&lt;li&gt;Tópico 10: Empreendedorimo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;É possível representar visualmente a diferença entre um par de tópicos, como o 6 e o 9:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot(fit, &amp;quot;perspective&amp;quot;, topics = c(9, 6))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-04-14-topic-modeling-nathalia-arcuri_files/figure-html/unnamed-chunk-12-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Percebe-se que as palavras “dívida” e “tesouro” separam bem esses dois tópicos.&lt;/p&gt;
&lt;div id=&#34;atribuicao-de-topicos-a-videos&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Atribuição de tópicos a vídeos&lt;/h3&gt;
&lt;p&gt;Um dos possíveis produtos da análise de Topic Modeling é atribuir a cada documento (ou vídeo) um tópico, de acordo com a proporção de cada tópico nele.&lt;/p&gt;
&lt;p&gt;O objeto &lt;code&gt;fit&lt;/code&gt; possui internamente um elemento que é uma matriz &lt;span class=&#34;math inline&#34;&gt;\(N \times K\)&lt;/span&gt;, onde N é o número de documentos. Assim, para cada vídeo, existe um porcentual de participação de cada tópico, cuja soma é igual a 1.&lt;/p&gt;
&lt;p&gt;Vejamos essa matriz para os cinco primeiros vídeos:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;head(fit$theta)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##             [,1]        [,2]        [,3]         [,4]         [,5]
## [1,] 0.000273920 0.003253428 0.004040834 0.0009959664 0.0001388210
## [2,] 0.018310800 0.030741680 0.708967915 0.0118358411 0.0006963748
## [3,] 0.070843633 0.041732621 0.013845361 0.0304166112 0.0839133871
## [4,] 0.008194601 0.002381194 0.008008110 0.0033322595 0.0018488345
## [5,] 0.010782705 0.026464694 0.010263833 0.0477243086 0.1095574639
## [6,] 0.006023939 0.872040010 0.005714869 0.0059672704 0.0481083479
##             [,6]        [,7]         [,8]        [,9]        [,10]
## [1,] 0.985509536 0.002881037 0.0001246733 0.002037421 0.0007443629
## [2,] 0.183050228 0.005301315 0.0292175603 0.008811973 0.0030663133
## [3,] 0.010378165 0.176350442 0.0412429834 0.262069287 0.2692075093
## [4,] 0.001888864 0.106774265 0.8546355488 0.011153510 0.0017828124
## [5,] 0.047119628 0.504319587 0.0113556114 0.156215296 0.0761968712
## [6,] 0.019619518 0.011139023 0.0019899886 0.006333920 0.0230631152&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Observando a primeira linha, nota-se que existe um claro predomínio do Tópico 6, que representa 98% da distribuição de tópicos. Qual é esse vídeo?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df %&amp;gt;% 
  filter(row_number() == 1) %&amp;gt;% 
  select(id, title)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 1 x 2
##   id          title                                                       
##   &amp;lt;chr&amp;gt;       &amp;lt;chr&amp;gt;                                                       
## 1 039orzgCUt0 10 DILEMAS MAIS FREQUENTES SOBRE TESOURO DIRETO! Tire as dú…&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vemos que o algoritmo acertou na mosca, pois de fato o vídeo é sobre Renda Fixa.&lt;/p&gt;
&lt;p&gt;A qual tópico pertence o vídeo mais assistido do Me Poupe?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# id do video Meu Primeiro Milhao
ind &amp;lt;- which(df$id == &amp;quot;4jaWDfTbytA&amp;quot;)
t(fit$theta[ind,])&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##            [,1]       [,2]      [,3]       [,4]         [,5]        [,6]
## [1,] 0.05483404 0.03399528 0.5101044 0.01345342 0.0007212358 0.003021568
##             [,7]        [,8]        [,9]     [,10]
## [1,] 0.003167582 0.001051847 0.003699136 0.3759515&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Os tópicos predominantes são 3 (Planejamento financeiro), com probabilidade de 51% e 10 (empreendedorismo), com 38%. Faz sentido, porque neste vídeo a Nathalia conta sua história como empreendedora para atingir seu primeiro milhão por meio de muito planejamento financeiro (não tô forçando, eu vi o vídeo).&lt;/p&gt;
&lt;p&gt;Vamos então, para cada vídeo, extrair seu tópico predominante e contar a frequência de videos por tópico:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;nomes_topicos &amp;lt;- c(&amp;quot;economia_domestica&amp;quot;, &amp;quot;generico&amp;quot;, &amp;quot;plan_financeiro&amp;quot;,
                   &amp;quot;mulher_familia&amp;quot;, &amp;quot;politica&amp;quot;, &amp;quot;renda_fixa&amp;quot;, &amp;quot;renda_variavel&amp;quot;,
                   &amp;quot;cartao_de_credito&amp;quot;, &amp;quot;dividas&amp;quot;, &amp;quot;empreendedorismo&amp;quot;)
# extrair a maior probabilidade pra cada video
maior_prob &amp;lt;- apply(fit$theta, 1, max)
# extrair o nome do topico com a maior probabilidade
topico_video &amp;lt;- nomes_topicos[apply(fit$theta, 1, which.max)]

# acrescentar esses dados no dataframe principal
df_topico &amp;lt;- df %&amp;gt;% 
  mutate(maior_prob = maior_prob,
         topico = topico_video)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;roxo &amp;lt;- &amp;quot;mediumpurple4&amp;quot;

# grafico da quantidade de videos por topico
df_topico %&amp;gt;% 
  count(topico) %&amp;gt;% 
  # classificar em ordem decrescente
  mutate(topico = forcats::fct_reorder(topico, n)) %&amp;gt;% 
  ggplot(aes(x = topico, y = n)) + 
  geom_col(fill = roxo) +
  theme_minimal() + 
  labs(x = NULL, y = &amp;quot;Vídeos&amp;quot;,
       title = &amp;quot;Quantidade de vídeos por tópico&amp;quot;) +
  coord_flip()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-04-14-topic-modeling-nathalia-arcuri_files/figure-html/unnamed-chunk-17-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Os tópicos sobre os quais a Nathalia mais fala são Renda Fixa, Economia Doméstica e Dívidas. Quem acompanha seu canal não vai ficar surpreso com estes resultados, o que é um índicio da qualidade do modelo construído neste post.&lt;/p&gt;
&lt;p&gt;Também é possível extrair os vídeos chaves de cada tópico, isto é, os vídeos com a maior probabilidade para cada tópico:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_topico %&amp;gt;% 
  group_by(topico) %&amp;gt;% 
  filter(maior_prob == max(maior_prob)) %&amp;gt;% 
  select(id, title, topico, maior_prob) %&amp;gt;% 
  knitr::kable()&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;id&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;title&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;topico&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;maior_prob&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;_alsKxI3-TI&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;NATH AO VIVO! COMO VIAJAR SEM COLOCAR A MÃO NO BOLSO! Dicas preciosas dos viciados por pontos!&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;cartao_de_credito&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9906875&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;eMQZRHoIgtQ&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;COMO EU TIRO AS METAS DO PAPEL! Técnica simples pra juntar mais dinheiro do que nunca!&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;plan_financeiro&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9773071&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;FHqHW8-xvLQ&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Dia das crianças - COMO FALAR QUE A GRANA TÁ CURTA&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;mulher_familia&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9717965&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;iA9Iqx2uByo&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Entrevista - Carlos Osório - Que Candidato é esse&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;politica&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9907593&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;kddZm50Gluw&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Perguntas e respostas #5 - Cartão de crédito e FIM DE NAMORO!&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;dividas&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9629534&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;liIQENCBlF0&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;SE VOCÊ NÃO ENTENDER ISSO, NUNCA VAI INVESTIR NA BOLSA!_ Renda variável do jeito mais simples&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;renda_variavel&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9695903&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;R0AQyTIvcvI&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;O que é SELIC E CDI Entenda isso HOJE e pare de PERDER DINHEIRO! _ SÉRIE INVESTIDORES INICIANTES&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;renda_fixa&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9899924&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;SfG934AzjYU&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;COMO EU COMPRO ROUPAS DUAS VEZES AO ANO! Episódio final _ A negociação!&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;economia_domestica&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9877744&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;WtFV03Suheg&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;A FINAL DA CONTRATAÇÃO QUE VOCÊ RESPEITA! Quem a Nath vai contratar (AGORA GRAVADO)&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;generico&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9605720&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;Zla1-t3aOZw&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;3 ESCOLHAS QUE ENRIQUECERAM O CERBASI _ meu guru&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;empreendedorismo&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;0.9151230&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Eu, pessoalmente, estou bastante satisfeito com os resultados. A modelagem de tópicos funcionou muito bem.&lt;/p&gt;
&lt;p&gt;Vamos estão analisar algumas estatísticas básicas para cada tópico: quais têm os vídeos mais longos? Quais são os mais populares?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;meu_grafico &amp;lt;- function(df, var){
  quo_var &amp;lt;- enquo(var)

  mediana_geral &amp;lt;- df %&amp;gt;% 
    summarise_at(vars(!!quo_var), median, na.rm = TRUE) %&amp;gt;% 
    pull()
  
  # obter grafico
  p &amp;lt;- df %&amp;gt;% 
    group_by(topico) %&amp;gt;% 
    summarise(m = median(!!quo_var, na.rm = TRUE)) %&amp;gt;% 
    ggplot(aes(x = topico, y = m)) +
    geom_col(fill = roxo) +
    geom_hline(yintercept = mediana_geral, linetype = &amp;quot;dashed&amp;quot;) +
    theme_minimal() +
    coord_flip() +
    labs(x =  NULL, y = NULL)
  
  p
}
  
meu_grafico(df_topico, duration) + ggtitle(&amp;quot;Média da duração dos vídeos por tópico do Me Poupe&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-04-14-topic-modeling-nathalia-arcuri_files/figure-html/graficos-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;meu_grafico(df_topico, view_count) + ggtitle(&amp;quot;Média de views por tópico do Me Poupe&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-04-14-topic-modeling-nathalia-arcuri_files/figure-html/graficos-2.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;meu_grafico(df_topico, like_count) + ggtitle(&amp;quot;Média de likes por tópico do Me Poupe&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-04-14-topic-modeling-nathalia-arcuri_files/figure-html/graficos-3.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Com estes gráficos, aprendemos que os vídeos sobre renda fixa, planejamento financeiro e empreendedorismo são os mais populares entre os “me poupeiros” (como a Nathalia chama quem acompanha o canal), pois possuem mais visualizações e curtidas. Outro fato interessante é que os vídeos categorizados como política são, por assim dizer, improdutivos, pois, apesar de serem os mais longos, possuem muitos poucos views. Existem alguns possíveis tipos de viés que poderiam explicar este dado, como o temporal.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;analise-de-sentimento&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Análise de Sentimento&lt;/h3&gt;
&lt;p&gt;Finalmente, vamos aplicar uma abordagem de &lt;a href=&#34;http://sillasgonzaga.com/post/o-sensacionalista-e-text-mining/&#34;&gt;análise de sentimento&lt;/a&gt; para analisar se os tópicos apresentam diferentes polaridades entre si. Quais tópicos são mais negativos?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;data(&amp;quot;sentiLex_lem_PT02&amp;quot;)
dict &amp;lt;- unique(sentiLex_lem_PT02)

# criacao do dataframe de sentimento por topico
df_sent &amp;lt;- df %&amp;gt;% 
  unnest_tokens(palavra, caption) %&amp;gt;% 
  inner_join(sentiLex_lem_PT02, by = c(&amp;quot;palavra&amp;quot; = &amp;quot;term&amp;quot;)) %&amp;gt;% 
  group_by(id) %&amp;gt;% 
  summarise(
    sentimento_soma = sum(polarity),
    sentimento_media = mean(polarity)
  )

# acrescentar ao dafaframe principal
df_topico_sent &amp;lt;- inner_join(df_topico, df_sent, by = &amp;quot;id&amp;quot;) 


# calcular sentimento geral dos videos
sent_medio_geral &amp;lt;- median(df_topico_sent$sentimento_media)

df_topico_sent %&amp;gt;% 
  mutate(topico = forcats::fct_reorder(topico, sentimento_media, median)) %&amp;gt;% 
  ggplot(aes(x = sentimento_media, y = topico)) +
  geom_density_ridges(fill = roxo) +
  geom_vline(xintercept = sent_medio_geral, linetype = &amp;quot;dashed&amp;quot;) +
  theme_minimal() +
  labs(x = &amp;quot;Sentimento&amp;quot;, y = NULL,
       title = &amp;quot;Distribuição dos sentimentos por tópico do Me Poupe&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Picking joint bandwidth of 0.0756&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-04-14-topic-modeling-nathalia-arcuri_files/figure-html/analise_sent-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Em novamente o que eu considero um acerto da metodologia, os vídeos de dívidas apresentaram polaridades mais baixas, ou sejas, um nível de sentimento mais negativo.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Conclusão&lt;/h2&gt;
&lt;p&gt;É relativamente difícil avaliar a acurácia e precisão de um algoritmo de topic modeling por essa técnica ser não-supervisionada, ou seja, os dados não possuírem um output com a resposta correta.&lt;/p&gt;
&lt;p&gt;Contudo, os resultados apresentados neste post mostram que é possível sim usar esse tipo de técnica, com um certo auxílio de interpretação humana, para categorizar um conjunto de vídeos sem ser necessário os assistir ou mesmo sem saber seus títulos.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;referencias&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Referências&lt;/h2&gt;
&lt;p&gt;Antes das referências formais, gostaria de indicar o post do &lt;a href=&#34;http://curso-r.com/blog/2018/02/23/2018-02-21-2cents/&#34;&gt;Júlio Trecenti&lt;/a&gt;, que é um outro exemplo de uso criativo de análise de dados para youtubers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Topic_model&#34; class=&#34;uri&#34;&gt;https://en.wikipedia.org/wiki/Topic_model&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mimno, D., Wallach, H. M., Talley, E., Leenders, M., &amp;amp; McCallum, A. (2011, July). “Optimizing semantic coherence in topic models.” In Proceedings of the Conference on Empirical Methods in Natural Language Processing (pp. 262-272). Association for Computational Linguistics. Chicago&lt;/p&gt;
&lt;p&gt;Roberts, M., Stewart, B., Tingley, D., Lucas, C., Leder-Luis, J., Gadarian, S., Albertson, B., et al. (2014). “Structural topic models for open ended survey responses.” American Journal of Political Science, 58(4), 1064-1082&lt;/p&gt;
&lt;p&gt;Margaret E. Roberts, Brandon M. Stewart and Dustin Tingley (2018). stm: R Package for Structural Topic Models. URL &lt;a href=&#34;http://www.structuraltopicmodel.com&#34; class=&#34;uri&#34;&gt;http://www.structuraltopicmodel.com&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>BBB no R: Estudando as interações entre membros por análise de redes</title>
      <link>http://www.sillasgonzaga.com/post/bbb-no-r/</link>
      <pubDate>Thu, 22 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/bbb-no-r/</guid>
      <description>&lt;p&gt;Eu &lt;strong&gt;realmente&lt;/strong&gt; não acredito que estou escrevendo um post sobre Big Brother Brasil.&lt;/p&gt;
&lt;p&gt;Ok, respirei fundo, vamos lá…&lt;/p&gt;
&lt;p&gt;Sejam bem-vindos a mais um post! Em 2018, um dos projetos mais incríveis que vou tocar é um curso online de Análise de Redes Sociais (ARS) no R a ser oferecido por mim e pelo &lt;a href=&#34;http://ibpad.com.br/&#34;&gt;IBPAD&lt;/a&gt;, que é referência nacional em ARS e em outras coisas. A previsão é de que o curso seja lançado até Maio de 2018.&lt;/p&gt;
&lt;p&gt;Como um aperitivo do conteúdo a ser abordado no curso, lhes apresento uma simples porém criativa (para quem gosta, né) e prática aplicação de ARS: analisar interações de brothers e sisters (meu Deus, que termos ridículos), descobrir participantes influentes e aplicar algoritmos de detecção de comunidades. Com isso, minha intenção é demonstrar que você pode usar seus conhecimentos em R para aplicar em basicamente qualquer projeto que você quiser.&lt;/p&gt;
&lt;p&gt;Uma breve introdução sobre ARS pode ser vista neste artigo na &lt;a href=&#34;https://pt.wikipedia.org/wiki/An%C3%A1lise_de_redes_sociais&#34;&gt;Wikipedia&lt;/a&gt;. Se prefere um conteúdo mais elaborado e completo, sugiro adquirir &lt;a href=&#34;http://ibpad.com.br/&#34;&gt;este livro&lt;/a&gt; dos caras do IBPAD.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# pacotes usados
library(tidyverse)
library(rvest)
library(igraph)
library(Rfacebook)
library(knitr)
library(viridis)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;coleta-dos-dados&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Coleta dos dados&lt;/h1&gt;
&lt;p&gt;Para preparar os dados para a realização da ARS no R, precisamos obter um dataset neste formato:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tribble(
  ~pessoa_1, ~pessoa_2,
  &amp;quot;Fulano&amp;quot;, &amp;quot;Ciclano&amp;quot;,
  &amp;quot;Fulano&amp;quot;, &amp;quot;Beltrano&amp;quot;,
  &amp;quot;Ciclano&amp;quot;, &amp;quot;Beltrano&amp;quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 3 x 2
##   pessoa_1 pessoa_2
##   &amp;lt;chr&amp;gt;    &amp;lt;chr&amp;gt;   
## 1 Fulano   Ciclano 
## 2 Fulano   Beltrano
## 3 Ciclano  Beltrano&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou seja, o formato desejado é uma simples matriz de 2 colunas onde cada linha representa uma interação entre a Pessoa 1 e a Pessoa 2. A ordem (ex.: Fulano na coluna 1 ou 2) não importa, porque não se trata de uma rede direcionada (&lt;em&gt;directed&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Como, então, obter esses dados?&lt;/p&gt;
&lt;p&gt;Existem diversas alternativas. Minha solução foi coletar as manchetes do &lt;a href=&#34;https://gshow.globo.com/realities/bbb/&#34;&gt;site do BBB&lt;/a&gt; (após me recuperar do desgaste psicológico que é entrar em contato com um material literário tão rico) e extrair os nomes presentes na manchete.&lt;/p&gt;
&lt;p&gt;Por exemplo, a partir da “notícia” &lt;a href=&#34;https://gshow.globo.com/realities/bbb/BBB18/agora-na-casa/noticia/diego-kaysar-e-lucas-limpam-espelho-do-banheiro.ghtml&#34;&gt;Diego, Kaysar e Lucas limpam espelho do banheiro&lt;/a&gt;, será retornado um dataframe com todas as possíveis combinações de duplas formadas pelos três participantes presentes no título.&lt;/p&gt;
&lt;p&gt;Para coletar todas as notícias sobre o BBB já publicadas no site, utilizei a mesma estratégia descrita &lt;a href=&#34;http://sillasgonzaga.com/post/analise-g1-01/&#34;&gt;neste post sobre o G1&lt;/a&gt;, ou seja: coletar todos os links já publicados na página do BBB no Facebook.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;posts &amp;lt;- getPage(&amp;quot;BigBrotherBrasil&amp;quot;, token, n = 5000, since = &amp;quot;2018/01/22&amp;quot;)
links &amp;lt;- posts$link[posts$type == &amp;quot;link&amp;quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Após extrair os links, o código abaixo faz um webscraping bem simples e extrair o título e o corpo das matérias.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;extrair_bbb &amp;lt;- function(url){
  url &amp;lt;- url %&amp;gt;% read_html()
  
  css_titulo &amp;lt;- &amp;quot;.content-head__title&amp;quot;
  css_texto &amp;lt;- &amp;quot;.content-text__container&amp;quot;
  
  noticia_titulo &amp;lt;- url %&amp;gt;% 
    html_nodes(css = css_titulo) %&amp;gt;% 
    html_text()
  
  noticia_texto &amp;lt;- url %&amp;gt;% 
    html_nodes(css = css_texto) %&amp;gt;% 
    html_text() %&amp;gt;% 
    dplyr::first()
  
  tibble(noticia_titulo, noticia_texto)
}

lst &amp;lt;- links %&amp;gt;% map(extrair_bbb)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# juntar a lista em um dataframe
df_bbb &amp;lt;- bind_rows(lst)
# remover noticias de resumo
df_bbb &amp;lt;- df_bbb %&amp;gt;% filter(!str_detect(noticia_titulo, &amp;quot;Resumo&amp;quot;))

glimpse(df_bbb)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 595
## Variables: 2
## $ noticia_titulo &amp;lt;chr&amp;gt; &amp;quot;Wagner elogia Gleici: &amp;#39;Você tá linda&amp;#39;&amp;quot;, &amp;quot;Ayrto...
## $ noticia_texto  &amp;lt;chr&amp;gt; &amp;quot; Na sala, Gleici chega perto de Wagner e eles ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A seguir, mostro funções para extrair os participantes mencionados nos títulos e retornar as combinações de duplas.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;participantes &amp;lt;- c(&amp;quot;Ayrton&amp;quot;, &amp;quot;Ana Clara&amp;quot;, &amp;quot;Ana Paula&amp;quot;, &amp;quot;Breno&amp;quot;, &amp;quot;Caruso&amp;quot;, &amp;quot;Diego&amp;quot;,
                   &amp;quot;Gleici&amp;quot;, &amp;quot;Jaqueline&amp;quot;, &amp;quot;Jéssica&amp;quot;, &amp;quot;Kaysar&amp;quot;, &amp;quot;Lucas&amp;quot;, &amp;quot;Mahmoud&amp;quot;,
                   &amp;quot;Mara&amp;quot;, &amp;quot;Nayara&amp;quot;, &amp;quot;Patrícia&amp;quot;, &amp;quot;Paula&amp;quot;, &amp;quot;Viegas&amp;quot;, &amp;quot;Wagner&amp;quot;)

extrair_mencionados &amp;lt;- function(x){
  participantes[str_detect(x, participantes)]
}

mencionados &amp;lt;- df_bbb$noticia_titulo %&amp;gt;% map(extrair_mencionados)
# manter na lista apenas onde length(.) &amp;gt; 2
mencionados &amp;lt;- mencionados %&amp;gt;% keep(~length(.) &amp;gt; 1)

# gerar lista de combinacoes entre todos os mencionados
gerar_pares &amp;lt;- function(x){
  x &amp;lt;- sort(unique(x))
  x &amp;lt;- x %&amp;gt;% combn(m = 2, simplify = TRUE) %&amp;gt;% t()
  x &amp;lt;- as.tibble(x)
  colnames(x) &amp;lt;- c(&amp;quot;P1&amp;quot;, &amp;quot;P2&amp;quot;)
  x
}
# exemplo
gerar_pares(c(&amp;quot;A&amp;quot;, &amp;quot;B&amp;quot;, &amp;quot;C&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 3 x 2
##   P1    P2   
##   &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt;
## 1 A     B    
## 2 A     C    
## 3 B     C&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_mencionados &amp;lt;- mencionados %&amp;gt;%
  map(gerar_pares) %&amp;gt;%
  bind_rows() %&amp;gt;% 
  group_by_all() %&amp;gt;% 
  summarise(n = n())

kable(head(df_mencionados))&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;P1&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;P2&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;n&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;Ana Clara&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Ana Paula&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;Ana Clara&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Ayrton&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;Ana Clara&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Breno&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;Ana Clara&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Caruso&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;Ana Clara&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Diego&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;Ana Clara&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;Gleici&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div id=&#34;visualizacao-dos-dados&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Visualização dos dados&lt;/h1&gt;
&lt;p&gt;Antes de partir para ARS, vamos ver como se dá a frequência de interações entre os participantes:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_mencionados %&amp;gt;% 
  ggplot(aes(x = P1, y = P2, fill = n)) +
  geom_tile() +
  viridis::scale_fill_viridis() +
  geom_text(aes(label = n), color = &amp;quot;black&amp;quot;) + 
  theme_classic() +
  theme(axis.text.x = element_text(angle = 90))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-02-22-bbb-no-r_files/figure-html/unnamed-chunk-8-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Temos que, praticamente, todo mudo interage com todo mundo. Por isso, antes mesmo de plotar o grafo dessa rede, espera-se que ela seja bem densa, com todos os vértices se ligando.&lt;/p&gt;
&lt;p&gt;Entretanto, não deixa de ser interessante notar que, dentro de um grupo de apenas 18 pessoas confinadas em um espaço limitado como uma casa durante algumas semanas ou meses, é incrível como existem pares que simplesmente não interagem entre si (de acordo com a metodologia deste post).&lt;/p&gt;
&lt;p&gt;Para deixar a análise interessante, vou remover os nós em que existem menos de 3 interações.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_mencionados &amp;lt;- df_mencionados %&amp;gt;% filter(n &amp;gt; 3)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;analise-da-rede&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Análise da Rede&lt;/h1&gt;
&lt;p&gt;Para definir o grafo, vamos criar um dataframe de metadados sobre os vértices (os participantes), como sexo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;vertices &amp;lt;- tibble(
  nome = participantes,
  sexo = c(&amp;quot;M&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;F&amp;quot;,
           &amp;quot;F&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;M&amp;quot;)
)

vertices &amp;lt;- vertices %&amp;gt;%
  filter(nome %in% df_mencionados$P1 | nome %in% df_mencionados$P2)

g &amp;lt;- graph_from_data_frame(d = df_mencionados, vertices = vertices,
                           directed = FALSE)

# adicionar cor aos pontos de acordo com o sexo
V(g)$color &amp;lt;- ifelse(V(g)$sexo == &amp;quot;M&amp;quot;, &amp;quot;lightblue&amp;quot;, &amp;quot;pink&amp;quot;)
# adicionar peso aos nós
E(g)$weight &amp;lt;- df_mencionados$n &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente, o grafo em si:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(123)
plot(g, vertex.label.color = &amp;quot;black&amp;quot;, layout = layout_with_kk(g),
     vertex.size = 20) #,edge.width = E(g)$n/50)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-02-22-bbb-no-r_files/figure-html/unnamed-chunk-11-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Ok, me tornei o que mais temia, um crítico de BBB, mas vamos lá:&lt;/p&gt;
&lt;p&gt;Pela grafo, nota-se que existem alguns participantes mais isolados, como Viegas, Jaqueline e Wagner. Sobre Jaqueline, existe um viés causado pelo tempo, visto que ela já foi eliminada no momento em que vos escrevo.&lt;/p&gt;
&lt;p&gt;Outra observação é que de fato existem alguns participantes mais importantes na rede, como Paula, Lucas e Patrícia, visto que estão localizados mais ao centro do grafo.&lt;/p&gt;
&lt;p&gt;Uma medida que pode ser usada para mensurar a importância de cada pessoa na rede é o &lt;em&gt;betweenness&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;rev(sort(betweenness(g)))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##     Nayara     Kaysar    Jéssica     Caruso      Lucas     Gleici 
## 21.9166667 16.8333333 16.5000000 13.2500000  9.7500000  9.4166667 
##      Paula    Mahmoud      Diego      Breno  Jaqueline     Wagner 
##  7.0000000  4.0000000  2.5833333  2.3333333  1.0000000  0.3333333 
##     Viegas   Patrícia  Ana Paula  Ana Clara     Ayrton 
##  0.0000000  0.0000000  0.0000000  0.0000000  0.0000000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;De acordo com essa métrica, Nayara, Kaysar e Jéssica são os brothers com maior articulação entre todos.&lt;/p&gt;
&lt;p&gt;Outra medida é o &lt;em&gt;degree&lt;/em&gt;, que é simplesmente a contagem de outras pessoas com as quais cada participante está conectado:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;rev(sort(degree(g)))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##     Paula     Lucas    Kaysar    Gleici   Mahmoud    Caruso  Patrícia 
##        14        13        12        11        10        10         9 
##    Nayara   Jéssica     Diego     Breno Ana Paula Ana Clara    Wagner 
##         9         9         7         7         7         7         4 
## Jaqueline    Ayrton    Viegas 
##         3         3         1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uma aplicação muito interessante em ARS é a detecção de comunidade. Com o perdão por não entrar em detalhes sobre o funcionamento do algoritmo (isso eu deixo para meu curso), segue abaixo um exemplo disso:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;cluster &amp;lt;- fastgreedy.community(g)
cluster&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## IGRAPH clustering fast greedy, groups: 3, mod: 0.23
## + groups:
##   $`1`
##   [1] &amp;quot;Ayrton&amp;quot;    &amp;quot;Ana Clara&amp;quot; &amp;quot;Breno&amp;quot;     &amp;quot;Caruso&amp;quot;    &amp;quot;Gleici&amp;quot;   
##   [6] &amp;quot;Jaqueline&amp;quot; &amp;quot;Nayara&amp;quot;    &amp;quot;Viegas&amp;quot;    &amp;quot;Wagner&amp;quot;   
##   
##   $`2`
##   [1] &amp;quot;Ana Paula&amp;quot; &amp;quot;Diego&amp;quot;     &amp;quot;Kaysar&amp;quot;    &amp;quot;Mahmoud&amp;quot;   &amp;quot;Patrícia&amp;quot; 
##   [6] &amp;quot;Paula&amp;quot;    
##   
##   $`3`
##   [1] &amp;quot;Jéssica&amp;quot; &amp;quot;Lucas&amp;quot;  
##   + ... omitted several groups/vertices&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(123)
plot(cluster, g)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2018-02-22-bbb-no-r_files/figure-html/unnamed-chunk-14-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;O algoritmno encontrou três comunidades dentro da rede, sendo um deles formados apenas por Jéssica e Lucas.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusao&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Conclusão&lt;/h1&gt;
&lt;p&gt;Você talvez tenha percebido pelo meu tom jocoso no post que não sou lá muito fã de BBB. Contudo, isso não me impediu de pensar nessa aplicação como exemplo de demonstração de situações em que é possível aplicar a Análise de Redes Sociais. Como tudo na vida quando se sabe R, os limites dependem da sua criatividade :)&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Sobre gráficos e a mensagem que eles querem transmitir</title>
      <link>http://www.sillasgonzaga.com/post/sobre-graficos/</link>
      <pubDate>Sun, 26 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/sobre-graficos/</guid>
      <description>&lt;p&gt;Recentemente, quando estava no trabalhando lendo uma revista de negócios sobre o varejo, me deparei com o seguinte gráfico:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;knitr::include_graphics(&amp;quot;https://i.imgur.com/f1dh4uw.jpg&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://i.imgur.com/f1dh4uw.jpg&#34; width=&#34;600px&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Mesmo sem contexto, é possível perceber que essa visualização foi criada para mostrar a divergência de opiniões sobre a importância dada a fatores de compra pelos executivos de varejo e pelos consumidores. Imediatamente, eu pensei que o jornalista perdeu uma ótima oportunidade de representar melhor a informação desejada.&lt;/p&gt;
&lt;p&gt;Primeiramente, a ordem dos fatores no gráfico não segue uma ordem clara. Se o gráfico foi feito para enfatizar essa divergência de opiniões, teria sido melhor que os fatores fossem ordenados no gráfico pela diferença numérica entre executivos e consumidores. Veja que o ponto onde há maior disparidade, &lt;em&gt;Acesso a informações, venda e serviços&lt;/em&gt;, aparece no canto inferior direito, fazendo com que os olhos do leitor tenham de percorrer o gráfico quase que até seu final para extrair sua informação mais importante. Além disso, o fator de compra que apresenta maior uniformidade, &lt;em&gt;Informações em tempo real sobre inventário e disponibilidade de informações&lt;/em&gt;, aparece logo ao lado da que apresenta mais divergência.&lt;/p&gt;
&lt;p&gt;Outra oportunidade perdida é que não é dada uma ênfase ao que os consumidores, os responsáveis pela empresa existir, preferem. Como esta é uma revista voltada para o segmento de varejo, seu público-alvo são executivos do setor, que estão sedentos por saber o que os clientes pensam. Contudo, os fatores que os consumidores mais preferem, &lt;em&gt;Pagamento simples e fluido&lt;/em&gt; e &lt;em&gt;Acesso a informaçoes, venda e serviços&lt;/em&gt;, aparecem em ordem aleatória no gráfico (em quinto e em oitavo lugar, respectivamente).&lt;/p&gt;
&lt;p&gt;Por isso, este post é dedicado a propor uma nova versão dessa visualização de dados, de maneira que atenda aos pontos citados acima. Ou seja, um gráfico que:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ordene os fatores de acordo com o grau de divergência entre consumidores e executivos;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Destaque de alguma maneira os fatores que os consumidores consideram mais importantes que os executivos.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(tidyverse)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recriando o dataset do gráfico:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df &amp;lt;- tribble(
  ~fator, ~Executivos, ~Consumidores,
  &amp;quot;Experiência personalizada&amp;quot;, 43, 27,
  &amp;quot;Experiência fluida em todos os canais&amp;quot;, 42, 26,
  &amp;quot;Pagamento simples e fluido&amp;quot;, 41, 50,
  &amp;quot;Tecnologia em loja&amp;quot;, 38, 18,
  &amp;quot;Informações em tempo real sobre inventário e disponibilidade de informações&amp;quot;, 37, 38,
  &amp;quot;Plataformas mobile e aplicativos de compra&amp;quot;, 36, 19,
  &amp;quot;Habilidade de customizar produtos e serviços&amp;quot;, 35, 24,
  &amp;quot;Engajamento e presença em redes sociais&amp;quot;, 35, 14,
  &amp;quot;Pagamento digital e opções de câmbio&amp;quot;, 34, 31,
  &amp;quot;Acesso a informações, venda e serviços&amp;quot;, 31, 50,
  &amp;quot;Gamificação e experiência digital interativa&amp;quot;, 21, 9
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida, calculamos a diferença entre as duas colunas numéricas e usamos a funçao &lt;code&gt;forcats::fct_reorder()&lt;/code&gt; para ordenar os fatores de acordo com essa diferença.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df &amp;lt;- df %&amp;gt;% 
  mutate(diferenca = abs(Executivos - Consumidores),
         # Quebrar o string do fator em parágrafos e reordenar
         fator = str_wrap(fator, 40),
         fator = fct_reorder(fator, diferenca, .desc = FALSE)) %&amp;gt;% 
  # criar colunas para armazenar os valores maximo e minimo entre executivos e consumidores 
  rowwise() %&amp;gt;% 
   mutate(maior_percentual = max(Executivos, Consumidores),
          menor_percentual = min(Executivos, Consumidores))

# criar coluna para identificar &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O código desse gráfico é um pouco mais complexo do que o usual. Como eu queria que o label de cada ponto ficasse à esquerda no caso do menor número e à direita do maior, é necessário criar duas camadas de &lt;code&gt;geom_text()&lt;/code&gt; separadamente, uma para o valor mínimo de cada fator e outra para o valor máximo, sendo que cada possui um valor diferente de &lt;code&gt;hjust&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df.long &amp;lt;- df %&amp;gt;% 
  # transformar para formato tidy (long)
  gather(fonte_opiniao, valor, 2:3)

p &amp;lt;- ggplot(df.long, aes(x = valor, y = fator)) +
  geom_point(aes(color = fonte_opiniao), size = 3) +
  # ajustar pontos
  scale_x_continuous(limits = c(0, 55), breaks = seq(0, 50, 10)) +
  # mudar aparencia. isso é opcional.
  theme_minimal() + 
  # adicionar manualmente o label do valor que cada ponto representa
  geom_text(data = df, aes(x = maior_percentual, label = maior_percentual), hjust = -0.3) +
  geom_text(data = df, aes(x = menor_percentual, label = menor_percentual), hjust = 1.4) +
  # mudar titulos dos eixos e do grafico
  labs(x = &amp;quot;%&amp;quot;, y = NULL, color = NULL, title = &amp;quot;Quais fatores influenciam a compra?&amp;quot;) +
  theme(legend.position = &amp;quot;bottom&amp;quot;)

p&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-11-26-sobre-graficos_files/figure-html/unnamed-chunk-3-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Com este gráfico, creio que atendemos ao primeiro ponto. Veja que, além dos objetivos listados anteriormente, o gráfico apresenta outras melhorias em relação ao original. Uma delas é que é possível descobrir muito mais facilmente quais são os fatores que os consumidores menos se lembram (&lt;em&gt;Gamificação&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Para atender à segunda melhoria proposta, a de destacar de alguma maneira os fatores considerados mais importantes pelos consumidores, decidi usar o seguinte método: plotar um segmento ligando os pontos em cada fator, sendo que, nos fatores de compra que foram mais lembrados por consumidores do que por executivos, o segmento fosse mais destacado (ou menos transparente) no gráfico.&lt;/p&gt;
&lt;p&gt;Para isso, vamos criar uma coluna onde o valor do &lt;code&gt;alpha&lt;/code&gt; do segmento, que correspondência ao seu nível de opacidade, seja definido manualmente:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# criar coluna para identificar quem da mais importancia ao fator
df &amp;lt;- df %&amp;gt;% 
  mutate(alpha_segmento = if_else(Executivos - Consumidores &amp;gt; 0, 0.2, 1))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vamos então adicionar esse novo elemento no gráfico.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p + 
  # para não fazer que o segmento fique em cima do ponto, eu removo 0,3 de cada lado dele
  geom_segment(data = df, aes(x = maior_percentual-0.3, xend = menor_percentual+0.3,
                              y = fator, yend = fator,
                              alpha = alpha_segmento),
               inherit.aes = FALSE, show.legend = FALSE) +
  scale_alpha_identity()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-11-26-sobre-graficos_files/figure-html/unnamed-chunk-5-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Acredito que, com essa visualização, as mensagens que o gráfico da revista foi criado para mostrar são transmitidas mais claramente.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Anunciando o lançamento de literaturaBR</title>
      <link>http://www.sillasgonzaga.com/post/literaturaBR-01/</link>
      <pubDate>Tue, 21 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/literaturaBR-01/</guid>
      <description>&lt;div id=&#34;paixao-por-dados-de-cara-nova&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Paixão por Dados de cara nova!&lt;/h2&gt;
&lt;p&gt;O blog está de cara nova! O &lt;a href=&#34;www.sillasgonzaga.github.io&#34;&gt;endereço antigo&lt;/a&gt; do blog começou a apresentar alguns bugs bem chatos, então tomei a decisão de finalmente migrar para uma nova plataforma, utilizando o pacote &lt;code&gt;blogdown&lt;/code&gt;, a mesma que o pessoal do Curso-R usa no site deles. Para comemorar essa migração, anuncio o lançamento do meu terceiro pacote R: o &lt;code&gt;literaturaBR&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;literaturabr-o-mais-novo-pacote-da-comunidade-r-brasil&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;literaturaBR, o mais novo pacote da comunidade R Brasil&lt;/h2&gt;
&lt;p&gt;Após lançar o pacote &lt;a href=&#34;https://github.com/sillasgonzaga/lexiconPT&#34;&gt;&lt;code&gt;lexiconPT&lt;/code&gt;&lt;/a&gt;, senti que a carência de datasets textuais na língua portuguesa poderia restringir seu potencial de alcance de desenvolvedores e cientistas de dados interessados em usar os léxicos para fazer análise de sentimento. Apesar de ter feito um post mostrando seu uso em &lt;a href=&#34;http://www.sillasgonzaga.com/post/o-sensacionalista-e-text-mining/&#34;&gt;dados obtidos do Facebook&lt;/a&gt;, eu admito que é complicado ter que fazer web scraping de algum site toda vez que se deseja praticar ou ensinar mineração de texto com textos em Português.&lt;/p&gt;
&lt;p&gt;Esse problema me inspirou a desenvolver mais um pacote R para facilitar pequenas demonstrações de Text Mining. Assim como a Julia Silge criou o &lt;a href=&#34;https://github.com/juliasilge/janeaustenr&#34;&gt;&lt;code&gt;janeaustenr&lt;/code&gt;&lt;/a&gt; com livros clássicos da Jane Austen, eu criei o &lt;a href=&#34;https://github.com/sillasgonzaga/literaturaBR&#34;&gt;&lt;code&gt;literaturaBR&lt;/code&gt;&lt;/a&gt;, um &lt;em&gt;data package&lt;/em&gt; criado para disponibilizar livros clássicos da literatura brasileira já prontos para serem importados e manuseados no R. Para saber quais livros estão disponíveis na versão atual do pacote e outras informações úteis, visite seu repositório.&lt;/p&gt;
&lt;p&gt;Este post se destina a apresentar algumas exemplos simples de tarefas de Text Mining, como:&lt;br /&gt;
* Análise de Sentimento;&lt;br /&gt;
* Complexidade Léxica;&lt;br /&gt;
* Analise de ocorrência de palavras em específico.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;introducao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Introdução&lt;/h2&gt;
&lt;p&gt;Os pacotes usados neste post são:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(literaturaBR) # meio obvio
library(tidytext) # excelente pacote de text mining
library(tidyverse) # &amp;lt;3
library(stringr) # indispensavel para manipulacao de texto
library(quanteda) # otimas funcoes para analise quantitativa de texto
library(qdap) # similar ao quanteda, embo ra eu nao me lembre exatamente se eu o uso neste post
library(forcats) # manipulacao de fatores
library(ggthemes) # temas para o ggplot2
library(lexiconPT)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vamos então importar os datasets presentes no &lt;code&gt;literaturaBR&lt;/code&gt; na data de hoje e os transformar em um dataset só:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;data(&amp;quot;memorias_de_um_sargento_de_milicias&amp;quot;)
data(&amp;quot;memorias_postumas_bras_cubas&amp;quot;)
data(&amp;quot;alienista&amp;quot;)
data(&amp;quot;escrava_isaura&amp;quot;)
data(&amp;quot;ateneu&amp;quot;)

df &amp;lt;- bind_rows(memorias_de_um_sargento_de_milicias,
                memorias_postumas_bras_cubas,
                alienista,
                escrava_isaura,
                ateneu)


# Olhando a estrutura do dataframe
glimpse(df)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 5,149
## Variables: 5
## $ book_name        &amp;lt;chr&amp;gt; &amp;quot;Memórias de um Sargento de Milícias&amp;quot;, &amp;quot;Memór...
## $ chapter_name     &amp;lt;chr&amp;gt; &amp;quot;Capítulo 1 - Origem, nascimento e batismo&amp;quot;, ...
## $ url              &amp;lt;chr&amp;gt; &amp;quot;https://pt.wikisource.org/wiki/Mem%C3%B3rias...
## $ paragraph_number &amp;lt;int&amp;gt; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, ...
## $ text             &amp;lt;chr&amp;gt; &amp;quot;Era no tempo do rei.&amp;quot;, &amp;quot;Uma das quatro esqui...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Todos&lt;/strong&gt; os datasets fornecidos pelo &lt;code&gt;literaturaBR&lt;/code&gt; possuem a mesma estrutura, onde cada linha corresponde a um parágrafo de um livro e contêm 5 variáveis:&lt;br /&gt;
* &lt;code&gt;book_name&lt;/code&gt;: Nome original do livro;&lt;br /&gt;
* &lt;code&gt;chapter_name&lt;/code&gt;: Nome original do capítulo do livro do parágrafo;&lt;br /&gt;
* &lt;code&gt;url&lt;/code&gt;: Link para artigo do Wikisouce de onde o capítulo do parágrafo foi extraído;&lt;br /&gt;
* &lt;code&gt;paragraph_number&lt;/code&gt;: Ordem do parágrafo em seu capítulo;&lt;br /&gt;
* &lt;code&gt;text&lt;/code&gt;: Texto do parágrafo. Contem acentos e pontuação.&lt;/p&gt;
&lt;p&gt;Um rápido adendo: os nomes das colunas está em inglês porque só tomei a decisão de escrever toda a documentação do pacote em Português meio tardiamente.&lt;/p&gt;
&lt;div id=&#34;analise-basica&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Análise básica&lt;/h3&gt;
&lt;p&gt;Para usar (parte) das funções do pacote &lt;code&gt;quanteda&lt;/code&gt;, precisamos converter o dataframe dos livros em um objeto do tipo &lt;code&gt;corpus&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_corpus &amp;lt;- df %&amp;gt;% 
  # agrupar por livro
  group_by(book_name) %&amp;gt;% 
  # formatar o dataframe para que so tenha uma linha por livro
  summarise(text = paste0(text, sep = &amp;quot;&amp;quot;, collapse = &amp;quot;. &amp;quot;))

dim(df_corpus)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 5 2&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;meu_corpus &amp;lt;- quanteda::corpus(df_corpus$text, docnames = df_corpus$book_name)
summary(meu_corpus)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Corpus consisting of 5 documents:
## 
##                                 Text Types Tokens Sentences
##                     A escrava Isaura  9349  64929      1751
##  Memórias de um Sargento de Milícias  8703  71391      2287
##      Memórias Póstumas de Brás Cubas 11058  74223      3199
##                          O Alienista  4387  19977       770
##                             O Ateneu 15341  73063      3551
## 
## Source:  /home/sillas/R/Projetos/paixaopordados-blogdown/content/post/* on x86_64 by sillas
## Created: Wed Nov 22 20:34:35 2017
## Notes:&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como vemos, a função &lt;code&gt;quanteda::corpus()&lt;/code&gt; identificou a quantidade de &lt;em&gt;Types&lt;/em&gt; (número de palavras distintas em um corpus), &lt;em&gt;Tokens&lt;/em&gt; (número total de palavras em um corpus) e &lt;em&gt;Sentences&lt;/em&gt; (frases) em cada livro.&lt;/p&gt;
&lt;p&gt;Vamos então criar uma &lt;em&gt;document-feature matrix&lt;/em&gt; a partir desse corpus criado, tomando o cuidade de remover pontuações e stopwords:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;corpus_dfm &amp;lt;- dfm(meu_corpus, remove_punct = TRUE,
                  remove = quanteda::stopwords(&amp;quot;portuguese&amp;quot;),
                  groups = df_corpus$book_name)

# Analisando as 15 palavras mais comuns no geral por livro
dfm_sort(corpus_dfm)[, 1:15]&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Document-feature matrix of: 5 documents, 15 features (5.33% sparse).
## 5 x 15 sparse Matrix of class &amp;quot;dfm&amp;quot;
##                                      features
## docs                                    é casa tempo ainda tudo bem todos
##   A escrava Isaura                    424   98    81   121   78 175    78
##   Memórias de um Sargento de Milícias 305  193   207   131  192 126   139
##   Memórias Póstumas de Brás Cubas     492  107   108    89   98  65    69
##   O Alienista                          88  108    20    29   26  10    37
##   O Ateneu                            199   51    56    97   56  56    91
##                                      features
## docs                                  tão ser havia   d leonardo coisa
##   A escrava Isaura                    137  96    56  20        0    35
##   Memórias de um Sargento de Milícias  63  75   153 194      366   128
##   Memórias Póstumas de Brás Cubas      95  97    53  83        0   140
##   O Alienista                          31  25     7  44        0    23
##   O Ateneu                             47  78   101  26        0    38
##                                      features
## docs                                  disse dia
##   A escrava Isaura                       67  41
##   Memórias de um Sargento de Milícias   119 128
##   Memórias Póstumas de Brás Cubas       130  89
##   O Alienista                            30  21
##   O Ateneu                               11  77&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A função &lt;code&gt;dfm_sort()&lt;/code&gt; retorna a ocorrência de cada palavra (também chamado de token ou feature) em cada livro. Para pesquisar a ocorrência de alguma palavra específica nos documentos, use a função &lt;code&gt;dfm_select()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# ocorrencias da palavra amor
dfm_select(corpus_dfm, &amp;quot;amor&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Document-feature matrix of: 5 documents, 1 feature (0% sparse).
## 5 x 1 sparse Matrix of class &amp;quot;dfm&amp;quot;
##                                      features
## docs                                  amor
##   A escrava Isaura                      71
##   Memórias de um Sargento de Milícias   30
##   Memórias Póstumas de Brás Cubas       53
##   O Alienista                            3
##   O Ateneu                              45&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Curioso para saber o contexto em que essa palavra aparece? Você pode usar a função &lt;code&gt;kwic()&lt;/code&gt; para isso:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# usar a função head() para o output nao ficar mt grande
kwic(meu_corpus, &amp;quot;amor&amp;quot;) %&amp;gt;% head()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##                                                              
##  [A escrava Isaura, 2011]    , bem pode conquistar o | amor |
##  [A escrava Isaura, 5090]    não se havia casado por | amor |
##  [A escrava Isaura, 5173]     o mais cego e violento | amor |
##  [A escrava Isaura, 6555] pudesse obter também o teu | amor |
##  [A escrava Isaura, 7215]          Ora, senhor, pelo | amor |
##  [A escrava Isaura, 7686]  disputar com o senhor por | amor |
##                         
##  de algum guapo mocetão,
##  , sentimento esse a que
##  , que de dia em        
##  !... És                
##  de Deus!..             
##  de uma escrava..&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para saber as palavras mais usadas, dentre os vários métodos possíveis para isso, pode-se usar a função &lt;code&gt;topfeatures()&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;topfeatures(corpus_dfm, groups = df_corpus$book_name)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## $`A escrava Isaura`
##       é  isaura  senhor leôncio  álvaro escrava     bem     tão     pai 
##     424     344     243     205     188     180     175     137     127 
##   ainda 
##     121 
## 
## $`Memórias de um Sargento de Milícias`
## leonardo        é    maria  comadre    tempo    porém        d     casa 
##      366      305      231      209      207      205      194      193 
##     tudo    major 
##      192      179 
## 
## $`Memórias Póstumas de Brás Cubas`
##        é virgília    coisa    olhos    disse     nada    outro    outra 
##      492      199      140      138      130      125      122      116 
##     vida   porque 
##      116      113 
## 
## $`O Alienista`
##      casa alienista         é     verde bacamarte  barbeiro    câmara 
##       108       106        88        77        59        54        52 
##   itaguaí     simão  evarista 
##        49        49        45 
## 
## $`O Ateneu`
##         é     sobre aristarco   diretor     havia     ainda    ateneu 
##       199       167       148       104       101        97        93 
##     todos       ser      dois 
##        91        78        77&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;analise-comparativa-entre-os-livros&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Análise comparativa entre os livros&lt;/h3&gt;
&lt;p&gt;Algo interessante a se fazer é quantificar a similaridade e a dissimilaridade ou distância entre os livros. As funções &lt;code&gt;textstat_simil&lt;/code&gt; e &lt;code&gt;textstat_dist&lt;/code&gt; implementam diversas técnicas e algoritmos para isso. Sugiro ler a documentação completa das funções e ler as referências indicadas para conhecer melhor os métodos de cálculo.&lt;/p&gt;
&lt;p&gt;Vamos então calcular a similaridade entre os livros presentes no pacote:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# normalizar os livros pelo seu tamanho
corpus_dfm_norm &amp;lt;- dfm_weight(corpus_dfm, &amp;quot;relfreq&amp;quot;)
corpus_simil &amp;lt;- textstat_simil(corpus_dfm_norm, method = &amp;quot;correlation&amp;quot;,
                               margin = &amp;quot;documents&amp;quot;, upper = TRUE,
                               diag = FALSE)
# ver os resultados individualmente para cada livro
round(corpus_simil, 3)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##                                     A escrava Isaura
## A escrava Isaura                                    
## Memórias de um Sargento de Milícias            0.524
## Memórias Póstumas de Brás Cubas                0.625
## O Alienista                                    0.431
## O Ateneu                                       0.512
##                                     Memórias de um Sargento de Milícias
## A escrava Isaura                                                  0.524
## Memórias de um Sargento de Milícias                                    
## Memórias Póstumas de Brás Cubas                                   0.642
## O Alienista                                                       0.502
## O Ateneu                                                          0.550
##                                     Memórias Póstumas de Brás Cubas
## A escrava Isaura                                              0.625
## Memórias de um Sargento de Milícias                           0.642
## Memórias Póstumas de Brás Cubas                                    
## O Alienista                                                   0.585
## O Ateneu                                                      0.631
##                                     O Alienista O Ateneu
## A escrava Isaura                          0.431    0.512
## Memórias de um Sargento de Milícias       0.502    0.550
## Memórias Póstumas de Brás Cubas           0.585    0.631
## O Alienista                                        0.450
## O Ateneu                                  0.450&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alguns resultados são bem interessantes. O Alienista é mais diferente de todos, tendo uma correlação superior a 0,51 apenas com o livro Memórias Póstumas de Brás Cubas, coincidentemente ou não ambos do mesmo autor. A maior correlação pertence aos livros Memórias Póstumas de Brás Cubas e Memórias de um Sargento de Milícias.&lt;/p&gt;
&lt;p&gt;Passemos então para a análise da dissimilaridade ou distância entre os livros, com o auxílio de um dendograma:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;corpus_dist &amp;lt;- textstat_dist(corpus_dfm_norm, method = &amp;quot;euclidean&amp;quot;,
                               margin = &amp;quot;documents&amp;quot;, upper = TRUE,
                               diag = FALSE)
# ver os resultados individualmente para cada livro
plot(hclust(corpus_dist))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-11-21-literaturaBR-01_files/figure-html/dist-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;analise-de-sentimento&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Análise de Sentimento&lt;/h3&gt;
&lt;p&gt;Como já publiquei no blog um post sobre Análise de Sentimento, não vou me alongar em repetir conceitos sobre o tema. Segue o código comentado:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Criar um dataframe em que cada linha corresponda a uma unica palavra
df.token &amp;lt;- df %&amp;gt;%
  unnest_tokens(term, text)

glimpse(df.token)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 252,299
## Variables: 5
## $ book_name        &amp;lt;chr&amp;gt; &amp;quot;Memórias de um Sargento de Milícias&amp;quot;, &amp;quot;Memór...
## $ chapter_name     &amp;lt;chr&amp;gt; &amp;quot;Capítulo 1 - Origem, nascimento e batismo&amp;quot;, ...
## $ url              &amp;lt;chr&amp;gt; &amp;quot;https://pt.wikisource.org/wiki/Mem%C3%B3rias...
## $ paragraph_number &amp;lt;int&amp;gt; 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...
## $ term             &amp;lt;chr&amp;gt; &amp;quot;era&amp;quot;, &amp;quot;no&amp;quot;, &amp;quot;tempo&amp;quot;, &amp;quot;do&amp;quot;, &amp;quot;rei&amp;quot;, &amp;quot;uma&amp;quot;, &amp;quot;da...&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# importar lexico de sentimentos
data(&amp;quot;oplexicon_v3.0&amp;quot;)
df.token &amp;lt;- df.token %&amp;gt;%
  inner_join(oplexicon_v3.0, by = &amp;quot;term&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Um pergunta muito interessante a se fazer é se o sentimento varia ao longo dos capítulos dos livros. Os livros ficam mais (ou menos) positivos a medida em que se aproximam do final?&lt;/p&gt;
&lt;p&gt;Para isso, primeiro precisamos normalizar os livros, visto que eles apresentam tamanhos e quantidades de capítulos diferentes.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# extrair capitulos de cada livro
df_chapter_number &amp;lt;- df.token %&amp;gt;%
  distinct(book_name, chapter_name) %&amp;gt;%
  group_by(book_name) %&amp;gt;%
  # normalizar capitulo de acordo com sua posicao no livro
  mutate(chapter_number_norm = row_number()/max(row_number()))

glimpse(df_chapter_number)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 252
## Variables: 3
## $ book_name           &amp;lt;chr&amp;gt; &amp;quot;Memórias de um Sargento de Milícias&amp;quot;, &amp;quot;Me...
## $ chapter_name        &amp;lt;chr&amp;gt; &amp;quot;Capítulo 1 - Origem, nascimento e batismo...
## $ chapter_number_norm &amp;lt;dbl&amp;gt; 0.02083333, 0.04166667, 0.06250000, 0.0833...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora calculamos o sentimento de cada capítulo, que corresponde à soma da polaridade de suas palavras:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df.sentiment &amp;lt;- df.token %&amp;gt;%
  # calcular sentimento por capitulo
  group_by(book_name, chapter_name) %&amp;gt;%
  summarise(polarity = sum(polarity, na.rm = TRUE)) %&amp;gt;%
  ungroup() %&amp;gt;%
  # retornar posicao relativa (ou normalizada) do capitulo de cada livro
  left_join(df_chapter_number) %&amp;gt;%
  arrange(book_name, chapter_number_norm)

# grafico
df.sentiment %&amp;gt;%
  ggplot(aes(x = chapter_number_norm, y = polarity)) +
    geom_line() +
    facet_wrap(~ book_name, ncol = 5, labeller = label_wrap_gen(20)) +
    labs(x = &amp;quot;Posição relativa no livro&amp;quot;, y = &amp;quot;Sentimento&amp;quot;) +
    theme_bw()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-11-21-literaturaBR-01_files/figure-html/sentimento%20por%20capitulo-1.png&#34; width=&#34;768&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Os resultados são muito interessantes: De acordo com esse método, &lt;strong&gt;A Escrava Isaura&lt;/strong&gt; e &lt;strong&gt;O Ateneu&lt;/strong&gt; são verdadeiras montanhas-russas de emoções, enquanto que livros os “dois Memórias” são mais estáveis. &lt;strong&gt;O Alienista&lt;/strong&gt; apresenta um sentimento que tem uma tendência decrescente até a metade e crescente após ela.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;complexidade-lexica-e-ocorrencia-de-palavras&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Complexidade léxica e ocorrência de palavras&lt;/h3&gt;
&lt;p&gt;A função &lt;code&gt;textstat_lexdiv&lt;/code&gt; traz várias métricas de complexidade e diversidade léxicas. Novamente, recomendo a leitura de sua documentação para conhecer as métricas disponíveis.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# aplicando a funcao no objeto sem stopwords e pontuação
lexdiv &amp;lt;- textstat_lexdiv(corpus_dfm, measure = &amp;quot;TTR&amp;quot;)
lexdiv&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##                    A escrava Isaura Memórias de um Sargento de Milícias 
##                           0.3062233                           0.2517707 
##     Memórias Póstumas de Brás Cubas                         O Alienista 
##                           0.3139269                           0.4291334 
##                            O Ateneu 
##                           0.4134223&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#grafico
lexdiv %&amp;gt;% 
  as.data.frame() %&amp;gt;% 
  magrittr::set_colnames(&amp;quot;TTR&amp;quot;) %&amp;gt;% 
  tibble::rownames_to_column(&amp;quot;livro&amp;quot;) %&amp;gt;% 
  mutate(livro = forcats::fct_reorder(livro, TTR)) %&amp;gt;% 
  ggplot(aes(x = livro, y = TTR)) + 
    geom_col(fill = &amp;quot;cadetblue4&amp;quot;) +
    coord_flip() + 
    labs(x = NULL, y = &amp;quot;TTR&amp;quot;) +
    theme_minimal()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-11-21-literaturaBR-01_files/figure-html/plot%20ttr-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;O livro que apresenta a maior diversidade léxica, de acordo com a métrica &lt;em&gt;Type-Token Ratio&lt;/em&gt; (TTR), é &lt;strong&gt;O Alienista&lt;/strong&gt;. &lt;strong&gt;Memórias de um Sargento de Milícias&lt;/strong&gt; vem bem atrás dos demais.&lt;/p&gt;
&lt;p&gt;De todas os gráficos que apresentei neste post, o mais legal vem a seguir. É possível plotar a ocorrência de uma determinada palavra ao longo dos livros analisados. Por exemplo, a ocorrência da palavra amor é uniformemente distribuída nos livros?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;kwic(meu_corpus, &amp;quot;amor&amp;quot;) %&amp;gt;% textplot_xray(scale = &amp;quot;relative&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-11-21-literaturaBR-01_files/figure-html/plot%20xray-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Com o gráfico acima, é possível ver que a palavra amor aparece, no livro Ateneu, muito mais frequentemente na primeira metade do que na segunda. Nos demais livros, a ocorrência da palavra é mais uniforme.&lt;/p&gt;
&lt;p&gt;Outro exemplo interessante surge com a busca da palavra fogo. Quem já leu O Ateneu já consegue imaginar como serão os resultados:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;kwic(meu_corpus, &amp;quot;fogo&amp;quot;) %&amp;gt;% textplot_xray(scale = &amp;quot;relative&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-11-21-literaturaBR-01_files/figure-html/unnamed-chunk-1-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Conclusão&lt;/h2&gt;
&lt;p&gt;Eu realmente torço para que o &lt;code&gt;literaturaBR&lt;/code&gt; e o &lt;code&gt;lexiconPT&lt;/code&gt; atinjam seus potenciais e sejam dois grandes marcos em pesquisas em Text Mining com textos em português. Toda sugestão ou pedido de melhorias serão muitíssimos bem-vindos.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Mineração de textos em notícias de G1: O que diferencia notícias sobre Rio de Janeiro e São Paulo?</title>
      <link>http://www.sillasgonzaga.com/post/analise-g1-01/</link>
      <pubDate>Sun, 15 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/analise-g1-01/</guid>
      <description>&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(rvest)
library(tidyverse)
library(magrittr)
library(stringr)
library(Rfacebook)
library(tidytext)
library(tm)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;motivacao-para-o-post&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Motivação para o post&lt;/h2&gt;
&lt;p&gt;Apesar de hoje em dia eu morar no Rio de Janeiro, morei e vivi (quase) a vida toda em Aracaju, a capital do menor estado do Brasil. Devido à irrelevância que a cidade tem (desculpa mas é verdade) no cenário político e econômico do país, era (e ainda é) muito raro ver qualquer notícia em um veículo de audiência nacional (como o Jornal Nacional ou a homepage do G1 ou Estadão) relacionada a Aracaju ou a Sergipe que não seja desgraça ou por um acontecimento inusitadamente ruim. Voltei a notar isso recentemente, quando saiu na homepage do G1 uma notícia de Sergipe sobre o &lt;a href=&#34;https://g1.globo.com/se/sergipe/noticia/camarote-desaba-durante-show-de-ivete-sangalo-em-aracaju.ghtml?utm_source=facebook&amp;amp;utm_medium=social&amp;amp;utm_campaign=g1&#34;&gt;desabamento de um camarote durante a Odonto Fantasy&lt;/a&gt;, uma das maiores festas a fantasia do Brasil.&lt;/p&gt;
&lt;p&gt;Como bom entusiasta por Data Science, decidi não me ater ao senso comum de que Sergipe só tem destaque nacional por motivos de desgraça e sujei as mãos: é possível testar a hipótese de que certos temas são mais associados a algumas cidades que outras? O que destaca as notícias sobre estados menores que saem na homepage do G1 comparadas com as de estados maiores?&lt;/p&gt;
&lt;p&gt;Consigo pensar em pelo menos dois (possivelmente existem mais) métodos para realizar essa análise: além da análise de frequência mencionada acima (a palavra X está mais associada a cidade Y1 do que a cidade Y2), poderia ser aplicada uma análise de sentimento para distinguir o sentimento de notícias relacionadas a Aracaju em comparação com o de cidades maiores. Neste post, o foco será na primeira alternativa.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;coleta-dos-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Coleta dos dados&lt;/h2&gt;
&lt;p&gt;Vejam que fui enfático sobre a notícia em si ter saído na homepage do G1 ou não. Ou seja, não basta ela ter saído na página local do estado (como a de &lt;a href=&#34;http://g1.globo.com/se/&#34;&gt;Sergipe&lt;/a&gt;). O primeiro problema é então ter um histórico de notícias que saíram na primeira página, o que não existe em nenhum lugar público na Internet. Não bastaria entrar na página de cada estado e coletar as notícias. Nesta análise, é fundamental ter a certeza de que pessoas de todo o Brasil puderam ter acesso fácil ao artigo.&lt;/p&gt;
&lt;p&gt;Para resolver o problema, utilizei o pressuposto de que, se a notícia foi postada na &lt;a href=&#34;https://www.facebook.com/g1&#34;&gt;página do G1 no Facebook&lt;/a&gt;, é porque ela saiu na home. Não sei o quanto que isso é verdade, mas acredito que seja uma hipótese válida.&lt;/p&gt;
&lt;p&gt;Para extrair os links postados na página do G1 no Facebook, utilizamos o pacote &lt;code&gt;Rfacebook&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;token &amp;lt;- readRDS(&amp;quot;/home/sillas/R/facebook_token.Rds&amp;quot;)
g1 &amp;lt;- Rfacebook::getPage(&amp;quot;180562885329138&amp;quot;, token = token, n = 5000)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O intervalo pesquisado é:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;range(sort(g1$created_time))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;2006-09-18T07:00:00+0000&amp;quot; &amp;quot;2017-10-14T20:46:01+0000&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;g1_links &amp;lt;- g1$link&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nem todos os links postados pela página redirecionam ao G1: alguns são de vídeos postados no próprio Facebook. Por isso, é necessário limpar os dados:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# remover links de videos do facebook
g1_links &amp;lt;- g1_links[!str_detect(g1_links, &amp;quot;www.facebook.com&amp;quot;)]

# ver links mais comuns
str_replace_all(g1_links, &amp;quot;http://|https://&amp;quot;, &amp;quot;&amp;quot;) %&amp;gt;% str_replace_all(&amp;quot;/.*&amp;quot;, &amp;quot;&amp;quot;) %&amp;gt;% table()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## .
##                 bit.ly   fantastico.globo.com           g1.globo.com 
##                      8                      1                    717 
##                 glo.bo globoesporte.globo.com        media.giphy.com 
##                    895                      8                      1 
##          s2.glbimg.com 
##                      1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ainda existem alguns links meio nebulosos. Vamos nos ater aos que são do G1 (&lt;em&gt;g1.globo.com&lt;/em&gt;) e do encurtador de URLs da Globo.com (&lt;em&gt;glo.bo&lt;/em&gt;). Para estes últimos, como precisamos extrair o estado da notícia da URL (de &lt;a href=&#34;https://g1.globo.com/se/sergipe/noticia/camarote-desaba-durante-show-de-ivete-sangalo-em-aracaju.ghtml&#34; class=&#34;uri&#34;&gt;https://g1.globo.com/se/sergipe/noticia/camarote-desaba-durante-show-de-ivete-sangalo-em-aracaju.ghtml&lt;/a&gt; precisamos extrair &lt;strong&gt;“se”&lt;/strong&gt;, a sigla de Sergipe), uso uma função postada no meu Gist para converter uma URL encurtada em normal:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# manter apenas links glo.bo e g1.globo.com
g1_links &amp;lt;- g1_links[str_detect(g1_links, &amp;quot;g1.globo.com|glo.bo&amp;quot;) &amp;amp; !is.na(g1_links)]
# carregar funcao para transformar url encurtada
devtools::source_gist(&amp;quot;2e92f880811f460c7edd0a622563a17a&amp;quot;, filename = &amp;quot;getLongURL.R&amp;quot;)
# converter links glo.bo para urls inteiras
ind &amp;lt;- str_which(g1_links, &amp;quot;glo.bo&amp;quot;)
getLongURL.curl.possibly &amp;lt;- possibly(getLongURL.curl, otherwise = NA)
novos_links &amp;lt;- g1_links[ind] %&amp;gt;% map_chr(getLongURL.curl.possibly)
g1_links[ind] &amp;lt;- novos_links
# manter apenas links glo.bo e g1.globo.com (para remover links de outros sites da globo, como o Globoesporte.com)
g1_links &amp;lt;- g1_links[str_detect(g1_links, &amp;quot;g1.globo.com|glo.bo&amp;quot;) &amp;amp; !is.na(g1_links)]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tendo os links corrigidos, podemos então criar uma função que extraia o corpo da notícia do link. Como o foco deste post não é o Web Scraping em si e sim os dados resultantes dele, não vou descrever muito o que a função abaixo faz. Em caso de dúvidas, escreva nos comentários do post ou entre em contato comigo que eu responderei com grande prazer.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# criar função
extrair_g1 &amp;lt;- function(url_g1){
  # exemplo de url:
  # &amp;quot;https://g1.globo.com/rio-de-janeiro/noticia/imagens-mostram-acao-de-criminosos-durante-assalto-na-zona-norte-do-rio.ghtml&amp;quot;
  if (!str_detect(url_g1, &amp;quot;glo.bo&amp;quot;) &amp;amp; !str_detect(url_g1, &amp;quot;g1.globo&amp;quot;)){
    warning(paste0(&amp;quot;Link inválido: &amp;quot;, url_g1))
    return(NA)
  }
  
  x &amp;lt;- url_g1 %&amp;gt;% read_html() 
  # extrair sessao da noticia
  css_nome_caderno &amp;lt;- &amp;quot;.header-editoria--link&amp;quot;
  caderno &amp;lt;- x %&amp;gt;% html_nodes(css_nome_caderno) %&amp;gt;% html_text()
  if (length(caderno) == 0) caderno &amp;lt;- NA
  # extrair corpo da noticia
  css_corpo_a &amp;lt;- &amp;quot;.content-text__container&amp;quot;
  css_corpo_b &amp;lt;- &amp;quot;#materia-letra p&amp;quot;
  css_corpo_c &amp;lt;- &amp;quot;.post-content p&amp;quot;
  
  corpo_noticia &amp;lt;- x %&amp;gt;% 
    html_nodes(css_corpo_a) %&amp;gt;% 
    html_text()
  # se o css selector nao funcionou, use outro:
  if (length(corpo_noticia) == 0) {
    corpo_noticia &amp;lt;- x %&amp;gt;% 
      html_nodes(css_corpo_b) %&amp;gt;% 
      html_text()
  } 
  
  if (length(corpo_noticia) == 0) {
    corpo_noticia &amp;lt;- x %&amp;gt;% 
      html_nodes(css_corpo_c) %&amp;gt;% 
      html_text()
  } 
  # colapsar corpo da noticia em um vetor de elemento unico
  corpo_noticia &amp;lt;- paste0(corpo_noticia, collapse = &amp;quot;. &amp;quot;)
  
  
  tibble(url = url_g1, caderno = caderno, corpo_noticia = corpo_noticia)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# agora sim extrair dados do g1:
df_g1 &amp;lt;- g1_links %&amp;gt;% map_df(extrair_g1)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# extrair caderno da url da noticia
df_g1$documento  &amp;lt;- df_g1$url %&amp;gt;% 
  str_replace_all(&amp;quot;https&amp;quot;, &amp;quot;http&amp;quot;) %&amp;gt;% 
  str_replace_all(&amp;quot;http://g1.globo.com/&amp;quot;, &amp;quot;&amp;quot;) %&amp;gt;% 
  str_replace_all(&amp;quot;/.*&amp;quot;, &amp;quot;&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uma amostra mínima dos dados coletados:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;str(df_g1[1,])&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Classes &amp;#39;tbl_df&amp;#39;, &amp;#39;tbl&amp;#39; and &amp;#39;data.frame&amp;#39;:    1 obs. of  4 variables:
##  $ url          : chr &amp;quot;https://g1.globo.com/politica/operacao-lava-jato/noticia/occhi-tinha-meta-mensal-de-propina-para-distribuir-a-p&amp;quot;| __truncated__
##  $ caderno      : chr &amp;quot;Operação Lava Jato&amp;quot;
##  $ corpo_noticia: chr &amp;quot; O doleiro Lúcio Funaro afirmou em seu acordo de delação premiada com a Procuradoria Geral da República (PGR) q&amp;quot;| __truncated__
##  $ documento    : chr &amp;quot;politica&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora podemos partir para a análise. Mas antes, um pouco de contexto teórico:&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;tf-idf&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;tf-idf&lt;/h2&gt;
&lt;p&gt;O &lt;strong&gt;Fator de Frequência Inversa no Documento&lt;/strong&gt; ou &lt;em&gt;term’s inverse document frequency&lt;/em&gt; (tf-idf) é uma técnica de mineração de texto que calcula a frequência de um determinado elemento textual (como uma palavra ou um n-gram) em um documento, diminuindo o peso de palavras comumente usadas em vários contextos diferentes (como “a”, “o” e “de”) e aumentando o peso de palavras que não são usadas em outros documentos da coleção.&lt;/p&gt;
&lt;p&gt;No glossário de mineração de texto, documento nada mais é do que uma coleção que categoriza um conjunto de textos. Pode ser um livro (inteiro ou apenas um ou mais capítulos), a letra de uma música, um poema, uma notícia, um conjunto de notícias, etc. No nosso contexto, definimos como documento as notícias do G1 coletadas nessa amostra sobre um determinado tema, como Rio de Janeiro, São Paulo, Política ou Economia. Daí o nome da última coluna.&lt;/p&gt;
&lt;p&gt;O livro online &lt;a href=&#34;http://tidytextmining.com/tfidf.html&#34;&gt;Text Mining with R&lt;/a&gt; é uma ótima referência para tf-idf, tanto teórica quanto prática.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;resultados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Resultados&lt;/h2&gt;
&lt;p&gt;Quais são os documentos mais comuns?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_g1$documento %&amp;gt;% table() %&amp;gt;% sort() %&amp;gt;% rev()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## .
##                   mundo               sao-paulo          rio-de-janeiro 
##                     205                     158                     118 
##                pop-arte              tecnologia                economia 
##                     110                     102                      99 
##                politica                      sp         ciencia-e-saude 
##                      95                      73                      60 
##         planeta-bizarro                bemestar                  musica 
##                      37                      30                      29 
##        distrito-federal                      mg                      pr 
##                      28                      27                      26 
##                  carros   vestibular-e-educacao                   goias 
##                      24                      21                      21 
##                  brasil                educacao             rock-in-rio 
##                      21                      18                      17 
##                natureza          espirito-santo                      sc 
##                      17                      15                      13 
##                      rs           resumo-do-dia            minas-gerais 
##                      13                      13                      13 
##             mato-grosso          11-de-setembro                   bahia 
##                      13                      13                      12 
##                vc-no-g1                  parana           agenda-do-dia 
##                      10                      10                       9 
##                      pi         bienal-do-livro                      rj 
##                       8                       8                       7 
##                     swu              pernambuco                      pb 
##                       6                       6                       6 
##      mato-grosso-do-sul                loterias                      rn 
##                       6                       6                       5 
##                      to                      rr                      ro 
##                       4                       4                       4 
##           revolta-arabe                 paraiba                      pa 
##                       4                       4                       4 
##              fantastico        dia-das-criancas                      ac 
##                       4                       4                       4 
##        turismo-e-viagem       rihanna-no-brasil                   platb 
##                       3                       3                       3 
## justin-bieber-no-brasil              e-ou-nao-e                   ceara 
##                       3                       3                       3 
##                  videos                      se                      ma 
##                       2                       2                       2 
##                 loteria                      ap                      am 
##                       2                       2                       2 
##                      al                    spfw          reveillon-2012 
##                       2                       1                       1 
##    monitor-da-violencia   especial-publicitario               especiais 
##                       1                       1                       1 
##            dia-dos-pais       dia-dos-namorados     concursos-e-emprego 
##                       1                       1                       1 
##                amazonas 
##                       1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As sessões do G1 que mais marcam presença na homepage são, sem muita surpresa, &lt;a href=&#34;http://g1.globo.com/mundo/&#34;&gt;Mundo&lt;/a&gt;, &lt;a href=&#34;http://g1.globo.com/sao-paulo/&#34;&gt;São Paulo&lt;/a&gt; e &lt;a href=&#34;http://g1.globo.com/rio-de-janeiro/&#34;&gt;Rio de Janeiro&lt;/a&gt;. Você pode ter percebido que existe tanto um documento chamado “sao-paulo” como “sp”, assim como “rio-de-janeiro” e “rj”. Isso acontece porque o G1, para os maiores estados, tem sessões separadas para a capital e para o resto das cidades.&lt;/p&gt;
&lt;p&gt;Os dois primeiros objetos de estudo são as cidades mais famosas do Brasil: Rio de Janeiro e São Paulo. O passo-a-passo da aplicação do tf-idf para essas duas cidades é descrito abaixo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;docs &amp;lt;- c(&amp;quot;rio-de-janeiro&amp;quot;, &amp;quot;sao-paulo&amp;quot;)

df_filtro  &amp;lt;- df_g1 %&amp;gt;%
  #mutate(caderno_url = if_else(caderno %in% vetor_cadernos, &amp;quot;Selecionado&amp;quot;, &amp;quot;Outros&amp;quot;))
  filter(documento %in% docs)

temp &amp;lt;- df_filtro %&amp;gt;% 
  # separar cada palavra do corpo da notícia em uma linha diferente, sem converter para minusculo
  unnest_tokens(palavra, corpo_noticia, token = &amp;quot;ngrams&amp;quot;, n = 1, to_lower = FALSE) %&amp;gt;% 
  # remover as duplicatas, isto é, as palavras que aparecem mais de uma vez em uma mesma noticia
  distinct(url, palavra, .keep_all = TRUE) %&amp;gt;% 
  # contar a ocorrencia de cada palavra em cada doc
  count(documento, palavra) %&amp;gt;% 
  tidytext::bind_tf_idf(term = palavra, document = documento, n = n) %&amp;gt;%  
  # ordenar as palavras com menor tf_idf por documento
  group_by(documento) %&amp;gt;% 
  arrange(desc(tf_idf)) %&amp;gt;% 
  mutate(palavra = factor(palavra, levels = rev(unique(palavra)))) %&amp;gt;% 
  #filtrar as 20 de maior destaque
  filter(row_number() &amp;lt;= 20) %&amp;gt;% 
  ungroup() %&amp;gt;% 
  arrange(desc(tf_idf)) %&amp;gt;% 
  mutate(palavra = factor(palavra, levels = rev(unique(palavra))))
    

# construir grafico
temp %&amp;gt;% 
  ggplot(aes(palavra, tf_idf, fill = documento)) +
      geom_col(show.legend = FALSE) +
      geom_text(aes(label = n), hjust = 1.3) +
      labs(x = NULL, y = &amp;quot;tf-idf&amp;quot;,
           title = &amp;quot;Termos mais frequentes associados a apenas uma das cidades&amp;quot;,
           caption = &amp;quot;Fonte: Mineração de texto aplicada em notícias do G1.&amp;quot;) +
      facet_wrap(~documento, ncol = 2, scales = &amp;quot;free&amp;quot;) +
      coord_flip()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-10-15-analise-g1-01_files/figure-html/unnamed-chunk-13-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Algumas observações sobre os resultados precisam ser feitas:&lt;br /&gt;
* No Rio de Janeiro, aparecem alguns termos meio estranhos como Envie e VC. Isso acontece porque, durante um período, no final das reportagens, o G1 escrevia algo como “Envie VC também uma reportagem para o G1RJ pelo Whatsapp ou pelo Viber”.&lt;br /&gt;
* Alguns nomes próprios se destacam, o que é natural, visto que Dória é prefeito de SP e Cabral um ex-governador do RJ. Em mineração de texto, esse tipo de dado é chamado de Entidade.&lt;/p&gt;
&lt;p&gt;Para limpar os resultados no segundo caso, são removidos os substantitos próprios. A maneira que eu bolei para isso talvez não seja a mais científica ou linguisticamente correta possível, mas aparenta ter funcionado. Ela se baseia em remover toda palavra que não é igual se escrita toda em minúsculo ou se escrita toda em maiúsculo. Por exemplo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;eh_substantivo_comum &amp;lt;- function(x) {x == str_to_lower(x) | x == str_to_upper(x)}
c(eh_substantivo_comum(&amp;quot;tiroteio&amp;quot;), eh_substantivo_comum(&amp;quot;RJTV&amp;quot;), eh_substantivo_comum(&amp;quot;Doria&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1]  TRUE  TRUE FALSE&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aproveitando o embalo da limpeza de dados, os termos mencionados acima como “Envie” e “VC” também serão removidos. Aplicando esta metodologia:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;temp &amp;lt;- df_filtro %&amp;gt;% 
  unnest_tokens(palavra, corpo_noticia, token = &amp;quot;ngrams&amp;quot;, n = 1, to_lower = FALSE) %&amp;gt;% 
  # aplicar regra do substantivo comum
  filter(eh_substantivo_comum(palavra)) %&amp;gt;% 
  # remover termos estranhos
  filter(!palavra %in% c(&amp;quot;VC&amp;quot;, &amp;quot;RJTV&amp;quot;)) %&amp;gt;% 
  distinct(url, palavra, .keep_all = TRUE) %&amp;gt;% 
  count(documento, palavra) %&amp;gt;% 
  tidytext::bind_tf_idf(term = palavra, document = documento, n = n) %&amp;gt;%  
  group_by(documento) %&amp;gt;% 
  arrange(desc(tf_idf)) %&amp;gt;% 
  mutate(palavra = factor(palavra, levels = rev(unique(palavra)))) %&amp;gt;% 
  filter(row_number() &amp;lt;= 20) %&amp;gt;% 
  ungroup() %&amp;gt;% 
  arrange(desc(tf_idf)) %&amp;gt;% 
  mutate(palavra = factor(palavra, levels = rev(unique(palavra))))
    

# construir grafico
temp %&amp;gt;% 
  ggplot(aes(palavra, tf_idf, fill = documento)) +
      geom_col(show.legend = FALSE) +
      geom_text(aes(label = n), hjust = 1.3) +
      labs(x = NULL, y = &amp;quot;tf-idf&amp;quot;,
           title = &amp;quot;Termos mais frequentes associados a apenas uma das cidades&amp;quot;,
           caption = &amp;quot;Fonte: Mineração de texto aplicada em notícias do G1.&amp;quot;) +
      facet_wrap(~documento, ncol = 2, scales = &amp;quot;free&amp;quot;) +
      coord_flip()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-10-15-analise-g1-01_files/figure-html/unnamed-chunk-15-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Agora é possível notar resultados mais interessantes: termos relacionados ao tráfico de drogas como &lt;strong&gt;tiroteio&lt;/strong&gt;, &lt;strong&gt;rival&lt;/strong&gt;, &lt;strong&gt;UPP&lt;/strong&gt; e &lt;strong&gt;propinas&lt;/strong&gt; possui uma frequência relativa muito maior em notícias sobre a cidade do Rio de Janeiro que na capital paulista. Por outro lado, notícias sobre &lt;strong&gt;estupro&lt;/strong&gt; e &lt;strong&gt;abuso&lt;/strong&gt; parecem ser mais comuns na terra onde biscoito é chamado de bolacha.&lt;/p&gt;
&lt;p&gt;Também é possível realizar essa mesma análise por expressões compostas por mais de uma palavra, que são os chamados n-grams, onde n é a quantidade de termos. Analisando os 2-grams:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;temp &amp;lt;- df_filtro %&amp;gt;% 
  unnest_tokens(palavra, corpo_noticia, token = &amp;quot;ngrams&amp;quot;, n = 2, to_lower = FALSE) %&amp;gt;% 
  # aplicar regra do substantivo comum
  filter(eh_substantivo_comum(palavra)) %&amp;gt;% 
  distinct(url, palavra, .keep_all = TRUE) %&amp;gt;% 
  count(documento, palavra) %&amp;gt;% 
  tidytext::bind_tf_idf(term = palavra, document = documento, n = n) %&amp;gt;%  
  group_by(documento) %&amp;gt;% 
  arrange(desc(tf_idf)) %&amp;gt;% 
  mutate(palavra = factor(palavra, levels = rev(unique(palavra)))) %&amp;gt;% 
  filter(row_number() &amp;lt;= 20) %&amp;gt;% 
  ungroup() %&amp;gt;% 
  arrange(desc(tf_idf)) %&amp;gt;% 
  mutate(palavra = factor(palavra, levels = rev(unique(palavra))))
    

# construir grafico
temp %&amp;gt;% 
  ggplot(aes(palavra, tf_idf, fill = documento)) +
      geom_col(show.legend = FALSE) +
      geom_text(aes(label = n), hjust = 1.3) +
      labs(x = NULL, y = &amp;quot;tf-idf&amp;quot;) +
      facet_wrap(~documento, ncol = 2, scales = &amp;quot;free&amp;quot;) +
      coord_flip()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-10-15-analise-g1-01_files/figure-html/unnamed-chunk-16-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;e-sergipe&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;E Sergipe?&lt;/h2&gt;
&lt;p&gt;Conforme foi mostrado um pouco acima, existem apenas 2 notícias de Sergipe na amostra coletada. O Sudeste é o grande monopolizador da primeira página do G1. Mesmo se aplicássemos o tf-idf comparando o Rio de Janeiro com Sergipe, os resultados seriam enviesados devido à baixíssima quantidade de dados do último. Por isso, Norte e Nordeste são agregadas como uma:&lt;/p&gt;
&lt;p&gt;Vamos então criar mais uma coluna para identificar a região do Brasil a qual a notícia se refere:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_g1 %&amp;lt;&amp;gt;% 
  mutate(regiao = case_when(
    documento %in% c(&amp;quot;ac&amp;quot;, &amp;quot;am&amp;quot;, &amp;quot;amazonas&amp;quot;, &amp;quot;ap&amp;quot;, &amp;quot;pa&amp;quot;, &amp;quot;ro&amp;quot;, &amp;quot;rr&amp;quot;) ~ &amp;quot;Norte&amp;quot;,
    documento %in% c (&amp;quot;al&amp;quot;, &amp;quot;bahia&amp;quot;, &amp;quot;ceara&amp;quot;, &amp;quot;ma&amp;quot;, &amp;quot;paraiba&amp;quot;, &amp;quot;pb&amp;quot;, &amp;quot;pernambuco&amp;quot;, &amp;quot;pi&amp;quot;, &amp;quot;rn&amp;quot;, &amp;quot;se&amp;quot;, &amp;quot;to&amp;quot;) ~ &amp;quot;Nordeste&amp;quot;,
    documento %in% c(&amp;quot;distrito-federal&amp;quot;, &amp;quot;goias&amp;quot;, &amp;quot;mato-grosso&amp;quot;, &amp;quot;mato-grosso-do-sul&amp;quot;) ~ &amp;quot;Centro-Oeste&amp;quot;,
    documento %in% c(&amp;quot;espirito&amp;quot;, &amp;quot;mg&amp;quot;, &amp;quot;minas-gerais&amp;quot;, &amp;quot;rio-de-janeiro&amp;quot;, &amp;quot;rj&amp;quot;, &amp;quot;sao-paulo&amp;quot;, &amp;quot;sp&amp;quot;) ~ &amp;quot;Sudeste&amp;quot;,
    documento %in% c(&amp;quot;parana&amp;quot;, &amp;quot;rs&amp;quot;, &amp;quot;sc&amp;quot;) ~ &amp;quot;Sul&amp;quot;,
    #caderno_url %in% &amp;quot;mundo&amp;quot; ~ &amp;quot;Mundo&amp;quot;,
    TRUE ~ NA_character_
  ))

df_g1$regiao[df_g1$regiao %in% c(&amp;quot;Norte&amp;quot;, &amp;quot;Nordeste&amp;quot;)] &amp;lt;- &amp;quot;Norte-Nordeste&amp;quot;

# regioes mais comuns:
df_g1$regiao %&amp;gt;% table() %&amp;gt;% sort() %&amp;gt;% rev()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## .
##        Sudeste Norte-Nordeste   Centro-Oeste            Sul 
##            396             75             68             36&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para não ter de repetir o código que constrói os gráficos acima, salvei a função &lt;a href=&#34;&#34;&gt;neste Gist&lt;/a&gt;, com direito a algumas parametrizações, como a opção de remover ou não substantivos próprios:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;id_gist &amp;lt;- &amp;quot;2d626f33ee1b635a0aa3beeda31ae720&amp;quot;
devtools::source_gist(id_gist, filename = &amp;quot;grafico_g1.R&amp;quot;)

df_g1 %&amp;gt;% 
  mutate(documento = regiao) %&amp;gt;% 
  filter(!is.na(documento)) %&amp;gt;% 
  grafico_tfidf(n_grams = 1, remover_nomes_proprios = TRUE, agregar_por_noticia = TRUE)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-10-15-analise-g1-01_files/figure-html/unnamed-chunk-18-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Mesmo agregando os dados por região, o desbalanceamento da quantidade de notícias por região prejudicou os resultados. Seria necessário coletar muito mais notícias para que os resultados fossem mais interessantes.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;bonus&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Bônus&lt;/h2&gt;
&lt;p&gt;A análise não precisa se restringir a regiões demográficas. Podemos, por exemplo, comparar notícias dos cadernos de Política e Ciência:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_g1 %&amp;gt;% 
  filter(documento %in% c(&amp;quot;politica&amp;quot;, &amp;quot;ciencia-e-saude&amp;quot;)) %&amp;gt;% 
  grafico_tfidf(n_grams = 1, remover_nomes_proprios = TRUE, agregar_por_noticia = TRUE)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-10-15-analise-g1-01_files/figure-html/unnamed-chunk-19-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>O Sensacionalista e Text Mining: Análise de sentimento usando o lexiconPT</title>
      <link>http://www.sillasgonzaga.com/post/o-sensacionalista-e-text-mining/</link>
      <pubDate>Sat, 23 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/o-sensacionalista-e-text-mining/</guid>
      <description>&lt;p&gt;De volta à ativa no blog!&lt;/p&gt;
&lt;p&gt;Recentemente, quando precisei fazer pela primeira vez algum tipo de análise em cima de textos (o chamado Text Mining ou Mineração de Texto) em Português, senti falta de ter um acesso fácil a um léxico na linguagem. O R já tem a sua disposição vários recursos para quem quer fazer Text Mining em inglês, como os pacotes &lt;code&gt;tokenizer&lt;/code&gt;, &lt;code&gt;tidytext&lt;/code&gt;, &lt;code&gt;tm&lt;/code&gt; e &lt;code&gt;lexicon&lt;/code&gt;, além de vários blog posts sobre Sentiment Analysis que você encontra no R-bloggers. Contudo, existe uma séria escassez de material de referência na língua portuguesa.&lt;/p&gt;
&lt;p&gt;O pacote &lt;a href=&#34;https://github.com/sillasgonzaga/lexiconPT&#34;&gt;&lt;code&gt;lexiconPT&lt;/code&gt;&lt;/a&gt;, que eu lancei em 20/09/2017 no Github (e em breve no CRAN também) nasceu para resolver parte desse problema. Até o momento, o &lt;code&gt;lexiconPT&lt;/code&gt; possui três datasets de léxicos: o OpLexicon (versões 2.1 e 3.0) e o SentiLex-PT02. Não pretendo (nem tenho competência para tal, pois sou iniciante em Text Mining - sem falsa modéstia) destrinchar como cada um deles funciona e em quê eles diferem. Para isso, sugiro ler as referências citadas na documentação dos próprios datasets (ex.: &lt;code&gt;help(&amp;quot;oplexicon_v2.1&amp;quot;)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Mas ter o léxico em mãos só resolve parte dos problemas: ainda faltam os textos em si para serem analisados. Algumas ideias de datasets poderiam ser notícias, letras de músicas, livros (tem vários em Domínio Público), tweets, etc. Para demonstrar um simples uso do pacote, eu decidi por analisar comentários feitos por usuários na página do &lt;a href=&#34;https://www.facebook.com/sensacionalista/&#34;&gt;Sensacionalista&lt;/a&gt;, uma das mais populares do Facebook. A coleta dos dados foi relativamente fácil graças ao pacote &lt;code&gt;Rfacebook&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Com o pacote &lt;code&gt;lexiconPT&lt;/code&gt;, podemos responder a perguntas como:&lt;br /&gt;
* Os comentários no Sensacionalista são mais negativos ou positivos?&lt;br /&gt;
* Qual termo está mais associado a comentários negativos? PT ou PSDB? Temer ou Dilma? Bolsonaro ou Lula?&lt;br /&gt;
* Qual o comentário feito por um usuário mais negativo da história do Sensacionalista (dentro da amostra coletada)? E qual o mais positivo?&lt;/p&gt;
&lt;p&gt;Vamos ao código.&lt;/p&gt;
&lt;div id=&#34;coleta-dos-dados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Coleta dos dados&lt;/h2&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(Rfacebook) # usado para extrair dados do facebook
library(tidyverse) # pq nao da pra viver sem
library(ggExtra)
library(magrittr) # &amp;lt;3
library(lubridate)
library(stringr) # essencial para trabalhar com textos
library(tidytext) # um dos melhores pacotes para text mining
library(lexiconPT)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este post só foi possível graças ao &lt;code&gt;Rfacebook&lt;/code&gt;. Para aprender como ele funciona, leia a documentação presente em seu &lt;a href=&#34;https://github.com/pablobarbera/Rfacebook&#34;&gt;repo&lt;/a&gt; no Github. Para este primeiro, primeiro usei a função &lt;code&gt;getPage()&lt;/code&gt; para extrair as últimas 5000 publicações do Sensacionalista.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# token que eu gerei com minha API key.
# Essa parte vc obviamente nao vai conseguir reproduzir.
# leia o README do Rfacebook para saber como obter seu token
fb_token &amp;lt;- readRDS(&amp;quot;/home/sillas/R/data/facebook_token.Rds&amp;quot;) 

# demora cerca de 10 min pra rodar:
pg &amp;lt;- getPage(&amp;quot;sensacionalista&amp;quot;, fb_token, n = 5000)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;É necessário corrigir o encoding do corpo da publicação para o R parar de reclamar sobre isso:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# corrigir encoding do texto do post
pg$message %&amp;lt;&amp;gt;% iconv(to = &amp;quot;ASCII//TRANSLIT&amp;quot;)
# remover emojis
pg$message %&amp;lt;&amp;gt;% iconv(sub=&amp;quot;&amp;quot;, &amp;#39;UTF-8&amp;#39;, &amp;#39;ASCII&amp;#39;)
# visualizar dataframe
glimpse(pg)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 4,666
## Variables: 11
## $ from_id        &amp;lt;chr&amp;gt; &amp;quot;108175739225302&amp;quot;, &amp;quot;108175739225302&amp;quot;, &amp;quot;10817573...
## $ from_name      &amp;lt;chr&amp;gt; &amp;quot;Sensacionalista&amp;quot;, &amp;quot;Sensacionalista&amp;quot;, &amp;quot;Sensacio...
## $ message        &amp;lt;chr&amp;gt; &amp;quot;Apos liminar da Justica do DF permitir o trata...
## $ created_time   &amp;lt;chr&amp;gt; &amp;quot;2017-09-18T18:58:53+0000&amp;quot;, &amp;quot;2017-09-18T17:25:0...
## $ type           &amp;lt;chr&amp;gt; &amp;quot;link&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;video&amp;quot;, &amp;quot;link&amp;quot;...
## $ link           &amp;lt;chr&amp;gt; &amp;quot;http://www.sensacionalista.com.br/2017/09/18/l...
## $ id             &amp;lt;chr&amp;gt; &amp;quot;108175739225302_1638598099516384&amp;quot;, &amp;quot;1081757392...
## $ story          &amp;lt;chr&amp;gt; NA, NA, NA, NA, &amp;quot;Sensacionalista shared Gshow -...
## $ likes_count    &amp;lt;dbl&amp;gt; 10172, 2162, 2503, 5793, 4676, 2585, 821, 766, ...
## $ comments_count &amp;lt;dbl&amp;gt; 230, 164, 285, 221, 329, 104, 32, 58, 493, 586,...
## $ shares_count   &amp;lt;dbl&amp;gt; 2290, 453, 410, 2751, 0, 930, 26, 36, 900, 92, ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Só esse dataset por si só já renderia (e renderá) análises interessantes, mas vou as deixar para um futuro post para não deixar este aqui grande demais.&lt;/p&gt;
&lt;p&gt;A coluna &lt;code&gt;id&lt;/code&gt; é a que usaremos como referência como input na função &lt;code&gt;getPost()&lt;/code&gt; para extrair os comentários dos usuários na publicação. Infelizmente, a API do Facebook apresenta uma certa instabilidade para requests de dados muito grandes. Em várias tentativas que fiz, o máximo de dados que consegui extrair foram 200 comentários de 500 publicações da página. Portanto, vou usar esses parâmetros:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# roda em cerca de 8 minutos:
df_posts &amp;lt;- pg$id[1:500] %&amp;gt;% map(getPost, fb_token, n = 200, comments = TRUE, likes = FALSE,
                            reactions = FALSE)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A função &lt;code&gt;getPost()&lt;/code&gt; fornece o seguinte output:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;str(df_posts)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## List of 500
##  $ :List of 2
##   ..$ post    :&amp;#39;data.frame&amp;#39;: 1 obs. of  10 variables:
##   .. ..$ from_id       : chr &amp;quot;108175739225302&amp;quot;
##   .. ..$ from_name     : chr &amp;quot;Sensacionalista&amp;quot;
##   .. ..$ message       : chr &amp;quot;Após liminar da Justiça do DF permitir o tratamento da homossexualidade como doença&amp;quot;
##   .. ..$ created_time  : chr &amp;quot;2017-09-18T18:58:53+0000&amp;quot;
##   .. ..$ type          : chr &amp;quot;link&amp;quot;
##   .. ..$ link          : chr &amp;quot;http://www.sensacionalista.com.br/2017/09/18/liminar-que-chancela-cura-gay-permite-tratar-justica-brasileira-como-doenca/&amp;quot;
##   .. ..$ id            : chr &amp;quot;108175739225302_1638598099516384&amp;quot;
##   .. ..$ likes_count   : num 11340
##   .. ..$ comments_count: num 248
##   .. ..$ shares_count  : num 2590
##   ..$ comments:&amp;#39;data.frame&amp;#39;: 200 obs. of  7 variables:
##   .. ..$ from_id       : chr [1:200] &amp;quot;1437254732976789&amp;quot; &amp;quot;1445900342154501&amp;quot; &amp;quot;10209941046899919&amp;quot; &amp;quot;173469923210786&amp;quot; ...
##   .. ..$ from_name     : chr [1:200] &amp;quot;Sérgio Henrique Reis&amp;quot; &amp;quot;Renata Gil&amp;quot; &amp;quot;Lucas Ferreira&amp;quot; &amp;quot;Leonardo Wesley&amp;quot; ...
##   .. ..$ message       :&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Error in strtrim(encodeString(object, quote = &amp;quot;\&amp;quot;&amp;quot;, na.encode = FALSE), : string multibyte inválida em &amp;#39;&amp;lt;a0&amp;gt;&amp;lt;be&amp;gt;\xed&amp;lt;b4&amp;gt;\u0094  Ahh do Distrito Federal.... Quando Juscelino Kubitschek fundou Brasília, será que ele tinha a mínima ideia do que sairia de lá?&amp;quot;&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para extrair os dataframes relativos aos comentários e aos metadados das publicações, o &lt;code&gt;purrr&lt;/code&gt; é uma mão na roda:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments &amp;lt;- df_posts %&amp;gt;% map_df(&amp;quot;comments&amp;quot;)
df_posts &amp;lt;- df_posts %&amp;gt;% map_df(&amp;quot;post&amp;quot;)
# repetir procedimento de consertar o encoding
df_comments$message %&amp;lt;&amp;gt;% iconv(to = &amp;quot;ASCII//TRANSLIT&amp;quot;) %&amp;gt;% iconv(sub=&amp;quot;&amp;quot;, &amp;#39;UTF-8&amp;#39;, &amp;#39;ASCII&amp;#39;)
df_posts$message %&amp;lt;&amp;gt;% iconv(to = &amp;quot;ASCII//TRANSLIT&amp;quot;) %&amp;gt;% iconv(sub=&amp;quot;&amp;quot;, &amp;#39;UTF-8&amp;#39;, &amp;#39;ASCII&amp;#39;)
# por questoes de anonimizacao, vou remover os dados pessoais referentes aos usuarios
df_comments %&amp;lt;&amp;gt;% select(-from_id, -from_name)

# olhar estrutura dos dataframes
str(df_comments)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## &amp;#39;data.frame&amp;#39;:    72100 obs. of  5 variables:
##  $ message       : chr  &amp;quot;Entao pode faltar no servico, ligar pro chefe(a), levar atestado e dizer que acordou com vontade de chupar rola?&amp;quot; &amp;quot;Nunca o sensacionalista foi tao verdadeiro. Pq a justica brasileira se demonstrou uma verdadeira praga na socie&amp;quot;| __truncated__ &amp;quot;O unico que precisa de tratamento e o sr juiz que autorizou.&amp;quot; &amp;quot;Isso viola todas os acordos internacionais e uma aberracao contra qqr liberdade individual e humana. Logo essa liminar cai.&amp;quot; ...
##  $ created_time  : chr  &amp;quot;2017-09-18T19:01:21+0000&amp;quot; &amp;quot;2017-09-18T19:03:57+0000&amp;quot; &amp;quot;2017-09-18T19:01:26+0000&amp;quot; &amp;quot;2017-09-18T19:00:54+0000&amp;quot; ...
##  $ likes_count   : num  575 392 270 227 310 145 113 73 38 50 ...
##  $ comments_count: num  58 5 4 3 12 16 10 0 0 8 ...
##  $ id            : chr  &amp;quot;1638598099516384_1638600206182840&amp;quot; &amp;quot;1638598099516384_1638602159515978&amp;quot; &amp;quot;1638598099516384_1638600266182834&amp;quot; &amp;quot;1638598099516384_1638599869516207&amp;quot; ...&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;str(df_posts)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## &amp;#39;data.frame&amp;#39;:    500 obs. of  10 variables:
##  $ from_id       : chr  &amp;quot;108175739225302&amp;quot; &amp;quot;108175739225302&amp;quot; &amp;quot;108175739225302&amp;quot; &amp;quot;108175739225302&amp;quot; ...
##  $ from_name     : chr  &amp;quot;Sensacionalista&amp;quot; &amp;quot;Sensacionalista&amp;quot; &amp;quot;Sensacionalista&amp;quot; &amp;quot;Sensacionalista&amp;quot; ...
##  $ message       : chr  &amp;quot;Apos liminar da Justica do DF permitir o tratamento da homossexualidade como doenca&amp;quot; &amp;quot;Ja recebeu encomendas de quarteis&amp;quot; &amp;quot;Aparelho esta sendo oferecido por importadores por ate 6 mil reais&amp;quot; &amp;quot;Temer desembarcou nos Estados Unidos para jantar com Trump e participacao na Assembleia Geral da ONU&amp;quot; ...
##  $ created_time  : chr  &amp;quot;2017-09-18T18:58:53+0000&amp;quot; &amp;quot;2017-09-18T17:25:00+0000&amp;quot; &amp;quot;2017-09-18T17:05:41+0000&amp;quot; &amp;quot;2017-09-18T17:00:02+0000&amp;quot; ...
##  $ type          : chr  &amp;quot;link&amp;quot; &amp;quot;link&amp;quot; &amp;quot;link&amp;quot; &amp;quot;link&amp;quot; ...
##  $ link          : chr  &amp;quot;http://www.sensacionalista.com.br/2017/09/18/liminar-que-chancela-cura-gay-permite-tratar-justica-brasileira-como-doenca/&amp;quot; &amp;quot;http://www.sensacionalista.com.br/2017/09/18/fabricante-de-pau-de-arara-comemora-falta-de-resposta-a-general-qu&amp;quot;| __truncated__ &amp;quot;http://www.sensacionalista.com.br/2017/09/18/empresa-lanca-servico-de-escolta-armada-para-quem-comprar-o-iphone-x/&amp;quot; &amp;quot;http://www.sensacionalista.com.br/2017/09/18/coreia-do-norte-nega-ter-lancado-temer-nos-eua/&amp;quot; ...
##  $ id            : chr  &amp;quot;108175739225302_1638598099516384&amp;quot; &amp;quot;108175739225302_1638510679525126&amp;quot; &amp;quot;108175739225302_1638503796192481&amp;quot; &amp;quot;108175739225302_1638456466197214&amp;quot; ...
##  $ likes_count   : num  11340 2271 2826 6361 4990 ...
##  $ comments_count: num  248 167 315 242 347 106 32 58 494 590 ...
##  $ shares_count  : num  2590 478 441 3008 0 ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Só pode ser trollagem da API do Facebook retornar aquela mensagem logo no topo do dataframe, mas enfim.&lt;/p&gt;
&lt;p&gt;O dataframe &lt;code&gt;df_comments&lt;/code&gt;, o objeto da nossa análise, não possui alguns dados que serão valiosos para a análise, como o link para o artigo no site do Sensacionalista. Por isso, vamos um &lt;code&gt;left_join&lt;/code&gt; com o &lt;code&gt;df_posts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Percebeu que nas colunas &lt;code&gt;df_comments$id&lt;/code&gt; e &lt;code&gt;df_posts$id&lt;/code&gt; existe um traço separando dois conjuntos numéricos? Por alguma razão que beira a imbecilidade, não é possível combinar imediatamente essas duas colunas para formatar um dataframe só com o &lt;code&gt;left_join&lt;/code&gt;. A sintaxe de identificação do Facebook funciona assim: O post é identificado &lt;code&gt;IDPAGINA_IDPUBLICAÇÃO&lt;/code&gt; e o comentário na publicação é identificado como &lt;code&gt;IDPUBLICAÇÃO_IDCOMENTÁRIO&lt;/code&gt;. Ou seja, para poder juntar os dois dataframes, vamos ter que combinar a sequência númerica à esquerda do underline (acho que esse traço tem algum outro nome, mas não me lembro no momento) em &lt;code&gt;df_comments$id&lt;/code&gt; e à direita em &lt;code&gt;df_posts$id&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# consertar colunas de id: no df_comments, deixar à esquerda do underline. no df_posts, deixar à direita.
df_comments$id_post_real &amp;lt;- df_comments$id
df_comments$id %&amp;lt;&amp;gt;% str_replace_all(&amp;quot;_.*&amp;quot;, &amp;quot;&amp;quot;)
df_posts$id %&amp;lt;&amp;gt;% str_replace_all(&amp;quot;.*_&amp;quot;, &amp;quot;&amp;quot;)

# juntar as duas tabelas
df_posts %&amp;lt;&amp;gt;% dplyr::select(id, post_message = message, horario_post = created_time,
                     type, post_comments_count = comments_count, post_link = link,
                     post_type = type, post_likes_count = likes_count)
df_comments %&amp;lt;&amp;gt;% rename(horario_comentario = created_time)

df_comments %&amp;lt;&amp;gt;% left_join(df_posts, by = &amp;quot;id&amp;quot;)
# remover NAs (nao sao muitos casos)
df_comments %&amp;lt;&amp;gt;% filter(!is.na(horario_post))
# converter colunas de horario
df_comments$horario_comentario %&amp;lt;&amp;gt;% str_sub(1, 19) %&amp;gt;% str_replace_all(&amp;quot;T&amp;quot;, &amp;quot;&amp;quot;) %&amp;gt;% ymd_hms()
df_comments$horario_post %&amp;lt;&amp;gt;% str_sub(1, 19) %&amp;gt;% str_replace_all(&amp;quot;T&amp;quot;, &amp;quot;&amp;quot;) %&amp;gt;% ymd_hms()
# Como ficou:
glimpse(df_comments)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 71,891
## Variables: 12
## $ message             &amp;lt;chr&amp;gt; &amp;quot;Entao pode faltar no servico, ligar pro c...
## $ horario_comentario  &amp;lt;dttm&amp;gt; 2017-09-18 19:01:21, 2017-09-18 19:03:57,...
## $ likes_count         &amp;lt;dbl&amp;gt; 575, 392, 270, 227, 310, 145, 113, 73, 38,...
## $ comments_count      &amp;lt;dbl&amp;gt; 58, 5, 4, 3, 12, 16, 10, 0, 0, 8, 69, 3, 0...
## $ id                  &amp;lt;chr&amp;gt; &amp;quot;1638598099516384&amp;quot;, &amp;quot;1638598099516384&amp;quot;, &amp;quot;1...
## $ id_post_real        &amp;lt;chr&amp;gt; &amp;quot;1638598099516384_1638600206182840&amp;quot;, &amp;quot;1638...
## $ post_message        &amp;lt;chr&amp;gt; &amp;quot;Apos liminar da Justica do DF permitir o ...
## $ horario_post        &amp;lt;dttm&amp;gt; 2017-09-18 18:58:53, 2017-09-18 18:58:53,...
## $ post_type           &amp;lt;chr&amp;gt; &amp;quot;link&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;l...
## $ post_comments_count &amp;lt;dbl&amp;gt; 248, 248, 248, 248, 248, 248, 248, 248, 24...
## $ post_link           &amp;lt;chr&amp;gt; &amp;quot;http://www.sensacionalista.com.br/2017/09...
## $ post_likes_count    &amp;lt;dbl&amp;gt; 11340, 11340, 11340, 11340, 11340, 11340, ...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;uso-do-lexiconpt&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Uso do lexiconPT&lt;/h2&gt;
&lt;p&gt;Agora temos o dataset em mãos para usar o &lt;code&gt;lexiconPT&lt;/code&gt;. Acho muito importante ressaltar que Text Mining é uma atividade razoavelmente complexa que envolve uma extensa etapa de limpeza e tratamento de dados, como remover (ou não) acentos e corrigir palavras com letras duplicadas (trist&lt;em&gt;ee&lt;/em&gt;) ou erros gramaticais (infelismente). Para fins de simplicidade, não vou me ater muito a esses detalhes e pular direto para a utilização dos léxicos portugueses e apresentação dos resultados.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# carregar datasets
data(&amp;quot;oplexicon_v3.0&amp;quot;)
data(&amp;quot;sentiLex_lem_PT02&amp;quot;)

op30 &amp;lt;- oplexicon_v3.0
sent &amp;lt;- sentiLex_lem_PT02

glimpse(op30)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 32,191
## Variables: 4
## $ term              &amp;lt;chr&amp;gt; &amp;quot;=[&amp;quot;, &amp;quot;=@&amp;quot;, &amp;quot;=p&amp;quot;, &amp;quot;=P&amp;quot;, &amp;quot;=x&amp;quot;, &amp;quot;=d&amp;quot;, &amp;quot;=D&amp;quot;, &amp;quot;;...
## $ type              &amp;lt;chr&amp;gt; &amp;quot;emot&amp;quot;, &amp;quot;emot&amp;quot;, &amp;quot;emot&amp;quot;, &amp;quot;emot&amp;quot;, &amp;quot;emot&amp;quot;, &amp;quot;emo...
## $ polarity          &amp;lt;int&amp;gt; -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, ...
## $ polarity_revision &amp;lt;chr&amp;gt; &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;, &amp;quot;A&amp;quot;,...&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;glimpse(sent)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Observations: 7,014
## Variables: 5
## $ term                    &amp;lt;chr&amp;gt; &amp;quot;a-vontade&amp;quot;, &amp;quot;abafado&amp;quot;, &amp;quot;abafante&amp;quot;, &amp;quot;a...
## $ grammar_category        &amp;lt;chr&amp;gt; &amp;quot;N&amp;quot;, &amp;quot;Adj&amp;quot;, &amp;quot;Adj&amp;quot;, &amp;quot;Adj&amp;quot;, &amp;quot;Adj&amp;quot;, &amp;quot;Adj&amp;quot;...
## $ polarity                &amp;lt;dbl&amp;gt; 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1...
## $ polarity_target         &amp;lt;chr&amp;gt; &amp;quot;N0&amp;quot;, &amp;quot;N0&amp;quot;, &amp;quot;N0&amp;quot;, &amp;quot;N0&amp;quot;, &amp;quot;N0&amp;quot;, &amp;quot;N0&amp;quot;, &amp;quot;N...
## $ polarity_classification &amp;lt;chr&amp;gt; &amp;quot;MAN&amp;quot;, &amp;quot;JALC&amp;quot;, &amp;quot;MAN&amp;quot;, &amp;quot;JALC&amp;quot;, &amp;quot;JALC&amp;quot;, ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ambos os datasets possuem colunas de polaridade de sentimento, que é a que usaremos para quantificar o quão negativo ou positivo é um comentário.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# criar ID unica para cada comentario
df_comments %&amp;lt;&amp;gt;% mutate(comment_id = row_number())
# usar funçao do tidytext para criar uma linha para cada palavra de um comentario
df_comments_unnested &amp;lt;- df_comments %&amp;gt;% unnest_tokens(term, message)

df_comments_unnested %&amp;gt;%
  select(comment_id, term) %&amp;gt;%
  head(20)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##      comment_id     term
## 1             1    entao
## 1.1           1     pode
## 1.2           1   faltar
## 1.3           1       no
## 1.4           1  servico
## 1.5           1    ligar
## 1.6           1      pro
## 1.7           1    chefe
## 1.8           1        a
## 1.9           1    levar
## 1.10          1 atestado
## 1.11          1        e
## 1.12          1    dizer
## 1.13          1      que
## 1.14          1  acordou
## 1.15          1      com
## 1.16          1  vontade
## 1.17          1       de
## 1.18          1   chupar
## 1.19          1     rola&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;De novo esse comentário… &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Enfim, veja que foi criada uma linha para cada palavra presetnte no comentário. Será essa nova coluna &lt;code&gt;term&lt;/code&gt; que usaremos como referência para quantificar o sentimento de um comentário.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments_unnested %&amp;gt;% 
  left_join(op30, by = &amp;quot;term&amp;quot;) %&amp;gt;% 
  left_join(sent %&amp;gt;% select(term, lex_polarity = polarity), by = &amp;quot;term&amp;quot;) %&amp;gt;% 
  select(comment_id, term, polarity, lex_polarity) %&amp;gt;% 
  head(10)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    comment_id    term polarity lex_polarity
## 1           1   entao       NA           NA
## 2           1    pode       NA           NA
## 3           1  faltar        1           NA
## 4           1      no       NA           NA
## 5           1 servico       NA           NA
## 6           1   ligar       -1           NA
## 7           1     pro       NA           NA
## 8           1   chefe       NA           NA
## 9           1       a       NA           NA
## 10          1   levar       -1           NA&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A amostra acima mostra que nem toads as palavras possuem uma polaridade registrada nos léxicos. Não só isso, mas algumas palavras (como &lt;strong&gt;faltar&lt;/strong&gt;, &lt;strong&gt;ligar&lt;/strong&gt; e &lt;strong&gt;levar&lt;/strong&gt;) estão presentes no OpLexicon mas não no SentiLex. A polaridade 1 em faltar significa que, de acordo com esse léxico, a palavra está associada a comentários positivos. Saber essa diferença é fundamental, pois a escolha do léxico pode ter uma grande influência nos resultados da análise.&lt;/p&gt;
&lt;p&gt;Vamos então manter no dataframe apenas as palavras que possuem polaridade tanto no OpLexicon como no SentiLex:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments_unnested &amp;lt;- df_comments_unnested %&amp;gt;% 
  inner_join(op30, by = &amp;quot;term&amp;quot;) %&amp;gt;% 
  inner_join(sent %&amp;gt;% select(term, lex_polarity = polarity), by = &amp;quot;term&amp;quot;) %&amp;gt;% 
  group_by(comment_id) %&amp;gt;% 
  summarise(
    comment_sentiment_op = sum(polarity),
    comment_sentiment_lex = sum(lex_polarity),
    n_words = n()
    ) %&amp;gt;% 
  ungroup() %&amp;gt;% 
  rowwise() %&amp;gt;% 
  mutate(
    most_neg = min(comment_sentiment_lex, comment_sentiment_op),
    most_pos = max(comment_sentiment_lex, comment_sentiment_op)
  )

head(df_comments_unnested)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 6 x 6
##   comment_id comment_sentiment_op comment_sentiment_lex n_words most_neg
##        &amp;lt;int&amp;gt;                &amp;lt;int&amp;gt;                 &amp;lt;dbl&amp;gt;   &amp;lt;int&amp;gt;    &amp;lt;dbl&amp;gt;
## 1          2                    0                     0       2        0
## 2          7                   -2                    -3       3       -3
## 3          8                    1                     1       2        1
## 4          9                    0                     0       3        0
## 5         11                   -1                    -1       3       -1
## 6         12                   -3                    -3       3       -3
## # ... with 1 more variables: most_pos &amp;lt;dbl&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;apresentacao-dos-resultados&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Apresentação dos resultados&lt;/h2&gt;
&lt;p&gt;O gráfico de pontos abaixo mostra a distribuição de polaridade para cada léxico:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments_unnested %&amp;gt;% 
  ggplot(aes(x = comment_sentiment_op, y = comment_sentiment_lex)) +
    geom_point(aes(color = n_words)) + 
    scale_color_continuous(low = &amp;quot;green&amp;quot;, high = &amp;quot;red&amp;quot;) +
    labs(x = &amp;quot;Polaridade no OpLexicon&amp;quot;, y = &amp;quot;Polaridade no SentiLex&amp;quot;) +
    #geom_smooth(method = &amp;quot;lm&amp;quot;) +
    geom_vline(xintercept = 0, linetype = &amp;quot;dashed&amp;quot;) +
    geom_hline(yintercept = 0, linetype = &amp;quot;dashed&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-09-23-o-sensacionalista-e-text-mining-analise-de-sentimento-usando-o-lexiconpt_files/figure-html/unnamed-chunk-14-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Existem pelo menos três outliers nos dados, todos causados pela grande quantidade de palavras do comentário, o que pode ser um indício de que simplesmente somar a polaridade de cada palavra do comentário pode não ser um bom método. Outra informação revelada pelo gráfico é que existem palavras que possuem sentimentos diferentes de acordo com o léxico usado. Ter isso em mente é fundamental para a análise.&lt;/p&gt;
&lt;p&gt;Após remover os outliers, já é possível descobrir quais os comentários mais positivos e mais negativos da amostra coletada:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments_unnested %&amp;lt;&amp;gt;% filter(between(comment_sentiment_op, -10, 10))

# comentario mais positivo da historia do sensacionalista
most_pos &amp;lt;- which.max(df_comments_unnested$most_pos)
most_neg &amp;lt;- which.min(df_comments_unnested$most_neg)

# mais positivo
cat(df_comments$message[df_comments$comment_id == df_comments_unnested$comment_id[most_pos]])&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## O mundo a esquerda e sempre melhor. A musica, o pincel, a pena e a talha. Sempre esteve para a esquerda. As grandes mentes criativas, os grandes pensadores, os humanistas, os geniais. Tudo que e original e belo e oriundo da esquerda.
## E tendencia natural do ser humano, ao passo q alcanca um minimo de consciencia critica do mundo sempre tender a esquerda.
## Todavia o mundo tambem precisa de mentes computacionais, a esse papel a direita tem seu valor. A direita funciona bem quando o objetivo e produzir o fisico, o tangivel e operacional. Embora isso tambem pudesse ser feito por maquinas, robos, ou mesmo por animais adestrados.
## A exemplo, os EUA, onde o cidadao comum e um ser vegetativo, destinado a operar, produzir e consumir. Sao seres incapazes de formular um pensamento critico e original. Uma nacao que por forca do capital tem seu vies ideologico voltado pra direita. Ainda q por vezes elejam um presidente com vies de esquerda, nunca irao evoluir sua consciencia. Sera sempre uma nacao de dementes e ignorantes.
## Nao por acaso que o capital, na sua forma economica ou politica sempre se poe sobre a direita quando tem por objetivo o ganho, financeiro ou de poder. E sobre a direita q se faz os movimentos de massa imbecilizada, pois como seres roboticos sao facilmente levados aonde se quer levar.
## A esquerda liga-se ao mundo das ideias, ao pensamento critico, a modelagem do ser humano como ser consciente. A esquerda e a construcao do pensamento, e o
## individuo pensante e critico, tudo que evolui e eleva o ser humano a um patamar de consciencia superior, provem da esquerda.
## Nacoes capitalistas, ainda q direitista, mas q sua massa possui em sua construcao um vies ideologico de esquerda serao sempre nacoes ricas financeiramente e ricas de estado de consciencia critica. Observa se isto em paises europeus, onde o
## capital existe por forca do consumo, mas coexiste com o estado de bem estar social, com a beleza das artes e com tudo aquilo e natural da esquerda, enquanto consciencia e beleza.
## A direita tem por tendencia natural, o simples, o computacional e robotizado. O argumento da direita e sempre algoritmo, linear e raso. A direita sera sempre um universo de seres ocos, massivos e imbecilizados.
## A.L.C.&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# mais negativo
cat(df_comments$message[df_comments$comment_id == df_comments_unnested$comment_id[most_neg]])&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## No ultimo domingo foi ao ar o programa do Silvio Santos, onde Maisa foi ridiculamente feita de idiota por duas pessoas na frente do Brasil todo. Onde mesmo ela pedindo pra parar, nao pararam. Mas eu to aqui apenas pra falar a verdade sobre uma dessas pessoas: DUDU CAMARGO. 
## Muitos o conhecem como o cara chato que apresenta o jornal da manha, mas eu o conheco como o EX ABUSIVO QUE FEZ COISAS IMPERDOAVEIS. 
## Tudo comecou quando nos conhecemos na escola, quando ele fez de tudo pra me conquistar, e eu era bobo. Cai. Comecamos a namorar e era tudo incrivel e flores, mesmo com o preconceito, era bom ter alguem com quem contar e gostar. 
## As coisas foram andando, e mesmo com todas as pessoas dizendo pra eu nao continuar esse relacionamento, eu continuei. Meus professores, meus amigos, e ate gente desconhecida tentaram abrir meus olhos. Mas ja era tarde. Eu estava apaixonado! Ia fazer de tudo pra ficar com ele!
## O terror comeca. 
## Estava tudo bem, ate ele comecar a ser agressivo e babaca. Gritava, ficava de cara feia, falava merda, e eu tambem, mas ele foi ao extremo. Ele estava vidrado em ser um astro da tv que esqueceu que tinha um relacionamento e comecou a me tratar como lixo, ate que ele me machucou feio, arranhou meu braco e deixou uma cicatriz em uma de nossas brigas. Nao sou santo, claro que tentei revidar para me defender. Mas apenas UMA vez.
## As brigas e abusos tornaram-se serios, ele cada dia era mais estourado, e nao so comigo, com todos, ate com a familia, que por sinal sempre o apoiou. Mas ele sempre queria ser mais que todos.
## Nao contente em 1, ele bateu 2, 3, 4... Varias vezes. Ja cheguei em casa com o rosto machucado e tive que mentir pra minha mae, mas ela nao era e nem e burra, sabia o que tinha acontecido. 
## Na escola, todos perguntavam e eu mentia, achando que estava tudo bem, mas nao estava. Eu estava tao apaixonado que nao conseguia sair daquele pesadelo.
## Outro ponto: ABUSO SEXUAL. Isso e bem serio, e se fosse brincadeira, eu nao estaria me expondo assim. Em um relacionamento voce consequentemente transa com a pessoa, e no comeco era bom, era normal. Mas depois das agressoes, eu fiquei tao em choque que inventava que estava doente para nao ter que fazer nada com ele. E ele reclamava, e bla bla bla, e dizia que sexo nao era nada, mas era. Ate que ele comecou a falar coisas horriveis para mim, comecou ficar tao frustrado que comecou a forcar coisas dizendo que se eu nao fizesse, era pra eu ir embora e nunca mais voltar, e que se eu voltasse, ele me trataria pior do que ja estava tratando. Mas eu fiquei, nao sei o porque, mas fiquei, com todo o medo e receio. 
## Toda vez que eu queria conversar com ele sobre algo, ele ia direto me mandando ficar quieto e me chamava pra fazer coisas... Quando ele nao estava no instagram ou implorando para alguma aparicao na tv. 
## Eu estava louco e assustado, a escola chamou minha mae pra conversar pois estavam com medo de que eu fizesse algo comigo mesmo, meu psicologico estava destruido e eu fiquei com depressao. 
## Algumas pessoas vao dizer que eu quem quis, ja que nao fui embora, mas nao e bem assim. 
## Nao tenho tudo registrado, - e acho que nem preciso - mas eu vivi, as pessoas viram e tentaram me ajudar, mas eu recusei, com medo. 
## Passei muitas coisas boas no comeco, mas depois eu vi o monstro que ele era/e. Ele pode se fazer de santo na tv, mas muita gente sabe quem ele realmente e. E eu sei o que eu sofri. 
## Vao dizer que e tudo mentira, e que eu quero fama. Talvez que sou louco, mas felizmente tenho testemunhas e nao tenho mais medo de me esconder. Depois de domingo, ele provou ser a pessoa mais falsa e artificial que existe. Engessado, segundo a Maisa. 
## Nao e porque ele e uma pessoa publica que pode fazer o que quer sem pagar. Sei das consequencias que posso ter, mas nada tira a paz que sinto sabendo que tenho pessoas incriveis que me apoiaram e apoiam ate hoje. 
## Espero que entendam e ajudem as pessoas que precisam. Se voce sabe alguem que esteja nessa vida, ajude-a a sair... Nao importa quem seja. Chega de abuso!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Por incrível que pareça, tanto o comentário mais positivo quanto o mais negativo falam sobre a esquerda.&lt;/p&gt;
&lt;p&gt;Para prosseguir com a análise, usaremos o léxico OpLexicon para a análise de sentimento:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments %&amp;lt;&amp;gt;% inner_join(
  df_comments_unnested %&amp;gt;% select(comment_id, sentiment = comment_sentiment_op),
  by = &amp;quot;comment_id&amp;quot;
  )
# criar coluna de data (variavel da classe Date)
df_comments$data &amp;lt;- as.Date(df_comments$horario_post)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora sim podemos demonstrar uma visualização de uma análise básica de sentimento: Como tem sido o sentimento geral dos comentários no Sensacionalista ao longo do tempo?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments_wide &amp;lt;- df_comments %&amp;gt;% 
  # filtrar fora palavras neutras
  filter(sentiment != 0) %&amp;gt;% 
  # converter numerico para categorico
  mutate(sentiment = ifelse(sentiment &amp;lt; 0, &amp;quot;negativo&amp;quot;, &amp;quot;positivo&amp;quot;)) %&amp;gt;% 
  # agrupar os dados
  count(data, post_link, post_type, sentiment) %&amp;gt;% 
  # converter para formato wide
  spread(sentiment, n, fill = 0) %&amp;gt;% 
  mutate(sentimento = positivo - negativo) %&amp;gt;% 
  ungroup() %&amp;gt;% 
  arrange(data)

head(df_comments_wide) %&amp;gt;% knitr::kable()&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;data&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;post_link&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;post_type&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;negativo&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;positivo&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;sentimento&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-04-13&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;https://www.facebook.com/sensacionalista/photos/a.187587037950838.39557.108175739225302/1460990897277106/?type=3&#34; class=&#34;uri&#34;&gt;https://www.facebook.com/sensacionalista/photos/a.187587037950838.39557.108175739225302/1460990897277106/?type=3&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;photo&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;7&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;17&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-04-13&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;http://www.sensacionalista.com.br/2017/04/13/temer-lula-e-fhc-articulam-pacto-de-nao-rir-de-brasileiro-que-desfez-amizade-no-facebook-por-politica/&#34; class=&#34;uri&#34;&gt;http://www.sensacionalista.com.br/2017/04/13/temer-lula-e-fhc-articulam-pacto-de-nao-rir-de-brasileiro-que-desfez-amizade-no-facebook-por-politica/&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;link&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;32&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;34&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-04-14&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;https://www.facebook.com/sensacionalista/photos/a.187587037950838.39557.108175739225302/1462221330487396/?type=3&#34; class=&#34;uri&#34;&gt;https://www.facebook.com/sensacionalista/photos/a.187587037950838.39557.108175739225302/1462221330487396/?type=3&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;photo&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;42&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;22&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-04-15&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;https://www.facebook.com/sensacionalista/photos/a.187587037950838.39557.108175739225302/1463589740350555/?type=3&#34; class=&#34;uri&#34;&gt;https://www.facebook.com/sensacionalista/photos/a.187587037950838.39557.108175739225302/1463589740350555/?type=3&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;photo&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;7&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;27&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-04-16&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;https://www.sensacionalista.com.br/2016/03/25/laja-jato-diz-que-lula-comprou-ovo-da-kopenhagen-em-nome-de-amigo/&#34; class=&#34;uri&#34;&gt;https://www.sensacionalista.com.br/2016/03/25/laja-jato-diz-que-lula-comprou-ovo-da-kopenhagen-em-nome-de-amigo/&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;link&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;23&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;35&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-04-16&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;http://www.sensacionalista.com.br/2017/04/10/as-18-melhores-coisas-com-sentimentos-a-nova-obsessao-da-internet/&#34; class=&#34;uri&#34;&gt;http://www.sensacionalista.com.br/2017/04/10/as-18-melhores-coisas-com-sentimentos-a-nova-obsessao-da-internet/&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;link&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;17&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;16&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Por exemplo, o primeiro link coletado na amostra, uma foto, teve 9 palavras contadas como negativas e 13 como positivas. O score geral dos comentários nessa publicação foi 13 - 9 = 4.&lt;/p&gt;
&lt;p&gt;Qual a publicação do Sensacionalista com maior nível de “positividade”? E o de “negatividade”?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments_wide %&amp;gt;% 
  arrange(sentimento) %&amp;gt;% 
  filter(row_number() == 1 | row_number() == nrow(df_comments_wide)) %&amp;gt;% 
   knitr::kable()&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;data&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;post_link&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;post_type&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;negativo&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;positivo&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;sentimento&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-09-11&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;http://www.sensacionalista.com.br/2017/09/11/projeto-do-mbl-pretende-vestir-obras-de-arte-em-museus-ao-redor-do-mundo/&#34; class=&#34;uri&#34;&gt;http://www.sensacionalista.com.br/2017/09/11/projeto-do-mbl-pretende-vestir-obras-de-arte-em-museus-ao-redor-do-mundo/&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;link&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;95&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;39&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;-56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2017-06-05&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;&lt;a href=&#34;http://www.sensacionalista.com.br/2017/06/05/festa-se-nada-der-certo-em-colegio-debocha-de-garis-e-faxineiras-e-mostra-que-ja-deu-tudo-errado/&#34; class=&#34;uri&#34;&gt;http://www.sensacionalista.com.br/2017/06/05/festa-se-nada-der-certo-em-colegio-debocha-de-garis-e-faxineiras-e-mostra-que-ja-deu-tudo-errado/&lt;/a&gt;&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;link&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;29&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;86&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;57&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A publicação que mais recebeu comentários negativos (não tenho certeza se é essa a interpretação mais correta dos resultados, mas enfim) é um link sobre o MBL, enquanto o mais positivo é sobre o famoso caso do “E se der errado”.&lt;/p&gt;
&lt;p&gt;O gráfico abaixo mostra a evolução do sentimento dos comentários nas publicações do Sensacionalista ao longo do tempo:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_comments_wide %&amp;gt;% 
  mutate(index = row_number()) %&amp;gt;% 
  ggplot(aes(x = index, y = sentimento)) +
    geom_col(aes(fill = post_type)) +
    scale_y_continuous(breaks = seq(-60, 60, 20), limits = c(-60, 60)) +
    labs(x = &amp;quot;Índice da publicação&amp;quot;, y = &amp;quot;Sentimento&amp;quot;,
         fill = NULL, title = &amp;quot;Evolução do sentimento em publicações do Sensacionalista&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-09-23-o-sensacionalista-e-text-mining-analise-de-sentimento-usando-o-lexiconpt_files/figure-html/unnamed-chunk-19-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Uma possível interpretação do gráfico é que a série temporal não possui uma clara tendência, apesar de os picos de negatividade serem bem mais frequentes que os de positividade.&lt;/p&gt;
&lt;p&gt;Outra análise que dá para fazer é investigar o nível de sentimento de comentários associados a determinadas palavras. Por exemplo, o quão negativo costuma ser um comentário quando ele menciona a palavra &lt;strong&gt;bolsonaro&lt;/strong&gt;?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# qual o sentimento mais associado a palavras em especifico
df_comments %&amp;gt;% 
  mutate(
    temer = str_detect(str_to_lower(message), &amp;quot;temer&amp;quot;),
    lula = str_detect(str_to_lower(message), &amp;quot;lula&amp;quot;),
    pmdb = str_detect(str_to_lower(message), &amp;quot;pmdb&amp;quot;),
    psdb = str_detect(str_to_lower(message), &amp;quot;psdb&amp;quot;),
    pt = str_detect(str_to_lower(message), &amp;quot;pt&amp;quot;),
    dilma = str_detect(str_to_lower(message), &amp;quot;dilma&amp;quot;),
    doria = str_detect(str_to_lower(message), &amp;quot;doria&amp;quot;),
    governo = str_detect(str_to_lower(message), &amp;quot;governo&amp;quot;),
    bolsonaro = str_detect(str_to_lower(message), &amp;quot;bolsonaro&amp;quot;)
  ) %&amp;gt;% 
  gather(termo, eh_presente, temer:bolsonaro) %&amp;gt;% 
  filter(eh_presente) %&amp;gt;% 
  group_by(termo) %&amp;gt;% 
  summarise(sentiment = mean(sentiment)) %&amp;gt;% 
  ggplot(aes(x = termo, y = sentiment)) + 
    geom_col(fill = &amp;quot;#C10534&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-09-23-o-sensacionalista-e-text-mining-analise-de-sentimento-usando-o-lexiconpt_files/figure-html/unnamed-chunk-20-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Temer e Dilma, os dois presidentes com os piores níveis de popularidade de República, estarem associados a comentários positivos é bem surpreendente. Na verdade, isso ocorre porque a própria palavra &lt;strong&gt;temer&lt;/strong&gt; possui polaridade positiva. Para consultar a polaridade de uma palavra nos datasets presentes no &lt;code&gt;lexiconPT&lt;/code&gt;, use a função &lt;code&gt;lexiconPT::get_word_sentiment()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;get_word_sentiment(&amp;quot;temer&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## $oplexicon_v2.1
##        term type polarity
## 28711 temer   vb        1
## 
## $oplexicon_v3.0
##        term type polarity polarity_revision
## 30160 temer   vb        1                 A
## 
## $sentilex
##       term grammar_category polarity polarity_target
## 6546 temer                V       -1           N0:N1
##      polarity_classification
## 6546                     MAN&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusao-e-chamada-para-futuros-trabalhos&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Conclusão e chamada para futuros trabalhos&lt;/h2&gt;
&lt;p&gt;O pacote &lt;code&gt;lexiconPT&lt;/code&gt;, apesar de simples, tem um enorme potencial para enriquecer o conteúdo de Text Mining em Português na comunidade brasileira de R. O exemplo dado nesse post pode ser considerado deveras simplório. Muitas etapas foram puladas ou desconsideradas com o intuito de fornecer a você uma rápida introdução às possibilidades criadas pelo pacote. Espero que o leitor deste post tenha se sentido motivado a fazer suas próprias análises de sentimento. As possibilidade são incontáveis.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;referencias&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Referências&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://tidytextmining.com/&#34;&gt;Text Mining with R - A Tidy Approach&lt;/a&gt;: Livro online gratuito sobre Text Mining feito pela autora do pacote &lt;code&gt;tidytext&lt;/code&gt;;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://blog.eighty20.co.za//package%20exploration/2017/06/12/sentiment-blog-post/&#34;&gt;Single Word Analysis of Early 19th Century Poetry Using tidytext&lt;/a&gt;;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/leobarone/FLS6397/blob/master/tutorials/tutorial11.Rmd&#34;&gt;Texto no R&lt;/a&gt;;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://ctlente.com/pt/trump-colbert/&#34;&gt;A Fixação de Colbert&lt;/a&gt;;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://leg.ufpr.br/~walmes/ensino/mintex/&#34;&gt;Mineração de Texto - Prof. Walmes M. Zeviani&lt;/a&gt;;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/juliasilge/tidytext&#34;&gt;Pacote tidytext&lt;/a&gt;;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://juliasilge.com/blog/text-mining-stack-overflow/&#34;&gt;Text Mining of Stack Overflow Questions&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/juliasilge/women-in-film&#34;&gt;Women in film&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Slides da minha apresentação no SER 2017</title>
      <link>http://www.sillasgonzaga.com/post/slides-da-minha-apresenta%C3%A7%C3%A3o-no-ser-2017/</link>
      <pubDate>Tue, 23 May 2017 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/slides-da-minha-apresenta%C3%A7%C3%A3o-no-ser-2017/</guid>
      <description>&lt;p&gt;Hoje (23/05), o &lt;strong&gt;Paixão por Dados&lt;/strong&gt; esteve presente no II Seminário Internacional de Estatística com R, realizado na UFF, em Niterói. Com muito orgulho, pude falar de parte dos projetos que realizei com R, conquistas pessoais e profissionais que obtive como resultado pela minha dedicação à linguagem e ainda dicas para quem quer se aprofundar no R.&lt;/p&gt;
&lt;p&gt;Os slides da minha apresentação, entitulada &lt;em&gt;Como o R pode despertar sua paixão por dados&lt;/em&gt; se encontram &lt;a href=&#34;https://www.slideshare.net/sillastg/como-o-r-pode-despertar-sua-paixo-por-dados&#34;&gt;aqui&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Blog Paixão por Dados estará no meetup 1º #AnalyticsEmTudo - Dados Abertos</title>
      <link>http://www.sillasgonzaga.com/post/analytics-em-tudo/</link>
      <pubDate>Thu, 16 Feb 2017 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/analytics-em-tudo/</guid>
      <description>&lt;p&gt;Com muito orgulho, anuncio que o blog Paixão por Dados estará representado no primeiro meetup do grupo &lt;a href=&#34;https://www.meetup.com/analyticsemtudo/&#34;&gt;#AnalyticsEmTudo&lt;/a&gt;, no Rio de Janeiro, onde moro atualmente. Serei um dos três palestrantes do encontro. Falarei um pouco sobre os principais projetos que realizei usando o R, dando maior ênfase aos relacionados a Dados Abertos, que é o tema dessa edição.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>mafs: Analisando a eficácia dos modelos preditivos usados no pacote</title>
      <link>http://www.sillasgonzaga.com/post/mafs02/</link>
      <pubDate>Sun, 29 Jan 2017 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/mafs02/</guid>
      <description>&lt;p&gt;Lancei recentemente a versão 0.0.2 do pacote &lt;code&gt;mafs&lt;/code&gt; tanto no &lt;a href=&#34;https://cran.r-project.org/web/packages/mafs/index.html&#34;&gt;CRAN&lt;/a&gt; como no &lt;a href=&#34;http://github.com/sillasgonzaga/mafs&#34;&gt;Github&lt;/a&gt;. Adicionei dois novos recursos:&lt;br /&gt;
* No data frame &lt;code&gt;df_models&lt;/code&gt; criado, foi acrescentada uma variável referente ao tempo de execução (runtime) do modelo para a série temporal de input. Isso foi uma necessidade devido ao fato de alguns modelos levarem muito tempo para rodar. Esse dado será importante para ser levado em consideração no segundo recurso adicionado:&lt;br /&gt;
* A função &lt;code&gt;select_forecast()&lt;/code&gt; agora tem um argumento chamado &lt;code&gt;dont_apply&lt;/code&gt;, no qual o usuário poderá inserir os modelos (em forma de vetor de caracteres) que não deverão ser usados na função para criar modelos preditivos. Esse recurso é muito útil para excluir da função os pacotes que demoram muito e que não costumam entregar bons resultados.&lt;/p&gt;
&lt;p&gt;Neste post, farei uma demonstração da aplicação do pacote &lt;code&gt;mafs&lt;/code&gt; em diversas séries temporais diferentes.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# carregar pacotes importantes
library(fpp)
library(dplyr)
library(ggplot2)
library(mafs)
library(magrittr)
library(ggrepel)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;os-dados&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Os dados&lt;/h1&gt;
&lt;p&gt;As séries temporais usadas pertencem ao pacote &lt;a href=&#34;https://cran.r-project.org/web/packages/fpp/index.html&#34;&gt;&lt;code&gt;fpp&lt;/code&gt;&lt;/a&gt;, que disponibiliza as séries temporais usadas no livro do Hyndman.&lt;/p&gt;
&lt;p&gt;Vamos armazenar essas diversas séries em uma lista:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;data_fpp &amp;lt;- list(a10 = a10, ausair = ausair, ausbeer = ausbeer,
                 austa = austa, austourists = austourists,
                 cafe = cafe, debitcards = debitcards,
                 elecequip = elecequip, elecsales = elecsales,
                 euretail = euretail, guinearice = guinearice,
                 h02 = h02, livestock = livestock,
                 oil = oil, sunspotarea = sunspotarea,
                 usmelec = usmelec, wmurders = wmurders
                 )
# confirmando que todas as séries são objetos do tipo &amp;#39;ts&amp;#39;, que é a classe
# usada como input para a funcão select_forecast()
lapply(data_fpp, class) %&amp;gt;% unlist&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##         a10      ausair     ausbeer       austa austourists        cafe 
##        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot; 
##  debitcards   elecequip   elecsales    euretail  guinearice         h02 
##        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot; 
##   livestock         oil sunspotarea     usmelec    wmurders 
##        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;        &amp;quot;ts&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Será que todas essas séries são mensais? Podemos confirmar essa informação com a função &lt;code&gt;frequency()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;lapply(data_fpp, frequency) %&amp;gt;% unlist &lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##         a10      ausair     ausbeer       austa austourists        cafe 
##          12           1           4           1           4           4 
##  debitcards   elecequip   elecsales    euretail  guinearice         h02 
##          12          12           1           4           1          12 
##   livestock         oil sunspotarea     usmelec    wmurders 
##           1           1           1          12           1&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# fazer um gráfico
lapply(data_fpp, frequency) %&amp;gt;% unlist %&amp;gt;% table %&amp;gt;% barplot()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-3-1.png&#34; width=&#34;864&#34; /&gt; Temos então 8 séries anuais (frequência 1), 4 trimestrais e 5 mensais. Esse será um bom teste para o pacote &lt;code&gt;mafs&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;modelagem&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Modelagem&lt;/h1&gt;
&lt;p&gt;Para aplicar a função &lt;code&gt;select_forecast()&lt;/code&gt; em todas as séries, é necessário um for loop:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# criar lista vazia para salvar os resultados
df_results &amp;lt;- vector(&amp;quot;list&amp;quot;, length = length(data_fpp))

# iniciar loop
for (i in 1:length(data_fpp)){
  print(i)
  # salvar serie do loop
  data &amp;lt;- data_fpp[[i]]
  # usar tamanho da serie de teste de 6. o horizonte de previsão não importa
  # nao usar modelo híbrido apenas como demonstração do novo arg dont_apply
  mafs_result &amp;lt;- select_forecast(data, test_size = 6, horizon = 3,
                                 error = &amp;quot;MAPE&amp;quot;, dont_apply = &amp;quot;hybrid&amp;quot;)
  
  mafs_result &amp;lt;- mafs_result$df_models
  # acrescentar nome da série no dataframe dos resultados
  mafs_result$serie &amp;lt;- names(data_fpp)[i]
  df_results[[i]] &amp;lt;- mafs_result
}

# converter para data frame
df_results &amp;lt;- bind_rows(df_results)
saveRDS(df_results, &amp;quot;/home/sillas/R/data/2017-01-29-mafs02.Rds&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;analise-dos-dados&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Análise dos dados&lt;/h1&gt;
&lt;p&gt;Uma rápida visualização tabular dos resultados:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;head(df_results)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##        model         ME      RMSE       MAE        MPE      MAPE     MASE
## 1 auto.arima -0.5266278  2.726579  2.317648  -3.404950 11.368897 1.867733
## 2       bats -0.6987866  2.543283  2.302901  -3.848498 11.063269 1.855849
## 3    croston  0.9954976  3.783578  2.770328   2.119148 11.608482 2.232536
## 4        ets -0.1664123  2.315405  2.059765  -1.141468  9.653898 1.659911
## 5      meanf 12.1695849 12.705245 12.169585  52.965233 52.965233 9.807155
## 6      naive -3.3000045  4.920821  4.586426 -17.426919 21.763365 3.696083
##          ACF1 best_model runtime_model serie
## 1 -0.48291837        ets         1.508   a10
## 2 -0.50875972        ets         4.643   a10
## 3 -0.07557645        ets         1.717   a10
## 4 -0.49674756        ets         1.501   a10
## 5 -0.07557645        ets         0.001   a10
## 6 -0.07557645        ets         0.004   a10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vamos ver então quais modelos despontam como os mais rápidos e os mais eficientes.&lt;/p&gt;
&lt;p&gt;Primeiro, um gráfico do tempo de execução por pacote&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(df_results, aes(x = reorder(model, runtime_model, FUN = median),
                       y = runtime_model)) +
  geom_boxplot() +
  labs(x = NULL, y = &amp;quot;Tempo de execução (s)&amp;quot;) +
  coord_flip()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-7-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Percebe-se que os modelos &lt;code&gt;tbats()&lt;/code&gt; e &lt;code&gt;bats()&lt;/code&gt; são os mais computacionalmente custosos. Os mais rápidos são, sem surpresas, os modelos de previsão simples, como o da média simples e o modelo ingênuo.&lt;/p&gt;
&lt;p&gt;Agora, um gráfico da acurácia dos modelos de acordo com a métrica MAPE:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(df_results, aes(x = reorder(model, -MAPE, FUN = median),
                       y = MAPE)) +
  geom_boxplot() +
  labs(x = NULL, y = &amp;quot;MAPE (%)&amp;quot;) +
  coord_flip()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-8-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Alguns modelos apresentaram outliers, o que distorceu o boxplot. Visto que esse gráfico não serviu para muita coisa, é melhor resumir a acurácia por meio da mediana simples do MAPE:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# calcular a mediana do MAPE para cada modelo
df_results %&amp;gt;%
  group_by(model) %&amp;gt;%
  summarise(MAPE_mediano = median(MAPE)) %&amp;gt;%
  ggplot(aes(x = reorder(model, -MAPE_mediano), y = MAPE_mediano)) +
    geom_bar(stat = &amp;quot;identity&amp;quot;) +
    labs(x = NULL, y = &amp;quot;MAPE mediano&amp;quot;) +
    coord_flip() +
    theme_bw()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-9-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Vê-se que os modelos que obtiveram os melhores resultados foram os modelos &lt;code&gt;stlm()&lt;/code&gt;, seja por arima ou por ets. Não vou entrar em detalhes estatísticos sobre o porquê desse resultado para não fugir do escopo do post.&lt;/p&gt;
&lt;p&gt;Vamos então analisar a relação entre tempo de execução e eficácia dos modelos por meio de um gráfico de pontos.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(df_results, aes(x = runtime_model, y = MAPE)) + 
  geom_point() +
  theme_bw()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-10-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;É difícil visualizar alguma relação muito clara nesse gráfico. Ao invés de plotar todos os data points, vamos resumir os dados pela mediana do MAPE e do runtime para cada modelo.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_results %&amp;gt;%
  group_by(model) %&amp;gt;%
  summarise(MAPE_mediano = median(MAPE),
            runtime_mediano = median(runtime_model)) %&amp;gt;%
  ggplot(aes(y = runtime_mediano,  x = MAPE_mediano)) +
    geom_point() +
    labs(y =  &amp;quot;Tempo de execução mediano (s)&amp;quot;,
         x = &amp;quot;MAPE mediano (%)&amp;quot;) +
    geom_text_repel(aes(label = model)) +
    theme_bw()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-11-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Novamente, não é possível determinar que a acurácia do modelo influencia o seu tempo de execução.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;analise-da-influencia-da-frequencia-da-serie&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Análise da influência da frequência da série&lt;/h1&gt;
&lt;div id=&#34;influencia-no-tempo-de-execucao&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Influência no tempo de execução&lt;/h2&gt;
&lt;p&gt;Já que estamos trabalhando ao mesmo tempo com séries trimestrais, mensais e anuais, por que não analisar a influência da variável da frequência da série nos resultados obtidos com o pacote?&lt;/p&gt;
&lt;p&gt;Primeiro, vamos criar um data frame com características sobre as séries analisadas&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_series &amp;lt;- data.frame(
  serie = names(data_fpp),
  frequencia = lapply(data_fpp, frequency) %&amp;gt;% unlist,
  tamanho_serie = lapply(data_fpp, length) %&amp;gt;% unlist
)

# juntar ao dataframe de resultados
df_results %&amp;lt;&amp;gt;% left_join(df_series, by = &amp;quot;serie&amp;quot;)

names(data_fpp)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##  [1] &amp;quot;a10&amp;quot;         &amp;quot;ausair&amp;quot;      &amp;quot;ausbeer&amp;quot;     &amp;quot;austa&amp;quot;       &amp;quot;austourists&amp;quot;
##  [6] &amp;quot;cafe&amp;quot;        &amp;quot;debitcards&amp;quot;  &amp;quot;elecequip&amp;quot;   &amp;quot;elecsales&amp;quot;   &amp;quot;euretail&amp;quot;   
## [11] &amp;quot;guinearice&amp;quot;  &amp;quot;h02&amp;quot;         &amp;quot;livestock&amp;quot;   &amp;quot;oil&amp;quot;         &amp;quot;sunspotarea&amp;quot;
## [16] &amp;quot;usmelec&amp;quot;     &amp;quot;wmurders&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para demosntrar a influência da frequência da série no tempo de execução dos modelos, uma boa opção de visualização é o gráfico de densidade:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_results$frequencia %&amp;lt;&amp;gt;% as.factor

df_results %&amp;gt;%
  filter(runtime_model &amp;lt;= quantile(runtime_model, 0.90)) %&amp;gt;%
  ggplot(aes(x = runtime_model, color = frequencia)) + geom_density()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-13-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;p&gt;É difícil tirar qualquer tipo de conclusão a partir do gráfico acima. Dá para afirmar que a probabilidade de um modelo ter um runtime muito curto (de até 0,25 segundos) é menor para séries mensais e trimestrais do que para mensais.&lt;/p&gt;
&lt;p&gt;Um teste estatístico que pode ser usado para mensura essa relação é o ANOVA e o teste de Tukey:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;anova.fit &amp;lt;- aov(runtime_model ~ frequencia, data = df_results)
summary(anova.fit)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##              Df Sum Sq Mean Sq F value Pr(&amp;gt;F)  
## frequencia    2    221  110.36   3.599 0.0287 *
## Residuals   262   8035   30.67                 
## ---
## Signif. codes:  0 &amp;#39;***&amp;#39; 0.001 &amp;#39;**&amp;#39; 0.01 &amp;#39;*&amp;#39; 0.05 &amp;#39;.&amp;#39; 0.1 &amp;#39; &amp;#39; 1&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Teste de Tukey
anova.fit %&amp;gt;% TukeyHSD()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = runtime_model ~ frequencia, data = df_results)
## 
## $frequencia
##           diff        lwr      upr     p adj
## 4-1  0.4026234 -1.6041101 2.409357 0.8840947
## 12-1 2.0767411  0.1990179 3.954464 0.0260572
## 12-4 1.6741176 -0.4496095 3.797845 0.1530838&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O resultado do teste ANOVA aponta um valor significante (valor p menor que 0,05), o que indica que a hipótese nula de que a frequência da série não influencia o tempo de execução do ajuste pode ser rejeitado.&lt;/p&gt;
&lt;p&gt;Já o teste de Tukey indica que apenas a hipótese nula só pode ser rejeitada para a comparação entre séries mensais e anuais. Para as outras duas comparações, o valor p é maior que 0,05.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;influencia-na-acuracia&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Influência na acurácia&lt;/h2&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df_results %&amp;gt;%
  filter(MAPE &amp;lt;= quantile(MAPE, 0.90)) %&amp;gt;%
  ggplot(aes(x = MAPE, color = frequencia)) + geom_density()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;http://www.sillasgonzaga.com/post/2017-01-29-mafs02_files/figure-html/unnamed-chunk-15-1.png&#34; width=&#34;864&#34; /&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;anova.fit &amp;lt;- aov(MAPE ~ frequencia, data = df_results)
summary(anova.fit)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##              Df   Sum Sq Mean Sq F value   Pr(&amp;gt;F)    
## frequencia    2   900822  450411   10.14 5.71e-05 ***
## Residuals   262 11633340   44402                     
## ---
## Signif. codes:  0 &amp;#39;***&amp;#39; 0.001 &amp;#39;**&amp;#39; 0.01 &amp;#39;*&amp;#39; 0.05 &amp;#39;.&amp;#39; 0.1 &amp;#39; &amp;#39; 1&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Teste de Tukey
anova.fit %&amp;gt;% TukeyHSD()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = MAPE ~ frequencia, data = df_results)
## 
## $frequencia
##             diff        lwr       upr     p adj
## 4-1  -120.929333 -197.28734 -44.57133 0.0006774
## 12-1 -115.580724 -187.02977 -44.13168 0.0005011
## 12-4    5.348609  -75.46111  86.15832 0.9866718&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusao&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Conclusão&lt;/h1&gt;
&lt;p&gt;Uma próxima análise poderia incluir um número maior de séries e de frequências diferentes, como diárias e semanais.&lt;/p&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Anunciando a criação do rbloggers-BR: Seu novo feed de artigos sobre o R</title>
      <link>http://www.sillasgonzaga.com/post/rbloggers/</link>
      <pubDate>Sat, 24 Dec 2016 00:00:00 +0000</pubDate>
      
      <guid>http://www.sillasgonzaga.com/post/rbloggers/</guid>
      <description>&lt;div class=&#34;figure&#34;&gt;
&lt;img src=&#34;https://pbs.twimg.com/profile_images/812516944041668608/r0iERhbF.jpg&#34; /&gt;

&lt;/div&gt;
&lt;p&gt;É com grande orgulho que eu anuncio a concretização de um antigo projeto meu: a criação de um agregador de blogs brasileiros sobre o R, no estilo do &lt;a href=&#34;https://www.r-bloggers.com/&#34;&gt;R-Bloggers&lt;/a&gt;: O &lt;a href=&#34;https://twitter.com/rbloggersbr&#34;&gt;rbloggers-BR&lt;/a&gt;, um bot do twitter que a cada período de tempo posta as mais recentes publicações de blogs relacionados a linguagem R.&lt;/p&gt;
&lt;p&gt;Atualmente, o bot não consegue funcionar em tempo real e sim a cada x horas por meio de um agendador de tarefas (como o Task Scheduler do Windows). Sugestões de como fazer a atualização do bot ser instantânea são bem-vindas.&lt;/p&gt;
&lt;p&gt;Usei apenas três blogs para testar o funcionamento do bot: o meu, o do &lt;a href=&#34;http://neylsoncrepalde.github.io/&#34;&gt;Neylson&lt;/a&gt; e o &lt;a href=&#34;https://blog.symposio.com.br/&#34;&gt;Symposio&lt;/a&gt;. Sugestões de outros blogs são muitíssimo bem-vindas, porém só serão aceitas se feitas por meio de issues no &lt;a href=&#34;https://github.com/sillasgonzaga/rbloggers-BR&#34;&gt;repositório do bot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Esse é um projeto que tem potencial para ser de grande valor para a comunidade R brasileira. Por isso, sinta-se a vontade para contribuir, seja por meio de pull requests ou apenas com ideias e sugestões de melhorias.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>