Uma boa maneira de melhorar suas habilidades em R é desenvolvendo projetos práticos, mesmo que não seja algo que envolva aplicar técnicas avançadas estatísticas. Suponha por exemplo que você gostaria que existisse um site que mostrasse no final do dia as principais movimentações na Bovespa, destacando às ações que tiveram uma maior variação em seu preço, em comparação com o preço de fechamento anterior.
Uma boa maneira de trabalhar com dados de ações seria com o pacote `quantmod`. Contudo, para cumprir o desafio proposto neste post, seria necessário baixar manualmente as ações de cada uma das empresas listadas na Bovespa (lista essa que você teria de criar manualmente), agrupar os dados em uma estrutura única (como um data frame) e aplicar as funções de data wrangling necessárias.
Outra, que será a abordada aqui, é pelo web scraping, que consiste em extrair dados de páginas na Internet de forma automatizada. Para isso, esta página no site da Exame nos ajuda. Ela (e as páginas subsequentes) traz uma tabela com o nome da empresa, o preço da ação em R$ e a variação em relação ao fechamento anterior, entre outras. Nós precisamos, então, extrair a tabela da página, fazer a limpeza necessária e trabalhar com os dados. Isso é muito mais fácil do que parece.
Para este tutorial, usaremos estes pacotes:

library(rvest) # web scraping
library(tidyverse) # suite de pacotes
library(magrittr) # pipe = S2
library(stringr) # manipulacao de texto
library(ggthemes) # usaremos para fazer graficos parecidos com o da The Economist
library(ggrepel) # para plotar o nome das principais acoes

Obtenção dos dados

Primeiramente, vamos construir um vetor com todas as páginas das ações.No momento deste post, eram 17:

url.exame 1:17)

Sobre cada página criada no comando acima, será executada a função criada abaixo para extrair a tabela que precisamos:

extrair.tabela %
read_html() %>% # le codigo fonte da pagina
html_table() %>% # extrai tabelas da pagina
.[[2]] %>% # por tentativa e erro, das tabelas retornadas, a que queremos é a segunda
select(1:3) %>% # apenas as tres primeiras colunas sao usadas
set_names(c("acao", "preco_reais", "variacao")) %>% # renomeia colunas
as.data.frame()
}
lista.acoes % map(extrair.tabela)
# checando se deu certo
lista.acoes %>% map_lgl(is.data.frame)

## [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [15] TRUE TRUE TRUE # transformar os 17 dataframes em um so df.acoes % bind_rows() # checando o arquivo str(df.acoes) ## 'data.frame': 489 obs. of 3 variables: ## $ acao : chr "ABCB4\n ABC Brasil PN" "ABEV3\n Ambev S.A" "AEFI11\n FII AESAPAR CI" "AELP3\n AES Elpa ON" ... ## $ preco_reais: chr "16,31" "19,39" "146,25" "6,07" ... ## $ variacao : chr "-0,49" "0,21" "-1,18" "-0,16" ...

 

Limpeza dos dados

Como visto, os dados precisam passar por uma certa limpeza:
A coluna `acao` precisa ser quebrada em duas: uma com o código da ação (Ex.: ABEV3) e outra com o nome da empresa. Ambas são separadas pelo string `\n`;
As duas outras colunas precisam ser convertidas para numéricas.
Essas tarefas também não são difíceis graças aos pacotes `tidyr` e `stringr`:

1) Separar a primeira coluna em duas diferentes pelo separador “\n”

df.acoes %<>% separate(acao, c("codigo_acao", "nome_empresa"), "[\n]")
df.acoes$nome_empresa %<>% str_trim()

2) Converter colunas de preco e de variacao para numerico

df.acoes$preco_reais %<>% str_replace("\\.", "") # remover pontos (ex.:1.004,15)
df.acoes$preco_reais %<>% str_replace(",", ".") # remover pontos (ex.:1.004,15)
df.acoes$preco_reais %<>% as.numeric()
df.acoes$variacao %<>% str_replace("\\.", "") # remover pontos (ex.:1.004,15)
df.acoes$variacao %<>% str_replace(",", ".") # remover pontos (ex.:1.004,15)
df.acoes$variacao %<>% as.numeric()
str(df.acoes)
## 'data.frame': 489 obs. of 4 variables:
## $ codigo_acao : chr "ABCB4" "ABEV3" "AEFI11" "AELP3" ...
## $ nome_empresa: chr "ABC Brasil PN" "Ambev S.A" "FII AESAPAR CI" "AES Elpa ON" ...
## $ preco_reais : num 16.31 19.39 146.25 6.07 4.9 ...
## $ variacao : num -0.49 0.21 -1.18 -0.16 0 -6.57 0.08 0 0 0 ...

 

Análise e apresentação dos dados

Agora já estamos prontos para partir para a análise.
Primeiramente, qual a distribuição da variação dos preços das ações?

df.acoes %>%
ggplot(aes(x = variacao)) +
geom_histogram() +
theme_economist()
unnamed chunk 6 1

No geral, as ações variaram em torno de 0%, com a mediana estando ligeiramente para a esquerda do zero. O histograma mostra que existem alguns outliers, tanto para cima como para baixo.
Vamos analisar quais foram as ações que mais variaram no dia de hoje:

# maiores subidas e quedas:
limite_inferior % sort %>% head(5) %>% .[5]
limite_superior % sort %>% tail(5) %>% .[1]
df.destaque %
filter(variacao <= limite_inferior | variacao >= limite_superior) %>%
arrange(desc(variacao))
df.destaque %>% knitr::kable()

| codigo\_acao | nome\_empresa | preco\_reais| variacao|
|:-------------|:-------------------|-------------:|---------:|
| ELPL3 | AES Eletropaulo ON | 17.58| 19.59|
| CTSA4 | Santanense PN | 2.93| 13.13|
| USIM6 | Usiminas PNB | 5.80| 10.06|
| DTCY3 | Dtcom ON | 3.74| 9.68|
| LLIS3 | Restoque ON | 40.00| 8.11|
| PLAS3 | Plascar ON | 4.10| -7.87|
| SPRI3 | Springer ON | 9.39| -8.83|
| CEPE6 | Celpe PNB | 15.91| -9.09|
| TXRX4 | Têxteis Renaux PN | 2.40| -9.77|
| OGSA3 | NOVA ON | 1.80| -15.89|

Uma boa maneira de resumir o dia de hoje seria em um gráfico que correlaciona o preço da ação com sua variação, destacando o TOP 10 acima:

df.acoes %>%
ggplot(aes(x = variacao, y = preco_reais)) +
geom_point() +
geom_text_repel(data = df.destaque, aes(label = codigo_acao)) +
theme_economist() +
labs(x = "Variação (%)", y = "Preço (R$)",
title = "Painel de resumo diário da Bovespa",
caption = "Blog do IBPAD - Sillas Gonzaga")
unnamed chunk 8 1

No código acima, o pacote `ggrepel` foi usado para plotar os nomes das ações de destaque, garantindo que eles não se cruzassem. Um outlier prejudicou a visualização:

df.acoes %>%
filter(preco_reais > 40000) %>%
knitr::kable()

| codigo\_acao | nome\_empresa | preco\_reais| variacao|
|:-------------|:--------------|-------------:|---------:|
| PPAR3 | Polpar ON | 50000| 0|

O código completo deste post está presente neste gist.