Ling Uage MR Omega Data Science
Ling Uage MR Omega Data Science
Prefácio 3
Contexto e motivação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Objetivos e público alvo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Seleção dos tópicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3 Estruturas de dados 45
3.1 Vetores atômicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.2 Matrizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.3 Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.4 Dados tabulares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5 Funções 77
1
5.1 Funções e seus componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.2 Tratando exceções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.3 Aspectos avançados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.4 De instruções para funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Contexto e motivação
O desenvolvimento da tecnologia da informação atrelado à disponibilidade cada vez maior de
dispositivos como smartphones e tablets conectados à internet vêm trazendo uma verdadeira revolução
em praticamente todas as áreas do conhecimento. A chamada Era da Informação vem alterando
drásticamente a forma como vivemos e interagimos como sociedade.
Com toda esta geração de dados resultante do acesso à internet, empresas de todos os ramos
estão buscando extrair algum tipo de valor destas informações com o objetivo de atrair, manter ou
ainda melhorar a forma de se relacionar com seus clientes. Neste contexto fica evidente que empresas
que sabem lidar de forma efetiva com dados tem uma grande vantagem competitiva. Empresas são
formadas por pessoas, e assim surgiu a profissão denominada de Cientista de Dados. Trata-se de uma
profissão para a qual ainda muito se discute sobre suas atribuições, contudo é composta minimamente
de três habilidades chaves:
i) Conhecimento de ferramentas para análise e resumo de dados. Estas ferramentas em geral são
herdadas de uma área mais antiga chamada de estatística que vem há anos sendo o principal
motor da ciência como um todo.
3
são dominadas por profissionais da ciência da computação.
iii) E por fim, de nada serve conhecer métodos estatísticos e ferramentas computacionais sem saber
exatamente o que fazer com elas para resolver problemas relevantes e gerar valor por meio da
análise de dados. Isso só é possível tendo um conhecimento razoável da área de negócio em
que se está trabalhando.
Neste cenário, o uso de ferramentas computacionais que permitam analisar quantidades mas-
sivas de dados usando métodos estatísticos cada vez mais complexos é de suma importância. A lingua-
gem de programação R pela sua flexibilidade, poder, sofisticação e expressividade vem se tornando
uma das principais ferramentas do cientista de dados em todo o mundo.
O R (como vamos chamar o software daqui em diante) é uma linguagem e ambiente para
computação estatística e gráficos. É um projeto da GNU similar a linguagem S desenvolvida nos
Laboratórios Bell por John Chambers e seus coautores. O R surgiu em 1993 e é de autoria de Ross
Ihaka e Robert Gentleman, do Departamento de Estatística da Universidade de Auckland, na Nova
Zelândia. O R é um software livre e de código aberto sob os termos da Free Software Foundation’s
GNU General Public License. Pode ser facilmente instalado em uma grande variedade de plataformas
UNIX incluindo FreeBSD e Linux, Windows e MacOS.
O público-alvo são profissionais que desejam uma introdução acessível aos fundamentos da
linguagem R de forma rápida e padronizada. Tais fundamentos são necessários para o profundo en-
tendimento da linguagem e uma maior desenvoltura no uso de frameworks específicos de ciência de
dados e modelagem estatística avançada. Formações usuais destes profissionais incluem, mas não está
limitada à: engenharia, ciência da computação, economia, física, administração, gestão da informação
e tecnologia em análise e desenvolvimento de sistemas.
Passada esta ambientação inicial, seguimos para a apresentação das operações matemáticas e
lógicas da linguagem. Após as operações passamos para as estruturas de controle como if, else, switch e
cases, bem como as estruturas de repetição for, foreach, while, repeat e do-until. O conhecimento de tais
estruturas levam a um domínio maior da linguagem, principalmente para implementação de solução
de problemas não explorados.
O texto foi escrito utilizando o sistema para análise reproduzível knitr. Por meio da lin-
guagem de marcação markdown e a estrutura de livro fornecida pelo pacote bookdown dentro da
linguagem R. É uma iniciativa da Ômega Data Science para formar uma geração de Cientistas de
Dados com forte fundamentação matemática e de computação estatística baseada em R.
W.H.B e W.M.Z
1.1 Instalando o R
O R pode ser instalado nos três principais sistemas operacionais em uso: Windows, Linux e
MacOS. Devido a popularidade do sistema operacional Windows nós vamos descrever como fazer a
instalação do R neste sistema operacional. Porém, recomendamos que você migre assim que possível
para um ambiente Linux. Este foi o ambiente no qual escrevemos este livro e é o sistema operacional
que recomendamos para trabalhar com ciência de dados de forma geral. O ambiente MacOS partilha
de muitas similaridades com o Linux e vamos apenas indicar alguns materiais extra sobre a instalação
do R neste ambiente. Vale ressaltar que após a instalação o uso da linguagem R é idêntico em qualquer
um dos sistemas operacionais mencionados.
7
8 Capítulo 1. Instalação do ambiente de trabalho
1. O primeiro passo é acessar o CRAN e selecionar a opção “Download R for Windows”. Proce-
dimento é ilustrado na Figura 1.1.
2. Você será redirecionado para a seguinte página, na qual deve selecionar a opção “install R for
the first time”. Procedimento é ilustrado na Figura 1.2.
3. Uma nova página será aberta, clique em “Download R X.X.X for Windows”. Nesta etapa um
arquivo executável será baixado em seu computador. Procedimento é ilustrado na Figura 1.3.
4. O arquivo executável aparecerá no canto inferior esquerdo do seu monitor, execute-o. Proce-
dimento é ilustrado na Figura 1.4.
Figura 1.4: Captura de tela que mostra o executável no canto inferior esquerdo da tela.
Figura 1.5: Captura de tela que mostra a seleção de idioma para instalação do R.
7. Escolha o local onde o R será salvo no computador, recomendamos seguir a sugestão do insta-
lador. Procedimento é ilustrado na Figura 1.7.
8. Selecione os componentes que devem ser instalados, recomendamos seguir a sugestão do insta-
lador. Procedimento é ilustrado na Figura 1.8.
Figura 1.6: Captura de tela com as informações a respeito do R que o usuário deve saber antes da
instalação.
na Figura 1.9.
10. Informe onde o instalador deve colocar os atalhos do programa, recomendamos seguir a suges-
tão do instalador. Procedimento é ilustrado na Figura 1.10.
11. Selecione as tarefas adicionais, como criar atalhos na área de trabalho. Procedimento é ilustrado
na Figura 1.11.
12. Após estas etapas, finalmente o R será instalado, conforme ilustrado na Figura 1.12.
14. Procure pelo R no menu iniciar e abra-o, conforme procedimento ilustrado na Figura 1.14.
15. Pronto, o R está instalado na sua máquina e pronto para uso. A tela inicial do R é apresentada
na Figura 1.15.
Figura 1.8: Captura de tela que mostra a etapa de seleção dos componentes para instalação.
sudo add-apt-repository \
”deb https://cloud.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran40/”
Note que no passo-a-passo anterior você precisa ser um super usuário (sudo), ou seja, precisa
ter privilégios para instalar softwares em sua distribuição Linux. Após cada comando usando a instru-
ção sudo a senha de super usuário do Linux será requisitada. Esse simples passo-a-passo irá instalar o
R em seu Linux.
Para acessar o R basta você abrir um novo terminal de comando e digitar R. Após esse processo
você deve ver um terminal Linux similar ao apresentado na Figura 1.16
Assim, concluímos com sucesso a instalação do R. Apesar de ser possível usar o R diretamente
pelo terminal Linux ou usando o R Terminal no Windows, tal estratégia é pouco usada por ser im-
produtiva. Em geral é bastante trabalhoso digitar o código direto no terminal. Além disso, o terminal
não oferece nenhum recurso de programação como highlight de código, indentação, acesso rápido e
com bom visual da documentação, entre outros. Por isso, recomendamos que você instale alguma
IDE (Integrated Development Environment). Na próxima seção nós vamos mostrar como instalar a IDE
RStudio e descrever alguns de seus aspectos gerais.
▶ Acesse RStudio download e faça download da versão adequada para seu sistema operacional.
2. Em “Products”, selecione a opção “RStudio: The premier IDE for R”. Procedimento é ilustrado
na Figura 1.18.
4. Role a página até encontrar a opção de download do RStudio Desktop Open Source Edition,
conforme ilustrado na Figura 1.20.
5. Ao clicar na opção de download, você será redirecionado para a página apresentada na Figura
1.21.
10. Escolha a pasta do menu iniciar, recomendamos seguir o padrão, conforme Figura 1.26.
11. Com estas opções feitas, o RStudio será instalado. O procedimento é ilustrada na Figura 1.27.
13. Por fim, você pode abrir o RStudio e terá uma tela similar a apresentada na Figura 1.29.
2. Será feito o download de um arquivo executável, ele aparecerá no canto inferior esquerdo da
tela, conforme ilustrado na Figura 1.31.
3. Ao abrir o executável você será redirecionado a uma tela do gerenciador de software do Ubuntu,
clique em instalar. Procedimento é ilustrado na Figura 1.32.
4. O RStudio será instalado (conforme Figura 1.33) e estará disponível e pronto para uso em seus
aplicativos.
5. Após a instalação você deverá ter a opção de abrir o software RStudio e verá uma tela similar a
apresentada na Figura 1.34.
Para o sistema MacOS o processo é similar e instruções mais detalhadas podem ser vistas em
Instalando RStudio no MacOS.
Figura 1.20: Captura de tela da página do produto Rstudio com as informações do RStudio Desktop
Open Source Edition.
No painel 1 é onde você vai digitar o código R que será executado no terminal R, localizado no
painel 2. Se você acabou de instalar o R no seu computador ou iniciou uma sessão sem abrir nenhum
arquivo com extensão .R, não haverá nada no painel 1. Para criar um arquivo com extensão .R você
deve clicar em File > New File > R script ou usar o atalho Ctrl + Shift + N. Este arquivo pode
ser nomeado e salvo em qualquer pasta de sua preferência, para isso siga o caminho File > Save ou
use o atalho Ctrl + S.
Figura 1.22: Captura de tela da página de download do RStudio IDE com botão de download.
A melhor forma de você conhecer o RStudio é explorando cada um de seus Menus e op-
ções. O RStudio já vem preparado para trabalhar com diversas tarefas envolvendo o R e também suas
interfaces para lidar com bancos de dados relacionais, elaboração de documentos dinâmicos e desen-
volvimento de aplicações web. Além disso tem integração com softwares de versionamento de código
Figura 1.23: Captura de tela que mostra o arquivo executável no canto inferior esquerdo da tela.
como o git. O processo de criação de pacotes R é totalmente integrado a IDE e também é possível
usar outras linguagens como o Python e o C++. É realmente uma ferramenta completa e bastante fácil
de usar.
Recomendamos que o leitor busque dominar alguns atalhos do teclado que podem ser con-
sultados no próprio Menu, do lado direito de cada comando. Uma outra recomendação é trabalhar
sempre com a versão mais atual do RStudio e para projetos grandes usar o recurso de projetos. Todos
esses recursos serão explorados ao longo dos próximos capítulos.
Uma das primeiras opções para usuários Windows do R foi o editor Tinn-R. Trata-se de um
editor genérico de texto para o sistema operacional Windows que integra perfeitamento com o soft-
ware R. O editor possui interface gráfica (GUI) e também o ambiente integrado de desenvolvimento
(IDE). É um projeto registrado sob a Licença Pública Geral GPL, e portanto é um software livre.
Figura 1.25: Captura de tela que mostra a opção de escolha do local de instalação do RStudio no
computador.
Figura 1.26: Captura de tela que mostra a opção de escolha da pasta do menu iniciar.
Importante mencionar que tal editor é mantido e desenvolvido pelo pesquisador José Claudio Faria.
Figura 1.38 apresenta a tela do Tinn-R.
Um dos editores mais poderosos que integra com diversas linguagens de programação é o
GNU-Emacs. O GNU-Emacs está disponível para todos os principais sistemas operacionais e tam-
bém se enquadra na categoria de software livre. É um dos editores mais maduros e estáveis disponíveis,
continua moderno e em evolução. O GNU-Emacs foi o primeiro editor a dar suporte a linguagem R
por meio do módulo ESS (Emacs speaks statistics). Também por meio do ESS o GNU-Emacs oferece
suporte a outros softwares estatísticos como S-Plus, SAS, Stata, Julia entre outros. Além disso, ainda
existem variantes como Spacemacs e Doom-Emacs. A Figura 1.39 apresenta a tela do GNU-Emacs
customizada para a linguagem R.
O Vim é uma outra opção de editor também muito maduro e de propósito geral como o
GNU-Emacs. O suporte para a linguagem R é oferecido pelo plugin Nvim-R. Interessante mencionar
que tal plugin é desenvolvido por dois brasileiros: Jackson Alves de Aquino e José Claudio Faria. A
Figura 1.40 apresenta a tela do Vim já configurada para trabalhar com o R.
O editor VS-Code tem ganhado grande popularidade entre desenvolvedores de outras lin-
guagens como JavaScript, Python entre outras. O suporte para R também é por meio de um plugin.
Mais instruções podem ser encontradas neste endereço https://producaoanimalcomr.wordpress.com/
2020/05/18/utilizando-o-r-no-visual-studio-code/. A Figura 1.41 apresenta a tela do VS-Code cus-
tomizada para o R.
Por fim, existem diversos outros editores que oferecem suporte para a linguagem R; entre eles
citamos:
▶ Atom;
▶ RGedit;
▶ RKward IDE;
▶ NppToR.
Com tantas opções é natural pensar se existe um melhor ou mais adequada para cada usuário.
De forma geral, a escolha do editor ou da IDE acaba sendo uma escolha de cada programador.
Em suma, recomendamos as IDE’s para usuários iniciantes uma vez que a curva de aprendi-
zado é mais rápida. O ambiente já vem todo configurado para uso e as funcionalidades em geral são
simples e fáceis de encontrar. Por outro lado, IDE’s não oferecem muitas opções de customização e
são limitadas em termos de linguagens suportadas.
Os editores são mais gerais, altamente customizáveis e podem trabalhar com diversas lingua-
gens. Geralmente precisam ser configurados e customizados, o que torna seu uso recomendado para
programadores mais experientes e que precisem usar diversas linguagens.
Figura 1.30: Captura de tela que mostra a página de download do RStudio com as possíveis distribui-
ções.
Figura 1.31: Captura de tela que mostra o arquivo executável no canto inferior esquerdo da tela.
Figura 1.33: Captura de tela que mostra a barra de progresso da instalação do RStudio no Ubuntu.
1. Modo REPL (Read, Eval, Print and Loop): a ideia é escrever as instruções ou códigos e avaliá-
las imediatamente no console (Painel 2 do RStudio), que faz o papel de interpretador. Neste
modo o analista vai supervisionando o processo quase que comando a comando. Você escreve
o código, executa, avalia o resultado e segue acrescentando as instruções até obter o resultado
esperado. É a forma mais comum para análise interativa de dados. Em geral, no decorrer deste
livro, vamos usar este modo.
2. Modo Batch: a ideia deste modo é primeiro escrever toda uma sequência de códigos e depois
executá-la de uma única vez através de um terminal de comando. Esse modo é necessário em
ambientes de produção onde as instruções devem rodar sem a supervisão do programador.
Neste livro nós estamos usando o ambiente RMarkdown para mesclar código R e texto usual.
Assim, tudo que estiver após ## é o resultado da execução de uma instrução R. Para você executar
uma instrução R dentro do RStudio você deve posicionar o cursor na linha da instrução que deseja
executar e pressionar CTRL+ENTER.
Dito isso, para começar, abra o RStudio, crie um arquivo com extensão .R clicando em File >
New File > R script. No Painel 1 digite 2 + 3 e tecle CTRL+ENTER para executar. O RStudio enviará
a instrução para ser executada em R e no Painel 2 será exibida a saída da instrução, deve aparecer algo
como:
29
30 Capítulo 2. Primeiros passos com o R
2 + 3
## [1] 5
Algo importante que devemos deixar claro já no começo de nossos estudos é a diferença entre
uma instrução e um comentário. Digite no Painel 1 os seguintes comandos:
Uma instrução vai dizer ao R o que fazer, por exemplo, a instrução 2 + 3 diz para o R somar
os números 2 e 3. Por outro lado, tudo que vem após uma # em R é considerado um comentário. Os
comentários são apenas para documentar o que as instruções fazem e não são executados pelo R. Um
código com comentários relevantes é uma ajuda do seu eu de hoje para o seu eu do futuro, mas evite
comentários óbvios e ambíguos.
Conforme já mencionado, para executar uma instrução no RStudio você deve ao lado de
cada instrução digitar CTRL+ENTER. O código é enviado para o Painel 2 e executado pelo R. É muito
importante ter claro que é a linguagem R quem realmente faz o processamento da instrução, ou seja,
manda a solicitação da tarefa para o seu computador executar. O RStudio é apenas a interface para
você solicitar a execução da instrução pelo R. Fazemos este comentário pois um erro muito comum é
confundir IDE com linguagem.
Com relação às instruções, elas podem ocupar uma única linha ou várias linhas. É possível
também uma linha ter várias instruções. Veja os exemplos abaixo.
## [1] 16
## [1] 16
## [1] 4
## [1] 12
Note que no caso de várias instruções em uma linha o caracter ; é obrigatório para distinguir
entre as instruções.
▶ Mantenha o código devidamente indentado, no RStudio use a tecla de atalho CTRL+I para in-
dentação automática.
P eso
IM C = .
Altura2
Temos que o IMC é igual ao peso dividido pela altura ao quadrado. Para executar tal tarefa
precisamos das seguintes informações: i) do peso e ii) da altura da pessoa. Vamos supor que o peso
seja 83kg e a altura seja 1.80m. Assim, temos os ingredientes para solicitar ao R que faça tal cálculo.
O primeiro passo é criar as quantidades peso e altura e atribuir os valores do nosso exemplo.
peso <- 83
altura <- 1.80
No Código 2.5 nós criamos os objetos peso e altura e atribuimos os valores 83 e 1.80. Note
que, diferente do que ocorre em outras linguagens, não foi necessário criar os objetos antes de atribuir
valores à eles. Além disso, usamos o operador de atribuição <-, leia-se RECEBE. O sinal de = também
pode ser usado como operador de atribuição. Porém, o <- é mais popular e recomendamos que você
o use. Com os objetos devidamente especificados, podemos criar um novo objeto chamado imc que
vai receber o valor da equação avaliada para o peso e altura do nosso exemplo.
## [1] 25.6
Note que ao atribuir a equação peso/altura^2 ao objeto imc o valor do objeto imc não é
mostrado na tela. Para mostrar o valor de um objeto executamos a instrução imc e o seu valor será
mostrado no console R, localizado no Painel 2.
Ao fazer essa sequência de atribuições são criados objetos no que chamamos de área de tra-
balho. Um objeto nada mais é do que um nome que você atribui para dados armazenados. Objetos
são úteis quando precisamos de uma mesma informação repetidas vezes no código, eles podem ser
reutilizados para criar outros objetos; podemos ainda reescrever objetos e até mesmo apagá-los. Para
listar os objetos disponíveis na sua área de trabalho usamos a função ls().
Para trocar o valor de um objeto, simplesmente fazemos uma nova atribuição. Por exemplo,
suponha que a altura da pessoa não é 1.80m mas 1.70m.
# Nova altura
altura <- 1.70
# imc anterior
imc
## [1] 25.6
# novo imc
imc <- peso/altura^2
imc
## [1] 28.7
Uma característica importante é que mesmo após modificar o objeto altura o objeto imc não
é alterado. Para recalcular o imc precisamos re-executar a instrução R que calcula o imc.
Quando há a necessidade de apagar um objeto da área de trabalho usamos a função rm(). Por
exemplo, vamos remover o objeto imc.
rm(imc)
Agora, quando listamos novamente os objetos em nossa área de trabalho, o objeto imc não
estará mais lá.
ls()
Quando você executa o comando ls() o R mostra para você o conteúdo do chamado
.GlobalEnv, ou seja, a área de trabalho global. Porém, existem diversos objetos em outros ambientes
ou espaços. Quando falamos em espaços falamos de pacotes. O R como um projeto colaborativo é
composto por uma grande quantidade de pacotes adicionais. A ideia é que, ao solicitar um objeto,
o R busca primeiro no .GlobalEnv. Ao não encontrar o objeto solicitado, o R começa a buscar em
outros espaços de trabalho. O comando search() mostra a lista de espaços de trabalhos onde o R
vai procurar os objetos e em qual ordem. Além disso, cada pacote tem o seu espaço, o chamado
namespace.
Por exemplo, vamos ver como acessar alguns conjuntos de dados nativos do R.
## height weight
## 1 58 115
## 2 59 117
## 3 60 120
## 4 61 123
## 5 62 126
## 6 63 129
Note que o objeto women não existe no .GlobalEnv. Então, onde está o objeto women? Para
identificar a origem do objeto women usamos a função search()
search()
Note que existem diversos espaços no caminho de busca do R. Por exemplo, podemos ver o
que está disponível dentro do pacote datasets.
ls(”package:datasets”)
Dentro do pacote datasets temos 104 datasets incluindo o conjunto de dados women. Note
que não incluímos a saída do comando no livro para poupar espaço. No entanto, você deve executar
o comando e analisar a saída. Uma pergunta que pode surgir é o que acontece se criarmos um objeto
chamado women.
Agora o objeto women existe no .GlobalEnv. Porém, esse objeto não sobrescreveu o objeto
women, uma vez que estão em namespaces diferentes. O objeto women que existe no .GlobalEnv é apenas
um vetor de caracteres. Por outro lado, o objeto women no namespace datasets continua existindo
e sendo um data.frame. Porém, para acessá-lo é necessário especificar o espaço no qual você está
procurando.
head(datasets::women)
## height weight
## 1 58 115
## 2 59 117
## 3 60 120
## 4 61 123
## 5 62 126
## 6 63 129
log(10)
## [1] 2.3
factorial(10)
## [1] 3628800
Para você começar a usar uma função, o ideal é verificar o que ela precisa para ser executada.
Em R os argumentos de uma função são as entradas que ela necessita para ser executada. Por exemplo,
args(log)
A função log() precisa da entrada x que é um número e tem um argumento extra chamado
de base que permite especificar em qual base o logaritmo deve ser calculado. Por default o valor do
argumento base é exp(1). Você pode usar outra base, por exemplo base 10.
## [1] 1
args(factorial)
## function (x)
## NULL
E outras vão ter diversos argumentos, como a função lm() usada para ajustar modelos lineares.
args(lm)
Neste momento, você deve estar se perguntando: Como saber o que fornecer para cada
argumento? É nesse ponto que consultar a documentação é fundamental. E esse é o assunto da nossa
próxima seção.
Nós ensinamos R há muitos anos e os alunos que estão começando sempre se assustam com
a necessidade de decorar um grande conjunto de comandos. Na verdade, esse é um sentimento que
todos temos quando começamos a programar. No entanto, hoje em dia com a quantidade de recursos
e documentação existentes não é uma preocupação que devemos ter. O importante é saber consultar
a documentação de forma eficiente.
Para acessar a documentação específica de uma função ou objeto podemos usar as funções ?
ou help(). Por exemplo, para consultar a documentação do objeto women fazemos:
# Opção 1
?women
# Opção 2
help(women)
Suponha que você não saiba exatamente o nome da função ou objeto para o qual você quer
consultar a documentação. Neste caso, você pode procurar por funções e objetos por meio de algum
termo de busca. Por exemplo, usando o termo de busca women e a função help.search() temos acesso
a toda a documentação interna que menciona o termo women. O Código 2.6 apresenta um exemplo
de tal estilo de procura.
help.search(”women”)
Uma outra forma de obter ajuda quando você não sabe exatamente o que está procurando
é usar a função apropos(). Basicamente, ela vai procurar por objetos no caminho de procura que
batem com o termo que você está procurando. Por exemplo, vamos consultar o que temos em nosso
caminho de busca que bate com o termo tukey.
apropos(”tukey”)
Neste caso, temos três objetos (você precisa executar o comando para ver os objetos). Agora
basta você consultar a documentação de cada um para descobrir qual é o que você está procurando.
Os vignettes são vínculados a pacotes específicos. Assim, você pode consultar todos os vignettes
disponíveis dentro de um pacote. Por exemplo, o pacote survival é muito popular em R para realizar
análise de sobrevivência. Este é um pacote bastante geral que tem diversas metodologias implementa-
das e tem diversos vignettes explicando em detalhes como usar as metodologias implementadas. Para
consultar os vignettes usamos a função browseVignettes(). O Código 2.7 apresenta como consultar
os vignettes do pacote survival.
browseVignettes(package = ”survival”)
Neste caso, o R abrirá uma nova janela em seu browser onde mostrará os títulos dos vignettes
disponíveis para o pacote solicitado.
Uma outra forma ainda mais geral de obter ajuda é procurar por um termo no site oficial do
R o r-project.org. O R fornece a função RSiteSearch() para você fazer tal busca direto do console R.
Por exemplo, vamos procurar opções para fazer um gráfico do tipo spider plot.
RSiteSearch(”spider plot”)
O R vai abrir uma nova janela em seu browser com os resultados da busca.
Uma outra opção ainda mais geral que é útil quando você está começando em uma área
da estatística ou quer saber como a literatura está se desenvolvendo em algum campo especifico é
consultar o task view. Trata-se de uma parte da documentação do R que agrupa os diversos pacotes
em grandes áreas tais como: Bayesian, Finance, Machine Learning, Natural Language Processing e diversas
outras. Em cada uma destas seções você vai encontrar os principais pacotes da área e uma breve
descrição de suas funcionalidades.
Esta parte da documentação é realmente muito útil. Suponha por exemplo que uma tarefa
relacionada ao processamento natural da linguagem foi dada a você. Porém, você não entende nada
disso. Você pode consultar rapidamente o task view Natural Language Processing e ter uma visão do
que você consegue fazer com os pacotes já implementados em R.
Finalmente, apresentamos uma lista de outras funções que você pode usar para obter ajuda e
conhecer mais sobre funções e objetos R. Convidamos você a explorar a documentação de cada uma
delas.
1. ls().
2. apropos().
3. help().
4. help.search().
5. help.start().
6. RSiteSearch().
7. example().
8. demo().
9. browseVignettes().
10. vignette().
11. str().
12. args().
13. class().
14. methods().
15. find().
Como apresentado nesta seção o R possui uma documentação completa e com diversas opções
para acesso. Assim, não se preocupe em decorar comandos. Quando necessário você pode consultar
a documentação e verificar a sintaxe adequada. É claro, com a experiência você vai acabar decorando
uma série de comandos que são usados diariamente e ganhando fluência na linguagem.
▶ .Rhistory: a cada sessão R você vai digitar diversos comandos e executá-los no console R. O
arquivo .Rhistory guarda todos esses comandos. Por default ele é criado ao abrir a sessão R.
Eventualmente, se no seu diretório de trabalho (pasta do seu computador onde está o script R)
já existir um arquivo .Rhistory ele será carregado automaticamente. Por isso, tome cuidado
com o que você tem ou é carregado em sua área de trabalho (.GlobalEnv). É muito comum ao
iniciar um novo script R limparmos a área de trabalho com o comando rm(list=ls()). Porém,
o recurso de projetos disponibilizado pelo RStudio é uma forma mais elegante de garantir uma
sessão limpa para iniciar a sua programação.
▶ .RData: durante uma sessão de programação em R você vai gerar diversos comandos e objetos
como: funções, conjuntos de dados intermediários, gráficos, entre outros. O R fornece uma
forma de você gravar tudo isso em um arquivo para retomar quando quiser do ponto em que
parou: o chamado .RData. Esse arquivo binário serve para restaurar a sua área de trabalho entre
uma sessão e outra. Isso é útil quando você tem uma sequência de comandos que demora algum
tempo para ser executada. Em geral no dia-a-dia para processamento rápido não usamos esse
recurso, simplesmente executamos tudo novamente.
▶ .Rprofile: esse é um arquivo de configurações carregado ao dar início a uma sessão R. Ele per-
mite ao programador customizar alguns aspectos de todas as sessões R. Por exemplo, você pode
já no inicio da sessão deixar um conjunto de pacotes carregados, configurar uma mensagem de
boas-vindas, entre outros.
▶ .Rproj: esse arquivo define as configurações de um projeto. Está disponível apenas para o
RStudio IDE. Tal arquivo é salvo em seu diretório de trabalho. Para configurá-lo acesse no
RStudio Tools → Global Options. O recurso de projetos do Rstudio é bastante útil, porém
por considerarmos ser de certa forma avançado não vamos explorá-lo em detalhes neste livro
introdutório. Além disso, este não é um recurso da linguagem R e sim da IDE RStudio.
getwd()
## [1] ”/home/wagner/gitprojects/RBASICO”
Na saída aparece todo o caminho de pastas dentro do meu computador até chegar ao diretório
atual de trabalho. Suponha por exemplo que ao invés de trabalhar dentro da pasta RBASICO você queira
que o diretório de trabalho seja apenas a pasta gitprojects. Podemos facilmente trocar o diretório
de trabalho, usando a função setwd().
setwd(”/home/wagner/gitprojects”)
Uma outra função bastante útil é a função dir() que lista todo o conteúdo do seu diretório
de trabalho atual.
dir()
ressaltar que alguns pacotes podem depender de bibliotecas do seu sistema operacional, as chamadas
libs. Neste caso, você terá que instalar tais libs no seu sistema operacional para poder usar o pacote.
Mas não se preocupe, em geral esse é um procedimento bastante simples.
1. Repositórios oficiais como o CRAN, Biocondutor e MRAN. Neste livro a menos que seja dito
o contrário nós vamos usar o CRAN. Quando usamos esse repositório, a instalação do pacote
é feita usando a função install.packages(”NOME_DO_PACOTE”).
2. Nestes mesmos repositórios você pode baixar um arquivo *.tar.gz* e fazer a instalação manual.
Isso é útil caso você precise instalar algum pacote em um computador que não tem acesso livre
a internet.
## CRAN
install.packages(”mcglm”)
## GitHub
install.packages(”devtools”)
devtools::install_github(”wbonat/mcglm”)
Note que, para usar a função install_github() do pacote devtools, nós primeiro tivemos
que instalar o pacote devtools direto do CRAN. Além disso, para não carregar todo o namespace do
pacote devtools usamos o artificio de usar apenas a função que nos interessa especificando qual o
pacote (devtools) e a função (install_github()) ligados por ::.
## Instala
install.packages(”dplyr”)
## Carrega
library(dplyr)
## Descarrega
detach(”package:dplyr”, unload = TRUE)
Após ter instalado um pacote você pode ver todo o seu conteúdo.
library(dplyr)
ls(”package:dplyr”)
help(package = ”dplyr”)
system.file(package = ”dplyr”)
2 + 3 # Soma.
2 - 3 # Subtração.
2 * 3 # Multiplicação.
2/3 # Divisão.
2^3 # Potenciação.
2^(1/3) # Radiciação.
10 %% 3 # Resto.
10 %/% 3 # Parte inteira.
De forma similar, temos diversas funções trigonométricas como apresentado no Código 2.11.
sin(3) # Seno.
cos(3) # Cosseno.
tan(3) # Tangente.
asin(0.5) # Arco seno.
acos(0.5) # Arco cosseno.
atan(0.5) # Arco tangente.
Também existem diversas funções para resumir dados usando técnicas estatísticas como mé-
dia, mediana, desvio-padrão, quantis entre outras, conforme ilustrado no Código 2.12.
Funções matemáticas como exponenciais, logaritmos, gama, beta, entre outras, também estão
prontamente disponíveis, conforme mostra o Código 2.13.
Não podemos deixar de mencionar os operadores lógicos. Tratam-se de expressões que nos
permitem decidir se uma determinada condição é VERDADEIRA ou FALSA. Por exemplo, pode ser
de interesse comparar se dois valores são iguais, diferentes, se um é maior ou menor do que o outro
e assim por diante. O Código 2.14 apresenta os principais operadores lógicos em R.
x <- 3
y <- 4
2 == 2 # São iguais?
2 != 2 # São diferentes?
x <= y # x é menor ou igual a y?
x >= y # x é maior ou igual a y?
x > y # x é maior do que y?
x < y # x é menor do que y?
Note que para todas as perguntas em 2.14 uma resposta lógica (TRUE ou FALSE) é retornada.
Podemos ir mais longe e combinar operações lógicas com os operadores & leia-se E, OR leia-se OU e
NOT leia-se NEGAR. Por exemplo,
Em alguns casos precisamos de alguns objetos especiais. Em particular quatro tipos são muito
populares:
5 + NA # O resultado é NA.
is.na(5 + NA) # Verifica se é NA.
10 + NULL # Retorna objeto vazio.
is.null(NULL) # Verifica se é vazio.
5/0 # Infinito.
is.finite(5/0) # Verifica se é finito.
0/0 # Valor indeterminado.
is.nan(0/0) # Verifica se é not a number.
Abaixo deixamos alguns cartões de referência para consulta futura. Recomendamos escolher
alguns e deixar sempre à mão.
▶ R Reference Card
▶ R Reference Card · Statistics
▶ R base
▶ R base · PT
▶ RStudio IDE
▶ RStudio IDE · PT
Neste Capítulo demos os primeiros passos com a linguagem R. Aprendemos a dar instruções
ao R, criar e manipular objetos. Usar algumas funções elementares, discutimos quais arquivos o R
gera e para que serve cada um. Entendemos onde o R está trabalhando e como mudar o diretório de
trabalho. Discutimos como instalar e gerenciar pacotes adicionais e por fim, operações aritméticas e
lógicas elementares foram apresentadas. Estamos aptos a caminhar na direção de analisar dados com o
R e para isso precisamos aprender quais são as estruturas de dados em R, assunto do próximo Capítulo.
Estruturas de dados
Na maioria das vezes, ao lidar com dados em R, você vai carregar ou ler um conjunto de
dados de alguma fonte externa, tais como de um simples arquivo de texto pleno tipo .csv ou .txt,
sistemas de planilhas eletrônicas como o Excel ou até mesmo de um banco de dados relacional como
o MySQL. Porém, para você entender o que o R faz quando carrega esses conjuntos de dados é
necessário conhecer as estruturas de dados presentes no R e suas principais características.
▶ Caracteres (character);
▶ Números reais (double e numeric);
▶ Números inteiros (integer);
▶ Lógico (logical);
▶ Complexo (complex).
▶ Raw (raw).
Note que temos 3 categorias para representar valores numéricos: double, numeric e integer.
O tipo numeric em R basicamente agrupa os tipos double e integer. Por exemplo,
is.numeric(inteiro)
## [1] TRUE
is.numeric(real)
45
46 Capítulo 3. Estruturas de dados
## [1] TRUE
is.double(inteiro)
## [1] FALSE
is.double(real)
## [1] TRUE
is.integer(inteiro)
## [1] TRUE
is.integer(real)
## [1] FALSE
Para facilitar, quando estivermos falando sobre números de forma geral, sejam inteiros ou
reais vamos relacionar a classe numeric. Só vamos distinguir quando estivermos tratando de números
inteiros e houver necessidade de fazer uso do tipo integer. Por isso, na lista acima colocamos double
e numeric na mesma linha.
O que chamamos de conjuntos de dados ou dados de forma geral nada mais são do que
combinações estruturadas dos tipos de objetos descritos acima. Como organizar ou estruturar tais
dados é o que chamamos de estruturas de dados. O R vem equipado com diversas estruturas para
trabalhar com dados. Essas estruturas recebem o nome genérico de classes (class()).
▶ atomic vector(vetores atômicos): a característica mais importante dos vetores é que todos os
seus elementos devem ser do mesmo tipo. É a estrutura mais simples para trabalhar com dados
em R. Na realidade, as outras estruturas são de alguma forma construídas baseadas em vetores
atômicos.
▶ matrix (matrizes): a classe matrix implementa a álgebra matricial usual em matemática. Você
pode pensar uma matriz como sendo um conjunto de vetores, empilhados ou colocados lado a
lado. Novamente, todos os elementos dentro de uma matriz devem ser do mesmo tipo.
▶ list (lista): a classe list, apesar de técnicamente ser também um vetor, é uma das estruturas
mais gerais para trabalhar com dados em R. Isso se deve ao fato de que os elementos dentro de
uma lista podem ser de tipos diferentes.
▶ data.frame (tabela): A classe data.frame é de certa forma uma simplificação da classe list. Na
verdade um data.frame é simplesmente uma lista bidimensional. É a classe mais usada para
armazenar dados para análise estatística em R. Você pode pensar na classe data.frame como o
equivalente a uma tabela do Excel em termos de formato, já que trata-se de uma estrutura que
agrupa vetores em um tabela bidimensional em que cada coluna pode conter um tipo diferente
de dado. Porém, dentro de cada coluna todos os registros devem ser do mesmo tipo.
Vamos discutir cada uma destas estruturas em detalhes nas seções seguintes.
## [1] 1 5 11 33
O R permite nomear objetos de forma bastante geral. Um nome pode consistir de letras,
números (dígitos), e _. No entanto, um nome não pode começar com _ ou um dígito. Além disso,
você não pode usar nenhuma das palavras reservadas, tais como, TRUE, FALSE, NULL, if entre outras.
Veja a documentação detalhada usando ?Reserved. Um nome que não seguir essas restrições será
considerado non-syntactic name e o R vai retornar um erro.
Você pode usar o nome que achar mais conveniente, desde que este nome atenda às men-
cionadas restrições. Em geral, recomendamos usar nomes simples e autoexplicativos considerando o
contexto que você está trabalhando. Podemos também facilmente definir um vetor de caracteres
números 1, caso TRUE ou 0, caso FALSE. Por outro lado, ao concatenar um vetor númerico com um
vetor de caracteres o mais geral é o caracter. Então, o R irá converter todos os números para caracteres
e o tipo do vetor resultante será caracter.
Para consultar qual é o tipo de um objeto usamos a função is.*. No lugar do * colocamos
o nome do objeto que queremos avaliar. Por exemplo, para saber se um objeto é do tipo numérico
executamos is.numeric() a resposta será sempre um resultado lógico. Veja os exemplos no Código
3.1.
x <- ”caracter”
typeof(x)
## [1] ”character”
x <- 10
typeof(x)
## [1] ”double”
x <- 10L
typeof(x)
## [1] ”integer”
x <- TRUE
typeof(x)
## [1] ”logical”
x <- 10
is.numeric(x)
## [1] TRUE
x <- as.logical(x)
is.numeric(x)
## [1] FALSE
Note que criamos um objeto chamado de x que contem o valor 10. Esse objeto foi criado pelo
R como um numeric. Depois convertemos esse objeto para o tipo logical. Assim, quando consulta-
mos a sua classe como numeric recebemos o lógico FALSE. O R é uma linguagem automaticamente
tipificada, isso significa que quando você criar um objeto, o R vai automaticamente atribuir um tipo
a ele. Obviamente, nem sempre o tipo que o R atribui é o desejado. Portanto, fique atento a essa
característica do R.
Outro aspecto importante é a classe (class) de um objeto R. Os métodos vão se orientar pela
classe para saber o que é possível fazer com um determinado objeto. Métodos são funções genéricas
do R que atuam de forma diferente de acordo com o tipo de objetos as quais são aplicadas. Por fim,
alguns objetos podem conter metadados que em R são chamados de atributos.
O Código 3.2 cria um vetor numérico, acessa sua classe e os métodos associados.
# Um vetor numérico.
x <- c(1, 2, 3)
class(x)
## [1] ”numeric”
methods(class = ”numeric”)
Veja que para um objeto da classe numeric você tem diversas opções de ações. Você pode
fazer por exemplo, um histograma (histrogram), um gráfico da distribuição acumulada (ecdfplot),
converter para um objeto da classe data.frame, ou mesmo para a classe Date e assim por diante.
Vamos criar um outro vetor numérico, porém agora com um atributo adicional.
## [1] ”numeric”
attributes(notas)
## $names
## [1] ”João” ”Bianca” ”Eduarda”
names(notas)
Neste exemplo, o atributo foi o nome, porém de forma geral um objeto R pode ter diversos
atributos dependendo de sua classe. Uma outra característica de um vetor é o seu tamanho (length).
A função length() acessa o tamanho de um vetor, por exemplo,
length(notas)
## [1] 3
Para lidar com dados em R é útil saber criar sequências e repetições de números de forma
estruturada. Uma outra tarefa que aparece com frequência em estatística e aprendizagem de máquina
é a geração de números aleatórios. O R fornece funções para rapidamente gerar sequências, repetições
e números aleatórios.
Para criar sequências regulares usamos a função seq() conforme ilustrado no Código 3.4.
# Sequencia de 1 a 7
1:7
## [1] 1 2 3 4 5 6 7
# Sequencia de 1 a 10 de 2 em 2
seq(from = 1, to = 10, by = 2)
## [1] 1 3 5 7 9
# Sequencia de 1 a 20 de tamanho 7
seq(from = 1, to = 20, length.out = 7)
## [1] 1 3 5 7 9 11 13
De forma similar, para criar repetições usamos a função rep() conforme ilustra o Código 3.5.
# Repita o 0 5 vezes
rep(0, 5)
## [1] 0 0 0 0 0
## [1] 1 2 3 1 2 3
## [1] 1 1 2 2 3 3
Dada uma sequência, podemos ter interesse em obter uma amostra aleatória desta sequência.
Para isso, usamos a função sample() conforme ilustrado no Código 3.6.
## [1] 15 9 1 2 14 11 5 20 3 13
## [1] ”b” ”b” ”c” ”b” ”b” ”c” ”a” ”a” ”b” ”a”
Por fim, podemos simular amostras aleatórias de acordo com distribuições de probabilidade
como a uniforme e normal. Se você não conhece essas distribuições não se preocupe, apenas fique
com essa possibilidade na memória.
## [1] 0.586 0.877 0.813 0.381 0.231 0.825 0.927 0.696 0.911 0.831
## [1] 1.81 1.84 1.81 1.75 1.90 1.72 1.90 1.90 1.77 1.79
Tendo um vetor, podemos ter interesse em selecionar apenas alguns elementos que tenham
alguma característica importante, ou ainda, por nome ou posição. Além disso, podemos querer mo-
dificar, remover ou adicionar elementos.
Para selecionar por posição usamos a seguinte sintaxe nome_objeto[posicao]. Por exemplo,
## João
## 7.8
notas[5] # A posição 5.
## Márcia
## 6.5
notas[1:2] # Um intervalo.
## João Bianca
## 7.8 10.0
## João Eduarda
## 7.8 8.5
notas[-1] # Remove.
No caso de seleção condicional combinamos as operações lógicas com a posição dos elemen-
tos. Neste caso criamos o que chamamos de máscara lógica. Por exemplo,
notas[mask]
## Bianca
## 10
## João
## 7.8
notas[c(”Márcia”, ”Eduarda”)]
## Márcia Eduarda
## 6.5 8.5
Além de objetos R para armazenar diferentes tipos de dados, vetores numéricos são também
entidades matemáticas para as quais diversas operações como soma, subtração, multiplicação por es-
calar, produto interno, entre outras são bem definidas.
O Código 3.7 ilustra as operações mais importantes com vetores. Para executar tais operações,
os vetores devem ser compatíveis. Novamente se você não está familiarizado com operações vetoriais
não se assuste. A Ômega Data Science oferece um curso único de matemática para Ciência de Dados
que explica em detalhes todas essas operações.
# Soma
a + b
## [1] 5 7 9
# Subtração
a - b
## [1] 3 3 3
## [1] 40 50 60
# Produto interno
a%*%b
## [,1]
## [1,] 32
## [1] 4 10 18
## [,1]
## [1,] 32
## [,1]
## [1,] 0
Note, no Código 3.7, a diferença entre o operador especial de multiplicação vetorial %*% e o
de multiplicação *.
a <- c(4,5,6,5,6,7)
b <- c(1,2,3)
a + b
## [1] 5 7 9 6 8 10
Note que os vetores a e b, definidos em 3.8, não são compatíveis para soma. Em termos
matemáticos a soma não faz sentido, no entanto o R retorna um vetor.
3.2 Matrizes
Em R uma matriz (matrix) é uma simples extensão dos vetores atômicos. Matrizes não são
um tipo diferente de objeto, são apenas vetores atômicos com dimensões, ou seja, número de linhas
e colunas. Da mesma forma que nos vetores, todos os elementos de uma matriz devem ser do mesmo
tipo. Para definir uma matriz em R precisamos minimamente definir os dados (data) que vão compor
a matriz e sua dimensão, número de linhas (nrow) e colunas (ncol).
## [,1] [,2]
## [1,] 1 0
## [2,] 0 1
Para acessar a dimensão de uma matriz usamos a função dim(matriz). Matrizes em R são
preenchidas por colunas. Por exemplo,
## [,1] [,2]
## [1,] 1 5
## [2,] 2 6
## [3,] 3 7
## [4,] 4 8
Caso seja de interesse, podemos pedir ao R para preencher a matriz por linhas usando o argu-
mento byrow = TRUE.
## [,1] [,2]
## [1,] 1 2
## [2,] 3 4
## [3,] 5 6
## [4,] 7 8
Os elementos de uma matriz são acessados pela sua posição. Uma matriz é indexada por dois
índices, linha e coluna.
## [1] 2
## [1] 5
## [1] 2 4
## [,1] [,2]
## [1,] 3 4
## [2,] 5 6
A seleção condicional é feita da mesma forma que para vetores usando máscara lógica, por
exemplo,
## [1] 5 7 6 8
Podemos combinar duas matrizes desde que tenham o número de linhas e colunas compatí-
veis.
## [,1] [,2]
## [1,] 1 2
## [2,] 3 4
## [3,] 5 6
## [4,] 7 8
## [5,] 1 2
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
# Matriz transposta
t(A)
## [,1] [,2]
## [1,] 10 40
## [2,] 20 50
## [3,] 30 60
# Soma e subtração
A <- matrix(c(1,2,3,4,5,6), nrow = 3, ncol = 2)
B <- matrix(c(10,20,30,40,50,60), nrow = 3, ncol = 2)
C <- A + B
D <- A - B
# Multiplicação matricial
A <- matrix(c(2,8,6,-1,3,7), nrow = 3, ncol = 2)
B <- matrix(c(4,-5,9,2,1,4,-3,6), nrow = 2, ncol = 4)
C <- A%*%B
# Produto de Hadamard
A <- matrix(c(1,2,3,4), nrow = 2, ncol = 2)
B <- matrix(c(10,20,30,40), nrow = 2, ncol = 2)
C <- A*B
# Matriz inversa
A <- matrix(c(4, 2, 7, 6), 2, 2)
A_inv <- solve(A)
## Conferindo: deve resultar na matriz identidade
A%*%A_inv
## [,1] [,2]
## [1,] 1 0
## [2,] 0 1
## $modulus
## [1] 8
## attr(,”logarithm”)
## [1] FALSE
##
## $sign
## [1] 1
##
## attr(,”class”)
## [1] ”det”
## [1] 7
3.3 Listas
A estrutura list é similar ao atomic vector porém não há a necessidade de todos os elemen-
tos serem do mesmo tipo. Por exemplo, podemos ter uma lista armazenando elementos dos tipos
numeric, logical, matrix, data.frame e até mesmo outra list. Assim, a estrutura de lista é bastante
flexível e útil. Para criar uma lista usamos list() e para converter um objeto para a classe list usamos
as.list(). O Código 3.10 ilustra a criação de uma lista com objetos da classe numeric, character,
logical e complex.
## [[1]]
## [1] 10
##
## [[2]]
## [1] ”Dez”
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] 1+10i
## [1] 10
## [1] 1+10i
## [[1]]
## [1] 10
##
## [[2]]
## [1] 1+10i
## $Números
## [1] 10 100
##
## $Caracter
## [1] ”Dez” ”Cem”
##
## $Logico
## [1] TRUE
##
## $Complexo
## [1] 1+10i
minha_lista$Logico
## [1] TRUE
minha_lista$Números
## [1] 10 100
attributes(minha_lista)
## $names
## [1] ”Números” ”Caracter” ”Logico” ”Complexo”
Listas são muito úteis dentro de funções, conforme será discutido no Capítulo 5. As vezes os
elementos da lista são chamados de slots.
str(iris)
Neste caso o objeto iris é um data.frame com 150 observações de 5 variáveis, sendo quatro
delas numéricas e uma do tipo fator. A classe factor é muito similar a classe character. No entanto
a classe factor é comumente usada para representar categorias ou níveis (levels). Por exemplo,
## [1] alta baixa baixa media alta media baixa media media
## Levels: alta baixa media
Uma funcionalidade dos fatores é que você pode definir a ordem dos níveis. Isso será útil
para ordenar os níveis em gráficos e tabelas. Por exemplo, suponha que desejamos que os níveis
sejam apresentados na seguinte ordem: baixa, media, alta.
Agora se fizermos uma tabela com esses dados a ordem será respeitada.
table(fator)
## fator
## baixa media alta
## 3 4 2
Até aqui nós usamos apenas o data.frame iris que já vem com o R. No entanto, podemos
criar um data.frame do zero. Por exemplo,
Para objetos da classe data.frame as opções de inspeção são amplas. Abaixo listamos as prin-
cipais funções.
Note que cada coluna de um data.frame é um atomic vector e portanto só pode ter ele-
mentos de mesmo tipo. Para acessar os elementos de um data.frame usamos uma sintaxe similar a
usada com matrizes.
Por fim, as colunas podem ser acessadas pelo nome, usando o operador $.
iris$Sepal.Length
▶ Os principais tipos de dados são: character, numeric, integer, logical, complex e raw.
▶ Lembre-se:
▶ Listas: caso especial de vetor em que cada elemento pode ser uma estrutura diferente.
▶ Data frames: tabela. Cada coluna é um vetor, portanto os elementos dentro da coluna são
de mesmo tipo mas os tipos entre colunas podem ser diferentes.
4.1.1 if-else
A estrutura if-else é a estrutura de controle mais simples e permite desviar a execução do
código conforme uma condição lógica. A Figura 4.1 ilustra o fluxograma da estrutura if-else.
A ideia é muito simples: uma instrução chega para ser executada e avalia-se uma condição,
representada pelo losango amarelo. Caso a condição seja verdadeira, a instrução representada pelo
retângulo verde é executada. Caso contrário, ou seja, condição é falsa, a instrução representada pelo
retângulo laranja é executada.
Vamos fazer um exemplo para ilustrar a ideia: suponha que ao final de um semestre um
professor avalia a frequência e nota de um aluno para decidir se ele será aprovado, reprovado por nota
ou reprovado por faltas. Por simplicidade, considere os seguintes critérios (condições) apresentados
abaixo.
63
64 Capítulo 4. Estruturas de controle e repetição
Note que a condição deve ser um lógico, ou seja, apenas os valores TRUE ou FALSE são possíveis
de serem avaliados. Note ainda que se um vetor lógico for fornecido a função retornará um erro.
Vamos retornar ao exemplo de calcular o IMC de uma pessoa. Porém, além de obter o IMC
vamos também avaliá-lo de acordo com os seguintes critérios:
Considere uma pessoa com 1, 60m e 80kg e que a tabela assume que o IMC foi calculado com
apenas uma casa decimal de precisão. Assim, precisamos forçar o R a usar apenas uma casa decimal.
Isso pode ser feito usando a função round().
peso <- 80
altura <- 1.60
imc <- round(peso/(altura^2), 1)
## [1] ”Obeso”
imc
## [1] 31.2
4.1.2 switch
A estrutura switch() é uma espécie de empilhamento de estruturas if-else. O objetivo é
oferecer uma estrutura mais clara e computacionalmente mais eficiente. O caso mais comum de uso
da estrutura switch() é para testar um valor contra um conjunto de strings. É também comum
incluir uma condição final de escape, conforme ilustrado na Figura 4.3.
A ideia é que existe um conjunto de condições representadas pelos losangos na Figura 4.3.
Quando uma delas é verdadeira, a instrução representada pelos quadrados é executada. No caso
da Figura 4.3, a condição de escape é o quadrado em azul, ou seja, se nenhuma das condições for
verdadeira a condição de escape será executada. A sintaxe é ilustrada na Figura 4.4.
Podemos adaptar a nossa rotina para avaliar o IMC de uma pessoa usando a estrutura switch()
conforme ilustrado abaixo. Porém, precisamos de uma função extra para descobrir em qual intervalo
de IMC a pessoa está.
## [1] ”Obeso”
Para exercitar o uso das estruturas de controle apresentadas vamos fazer mais um exemplo.
Considere a tabela de alíquotas do imposto de renda de pessoa física apresentada na Figura 4.5.
A faixa de salário está na coluna 1 da tabela, ao lado da alíquota que deve ser paga. Vamos
começar usando a estrutura if-else considerando um usuário com renda = 2000.
renda = 2000
if (renda < 1903.98) {
aliquota <- 0
} else if (renda > 1903.99 & renda < 2826.65) {
aliquota <- 7.5
} else if (renda > 2826.66 & renda < 3751.05) {
aliquota <- 15
} else if (renda > 3751.06 & renda < 4664.68) {
aliquota <- 22.5
} else {
aliquota <- 27.5
}
aliquota
## [1] 7.5
## [1] 7.5
Para usar a função findInterval() primeiro foi necessário criar um vetor com as classes de
renda. Considerando uma determinada renda (2000, em nosso exemplo), a função findInterval()
retorna um inteiro dizendo a qual classe a renda pertence. Por exemplo, renda = 2000 está na segunda
faixa. Como a função começa contando do 0, o valor retornado é 1. A entrada para a função switch()
precisa ser uma string, ou seja, um objeto da classe caracter. Por isso, foi necessário usar a função
as.character() para converter o inteiro retornado pela função findInterval() em uma string. Por
fim, a função switch() encontra qual a faixa de aliquota. Caso não seja nenhum valor inteiro entre 0
e 3 a função retorna aliquota = 27.5, a nossa condição de escape.
As estruturas if-else e switch não são vetorizadas. Isso significa que para um vetor elas
devem ser avaliadas em cada um dos seus elementos de forma separada. Existem algumas opções
nativas do R que são vetorizadas. Isso significa que quando aplicadas a um vetor a função irá avaliar
cada um de seus elementos.
Nosso interesse é criar uma regra para avaliar se o aluno está aprovado, em exame ou repro-
vado. A regra é a seguinte, se a nota for maior ou igual a 70, o aluno está aprovado. Se a nota for
maior ou igual a 40 e menor que 70, o aluno está em exame. E se a nota for menor que 40, o aluno
está reprovado. Agora, nós queremos fazer esse teste para todos os alunos. A função ifelse permite
realizar tal tarefa facilmente.
Note que executamos o teste lógico para todos os elementos do vetor notas, por isso dizemos
que esta função está vetorizada ou é vetorizada.
De maneira similar, o pacote dplyr oferece uma versão vetorizada da função switch(). Por
exemplo,
Note que para usar a função case_when() é necessário ter instalado o pacote dplyr. Caso
você não tenha execute install.packages(”dplyr”).
4.2.1 for
Em geral, quando o número de execuções é conhecido usamos a estrutura for. O for vai
iterar sobre uma sequência de elementos executando alguma tarefa em cada elemento. A ideia é
ilustrada na Figura 4.6.
A instrução pode ser lida da seguinte forma: para i indo de 1 até n faça tal tarefa. O i é o
índice de iteração e vai percorrer cada entrada do vetor onde a operação deve ser aplicada.
Para ilustrar a ideia considere que é de interesse obter o quadrado de cada um dos valores
inteiros entre 1 e 5. O Código 4.2 realiza esta tarefa.
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25
quadrado
Figura 4.6: Fluxograma de uma estrutura de repetição tipo FOR. Fonte: os autores.
## [1] 1 4 9 16 25
▶ Usamos a variável i como indexador, porém poderíamos usar qualquer outra letra.
▶ Para ilustração usamos a função print() para imprimir cada iteração do nosso for.
Vamos fazer um segundo exemplo que consiste em somar dois vetores. A soma de dois
vetores é possível quando os vetores são do mesmo tamanho e é definida por a + b = (ai + bi ) =
(a1 + b1 , . . . , an +n ).
Em R podemos implementar esta operação usando o loop for, conforme o Código 4.3.
a <- c(1, 2, 3)
b <- c(5, 6, 7)
resultado <- c()
for(i in 1:length(a)) {
resultado[i] <- a[i] + b[i]
}
resultado
## [1] 6 8 10
Por fim, é importante saber como parar um loop for de acordo com alguma condição con-
trolada durante a avaliação das instruções, ou mesmo não parar dado alguma condição não esperada
nas iterações. Para estas duas tarefas temos as funções break e next. Pra ilustrar o seu uso, suponha
que desejamos somar os elementos de um vetor, porém quando a soma for superior a 10 devemos
parar o loop for. Além disso, caso o vetor tenha um elemento como NA o loop não deve parar, apenas
passar para o próximo elemento.
## [1] 1
## [1] 3
## [1] 4
## [1] 8
## [1] 8
## [1] 11
## [1] 11
4.2.2 while
Outra estrutura de repetição é o while. A principal diferença em comparação com o for é
que o número de repetições é desconhecido. A ideia é que a instrução será executada até que uma
condição lógica seja atendida. A Figura 4.8 ilustra a estrutura while.
No while a condição de parada deve ser especificada. Para ilustrar esta estrutura vamos criar
uma rotina que vai somar números aleatórios de uma distribuição Uniforme(a = 0, b = 1) até que a
soma destes números seja maior do que quatro. A condição final de parada será simular 12 números.
n_numbers <- 12
total <- 0
i <- 1L
while(i < n_numbers) {
u <- total + runif(1)
if(sum(u) > 4) break
total <- u
i <- i + 1L
}
4.2.3 repeat
A última estrutura de repetição que vamos discutir neste capítulo é o repeat. É uma estrutura
muito parecida com a estrutura while e usada quando precisamos executar um conjunto de instruções
por um número desconhecido de vezes até que uma condição seja atendida. A principal diferença
é que a condição pode ser testada em qualquer posição dentro das instruções a serem executadas.
Novamente, é muito importante que a condição de parada seja declarada para evitar que o loop seja
interminável. A Figura 4.10 ilustra a ideia da estrutura repeat.
Como ilustração vamos usar o mesmo exemplo da estrutura while, conforme o Código 4.4.
total <- 0
i <- 1L
repeat {
u <- total + runif(1)
if(sum(u) > 4) break
total <- u
i <- i + 1L
}
Para terminar este Capítulo vamos ver como usar tais estruturas para resolver um problema
um pouco mais complicado.
Considere o seguinte jogo: lançar três dados até que os valores das faces sejam uma sequência,
por exemplo, 3, 4 e 5 ou 1, 2 e 3. Nosso objetivo é contar quantas tentativas são necessárias até atingir
o resultado.
Figura 4.10: Fluxograma de uma estrutura de repetição tipo REPEAT. Fonte: os autores.
Para simular o lançamento de três dados podemos usar a função sample(). Note que o lan-
çamento de um dado nada mais é do que sortear um número aleatóriamente entre 1 e 6 todos com
igual probablidade, ou seja, 1/6. O segundo passo é ordenar os valores simulados. A função sort()
serve a este propósito. Por fim, precisamos decidir se os valores são ou não uma sequência. Uma
ideia simples é calcular a diferença entre os valores ordenados. Se todas as diferenças forem iguais a 1
temos uma sequência, caso contrário não temos. Em R a função diff() calcula a diferença entre os
elementos de um vetor. Vamos primeiro fazer um protótipo.
## [1] 1 5 3
# Ordenando
l1_ordenado <- sort(l1)
# Decidindo se é sequencia
diff(l1_ordenado)
## [1] 2 2
Note que no passo 3 precisamos olhar se todos os elementos do vetor de diferenças são iguais
a 1. Existem diversas formas de fazer isso. Neste exemplo, optamos por usar a função ifelse que já
é vetorizada, ou seja, vai avaliar todos os elementos do vetor.
Porém, ela retorna dois valores, o que ainda não é suficiente para decidir se é uma sequencia
ou não. Para ser sequência ambos os testes lógicos devem ser verdadeiros. Assim, se somarmos o
resultado do ifelse, o R vai converter a saída lógica para numeric. Por fim, se a soma for igual a dois
significa que temos uma sequência, caso contrário não é uma sequência. Com esses detalhes, estamos
aptos a implementar a nossa rotina.
## [1] 3 3 6
## [1] 2 2 6
## [1] 3 4 5
tentativas
## [1] 3
Neste caso foram necessárias três tentativas até obter uma sequência. Perceba que quando
você realizar essa mesma instrução no seu computador provavelmente você vai obter um número
diferente de tentativas. Isso se deve ao fato do algoritmo ter um componente aleatório: o lançamento
dos dados. Em termos computacionais nós não estamos realmente jogando os dados. O R tem um
mecanismo para imitar o que seria o lançamento do dado, porém não é realmente aleatório, é o que
chamamos de pseudo aleatório. Assim, se você quiser obter exatamente o mesmo resultado que foi
apresentado antes de executar a sua instrução inclua set.seed(123). Esse comando fixa a semente
aleatória e permite você replicar exatamente o mesmo experimento realizado no livro.
Também fica claro que cada vez que você fizer o experimento você vai obter um número
diferente de tentativas até obter uma sequência. Por exemplo, se realizarmos o experimento nova-
mente
## [1] 1 2 4
## [1] 1 2 6
## [1] 3 4 4
## [1] 1 4 6
## [1] 1 4 6
## [1] 1 2 4
## [1] 5 5 5
## [1] 3 4 5
tentativas
## [1] 8
observamos um número diferente de tentativas, neste caso oito foram necessárias para obter
uma sequência. Uma pergunta natural agora é: em média quantos lançamentos são necessários para
obter uma sequência?
A resposta pode ser obtida repetindo o conjunto de instruções um grande número de vezes,
digamos, 1000. E guardar o número de tentativas necessárias para posteriormente calcular a média.
Porém, vamos postergar tal tarefa para o próximo Capítulo onde vamos ver como encapsular um
conjunto de instruções em uma função.
▶ if-else: estrutura usada para definir o que fazer caso uma condição seja aceita.
▶ while e repeat: realizam uma tarefa até que uma condição seja aceita.
Funções
Basicamente, uma função recebe um conjunto de entradas, faz algum tipo de processamento
77
78 Capítulo 5. Funções
e retorna uma saída. Funções permitem organizar o código separando-o em partes específicas. Essa
prática facilita encontrar e corrigir erros e evita incluir novos erros ao tentar corrigir outros. A ideia
é que, como as tarefas estão divididas em funções específicas, quando um erro surgir você saberá em
qual função ele ocorreu e poderá proceder com a correção. Além disso, você pode aproveitar funções
desenvolvidas em um projeto para outros projetos similares.
## $IMC
## [1] 24.7
##
## $Classificao
## [1] ”Adequado”
Neste exemplo usamos uma abordagem diferente. Apenas encontramos a classe em que o
IMC se encaixa e pareamos com as classificações. Assim, não foi necessário usar as estruturas de
controle como o if-else ou switch. Essa é uma característica interessante do R. Existem diversas
formas de fazer a mesma tarefa.
A Figura 5.2 ilustra cada um dos componentes da função implementada no Código 5.1.
▶ Nome da função: usado para chamá-la. Em geral usamos as mesmas premissas usadas para
nomear objetos.
▶ Lista de parâmetros: também chamado de lista de argumentos formais que passa argumentos
atuais ou valores como variáveis de entrada.
Os argumentos de uma função podem ser qualquer estrutura de dados que vimos no Capítulo
4. Exemplos comuns são vetores, matrizes, listas e data.frames. Os argumentos podem conter valores
default, ou seja, se nada for especificado para um argumento o valor default será usado. Por exemplo,
vamos especificar na nossa função para calcular o IMC que o valor default para o peso é de 80kg. A
nova função fica dada por
## $IMC
## [1] 24.7
##
## $Classificao
## [1] ”Adequado”
Note que agora na chamada da função calcula_imc() não foi necessário informar o peso da
pessoa. Por default este argumento tem o valor de 80kg. No caso de não querer usar o valor default
basta passar o valor desejado para o argumento da função, por exemplo,
## $IMC
## [1] 27.8
##
## $Classificao
## [1] ”Pré-obeso”
Recomendamos que os argumentos para os quais você especifica valores default sejam decla-
rados após os argumentos sem valores default. No nosso exemplo, o primeiro argumento é a altura
para a qual não especificamos um valor default.
Outra curiosidade sobre funções em R é que você pode passar os valores para os argumentos
simplesmente pela ordem em que são definidos. Por exemplo,
calcula_imc(1.80, 90)
## $IMC
## [1] 27.8
##
## $Classificao
## [1] ”Pré-obeso”
Note que não foi necessário digitar o nome dos argumentos. Apesar de ser possível passar
os argumentos desta forma, recomendamos que sempre passe os argumentos nomeados. Isso ajuda
muito a encontrar erros, dar manutenção no código e a trabalhar em equipe.
Outro cuidado é sempre deixar explícito o que a sua função está retornando usando a função
return. Apesar de não ser obrigatório consideramos uma boa prática. Se você não usar a função
return() a sua função irá retornar o que for executado por último no corpo da função.
## $IMC
## [1] -10
##
## $Classificao
## character(0)
Neste caso o IMC foi calculado como −10, um valor que obviamente não faz sentido prático.
Além disso, a nossa estrutura de classificação não encontrou uma classificação coerente e retornou va-
zio. Se o resultado de nossa função fosse usado como entrada de algum outro cálculo, provavelmente
teríamos um erro. Assim, é importante tratar as exceções, ou seja, avisar ao nosso usuário quando
ele informar valores para os argumentos que não fazem sentido. Temos diversas opções para fazer o
tratamento de exceções as mais populares são:
▶ warning(): usa-se para imprimir mensagens de aviso. Ela causa a saída [Warning].
▶ stop(): usa-se para imprimir mensagens de erro. Ela causa a saída [Error] e interrompe a
execução do código.
No caso do cálculo do IMC tanto o peso quanto a altura devem ser valores estritamente
positivos. Assim, vamos parar a execução do código caso a altura ou o peso sejam menores do que
zero.
## Error in calcula_imc(altura = 1.9, peso = -90): Peso deve ser maior do que zero.
Quando se implementa funções para usos gerais que provavelmente serão usadas por diferen-
tes públicos, é importante fazer os tratamentos de exceção e fazer a função se comunicar de forma
clara com o usuário. Tratar exceções é prever desvios de uso e/ou de processamento causados, por
exemplo, por:
Suponha que na função para calcular o IMC não desejamos especificar quais serão seus argu-
mentos. Podemos escrever a função da seguinte forma:
## $IMC
## [1] 31.2
##
## $Classificao
## [1] ”Obesidade”
Note que não especificamos os argumentos da função, apenas usamos os três pontos. Porém,
na chamada da função explicitamente passamos o peso e a altura. Obviamente, se você passar qualquer
nome para os argumentos, o R não saberá o que fazer e retornará um erro. O uso deste tipo de recurso é
enconrajado apenas para usuários avançados e quando realmente necessário, contudo é algo que você
verá ao usar diversas funções do R.
## $IMC
## [1] 24.9
##
## $Classificao
## [1] ”Adequado”
Note que mesmo não passando nenhum valor para o argumento altura2 o R não reportou
nenhum erro. Isso se deve simplesmente ao fato de que o R não avaliou o argumento altura2. Além
disso, mesmo que um valor seja passado para altura2 nenhum erro será reportado, simplesmente
porque esse argumento não é usado em nenhum lugar dentro da função.
## $IMC
## [1] 24.9
##
## $Classificao
## [1] ”Adequado”
Assim, quando a sua função tiver diversas opções de avaliação, é importante verificar que
todas funcionam conforme esperado e retornar mensagens de erros informativas quando necessário.
Agora vamos pegar esse conjunto de instruções e transformar em uma função. Novamente,
usando o pensamento algorítmico divimos a tarefa nos seguintes passos:
Vamos nomear a função de acordo com o que ela faz: joga_dados(). Os argumentos são os
aspectos que vamos controlar do jogo. Neste caso, vamos controlar o número de dados que vamos
jogar, que será o argumento n_dados. Precisamos ter uma condição de escape para o loop while que
é o número máximo de tentativas, vamos chamar de n_max. Por default vamos fixar que vamos tentar
no máximo 1000 vezes. Por fim, a função irá retornar um número (numeric) que é o número de
tentativas necessárias para obter uma sequência.
## [1] 8
Finalmente, podemos usar a nossa função para responder a pergunta: em média quantos
lançamentos são necessários para obter uma sequência?
## [1] 8.97
Ao trabalhar com ciência de dados lidamos com dados de todos os tipos e formatos diaria-
mente. O R nativamente apresenta um conjunto completo de ferramentas para importar, manipular
e exportar conjuntos de dados. Neste Capítulo vamos discutir os aspectos básicos da manipulação de
dados usando os princípios do tidy data ou dados arrumados. Este termo ficou famoso após o artigo
Tidy Data. O chamado tidy data segue três principios:
A Figura 6.1 mostra uma representação de conjunto de dados em que os 3 princípios do tidy
data são atendidos.
A ideia parece simples mas na prática pode ser confuso. A melhor forma de entender esses
conceitos é por meio da prática e exemplos. Neste Capípulo vamos lidar com um conjunto de dados
bem simples para ilustrar os principais aspectos da importação, manipulação e exportação de dados.
No Capítulo 8 nós vamos retornar a todos esses conceitos por meio de um exemplo real similar ao
que você vai encontrar no dia-a-dia ao analisar dados.
85
86 Capítulo 6. Lidando com dados com o R base
A principal diferença entre esses formatos é o caracter ou estratégia usada para separar as
colunas da base de dados. Os nomes são auto-explicativos, por exemplo, no formato .csv as colunas
são separadas pelo símbolo , (vírgula). Em português usamos a , para representar decimais, por isso
usamos o ; (ponto e vírgula) para separar as colunas. Na prática tanto faz o formato desde que você
saiba qual é o caracter ou estratégia usada para delimitar as colunas.
Você pode estar pensando: por que não ler a planilha eletrônica diretamente? Isso é uma
possibilidade, porém a extensão das planilhas está atralada ao sistema operacional. Por exemplo: você
não terá planilhas do famoso Microsoft Excel em um sistema operacional Linux. Da mesma forma,
você não terá arquivos do tipo .odf do pacote OpenOffice, popular em ambientes Linux, em sistemas
operacionais Windows. Assim, optamos por usar arquivos de texto pleno porque são mais portáveis
e, de certa forma, agnósticos ao sistema operacional e/ou software fabricante.
Sigla;Código;Município;idh;renda;alfab;
AC;1200013;Acrelândia;;136,539;73,309;
AC;1200054;Assis Brasil;;115,16;70,954;
AC;1200104;Brasiléia;;132,383;75,493;
AC;1200138;Bujari;;118,815;61,23;
AC;1200179;Capixaba;;108,164;62,283;
AC;1200203;Cruzeiro do Sul;;140,199;71,548;
AC;1200252;Epitaciolândia;;134,364;75,381;
Note que o caracter ; separa as colunas. Isso significa que ; é o caracter delimitador de
campos. De forma similar um arquivo .tsv tem a seguinte estrutura
Neste caso o espaço em branco é quem delimita as colunas. Já o formato .fwf tem a seguinte
estrutura
1 2 PAUL TERGAT (69) M2529 0:44:47
2 6 HENDRICK RAMAALA (72) M2529 0:45:05
3 4 ELIJAH KIPTARBEI LAGAT (98) M0014 0:45:08
4 7 SILVIO GUERRA (68) M3034 0:45:17
5 12 JOHN GWAKO (78) M2024 0:45:29
6 1 EMERSON ISER BEM (73) M2529 0:45:37
7 134 JOSE TELES DE SOUZA (71) M2529 0:45:56
8 25 GERALDO DE ASSIS (65) M3034 0:46:04
9 14 PATRICK NDAYISENGA (71) M2529 0:46:11
Este formato é um pouco mais complicado, uma vez que não tem um caracter delimitador
de colunas. As colunas são especificadas pelo número de caracteres. Assim, você precisará saber qual
o comprimento em número de caracteres de cada coluna. Por ser mais complicado é um formato
menos popular.
Em R a função read.table() oferece uma forma simples de importar a maioria dos formatos
de dados em texto pleno. Ela oferece uma série de opções, vejamos os seus argumentos
args(read.table)
## function (file, header = FALSE, sep = ””, quote = ”\”’”, dec = ”.”,
## numerals = c(”allow.loss”, ”warn.loss”, ”no.loss”), row.names,
## col.names, as.is = !stringsAsFactors, na.strings = ”NA”,
## colClasses = NA, nrows = -1, skip = 0, check.names = TRUE,
## fill = !blank.lines.skip, strip.white = FALSE, blank.lines.skip = TRUE,
## comment.char = ”#”, allowEscapes = FALSE, flush = FALSE,
## stringsAsFactors = FALSE, fileEncoding = ””, encoding = ”unknown”,
## text, skipNul = FALSE)
## NULL
Você deve estar atento a alguns aspectos da base de dados que quer importar:
▶ Qual é o caracter que representa decimais? Lembre-se, em inglês o decimal é representado pelo
ponto (.) ao invés da vírgula (,) como usamos em português.
▶ Qual foi o encoding usado para criar a base de dados? Esse é um aspecto mais avançado e diz res-
peito a forma como o computador armazena os caracteres que usamos de forma geral. Existem
diversos tipos de encoding e o tipo usado vai depender do seu sistema operacional. Geralmente,
sempre que criar e trabalhar com a base de dados no mesmo sistema operacional você não vai
precisar se preocupar com encoding. Porém, tenha em mente que se você importar um con-
junto de dados para o R e surgirem alguns caracteres estranhos, provavelmente você tem um
problema de encoding. Em geral, basta você abrir na sua planilha preferida e trocar o encoding
para algo como utf8 e dizer isso ao R por meio do argumento encoding.
Além destes tópicos devemos saber qual é o diretório dentro do computador onde o arquivo
está gravado. Neste livro leremos a base de dados direto de um endereço web. Porém, se não for o
seu caso, basta você especificar o caminho do seu arquivo no argumento file. Uma recomendação
é deixar a base de dados no mesmo diretório do seu arquivo .R. Assim, você pode simplesmente
fixar o diretório de trabalho como o diretório atual usando a função setwd() e ler o arquivo apenas
especificando o nome.
Para começar, vamos importar um arquivo no formato .txt que está no seguinte endereço:
http://leg.ufpr.br/~walmes/data/anovareg.txt. Você pode acessar este endereço e ver que o caracter
separar neste exemplo é um tab. Assim, devemos especificar o argumento sep = ”\t”. Além disso,
cada coluna tem um nome, por isso o argumento header = TRUE deve ser usado. O Código 6.1 ilustra
como importar esta base de dados para o R.
Vamos importar um outro conjunto de dados agora no formato .csv que está no seguinte
endereço web: http://leg.ufpr.br/~wagner/data/reglinear.csv. Novamente, você pode abrir o arquivo
em um editor de texto e ver que o caracter separador é um espaço em branco.
Se a leitura foi executada com sucesso você não deve receber nenhuma mensagem. O erro
mais comum neste ponto é não especificar o caminho de diretórios corretamente, neste caso você vai
receber uma mensagem como esta
O R está dizendo que não conseguiu encontrar o arquivo que você deseja ler no diretório
que ele está procurando. Lembre-se: para saber onde o R está trabalhando use o comando getwd().
Com a leitura bem sucedida, é hora de inspecionar o que foi carregado. Recomendamos usar
a função str() para ter uma visão geral do arquivo.
#str(dados)
A função str() começa mostrando quantas observações e quantas colunas a base de dados
contém. Neste caso temos 20 observações (linhas) e 2 variáveis (colunas). Na sequência é apresentado
o nome de cada coluna e o tipo de dado que ela representa. Neste exemplo, temos variáveis do tipo
num e int que são abreviações dos tipos de vetores numeric e integer.
Uma situação comum é quando os dados vêm em muitas colunas. Este caso ocorre muito
na tabulação de dados de experimentos nos quais se tem medidas realizadas ao longo do tempo. Por
exemplo,
n <- 3
tb1 <- data.frame(”trat” = LETTERS[1:n],
aval1 = rpois(n, 4),
aval2 = rpois(n, 4),
aval3 = rpois(n, 4))
tb1
Neste caso temos que as observações da variável de interesse está em três colunas. Além disso,
a variável avaliação, que deveria ser uma única coluna, está no título de três outras colunas. Este
é um exemplo típico de dados desorganizados que está no formato amplo (wide). Nós queremos
transformá-lo para o formato longo (long), de modo que cada variável esteja em uma coluna e cada
observação em uma linha.
▶ O argumento direction indica que vamos criar um novo data.frame no formato longo.
▶ O argumento v.names recebe o nome da coluna que vamos criar para receber os valores que
estavam nas colunas aval1, aval2 e aval3.
▶ O argumento varying indica quais colunas tem os valores que queremos empilhar.
▶ O argumento idvar é o nome da coluna que vai identificar múltiplas observações provenientes
do mesmo grupo, neste caso os tratamentos entre as múltiplas avaliações.
▶ O argumento new.row.names dá nomes novos às linhas do data.frame criado. Neste caso como
o novo data.frame terá nove linhas, elas foram nomeadas com os inteiros entre 1 e 9.
Podemos também fazer a operação inversa, ou seja, desempilhar caso seja de interesse.
reshape(data = tb1_long,
direction = ”wide”,
timevar = ”avaliação”,
idvar = ”trat”,
v.names = ”resposta”)
Outra operação que pode ser necessária é separar uma variável em várias outras. Isso é comum
quando um campo de texto é a união de várias informações. Considere o seguinte data.frame.
A coluna ano_mod é a mistura das variáveis ano do veículo e modelo do veículo. De forma
similar, a coluna local é a mistura de duas variáveis: município e unidade federativa (UF). Assim,
precisamos separar estas informações. Note que o caracter / é quem separa as informações na coluna
ano_mod enquanto que na coluna local o caracter - é o separador. Neste caso precisamos primeiro
separar as informações e depois criar novas colunas com as informações corretas. Para separar as
informações usamos a função strsplit.
## [[1]]
## [1] ”2011” ”2012”
##
## [[2]]
## [1] ”2012” ”2012”
##
## [[3]]
## [1] ”2015” ”2016”
## [[1]]
## [1] ”Curitiba” ”PR”
##
## [[2]]
## [1] ”Santos” ”SP”
##
## [[3]]
## [1] ”Viçosa” ”MG”
A função strsplit() retornou uma lista. Agora precisamos manipular esta lista para depois
juntá-la com o data.frame original.
## X1 X2
## 1 2011 2012
## 2 2012 2012
## 3 2015 2016
A ideia deste código é que a função do.call() vai a cada slot da lista e aplica a função
rbind() que serve para agregar linhas em um vetor. O objeto resultante é uma matrix que é então
convertida para um data.frame. Podemos renomear as colunas do data.frame gerado e acoplar com o
data.frame original.
# Renomeando
names(ano_temp) <- c(”Ano”, ”Modelo”)
names(local_temp) <- c(”Município”, ”UF”)
# Acoplando
tb_nova <- cbind(tb, ano_temp, local_temp)
tb_nova
Para manter os princípios do tidy data precisamos remover as colunas desnecessárias. Para
isso vamos usar a função subset(). Como o nome sugere, esta função captura um sub conjunto do
conjunto de dados original. O argumento select permite remover as colunas não necessárias pelo
nome, basta colocar o sinal - na frente do nome das colunas que desejamos remover.
Da mesma forma que podemos separar variáveis que estão em uma mesma coluna pode ser
necessário criar novas variáveis unindo os valores de duas ou mais colunas. Por exemplo, suponha que
em um data.frame os campos dia, mês e ano estão cada um em uma coluna. Porém, você precisa de
apenas uma coluna que represente a data da observação. Assim, precisamos juntar essas três colunas
em uma nova. Uma função bastante útil nesta situação é a função paste(). A ideia é simplesmente
colar os caracteres criando um novo caracter.
A solução de certa forma funcionou. No entanto, o caracter criado não tem a classe de data.
Podemos agora converter para a classe Date
Por fim, teremos frequentemente que lidar com dados faltantes. Algumas vezes podemos
simplesmente querer remover todas as linhas que tenham algum NA. Outras vezes queremos preencher
com algum valor específico. Ambas as situações são comuns e facilmente executadas em R. Veja o
seguinte conjunto de dados
Para excluir todas as linhas que contenham pelo menos um NA temos a função na.exclude.
na.exclude(tb)
Para substituir os campos iguais a NA por algum valor, como 0, primeiro precisamos localizá-
los e depois atribuir o valor.
tb[is.na(tb)] <- 0
tb
Uma estratégia similar pode ser aplicada para cada coluna atribuindo valores diferentes de-
pendendo do contexto. É importante salientar que tratar dados ausentes de forma estatisticamente
coerente nem sempre é uma tarefa fácil. Assim, pense muito bem o que os dados faltantes significam
no contexto que você está trabalhando e se faz sentido substituir por algum número ou mesmo excluir
da base de dados.
▶ Resumir ou sumarizar dados. Essa ação é a mais complexa e em geral envolve o uso de medidas
estatísticas como média, mediana, desvio-padrão entre outros para criar resumo dos dados.
Para ilustrar o uso de cada uma destas ações vamos considerar os dados apresentados na Figura
6.2.
Figura 6.2: Uma tabela com dados fictícios sobre alunos e seus desempenhos.
A ação mais simples é a de ordenação, ilustrada na Figura 6.3. A ordenação pode ser feita de
acordo com os valores de uma ou mais variáveis.
Em R a função order() retorna a ordem crescente de um vetor. Podemos usar esta função
para reordenar um data.frame de acordo com os valores de uma ou mais de suas colunas. Por exemplo,
podemos ordenar os alunos pelo número de matrícula, como ilustrado na Figura 6.3 (linha 1).
df1[order(df1[, ”matricula”]),]
A ideia é similar para ordenar por duas ou mais colunas. Por exemplo, ordenando por curso
e nota da prova 1.
Para ordenar de forma descrescente basta usar a função order() com o argumento decreasing
= TRUE.
Suponha que temos interesse apenas nas colunas nome, curso e faltas. Podemos selecionar
as colunas pelos seus nomes.
df1[, c(2,3,7)]
df1[3:5,]
E pelas extremidades: cabeça (head) mostra as primeiras seis observações e cauda (tail) mostras
as últimas seis observações. O número de observações pode ser alterado pelo argumento n.
# Cabeça
head(df1)
# Cauda
tail(df1)
Entre as diversas ações possíveis, a de filtrar observações de acordo com suas características é
uma das mais importantes. Em R nós já vimos a função subset(), que permite fazer filtros ou obter
subconjuntos que tenham características de interesse. Similar a ação de seleção, a ação de filtro pode
ser realizada por uma ou mais características. Por exemplo, podemos selecionar só os alunos do curso
de estatística.
Outro exemplo é selecionar alunos do curso de estatística e que a soma das notas das provas
1, 2 e 3 seja maior que 180.
subset(df1, faltas != 0)
Trocar a ordem das colunas para que as notas das provas apareçam primeiro pode ser feito
tanto pelo nome como pelos números associados às colunas.
df1[,c(4,5,6,1,2,3)]
A ação de transformação consiste em criar novas variáveis a partir das variáveis existentes. A
ideia é ilustrada na Figura 6.5.
Note que, onde os alunos não tem nota na prova, o cálculo da média resultou em NA. Vamos
supor que se o aluno não realizou a prova a nota dele será zero.
df1[is.na(df1)] <- 0
df1$media <- (df1$p1 + df1$p2 + df1$p3)/3
df1
Podemos criar uma coluna para verificar se o aluno está aprovado (média > 70), em exame
(40 < média < 70) ou reprovado (média < 40) usando a função cut() temos
A última ação que vamos discutir é a de resumo ou sumarização. Esta ação está muito ligada
com a ideia de dados no formato longo e amplo. No exemplo das notas dos alunos o conjunto de dados
está no formato amplo. Se desejarmos calcular a média de cada prova precisamos percorrer as colunas
p1, p2 e p3 uma a uma calculando a média. Se os dados estiverem no formato longo poderíamos mais
facilmente usar a ideia de resumir uma variável condicional aos valores de uma outra, neste caso: a
prova. A Figura 6.6 ilustra como é o processo de tornar um conjunto de dados do formato amplo
para o longo ou empilhado.
Usando a função reshape podemos facilmente passar um conjunto de dados do formato am-
plo para longo. Note que vamos remover as colunas mat., fl, media e condicao antes de fazer a
manipulação.
Agora suponha que queremos saber qual é a nota média por prova. Estamos fazendo um
resumo da coluna notas porém de acordo com os valores da coluna exame. Informalmente, dizemos
que estamos agrupando pela coluna exame ou fazendo um group by por exame. Este tipo de sumariza-
ção é tão comum em análise de dados que em R temos a função aggregate(), especializada neste tipo
de sumarização. Vejamos a sintaxe no Código 6.3.
aggregate(x = df1_long$notas,
by = list(df1_long$exame),
FUN = mean)
## Group.1 x
## 1 p1 64.3
## 2 p2 65.0
## 3 p3 57.1
Em detalhes, temos:
▶ x: neste argumento passamos os valores que queremos resumir. No exemplo: as notas dos
alunos.
▶ by: este argumento deve ser uma lista com os valores pelos quais queremos calcular o resumo.
No nosso exemplo: por cada prova.
▶ FUN: neste argumento dizemos qual é o resumo que queremos calcular. Neste exemplo: a média
(mean).
O retorno da função é um data.frame com os valores calculados. Podemos agrupar por mais
de uma coluna. Por exemplo, a nota média de cada curso para cada exame.
aggregate(x = df1_long$notas,
by = list(df1_long$curso, df1_long$exame),
FUN = mean)
## Group.1 Group.2 x
## 1 Est p1 60.0
## 2 Mat p1 70.0
## 3 Est p2 53.8
## 4 Mat p2 80.0
## 5 Est p3 38.8
## 6 Mat p3 81.7
Outra opção é calcular mais do que uma medida resumo. Neste caso a sintaxe fica um pouco
mais complicada porque devemos passar uma função no argumento FUN que calcula todas os resumos
que desejarmos. No exemplo do Código 6.4 calculamos a média e o desvio-padrão por curso e prova.
Código 6.4. Exemplo de sintaxe da função aggregate para obter múltiplas medidas resumo.
aggregate(x = df1_long$notas,
by = list(df1_long$curso, df1_long$exame),
FUN = function(x) {
c(”media” = mean(x), ”sd” = sd(x))
})
Este tipo de tarefa é também chamada de split-apply-combine. A Figura 6.7 mostra em detalhes
o que foi realizado.
Primeiro nós dividimos (split) o conjunto de dados pelas variáveis curso e exame. Para cada
pedaço foram calculadas as medidas resumo (apply). Finalmente, combinamos os resultados em um
novo data.frame (combine).
Para combinar o novo conjunto de dados com o antigo a primeira coisa a fazer é deixá-los
compatíveis. Isso significa que devem ter os mesmos dados e as colunas devem ter os mesmos nomes.
Como fizemos diversos cálculos com o df1, vamos recriá-lo.
Note que no conjunto df2 não temos uma coluna chamada de prova3. Assim, primeiro
precisamos criar esta coluna.
df2$prova3 <- NA
Uma vez que os conjuntos de dados são compatíveis, podemos simplesmente concatenar suas
linhas usando a função rbind().
Por fim, uma outra operação que aparece com frequência ao analisar dados é a de juntar/parear
duas tabelas que possuem uma chave primária. Essas operações são geralmente chamadas de junções
( join ) e existem diversas versões, as mais comuns são:
# Left join
merge(df1, df_extra, by = ”matricula”, all.x = TRUE)
# Right join
merge(df1, df_extra, by = ”matricula”, all.y = TRUE)
# inner join
merge(df1, df_extra, by = ”matricula”, all = FALSE)
# full join
merge(df1, df_extra, by = ”matricula”, all = TRUE)
▶ left join: mantém todos os registros da tabela a esquerda (df1) e completa com os corresponden-
tes na tabela da direita (df_extra).
▶ right join: mantém todos os registros da tabela da direita (df_extra) e completa com os dados
da tabela da esquerda (df1).
▶ full join: mantém todos os registros e preenche com NA onde não tem correspondência entre
as tabelas.
Figura 6.10 ilustra os diferentes tipos de join para um subconjunto dos dados das notas dos
alunos.
Código 6.5. Exemplo de uso da função write.table() para exportar arquivos de texto pleno.
write.table(x = final,
file = ”Nome_do_arquivo.csv”,
row.names = FALSE)
Os argumentos básicos são o data.frame que você quer salvar em disco e o nome desejado.
Neste caso foi solicitado para que o nome das linhas não fosse incluído no arquivo salvo. A maioria dos
argumentos de escrita são iguais ou muito similares aos argumentos de leitura. Assim, exportação de
dados não gera mais complexidade e tudo que você já aprendeu para leitura se aplica imediatamente.
Sem dúvida, exportar dados em texto pleno é muito fácil e simples. No entanto, existem
algumas desvantagens. A principal é que as classes das colunas podem ser perdidas ao ler novamente
o conjunto de dados em R. Por ser um texto pleno o arquivo não carrega metadados que foram criados
em R. Caso seja de interesse, é possível salvar o próprio objeto R que contém o seu data.frame usando
a função save().
save(final,
file = ”Nome_do_arquivo.RData”)
A principal vantagem é que tudo que você fez no objeto final será mantido. Outra vanta-
gem é que você pode salvar outros formatos de dados como listas, matrizes e arrays. Para carregar
novamente o data.frame você deve usar a função load().
load(”Nome_do_arquivo.RData”)
Caso você tenha um objeto mais complicado, como por exemplo uma série de listas, a função
dput() é uma opção interessante. Ela permite salvar um objeto R como um texto simples que pode
ser copiado e colado.
dput(final)
É possível também escrever um arquivo com extensão .R que pode ser posteriormente carre-
gado no R.
dput(final,
file = ”Nome_do_arquivo.R”)
dget(”Nome_do_arquivo.R”)
Essa estratégia pode ser usada para múltiplos objetos usando a função dump().
dump(c(”final”, ”df1”),
file = ”Nome_do_arquivo.R”)
source(”Nome_do_arquivo.R”)
Um outro formato que o R exporta é o formato binário com a extensão .rda. As vantagens
deste formato são que ocupa pouco espaço em disco e preserva as propriedades do objeto R.
save(final,
file = ”Nome_do_arquivo.rda”)
Neste caso, podemos usar novamente a função load() para fazer a leitura em R.
Por fim, você pode salvar todos os objetos e instruções que você usou em sua sessão R usando
a função save.image().
Lembre-se: sempre que você não especificar o caminho de escrita explicitamente, o R vai
escrever no diretório de trabalho atual. Algumas funções úteis para você ter controle de onde o R está
escrevendo os arquivos são:
Neste Capítulo foram vistas as principais operações com dados tabulares. A esta altura, esta-
mos prontos para falar sobre análise descritiva e exploratória de dados.
Ao trabalhar com ciência de dados lidamos com dados de todos os tipos e formatos diaria-
mente. O R nativamente apresenta um conjunto completo de ferramentas para importar, manipular
e exportar conjuntos de dados. Também existem pacotes especializados para estas tarefas, tais como
os pacotes tidyverse: readr para leitura, tidyr para arrumação e o dplyr para manipulação. Neste
Capítulo vamos discutir os aspectos básicos da manipulação de dados usando os princípios do tidy
data ou dados arrumados. Este termo ficou famoso após o artigo Tidy Data. O chamado tidy data
segue três principios:
A Figura 7.1 mostra uma representação de conjunto de dados em que os 3 princípios do tidy
data são atendidos.
A ideia parece simples mas na prática pode ser confuso. A melhor forma de entender esses
conceitos é por meio da prática e exemplos. Neste Capítulo vamos lidar com conjuntos de dados bem
simples para ilustrar os principais aspectos da importação, manipulação e exportação de dados. No
109
110 Capítulo 7. Lidando com dados com o tidyverse
Capítulo 8 vamos retornar a todos esses conceitos por meio de um exemplo real similar ao que você
vai encontrar no dia-a-dia ao analisar dados.
A principal diferença entre esses formatos é o caracter ou estratégia usada para separar as
colunas da base de dados. Os nomes são auto-explicativos, por exemplo, no formato .csv as colunas
são separadas pelo símbolo , (vírgula). Em português usamos a , para representar decimais, por isso
usamos o ; (ponto e vírgula) para separar as colunas. Na prática tanto faz o formato desde que você
saiba qual é o caracter ou estratégia usada para delimitar as colunas.
Você pode estar pensando: por que não ler a planilha eletrônica diretamente? Isso é uma
possibilidade, porém a extensão das planilhas está atrelada ao sistema operacional. Por exemplo: você
não terá planilhas do famoso Microsoft Excel em um sistema operacional Linux. Da mesma forma,
você dificilmente terá arquivos do tipo .odf do pacote OpenOffice, popular em ambientes Linux, em
sistemas operacionais Windows. Assim, optamos por usar arquivos de texto pleno porque são mais
portáveis e, de certa forma, agnósticos ao sistema operacional e/ou software fabricante.
Sigla;Código;Município;idh;renda;alfab;
AC;1200013;Acrelândia;;136,539;73,309;
AC;1200054;Assis Brasil;;115,16;70,954;
AC;1200104;Brasiléia;;132,383;75,493;
AC;1200138;Bujari;;118,815;61,23;
AC;1200179;Capixaba;;108,164;62,283;
AC;1200203;Cruzeiro do Sul;;140,199;71,548;
AC;1200252;Epitaciolândia;;134,364;75,381;
Note que o caracter ; separa as colunas. Isso significa que ; é o caracter delimitador de
campos. De forma similar um arquivo .tsv tem a seguinte estrutura
Neste caso o espaço em branco é quem delimita as colunas. Já o formato .fwf tem a seguinte
estrutura
1 2 PAUL TERGAT (69) M2529 0:44:47
2 6 HENDRICK RAMAALA (72) M2529 0:45:05
3 4 ELIJAH KIPTARBEI LAGAT (98) M0014 0:45:08
4 7 SILVIO GUERRA (68) M3034 0:45:17
5 12 JOHN GWAKO (78) M2024 0:45:29
6 1 EMERSON ISER BEM (73) M2529 0:45:37
7 134 JOSE TELES DE SOUZA (71) M2529 0:45:56
8 25 GERALDO DE ASSIS (65) M3034 0:46:04
9 14 PATRICK NDAYISENGA (71) M2529 0:46:11
Este formato é um pouco mais complicado, uma vez que não tem um caracter delimitador
de colunas. As colunas são especificadas pelo número de caracteres. Assim, você precisará saber qual
o comprimento em número de caracteres de cada coluna. Por ser mais complicado é um formato
menos popular.
Em R a função read.table() oferece uma forma simples de importar a maioria dos formatos
de dados em texto pleno. Além desta função nativa existem pacotes especializados na leitura de dados,
tal como o readr.
O objetivo do readr é fornecer uma maneira rápida e amigável de ler dados retangulares.
Este pacote foi criado para lidar com os mais diversos tipos de dados encontrados na prática e dispõe
das funções read_csv(), read_tsv(), read_delim(), read_fwf(), read_table() e read_log(). Além
de serem funções bastante simples do ponto de vista do usuário, elas possuem um recurso interessante:
por meio de um argumento col_types é possível fornecer uma lista que informa já na leitura o tipo
da variável, o que poupa esforços na fase de tratamento dos dados para análise.
library(readr)
help(readr)
Para leitura é importante saber qual é o diretório dentro do computador onde o arquivo
de interesse está gravado. Neste livro leremos a base de dados direto de um endereço web. Porém,
se não for o seu caso, basta você especificar o caminho do seu arquivo no argumento file. Uma
recomendação é deixar a base de dados no mesmo diretório do seu arquivo .R. Assim, você pode
simplesmente fixar o diretório de trabalho como o diretório atual usando a função setwd() e ler o
arquivo apenas especificando o nome.
Para começar, vamos importar um arquivo no formato .txt que está no seguinte endereço:
http://leg.ufpr.br/~walmes/data/anovareg.txt. Você pode acessar este endereço e ver que o caracter
separar neste exemplo é um tab. Por este motivo usaremos a função do readr destinada a conjuntos
de dados separados por tabulação: a read_tsv(). Ao abrir o txt nota-se que cada coluna tem um
nome, por isso o argumento col_names = TRUE deve ser usado. O Código 7.1 ilustra como importar
esta base de dados para o R.
Vamos importar um outro conjunto de dados agora no formato .csv que está no seguinte
endereço web: http://leg.ufpr.br/~wagner/data/reglinear.csv. Novamente, você pode abrir o arquivo
em um editor de texto e ver que o caracter separador é um espaço em branco. Neste caso a função
readr para leitura é a read_table().
Se a leitura foi executada com sucesso você não deve receber nenhuma mensagem. O erro
mais comum neste ponto é não especificar o caminho de diretórios corretamente, neste caso você vai
receber uma mensagem como esta
O R está dizendo que não conseguiu encontrar o arquivo que você deseja ler no diretório
que ele está procurando. Lembre-se: para saber onde o R está trabalhando use o comando getwd().
Com a leitura bem sucedida, é hora de inspecionar o que foi carregado. Recomendamos usar
a função str() para ter uma visão geral do arquivo.
#str(dados)
A função str() começa mostrando quantas observações e quantas colunas a base de dados
contém. Neste caso temos 20 observações (linhas) e 2 variáveis (colunas). Na sequência é apresentado
o nome de cada coluna e o tipo de dado que ela representa.
estarão neste formato e você precisará trabalhar para deixá-lo desta forma. Um dos pacotes R mais
famosos para arrumação de dados é o tidyr.
library(tidyr)
help(tidyr)
Uma situação comum é quando os dados vêm em muitas colunas. Este caso ocorre muito
na tabulação de dados de experimentos nos quais se tem medidas realizadas ao longo do tempo. Por
exemplo,
n <- 3
tb1
Neste caso temos que as observações da variável de interesse estão em três colunas. Além
disso, a variável avaliação, que deveria ser uma única coluna, está no título de três outras colunas.
Este é um exemplo típico de dados desorganizados que estão no formato amplo (wide). Nós queremos
transformá-los para o formato longo (long), de modo que cada variável esteja em uma coluna e cada
observação em uma linha.
Podemos realizar esta operação utilizando o pacote tidyr. Para o caso de empilhar colunas
temos a função pivot_longer().
library(magrittr)
tb1_long
## # A tibble: 9 x 3
## trat avaliação resposta
## <chr> <chr> <int>
## 1 A aval1 2
## 2 A aval2 3
## 3 A aval3 5
## 4 B aval1 7
## 5 B aval2 6
## 6 B aval3 2
## 7 C aval1 4
## 8 C aval2 6
## 9 C aval3 8
▶ O argumento names_to nomeia a coluna que vai converter os nomes das colunas para um fator.
▶ O argumento values_to nomeia a coluna que vai receber os valores empilhados das colunas de
interesse.
▶ Por fim especificamos quais as colunas envolvidas na operação. Este argumento pode ser de-
clarado de formas diversas formas. Por exemplo, no lugar de -trat, bastaria utilizar alguma
destas opções: aval1:aval3, 2:4, c(aval1,aval2,aval3) ou ainda c(2,3,4). Note, basta es-
pecificar as colunas que desejamos empilhar por nome, posição ou apenas as colunas que não
estão envolvidas na operação.
Note que introduzimos um novo operador: o %>%, chamado de pipe. O objetivo do operador
é encaminhar um valor para uma expressão ou chamada de função. Neste caso, utilizamos o %>%
para dizer que na função pivot_longer() o objeto de interesse é o nosso data.frame tb1. É importante
ressaltar que dentro da função pivot_longer() podemos especificar diretamente o data.frame por meio
do argumento data, contudo o código fica mais organizado e elegante quando usamos o %>%.
Podemos também fazer a operação inversa, ou seja, desempilhar caso seja de interesse. Isso
pode ser feito com a função pivot_wider() do tidyr.
## # A tibble: 3 x 4
## trat aval1 aval2 aval3
## <chr> <int> <int> <int>
## 1 A 2 3 5
## 2 B 7 6 2
## 3 C 4 6 8
Outra operação que pode ser necessária é separar uma variável em várias outras. Isso é comum
quando um campo de texto é a união de várias informações. Considere o seguinte data.frame.
tb
A coluna ano_mod é a mistura das variáveis ano do veículo e modelo do veículo. De forma
similar, a coluna local é a mistura de duas variáveis: município e unidade federativa (UF). Assim,
precisamos separar estas informações. Note que o caracter / é quem separa as informações na coluna
ano_mod enquanto que na coluna local o caracter - é o separador.
Uma alternativa simples e que exige poucos tratamentos para separação de variáveis é a função
separate() do pacote tidyr. Para esta função passamos como argumentos o data.frame, a coluna de
interesse, quais colunas novas serão criadas e qual o separador.
tb_nova1
tb_nova2
Da mesma forma que podemos separar variáveis que estão em uma mesma coluna pode ser
necessário criar novas variáveis unindo os valores de duas ou mais colunas. Por exemplo, suponha que
em um data.frame os campos dia, mês e ano estão cada um em uma coluna. Porém, você precisa de
apenas uma coluna que represente a data da observação. Assim, precisamos juntar essas três colunas
em uma nova.
Esta tarefa pode ser realizada com a função unite() do pacote tidyr em que especifica-se o
data.frame, o nome da coluna que receberá a união das outras, o separador de interesse e quais colunas
serão unidas. Neste caso, com intuito de manter as variáveis originais acrescentamos o argumento
remove = FALSE.
tb
Por fim, teremos frequentemente que lidar com dados faltantes. Algumas vezes podemos
simplesmente querer remover todas as linhas que tenham algum NA. Outras vezes queremos preencher
com algum valor específico. O pacote tidyr dispõe de funções bastante úteis para tratamento de dados
ausentes. Veja o seguinte conjunto de dados
Para excluir todas as linhas que contenham pelo menos um NA temos a função drop_na()
drop_na(tb)
Para substituir os campos iguais a NA por algum valor o pacote tidyr dispõe da função
replace_na(). Esta função recebe como argumento o data.frame e uma lista com o nome da variável
no dataframe e que valor um dado ausente encontrado naquela variável deve assumir.
tb %>% replace_na(list(gols = 0,
faltas = 0))
É importante salientar que tratar dados ausentes de forma estatisticamente coerente nem sem-
pre é uma tarefa fácil. Assim, pense muito bem o que os dados faltantes significam no contexto que
você está trabalhando e se faz sentido substituir por algum número ou mesmo excluir da base de
dados.
Uma vez que os dados estejam arrumados é a hora de começar a conhecê-los. Seguindo os
princípios do tidy data para manipular um conjunto de dados temos cinco ações essenciais:
▶ Resumir ou sumarizar dados. Essa ação é a mais complexa e em geral envolve o uso de medidas
estatísticas como média, mediana, desvio-padrão entre outros para criar resumo dos dados.
Existem diversas alternativas em R para manipulação de dados. Uma delas é o pacote dplyr.
library(dplyr)
help(dplyr)
Para ilustrar o uso de cada uma das ações de manipulação vamos considerar os dados apresen-
tados na Figura 7.2.
Figura 7.2: Uma tabela com dados fictícios sobre alunos e seus desempenhos.
A ação mais simples é a de ordenação, ilustrada na Figura 7.3. A ordenação pode ser feita de
acordo com os valores de uma ou mais variáveis.
Por exemplo, podemos ordenar os alunos pelo número de matrícula, como ilustrado na Fi-
gura 7.3 (linha 1). No dplyr a função destinada para ordenação é a arrange().
Note que a variável matrícula foi ordenada de forma crescente. Para ordenar de maneira
descrescente:
Suponha que temos interesse apenas nas colunas nome, curso e faltas. Podemos selecionar
as colunas pelos seus nomes. Utilizando a função select do dplyr, temos
Podemos selecionar apenas os alunos com mais de dez faltas utilizando a função filter() do
dplyr.
Podemos também selecionar pelo número das linhas usando a função slice() do dplyr.
Além disso podemos selecionar apenas as extremidades cabeça (head) ou cauda (tail).
# Cabeça
df1 %>% slice_head(n = 6)
# Cauda
df1 %>% slice_tail(n = 6)
Entre as diversas ações possíveis, a de filtrar observações de acordo com suas características é
uma das mais importantes. Já vimos a função filter() do dplyr, que permite fazer filtros ou obter
subconjuntos que tenham características de interesse. Similar a ação de seleção, a ação de filtro pode
ser realizada por uma ou mais características. Por exemplo, podemos selecionar só os alunos do curso
de estatística.
Outro exemplo é selecionar alunos do curso de estatística e que a soma das notas das provas
1, 2 e 3 seja maior que 180.
df1 %>% filter(curso == ”Est” & (prova1 + prova2 + prova3) > 180)
Para renomear as colunas de um data.frame basta atribuir novos nomes. Para esta tarefa o
dplyr possui a função rename().
Trocar a ordem das colunas para que as notas das provas apareçam primeiro pode ser feito
tanto pelo nome como pelos números associados às colunas.
df1[,c(4,5,6,1,2,3)]
A ação de transformação consiste em criar novas variáveis a partir das variáveis existentes. A
ideia é ilustrada na Figura 7.5.
Por exemplo, podemos calcular a média dos alunos. Para isso é possível utilizar a função
mutate() do dplyr
Note que, onde os alunos não tem nota na prova, o cálculo da média resultou em NA. Vamos
supor que se o aluno não realizou a prova a nota dele será zero.
df1
df1
Podemos criar uma coluna para verificar se o aluno está aprovado (média > 70), em exame
(40 < média < 70) ou reprovado (média < 40) usando a função cut() temos
A última ação que vamos discutir é a de resumo ou sumarização. Esta ação está muito ligada
com a ideia de dados no formato longo e amplo. No exemplo das notas dos alunos o conjunto de
dados está no formato amplo. Se desejarmos calcular a média de cada prova precisamos percorrer as
colunas prova1, prova2 e prova3 uma a uma calculando a média. Se os dados estiverem no formato
longo poderíamos mais facilmente usar a ideia de resumir uma variável condicional aos valores de
uma outra, neste caso: a prova. A Figura 7.6 ilustra como é o processo de tornar um conjunto de
dados do formato amplo para o longo ou empilhado.
df1_long
## # A tibble: 21 x 4
## nome curso exame notas
## <chr> <chr> <chr> <dbl>
## 1 João Mat p1 80
## 2 João Mat p2 90
## 3 João Mat p3 80
## 4 Vanessa Mat p1 75
## 5 Vanessa Mat p2 75
## 6 Vanessa Mat p3 75
## 7 Tiago Est p1 95
## 8 Tiago Est p2 80
## 9 Tiago Est p3 75
## 10 Luana Est p1 70
## # ... with 11 more rows
Agora suponha que queremos saber qual é a nota média por prova. Estamos fazendo um
resumo da coluna notas porém de acordo com os valores da coluna exame. Informalmente, dizemos
que estamos agrupando pela coluna exame ou fazendo um group by por exame. Este resultado pode
ser obtido por meio da função summarise() do dplyr
df1_long %>%
group_by(exame) %>%
summarise(media = mean(notas))
## # A tibble: 3 x 2
## exame media
## <chr> <dbl>
## 1 p1 64.3
## 2 p2 65
## 3 p3 57.1
Podemos agrupar por mais de uma coluna. Por exemplo, a nota média de cada curso para
cada exame.
df1_long %>%
group_by(curso, exame) %>%
summarise(media = mean(notas))
## # A tibble: 6 x 3
## # Groups: curso [2]
## curso exame media
## <chr> <chr> <dbl>
## 1 Est p1 60
## 2 Est p2 53.8
## 3 Est p3 38.8
## 4 Mat p1 70
## 5 Mat p2 80
## 6 Mat p3 81.7
Outra opção é calcular mais do que uma medida resumo. No caso da função summarise()
basta acrescentar uma linha especificando que temos interesse em obter outra medida.
df1_long %>%
group_by(curso, exame) %>%
summarise(media = mean(notas),
sd = sd(notas))
## # A tibble: 6 x 4
## # Groups: curso [2]
## curso exame media sd
## <chr> <chr> <dbl> <dbl>
## 1 Est p1 60 28.6
## 2 Est p2 53.8 39.0
## 3 Est p3 38.8 31.7
## 4 Mat p1 70 13.2
## 5 Mat p2 80 8.66
## 6 Mat p3 81.7 7.64
Este tipo de tarefa é também chamada de split-apply-combine. A Figura 7.7 mostra em detalhes
o que foi realizado.
Primeiro nós dividimos (split) o conjunto de dados pelas variáveis curso e exame. Para cada
pedaço foram calculadas as medidas resumo (apply). Finalmente, combinamos os resultados em um
novo data.frame (combine).
Para combinar o novo conjunto de dados com o antigo a primeira coisa a fazer é deixá-los
compatíveis. Isso significa que devem ter os mesmos dados e as colunas devem ter os mesmos nomes.
Como fizemos diversos cálculos com o df1, vamos recriá-lo.
Note que no conjunto df2 não temos uma coluna chamada de prova3. Assim, primeiro
precisamos criar esta coluna.
df2$prova3 <- NA
Uma vez que os conjuntos de dados são compatíveis, podemos simplesmente concatenar suas
linhas usando a função rbind().
Por fim, uma outra operação que aparece com frequência ao analisar dados é a de juntar/parear
duas tabelas que possuem uma chave primária. Essas operações são geralmente chamadas de junções
( join ) e existem diversas versões, as mais comuns são:
# Left join
left_join(df1, df_extra, by = ”matricula”)
# Right join
right_join(df1, df_extra, by = ”matricula”)
# inner join
inner_join(df1, df_extra, by = ”matricula”)
# full join
full_join(df1, df_extra, by = ”matricula”)
▶ left join: mantém todos os registros da tabela a esquerda (df1) e completa com os corresponden-
tes na tabela da direita (df_extra).
▶ right join: mantém todos os registros da tabela da direita (df_extra) e completa com os dados
da tabela da esquerda (df1).
▶ full join: mantém todos os registros e preenche com NA onde não tem correspondência entre
as tabelas.
Figura 7.10 ilustra os diferentes tipos de join para um subconjunto dos dados das notas dos
alunos.
A forma mais comum é exportar um arquivo de texto pleno nos formatos .csv ou .txt. Para
esta tarefa as funções write_tsv() e write_csv() podem ser usadas. Podemos exportar o resultado da
junção das tabelas df1 e df_extra como o resultado final de nossa análise, conforme ilustra o Código
7.3.
Código 7.3. Exemplo de uso da função write_csv() para exportar arquivos de texto pleno.
write_csv(final,
file = ”Nome_do_arquivo.csv”)
Os argumentos básicos são o data.frame que você quer salvar em disco e o nome desejado. A
maioria dos argumentos de escrita são iguais ou muito similares aos argumentos de leitura. Assim,
exportação de dados não gera mais complexidade e tudo que você já aprendeu para leitura se aplica
imediatamente.
Sem dúvida, exportar dados em texto pleno é muito fácil e simples. No entanto, existem
algumas desvantagens. A principal é que as classes das colunas podem ser perdidas ao ler novamente
o conjunto de dados em R. Por ser um texto pleno o arquivo não carrega metadados que foram criados
em R. Caso seja de interesse, é possível salvar o próprio objeto R que contém o seu data.frame usando
a função save().
save(final,
file = ”Nome_do_arquivo.RData”)
A principal vantagem é que tudo que você fez no objeto final será mantido. Outra vanta-
gem é que você pode salvar outros formatos de dados como listas, matrizes e arrays. Para carregar
novamente o data.frame você deve usar a função load().
load(”Nome_do_arquivo.RData”)
Caso você tenha um objeto mais complicado, como por exemplo uma série de listas, a função
dput() é uma opção interessante. Ela permite salvar um objeto R como um texto simples que pode
ser copiado e colado.
dput(final)
É possível também escrever um arquivo com extensão .R que pode ser posteriormente carre-
gado no R.
dput(final,
file = ”Nome_do_arquivo.R”)
dget(”Nome_do_arquivo.R”)
Essa estratégia pode ser usada para múltiplos objetos usando a função dump().
dump(c(”final”, ”df1”),
file = ”Nome_do_arquivo.R”)
source(”Nome_do_arquivo.R”)
Um outro formato que o R exporta é o formato binário com a extensão .rda. As vantagens
deste formato são que ocupa pouco espaço em disco e preserva as propriedades do objeto R.
save(final,
file = ”Nome_do_arquivo.rda”)
Neste caso, podemos usar novamente a função load() para fazer a leitura em R.
Por fim, você pode salvar todos os objetos e instruções que você usou em sua sessão R usando
a função save.image().
Lembre-se: sempre que você não especificar o caminho de escrita explicitamente, o R vai
escrever no diretório de trabalho atual. Algumas funções úteis para você ter controle de onde o R está
escrevendo os arquivos são:
Neste Capítulo foram vistas as principais operações com dados tabulares. A esta altura, esta-
mos prontos para falar sobre análise descritiva e exploratória de dados.
No Capítulo 6 foi apresentado como lidar com dados desde a sua importação, até arrumação,
manipulação e exportação usando os princípios do tidy data. No entanto, apenas ter os dados organi-
zados não significa ter informações sobre o problema de interesse. Para realmente tirar o melhor dos
nossos dados precisamos analisá-los usando métodos estatísticos apropriados.
A melhor forma de aprender a fazer uma análise descritiva é fazendo uma análise descri-
tiva. Neste Capítulo vamos explorar um conjunto de dados de uma área muito interessante cha-
mada de people analytics (análise de pessoas). Os dados são uma adaptação do conjunto de dados
Human Resources Data Set disponível na plataforma kaggle. As adaptações foram foitas para tor-
nar o conjunto de dados mais simples por meio da retirada de algumas variáveis e também da tra-
dução e adaptação dos dados para o contexto brasileiro. Os dados estão disponíveis no endereço
www.leg.ufpr.br/~wagner/data/people_analytics.csv. O caracterer delimitador é ; e podemos facil-
mente ler essa base de dados usando a função read.table().
133
134 Capítulo 8. Estatística descritiva e exploratória com o R base
▶ performance_score: escore dado pelo recrutador que indica o grau de compatibilidade entre o
candidato e a vaga.
▶ razao_demissao: razão pela qual o colaborador foi demitido. No caso de não ter sido demitido
é colocado como ainda empregado.
Por outro lado, variáveis quantitativas representam medidas numéricas. Se essas medidas
puderem assumir apenas um número finito ou contável de valores, dizemos que se trata de uma
variável quantitativa discreta. Se as medidas puderem assumir um número infinito de valores então
temos uma variável quantitativa contínua. Uma dica para diferenciar entre variáveis discretas e
contínuas é: se puder contar é discreta, se medir é contínua.
Olhando para o conjunto de dados people analytics podemos facilmente classificar cada uma
de suas variáveis usando as diretrizes apresentadas na Figura 8.1.
# Tabela genero
table(dados$genero)
##
## feminino masculino
## 176 135
##
## casado divorciado separado solteiro viúvo
## 124 30 12 137 8
##
## administracao engenharia de software executivo
## 2.894 3.537 0.322
## producao TI vendas
## 67.203 16.077 9.968
##
prop.table(table(dados$forma_recrutamento))*100
##
## aplicação online careerbuilder google Indeed
## 0.322 7.395 15.756 27.974
## indicação LinkedIn outros vagas inclusivas
## 9.968 24.437 0.643 9.325
## website
## 4.180
Para representação gráfica, o mais indicado para variáveis qualitativas nominais é o gráfico
de barras e de setores. O gráfico de setores apesar de seu aspecto histórico não é recomendado por ser
de difícil interpretação. Em geral nossos olhos têm dificuldade em diferenciar o tamanho das fatias.
Vamos ilustrar o seu uso, porém sugerimos o uso cauteloso desta opção. Em R as funções barplot()
e pie() são as mais simples para obter os gráficos de barras e setores, conforme ilustrado no Código
8.3. O comando par(mfrow = c(1,2)) serve para particionar a janela gráfica em duas para que os
gráficos apareçam lado a lado, após o uso desta opção retornamos a partição da janela gráfica para a
configuração original com o comando par(mfrow = c(1,1)).
par(mfrow = c(1,2))
barplot(table(dados$estado_civil))
pie(table(dados$estado_civil))
casado
100
divorciado
80
separado viúvo
60
40
solteiro
20
0
par(mfrow = c(1,1))
No caso de uma variável qualitativa com vários níveis, como o estado civil, é interessante
ordenar o gráfico do maior para o menor. Isso ajuda a identificar pontos extremos e estabelecer
comparações de forma mais rápida. Para isso, precisamos trocar a classe da variável estado_civil
para fator e dizer qual a ordem de seus níveis. O Código 8.4 ilustra este processo.
barplot(table(dados$estado_civil))
100
80
60
40
20
0
Da mesma forma que as tabelas de frequência, o gráfico de barras pode ser feito usando a
frequência absoluta ou relativa.
par(mfrow = c(1,2))
barplot(table(dados$estado_civil), main = ”Freq. Absoluta”)
barplot(prop.table(table(dados$estado_civil)), main = ”Freq. Relativa”)
0.3
80
0.2
60
40
0.1
20
0.0
0
table(dados$performance_score)
##
## atende totalmente excede insuficiente
## 243 37 13
## precisa melhorar
## 18
Código 8.5. Exemplo de como definir a ordem de uma variável qualitativa ordinal.
##
## insuficiente precisa melhorar atende totalmente
## 4.18 5.79 78.14
## excede
## 11.90
Neste caso também faz sentido incluir a chamada frequência acumulada que nada mais é
do que somar os percentuais até determinado nível da variável qualitativa. Por exemplo, se temos
interesse no percentual de colaborares que tem escore precisa melhorar ou menor precisamos somar
insuficiente e precisa melhorar. Para obter as frequências acumuladas usamos a função cumsum(),
conforme ilustrado no Código 8.6.
cumsum(prop.table(table(dados$performance_score))*100)
Novamente o gráfico de barras e setores são os mais indicados para análise gráfica.
barplot(table(dados$performance_score))
200
150
100
50
0
Neste caso, precisa ser avaliado se vale a pena mudar a ordem para que as classes mais fre-
quentes apareçam a esquerda do gráfico. Em geral é preferível manter a ordem natural da variável,
mas sempre existem exceções.
Outro aspecto a considerar é quando a variável tem muitos níveis. Por exemplo, a variá-
vel posicao tem 26 níveis. Ao fazer uma tabela com tantos níveis pode ser difícil de visualizar os
resultados.
table(dados$posicao)
##
## Administrador BD Analista de dados
## 5 8
## Arquiteto de dados Arquiteto de empreendimentos
## 2 1
## Assistente administrativo CEO
## 3 1
## CIO Contador
## 1 3
## Contador senior DBA senior
## 2 2
## Desenvolvedor BI Desenvolvedor BI senior
## 4 3
## Diretor BI Diretor de operações
## 1 1
## Diretor de TI Diretos de vendas
## 1 1
## Engenheiro de rede senior Engenheiro de redes
## 5 5
## Engenheiro de software Gerente de produção
## 10 14
## Gerente de serviços Gerente de TI
## 1 12
## Gerente de vendas Gerente engenheiro de software
## 30 1
## Técnico de produção I Técnico de produção II
## 137 57
Uma dica útil é tentar agrupar os níveis que tenham baixa ocorrência ou que façam sentido
serem agrupados. Por exemplo, poderíamos juntar os níveis Técnico de produção I e Técnico de
produção II, desde que faça sentido prático. Essa é uma decisão que deve ser tomada junto com o
time que vai usar suas análises. Outra opção seria agrupar Contador e Contador Senior. O importante
é que a forma de agrupar deve estar de acordo com os objetivos da análise e alinhada com toda a equipe
que vai se beneficiar dela.
A análise exploratória de variáveis quantitativas discretas com poucos valores é de certa forma
similar a análise de variáveis qualitativas ordinais. Assim, tabelas de frequência absoluta, relativa e
acumulada são ferramentas populares. Os gráficos de barras também são indicados. Em nosso exemplo
a única variável quantitativa discreta é o número de faltas. Podemos fazer uma tabela de frequência
absoluta, relativa e acumulada, conforme ilustra o Código 8.7.
# Frequencia absoluta
abs <- table(dados$faltas)
# Frequencia relativa
percentual <- prop.table(table(dados$faltas))*100
## Frequencia acumulada
acumulada <- cumsum(prop.table(table(dados$faltas)))
Apesar de ser possível, a tabela gerada ficou grande e de difícil interpretação, pois existem 20
valores distintos para a variável faltas. Nestes casos o mais indicado é tratar as variáveis quantitativas
discretas como quantitativas contínuas, conforme veremos na próxima subseção.
Em R a função cut() é bastante útil para agrupar as observações em faixas. Criar as faixas
é um assunto que requer bastante cuidado. Se você estiver em alguma situação em que as classes
podem ser criadas de acordo com alguma regra de negócio ou mesmo conforme interesse da equipe,
é perfeitamente aceitável usar essa informação para criar as classes. Caso contrário existem algumas
técnicas estatísticas para ajudar com esta tarefa. O importante é ter em mente que essa deve ser uma
decisão pensada de acordo com o negócio que você está lidando.
Vamos supor que um interesse da equipe é comparar as faixas de renda dentro da empresa
com as da população de forma geral. Você consultou o site do IBGE e constatou que para pesquisas
populacionais o IBGE usa as seguintes faixas de renda para obter a classe social de uma pessoa.
Classe Renda
A R$ 20.900,01 ou mais
B R$ 10.450,01 até R$ 20.900,00
C R$ 4.180,01 até R$ 10.450,01
D R$ 2.090,01 até R$ 4.180,00
E até R$ 2.090,01
Note que não precisamos explicitamente seguir a tabela do IBGE, uma vez que podemos
usar intervalos abertos a direita. Isso significa que entre 0 e 2.090, 01 o valor exato 2.090, 01 não será
contado na primeira faixa e assim por diante. Outro aspecto para estar atento é que a classificação do
IBGE é baseada na renda mensal. Por outro lado, nossa base de dados reporta a renda anual. Assim,
vamos primeiro dividir a variável por 12 para ter a renda mensal média. Para definir um intervalo
aberto a direita usamos as opção right = FALSE.
##
## (0,2.09e+03] (2.09e+03,4.18e+03] (4.18e+03,1.05e+04]
## 0 31 270
## (1.05e+04,2.09e+04] (2.09e+04,Inf]
## 10 0
60
40
20
0
Salário anual
Neste caso, as observações são agrupadas em faixas usando uma metodologia estatística cha-
mada de regra de Sturges, veja ?hist para detalhes.
Apesar do gráfico da Figura 8.2 ser popularmente chamado de histograma, técnicamente ele
é uma adaptação do histograma original. Note que, no eixo y, temos a representação das frequências
absolutas de cada faixa de valores. Contudo, na concepção original do histograma, o eixo y é usado
para representar o que chamamos de densidade, conforme ilustrado na Figura 8.3.
hist(dados$renda_mensal,
main = ””,
probability = TRUE,
xlab = ”Salário anual”,
ylab = ”Densidade”)
0.00030
0.00020
Densidade
0.00010
0.00000
Salário anual
Figura 8.3: Histograma para a variável salário anual com a densidade representada na eixo y.
A ideia da densidade consiste em pensar qual o valor deve ser colocado no eixo y para que
quando a área de cada retângulo for calculada seja obtida a frequência relativa. Assim, a densidade é
calculada da seguinte forma
Lembre-se que a área de um retângulo é calculada como o tamanho da base vezes a altura,
ou seja,
Como queremos que a área seja igual a frequência relativa, basta isolar a altura. Chamamos
esta altura de densidade. Para ilustrar, vamos usar como exemplo o cálculo da densidade para a variável
engajamento. O histograma é mostrado na Figura 8.4.
Histogram of dados$engajamento
0.0 0.1 0.2 0.3 0.4 0.5 0.6
Densidade
1 2 3 4 5
Engajamento
histograma
## $breaks
## [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
##
## $counts
## [1] 2 6 11 8 37 46 98 103
##
## $density
## [1] 0.0129 0.0386 0.0707 0.0514 0.2379 0.2958 0.6302 0.6624
##
## $mids
## [1] 1.25 1.75 2.25 2.75 3.25 3.75 4.25 4.75
##
## $xname
## [1] ”dados$engajamento”
##
## $equidist
## [1] TRUE
##
## attr(,”class”)
## [1] ”histogram”
Ao analisar o conteúdo do objeto histograma vemos que temos as classes (breaks), a frequência
absoluta (counts) e as densidades (density). A classe modal (maior frequência) é a que vai de .5 até 5.
Para calcular a densidade desta classe:
1. Calculamos a amplitude da classe, ou seja, o limite superior (5) menos o limite inferior (4.5).
Logo, a amplitude é 5 − 4.5 = 0.5.
2. Calculamos a frequência relativa da classe. Neste caso 103/311 = 0.331.
3. Calculamos a densidade 0.331/0.5 = 0.662.
Os cálculos são idênticos para as demais classes. Mas você deve estar se perguntando: por
que complicar o que estava fácil?
par(mfrow = c(1,2))
hist(subset(dados$engajamento, dados$genero == ”feminino”),
main = ”Feminino”, xlab = ”Engajamento”,
ylab = ”Freq. Absoluta”)
hist(subset(dados$engajamento, dados$genero == ”masculino”),
main = ”Masculino”, xlab = ”Engajamento”,
ylab = ”Freq. Absoluta”)
Para variáveis com muitos níveis pode ficar difícil avaliar tantos histogramas. Além disso, os
histogramas podem ter comportamentos parecidos, o que torna difícil encontrar diferenças apenas
visualmente. A estatística nos proporciona um conjunto de medidas numéricas para resumir a distri-
buição de frequências. Em geral olhamos para dois aspectos básicos da distribuição de frequência: a
sua tendência central e sua dispersão ou espalhamento.
▶ Média aritmética: para uma variável quantitativa basta somar todos os seus valores e dividir
pelo número de observações. A interpretação física é que a média é o centro de gravidade da
60 Feminino Masculino
40
50
Freq. Absoluta
Freq. Absoluta
30
40
30
20
20
10
10
0
0
2.0 3.0 4.0 5.0 1 2 3 4 5
Engajamento Engajamento
distribuição empírica. Pense que cada um dos retângulos é uma massa com peso igual a sua
densidade. Imagine que o eixo x é uma régua. O ponto médio é onde você deve colocar uma
agulha para perfeitamente equilibrar as massas sobre a régua.
▶ Moda: valor mais frequente. No caso de uma variável onde praticamente só existem valores
únicos, essa medida tem pouca aplicação prática. Você pode reportar a classe modal se for de
interesse. Em geral, a média e a mediana costumam ser suficientes para ter um boa noção da
centralidade da distribuição empírica.
Neste livro não vamos dar ênfase as formulas de como obter tais estatísticas. Nosso objetivo é
mostrar como obtê-las usando o R. Para a variável renda_mensal podemos facilmente calcular a média
e a mediana, conforme ilustra o Código 8.8.
# Média
mean(dados$renda_mensal)
## [1] 5752
# Mediana
median(dados$renda_mensal)
## [1] 5234
quantile(dados$renda_mensal)
Além dos quartis, a função quantile() apresenta os valores mínimos e máximos. Juntos, esses
valores são o chamado resumo de cinco números. É muito comum apresentar o resumo dos cinco
números em um diagrama de caixas ou boxplot, conforme ilustrado na Figura 8.6.
boxplot(dados$renda_mensal)
20000
15000
10000
5000
▶ A primeira linha horizontal representa o valor mínimo, sem considerar os valores que ultrapas-
sam o desvio interquartílico.
▶ A segunda linha horizontal representa o primeiro quartil.
▶ A linha mais grossa no meio da caixa representa a mediana.
▶ A terceira linha horizontal representa o terceiro quartil.
▶ A última linha horizontal representa o valor máximo, sem considerar os valores que ultrapassam
o desvio interquartílico.
O desvio interquartílico é definido como 1.5 vezes o terceiro quartil menos o primeiro quartil.
É um valor de referência e cria a chamada barreira de valores atípicos. Muitos analistas chamam os
valores além do desvio interquartilico de outliers. Porém, isso não é verdade e a definição exata do
que é um outliers depende de outros aspectos como do modelo que será ajustado. Neste livro não
vamos entrar neste tipo de detalhe. Tenha em mente de que estamos apenas na parte inicial da análise
e a detecção de outliers é uma das etapas finais da modelagem estatística e portanto além dos objetivos
deste livro.
## [1] 17080
# Variância
var(dados$renda_mensal)
## [1] 4394836
# Desvio-padrão
sd(dados$renda_mensal)
## [1] 2096
## Coeficiente de variação
(sd(dados$renda_mensal)/mean(dados$renda_mensal))*100
## [1] 36.4
Existem diversas outras medidas resumo tanto de tendência central quanto de dispersão. Para
mais detalhes considere o curso Probabilidade e Estatística para Ciência de dados e a bibliografia
complementar ao final deste livro.
A vantagem de usar essas medidas resumo é mais facilmente poder comparar diferentes grupos
com relação a tendência central e/ou variabilidade. Por exemplo, podemos estar interessados em
comparar o salário médio dos colaboradores de acordo com o departamento em que trabalham. Já
vimos que a função aggregate() é especialmente útil para este tipo de resumo.
aggregate(x = dados$renda_mensal,
by = list(dados$departamento),
FUN = mean)
## Group.1 x
## 1 administracao 5983
## 2 engenharia de software 7916
## 3 executivo 20833
## 4 producao 4996
## 5 TI 8089
## 6 vendas 5755
Note que agora estamos usando uma variável para condicionar o comportamento de outra.
Isso é o que chamamos de análise descritiva bivariada e será o assunto da próxima subseção.
▶ Qualitativa × qualitativa;
▶ Qualitativa × quantitativa;
▶ Quantitativa × quantitativa.
Novamente, temos gráficos e medidas numéricas para resumir a relação entre as variáveis.
Nesta seção vamos ver apenas as medidas e gráficos mais populares. Para mais detalhes considere o
curso Manipulação e visualização com o R e as referências no final deste livro.
table(dados$forma_recrutamento, dados$genero)
##
## feminino masculino
## aplicação online 0 1
## careerbuilder 16 7
## google 33 16
## Indeed 50 37
## indicação 11 20
## LinkedIn 44 32
## outros 1 1
## vagas inclusivas 15 14
## website 6 7
Para economizar um pouco na digitação dos comandos podemos usar a função with().
## genero
## forma_recrutamento feminino masculino
## aplicação online 0 1
## careerbuilder 16 7
## google 33 16
## Indeed 50 37
## indicação 11 20
## LinkedIn 44 32
## outros 1 1
## vagas inclusivas 15 14
## website 6 7
A função prop.table() continua sendo a nossa escolha, conforme ilustra o Código 8.12.
## genero
## forma_recrutamento feminino masculino
## aplicação online 0.00000 0.00322
## careerbuilder 0.05145 0.02251
## google 0.10611 0.05145
## Indeed 0.16077 0.11897
## indicação 0.03537 0.06431
## LinkedIn 0.14148 0.10289
## outros 0.00322 0.00322
## vagas inclusivas 0.04823 0.04502
## website 0.01929 0.02251
## genero
## forma_recrutamento feminino masculino
## aplicação online 0.000 1.000
## careerbuilder 0.696 0.304
## google 0.673 0.327
## Indeed 0.575 0.425
## indicação 0.355 0.645
## LinkedIn 0.579 0.421
## outros 0.500 0.500
## vagas inclusivas 0.517 0.483
## website 0.462 0.538
## genero
## forma_recrutamento feminino masculino
## aplicação online 0.00000 0.00741
## careerbuilder 0.09091 0.05185
## google 0.18750 0.11852
## Indeed 0.28409 0.27407
## indicação 0.06250 0.14815
## LinkedIn 0.25000 0.23704
## outros 0.00568 0.00741
## vagas inclusivas 0.08523 0.10370
## website 0.03409 0.05185
As vezes pode ser de interesse adicionar o total das linhas e colunas para facilitar a interpreta-
ção. A função addmargins() cumpre esse papel, conforme ilustra o Código 8.13.
## genero
## forma_recrutamento feminino masculino Sum
## aplicação online 0.00000 0.00322 0.00322
## careerbuilder 0.05145 0.02251 0.07395
## google 0.10611 0.05145 0.15756
## Indeed 0.16077 0.11897 0.27974
## indicação 0.03537 0.06431 0.09968
## LinkedIn 0.14148 0.10289 0.24437
## outros 0.00322 0.00322 0.00643
## vagas inclusivas 0.04823 0.04502 0.09325
## website 0.01929 0.02251 0.04180
## Sum 0.56592 0.43408 1.00000
Na Figura 8.7(A) enfatizamos a variável que representa a forma de recrutamento e por isso é
ela quem aparece formando as barras. A variável gênero tem um papel apenas complementar. Note
que a comparação em termos de composição por gênero deve ser feita dentro de cada coluna, uma
vez que a comparação entre colunas é difícil por não terem a mesma altura.
Por outro lado, na Figura 8.7(B), enfatizamos a composição de cada nível da variável gênero
de acordo com a variável status. Como as barras apresentam a frequência relativa, podemos comparar
as frequências não só dentro de cada barra mas também entre as barras.
Quando o objetivo for comparar os níveis de uma variável de acordo com os níveis de uma
outra variável podemos usar as barras lado a lado, conforme ilustra a Figura 8.8.
Por fim, em casos em que o número de níveis da variável é grande podemos apresentar as
barras na horizontal. A Figura 8.9 ilustra esta opção.
A B
80
1.2
feminino justa causa
ativo
60
0.8
40
0.4
20
0.0
0
É importante também apresentar os nomes dos níveis da variável na horizontal. Esse é o papel
do argumento las = 1. No entanto ao fazer isso é provável que o texto fique fora das margens do
gráfico. Para corrigir esse problema usamos os parâmetros par(mai=c(1,2,1,1)).
Código 8.14. Calculando a média, mediana e desvio-padrão da renda mensal por departamento.
0.8 insuficiente
precisa melhorar
0.6
atende totalmente
excede
0.4
0.2
0.0
aggregate(x = dados$renda_mensal,
by = list(dados$departamento),
FUN = function(x) {
c(”media” = mean(x),
”mediana” = median(x),
”sd” = sd(x))
})
Tente entender o porquê o desvio-padrão da renda mensal na classe executivo retornou como
NA.
Recomendamos usar o boxplot da variável quantitativa por nível da variável qualitativa para
análise gráfica, conforme ilustrado na Figura 8.10.
Para melhorar a visualização você pode ordenar as caixas de acordo com a renda mediana
conforme Figura 8.11.
aplicação online
outros
website
careerbuilder
vagas inclusivas
indicação
google
LinkedIn
Indeed
0 20 40 60 80
Figura 8.9: Exemplo de gráfico com barras horizontais.
Uma outra opção é criar faixas para a variável quantitativa e usar gráficos de barras como na
subseção 7.3.1.
Olhando para o diagrama de dispersão da Figura 8.12 tente imaginar uma linha descrevendo
a relação entre as variáveis renda mensal e engajamento. Se a linha que você imaginar for conforme a
da Figura 8.13(A) significa que visualmente as duas variáveis não estão associadas ou tem uma relação
20000
15000
Renda mensal
10000
5000
Forma de recrutamento
Figura 8.10: Boxplot da variável renda mensal por níveis da variável forma de recrutamento.
fraca. Por outro lado, se a linha estiver como na Figura 8.13(B) indica que as duas variáveis estão
positivamente associadas, ou seja, ao aumentar o valor de uma a outra também aumenta. Contudo,
se a linha for como na Figura 8.13(C) indica que a relação é negativa, ou seja, ao aumentar uma das
variáveis a outra diminui.
Para o nosso exemplo vemos uma leve tendência de relação positiva entre engajamento e
renda mensal. Porém a variabilidade (dispersão dos pontos) é bem alta e parece depender dos valores
da variável renda mensal.
Como você pode notar, interpretar um gráfico como o diagrama de dispersão é uma ativi-
dade bastante subjetiva. Existem algumas medidas numéricas que buscam resumir a relação linear
entre duas variáveis quantitativas, são os chamados coeficientes de correlação. Em geral usamos três
coeficientes de correlação: Pearson, Spearman e Kendal. Os três variam entre -1 e 1, sendo que −1
indica correlação perfeita negativa, todos os pontos caem exatamente em cima de uma reta como
da Figura 8.13(C). Uma correlação igual a 1 indica correlação positiva perfeita e neste caso todos os
pontos estariam em cima de uma reta como a apresentada na Figura 8.13(B). Se o valor da correlação
for zero não temos relação entre as variáveis e todos os pontos estariam em cima de uma reta como a
apresentada na Figura 8.13(A).
Na prática, esses valores são apenas referências e o que é considerada uma correlação forte
ou fraca vai depender do contexto. Além disso, um erro comum é dizer que se duas variáveis tem
correlação igual a zero elas são independentes. Essa afirmação em geral é falsa. O conceito de in-
dependência estatística requer a suposição de uma distribuição conjunta de probabilidades e não é
20000
15000
Renda mensal
10000
5000
Forma de recrutamento
facilmente definido para qualquer distribuição a parte da distribuição normal bivariada. Conceitos de
Teoria das Probabilidades estão além do escopo deste livro. Para referência consulte a bibliografia
no final deste livro.
Por fim, sempre que temos mais do que uma opção, a pergunta natural é “qual é o melhor
coeficiente de correlação?”. Apesar de uma pergunta razoável, a resposta depende da situação e, em
geral, o melhor é calcular dois ou três deles e comparar os resultados. O coeficiente de correlação de
Pearson é o estimador de máxima verossimilhança do parâmetro de correlação de uma distribuição
normal bivariada. Neste contexto, se a distribuição conjunta for uma normal bivariada ele seria o
indicado. Porém, na prática não sabemos qual é a verdadeira distribuição conjunta das variáveis
envolvidas na análise. Uma boa indicação é que a distribuição de frequência empírica de cada variável
é aproximadamente simétrica. Novamente é um critério subjetivo e difícil de se verificar em termos
práticos. Porém, nesta etapa de análise exploratória onde não temos nenhuma suposição distribucional
o coeficiente de correlação de Pearson é uma boa indicação da correlação linear entre as variáveis.
20000
15000
Renda mensal
10000
5000
1 2 3 4 5
Engajamento
40
40
12
20
20
f (x)
f (x)
f (x)
10
0
8
-20
-20
6
simétricas use o de Pearson. Caso contrário use Spearman. É importante ressaltar que essas são re-
comendações práticas para o caso em que o interesse seja obter apenas um. Na prática você pode
facilmente calcular os três, conforme ilustrado no Código 8.15.
# Pearson
with(dados, cor(renda_mensal, engajamento, method = ”pearson”))
## [1] 0.065
# Spearman
with(dados, cor(renda_mensal, engajamento, method = ”spearman”))
## [1] 0.0311
# Kendall
with(dados, cor(renda_mensal, engajamento, method = ”kendall”))
## [1] 0.0214
Conforme a Figura 8.12 já havia indicado, a correlação entre as variáveis renda mensal e
engajamento é fraca (valor próximo de zero). Também vemos que o coeficiente de Pearson é um
pouco maior que o de Spearman que por sua vez é um pouco maior que o de Kendall. Em geral essa
ordem vai se manter para a maioria das análises de correlação que você fizer. Isso se deve pela forma
como cada coeficiente é construído. Não vamos entrar em detalhes nas equações, porém mantenha
isso em mente e não se preocupe com o valor exato. O importante é a avaliação de existência ou não
de relação entre variáveis e qual a força desta associação.
Neste capítulo apresentamos uma visão geral das técnicas de estatística descritiva e explora-
tória. Começando pela classificação das variáveis, descrevemos como apresentar e resumir cada uma
delas, bem como, verificar as suas relações por meio das medidas resumo e opções gráficas mais simples
e conhecidas. Ressaltamos que o tópico de análise descritiva e exploratória é um campo sem fim e que
este capítulo apenas fornece o ferramental básico para uma análise mínima. No próximo Capítulo
vamos juntar tudo que aprendemos nos Capítulos anteriores em uma análise de dados real.
No Capítulo 6 foi apresentado como lidar com dados desde a sua importação, até arrumação,
manipulação e exportação usando os princípios do tidy data. No entanto, apenas ter os dados organi-
zados não significa ter informações sobre o problema de interesse. Para realmente tirar o melhor dos
nossos dados precisamos analisá-los usando métodos estatísticos apropriados.
A melhor forma de aprender a fazer uma análise descritiva é fazendo uma análise descri-
tiva. Neste Capítulo vamos explorar um conjunto de dados de uma área muito interessante cha-
mada de people analytics (análise de pessoas). Os dados são uma adaptação do conjunto de dados
Human Resources Data Set disponível na plataforma kaggle. As adaptações foram foitas para tor-
nar o conjunto de dados mais simples por meio da retirada de algumas variáveis e também da tra-
dução e adaptação dos dados para o contexto brasileiro. Os dados estão disponíveis no endereço
www.leg.ufpr.br/~wagner/data/people_analytics.csv. O caracterer delimitador é ; e podemos facil-
mente ler essa base de dados usando a função read.table().
163
164 Capítulo 9. Estatística descritiva e exploratória com o tidyverse
▶ performance_score: escore dado pelo recrutador que indica o grau de compatibilidade entre o
candidato e a vaga.
▶ razao_demissao: razão pela qual o colaborador foi demitido. No caso de não ter sido demitido
é colocado como ainda empregado.
Por outro lado, variáveis quantitativas representam medidas numéricas. Se essas medidas
puderem assumir apenas um número finito ou contável de valores, dizemos que se trata de uma
variável quantitativa discreta. Se as medidas puderem assumir um número infinito de valores então
temos uma variável quantitativa contínua. Uma dica para diferenciar entre variáveis discretas e
contínuas é: se puder contar é discreta, se medir é contínua.
Olhando para o conjunto de dados people analytics podemos facilmente classificar cada uma
de suas variáveis usando as diretrizes apresentadas na Figura 8.1.
# Tabela genero
table(dados$genero)
##
## feminino masculino
## 176 135
##
## casado divorciado separado solteiro viúvo
## 124 30 12 137 8
##
## administracao engenharia de software executivo
## 2.894 3.537 0.322
## producao TI vendas
## 67.203 16.077 9.968
##
prop.table(table(dados$forma_recrutamento))*100
##
## aplicação online careerbuilder google Indeed
## 0.322 7.395 15.756 27.974
## indicação LinkedIn outros vagas inclusivas
## 9.968 24.437 0.643 9.325
## website
## 4.180
Para representação gráfica, o mais indicado para variáveis qualitativas nominais é o gráfico
de barras e de setores. O gráfico de setores apesar de seu aspecto histórico não é recomendado por ser
de difícil interpretação. Em geral nossos olhos têm dificuldade em diferenciar o tamanho das fatias.
Vamos ilustrar o seu uso, porém sugerimos o uso cauteloso desta opção.
Em R existem funções nativas como as funções barplot() e pie() para obter os gráficos de
barras e setores. Contudo vamos ilustrar como obter gráficos utilizando outro pacote do tidyverse:
o ggplot2. Trata-se de um pacote baseado na gramática dos gráficos, ou seja, tem um modelo mental
mais claro para elaboração de gráficos. O pacote permite plotagem multi-painel e mapeamento em
variáveis visuais retinais.
library(ggplot2)
help(ggplot2)
ggplot(data = dados,
mapping = aes(x = estado_civil)) +
geom_bar() +
xlab(”Estado civil”) +
ylab(”Frequência absoluta”)
100
Frequência absoluta
50
ggplot(data = dados,
mapping = aes(x = factor(1),
fill = factor(estado_civil))) +
labs(fill = ”Estado civil”) +
geom_bar(width = 0.75,
col = 1) +
coord_polar(theta = ”y”) +
xlab(NULL) +
ylab(NULL)
No caso de uma variável qualitativa com vários níveis, como o estado civil, é interessante
ordenar o gráfico do maior para o menor. Isso ajuda a identificar pontos extremos e estabelecer
comparações de forma mais rápida. Para isso, precisamos trocar a classe da variável estado_civil
para fator e dizer qual a ordem de seus níveis. O Código 9.3 ilustra este processo.
300 0
1
Estado civil
casado
divorciado
separado
solteiro
viúvo
100
200
ggplot(data = dados,
mapping = aes(x = estado_civil)) +
geom_bar() +
xlab(”Estado civil”) +
ylab(”Frequência absoluta”)
100
Frequência absoluta
50
Da mesma forma que as tabelas de frequência, o gráfico de barras pode ser feito usando a
frequência absoluta ou relativa.
ggplot(data = dados,
mapping = aes(x = estado_civil)) +
geom_bar(aes(y = (..count..)/sum(..count..))) +
xlab(”Estado civil”) +
ylab(”Frequência absoluta”)
0.4
0.3
Frequência absoluta
0.2
0.1
0.0
table(dados$performance_score)
##
## atende totalmente excede insuficiente
## 243 37 13
## precisa melhorar
## 18
Código 9.4. Exemplo de como definir a ordem de uma variável qualitativa ordinal.
##
## insuficiente precisa melhorar atende totalmente
## 4.18 5.79 78.14
## excede
## 11.90
Neste caso também faz sentido incluir a chamada frequência acumulada que nada mais é
do que somar os percentuais até determinado nível da variável qualitativa. Por exemplo, se temos
interesse no percentual de colaborares que tem escore precisa melhorar ou menor precisamos somar
insuficiente e precisa melhorar. Para obter as frequências acumuladas usamos a função cumsum(),
conforme ilustrado no Código 9.5.
cumsum(prop.table(table(dados$performance_score))*100)
Novamente o gráfico de barras e setores são os mais indicados para análise gráfica.
ggplot(data = dados,
mapping = aes(x = performance_score)) +
geom_bar() +
xlab(”Escore”) +
ylab(”Frequência absoluta”)
250
200
Frequência absoluta
150
100
50
Neste caso, precisa ser avaliado se vale a pena mudar a ordem para que as classes mais fre-
quentes apareçam a esquerda do gráfico. Em geral é preferível manter a ordem natural da variável,
mas sempre existem exceções.
Outro aspecto a considerar é quando a variável tem muitos níveis. Por exemplo, a variá-
vel posicao tem 26 níveis. Ao fazer uma tabela com tantos níveis pode ser difícil de visualizar os
resultados.
table(dados$posicao)
##
## Administrador BD Analista de dados
## 5 8
## Arquiteto de dados Arquiteto de empreendimentos
## 2 1
## Assistente administrativo CEO
## 3 1
## CIO Contador
## 1 3
## Contador senior DBA senior
## 2 2
## Desenvolvedor BI Desenvolvedor BI senior
## 4 3
## Diretor BI Diretor de operações
## 1 1
## Diretor de TI Diretos de vendas
## 1 1
## Engenheiro de rede senior Engenheiro de redes
## 5 5
## Engenheiro de software Gerente de produção
## 10 14
## Gerente de serviços Gerente de TI
## 1 12
## Gerente de vendas Gerente engenheiro de software
## 30 1
## Técnico de produção I Técnico de produção II
## 137 57
Uma dica útil é tentar agrupar os níveis que tenham baixa ocorrência ou que façam sentido
serem agrupados. Por exemplo, poderíamos juntar os níveis Técnico de produção I e Técnico de
produção II, desde que faça sentido prático. Essa é uma decisão que deve ser tomada junto com o
time que vai usar suas análises. Outra opção seria agrupar Contador e Contador Senior. O importante
é que a forma de agrupar deve estar de acordo com os objetivos da análise e alinhada com toda a equipe
que vai se beneficiar dela.
A análise exploratória de variáveis quantitativas discretas com poucos valores é de certa forma
similar a análise de variáveis qualitativas ordinais. Assim, tabelas de frequência absoluta, relativa e
acumulada são ferramentas populares. Os gráficos de barras também são indicados. Em nosso exemplo
a única variável quantitativa discreta é o número de faltas. Podemos fazer uma tabela de frequência
absoluta, relativa e acumulada, conforme ilustra o Código 9.6.
# Frequencia absoluta
abs <- table(dados$faltas)
# Frequencia relativa
percentual <- prop.table(table(dados$faltas))*100
## Frequencia acumulada
acumulada <- cumsum(prop.table(table(dados$faltas)))
Apesar de ser possível, a tabela gerada ficou grande e de difícil interpretação, pois existem 20
valores distintos para a variável faltas. Nestes casos o mais indicado é tratar as variáveis quantitativas
discretas como quantitativas contínuas, conforme veremos na próxima subseção.
Em R a função cut() é bastante útil para agrupar as observações em faixas. Criar as faixas
é um assunto que requer bastante cuidado. Se você estiver em alguma situação em que as classes
podem ser criadas de acordo com alguma regra de negócio ou mesmo conforme interesse da equipe,
é perfeitamente aceitável usar essa informação para criar as classes. Caso contrário existem algumas
técnicas estatísticas para ajudar com esta tarefa. O importante é ter em mente que essa deve ser uma
decisão pensada de acordo com o negócio que você está lidando.
Vamos supor que um interesse da equipe é comparar as faixas de renda dentro da empresa
com as da população de forma geral. Você consultou o site do IBGE e constatou que para pesquisas
populacionais o IBGE usa as seguintes faixas de renda para obter a classe social de uma pessoa.
Classe Renda
A R$ 20.900,01 ou mais
B R$ 10.450,01 até R$ 20.900,00
C R$ 4.180,01 até R$ 10.450,01
D R$ 2.090,01 até R$ 4.180,00
E até R$ 2.090,01
Note que não precisamos explicitamente seguir a tabela do IBGE, uma vez que podemos
usar intervalos abertos a direita. Isso significa que entre 0 e 2.090, 01 o valor exato 2.090, 01 não será
contado na primeira faixa e assim por diante. Outro aspecto para estar atento é que a classificação do
IBGE é baseada na renda mensal. Por outro lado, nossa base de dados reporta a renda anual. Assim,
vamos primeiro dividir a variável por 12 para ter a renda mensal média. Para definir um intervalo
aberto a direita usamos as opção right = FALSE.
##
## (0,2.09e+03] (2.09e+03,4.18e+03] (4.18e+03,1.05e+04]
## 0 31 270
## (1.05e+04,2.09e+04] (2.09e+04,Inf]
## 10 0
ggplot(data = dados,
mapping = aes(x = salario)) +
geom_histogram() +
xlab(”Salário anual”) +
ylab(”Frequência absoluta”)
Apesar do gráfico da Figura 9.4 ser popularmente chamado de histograma, técnicamente ele
é uma adaptação do histograma original. Note que, no eixo y, temos a representação das frequências
absolutas de cada faixa de valores. Contudo, na concepção original do histograma, o eixo y é usado
para representar o que chamamos de densidade, conforme ilustrado na Figura 9.5.
ggplot(data = dados,
mapping = aes(x = salario,
y = ..density..)) +
geom_histogram() +
xlab(”Salário anual”) +
ylab(”Densidade”)
A ideia da densidade consiste em pensar qual o valor deve ser colocado no eixo y para que
quando a área de cada retângulo for calculada seja obtida a frequência relativa. Assim, a densidade é
calculada da seguinte forma
75
Frequência absoluta
50
25
Lembre-se que a área de um retângulo é calculada como o tamanho da base vezes a altura,
ou seja,
Como queremos que a área seja igual a frequência relativa, basta isolar a altura. Chamamos
esta altura de densidade. Para ilustrar, vamos usar como exemplo o cálculo da densidade para a variável
engajamento utilizando a função hist().
4e-05
3e-05
Densidade
2e-05
1e-05
0e+00
Figura 9.5: Histograma para a variável salário anual com a densidade representada na eixo y.
## $breaks
## [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
##
## $counts
## [1] 2 6 11 8 37 46 98 103
##
## $density
## [1] 0.0129 0.0386 0.0707 0.0514 0.2379 0.2958 0.6302 0.6624
##
## $mids
## [1] 1.25 1.75 2.25 2.75 3.25 3.75 4.25 4.75
##
## $xname
## [1] ”dados$engajamento”
##
## $equidist
## [1] TRUE
##
## attr(,”class”)
## [1] ”histogram”
Ao analisar o conteúdo do objeto histograma vemos que temos as classes (breaks), a frequência
absoluta (counts) e as densidades (density). A classe modal (maior frequência) é a que vai de 4.5 até 5.
Para calcular a densidade desta classe:
1. Calculamos a amplitude da classe, ou seja, o limite superior (5) menos o limite inferior (4.5).
Logo, a amplitude é 5 − 4.5 = 0.5.
2. Calculamos a frequência relativa da classe. Neste caso 103/311 = 0.331.
3. Calculamos a densidade 0.331/0.5 = 0.662.
Os cálculos são idênticos para as demais classes. Mas você deve estar se perguntando: por
que complicar o que estava fácil?
ggplot(data = dados,
mapping = aes(x = engajamento)) +
geom_histogram() +
facet_wrap(facets = ~genero) +
xlab(”Engajamento”) +
ylab(”Freq. Absoluta”)
Para variáveis com muitos níveis pode ficar difícil avaliar tantos histogramas. Além disso, os
histogramas podem ter comportamentos parecidos, o que torna difícil encontrar diferenças apenas
visualmente. A estatística nos proporciona um conjunto de medidas numéricas para resumir a distri-
buição de frequências. Em geral olhamos para dois aspectos básicos da distribuição de frequência: a
sua tendência central e sua dispersão ou espalhamento.
▶ Média aritmética: para uma variável quantitativa basta somar todos os seus valores e dividir
pelo número de observações. A interpretação física é que a média é o centro de gravidade da
distribuição empírica. Pense que cada um dos retângulos é uma massa com peso igual a sua
densidade. Imagine que o eixo x é uma régua. O ponto médio é onde você deve colocar uma
agulha para perfeitamente equilibrar as massas sobre a régua.
feminino masculino
30
20
Freq. Absoluta
10
1 2 3 4 5 1 2 3 4 5
Engajamento
menor para o maior. A mediana é o valor que estiver na posição i) (N + 1)/2 se N é ímpar e a
média dos valores das posições (N/2) e (N/2)+1 se N par. A ideia é saber exatamente que 50%
das observações tem valores menores do que a mediana. Óbviamente, 50% das observações tem
valores maiores do que a mediana.
▶ Moda: valor mais frequente. No caso de uma variável onde praticamente só existem valores
únicos, essa medida tem pouca aplicação prática. Você pode reportar a classe modal se for de
interesse. Em geral, a média e a mediana costumam ser suficientes para ter um boa noção da
centralidade da distribuição empírica.
Neste livro não vamos dar ênfase as formulas de como obter tais estatísticas. Nosso objetivo é
mostrar como obtê-las usando o R. Para a variável renda_mensal podemos facilmente calcular a média
e a mediana, conforme ilustra o Código 9.7.
# Média
mean(dados$renda_mensal)
## [1] 5752
# Mediana
median(dados$renda_mensal)
## [1] 5234
quantile(dados$renda_mensal)
Além dos quartis, a função quantile() apresenta os valores mínimos e máximos. Juntos, esses
valores são o chamado resumo de cinco números. É muito comum apresentar o resumo dos cinco
números em um diagrama de caixas ou boxplot, conforme ilustrado na Figura 8.10.
ggplot(data = dados,
mapping = aes(x = 1, y = renda_mensal)) +
geom_boxplot() +
xlab(””)+
ylab(”Renda mensal”)
20000
15000
Renda mensal
10000
5000
▶ A primeira linha horizontal representa o valor mínimo, sem considerar os valores que ultrapas-
sam o desvio interquartílico.
O desvio interquartílico é definido como 1.5 vezes o terceiro quartil menos o primeiro quartil.
É um valor de referência e cria a chamada barreira de valores atípicos. Muitos analistas chamam os
valores além do desvio interquartilico de outliers. Porém, isso não é verdade e a definição exata do
que é um outliers depende de outros aspectos como do modelo que será ajustado. Neste livro não
vamos entrar neste tipo de detalhe. Tenha em mente de que estamos apenas na parte inicial da análise
e a detecção de outliers é uma das etapas finais da modelagem estatística e portanto além dos objetivos
deste livro.
## [1] 17080
# Variância
var(dados$renda_mensal)
## [1] 4394836
# Desvio-padrão
sd(dados$renda_mensal)
## [1] 2096
## Coeficiente de variação
(sd(dados$renda_mensal)/mean(dados$renda_mensal))*100
## [1] 36.4
Existem diversas outras medidas resumo tanto de tendência central quanto de dispersão. Para
mais detalhes considere o curso Probabilidade e Estatística para Ciência de dados e a bibliografia
complementar ao final deste livro.
A vantagem de usar essas medidas resumo é mais facilmente poder comparar diferentes grupos
com relação a tendência central e/ou variabilidade. Por exemplo, podemos estar interessados em
comparar o salário médio dos colaboradores de acordo com o departamento em que trabalham. Já
vimos que a função summarise() do pacote dplyr é especialmente útil para este tipo de resumo.
library(dplyr)
dados %>%
group_by(departamento) %>%
summarise(media = mean(renda_mensal))
## # A tibble: 6 x 2
## departamento media
## <chr> <dbl>
## 1 administracao 5983.
## 2 engenharia de software 7916.
## 3 executivo 20833.
## 4 producao 4996.
## 5 TI 8089.
## 6 vendas 5755.
Note que agora estamos usando uma variável para condicionar o comportamento de outra.
Isso é o que chamamos de análise descritiva bivariada e será o assunto da próxima subseção.
▶ Qualitativa × qualitativa;
▶ Qualitativa × quantitativa;
▶ Quantitativa × quantitativa.
Novamente, temos gráficos e medidas numéricas para resumir a relação entre as variáveis.
Nesta seção vamos ver apenas as medidas e gráficos mais populares. Para mais detalhes considere o
curso Manipulação e visualização com o R e as referências no final deste livro.
table(dados$forma_recrutamento, dados$genero)
##
## feminino masculino
## aplicação online 0 1
## careerbuilder 16 7
## google 33 16
## Indeed 50 37
## indicação 11 20
## LinkedIn 44 32
## outros 1 1
## vagas inclusivas 15 14
## website 6 7
Para economizar um pouco na digitação dos comandos podemos usar a função with().
## genero
## forma_recrutamento feminino masculino
## aplicação online 0 1
## careerbuilder 16 7
## google 33 16
## Indeed 50 37
## indicação 11 20
## LinkedIn 44 32
## outros 1 1
## vagas inclusivas 15 14
## website 6 7
A função prop.table() continua sendo a nossa escolha, conforme ilustra o Código 9.11.
## genero
## forma_recrutamento feminino masculino
## aplicação online 0.00000 0.00322
## careerbuilder 0.05145 0.02251
## google 0.10611 0.05145
## Indeed 0.16077 0.11897
## indicação 0.03537 0.06431
## LinkedIn 0.14148 0.10289
## outros 0.00322 0.00322
## vagas inclusivas 0.04823 0.04502
## website 0.01929 0.02251
## genero
## forma_recrutamento feminino masculino
## aplicação online 0.000 1.000
## careerbuilder 0.696 0.304
## google 0.673 0.327
## Indeed 0.575 0.425
## indicação 0.355 0.645
## LinkedIn 0.579 0.421
## outros 0.500 0.500
## vagas inclusivas 0.517 0.483
## website 0.462 0.538
## genero
## forma_recrutamento feminino masculino
## aplicação online 0.00000 0.00741
## careerbuilder 0.09091 0.05185
## google 0.18750 0.11852
## Indeed 0.28409 0.27407
## indicação 0.06250 0.14815
## LinkedIn 0.25000 0.23704
## outros 0.00568 0.00741
## vagas inclusivas 0.08523 0.10370
## website 0.03409 0.05185
As vezes pode ser de interesse adicionar o total das linhas e colunas para facilitar a interpreta-
ção. A função addmargins() cumpre esse papel, conforme ilustra o Código 9.12.
## genero
## forma_recrutamento feminino masculino Sum
## aplicação online 0.00000 0.00322 0.00322
## careerbuilder 0.05145 0.02251 0.07395
## google 0.10611 0.05145 0.15756
## Indeed 0.16077 0.11897 0.27974
## indicação 0.03537 0.06431 0.09968
## LinkedIn 0.14148 0.10289 0.24437
## outros 0.00322 0.00322 0.00643
## vagas inclusivas 0.04823 0.04502 0.09325
## website 0.01929 0.02251 0.04180
## Sum 0.56592 0.43408 1.00000
ggplot(data = dados,
mapping = aes(x = forma_recrutamento,
fill = genero)) +
geom_bar(position = ”stack”) +
xlab(”Recrutamento”)+
ylab(”Freq. absoluta”) +
theme(legend.position = ”bottom”)
ggplot(data = dados,
mapping = aes(x = genero,
fill = status)) +
geom_bar(position = ”stack”) +
xlab(”Gênero”)+
ylab(”Freq. absoluta”)
Na Figura 9.8 enfatizamos a variável que representa a forma de recrutamento e por isso é ela
quem aparece formando as barras. A variável gênero tem um papel apenas complementar. Note que
a comparação em termos de composição por gênero deve ser feita dentro de cada coluna, uma vez
que a comparação entre colunas é difícil por não terem a mesma altura.
Por outro lado, na Figura 9.9, enfatizamos a composição de cada nível da variável gênero de
acordo com a variável status. Como as barras apresentam a frequência relativa, podemos comparar as
frequências não só dentro de cada barra mas também entre as barras.
Quando o objetivo for comparar os níveis de uma variável de acordo com os níveis de uma
75
Freq. absoluta
50
25
outra variável podemos usar as barras lado a lado, conforme ilustra a Figura 9.10.
ggplot(data = dados,
mapping = aes(x = status,
fill = performance_score)) +
geom_bar(position = ”dodge”) +
xlab(”Status”)+
ylab(”Freq. absoluta”)
Por fim, em casos em que o número de níveis da variável é grande podemos apresentar as
barras na horizontal. A Figura 9.11 ilustra esta opção.
ggplot(data = dados,
mapping = aes(x = forma_recrutamento)) +
geom_bar(position = ”dodge”) +
xlab(”Recrutamento”)+
ylab(”Freq. absoluta”) +
coord_flip()
150
Freq. absoluta
status
100
ativo
justa causa
pediu demissão
50
feminino masculino
Gênero
comparar o salário médio dos colaboradores de acordo com o departamento em que eles trabalham.
Nós já vimos que para este tipo de tarefa a função summarise() atende ao propósito. O Código 9.13
ilustra como calcular não apenas a média mas também a mediana e o desvio-padrão da renda mensal
por nível da variável departamento.
Código 9.13. Calculando a média, mediana e desvio-padrão da renda mensal por departamento.
dados %>%
group_by(departamento) %>%
summarise(media = mean(renda_mensal),
mediana = median(renda_mensal),
sd = sd(renda_mensal))
## # A tibble: 6 x 4
## departamento media mediana sd
## <chr> <dbl> <dbl> <dbl>
## 1 administracao 5983. 5250. 1809.
## 2 engenharia de software 7916. 7972. 797.
## 3 executivo 20833. 20833. NA
## 4 producao 4996. 4956 952.
## 5 TI 8089. 7694. 2767.
## 6 vendas 5755. 5442. 1773.
Tente entender o porquê o desvio-padrão da renda mensal na classe executivo retornou como
NA.
Recomendamos usar o boxplot da variável quantitativa por nível da variável qualitativa para
análise gráfica, conforme ilustrado na Figura 9.12.
150
performance_score
Freq. absoluta
100
insuficiente
precisa melhorar
atende totalmente
excede
50
ggplot(data = dados,
mapping = aes(x = forma_recrutamento,
y = renda_mensal)) +
geom_boxplot() +
xlab(”Recrutamento”)+
ylab(”Renda”)
Para melhorar a visualização você pode ordenar as caixas de acordo com a renda mediana
conforme Figura 9.13.
ggplot(data = dados,
mapping = aes(x = forma_recrutamento,
y = renda_mensal)) +
geom_boxplot() +
xlab(”Recrutamento”)+
ylab(”Renda”)
Uma outra opção é criar faixas para a variável quantitativa e usar gráficos de barras como na
subseção 7.3.1.
aplicação online
outros
website
careerbuilder
Recrutamento
vagas inclusivas
indicação
Indeed
0 25 50 75
Freq. absoluta
ggplot(data = dados,
mapping = aes(x = engajamento,
y = renda_mensal)) +
geom_point() +
xlab(”Engajamento”)+
ylab(”Renda”)
Olhando para o diagrama de dispersão da Figura 9.14 tente imaginar uma linha descrevendo
a relação entre as variáveis renda mensal e engajamento. Se a linha que você imaginar for conforme a
da Figura 9.15(A) significa que visualmente as duas variáveis não estão associadas ou tem uma relação
fraca. Por outro lado, se a linha estiver como na Figura 9.15(B) indica que as duas variáveis estão
positivamente associadas, ou seja, ao aumentar o valor de uma a outra também aumenta. Contudo,
se a linha for como na Figura 9.15(C) indica que a relação é negativa, ou seja, ao aumentar uma das
variáveis a outra diminui.
Para o nosso exemplo vemos uma leve tendência de relação positiva entre engajamento e
renda mensal. Porém a variabilidade (dispersão dos pontos) é bem alta e parece depender dos valores
20000
15000
Renda
10000
5000
Figura 9.12: Boxplot da variável renda mensal por níveis da variável forma de recrutamento.
Como você pode notar, interpretar um gráfico como o diagrama de dispersão é uma ativi-
dade bastante subjetiva. Existem algumas medidas numéricas que buscam resumir a relação linear
entre duas variáveis quantitativas, são os chamados coeficientes de correlação. Em geral usamos três
coeficientes de correlação: Pearson, Spearman e Kendal. Os três variam entre -1 e 1, sendo que −1
indica correlação perfeita negativa, todos os pontos caem exatamente em cima de uma reta como
da Figura 9.15(C). Uma correlação igual a 1 indica correlação positiva perfeita e neste caso todos os
pontos estariam em cima de uma reta como a apresentada na Figura 9.15(B). Se o valor da correlação
for zero não temos relação entre as variáveis e todos os pontos estariam em cima de uma reta como a
apresentada na Figura 9.15(A).
Na prática, esses valores são apenas referências e o que é considerada uma correlação forte
ou fraca vai depender do contexto. Além disso, um erro comum é dizer que se duas variáveis tem
correlação igual a zero elas são independentes. Essa afirmação em geral é falsa. O conceito de in-
dependência estatística requer a suposição de uma distribuição conjunta de probabilidades e não é
facilmente definido para qualquer distribuição a parte da distribuição normal bivariada. Conceitos de
Teoria das Probabilidades estão além do escopo deste livro. Para referência consulte a bibliografia
no final deste livro.
20000
15000
Renda
10000
5000
Por fim, sempre que temos mais do que uma opção, a pergunta natural é “qual é o melhor
coeficiente de correlação?”. Apesar de uma pergunta razoável, a resposta depende da situação e, em
geral, o melhor é calcular dois ou três deles e comparar os resultados. O coeficiente de correlação de
Pearson é o estimador de máxima verossimilhança do parâmetro de correlação de uma distribuição
normal bivariada. Neste contexto, se a distribuição conjunta for uma normal bivariada ele seria o
indicado. Porém, na prática não sabemos qual é a verdadeira distribuição conjunta das variáveis
envolvidas na análise. Uma boa indicação é que a distribuição de frequência empírica de cada variável
é aproximadamente simétrica. Novamente é um critério subjetivo e difícil de se verificar em termos
práticos. Porém, nesta etapa de análise exploratória onde não temos nenhuma suposição distribucional
o coeficiente de correlação de Pearson é uma boa indicação da correlação linear entre as variáveis.
20000
15000
Renda
10000
5000
1 2 3 4 5
Engajamento
simétricas use o de Pearson. Caso contrário use Spearman. É importante ressaltar que essas são re-
comendações práticas para o caso em que o interesse seja obter apenas um. Na prática você pode
facilmente calcular os três, conforme ilustrado no Código 9.14.
# Pearson
with(dados, cor(renda_mensal, engajamento, method = ”pearson”))
## [1] 0.065
# Spearman
with(dados, cor(renda_mensal, engajamento, method = ”spearman”))
## [1] 0.0311
# Kendall
with(dados, cor(renda_mensal, engajamento, method = ”kendall”))
## [1] 0.0214
Conforme a Figura 9.14 já havia indicado, a correlação entre as variáveis renda mensal e
engajamento é fraca (valor próximo de zero). Também vemos que o coeficiente de Pearson é um
pouco maior que o de Spearman que por sua vez é um pouco maior que o de Kendall. Em geral essa
ordem vai se manter para a maioria das análises de correlação que você fizer. Isso se deve pela forma
como cada coeficiente é construído. Não vamos entrar em detalhes nas equações, porém mantenha
40
40
12
20
20
f (x)
f (x)
f (x)
10
0
8
-20
-20
6
isso em mente e não se preocupe com o valor exato. O importante é a avaliação de existência ou não
de relação entre variáveis e qual a força desta associação.
Neste capítulo apresentamos uma visão geral das técnicas de estatística descritiva e explora-
tória. Começando pela classificação das variáveis, descrevemos como apresentar e resumir cada uma
delas, bem como, verificar as suas relações por meio das medidas resumo e opções gráficas mais simples
e conhecidas. Ressaltamos que o tópico de análise descritiva e exploratória é um campo sem fim e que
este capítulo apenas fornece o ferramental básico para uma análise mínima. No próximo Capítulo
vamos juntar tudo que aprendemos nos Capítulos anteriores em uma análise de dados real.
Nosso exemplo corresponde a um conjunto de dados coletado do site tabsus, mantido pelo
Ministério da Saúde. Os dados referem-se a episódios de violência contra crianças e adolescentes
por ano, faixa etária e tipo de crime (violência sexual, assédio sexual, estupro, pornografia infantil e
atentado violento ao pudor). O conjunto de dados está disponível no link no formato .csv (comma-
separated value).
Os dados foram mantidos da forma mais crua possível para que você vivencie todos os desafios
nas etapas de importar, manipular, analisar e apresentar os resultados de uma análise de dados.
Se a leitura foi executada com sucesso você não deve receber nenhuma mensagem. Assim,
é hora de inspecionar o que foi carregado. Recomendamos usar a função str() para ter uma visão
195
196 Capítulo 10. Praticando com o R base
str(dados)
A função str() começa mostrando quantas observações e quantas colunas a base de dados
contém. Neste caso temos 70115 observações (linhas) e 11 variáveis (colunas). Na sequência é apre-
sentado o nome de cada coluna e o tipo de dado que ela representa. Neste exemplo, temos variáveis
do tipo int e chr que são abreviações dos tipos de vetores integer e character. Apesar do nome das
colunas ser auto explicativo é sempre interessante manter um dicionário da sua base de dados. Um
dicionário nada mais é do que uma lista com o nome de cada coluna e o que ela representa. Por
exemplo,
É importante notar que o mesmo episódio pode envolver mais de um tipo de crime. Cada
linha corresponde a características de episódios sendo que a coluna casos diz quantos episódios tive-
ram as características. Na colunas correspondentes aos crimes temos apenas os valores sim caso aquele
crime fez parte do episódio, caso contrário teremos o valor NA. Lembre-se, essa é uma base de dados
real e foi assim que a captura dos dados os armazenou.
Ao se deparar com um conjunto de dados como este precisamos nos perguntar o que quere-
mos mostrar/saber. Nem sempre o objetivo é claro e precisamos investigar os aspectos dos dados para
saber quais são as possibilidades. É neste momento que precisamos saber como manipular a base de
Vamos começar a explorar a nossa base de dados acessando as variáveis e verificando quais
são os seus possíveis valores. Por exemplo, podemos fazer uma tabela de frequência para saber a quais
anos os dados se referem e quantos registros temos em cada ano.
table(dados$ano)
##
## 2015 2016 2017 2018 2019
## 10973 11625 13872 16370 17275
Neste caso vemos que os dados são de 2015 a 2019 e o ano com maior número de registros é
o ano de 2018 com 17275 registros. É importante notar que o número de registros não é o número
de episódios, mas sim o número de episódios que foram devidamente reportados.
Neste exemplo, nós usamos o operador $ para acessar os dados da variável ano e a função
table() para obter a tabela de frequências. Podemos usar a mesma ideia para conhecer as outras
variáveis. Por exemplo, para a faixa etária temos
table(dados$faixa_etaria)
##
## <1 Ano 1-4 10-14 15-19 5-9
## 1759 11694 25312 15004 16346
Aqui temos um resultado interessante. A variável faixa etária tem uma ordenação natural,
porém da forma como a tabela foi feita, essa ordem não foi respeitada.
Para informar ao R que a variável faixa_etaria é um fator devemos converter a sua classe
para factor e indicar a sua ordem natural.
##
## <1 Ano 1-4 5-9 10-14 15-19
## 1759 11694 16346 25312 15004
Com isso, temos uma ideia inicial dos dados. Mas para realmente fazer uma análise precisamos
definir qual será o nosso objetivo. Vamos supor que o objetivo da análise é comparar o número de
episódios de cada crime em cada estado por ano de ocorrência.
Assim, precisamos contar quantos episódios de cada crime ocorreu em cada estado e em cada
ano. Porém, não temos uma variável estado em nossa base de dados. Neste caso vamos precisar obter
esta informação por meio de outra base de dados. O arquivo Pop_2018.csv traz a lista de municipios
com a sua população estimada para 2018 e também a qual unidade federativa ele pertence. Vamos
importá-lo e ver o seu conteúdo
str(pop2018)
O conjunto de dados é bem simples e tem uma coluna chamada UF para representar a unidade
federativa. Além disso, temos duas colunas: uma chamada COD e outra chamada COD_MUNI que juntas
vão nos ajudar a unir as bases de dados pop2018 e dados. Neste passo precisamos fazer a junção das
duas bases de dados para sabermos a qual estado cada município pertence. Para isso vamos usar a
função merge(). Porém, antes disso, precisamos garantir que existe um código único para cruzar as
bases. O primeiro passo nesta direção é criar uma nova coluna em pop2018 juntando as colunas COD
e COD_MUNI.
No conjunto de dados dados a coluna cod tem 6 dígitos, enquanto que juntando as colunas
COD e COD_MUNI temos 7 dígitos. Após uma busca na documentação das fontes dos dados percebemos
que o último dígito da coluna cod na base de dados pop2018 é desnecessário. Assim, precisamos
eliminá-lo para então poder fazer a junção. A função substring() nos permite extrair os seis primeiros
dígitos de uma string.
## UF MUNICIPIO cod
## 1 RO Alta Floresta D Oeste 110001
## 2 RO Ariquemes 110002
## 3 RO Cabixi 110003
## 4 RO Cacoal 110004
## 5 RO Cerejeiras 110005
## 6 RO Colorado do Oeste 110006
Com as colunas de código único corretas precisamos pensar no tipo de junção que deseja-
mos fazer. No data.frame pop2018 temos todos os munícipios brasileiros. Por outro lado, no data.frame
dados temos apenas aqueles munícipios onde houve um episódio de crime contra criança ou adoles-
cente.
Neste caso é mais fácil primeiro calcularmos o número de episódios por tipo de crime, mu-
nicípio e ano. Assim, podemos resumir um pouco a base de dados antes de fazer a junção. Outro
aspecto complicador é que nós não temos números nas colunas que indicam os crimes. Assim, não
podemos simplesmente somar. A estratégia que vamos adotar aqui será a seguinte:
▶ Primeiro transformar cada coluna dos crimes em binárias (1 se o crime ocorreu e 0 caso contrá-
rio).
▶ Criar novas colunas multiplicando a indicadora de cada crime pela coluna casos.
▶ Fazer a agregação por municipio, ano e tipo de crime.
Temos as contagens de cada tipo de crime, porém para fazer a agregação o ideal é que haja
uma coluna com as observações de todos os crimes para que se torne mais fácil fazer a agregação por
crime, ou seja, precisamos que o conjunto de dados esteja no formato longo. Neste caso vamos fazer
manualmente para deixar o processo bem explícito.
Com os dados no formato longo, podemos finalmente fazer o resumo por ano, município e
tipo de crime.
Agora temos a coluna UF para finalmente fazer a agregação por estado, ano e tipo de crime.
SP
PR
MG
RS
RJ
AM
PA
PE
DF
SC
GO
BA
ES
TO
CE
PI
MA
AC
MT
MS
AL
SE
RO
RN
PB
RR
AP
Temos um gráfico, mas o que exatamente esse gráfico está representando? Note que os
estados em que mais temos episódios são aqueles com maior população. Para termos uma análise que
leve isso em conta precisamos do tamanho da população. Para nossa sorte o data.frame pop2018 já
tem a população para o ano de 2018, porém por município. Assim, se somarmos a população de cada
município que compõe os estados, teremos a população do estado. Posteriormente, poderemos juntar
com o data.frame tab e calcular a taxa de episódios, por exemplo, por cada 100.000 habitantes. Assim,
precisamos apenas juntar
Com a população de cada estado agora juntamos com os dados de episódios e calculamos a
taxa por cem mil habitantes.
AC
TO
AM
DF
PR
RR
PA
RS
ES
PE
SC
PI
MS
AP
RO
MG
RJ
MT
GO
SE
SP
AL
CE
MA
RN
BA
PB
0 5 10 15 20 25 30 35
Podemos notar que as taxas são consideravelmente diferentes. Não é de interesse entrar
em discussões a respeito dos dados, porém ressaltamos que as taxas são calculadas baseadas nos casos
reportados. Logo, estados que promovem um ambiente favorável ao reporte tendem a ter um número
de episódios maior que estados que não tem o aparato para tanto. Em resumo, muito cuidado ao
interpretar esses resultados.
O gráfico de barras é sem dúvida uma ferramenta muito útil. Entretanto, neste exemplo os
estados tem uma localização geográfica e seria interessante trazer esse componente espacial para a
análise descritiva. A forma mais simples de fazer isso é por meio de um mapa coropédico, ou seja, um
mapa que colore o poligono de cada estado de acordo com os valores das suas taxas de episódios.
O pacote geobr torna muito fácil fazer mapas em R uma vez que traz os mapas do Brasil por
install.packages(”geobr”)
O pacote geobr tem muitas dependências e pode ser demorada a instalação de todas elas. Após
a instalação carregamos o pacote e o mapa do Brasil por estado.
library(geobr)
mapa_estado <- read_state(year = 2018)
names(mapa_estado)[2] <- ”UF”
O pacote geobr tem uma forma de implementação compatível com os pacotes do chamado
tidyverse. Portanto, para usá-lo de forma simples precisamos instalar e carregar os pacotes ggplot2
que será o responsável por desenhar o mapa e o pacote dplyr que fará a junção da base de dados tab
com o mapa.
library(ggplot2)
library(dplyr)
mapa <- left_join(mapa_estado, tab, by = ”UF”)
ggplot() +
geom_sf(data = mapa, aes(fill = taxa))+
scale_fill_distiller(palette = ”Blues”)
0°
taxa
10°S
30
20
20°S 10
30°S
Com isso, encerramos este capítulo que teve como ideia fazer um apanhado geral de boa
parte dos conteúdos vistos anteriormente. De uma forma mais prática, focada em um problema
real, abordamos apenas os aspectos mais básicos da análise deste conjunto de dados. Convidamos o
leitor a explorar os outros tipos de crimes, anos e também análises que não foram feitas. Note que,
para chegar a um resultado apresentável, por mais simples que seja, foi necessário manipular, juntar
e resumir os diversos conjuntos de dados até a forma adequada para realizar uma análise descritiva.
Assim, ilustramos a maior parte do que precisa ser feito para trabalhar com dados em situações reais.
Nosso exemplo corresponde a um conjunto de dados coletado do site tabsus, mantido pelo
Ministério da Saúde. Os dados referem-se a episódios de violência contra crianças e adolescentes
por ano, faixa etária e tipo de crime (violência sexual, assédio sexual, estupro, pornografia infantil e
atentado violento ao pudor). O conjunto de dados está disponível no link no formato .csv (comma-
separated value).
Os dados foram mantidos da forma mais crua possível para que você vivencie todos os desafios
nas etapas de importar, manipular, analisar e apresentar os resultados de uma análise de dados.
library(readr)
dados <- read_csv(”data/ano-faixa-crime.csv”)
Agora é hora de inspecionar o que foi carregado. Recomendamos usar a função str() para
207
208 Capítulo 11. Praticando com o tidyverse
str(dados)
A função str() começa mostrando quantas observações e quantas colunas a base de dados
contém. Neste caso temos 70115 observações (linhas) e 11 variáveis (colunas). Na sequência é apre-
sentado o nome de cada coluna e o tipo de dado que ela representa. Neste exemplo, temos variáveis
do tipo num e chr que são abreviações dos tipos de vetores numeric e character. Apesar do nome das
colunas ser auto explicativo é sempre interessante manter um dicionário da sua base de dados. Um
dicionário nada mais é do que uma lista com o nome de cada coluna e o que ela representa. Por
exemplo,
pudor.
É importante notar que o mesmo episódio pode envolver mais de um tipo de crime. Cada
linha corresponde a características de episódios sendo que a coluna casos diz quantos episódios tive-
ram as características. Na colunas correspondentes aos crimes temos apenas os valores sim caso aquele
crime fez parte do episódio, caso contrário teremos o valor NA. Lembre-se, essa é uma base de dados
real e foi assim que a captura dos dados os armazenou.
Ao se deparar com um conjunto de dados como este precisamos nos perguntar o que quere-
mos mostrar/saber. Nem sempre o objetivo é claro e precisamos investigar os aspectos dos dados para
saber quais são as possibilidades. É neste momento que precisamos saber como manipular a base de
dados para entendê-la e investigar possíveis análises.
Vamos começar a explorar a nossa base de dados acessando as variáveis e verificando quais
são os seus possíveis valores. Por exemplo, podemos fazer uma tabela de frequência para saber a quais
anos os dados se referem e quantos registros temos em cada ano.
table(dados$ano)
##
## 2015 2016 2017 2018 2019
## 10973 11625 13872 16370 17275
Neste caso vemos que os dados são de 2015 a 2019 e o ano com maior número de registros é
o ano de 2019 com 17275 registros. É importante notar que o número de registros não é o número
de episódios, mas sim o número de episódios que foram devidamente reportados.
Neste exemplo, nós usamos o operador $ para acessar os dados da variável ano e a função
table() para obter a tabela de frequências. Podemos usar a mesma ideia para conhecer as outras
variáveis. Por exemplo, para a faixa etária temos
table(dados$faixa_etaria)
##
## <1 Ano 1-4 10-14 15-19 5-9
## 1759 11694 25312 15004 16346
Aqui temos um resultado interessante. A variável faixa etária tem uma ordenação natural,
porém da forma como a tabela foi feita, essa ordem não foi respeitada.
Para informar ao R que a variável faixa_etaria é um fator devemos converter a sua classe
para factor e indicar a sua ordem natural.
##
## <1 Ano 1-4 5-9 10-14 15-19
## 1759 11694 16346 25312 15004
Com isso, temos uma ideia inicial dos dados. Mas para realmente fazer uma análise precisamos
definir qual será o nosso objetivo. Vamos supor que o objetivo da análise é comparar o número de
episódios de cada crime em cada estado por ano de ocorrência.
Assim, precisamos contar quantos episódios de cada crime ocorreu em cada estado e em cada
ano. Porém, não temos uma variável estado em nossa base de dados. Neste caso vamos precisar obter
esta informação por meio de outra base de dados. O arquivo Pop_2018.csv traz a lista de municipios
com a sua população estimada para 2018 e também a qual unidade federativa ele pertence. Vamos
importá-lo e ver o seu conteúdo
str(pop2018)
O conjunto de dados é bem simples e tem uma coluna chamada UF para representar a unidade
federativa. Além disso, temos duas colunas: uma chamada COD e outra chamada COD_MUNI que juntas
vão nos ajudar a unir as bases de dados pop2018 e dados. Neste passo precisamos fazer a junção das
duas bases de dados para sabermos a qual estado cada município pertence. Para isso vamos usar a
função merge(). Porém, antes disso, precisamos garantir que existe um código único para cruzar as
bases. O primeiro passo nesta direção é criar uma nova coluna em pop2018 juntando as colunas COD
e COD_MUNI.
No conjunto de dados dados a coluna cod tem 6 dígitos, enquanto que juntando as colunas
COD e COD_MUNI temos 7 dígitos. Após uma busca na documentação das fontes dos dados percebemos
que o último dígito da coluna cod na base de dados pop2018 é desnecessário. Assim, precisamos
eliminá-lo para então poder fazer a junção. A função str_sub(), do pacote stringr nos permite
extrair os seis primeiros dígitos de uma string.
library(stringr)
pop2018$cod <- str_sub(pop2018$cod, 1, 6)
head(pop2018[,c(1,4,6)])
## # A tibble: 6 x 3
## UF MUNICIPIO cod
## <chr> <chr> <chr>
## 1 RO Alta Floresta D Oeste 110001
## 2 RO Ariquemes 110002
## 3 RO Cabixi 110003
## 4 RO Cacoal 110004
## 5 RO Cerejeiras 110005
## 6 RO Colorado do Oeste 110006
Com as colunas de código único corretas precisamos pensar no tipo de junção que deseja-
mos fazer. No data.frame pop2018 temos todos os munícipios brasileiros. Por outro lado, no data.frame
dados temos apenas aqueles munícipios onde houve um episódio de crime contra criança ou adoles-
cente.
Neste caso é mais fácil primeiro calcularmos o número de episódios por tipo de crime, mu-
nicípio e ano. Assim, podemos resumir um pouco a base de dados antes de fazer a junção. Outro
aspecto complicador é que nós não temos números nas colunas que indicam os crimes. Assim, não
podemos simplesmente somar. A estratégia que vamos adotar aqui será a seguinte:
▶ Primeiro transformar cada coluna dos crimes em binárias (1 se o crime ocorreu e 0 caso contrá-
rio).
▶ Criar novas colunas multiplicando a indicadora de cada crime pela coluna casos.
▶ Fazer a agregação por municipio, ano e tipo de crime.
Temos as contagens de cada tipo de crime, porém para fazer a agregação o ideal é que haja
uma coluna com as observações de todos os crimes para que se torne mais fácil fazer a agregação por
crime, ou seja, precisamos que o conjunto de dados esteja no formato longo. Neste caso vamos fazer
manualmente para deixar o processo bem explícito.
Com os dados no formato longo, podemos finalmente fazer o resumo por ano, município e
tipo de crime.
library(dplyr)
estado <- left_join(pop2018, resumo_casos, by = ”cod”)
Agora temos a coluna UF para finalmente fazer a agregação por estado, ano e tipo de crime.
library(ggplot2)
Temos um gráfico, mas o que exatamente esse gráfico está representando? Note que os
estados em que mais temos episódios são aqueles com maior população. Para termos uma análise que
leve isso em conta precisamos do tamanho da população. Para nossa sorte o data.frame pop2018 já
tem a população para o ano de 2018, porém por município. Assim, se somarmos a população de cada
município que compõe os estados, teremos a população do estado. Posteriormente, poderemos juntar
com o data.frame tab e calcular a taxa de episódios, por exemplo, por cada 100.000 habitantes. Assim,
precisamos apenas juntar
Com a população de cada estado agora juntamos com os dados de episódios e calculamos a
taxa por cem mil habitantes.
SP
PR
MG
RS
RJ
AM
PA
PE
DF
SC
GO
BA
ES
TO
CE
PI
MA
AC
MT
MS
AL
SE
RO
RN
PB
RR
AP
0 1000 2000 3000 4000
AC
TO
AM
DF
PR
RR
PA
RS
ES
PE
SC
PI
MS
AP
RO
MG
RJ
MT
GO
SE
SP
AL
CE
MA
RN
BA
PB
0 10 20 30
Podemos notar que as taxas são consideravelmente diferentes. Não é de interesse entrar
em discussões a respeito dos dados, porém ressaltamos que as taxas são calculadas baseadas nos casos
reportados. Logo, estados que promovem um ambiente favorável ao reporte tendem a ter um número
de episódios maior que estados que não tem o aparato para tanto. Em resumo, muito cuidado ao
interpretar esses resultados.
O gráfico de barras é sem dúvida uma ferramenta muito útil. Entretanto, neste exemplo os
estados tem uma localização geográfica e seria interessante trazer esse componente espacial para a
análise descritiva. A forma mais simples de fazer isso é por meio de um mapa coropédico, ou seja, um
mapa que colore o poligono de cada estado de acordo com os valores das suas taxas de episódios.
O pacote geobr torna muito fácil fazer mapas em R uma vez que traz os mapas do Brasil por
estado e também por município. O primeiro passo é instalar o pacote.
install.packages(”geobr”)
O pacote geobr tem muitas dependências e pode ser demorada a instalação de todas elas. Após
a instalação carregamos o pacote e o mapa do Brasil por estado.
library(geobr)
mapa_estado <- read_state(year = 2018)
names(mapa_estado)[2] <- ”UF”
O pacote geobr tem uma forma de implementação compatível com os pacotes do chamado
tidyverse. Portanto, para usá-lo de forma simples precisamos instalar e carregar os pacotes ggplot2
que será o responsável por desenhar o mapa e o pacote dplyr que fará a junção da base de dados tab
com o mapa.
library(ggplot2)
library(dplyr)
mapa <- left_join(mapa_estado, tab, by = ”UF”)
ggplot() +
geom_sf(data = mapa, aes(fill = taxa))+
scale_fill_distiller(palette = ”Blues”)
0°
taxa
10°S
30
20
20°S 10
30°S
Com isso, encerramos este capítulo que teve como ideia fazer um apanhado geral de boa
parte dos conteúdos vistos anteriormente. De uma forma mais prática, focada em um problema
real, abordamos apenas os aspectos mais básicos da análise deste conjunto de dados. Convidamos o
leitor a explorar os outros tipos de crimes, anos e também análises que não foram feitas. Note que,
para chegar a um resultado apresentável, por mais simples que seja, foi necessário manipular, juntar
e resumir os diversos conjuntos de dados até a forma adequada para realizar uma análise descritiva.
Assim, ilustramos a maior parte do que precisa ser feito para trabalhar com dados em situações reais.