Google Trends no R


O pacote gtrendsR está passando por uma reformulação e parece que vai ficar ainda mais fácil analisar dados do Google Trends no R. A nova versão ainda não está no CRAN, mas já pode ser testada pelo github. Para instalar:

install.packages("devtools")
devtools::install_github('PMassicotte/gtrendsR', ref = 'new-api')

A grande novidade dessa versão é que não será mais preciso fazer login no google trends para ter acesso. Para brasileiros, outra novidade é que os bugs com problema de encoding parecem estar diminuindo.

Vejamos um exemplo simples, pegando dados das buscas pelos nomes dos candidatos nas eleições de 2014 no Brasil:

library(gtrendsR)
eleicoes2014 <- gtrends(c("Dilma Rousseff", "Aécio Neves", "Marina Silva"), geo = c("BR"), time = "2014-01-01 2014-12-31")
plot(eleicoes2014)

rplot01

Para ilustrar novamente, vejamos um exemplo mais recente — as buscas pelos nomes dos candidatos das eleições norte-americanas:

USelections2016 <- gtrends(c("Donald Trump", "Hillary Clinton"), geo = c("US"), time = "2016-01-01 2016-12-31")
plot(USelections2016)

rplot

Computer age statistical inference e The undoing project


Segue minha sugestão de leitura para as férias de final de ano: um livro de estatística e outro de psicologia/economia comportamental.

Para falar a verdade, ainda não os li, mas já recomendo.

O primeiro livro é do Michael Lewis, sem dúvida um dos melhores cronistas da atualidade (entre outros ótimos livros: Flash Boys, Moneyball, The Big Short). Lewis conta a história da vida e amizade dos dois psicólogos israelenses que começaram a revolução da economia comportamental:Daniel Kahneman e Amos Tversky. Para quem ainda não conhece o trabalho da dupla, vale a pena recomendar de novo o já clássico Thiking, Fast and Slow.

O segundo livro é o mais novo lançamento dos estatísticos Bradley Efron e Trevor Hastie. Os dois fazem um tour histórico e técnico pela revolução computacional dos últimos 60 anos da estatística. Para quem está começando na área, Efron é mais conhecido por seu trabalho no bootstrap; Trevor (junto com Tibshirani), por seus trabalhos em GAMs e modelos esparsos entre outros. Trevor também é co-autor dos já famosos Elements of Statistical Learning e sua recente versão baby An introduction to Statistical Learning — ambos com versões gratuitas na internet (aqui e aqui).

Personalizando seu gráfico do ggplot2 – Exports and Imports, William Playfair


O ggplot2 é muito bom para explorar visualmente, de forma dinâmica, sua base de dados.  Mas às vezes queremos editar cada detalhe do gráfico para uma publicação, é possível fazer isso?

Como, por exemplo, reproduzir o famoso gráfico de exportações e importações do William Playfair?

Playfair-bivariate-area-chart

Hoje resolvi testar o quão difícil seria gerar uma imagem parecida e, brincando um pouco com os parâmetros, cheguei na figura abaixo. É um pouco trabalhoso – pois temos que colocar cada texto separadamente – mas não é difícil, nem tão demorado assim.

playfair

Se você tiver um pouco mais de paciência para ajustar detalhes talvez consiga tornar a reprodução ainda mais fiel. E, caso o faça, favor compartilhar o código com todos por aqui!

***

Segue abaixo o código para gerar o gráfico acima. Os dados bem como o próprio código também estão no github.

 

# load packages -----------------------------------------------------------
library(reshape2)
library(ggplot2)

# prepare data for ggplot2 ------------------------------------------------
## data extracted from https://plot.ly/~MattSundquist/2404/exports-and-imports-to-and-from-denmark-norway-from-1700-to-1780/#plot
playfair <- readRDS("william_playfair.rds")

## create min for geom_ribbon
playfair$min <- with(playfair, pmin(exp, imp))
year <- playfair$year

## melt data
molten_data <- melt(playfair, id.vars = c("year", "min"))

# ggplot2 -----------------------------------------------------------------
ggplot(molten_data, aes(x = year, y = value)) +
geom_line(aes(col = variable), size = 1) +
geom_ribbon(aes(ymin = min, ymax = value, fill = variable), alpha = 0.3) +
scale_color_manual(values = c("darkred", "gold3"), guide = F) +
scale_fill_manual(values = c("#90752d", "#BB5766"), guide = F) +
theme_bw() +
annotate("text", x = year[5], y = 100000, label = "Line", angle = 25, size = 3, family = "Garamond") +
annotate("text", x = year[6] - 100, y = 104000, label = "of", angle = 0, size = 3, family = "Garamond") +
annotate("text", x = year[7], y = 101000, label = "Imports", angle = 340, size = 3, family = "Garamond") +
annotate("text", x = year[5] + 400, y = 73000, label = "Line", angle = 345, size = 3, family = "Garamond") +
annotate("text", x = year[6], y = 70000, label = "of", angle = 330, size = 3, family = "Garamond") +
annotate("text", x = year[7] - 200, y = 64000, label = "Exports", angle = 335, size = 3, family = "Garamond") +
annotate("text", x = year[8], y = 83000, label = "italic('BALANCE AGAINST')", angle = 0, family = "Garamond", parse = TRUE) +
annotate("text", x = year[16] + 400, y = 110000, label = "italic('BALANCE in\nFAVOUR of\nENGLAND')", angle = 0, family = "Garamond", parse = TRUE) +
annotate("text", x = year[16], y = 82000, label = "Imports", angle = 30, size = 3, family = "Garamond") +
annotate("text", x = year[14] + 200, y = 131000, label = "Exports", angle = 65, size = 3, family = "Garamond") +
ggtitle("Exports and Imports to and from DENMARK & NORWAY from 1700 to 1780") +
scale_x_date(breaks = seq(year[1], year[18], by = "10 years"),
labels = format(seq(year[1], year[18], by = "10 years"), "%Y")) +
scale_y_continuous(breaks = seq(0, 190e3, by = 10e3),
labels = seq(0, 190, by = 10)) +
theme(title = element_text(size = 8, face = 'bold', family = "Garamond"),
axis.title = element_blank(),
axis.text = element_text(family = "Garamond"),
panel.grid.minor = element_blank())

Analisando seu histórico de pesquisas do Google


Hoje descobri que é possível fazer o download de todo seu histórico de buscas no Google. TODO seu histórico de TUDO o que você busca no Google. Já que a opção está disponível, por que não dar uma olhada nos dados?

Por alguma razão meu histórico só vai até 2014 — acredito que tenha deletado o histórico anterior — então no meu caso temos apenas dois anos de dados para analisar (não vou considerar 2016 aqui pois o ano ainda não terminou). Além disso, esses dados certamente não contemplam tudo o que pesquisei na internet neste período, porque: (i) além do Google eu uso o DuckDuckGo; e, (ii) muitas vezes não estou logado quando faço pesquisas no próprio Google.

Feitas as ressalvas anteriores, a primeira coisa que tentei montar foi uma nuvem com as palavras mais utilizadas nas buscas. Em 2014 e 2015, segundo o registro do google, fiz aproximadamente 19 mil buscas, utilizando aproximadamente 69 mil palavras-chave. Após remover algumas “stopwords” em inglês e português — isto é, preposições, artigos etc — fiz uma nuvem com aquelas palavras que representam cerca de 20% da frequência total, e o resultado foi o seguinte:

Não tem muita surpresa aí. Previsivelmente, “R” foi a palavra chave mais utilizada, seguida de “package”, “statistics”, “Mac”, “Data”, “Los Angeles”, “UCLA” entre outras.

Após verificar as palavras mais utilizadas, procurei ver se encontrava alguns padrões nos meus hábitos de busca. Primeiramente, calculei a média de buscas por dia da semana. Nesses dois anos, as buscas parecem ter alcançado seu pico de segunda a quarta:

por_semana

Em seguida calculei a média por hora. Tirando a madrugada e o início da manhã, não parece existir diferença significativa entre os horários.  Há, contudo, um problema com essa informação: elas estão no horário brasileiro. Como estive fora do país em certas datas, isso distorce o horário original de algumas pesquisas — e ainda não descobri como consertar esse problema de maneira automática.

por_hora

Essa questão das viagens para fora do país suscitou outra pergunta: o total de buscas no Google Maps altera quando estou viajando? A princípio, diria que sim, e é isso o que o gráfico a seguir mostra, com algumas viagens destacadas:

Isto é, pelo menos neste caso, é muito fácil identificar viagens utilizando apenas a série histórica do total de buscas do Google Maps.

Para finalizar, montei um gráfico com a média de pesquisas por hora, separados por dia da semana e ano, mas não parece ter havido mudança relevante entre os padrões de 2014 e 2015.

hora_semana_ano
Quer analisar seus dados também?

Para fazer o download dos dados, basta seguir essas instruções. Os dados virão em um arquivo zip com vários arquivos no formato JSON. Para tratá-los, você pode se basear no script de R que coloquei aqui.

PS: É um pouco assustador perceber que, com análises bastante simples de dados de busca, já é possível inferir bastante coisa sobre os hábitos de uma pessoa.

Simulando modelos baseados em agentes no R


Rogério começou uma série de posts sobre Agent-Based Models (Modelos Baseados em Agentes) no R. O primeiro post é uma breve explicação sobre Reference Classes e  o segundo post descreve um modelo simples de Predador e Presa.  Vale a pena conferir.

Programação no R: if(), if() else e ifelse()


***

Parte do livro Introdução à análise de dados com R.  Este trabalho está em andamento, o texto é bastante preliminar e sofrerá muitas alterações. 

Quer fazer um curso presencialmente!? Estamos com turmas abertas em Brasília e São Paulo!

Quer fazer sugestões? Deixe um comentário abaixo ou, se você sabe utilizar o github, acesse aqui.

Não copie ou reproduza este material sem autorização.

Volte para ver atualizações!

***

Há ocasiões em queremos ou precisamos executar parte do código apenas se alguma condição for atendida. O R fornece três opções básicas para estruturar seu código dessa maneira: if(), if() else e ifelse(). Vejamos cada uma delas.

O if() sozinho

A estrutura básica do if() é a seguinte:

if (condicao) {

  # comandos que
  # serao rodados
  # caso condicao = TRUE

}
  • O início do código se dá com o comando if seguido de parênteses e chaves;
  • Dentro do parênteses temos uma condição lógica, que deverá ter como resultado ou TRUE ou FALSE;
  • Dentro das chaves temos o bloco de código que será executado se – e somente se – a condição do parênteses for TRUE.

Vejamos um exemplo muito simples. Temos dois blocos de código que criam as variáveis x e y, mas eles só serão executados se as variáveis cria_x e cria_y forem TRUE, respectivamente.

# vetores de condição lógica
cria_x <- TRUE
cria_y <- FALSE

# só executa se cria_x = TRUE
if (cria_x) {
  x <- 1
}

# só executa se cria_y = TRUE
if (cria_y) {
  y <- 1
}

# note que x foi criado
exists("x")
## [1] TRUE

# note que y não foi criado
exists("y")
## [1] FALSE

Note que somente a variável x foi criada. Vamos agora rodar o mesmo bloco mas com TRUE e FALSE diferentes.

# remove x que foi criado
rm(x)

# vetores de condição lógica
cria_x <- FALSE
cria_y <- TRUE

# só executa se cria_x = TRUE
if (cria_x) {
  x <- 1
}

# só executa se cria_y = TRUE
if (cria_y) {
  y <- 1
}

# note que x não foi criado
exists("x")
## [1] FALSE

# note que y foi criado
exists("y")
## [1] TRUE

Note que agora apenas o y foi criado.

O if() com o else

Outra forma de executar códigos de maneira condicional é acrescentar ao if() o opcional else.

A estrutura básica do if() else é a seguinte:

if (condicao) {

  # comandos que
  # serao rodados
  # caso condicao = TRUE

} else {

  # comandos que
  # serao rodados
  # caso condicao = FALSE

}
  • O início do código se dá com o comando if seguido de parênteses e chaves;
  • Dentro do parênteses temos uma condição lógica, que deverá ter como resultado ou TRUE ou FALSE;
  • Dentro das chaves do if() temos um bloco de código que será executado se – e somente se – a condição do parênteses for TRUE.
  • Logo em seguida temos o else seguido de chaves;
  • Dentro das chaves do else temos um bloco de código que será executado se – e somente se – a condição do parênteses for FALSE.

Como no caso anterior, vejamos primeiramente um exemplo bastante simples.

numero <- 1

if (numero == 1) {
  cat("o numero é igual a 1")
} else {
  cat("o numero não é igual a 1")
}
## o numero é igual a 1

É possível encadear diversos if() else em sequência:

numero <- 10

if (numero == 1) {
  cat("o numero é igual a 1")
} else if (numero == 2) {
  cat("o numero é igual a 2")
} else {
  cat("o numero não é igual nem a 1 nem a 2")
}
## o numero não é igual nem a 1 nem a 2

Para fins de ilustração, vamos criar uma função que nos diga se um número é par ou ímpar. Nela vamos utilizar tanto o if() sozinho quanto o if() else.

Vale relembrar que um número (inteiro) é par se for divisível por 2 e que podemos verificar isso se o resto da divisão (operador %% no R) deste número por 2 for igual a zero.

par_ou_impar <- function(x){

  # verifica se o número é um decimal comparando o tamanho da diferença de x e round(x)
  # se for decimal retorna NA (pois par e ímpar não fazem sentido para decimais)
  if (abs(x - round(x)) > 1e-7) {
    return(NA)
  }

  # se o número for divisível por 2 (resto da divisão zero) retorna "par"
  # caso contrário, retorna "ímpar"
  if (x %% 2 == 0) {
    return("par")
  } else {
    return("impar")
  }

}

Vamos testar nossa função:

par_ou_impar(4)
## [1] "par"
par_ou_impar(5)
## [1] "impar"
par_ou_impar(2.1)
## [1] NA

Parece que está funcionando bem… só tem um pequeno problema. Se quisermos aplicar nossa função a um vetor de números, olhe o que ocorrerá:

x <- 1:5
par_ou_impar(x)
## Warning in if (abs(x - round(x)) > 1e-07) {: a condição tem comprimento > 1 e somente o primeiro
## elemento será usado
## Warning in if (x%%2 == 0) {: a condição tem comprimento > 1 e somente o primeiro elemento será usado
## [1] "impar"

Provavelmente não era isso o que esperávamos. O que está ocorrendo aqui?

A função ifelse()

Os comandos if() e if() else não são vetorizados. Uma alternativa para casos como esses é utilizar a função ifelse().

A função ifelse() tem a seguinte estrutura básica:

ifelse(vetor_de_condicoes, valor_se_TRUE, valor_se_FALSE)
  • o primeiro argumento é um vetor (ou uma expressão que retorna um vetor) com vários TRUE e FALSE;
  • o segundo argumento é o valor que será retornado quando o elemento do vetor_de_condicoes for TRUE;
  • o terceiro argumento é o valor que será retornado quando o elemento do vetor_de_condicoes for FALSE.

Primeiramente, vejamos um caso trivial, para entender melhor como funciona o ifelse():

ifelse(c(TRUE, FALSE, FALSE, TRUE), 1, -1)
## [1]  1 -1 -1  1

Note que passamos um vetor de condições com TRUE, FALSE, FALSE e TRUE. O valor para o caso TRUE é 1 e o valor para o caso FALSE é -1. Logo, o resultado é 1, -1, -1 e 1.

Façamos agora um exemplo um pouco mais elaborado. Vamos criar uma versão com ifelse da nossa função que nos diz se um número é par ou ímpar.

par_ou_impar_ifelse <- function(x){

  # se x for decimal, retorna NA, se não for, retorna ele mesmo (x)
  x <- ifelse(abs(x - round(x)) > 1e-7, NA, x)

  # se x for divisivel por 2, retorna 'par', se não for, retorna impar
  ifelse(x %% 2 == 0, "par", "impar")
}

Testemos a função com vetores. Perceba que agora funciona sem problemas!

par_ou_impar_ifelse(x)
## [1] "impar" "par"   "impar" "par"   "impar"
par_ou_impar_ifelse(c(x, 1.1))
## [1] "impar" "par"   "impar" "par"   "impar" NA

Vetorização e ifelse()

Um tema constante neste livro é fazer com que você comece a pensar em explorar a vetorização do R. Este caso não é diferente, note que poderíamos ter feito a função utilizando apenas comparações vetorizadas:

par_ou_impar_vec <- function(x){

  # transforma decimais em NA
  decimais <- abs(x - round(x)) > 1e-7
  x[decimais] <- NA

  # Cria vetor para aramazenar resultados
  res <- character(length(x))

  # verificar quem é divisível por dois
  ind <- (x %% 2) == 0

  # quem for é par
  res[ind] <- "par"

  # quem não for é ímpar
  res[!ind] <- "impar"

  # retorna resultado
  return(res)
}

Na prática, o que a função ifelse() faz é mais ou menos isso o que fizemos acima – comparações e substituições de forma vetorizada. Note que, neste caso, nossa implementação ficou inclusive um pouco mais rápida do que a solução anterior com ifelse():

library(microbenchmark)
microbenchmark(par_ou_impar_vec(1:1e3), par_ou_impar_ifelse(1:1e3))
## Unit: microseconds
##                         expr min  lq mean median  uq  max neval cld
##     par_ou_impar_vec(1:1000)  56  58   85     59  83 1428   100  a 
##  par_ou_impar_ifelse(1:1000) 322 324  411    326 414 2422   100   b