0% acharam este documento útil (0 voto)
16 visualizações270 páginas

Aprenda Programação Prática com R

O livro 'Programação Prática com R' de Garrett Grolemund é uma introdução amigável à programação em R, voltada para não programadores, com foco em habilidades práticas para ciência de dados. Ele ensina a carregar e manipular dados, escrever funções e resolver problemas logísticos na ciência de dados através de projetos práticos. O autor enfatiza a importância de aprender a programar para se tornar um cientista de dados eficaz, utilizando exemplos concretos para facilitar a compreensão.

Enviado por

P Fernando
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato DOCX, PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
16 visualizações270 páginas

Aprenda Programação Prática com R

O livro 'Programação Prática com R' de Garrett Grolemund é uma introdução amigável à programação em R, voltada para não programadores, com foco em habilidades práticas para ciência de dados. Ele ensina a carregar e manipular dados, escrever funções e resolver problemas logísticos na ciência de dados através de projetos práticos. O autor enfatiza a importância de aprender a programar para se tornar um cientista de dados eficaz, utilizando exemplos concretos para facilitar a compreensão.

Enviado por

P Fernando
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato DOCX, PDF, TXT ou leia on-line no Scribd

Programação prática com R

Garrett Grolemund

Bem-vindo
Este é o site para "Programação Prática
com R" . Este livro ensinará você a
programar em R, com exemplos práticos.
Eu o escrevi para não programadores, a
fim de fornecer uma introdução amigável
à linguagem R. Você aprenderá a carregar
dados, montar e desmontar objetos de
dados, navegar pelo sistema de ambiente
do R, escrever suas próprias funções e
usar todas as ferramentas de
programação do R. Ao longo do livro,
você usará suas novas habilidades para
resolver problemas práticos de ciência de
dados.
Se você já se sente confortável com R e
gostaria de se concentrar em como
analisar dados usando os pacotes
Tidyverse do R, recomendo R for Data Science , um livro que escrevi em coautoria
com Hadley Wickham.
Este site é (e sempre será) gratuito e está licenciado sob a Licença Creative
Commons Atribuição-NãoComercial-SemDerivações 4.0 . Se você quiser uma cópia
física do livro, pode encomendá-lo na Amazon ; ele foi publicado pela O'Reilly em
julho de 2014. Se quiser retribuir, considere relatar um erro de digitação ou deixar
um pull request em [Link]/rstudio-education/hopr .
O livro foi escrito em RMarkdown com bookdown .
Prefácio
Este livro lhe ensinará a programar em R. Você passará do carregamento de dados à
escrita de suas próprias funções (que superarão as funções de outros usuários de R).
Mas esta não é uma introdução típica ao R. Quero ajudá-lo a se tornar um cientista
de dados, além de um cientista da computação, portanto, este livro se concentrará
nas habilidades de programação mais relacionadas à ciência de dados.
Os capítulos do livro estão organizados de acordo com três projetos práticos –
considerando que são projetos bastante substanciais, eles abrangem vários
capítulos. Escolhi esses projetos por dois motivos. Primeiro, eles abrangem toda a
amplitude da linguagem R. Você aprenderá a carregar dados, montar e desmontar
objetos de dados, navegar pelo sistema de ambiente do R, escrever suas próprias
funções e usar todas as ferramentas de programação do R, como if elseinstruções,
laços for, classes S3, o sistema de pacotes do R e as ferramentas de depuração do R.
Os projetos também ensinarão como escrever código R vetorizado, um estilo de
código extremamente rápido que aproveita tudo o que o R faz de melhor.
Mas, mais importante, os projetos lhe ensinarão como resolver os problemas
logísticos da ciência de dados — e há muitos problemas logísticos. Ao trabalhar com
dados, você precisará armazenar, recuperar e manipular grandes conjuntos de
valores sem introduzir erros. À medida que você avança no livro, ensinarei não
apenas como programar em R, mas também como usar as habilidades de
programação para apoiar seu trabalho como cientista de dados.
Nem todo programador precisa ser um cientista de dados, então nem todo
programador achará este livro útil. Você achará este livro útil se se enquadra em
uma das seguintes categorias:
Você já usa R como uma ferramenta estatística, mas gostaria de aprender a
escrever suas próprias funções e simulações com R.
Você gostaria de aprender a programar e vê sentido em aprender uma
linguagem relacionada à ciência de dados.
Uma das maiores surpresas deste livro é que não abordo aplicações tradicionais do
R, como modelos e gráficos; em vez disso, trato o R puramente como uma
linguagem de programação. Por que esse foco tão restrito? O R foi projetado para
ser uma ferramenta que ajuda cientistas a analisar dados. Ele possui muitas funções
excelentes que criam gráficos e ajustam modelos aos dados. Como resultado,
muitos estatísticos aprendem a usar o R como se fosse um software — eles
aprendem quais funções fazem o que eles querem e ignoram o resto.
Esta é uma abordagem compreensível para aprender R. Visualizar e modelar dados
são habilidades complexas que exigem toda a atenção do cientista. Extrair insights
confiáveis de um conjunto de dados exige expertise, discernimento e foco. Eu não
recomendaria que nenhum cientista de dados se distraísse com programação de
computadores até se sentir confortável com a teoria básica e a prática de sua arte.
Se você deseja aprender a arte da ciência de dados, recomendo o livro "R para
Ciência de Dados" , meu volume complementar a este livro, coescrito com Hadley
Wickham.
No entanto, aprender a programar deveria estar na lista de tarefas de todo cientista
de dados. Saber programar tornará você um analista mais flexível e aumentará seu
domínio da ciência de dados em todos os sentidos. Minha metáfora favorita para
descrever isso foi apresentada por Greg Snow na lista de discussão de ajuda do R em
maio de 2006. Usar funções em R é como andar de ônibus. Escrever funções em R é
como dirigir um carro.
Ônibus são muito fáceis de usar: você só precisa saber qual ônibus pegar, onde embarcar e
onde desembarcar (e precisa pagar a passagem). Carros, por outro lado, exigem muito mais
trabalho: você precisa ter algum tipo de mapa ou instruções (mesmo que o mapa esteja na
sua cabeça), precisa abastecer de vez em quando e precisa conhecer as regras de trânsito
(ter algum tipo de carteira de motorista). A grande vantagem do carro é que ele pode levá-
lo a vários lugares que o ônibus não vai e é mais rápido para algumas viagens que exigiriam
baldeação entre ônibus.
Usando essa analogia, programas como o SPSS são barramentos, fáceis de usar para coisas
padrão, mas muito frustrantes se você quiser fazer algo que ainda não esteja pré-
programado.
R é um SUV com tração nas quatro rodas (embora ecologicamente correto) com uma
bicicleta na traseira, um caiaque em cima, bons tênis para caminhada e corrida no banco do
passageiro e equipamento para escalada e exploração de cavernas na parte de trás.
O R pode levá-lo a qualquer lugar que você queira ir se você dedicar um tempo para
aprender a usar o equipamento, mas isso levará mais tempo do que aprender onde ficam os
pontos de ônibus no SPSS. - Greg Snow

Greg compara R ao SPSS, mas ele assume que você usa todos os poderes do R; em
outras palavras, que você aprende a programar em R. Se você usa apenas funções
que já existem em R, você está usando R como o SPSS: é um ônibus que só pode
levá-lo a certos lugares.
Essa flexibilidade é importante para cientistas de dados. Os detalhes exatos de um
método ou simulação variam de acordo com o problema. Se você não conseguir
criar um método adaptado à sua situação, poderá se sentir tentado a fazer
suposições irrealistas apenas para usar um método inadequado que já existe.
Este livro ajudará você a dar o salto do ônibus para o carro. Eu o escrevi para
programadores iniciantes. Não falo sobre a teoria da ciência da computação — não
há discussões sobre grandes O()e pequenas coisas o()nestas páginas. Também não
entro em detalhes avançados, como o funcionamento da avaliação preguiçosa .
Esses aspectos são interessantes se você pensar em ciência da computação no nível
teórico, mas são uma distração quando você aprende a programar pela primeira
vez.
Em vez disso, ensino como programar em R com três exemplos concretos. Esses
exemplos são curtos, fáceis de entender e abrangem tudo o que você precisa saber.
Já ensinei este material diversas vezes como Master Instructor na RStudio. Como
professor, descobri que os alunos aprendem conceitos abstratos muito mais rápido
quando ilustrados com exemplos concretos. Os exemplos também têm uma segunda
vantagem: proporcionam prática imediata. Aprender a programar é como aprender
a falar outro idioma — você progride mais rápido quando pratica. Na verdade,
aprender a programar é aprender a falar outro idioma. Você obterá os melhores
resultados se seguir os exemplos do livro e experimentar sempre que uma ideia
surgir.

O livro é um complemento ao R para Ciência de Dados . Nesse livro, Hadley


Wickham e eu explicamos como usar o R para criar gráficos, modelar dados e
escrever relatórios. O livro ensina essas tarefas como habilidades de ciência de
dados, que exigem julgamento e expertise — não como exercícios de programação,
que também são. Este livro lhe ensinará a programar em R. Ele não pressupõe que
você domine as habilidades de ciência de dados ensinadas em R para Ciência de
Dados (nem que você pretenda dominar). No entanto, este conjunto de habilidades
amplifica aquela. E se você dominar ambas, será um cientista de dados poderoso,
aumentado por computador, apto a receber um alto salário e influenciar o diálogo
científico.

0.1 Convenções usadas neste livro


As seguintes convenções tipográficas são usadas neste livro:
Itálico :: Indica novos termos, URLs, endereços de e-mail, nomes de arquivos e
extensões de arquivo.
Constant width:: Usado para listagens de programas, bem como dentro de
parágrafos para se referir a elementos do programa, como nomes de variáveis ou
funções, bancos de dados, tipos de dados, variáveis de ambiente, instruções e
palavras-chave.
Constant width bold:: Mostra comandos ou outro texto que deve ser digitado
literalmente pelo usuário.
Constant width italic:: Mostra o texto que deve ser substituído por valores
fornecidos pelo usuário ou por valores determinados pelo contexto.
Para comentar ou fazer perguntas técnicas sobre este livro, registre um problema
em [Link]/rstudio-education/hopr .

0.2 Agradecimentos
Muitas pessoas excelentes me ajudaram a escrever este livro, desde meus dois
editores, Courtney Nash e Julie Steele, até o restante da equipe da O'Reilly, que
projetou, revisou e indexou o livro. Além disso, Greg Snow generosamente me
permitiu citá-lo neste prefácio. A todos eles, meus sinceros agradecimentos.
Gostaria também de agradecer a Hadley Wickham, que moldou a maneira como
penso e ensino R. Muitas das ideias neste livro vêm do Statistics 405, um curso que
ajudei Hadley a ministrar quando eu era aluno de doutorado na Rice University.
Mais ideias vieram dos alunos e professores de Introdução à Ciência de Dados com
R, um workshop que ministro em nome do RStudio. Agradeço a todos. Gostaria de
agradecer especialmente aos meus assistentes de ensino Josh Paulson, Winston
Chang, Jaime Ramos, Jay Emerson e Vivian Zhang.
Agradeço também a JJ Allaire e aos meus demais colegas da RStudio que fornecem o
RStudio IDE, uma ferramenta que torna muito mais fácil usar, ensinar e escrever
sobre R.
Por fim, gostaria de agradecer à minha esposa, Kristin, por seu apoio e compreensão
enquanto eu escrevia este livro.

1 Projeto 1: Dados pesados


Os computadores permitem montar, manipular e visualizar conjuntos de dados,
tudo a velocidades que impressionariam os cientistas de ontem. Resumindo, os
computadores lhe dão superpoderes científicos! Mas se você quiser usá-los,
precisará adquirir algumas habilidades de programação.
Como um cientista de dados que sabe programar, você melhorará sua capacidade
de:
Memorizar (armazenar) conjuntos de dados inteiros
Recuperar valores de dados sob demanda
Execute cálculos complexos com grandes quantidades de dados
Realize tarefas repetitivas sem se tornar descuidado ou entediado
Os computadores podem fazer todas essas coisas rapidamente e sem erros, o
que permite que sua mente faça o que ela faz bem: tomar decisões e atribuir
significados.
Parece emocionante? ótimo! vamos começar.
Quando eu era estudante universitário, às vezes sonhava em ir para Las Vegas.
Achava que saber estatística poderia me ajudar a ganhar muito. Se foi isso
que te levou à ciência de dados, é melhor você se sentar; tenho más notícias. Até
um estatístico perde dinheiro em um cassino a longo prazo. Isso porque as
probabilidades de cada jogo estão sempre a favor do cassino. No entanto, há uma
brecha nessa regra. Você pode ganhar dinheiro – e de forma confiável também.
Tudo o que você precisa fazer é ser o cassino .
Acredite ou não, R pode te ajudar com isso. Ao longo do livro, você usará R para
construir três objetos virtuais: um par de dados que você pode rolar para gerar
números aleatórios, um baralho de cartas que você pode embaralhar e distribuir, e
uma máquina caça-níqueis modelada com base em terminais de loteria de vídeo da
vida real. Depois disso, você só precisará adicionar alguns gráficos de vídeo e uma
conta bancária (e talvez obter algumas licenças governamentais) e estará no
negócio. Deixo esses detalhes com você.
Esses projetos são leves, mas também profundos. Ao concluí-los, você se tornará um
especialista nas habilidades necessárias para trabalhar com dados como cientista de
dados. Você aprenderá como armazenar dados na memória do seu computador,
como acessar dados que já estão lá e como transformar valores de dados na
memória quando necessário. Você também aprenderá a escrever seus próprios
programas em R, que poderão ser usados para analisar dados e executar
simulações.
Se simular uma máquina caça-níqueis (ou dados, ou cartas) parece trivial, pense
desta forma: jogar em uma máquina caça-níqueis é um processo. Depois de
conseguir simular, você poderá simular outros processos, como amostragem
bootstrap, Monte Carlo de cadeias de Markov e outros procedimentos de análise de
dados. Além disso, esses projetos fornecem exemplos concretos para o aprendizado
de todos os componentes da programação em R: objetos, tipos de dados, classes,
notação, funções, ambientes, ifárvores, laços e vetorização. Este primeiro projeto
facilitará o estudo dessas coisas, ensinando os conceitos básicos de R.
Sua primeira missão é simples: montar um código R que simule o lançamento de um
par de dados, como em uma mesa de dados. Depois disso, vamos ponderar um
pouco os dados a seu favor, só para manter as coisas interessantes.
Neste projeto, você aprenderá como:
 Use as interfaces R e RStudio
 Executar comandos R
 Criar objetos R
 Escreva suas próprias funções e scripts R
 Carregar e usar pacotes R
 Gerar amostras aleatórias
 Crie gráficos rápidos
 Obtenha ajuda quando precisar
Não se preocupe se parecer que abordamos muito assunto rapidamente. Este
projeto foi criado para oferecer uma visão geral concisa da linguagem R. Você
retornará a muitos dos conceitos que encontramos aqui nos projetos 2 e 3, onde os
examinará em profundidade.
Você precisará ter o R e o RStudio instalados no seu computador antes de usá-los.
Ambos são gratuitos e fáceis de baixar. Consulte o Apêndice A para obter instruções
completas. Se estiver pronto para começar, abra o RStudio no seu computador e
continue lendo.

2 O básico
Este capítulo oferece uma visão geral da linguagem R que o ajudará a programar
imediatamente. Nele, você construirá um par de dados virtuais que poderá usar
para gerar números aleatórios. Não se preocupe se você nunca programou antes; o
capítulo lhe ensinará tudo o que você precisa saber.
Para simular um par de dados, você terá que destilar cada dado em suas
características essenciais. Você não pode colocar um objeto físico, como um dado,
em um computador (bem, não sem desparafusar alguns parafusos), mas pode
salvar informações sobre o objeto na memória do seu computador.
Quais informações você deve salvar? Em geral, um dado contém seis informações
importantes: quando você rola um dado, ele só pode resultar em um de seis
números: 1, 2, 3, 4, 5 e 6. Você pode capturar as características essenciais de um
dado salvando os números 1, 2, 3, 4, 5 e 6 como um grupo de valores na memória
do seu computador.
Vamos trabalhar primeiro para salvar esses números e depois considerar um
método para “rolar” nossos dados.
2.1 A interface do usuário R
Antes de pedir ao seu computador para salvar alguns números, você precisa saber
como se comunicar com ele. É aí que entram o R e o RStudio. O RStudio oferece
uma maneira de se comunicar com o seu computador. O R oferece uma linguagem
para você se comunicar. Para começar, abra o RStudio como faria com qualquer
outro aplicativo no seu computador. Ao fazer isso, uma janela deverá aparecer na
tela, como a mostrada na Figura 2.1 .

Figura 2.1: Seu computador obedece a seus comandos quando você digita comandos do R no prompt
na linha inferior do painel do console. Não se esqueça de pressionar a tecla Enter. Ao abrir o RStudio
pela primeira vez, o console aparece no painel à sua esquerda, mas você pode alterar isso em
Arquivo > Preferências na barra de menus.

Se você ainda não tem o R e o RStudio instalados no seu


computador — ou não sabe do que estou falando — visite o
Apêndice A. O apêndice lhe dará uma visão geral das duas
ferramentas gratuitas e lhe dirá como baixá-las.

A interface do RStudio é simples. Você digita o código R na linha inferior do painel


do console do RStudio e clica em Enter para executá-lo. O código digitado é
chamado de comando , pois ele comandará seu computador a fazer algo por você. A
linha em que você o digita é chamada de linha de comando .
Quando você digita um comando no prompt e pressiona Enter, o computador
executa o comando e mostra os resultados. Em seguida, o RStudio exibe um novo
prompt para o seu próximo comando. Por exemplo, se você digitar 1 + 1e pressionar
Enter, o RStudio exibirá:

>1+1
[1] 2
>

Você notará que um "a" [1]aparece ao lado do seu resultado. O R está apenas
informando que esta linha começa com o primeiro valor do seu resultado. Alguns
comandos retornam mais de um valor, e seus resultados podem preencher várias
linhas. Por exemplo, o comando 100:130retorna 31 valores; ele cria uma sequência
de inteiros de 100 a 130. Observe que novos números entre colchetes aparecem no
início da segunda e terceira linhas da saída. Esses números significam apenas que a
segunda linha começa com o 14º valor no resultado, e a terceira linha começa com o
25º valor. Você pode ignorar os números que aparecem entre colchetes:

> 100:130
[1] 100 101 102 103 104 105 106 107 108 109 110 111 112
[14] 113 114 115 116 117 118 119 120 121 122 123 124 125
[25] 126 127 128 129 130

O operador dois pontos ( :) retorna todos os números inteiros entre dois


números inteiros. É uma maneira fácil de criar uma sequência de números.

R não é uma linguagem?


Você pode me ouvir falando de R na terceira pessoa. Por
exemplo, eu poderia dizer: "Diga ao R para fazer isso" ou
"Diga ao R para fazer aquilo", mas é claro que R não pode
fazer nada; é apenas uma linguagem. Essa forma de falar é
uma abreviação de dizer: "Diga ao seu computador para fazer
isso escrevendo um comando na linguagem R na linha de comando do
seu console RStudio". Seu computador, e não o R, faz o trabalho de fato.
Esta abreviação é confusa e um pouco preguiçosa de usar? Sim. Muita
gente usa? Todo mundo que eu conheço — provavelmente porque é
muito conveniente.
Quando compilamos?
Em algumas linguagens, como C, Java e FORTRAN, você precisa compilar
seu código legível por humanos em código legível por máquinas
(geralmente 1s e 0s) antes de poder executá-lo. Se você já programou
em uma dessas linguagens, pode se perguntar se precisa compilar seu
código R antes de poder usá-lo. A resposta é não. R é uma linguagem de
programação dinâmica, o que significa que R interpreta seu código
automaticamente conforme você o executa.

Se você digitar um comando incompleto e pressionar Enter, o R exibirá um +prompt,


o que significa que ele está aguardando que você digite o restante do comando.
Conclua o comando ou pressione Escape para recomeçar:

>5-
+
+1
[1] 4

Se você digitar um comando que o R não reconhece, ele retornará uma mensagem
de erro. Se você vir uma mensagem de erro, não entre em pânico. O R está apenas
informando que seu computador não conseguiu entender ou executar o que você
pediu. Você pode então tentar um comando diferente no próximo prompt:
>3%5
Error: unexpected input in "3 % 5"
>

Depois de se acostumar com a linha de comando, você poderá facilmente fazer em


R qualquer coisa que faria com uma calculadora. Por exemplo, você pode fazer
algumas contas básicas:
2*3
## 6

4-1
## 3

6 / (4 - 1)
## 2

Você notou algo diferente neste código? Eu omiti os >"s" e [1]"s". Isso tornará o
código mais fácil de copiar e colar se você quiser colocá-lo no seu próprio console.
R trata o caractere de hashtag, #, de uma maneira especial; R não executará nada
que siga uma hashtag em uma linha. Isso torna as hashtags muito úteis para
adicionar comentários e anotações ao seu código. Humanos poderão ler os
comentários, mas seu computador os ignorará. A hashtag é conhecida como
o símbolo de comentário em R.
No restante do livro, usarei hashtags para exibir a saída do código R. Usarei uma
única hashtag para adicionar meus próprios comentários e uma hashtag dupla, ##,
para exibir os resultados do código. Evitarei mostrar >s e [1]s, a menos que queira
que você os veja.

Cancelando comandos
Alguns comandos do R podem demorar para serem
executados. Você pode cancelar um comando após ele ter
sido iniciado pressionando Ctrl + C. Observe que o R também
pode levar muito tempo para cancelar o comando.

Exercício 2.1 (Mágica com Números) Esta é a interface básica para executar código
R no RStudio. Acha que a domina? Se sim, tente executar estas tarefas simples. Se
você executar tudo corretamente, deverá terminar com o mesmo número inicial:
1. Escolha qualquer número e adicione 2 a ele.
2. Multiplique o resultado por 3.
3. Subtraia 6 da resposta.
4. Divida o resultado por 3.
Ao longo do livro, dividirei os exercícios em blocos, como o acima. Após cada
exercício, enviarei uma resposta modelo, como a abaixo.
Solução. Você pode começar com o número 10 e seguir os seguintes passos:
10 + 2
## 12
12 * 3
## 36
36 - 6
## 30
30 / 3
## 10

2.2 Objetos
Agora que você sabe como usar o R, vamos usá-lo para criar um dado virtual.
O :operador de algumas páginas atrás oferece uma maneira interessante de criar
um grupo de números de um a seis. O :operador retorna seus resultados como
um vetor , um conjunto unidimensional de números:
1:6
## 1 2 3 4 5 6

É só isso que um dado virtual parece! Mas você ainda não terminou. A
execução 1:6 gerou um vetor de números para você ver, mas não salvou esse vetor
em nenhum lugar da memória do seu computador. O que você está vendo são
basicamente as pegadas de seis números que existiram brevemente e depois
voltaram para a RAM do seu computador. Se quiser usar esses números novamente,
você terá que pedir ao seu computador para salvá-los em algum lugar. Você pode
fazer isso criando um objeto R.
O R permite salvar dados armazenando-os dentro de um objeto R. O que é
um objeto? Apenas um nome que você pode usar para acessar os dados
armazenados. Por exemplo, você pode salvar dados em um objeto como aou b.
Sempre que o R encontrar o objeto, ele o substituirá pelos dados salvos nele, assim:
a <- 1
a
## 1
a+2
## 3

O que acabou de acontecer?


1. Para criar um objeto R, escolha um nome e use o símbolo de menor
que, <, seguido por um sinal de menos, -, para salvar dados
nele. Essa combinação se parece com uma seta, <-. O R criará
um objeto, dará a ele seu nome e armazenará nele tudo o que
vier depois da seta. Portanto, a <- 1armazena 1em um objeto
chamado a.
2. Quando você pergunta a R o que há em a, R lhe diz na próxima linha.
3. Você também pode usar seu objeto em novos comandos do R. Como ao
valor de foi armazenado anteriormente 1, agora você está
adicionando 1a 2.

Então, para outro exemplo, o código a seguir criaria um objeto chamado die que contém
os números de um a seis. Para ver o que está armazenado em um objeto, basta digitar o
nome do objeto sozinho:

die <- 1:6


die
## 1 2 3 4 5 6

Ao criar um objeto, ele aparecerá no painel de ambiente do RStudio, conforme


mostrado na Figura 2.2 . Este painel mostrará todos os objetos que você criou desde
a abertura do RStudio.

Figura 2.2: O painel de ambiente do RStudio monitora os objetos R que você cria
Você pode nomear um objeto em R praticamente como quiser, mas existem
algumas regras. Primeiro, um nome não pode começar com um número. Segundo,
um nome não pode usar alguns símbolos especiais, como ^, !, $, @, +, -, /, ou *:

Bons nomes Nomes que causam erros

um 1º teste

b $

FOO ^significa

minha_var 2º

.dia !ruim

Capitalização
R diferencia maiúsculas de minúsculas, então name e Name se
referirá a objetos diferentes:
Name <- 1
name <- 0
Name + 1
## 2

Por fim, o R sobrescreverá qualquer informação armazenada anteriormente em um


objeto sem pedir sua permissão. Portanto, é uma boa ideia não usar nomes que já
estejam em uso:
my_number <- 1
my_number
## 1
my_number <- 999
my_number
## 999

Você pode ver quais nomes de objetos você já usou com a função ls:
ls()
## "a" "die" "my_number" "name" "Name"

Você também pode ver quais nomes usou examinando o painel de ambiente do
RStudio.
Agora você tem um dado virtual armazenado na memória do seu computador. Você
pode acessá-lo sempre que quiser digitando a palavra die. Então, o que você pode
fazer com este dado? Bastante coisa. O R substituirá um objeto pelo seu conteúdo
sempre que o nome do objeto aparecer em um comando. Assim, por exemplo, você
pode fazer todo tipo de cálculo com o dado. A matemática não é tão útil para rolar
dados, mas manipular conjuntos de números será sua especialidade como cientista
de dados. Então, vamos ver como fazer isso:
die - 1
## 0 1 2 3 4 5

die / 2
## 0.5 1.0 1.5 2.0 2.5 3.0

die * die
## 1 4 9 16 25 36

Se você é um grande fã de álgebra linear (e quem não é?), pode notar que R nem
sempre segue as regras de multiplicação de matrizes. Em vez disso, R usa a
execução elemento a elemento . Quando você manipula um conjunto de números, R
aplicará a mesma operação a cada elemento do conjunto. Assim, por exemplo,
quando você executa die - 1, R subtrai um de cada elemento de die.
Ao usar dois ou mais vetores em uma operação, o R alinha os vetores e executa uma
sequência de operações individuais. Por exemplo, ao executar die * die, o R alinha
os dois dievetores e multiplica o primeiro elemento do vetor 1 pelo primeiro
elemento do vetor 2. O R então multiplica o segundo elemento do vetor 1 pelo
segundo elemento do vetor 2, e assim por diante, até que todos os elementos
tenham sido multiplicados. O resultado será um novo vetor com o mesmo
comprimento dos dois primeiros, conforme mostrado na Figura 2.3 .

Figura 2.3: Quando o R executa


elementos, ele combina vetores e manipula cada par de elementos independentemente.

Se você der a R dois vetores de comprimentos desiguais, R repetirá o vetor menor


até que ele tenha o mesmo comprimento do vetor maior e, em seguida, fará os
cálculos, como mostrado na Figura 2.4 . Esta não é uma mudança permanente – o
vetor menor retornará ao seu tamanho original após R fazer os cálculos. Se o
comprimento do vetor menor não for dividido igualmente pelo comprimento do
vetor maior, R retornará uma mensagem de aviso. Esse comportamento é conhecido
como reciclagem de vetores e ajuda R a realizar operações elementares:
1:2
## 1 2
1:4
## 1 2 3 4
die
## 1 2 3 4 5 6
die + 1:2
## 2 4 4 6 6 8
die + 1:4
## 2 4 6 8 6 8
Warning message:
In die + 1:4 :
longer object length is not a multiple of shorter object length

Figura 2.4: R repetirá um vetor curto para fazer operações elemento a elemento com dois vetores de
comprimentos desiguais.

Operações elemento a elemento são um recurso muito útil em R, pois manipulam


grupos de valores de forma ordenada. Ao começar a trabalhar com conjuntos de
dados, as operações elemento a elemento garantem que os valores de uma
observação ou caso sejam pareados apenas com valores da mesma observação ou
caso. As operações elemento a elemento também facilitam a escrita de seus
próprios programas e funções em R.
Mas não pense que R desistiu da multiplicação tradicional de matrizes. Você só
precisa pedir quando quiser. Você pode fazer multiplicação interna com o %*%
operador e multiplicação externa com o %o% operador:
die %*% die
## 91
die %o% die
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 1 2 3 4 5 6
## [2,] 2 4 6 8 10 12
## [3,] 3 6 9 12 15 18
## [4,] 4 8 12 16 20 24
## [5,] 5 10 15 20 25 30
## [6,] 6 12 18 24 30 36

Você também pode fazer coisas como transpor uma matriz com t e obter seu
determinante com det.
Não se preocupe se você não estiver familiarizado com essas operações. Elas são
fáceis de consultar e você não precisará delas para este livro.
Agora que você já sabe fazer contas com seu dieobjeto, vamos ver como você
poderia "lançá-lo". Rolar o dado exigirá algo mais sofisticado do que aritmética
básica; você precisará selecionar aleatoriamente um dos valores do dado. E para
isso, você precisará de uma função .

2.3 Funções
O R vem com muitas funções que você pode usar para realizar tarefas sofisticadas,
como amostragem aleatória. Por exemplo, você pode arredondar um número com
a round função ou calcular seu fatorial com a factorial função. Usar uma função é
bem simples. Basta escrever o nome da função e, em seguida, os dados sobre os
quais você deseja que a função opere entre parênteses:
round(3.1415)
## 3
factorial(3)
## 6

Os dados que você passa para a função são chamados de argumento da função . O
argumento pode ser um dado bruto, um objeto R ou até mesmo os resultados de
outra função R. Neste último caso, o R trabalhará da função mais interna para a
mais externa, como na Figura 2.5
mean(1:6)
## 3.5
mean(die)
## 3.5
round(mean(die))
## 4
Figura 2.5: Ao vincular funções, o R as resolverá da operação mais interna para a mais externa. Aqui,
o R primeiro consulta o dado, depois calcula a média de um a seis e, em seguida, arredonda a média.

Para nossa sorte, existe uma função em R que pode ajudar a "rolar" o dado. Você
pode simular um lançamento de dado com sample a função em R. sample
recebe dois argumentos: um vetor chamado x e um número
chamado size. Sample retornará size elementos do vetor:
sample(x = 1:4, size = 2)
## 3 2

Para rolar o dado e obter um número, defina x e die selecione um elemento dele.
Você obterá um número novo (talvez diferente) a cada vez que rolar o dado:
sample(x = die, size = 1)
## 2
sample(x = die, size = 1)
## 1
sample(x = die, size = 1)
## 6

Muitas funções R aceitam múltiplos argumentos que as ajudam a realizar seu


trabalho. Você pode atribuir a uma função quantos argumentos quiser, desde que
separe cada argumento com uma vírgula.
Você deve ter notado que eu defini die e 1 igual aos nomes dos argumentos
em sample, x e size. Cada argumento em cada função R tem um nome. Você
pode especificar quais dados devem ser atribuídos a qual argumento definindo um
nome igual a data, como no código anterior. Isso se torna importante à medida que
você começa a passar vários argumentos para a mesma função; nomes ajudam a
evitar passar os dados errados para o argumento errado. No entanto, o uso de
nomes é opcional. Você notará que os usuários de R não costumam usar o nome do
primeiro argumento em uma função. Portanto, você pode ver o código anterior
escrito como:
sample(die, size = 1)
## 2

Muitas vezes, o nome do primeiro argumento não é muito descritivo e, de qualquer


forma, costuma ser óbvio a que o primeiro dado se refere.
Mas como saber quais nomes de argumentos usar? Se você tentar usar um nome
que a função não espera, provavelmente receberá um erro:
round(3.1415, corners = 2)
## Error in round(3.1415, corners = 2) : unused argument(s)
(corners = 2)

Se não tiver certeza de quais nomes usar com uma função, você pode consultar os
argumentos da função com args. Para isso, coloque o nome da função entre
parênteses depois de args. Por exemplo, você pode ver que a round função
recebe dois argumentos, um chamado x e outro chamado digits:
args(round)
## function (x, digits = 0)
## NULL

Você notou que isso args mostra que o digit sargumento de round já está
definido como 0? Frequentemente, uma função R aceita argumentos opcionais
como digits. Esses argumentos são considerados opcionais porque vêm com um
valor padrão. Você pode passar um novo valor para um argumento opcional, se
desejar, e o R usará o valor padrão caso contrário. Por exemplo, round arredondará
seu número para 0 dígitos após a vírgula decimal por padrão. Para substituir o
padrão, forneça seu próprio valor para digits:
round(3.1415)
## 3
round(3.1415, digits = 2)
## 3.14
Você deve escrever os nomes de cada argumento após o primeiro ou os dois
primeiros ao chamar uma função com múltiplos argumentos. Por quê? Primeiro, isso
ajudará você e outros a entenderem seu código. Geralmente é óbvio a qual
argumento sua primeira entrada se refere (e às vezes também à segunda entrada).
No entanto, você precisaria de muita memória para lembrar o terceiro e o quarto
argumentos de cada função R. Segundo, e mais importante, escrever os nomes dos
argumentos evita erros.
Se você não escrever os nomes dos seus argumentos, o R corresponderá seus
valores aos argumentos da sua função por ordem. Por exemplo, no código a seguir,
o primeiro valor, die, será correspondido ao primeiro argumento de sample, que
é chamado x. O próximo valor, 1, será correspondido ao próximo argumento, size:
sample(die, 1)
## 2

À medida que você fornece mais argumentos, torna-se mais provável que sua ordem
e a ordem de R não se alinhem. Como resultado, valores podem ser passados para o
argumento errado. Nomes de argumentos evitam isso. R sempre corresponderá um
valor ao nome do seu argumento, independentemente de onde ele apareça na
ordem dos argumentos:
sample(size = 1, x = die)
## 2

2.3.1 Amostra com Substituição


Se você definir size = 2, poderá simular quase um par de dados. Antes de
executarmos esse código, pense por um minuto por que isso pode ser
verdade. sampleretornará dois números, um para cada dado:
sample(die, size = 2)
## 3 4

Eu disse que esse "quase" funciona porque esse método tem um efeito estranho. Se
você usá-lo muitas vezes, notará que o segundo dado nunca tem o mesmo valor que
o primeiro, o que significa que você nunca tirará algo como um par de três ou olhos
de cobra. O que está acontecendo?
Por padrão, sample cria uma amostra sem reposição . Para ver o que isso significa,
imagine que sample coloca todos os valores de die em um jarro ou urna. Então
imagine que sample alcança o jarro e retira os valores um por um para construir
sua amostra. Uma vez que um valor foi retirado do jarro, sample o coloca de lado.
O valor não retorna para o jarro, então ele não pode ser retirado novamente. Então,
se sample seleciona um seis em seu primeiro sorteio, ele não será capaz de
selecionar um seis no segundo sorteio; seis não está mais no jarro para ser
selecionado. Embora sample crie sua amostra eletronicamente, ele segue esse
comportamento aparentemente físico.
Um efeito colateral desse comportamento é que cada compra depende das compras
anteriores. No mundo real, porém, quando você rola um par de dados, cada dado é
independente do outro. Se o primeiro dado sair seis, isso não impede que o segundo
dado saia seis. Na verdade, isso não influencia o segundo dado de forma alguma.
Você pode recriar esse comportamento sample adicionando o
argumento replace = TRUE:
sample(die, size = 2, replace = TRUE)
## 5 5

O argumento replace = TRUE faz com que sample a amostragem seja


feita com substituição . Nosso exemplo de jar fornece uma boa maneira de entender
a diferença entre amostragem com e sem substituição. Quando sample usa
substituição, ele extrai um valor do jar e o registra. Em seguida, ele o coloca de volta
no jar. Em outras palavras, sample substitui cada valor após cada extração. Como
resultado, sample pode selecionar o mesmo valor na segunda extração. Cada valor
tem uma chance de ser selecionado a cada vez. É como se cada extração fosse a
primeira.
A amostragem com substituição é uma maneira fácil de criar amostras aleatórias
independentes . Cada valor na sua amostra será uma amostra de tamanho um,
independente dos outros valores. Esta é a maneira correta de simular um par de
dados:
sample(die, size = 2, replace = TRUE)
## 2 4

Parabéns! Você acabou de executar sua primeira simulação em R! Agora você tem
um método para simular o resultado do lançamento de um par de dados. Se quiser
somar os dados, pode inserir o resultado diretamente na sum função:
dice <- sample(die, size = 2, replace = TRUE)
dice
## 2 4
sum(dice)
## 6

O que aconteceria se você chamasse dice várias vezes? O R geraria um novo par de
valores de dados a cada vez? Vamos tentar:
dice
## 2 4
dice
## 2 4
dice
## 2 4

Não. Cada vez que você chamar dice, o R mostrará o resultado daquela única vez
que você chamou sample e salvou a saída em dice. O R não executará
novamente sample(die, 2, replace = TRUE) para criar um novo lançamento
de dados. Isso é um alívio, de certa forma. Depois que você salva um conjunto de
resultados em um objeto R, esses resultados não mudam. Programar seria bem
difícil se os valores dos seus objetos mudassem cada vez que você os chamasse.
No entanto, seria conveniente ter um objeto que pudesse rolar os dados novamente
sempre que você o chamasse. Você pode criar um objeto assim escrevendo sua
própria função R.

2.4 Escrevendo suas próprias funções


Para recapitular, você já tem um código R funcional que simula o lançamento de um
par de dados:
die <- 1:6
dice <- sample(die, size = 2, replace = TRUE)
sum(dice)

Você pode digitar este código novamente no console sempre que quiser rolar seus
dados novamente. No entanto, esta é uma maneira estranha de trabalhar com o
código. Seria mais fácil usar seu código se você o encapsulasse em uma função
própria, que é exatamente o que faremos agora. Vamos escrever uma função
chamada roll que você pode usar para rolar seus dados virtuais. Quando terminar, a
função funcionará assim: cada vez que você chamar roll(), o R retornará a soma dos
lançamentos de dois dados:
roll()
## 8
roll()
## 3
roll()
## 7

Funções podem parecer misteriosas ou sofisticadas, mas são apenas mais um tipo
de objeto R. Em vez de conter dados, elas contêm código. Esse código é armazenado
em um formato especial que facilita sua reutilização em novas situações. Você pode
escrever suas próprias funções recriando esse formato.

2.4.1 O Construtor de Função


Cada função em R tem três partes básicas: um nome, um corpo de código e um
conjunto de argumentos. Para criar sua própria função, você precisa replicar essas
partes e armazená-las em um objeto R, o que pode ser feito com a function
função. Para fazer isso, chame-a function() e coloque um par de chaves depois
dela {}:

my_function <- function() {}

function criará uma função a partir de qualquer código R que você colocar entre
chaves. Por exemplo, você pode transformar seu código de dado em uma função
chamando:
roll <- function() {
die <- 1:6
dice <- sample(die, size = 2, replace = TRUE)
sum(dice)
}

Observe que indentei cada linha de código entre chaves. Isso facilita a
leitura para você e para mim, mas não afeta a execução do código. O R
ignora espaços e quebras de linha e executa uma expressão completa de
cada vez.

Basta pressionar a tecla Enter entre cada linha após a primeira chave, { . O R
esperará que você digite a última chave, }, antes de responder.
Não se esqueça de salvar a saída de function nem um objeto R. Este objeto se
tornará sua nova função. Para usá-lo, escreva o nome do objeto seguido por um
parênteses de abertura e fechamento:
roll()
## 9
Você pode pensar nos parênteses como o "gatilho" que faz com que o R execute a
função. Se você digitar o nome de uma função sem os parênteses, o R mostrará o
código armazenado nela. Se você digitar o nome com os parênteses, o R executará
esse código:
roll
## function() {
## die <- 1:6
## dice <- sample(die, size = 2, replace = TRUE)
## sum(dice)
## }
roll()
## 6

O código que você insere na sua função é conhecido como o corpo da função.
Quando você executa uma função em R, o R executa todo o código do corpo e
retorna o resultado da última linha de código. Se a última linha de código não
retornar um valor, sua função também não retornará, então você precisa garantir
que a linha final de código retorne um valor. Uma maneira de verificar isso é pensar
no que aconteceria se você executasse o corpo do código linha por linha na linha de
comando. O R exibiria um resultado após a última linha ou não?

Aqui está um código que exibiria um resultado:


dice
1+1
sqrt(2)

E aqui está um código que não faria isso:


dice <- sample(die, size = 2, replace = TRUE)
two <- 1 + 1
a <- sqrt(2)

Você percebeu o padrão? Estas linhas de código não retornam um valor para a linha
de comando; elas salvam um valor em um objeto.

2.5 Argumentos
E se removêssemos uma linha de código da nossa função e alterássemos o
nome die para bones, assim?
roll2 <- function() {
dice <- sample(bones, size = 2, replace = TRUE)
sum(dice)
}
Agora, recebo um erro ao executar a função. A função precisa do objeto bones
para realizar sua função, mas não há nenhum objeto nomeado bones para ser
encontrado:
roll2()
## Error in sample(bones, size = 2, replace = TRUE) :
## object 'bones' not found

Você pode fornecer bonesquando chamar, roll2 se criar bones um argumento da


função. Para fazer isso, coloque o nome bones entre parênteses function após
definir roll2:
roll2 <- function(bones) {
dice <- sample(bones, size = 2, replace = TRUE)
sum(dice)
}
Agora roll2 funcionará, desde que você forneça _ bones ao chamar a função. Você
pode aproveitar isso para rolar diferentes tipos de dados cada vez que
chamar roll2. Dungeons and Dragons, aí vamos nós!

Lembre-se, estamos lançando pares de dados:


roll2(bones = 1:4)
## 3
roll2(bones = 1:6)
## 10
roll2(1:20)
## 31

Observe que roll2 ainda ocorrerá um erro se você não fornecer um valor para
o bonesargumento ao chamar roll2:
roll2()
## Error in sample(bones, size = 2, replace = TRUE) :
## argument "bones" is missing, with no default

Você pode evitar esse erro atribuindo bones um valor padrão ao argumento. Para
isso, defina bones e qual como um valor ao definir roll2:
roll2 <- function(bones = 1:6) {
dice <- sample(bones, size = 2, replace = TRUE)
sum(dice)
}

Agora você pode fornecer um novo valor bones se quiser e roll2 usar o padrão se
não quiser:
roll2()
## 9
Você pode dar às suas funções quantos argumentos desejar. Basta listar os nomes,
separados por vírgulas, entre parênteses function. Quando a função for executada,
o R substituirá cada nome de argumento no corpo da função pelo valor fornecido
pelo usuário para o argumento. Se o usuário não fornecer um valor, o R substituirá o
nome do argumento pelo valor padrão do argumento (se você tiver definido um).
Resumindo, function ajuda você a construir suas próprias funções R. Você cria um
corpo de código para sua função executar, escrevendo o código entre as chaves que
seguem function. Você cria argumentos para sua função usar, fornecendo seus
nomes entre parênteses que seguem function. Por fim, você dá um nome à sua
função salvando sua saída em um objeto R, como mostrado na Figura 2.6 .
Depois de criar sua função, o R a tratará como qualquer outra função em R. Pense
em como isso é útil. Você já tentou criar uma nova opção no Excel e adicioná-la à
barra de menus da Microsoft? Ou uma nova animação de slide e adicioná-la às
opções do PowerPoint? Ao trabalhar com uma linguagem de programação, você
pode fazer esse tipo de coisa. À medida que aprende a programar em R, você
poderá criar ferramentas novas, personalizadas e reproduzíveis para si mesmo
sempre que quiser. O Projeto 3: Caça-níqueis lhe ensinará muito mais sobre como
escrever funções em R.

Figura 2.6: Cada função em R tem as mesmas partes, e você pode usar function para criá-las. Atribua
um nome ao resultado para poder chamar a função posteriormente.

2.6 Scripts
E se você quiser editar roll2 novamente? Você poderia voltar e digitar novamente
cada linha de código em roll2, mas seria muito mais fácil se você tivesse um
rascunho do código para começar. Você pode criar um rascunho do seu código à
medida que avança usando um script R. Um script R é apenas um arquivo de texto
simples no qual você salva o código R. Você pode abrir um script R no RStudio
acessando File > New File > R script na barra de menus. O RStudio então
abrirá um novo script acima do painel do console, como mostrado na Figura 2.7 .
Recomendo fortemente que você escreva e edite todo o seu código R em um script
antes de executá-lo no console. Por quê? Esse hábito cria um registro reproduzível
do seu trabalho. Ao terminar o dia, você pode salvar seu script e usá-lo para
executar toda a sua análise novamente no dia seguinte. Scripts também são muito
úteis para editar e revisar seu código, além de criarem uma cópia do seu trabalho
para compartilhar com outras pessoas. Para salvar um script, clique no painel de
scripts e, em seguida, vá para File > Save As na barra de menus.

Figura 2.7: Quando você abre um script R (Arquivo > Novo arquivo > Script R na barra de menu), o
RStudio cria um quarto painel acima do console, onde você pode escrever e editar seu código.

O RStudio vem com muitos recursos integrados que facilitam o trabalho com scripts.
Primeiro, você pode executar automaticamente uma linha de código em um script
clicando no botão Executar, como mostrado na Figura 2.8 .
O R executará qualquer linha de código sobre a qual o cursor estiver. Se você tiver
uma seção inteira destacada, o R executará o código destacado. Como alternativa,
você pode executar o script inteiro clicando no botão Código-fonte. Não gosta de
clicar em botões? Você pode usar Control + Return como atalho para o botão
Executar. Em Macs, seria Command + Return.
Figura 2.8: Você pode executar uma parte destacada do código do seu script clicando no botão
Executar na parte superior do painel de scripts.

Você pode executar o script inteiro clicando no botão Código-fonte. Se você não
está convencido sobre scripts, logo ficará. Torna-se um incômodo escrever código
multilinha na linha de comando de linha única do console. Vamos evitar essa dor de
cabeça e abrir seu primeiro script agora, antes de passarmos para o próximo
capítulo.

Função de extração
O RStudio vem com uma ferramenta que pode ajudar você a criar
funções. Para usá-la, destaque as linhas de código no seu script R que
você deseja transformar em uma função. Em seguida, clique Code >
Extract Function na barra de menu. O RStudio solicitará um nome de
função para usar e, em seguida, encapsulará seu código em
uma function chamada. Ele varrerá o código em busca de variáveis
indefinidas e as usará como argumentos.
Talvez seja interessante verificar novamente o trabalho do RStudio. Ele
pressupõe que seu código esteja correto, então, se algo inesperado
acontecer, pode haver um problema no seu código.

2.7 Resumo
Você já percorreu um longo caminho. Agora você tem um dado virtual armazenado
na memória do seu computador, bem como sua própria função R que rola um par de
dados. Você também começou a falar a linguagem R.
Como você viu, R é uma linguagem que você pode usar para se comunicar com seu
computador. Você escreve comandos em R e os executa na linha de comando para o
seu computador ler. Às vezes, seu computador responde – por exemplo, quando
você comete um erro – mas geralmente ele apenas faz o que você pede e exibe o
resultado.
Os dois componentes mais importantes da linguagem R são objetos, que
armazenam dados, e funções, que manipulam dados. R também usa uma série de
operadores como +, -, *, /e <-para realizar tarefas básicas. Como cientista de dados,
você usará objetos R para armazenar dados na memória do seu computador e
funções para automatizar tarefas e fazer cálculos complexos. Examinaremos os
objetos com mais detalhes posteriormente no Projeto 2: Cartas de Baralho e nos
aprofundaremos nas funções no Projeto 3: Caça-Níqueis . O vocabulário que você
desenvolveu aqui tornará cada um desses projetos mais fácil de entender. No
entanto, ainda não terminamos com seus dados.
Em Pacotes e páginas de ajuda , você executará algumas simulações em seus dados
e criará seus primeiros gráficos em R. Você também verá dois dos componentes
mais úteis da linguagem R: pacotes R , que são coleções de funções escritas pela
talentosa comunidade de desenvolvedores de R, e documentação R, que é uma
coleção de páginas de ajuda incorporadas ao R que explicam cada função e conjunto
de dados na linguagem.

3 Pacotes e Páginas de Ajuda


Agora você tem uma função que simula o lançamento de um par de dados. Vamos
tornar as coisas um pouco mais interessantes, ponderando os dados a seu favor. A
casa sempre ganha, certo? Vamos fazer com que os dados rolem números altos com
um pouco mais de frequência do que os números baixos.
Antes de pesarmos os dados, devemos garantir que eles sejam justos desde o início.
Duas ferramentas ajudarão você a fazer isso: repetição e visualização . Por
coincidência, essas ferramentas também são dois dos superpoderes mais úteis no
mundo da ciência de dados.
Repetiremos nossos lançamentos de dados com uma função chamada replicate e
visualizaremos nossos lançamentos com uma função chamada qplot. qplot não
vem com o R quando você o baixa; qplot vem em um pacote R independente.
Muitas das ferramentas R mais úteis vêm em pacotes R, então vamos dedicar um
momento para ver o que são pacotes R e como você pode usá-los.

3.1 Pacotes
Você não é a única pessoa que escreve suas próprias funções com R. Muitos
professores, programadores e estatísticos usam R para projetar ferramentas que
podem ajudar as pessoas a analisar dados. Eles então disponibilizam essas
ferramentas gratuitamente para qualquer pessoa. Para usá-las, basta baixá-las. Elas
vêm como coleções pré-montadas de funções e objetos chamadas pacotes. O
Apêndice 2: Pacotes R contém instruções detalhadas para baixar e atualizar pacotes
R, mas abordaremos o básico aqui.
Usaremos a qplot função para criar alguns gráficos rápidos. qplot Ela está incluída
no pacote ggplot2 , um pacote popular para criação de gráficos. Antes de
usar qplot, ou qualquer outro recurso do pacote ggplot2, você precisa baixá-lo e
instalá-lo.

3.1.1 [Link]
Cada pacote R está hospedado em [Link] , o mesmo site que
hospeda o R. No entanto, você não precisa visitar o site para baixar um pacote R;
você pode baixar pacotes diretamente da linha de comando do R. Veja como:
 Abra o RStudio.
 Certifique-se de que você esteja conectado à Internet.
 Execute [Link]("ggplot2") na linha de comando.

Pronto. O R fará com que seu computador visite o site, baixe o ggplot2 e instale o
pacote no seu disco rígido exatamente onde o R deseja encontrá-lo. Agora você tem
o pacote ggplot2. Se quiser instalar outro pacote, substitua ggplot2 pelo nome do
seu pacote no código.

3.1.2 Biblioteca
Instalar um pacote não coloca suas funções ao seu alcance ainda: simplesmente as
coloca no seu disco rígido. Para usar um pacote R, você precisa carregá-lo na sua
sessão R com o comando library("ggplot2"). Se quiser carregar um pacote
diferente, substitua ggplot2 pelo nome do seu pacote no código.
Para ver o que isso faz, faça um experimento. Primeiro, peça ao R para mostrar
a qplot função. O R não conseguirá encontrar qplot porque qplot ela está no
pacote ggplot2, que você não carregou:
qplot
## Error: object 'qplot' not found
Agora carregue o pacote ggplot2:
library("ggplot2")
Se você instalou o pacote [Link] conforme as instruções, tudo deve
correr bem. Não se preocupe se não vir nenhum resultado ou mensagem. A
ausência de notícias é uma boa notícia ao carregar um pacote. Não se preocupe se
vir uma mensagem também; o ggplot2 às vezes exibe mensagens úteis de
inicialização. Contanto que você não veja nada que diga "Erro", está tudo bem.
Agora, se você pedir para ver qplot, R mostrará um pouco de código ( qplot é uma
função longa):
qplot
## (quite a bit of code)
O Apêndice 2: Pacotes do R contém muito mais detalhes sobre a aquisição e o uso
de pacotes. Recomendo a leitura caso você não esteja familiarizado com o sistema
de pacotes do R. O principal a lembrar é que você só precisa instalar um pacote uma
vez, mas precisa carregá-lo library sempre que desejar usá-lo em uma nova sessão
do R. O R descarregará todos os seus pacotes sempre que você fechar o RStudio.

Agora que você carregou qplot, vamos dar uma olhada. qplotcria "gráficos
rápidos". Se você fornecer qplot dois vetores de comprimentos iguais, qplot
desenhará um gráfico de dispersão para você. qplot Usará o primeiro vetor como
um conjunto de valores x e o segundo vetor como um conjunto de valores y.
Observe que o gráfico aparecerá na aba Gráficos do painel inferior direito da janela
do RStudio.
O código a seguir criará o gráfico que aparece na Figura 3.1 . Até agora, criamos
sequências de números com o : operador ; mas você também pode criar vetores de
números com a c função . Forneça c todos os números que você deseja que
apareçam no vetor, separados por vírgula. c significa concatenar , mas você pode
pensar nisso como "coletar" ou "combinar":
x <- c(-1, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1)
x
## -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 1.0
y <- x^3
y
## -1.000 -0.512 -0.216 -0.064 -0.008 0.000 0.008
## 0.064 0.216 0.512 1.000
qplot(x, y)
Figura 3.1: qplot cria um gráfico de dispersão quando você fornece dois vetores.

Você não precisa nomear seus vetores x e y. Eu só fiz isso para deixar o exemplo
mais claro. Como você pode ver na Figura 3.1 , um gráfico de dispersão é um
conjunto de pontos, cada um plotado de acordo com seus valores x e y. Juntos, os
vetores xe ydescrevem um conjunto de 10 pontos. Como o R combinou os valores
em x e y para formar esses pontos? Com execução elemento a elemento, como
vimos na Figura 2.3 .
Gráficos de dispersão são úteis para visualizar a relação entre duas variáveis. No
entanto, usaremos um tipo diferente de gráfico: um histograma . Um histograma
visualiza a distribuição de uma única variável; ele exibe quantos pontos de dados
aparecem em cada valor de x.
Vamos dar uma olhada em um histograma para ver se isso faz sentido. qplot criará
um histograma sempre que você atribuir apenas um vetor para plotar. O código a
seguir cria o gráfico à esquerda na Figura 3.2 (cuidaremos do gráfico à direita em
breve). Para garantir que nossos gráficos tenham a mesma aparência, use o
argumento extra binwidth = 1:
x <- c(1, 2, 2, 2, 3, 3)
qplot(x, binwidth = 1)
Figura 3.2: qplot cria um histograma quando você atribui a ele um único vetor.

Este gráfico mostra que nosso vetor contém um valor no intervalo, [1, 2)
colocando uma barra de altura 1 acima desse intervalo. Da mesma forma, o gráfico
mostra que o vetor contém três valores no intervalo, [2, 3) colocando uma barra
de altura 3 nesse intervalo. Ele mostra que o vetor contém dois valores no
intervalo, [3, 4) colocando uma barra de altura 2 nesse intervalo. Nesses
intervalos, o colchete rígido, [, significa que o primeiro número está incluído no
intervalo. O parêntese, ), significa que o último número não está incluído.
Vamos tentar outro histograma. Este código cria o gráfico à direita na Figura 3.2 .
Observe que há cinco pontos com valor 1 em x2. O histograma exibe isso plotando
uma barra de altura 5 acima do intervalo x² = [1, 2):
x2 <- c(1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4)
qplot(x2, binwidth = 1)

Exercício 3.1 (Visualizar um histograma) Seja x3 o seguinte vetor:


x3 <- c(0, 1, 1, 2, 2, 2, 3, 3, 4)
Imagine como seria um histograma de x3. Suponha que o histograma tenha uma
largura de compartimento de 1. Quantas barras o histograma terá? Onde elas
aparecerão? Qual será a altura de cada uma?
Quando terminar, trace um histograma de x3 com binwidth = 1 e veja se você
está certo.
Solução. Você pode criar um histograma de x3 com qplot(x3, binwidth = 1). O
histograma se parecerá com uma pirâmide simétrica. A barra do meio terá uma
altura de 3 e aparecerá acima de[2, 3], mas não deixe de experimentar e ver por si
mesmo.
Você pode usar um histograma para mostrar visualmente a frequência de diferentes
valores de x. Números cobertos por uma barra alta são mais comuns do que
números cobertos por uma barra curta.
Como você pode usar um histograma para verificar a precisão dos seus dados?
Bem, se você rolar seus dados muitas vezes e acompanhar os resultados, esperaria
que alguns números ocorressem mais do que outros. Isso ocorre porque há mais
maneiras de obter alguns números somando dois dados do que de obter outros
números, como mostrado na Figura 3.3 .
Se você rolar os dados várias vezes e plotar os resultados com qplot, o histograma
mostrará a frequência com que cada soma ocorreu. As somas que ocorreram com
mais frequência terão as barras mais altas. O histograma deve se parecer com o
padrão da Figura 3.3 se os dados estiverem ponderados de forma justa.
É aqui que replicate entra. replicate fornece uma maneira fácil de repetir um
comando R várias vezes. Para usá-lo, primeiro informe replicate o número de
vezes que você deseja repetir um comando R e, em seguida, informe o comando que
deseja repetir. replicate executará o comando várias vezes e armazenará os
resultados como um vetor:
replicate(3, 1 + 1)
## 2 2 2
replicate(10, roll())
## 3 7 5 3 6 2 3 8 11 7

Figura 3.3: Cada combinação individual de dados deve ocorrer com a mesma frequência. Como
resultado, algumas somas ocorrerão com mais frequência do que outras. Em dados honestos, cada
soma deve aparecer em proporção ao número de combinações que a formam.

Um histograma dos seus primeiros 10 lançamentos provavelmente não se parecerá


com o padrão mostrado na Figura 3.3 . Por que não? Há muita aleatoriedade
envolvida. Lembre-se de que usamos dados na vida real porque eles são geradores
eficazes de números aleatórios. Padrões de frequências de longo prazo só
aparecerão a longo prazo . Então, vamos simular 10.000 lançamentos de dados e
plotar os resultados. Não se preocupe; qplot e replicate você pode lidar com
isso. Os resultados aparecem na Figura 3.4 :
rolls <- replicate(10000, roll())
qplot(rolls, binwidth = 1)
Os resultados sugerem que os dados são honestos. A longo prazo, cada número
aparece em proporção ao número de combinações que o geram.
Agora, como você pode enviesar esses resultados? O padrão anterior ocorre porque
cada combinação subjacente de dados (por exemplo, (3, 4)) ocorre com a mesma
frequência. Se você pudesse aumentar a probabilidade de um 6 sair em qualquer
dado, qualquer combinação com um 6 ocorreria com mais frequência do que
qualquer combinação sem um 6. A combinação (6, 6) ocorreria com mais
frequência. Isso não fará com que os dados somem 12 com mais frequência do que
somam 7, mas distorcerá os resultados em direção aos números mais altos.

Figura 3.4: O comportamento dos nossos dados sugere que eles são honestos. O sete ocorre com
mais frequência do que qualquer outro número, e as frequências diminuem proporcionalmente ao
número de combinações de dados que criam cada número.

Em outras palavras, a probabilidade de tirar qualquer número em um dado honesto


é de 1/6. Gostaria que você alterasse a probabilidade para 1/8 para cada número
abaixo de seis e, em seguida, aumentasse a probabilidade de tirar um seis para 3/8:
Probabilidade
Número Probabilidade ponderada
justa

1 1/6 1/8

2 1/6 1/8

3 1/6 1/8

4 1/6 1/8

5 1/6 1/8

6 1/6 3/8

Você pode alterar as probabilidades adicionando um novo argumento à sample


função. Não vou dizer qual é o argumento; em vez disso, vou indicar a página de
ajuda da sample função. O que é isso? Funções em R vêm com páginas de ajuda?
Sim, vêm, então vamos aprender a ler uma.

3.2 Obtendo ajuda com páginas de ajuda


Existem mais de 1.000 funções no núcleo do R, e novas funções são criadas o tempo
todo. Isso pode ser muito material para memorizar e aprender! Felizmente, cada
função do R vem com sua própria página de ajuda, que você pode acessar digitando
o nome da função após um ponto de interrogação. Por exemplo, cada um desses
comandos abrirá uma página de ajuda. Procure as páginas que aparecem na aba
Ajuda do painel inferior direito do RStudio:
?sqrt
?log10
?sample

As páginas de ajuda contêm informações úteis sobre o que cada função faz. Essas
páginas de ajuda também servem como documentação de código, então lê-las pode
ser um pouco agridoce. Muitas vezes, parecem ter sido escritas para pessoas que já
entendem a função e não precisam de ajuda.
Não se incomode com isso — você pode tirar muito proveito de uma página de
ajuda examinando-a em busca de informações que façam sentido e ignorando o
restante. Essa técnica inevitavelmente o levará à parte mais útil de cada página de
ajuda: o final. Aqui, quase todas as páginas de ajuda incluem algum código de
exemplo que coloca a função em ação. Executar esse código é uma ótima maneira
de aprender com exemplos.
Se uma função vier em um pacote R, o R não conseguirá
encontrar sua página de ajuda a menos que o pacote seja
carregado.

3.2.1 Partes de uma página de ajuda


Cada página de ajuda é dividida em seções. As seções exibidas podem variar de
página para página, mas geralmente você encontrará estes tópicos úteis:
Descrição - Um breve resumo do que a função faz.
Uso - Um exemplo de como você digitaria a função. Cada argumento da função
aparecerá na ordem que o R espera que você o forneça (se você não usar nomes de
argumentos).
Argumentos - Uma lista de cada argumento que a função aceita, que tipo de
informação R espera que você forneça para o argumento e o que a função fará com
a informação.
Detalhes - Uma descrição mais aprofundada da função e de como ela opera. A seção
de detalhes também oferece ao autor da função a oportunidade de alertá-lo sobre
qualquer informação que você queira saber ao usá-la.
Valor - Uma descrição do que a função retorna quando você a executa.
Veja também - Uma pequena lista de funções R relacionadas.
Exemplos - Código de exemplo que usa a função e tem garantia de funcionamento.
A seção de exemplos de uma página de ajuda geralmente demonstra algumas
maneiras diferentes de usar uma função. Isso ajuda a dar uma ideia do que a função
é capaz.
Se você quiser consultar a página de ajuda de uma função, mas esqueceu o nome
dela, pode pesquisar por palavra-chave. Para isso, digite dois pontos de
interrogação seguidos de uma palavra-chave na linha de comando do R. O R exibirá
uma lista de links para páginas de ajuda relacionadas à palavra-chave. Você pode
pensar nisso como a página de ajuda da página de ajuda:
??log
Vamos dar uma olhada na sample página de ajuda do . Lembre-se: estamos
procurando por qualquer coisa que possa ajudar você a alterar as probabilidades
envolvidas no processo de amostragem. Não vou reproduzir a página de ajuda
inteira aqui (apenas as partes mais interessantes), então você deve acompanhar no
seu computador.
Primeiro, abra a página de ajuda. Ela aparecerá no mesmo painel do RStudio em que
seus gráficos estavam (mas na aba Ajuda, não na aba Gráficos):
?sample

O que você vê? Começando de cima:


Random Samples and Permutations
´
Description
sample takes a sample of the specified size from the elements of
x using
either with or without replacement.

Até aqui, tudo bem. Você sabia de tudo isso. A próxima seção, Uso, tem uma
possível pista. Ela menciona um argumento chamado prob:

Usage
sample(x, size, replace = FALSE, prob = NULL)

Se você rolar para baixo até a seção de argumentos, a descrição de +prob+


parece muito promissora:
A vector of probability weights for obtaining the elements of the
vector being
sampled.

A seção Detalhes confirma nossas suspeitas. Neste caso, ela também informa como
proceder:
The optional prob argument can be used to give a vector of weights
for obtaining
the elements of the vector being sampled. They need not sum to
one, but they
should be nonnegative and not all zero.

Embora a página de ajuda não mencione isso aqui, esses pesos serão comparados
aos elementos amostrados, elemento a elemento. O primeiro peso descreverá o
primeiro elemento, o segundo peso, o segundo elemento, e assim por diante. Essa é
uma prática comum em R.
Lendo em:
If replace is true, Walker's alias method (Ripley, 1987) is used...

Certo, parece que está na hora de começar a ler. Já devemos ter informações
suficientes para descobrir como ponderar nossos dados.

Exercício 3.2 (Rolar um par de dados) Reescreva a roll função abaixo para rolar um
par de dados ponderados:
roll <- function() {
die <- 1:6
dice <- sample(die, size = 2, replace = TRUE)
sum(dice)
}
Você precisará adicionar um prob argumento à sample função dentro de roll.
Este argumento deve indicar sample a amostragem dos números de um a cinco
com probabilidade de 1/8 e do número 6 com probabilidade de 3/8.
Quando terminar, continue lendo para ver um modelo de resposta.
Solução. Para ponderar seus dados, você precisa adicionar um prob argumento com
um vetor de pesos a sample, assim:
roll <- function() {
die <- 1:6
dice <- sample(die, size = 2, replace = TRUE,
prob = c(1/8, 1/8, 1/8, 1/8, 1/8, 3/8))
sum(dice)
}
Isso fará com roll que você escolha de 1 a 5 com probabilidade de 1/8 e 6 com
probabilidade de 3/8.
Sobrescreva sua versão anterior de roll pela nova função (executando o trecho de
código anterior na sua linha de comando). Em seguida, visualize o novo
comportamento de longo prazo do seu dado. Coloquei os resultados na
Figura 3.5 ao lado dos nossos resultados originais:
rolls <- replicate(10000, roll())
qplot(rolls, binwidth = 1)

Isso confirma que ponderamos os dados de forma eficaz. Números altos ocorrem
com muito mais frequência do que números baixos. O notável é que esse
comportamento só será aparente ao examinar frequências de longo prazo. Em
qualquer lançamento, os dados parecerão se comportar aleatoriamente. Isso é
ótimo se você joga Colonizadores de Catan (basta dizer aos seus amigos que você
perdeu os dados), mas deve ser preocupante se você analisar dados, pois significa
que esse viés pode facilmente ocorrer sem que ninguém perceba a curto prazo.

Figura 3.5: Os dados agora estão claramente tendenciosos a números altos, já que somas altas
ocorrem com muito mais frequência do que somas baixas.

3.2.2 Obtendo mais ajuda


O R também conta com uma comunidade superativa de usuários, à qual você pode
recorrer para obter ajuda na lista de discussão R-help . Você pode enviar e-mails
para a lista com perguntas, mas há uma grande chance de que sua pergunta já tenha
sido respondida. Descubra pesquisando nos arquivos .
Ainda melhor que a lista de ajuda do R é o Stack Overflow , um site que permite que
programadores respondam a perguntas e que usuários classifiquem as respostas
com base na utilidade. Pessoalmente, considero o formato do Stack Overflow mais
intuitivo do que a lista de e-mails do R-help (e os respondentes mais amigáveis).
Você pode enviar sua própria pergunta ou pesquisar nas perguntas já respondidas
do Stack Overflow relacionadas a R. São mais de 30.000.
O melhor de tudo é o [Link] , um lugar amigável e inclusivo para
compartilhar perguntas relacionadas ao R. [Link] é um fórum
muito ativo focado em R. Não se surpreenda se você fizer uma pergunta sobre um
pacote R e o autor do pacote aparecer para responder.
Para toda a lista de ajuda do R, Stack Overflow e [Link], você terá
mais chances de obter uma resposta útil se fornecer um exemplo reproduzível junto
com sua pergunta. Isso significa colar um pequeno trecho de código que os usuários
podem executar para chegar ao bug ou à pergunta que você tem em mente.

3.3 Resumo
Os pacotes e páginas de ajuda do R podem torná-lo um programador mais
produtivo. Você viu em "The Very Basics" que o R lhe dá o poder de escrever suas
próprias funções que fazem coisas específicas, mas frequentemente a função que
você deseja escrever já existe em um pacote do R. Professores, programadores e
cientistas desenvolveram mais de 13.000 pacotes para você usar, o que pode
economizar um tempo valioso de programação. Para usar um pacote, você precisa
instalá-lo no seu computador uma vez com [Link], e então carregá-lo
em cada nova sessão do R com library.
As páginas de ajuda do R ajudarão você a dominar as funções presentes no R e seus
pacotes. Cada função e conjunto de dados do R possui sua própria página de ajuda.
Embora as páginas de ajuda geralmente contenham conteúdo avançado, elas
também contêm dicas e exemplos valiosos que podem ajudar você a aprender a
usar uma função.
Agora você já viu o suficiente de R para aprender na prática, que é a melhor maneira
de aprender R. Você pode criar seus próprios comandos em R, executá-los e obter
ajuda quando precisar entender algo que eu não expliquei. Recomendo que você
experimente suas próprias ideias em R enquanto lê os próximos dois projetos.

3.4 Conclusão do Projeto 1


Você fez mais neste projeto do que permitir fraudes e jogos de azar; você também
aprendeu a falar com seu computador na linguagem de R. R é uma linguagem como
inglês, espanhol ou alemão, exceto que R ajuda você a falar com computadores, não
com humanos.
Você conheceu os substantivos da linguagem R: objetos. E espero que tenha
adivinhado que funções são os verbos (suponho que argumentos de função seriam
os advérbios). Ao combinar funções e objetos, você expressa um pensamento
completo. Ao encadear pensamentos em uma sequência lógica, você pode construir
declarações eloquentes, até mesmo artísticas. Nesse aspecto, R não é tão diferente
de qualquer outra linguagem.
R compartilha outra característica das linguagens humanas: você não se sentirá
muito confortável falando R até desenvolver um vocabulário de comandos em R
para usar. Felizmente, você não precisa ser tímido. Seu computador será o único a
"ouvir" você falando R. Seu computador não é muito compreensivo, mas também
não julga. Não que você precise se preocupar; você ampliará seu vocabulário em R
tremendamente entre agora e o final do livro.
Agora que você sabe usar R, é hora de se tornar um especialista em usá-lo para
fazer ciência de dados. A base da ciência de dados é a capacidade de armazenar
grandes quantidades de dados e recuperar valores sob demanda. A partir disso,
todo o resto se segue — manipulação de dados, visualização de dados, modelagem
de dados e muito mais. No entanto, você não pode armazenar facilmente um
conjunto de dados em sua mente memorizando-o. Nem pode armazenar facilmente
um conjunto de dados no papel escrevendo-o. A única maneira eficiente de
armazenar grandes quantidades de dados é com um computador. De fato, os
computadores são tão eficientes que seu desenvolvimento nas últimas três décadas
mudou completamente o tipo de dados que podemos acumular e os métodos que
podemos usar para analisá-los. Em suma, o armazenamento de dados em
computadores impulsionou a revolução na ciência que chamamos de ciência de
dados.
Projeto 2: Cartas de Baralho fará com que você faça parte dessa revolução ao lhe
ensinar como usar R para armazenar conjuntos de dados na memória do seu
computador e como recuperar e manipular dados quando eles estiverem lá.

4 Projeto 2: Cartas de Baralho


Este projeto – que abrange os próximos quatro capítulos – ensinará como
armazenar, recuperar e alterar valores de dados na memória do seu computador.
Essas habilidades ajudarão você a salvar e gerenciar dados sem acumular erros. No
projeto, você criará um baralho de cartas que poderá embaralhar e distribuir. O
melhor de tudo é que o baralho se lembrará de quais cartas foram distribuídas –
como um baralho de verdade. Você pode usar o baralho para jogar cartas, ler a
sorte e testar estratégias de contagem de cartas.
Ao longo do caminho, você aprenderá como:
 Salvar novos tipos de dados, como cadeias de caracteres e valores
lógicos
 Salvar um conjunto de dados como um vetor, matriz, array, lista ou
quadro de dados
 Carregue e salve seus próprios conjuntos de dados com R
 Extrair valores individuais de um conjunto de dados
 Alterar valores individuais dentro de um conjunto de dados
 Escreva testes lógicos
 Use o símbolo de valor ausente de R, NA

Para simplificar o projeto, dividi-o em quatro tarefas. Cada tarefa ensinará uma nova
habilidade para gerenciar dados com R:
Tarefa 1: construir o baralho - Em Objetos R , você projetará e construirá um
baralho virtual de cartas. Este será um conjunto de dados completo, assim como
aqueles que você usará como cientista de dados. Você precisará saber como usar os
tipos e estruturas de dados do R para que isso funcione.
Tarefa 2: escrever funções que distribuem e embaralham - Em seguida, na Notação
R , você escreverá duas funções para usar com o baralho. Uma função distribuirá as
cartas do baralho e a outra o embaralhará novamente. Para escrever essas funções,
você precisará saber como extrair valores de um conjunto de dados com R.
Tarefa 3: alterar o sistema de pontuação para se adequar ao seu jogo -
Em "Modificando Valores" , você usará o sistema de notação do R para alterar os
valores de pontuação das suas cartas para corresponder aos jogos de cartas que
você deseja jogar, como guerra, copas ou vinte-e-um. Isso ajudará você a alterar os
valores existentes nos conjuntos de dados existentes.
Tarefa 4: gerenciar o estado do baralho - Por fim, em Ambientes , você garantirá
que seu baralho se lembre de quais cartas foram distribuídas. Esta é uma tarefa
avançada e apresentará o sistema de ambientes e as regras de escopo do R. Para
realizá-la com sucesso, você precisará aprender os mínimos detalhes de como o R
consulta e utiliza os dados armazenados no seu computador.

5 Objetos R
Neste capítulo, você usará R para montar um baralho de 52 cartas. Você começará
construindo objetos R simples que representam cartas de baralho e, em seguida,
avançará para uma tabela de dados completa. Resumindo, você construirá o
equivalente a uma planilha do Excel do zero. Quando terminar, seu baralho de
cartas ficará mais ou menos assim:
face suit value
king spades 13
queen spades 12
jack spades 11
ten spades 10
nine spades 9
eight spades 8
...

Você precisa construir um conjunto de dados do zero para usá-lo em R? De jeito


nenhum. Você pode carregar a maioria dos conjuntos de dados em R com um único
passo simples; veja " Carregando Dados" . Mas este exercício lhe ensinará como o R
armazena dados e como você pode montar — ou desmontar — seus próprios
conjuntos de dados. Você também aprenderá sobre os vários tipos de objetos
disponíveis para uso em R (nem todos os objetos em R são iguais!). Considere este
exercício um rito de passagem; ao fazê-lo, você se tornará um especialista em
armazenamento de dados em R.
Começaremos com o básico. O tipo mais simples de objeto em R é um vetor
atômico . Vetores atômicos não são movidos a energia nuclear, mas são muito
simples e aparecem em todos os lugares. Se você observar com atenção, verá que a
maioria das estruturas em R são construídas a partir de vetores atômicos.

5.1 Vetores Atômicos


Um vetor atômico é apenas um vetor simples de dados. Na verdade, você já criou
um vetor atômico, seu die objeto do Projeto 1: Dados Ponderados . Você pode criar
um vetor atômico agrupando alguns valores de dados com c:
die <- c(1, 2, 3, 4, 5, 6)
die
## 1 2 3 4 5 6
[Link](die)
## TRUE

é.vetor
[Link] Testa se um objeto é um vetor atômico. Retorna TRUE se o
objeto é um vetor atômico e FALSE caso contrário.

Você também pode criar um vetor atômico com apenas um valor. O R salva valores
individuais como um vetor atômico de comprimento 1:
five <- 5
five
## 5
[Link](five)
## TRUE
length(five)
## 1
length(die)
## 6

comprimento
Length retorna o comprimento de um vetor atômico.

Cada vetor atômico armazena seus valores como um vetor unidimensional, e cada
vetor atômico pode armazenar apenas um tipo de dado. Você pode salvar diferentes
tipos de dados em R usando diferentes tipos de vetores atômicos. Ao todo, R
reconhece seis tipos básicos de vetores
atômicos: doubles , integers , characters , logicals , complex e raw .
Para criar seu baralho de cartas, você precisará usar diferentes tipos de vetores
atômicos para armazenar diferentes tipos de informação (texto e números). Você
pode fazer isso usando algumas convenções simples ao inserir seus dados. Por
exemplo, você pode criar um vetor de inteiros incluindo uma letra maiúscula L na
sua entrada. Você pode criar um vetor de caracteres colocando sua entrada entre
aspas:
int <- 1L
text <- "ace"
Cada tipo de vetor atômico tem sua própria convenção (descrita abaixo). O R
reconhecerá a convenção e a utilizará para criar um vetor atômico do tipo
apropriado. Se desejar criar vetores atômicos que contenham mais de um elemento,
você pode combinar um elemento com a c função em Pacotes e Páginas de Ajuda .
Use a mesma convenção com cada elemento:
int <- c(1L, 5L)
text <- c("ace", "hearts")

Você pode se perguntar por que o R usa vários tipos de vetores. Os tipos de vetores
ajudam o R a se comportar como esperado. Por exemplo, o R fará cálculos com
vetores atômicos que contêm números, mas não com vetores atômicos que contêm
cadeias de caracteres:
sum(int)
## 6
sum(text)
## Error in sum(text) : invalid 'type' (character) of argument

Mas estamos nos precipitando! Prepare-se para dar as boas-vindas aos seis tipos de
vetores atômicos em R.

5.1.1 Duplas
Um vetor double armazena números regulares. Os números podem ser positivos ou
negativos, grandes ou pequenos, e ter dígitos à direita da casa decimal ou não. Em
geral, o R salva qualquer número que você digitar como um double. Então, por
exemplo, o dado que você fez no Projeto 1: Dados Ponderados era um objeto
double:
die <- c(1, 2, 3, 4, 5, 6)
die
## 1 2 3 4 5 6

Geralmente, você sabe com que tipo de objeto está trabalhando em R (será óbvio),
mas também pode perguntar a R com que tipo de objeto um objeto está typeof.
Por exemplo:
typeof(die)
## "double"

Algumas funções do R se referem a doubles como "numéricos", e eu costumo fazer


o mesmo. Double é um termo da ciência da computação. Refere-se ao número
específico de bytes que seu computador usa para armazenar um número, mas acho
que "numérico" é muito mais intuitivo quando se faz ciência de dados.

5.1.2 Inteiros
Vetores inteiros armazenam números inteiros, que podem ser escritos sem um
componente decimal. Como cientista de dados, você não usará o tipo inteiro com
muita frequência, pois pode salvar inteiros como um objeto double.
Você pode criar um inteiro específico em R digitando um número seguido de uma
letra maiúscula L. Por exemplo:
int <- c(-1L, 2L, 4L)
int
## -1 2 4
typeof(int)
## "integer"

Observe que o R não salva um número como inteiro a menos que você inclua o L.
Números inteiros sem o L serão salvos como doubles. A única diferença entre 4
e 4L é como o R salva o número na memória do seu computador. Os inteiros são
definidos com mais precisão na memória do seu computador do que os doubles (a
menos que o inteiro seja muito grande ou pequeno).
Por que você salvaria seus dados como um inteiro em vez de um double? Às vezes,
uma diferença na precisão pode ter efeitos surpreendentes. Seu computador aloca
64 bits de memória para armazenar cada double em um programa R. Isso permite
muita precisão, mas alguns números não podem ser expressos exatamente em 64
bits, o equivalente a uma sequência de 64 uns e zeros. Por exemplo, o
número contém uma sequência infinita de dígitos à direita da casa decimal. Seu
computador precisa arredondar para algo próximo, mas não exatamente igual,
a para armazenar em sua memória. Muitos números decimais compartilham um
destino semelhante.
Como resultado, cada double tem uma precisão de cerca de 16 dígitos significativos.
Isso introduz um pequeno erro. Na maioria dos casos, esse erro de arredondamento
passa despercebido. No entanto, em algumas situações, o erro de arredondamento
pode causar resultados surpreendentes. Por exemplo, você pode esperar que o
resultado da expressão abaixo seja zero, mas não é:
sqrt(2)^2 - 2
## 4.440892e-16

A raiz quadrada de dois não pode ser expressa exatamente em 16 algarismos


significativos. Como resultado, R precisa arredondar a quantidade, e a expressão
resulta em algo muito próximo de — mas não exatamente — zero.
Esses erros são conhecidos como erros de ponto flutuante , e fazer cálculos nessas
condições é conhecido como aritmética de ponto flutuante . A aritmética de ponto
flutuante não é um recurso do R; é um recurso da programação de computadores.
Normalmente, erros de ponto flutuante não são suficientes para arruinar o seu dia.
Lembre-se apenas de que eles podem ser a causa de resultados surpreendentes.
Você pode evitar erros de ponto flutuante evitando decimais e usando apenas
números inteiros. No entanto, isso não é uma opção na maioria das situações de
ciência de dados. Não é possível fazer muita matemática com números inteiros
antes de precisar de um não inteiro para expressar o resultado. Felizmente, os erros
causados pela aritmética de ponto flutuante geralmente são insignificantes (e
quando não são, são fáceis de detectar). Como resultado, você geralmente usará
números duplos em vez de números inteiros como cientista de dados.

5.1.3 Personagens
Um vetor de caracteres armazena pequenos trechos de texto. Você pode criar um
vetor de caracteres em R digitando um caractere ou uma sequência de caracteres
entre aspas:
text <- c("Hello", "World")
text
## "Hello" "World"
typeof(text)
## "character"
typeof("Hello")
## "character"
Os elementos individuais de um vetor de caracteres são conhecidos como strings .
Observe que uma string pode conter mais do que apenas letras. Você também pode
montar uma string de caracteres a partir de números ou símbolos.

Exercício 5.1 (Caractere ou Número?) Você consegue identificar a diferença entre


uma sequência de caracteres e um número? Aqui vai um teste: quais destes são
sequências de caracteres e quais são números?1, " 1 ", " one ".
Solução. " 1 " e" one " são ambas cadeias de caracteres.
Cadeias de caracteres podem conter caracteres numéricos, mas isso não as torna
numéricas. São apenas cadeias de caracteres que, por acaso, contêm números. É
possível diferenciar cadeias de caracteres de números reais porque elas vêm entre
aspas. Na verdade, qualquer coisa entre aspas em R será tratada como uma cadeia
de caracteres — independentemente do que apareça entre aspas.
É fácil confundir objetos R com cadeias de caracteres. Por quê? Porque ambos
aparecem como trechos de texto no código R. Por exemplo, xse o nome de um
objeto R for "x", " x" é uma cadeia de caracteres que contém o caractere "x". Um é
um objeto que contém dados brutos, o outro é um trecho de dados brutos em si.
Espere um erro sempre que você esquecer suas aspas; R começará a procurar por
um objeto que provavelmente não existe.

5.1.4 Lógicos
Vetores lógicos armazenam TRUES e FALSES, a forma de dados booleanos de R.
Vetores lógicos são muito úteis para fazer coisas como comparações:
3>4
## FALSE

Sempre que você digitar TRUE ou FALSE em letras maiúsculas (sem aspas), o R
tratará sua entrada como dados lógicos. O R também assume que Te Fsão
abreviações para TRUE e FALSE, a menos que sejam definidos em outro lugar (por
exemplo, T <- 500). Como o significado de T e F pode mudar, é melhor
usar TRUE e FALSE:
logic <- c(TRUE, FALSE, TRUE)
logic
## TRUE FALSE TRUE
typeof(logic)
## "logical"
typeof(F)
## "logical"

5.1.5 Complexo e Bruto


Doubles, inteiros, caracteres e lógicos são os tipos mais comuns de vetores atômicos
em R, mas R também reconhece mais dois tipos: complexos e brutos. É improvável
que você os utilize para analisar dados, mas aqui estão eles para fins de
detalhamento.
Vetores complexos armazenam números complexos. Para criar um vetor complexo,
adicione um termo imaginário a um número com i:
comp <- c(1 + 1i, 1 + 2i, 1 + 3i)
comp
## 1+1i 1+2i 1+3i
typeof(comp)
## "complex"

Vetores brutos armazenam bytes brutos de dados. Criar vetores brutos é


complicado, mas você pode criar um vetor bruto vazio de
comprimento n com raw(n). Consulte a página de ajuda raw para obter mais
opções ao trabalhar com este tipo de dados:
raw(3)
## 00 00 00
typeof(raw(3))
## "raw"

Exercício 5.2 (Vetor de Cartas) Crie um vetor atômico que armazene apenas os
nomes das cartas em um royal flush, por exemplo, o ás de espadas, o rei de espadas,
a rainha de espadas, o valete de espadas e o dez de espadas. O nome da face do ás
de espadas seria "ás" e "espadas" seria o naipe.
Que tipo de vetor você usará para salvar os nomes?
Solução. Um vetor de caracteres é o tipo mais apropriado de vetor atômico para
salvar nomes de cartas. Você pode criar um com acfunção colocando cada nome
entre aspas:
hand <- c("ace", "king", "queen", "jack", "ten")
hand
## "ace" "king" "queen" "jack" "ten"
typeof(hand)
## "character"
Isso cria um grupo unidimensional de nomes de cartas — ótimo trabalho! Agora,
vamos criar uma estrutura de dados mais sofisticada, uma tabela bidimensional de
nomes e naipes de cartas. Você pode construir um objeto mais sofisticado a partir
de um vetor atômico, atribuindo a ele alguns atributos e uma classe.

5.2 Atributos
Um atributo é uma informação que você pode anexar a um vetor atômico (ou a
qualquer objeto R). O atributo não afetará nenhum dos valores do objeto e não
aparecerá quando você o exibir. Você pode pensar em um atributo como
"metadados"; é apenas um local conveniente para inserir informações associadas a
um objeto. O R normalmente ignora esses metadados, mas algumas funções R
verificam atributos específicos. Essas funções podem usar os atributos para fazer
coisas especiais com os dados.

Você pode ver quais atributos um objeto possui com attributes. atributes
retornará NULL se um objeto não tiver atributos. Um vetor atômico, como die,
não terá atributos a menos que você atribua alguns a ele:
attributes(die)
## NULL

NULO
O R usa NULLum objeto vazio para representar o conjunto
nulo. NULLGeralmente, é retornado por funções cujos
valores são indefinidos. Você pode criar um NULLobjeto
digitando NULLem letras maiúsculas.

5.2.1 Nomes
Os atributos mais comuns para atribuir a um vetor atômico são nomes, dimensões
(dim) e classes. Cada um desses atributos tem sua própria função auxiliar que você
pode usar para atribuir atributos a um objeto. Você também pode usar as funções
auxiliares para consultar o valor desses atributos para objetos que já os possuem.
Por exemplo, você pode consultar o valor do atributo "names" de die com names:
names(die)
## NULL

NULL significa que die não possui um atributo "names". Você pode atribuir um
a die atribuindo um vetor de caracteres à saída de names. O vetor deve incluir um
nome para cada elemento em die:
names(die) <- c("one", "two", "three", "four", "five", "six")

Agora die tem um atributo de nomes:


names(die)
## "one" "two" "three" "four" "five" "six"
attributes(die)
## $names
## [1] "one" "two" "three" "four" "five" "six"

R exibirá os nomes acima dos elementos die sempre que você olhar para o vetor:
die
## one two three four five six
## 1 2 3 4 5 6

Entretanto, os nomes não afetarão os valores reais do vetor, nem serão afetados
quando você manipular os valores do vetor:
die + 1
## one two three four five six
## 2 3 4 5 6 7

Você também pode usar names para alterar o atributo de nomes ou removê-lo
completamente. Para alterar os nomes, atribua um novo conjunto de rótulos
a names:
names(die) <- c("uno", "dos", "tres", "quatro", "cinco", "seis")
die
## uno dos tres quatro cinco seis
## 1 2 3 4 5 6

Para remover o atributo names, defina-o como NULL:


names(die) <- NULL
die
## 1 2 3 4 5 6

5.2.2 Escurecimento
Você pode transformar um vetor atômico em uma matriz n- dimensional atribuindo
a ele o atributo dimensions com dim. Para isso, defina o dim atributo como um
vetor numérico de comprimento n . R reorganizará os elementos do vetor
em n dimensões.
Cada dimensão terá tantas linhas (ou colunas, etc.) quanto o valor n-ésimo do dim
vetor. Por exemplo, você pode reorganizar die em uma matriz 2 × 3 (que tem 2
linhas e 3 colunas):
dim(die) <- c(2, 3)
die
## [,1] [,2] [,3]
## [1,] 1 3 5
## [2,] 2 4 6

ou uma matriz 3 × 2 (que tem 3 linhas e 2 colunas):


dim(die) <- c(3, 2)
die
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
ou um hipercubo 1 × 2 × 3 (que tem 1 linha, 2 colunas e 3 "fatias"). Esta é uma
estrutura tridimensional, mas o R precisará mostrá-la fatia por fatia na tela
bidimensional do seu computador:
dim(die) <- c(1, 2, 3)
die
## , , 1
##
## [,1] [,2]
## [1,] 1 2
##
## , , 2
##
## [,1] [,2]
## [1,] 3 4
##
## , , 3
##
## [,1] [,2]
## [1,] 5 6

O R sempre usará o primeiro valor dim para o número de linhas e o segundo valor
para o número de colunas. Em geral, as linhas sempre vêm primeiro em operações
do R que lidam com linhas e colunas.

Você pode notar que não tem muito controle sobre como o R reorganiza os valores
em linhas e colunas. Por exemplo, o R sempre preenche cada matriz por colunas, em
vez de linhas. Se quiser mais controle sobre esse processo, você pode usar uma das
funções auxiliares do R, matrix ou array. Elas fazem o mesmo que alterar o dim
atributo, mas fornecem argumentos extras para personalizar o processo.

5.3 Matrizes
Matrizes armazenam valores em uma matriz bidimensional, assim como uma matriz
de álgebra linear. Para criar uma, primeiro forneça matrix um vetor atômico para
reorganizar em uma matriz. Em seguida, defina quantas linhas devem estar na
matriz, definindo o nrow argumento como um número. Matrix organizará seu
vetor de valores em uma matriz com o número especificado de linhas. Como
alternativa, você pode definir o ncol argumento, que informa ao R quantas colunas
incluir na matriz:
m <- matrix(die, nrow = 2)
m
## [,1] [,2] [,3]
## [1,] 1 3 5
## [2,] 2 4 6

Matrix preencherá a matriz coluna por coluna por padrão, mas você pode
preencher a matriz linha por linha se incluir o argumento byrow = TRUE:
m <- matrix(die, nrow = 2, byrow = TRUE)
m
## [,1] [,2] [,3]
## [1,] 1 2 3
## [2,] 4 5 6

Matrix também possui outros argumentos padrão que você pode usar para
personalizar sua matriz. Você pode ler sobre eles na matrix página de ajuda do
(acessível por ?matrix).

5.4 Matrizes
A array função cria um array n-dimensional. Por exemplo, você pode usar array
para classificar valores em um cubo de três dimensões ou um hipercubo de 4, 5
ou n dimensões. array Não é tão personalizável quanto matrix e basicamente faz
a mesma coisa que definir o dim atributo. Para usar array, forneça um vetor
atômico como o primeiro argumento e um vetor de dimensões como o segundo
argumento, agora chamado dim:
ar <- array(c(11:14, 21:24, 31:34), dim = c(2, 2, 3))
ar
## , , 1
##
## [,1] [,2]
## [1,] 11 13
## [2,] 12 14
##
## , , 2
##
## [,1] [,2]
## [1,] 21 23
## [2,] 22 24
##
## , , 3
##
## [,1] [,2]
## [1,] 31 33
## [2,] 32 34

Exercício 5.3 (Criar uma matriz) Crie a seguinte matriz, que armazena o nome e o
naipe de cada carta em um royal flush.
## [,1] [,2]
## [1,] "ace" "spades"
## [2,] "king" "spades"
## [3,] "queen" "spades"
## [4,] "jack" "spades"
## [5,] "ten" "spades"

Solução. Há mais de uma maneira de construir esta matriz, mas em todos os casos,
você precisará começar criando um vetor de caracteres com 10 valores. Se você
começar com o seguinte vetor de caracteres, poderá transformá-lo em uma matriz
com qualquer um dos três comandos a seguir:
hand1 <- c("ace", "king", "queen", "jack", "ten", "spades", "spades",
"spades", "spades", "spades")
matrix(hand1, nrow = 5)
matrix(hand1, ncol = 2)
dim(hand1) <- c(5, 2)

Você também pode começar com um vetor de caracteres que lista as cartas em uma
ordem ligeiramente diferente. Nesse caso, você precisará pedir ao R para preencher
a matriz linha por linha, em vez de coluna por coluna:
hand2 <- c("ace", "spades", "king", "spades", "queen", "spades", "jack",
"spades", "ten", "spades")
matrix(hand2, nrow = 5, byrow = TRUE)
matrix(hand2, ncol = 2, byrow = TRUE)

Classe 5.5
Observe que alterar as dimensões do seu objeto não alterará o tipo do objeto,
mas alterará o atributo do objeto class:
dim(die) <- c(2, 3)
typeof(die)
## "double"
class(die)
## "matrix"
Uma matriz é um caso especial de um vetor atômico. Por exemplo, a diematriz é um
caso especial de um vetor duplo. Cada elemento na matriz ainda é um duplo, mas os
elementos foram organizados em uma nova estrutura. O R adicionou um class
atributo a diequando você alterou suas dimensões. Esta classe descreve dieo novo
formato de . Muitas funções do R procurarão especificamente o class atributo de
um objeto e, em seguida, o manipularão de uma maneira predeterminada com base
no atributo.
Observe que o atributo de um objeto class nem sempre aparecerá quando você
executar attributes; pode ser necessário procurá-lo especificamente com class:
attributes(die)
## $dim
## [1] 2 3
Você pode aplicar class a objetos que não possuem um class atributo. Class
retornará um valor baseado no tipo atômico do objeto. Observe que a "classe" de
um double é "numeric", um desvio estranho, mas pelo qual sou grato. Acredito que
a propriedade mais importante de um vetor double é que ele contém números, uma
propriedade que "numeric" torna óbvia:
class("Hello")
## "character"
class(5)
## "numeric"
Você também pode usar class para definir o atributo de um objeto class, mas isso
geralmente é uma má ideia. O R espera que objetos de uma classe compartilhem
certas características, como atributos, que o seu objeto pode não possuir. Você
aprenderá a criar e usar suas próprias classes no Projeto 3: Caça-níqueis .

5.5.1 Datas e Horários


O sistema de atributos permite que o R represente mais tipos de dados do que
apenas doubles, inteiros, caracteres, lógicos, complexos e brutos. O tempo parece
uma sequência de caracteres quando você o exibe, mas seu tipo de dado é, na
verdade "double", , e sua classe é "POSIXct" "POSIXt" (possui duas classes):
now <- [Link]()
now
## "2014-03-17 12:00:00 UTC"
typeof(now)
## "double"
class(now)
## "POSIXct" "POSIXt"

POSIXct é uma estrutura amplamente utilizada para representar datas e horas. Na


estrutura POSIXct, cada hora é representada pelo número de segundos decorridos
entre a hora e a meia-noite de 1º de janeiro de 1970 (no fuso horário UTC). Por
exemplo, a hora acima ocorre [Link] segundos depois. Portanto, no sistema
POSIXct, a hora seria salva como 1395057600.
O R cria o objeto de tempo construindo um vetor duplo com um
elemento, 1395057600. Você pode ver esse vetor removendo o classatributo
de now, ou usando a unclass função , que faz a mesma coisa:
unclass(now)
## 1395057600
O R então atribui ao vetor duplo um class atributo que contém duas
classes, "POSIXct"e "POSIXt". Este atributo alerta as funções R de que estão
lidando com um horário POSIXct, para que possam tratá-lo de maneira especial. Por
exemplo, as funções R usarão o padrão POSIXct para converter o horário em uma
string de caracteres amigável antes de exibi-lo.
Você pode tirar proveito desse sistema atribuindo a POSIXctclasse a objetos
aleatórios do R. Por exemplo, você já se perguntou que dia era um milhão de
segundos depois da meia-noite de 1º de janeiro de 1970?
mil <- 1000000
mil
## 1e+06
class(mil) <- c("POSIXct", "POSIXt")
mil
## "1970-01-12 13:46:40 UTC"

12 de janeiro de 1970. Nossa! Um milhão de segundos passa mais rápido do que


você imagina. Essa conversão funcionou bem porque a POSIXct classe não
depende de nenhum atributo adicional, mas, em geral, forçar a classe de um objeto
é uma má ideia.
Existem muitas classes diferentes de dados em R e seus pacotes, e novas classes são
inventadas todos os dias. Seria difícil aprender sobre todas as classes, mas você não
precisa. A maioria das classes só é útil em situações específicas. Como cada classe
vem com sua própria página de ajuda, você pode esperar para aprender sobre uma
classe até encontrá-la. No entanto, há uma classe de dados que é tão onipresente
em R que você deve aprender sobre ela juntamente com os tipos de dados
atômicos. Essa classe é factors.

5.5.2 Fatores
Fatores são a maneira do R armazenar informações categóricas, como etnia ou cor
dos olhos. Pense em um fator como algo como um gênero; ele só pode ter certos
valores (masculino ou feminino), e esses valores podem ter sua própria ordem
idiossincrática (primeiro as damas). Essa disposição torna os fatores muito úteis
para registrar os níveis de tratamento de um estudo e outras variáveis categóricas.
Para criar um fator, passe um vetor atômico para a factor função. O R recodificará
os dados do vetor como inteiros e armazenará os resultados em um vetor de
inteiros. O R também adicionará um levels atributo ao inteiro, que contém um
conjunto de rótulos para exibir os valores dos fatores, e um class atributo, que
contém a classe factor:
gender <- factor(c("male", "female", "female", "male"))
typeof(gender)
## "integer"
attributes(gender)
## $levels
## [1] "female" "male"
##
## $class
## [1] "factor"

Você pode ver exatamente como R está armazenando seu fator com unclass:
unclass(gender)
## [1] 2 1 1 2
## attr(,"levels")
## [1] "female" "male"

O R usa o atributo levels ao exibir o fator, como você verá. O R exibirá cada um 1
como female, o primeiro rótulo no vetor levels, e cada um 2 como male, o
segundo rótulo. Se o fator incluísse 3 s, eles seriam exibidos como o terceiro rótulo,
e assim por diante:
gender
## male female female male
## Levels: female male

Fatores facilitam a inserção de variáveis categóricas em um modelo estatístico, pois


as variáveis já estão codificadas como números. No entanto, fatores podem ser
confusos, pois parecem sequências de caracteres, mas se comportam como
números inteiros.
O R frequentemente tenta converter sequências de caracteres em fatores quando
você carrega e cria dados. Em geral, você terá uma experiência mais tranquila se
não deixar o R criar fatores até que você os solicite. Mostrarei como fazer isso
quando começarmos a ler os dados.
Você pode converter um fator em uma sequência de caracteres com
a [Link] função. O R manterá a versão de exibição do fator, não os inteiros
armazenados na memória:
[Link](gender)
## "male" "female" "female" "male"

Agora que você entende as possibilidades fornecidas pelos vetores atômicos de R,


vamos fazer um tipo mais complicado de carta de baralho.

Exercício 5.4 (Escreva uma carta) Muitos jogos de cartas atribuem um valor
numérico a cada carta. Por exemplo, no blackjack, cada carta figurada vale 10
pontos, cada carta numérica vale entre 2 e 10 pontos e cada ás vale 1 ou 11 pontos,
dependendo da pontuação final.
Crie uma carta de baralho virtual combinando "ás", "copas" e 1 em um vetor. Que
tipo de vetor atômico resultará? Verifique se você acertou.
Solução. Você pode ter imaginado que este exercício não daria certo. Cada vetor
atômico só pode armazenar um tipo de dado. Como resultado, o R converte todos
os seus valores para strings de caracteres:
card <- c("ace", "hearts", 1)
card
## "ace" "hearts" "1"

Isso causará problemas se você quiser fazer contas com esse valor de pontos, por
exemplo, para ver quem ganhou seu jogo de blackjack.

Tipos de dados em vetores


Se você tentar colocar vários tipos de dados em um vetor, R converterá
os elementos em um único tipo de dado.
Como matrizes e arranjos são casos especiais de vetores atômicos, eles
sofrem do mesmo comportamento. Cada um pode armazenar apenas um tipo de
Isso cria alguns problemas. Primeiro, muitos conjuntos de dados contêm vários tipos
de dados. Programas simples como Excel e Numbers podem salvar vários tipos de
dados no mesmo conjunto, e você deve torcer para que o R também consiga. Não se
preocupe, ele consegue.
Segundo, a coerção é um comportamento comum em R, então você vai querer saber
como ela funciona.

5.6 Coerção
O comportamento de coerção do R pode parecer inconveniente, mas não é
arbitrário. O R sempre segue as mesmas regras ao coagir tipos de dados. Depois de
se familiarizar com essas regras, você poderá usar o comportamento de coerção do
R para fazer coisas surpreendentemente úteis.
Então, como o R força os tipos de dados? Se uma sequência de caracteres estiver
presente em um vetor atômico, o R converterá todo o resto do vetor em sequências
de caracteres. Se um vetor contiver apenas lógicas e números, o R converterá as
lógicas em números; cada valor TRUEse torna 1 e cada valor FALSEse torna 0, como
mostrado na Figura 5.1 .

Figura 5.1: O R sempre usa as mesmas regras para converter dados para um único tipo. Se houver
cadeias de caracteres, tudo será convertido para uma cadeia de caracteres. Caso contrário, os
valores lógicos serão convertidos para numéricos.

Esse arranjo preserva informações. É fácil olhar para uma sequência de caracteres e
identificar quais informações ela continha. Por exemplo, você pode identificar
facilmente as origens de "TRUE"e "5". Você também pode facilmente transformar
de volta um vetor de 1s e 0s para TRUEs e FALSEs.
O R usa as mesmas regras de coerção quando você tenta fazer cálculos com valores
lógicos. Então, o código a seguir:
sum(c(TRUE, TRUE, FALSE, FALSE))

se tornará:
sum(c(1, 1, 0, 0))
## 2

Isso significa que sumcontará o número de TRUE s em um vetor lógico (e mean


calculará a proporção de TRUE s). Legal, né
Você pode pedir explicitamente ao R para converter dados de um tipo para outro
com as as funções. O R converterá os dados sempre que houver uma maneira
sensata de fazer isso:
[Link](1)
## "1"
[Link](1)
## TRUE
[Link](FALSE)
## 0

Agora você sabe como o R impõe coerções a tipos de dados, mas isso não vai te
ajudar a economizar dinheiro. Para isso, você precisará evitar completamente a
coerção. Você pode fazer isso usando um novo tipo de objeto, uma lista .
Antes de analisarmos as listas, vamos responder a uma pergunta que pode estar em
sua mente.
Muitos conjuntos de dados contêm múltiplos tipos de informação. A incapacidade
de vetores, matrizes e arrays de armazenar múltiplos tipos de dados parece ser uma
grande limitação. Então, por que se preocupar com eles?
Em alguns casos, usar apenas um único tipo de dado é uma grande vantagem.
Vetores, matrizes e arrays facilitam muito a execução de cálculos com grandes
conjuntos de números, pois o R sabe que pode manipular cada valor da mesma
maneira. Operações com vetores, matrizes e arrays também tendem a ser rápidas,
pois os objetos são muito fáceis de armazenar na memória.
Em outros casos, permitir apenas um único tipo de dado não é uma desvantagem.
Vetores são a estrutura de dados mais comum em R porque armazenam variáveis
muito bem. Cada valor em uma variável mede a mesma propriedade, portanto, não
há necessidade de usar tipos diferentes de dados.

5.7 Listas
Listas são como vetores atômicos porque agrupam dados em um conjunto
unidimensional. No entanto, listas não agrupam valores individuais; listas agrupam
objetos do R, como vetores atômicos e outras listas. Por exemplo, você pode criar
uma lista que contenha um vetor numérico de comprimento 31 em seu primeiro
elemento, um vetor de caracteres de comprimento 1 em seu segundo elemento e
uma nova lista de comprimento 2 em seu terceiro elemento. Para fazer isso, use
a list função .
List Cria uma lista da mesma forma cque cria um vetor. Separe cada elemento da
lista com uma vírgula:
list1 <- list(100:130, "R", list(TRUE, FALSE))
list1
## [[1]]
## [1] 100 101 102 103 104 105 106 107 108 109 110 111 112
## [14] 113 114 115 116 117 118 119 120 121 122 123 124 125
## [27] 126 127 128 129 130
##
## [[2]]
## [1] "R"
##
## [[3]]
## [[3]][[1]]
## [1] TRUE
##
## [[3]][[2]]
## [1] FALSE
Deixei a [1] notação na saída para que você possa ver como ela muda para listas. Os
índices entre colchetes duplos indicam qual elemento da lista está sendo exibido. Os
índices entre colchetes simples indicam qual subelemento de um elemento está
sendo exibido. Por exemplo, 100 é o primeiro subelemento do primeiro elemento
da lista. "R" é o primeiro subelemento do segundo elemento. Essa notação de dois
sistemas surge porque cada elemento de uma lista pode ser qualquer objeto R,
incluindo um novo vetor (ou lista) com seus próprios índices.
Listas são um tipo básico de objeto em R, assim como vetores atômicos. Assim como
vetores atômicos, elas são usadas como blocos de construção para criar muitos
tipos mais sofisticados de objetos em R.
Como você pode imaginar, a estrutura das listas pode se tornar bastante
complicada, mas essa flexibilidade faz das listas uma ferramenta de armazenamento
útil para todos os fins em R: você pode agrupar qualquer coisa com uma lista.
No entanto, nem toda lista precisa ser complicada. Você pode armazenar uma carta
de baralho em uma lista bem simples.

Exercício 5.5 (Use uma Lista para Criar uma Carta) Use uma lista para armazenar
uma única carta de baralho, como o ás de copas, que vale um ponto. A lista deve
armazenar a face da carta, o naipe e o valor em pontos em elementos separados.
Solução. Você pode criar seu cartão assim. No exemplo a seguir, o primeiro
elemento da lista é um vetor de caracteres (de comprimento 1). O segundo
elemento também é um vetor de caracteres, e o terceiro elemento é um vetor
numérico:
card <- list("ace", "hearts", 1)
card
## [[1]]
## [1] "ace"
##
## [[2]]
## [1] "hearts"
##
## [[3]]
## [1] 1

Você também pode usar uma lista para armazenar um baralho inteiro de cartas.
Como você pode salvar uma única carta como uma lista, você pode salvar um
baralho como uma lista de 52 sublistas (uma para cada carta). Mas não vamos nos
dar ao trabalho — há uma maneira muito mais simples de fazer a mesma coisa.
Você pode usar uma classe especial de lista, conhecida como dataframe .
5.8 Quadros de Dados
Os data frames são a versão bidimensional de uma lista. São, de longe, a estrutura
de armazenamento mais útil para análise de dados e oferecem uma maneira ideal
de armazenar um baralho inteiro de cartas. Você pode pensar em um data frame
como o equivalente em R à planilha do Excel, pois ele armazena dados em um
formato semelhante.
Os quadros de dados agrupam vetores em uma tabela bidimensional. Cada vetor se
torna uma coluna na tabela. Como resultado, cada coluna de um quadro de dados
pode conter um tipo diferente de dado; mas dentro de uma coluna, cada célula deve
ser do mesmo tipo de dado, como na Figura 5.2 .

Figura 5.2: Os quadros de dados armazenam dados como uma sequência de colunas. Cada coluna
pode ser de um tipo de dado diferente. Todas as colunas em um quadro de dados devem ter o
mesmo comprimento.

Criar um quadro de dados manualmente exige muita digitação, mas você pode fazer
isso (se quiser) com a [Link] função. Forneça [Link] qualquer número
de vetores, cada um separado por vírgula. Cada vetor deve ser definido como igual a
um nome que o descreva. [Link] transformará cada vetor em uma coluna do
novo quadro de dados:
df <- [Link](face = c("ace", "two", "six"),
suit = c("clubs", "clubs", "clubs"), value = c(1, 2, 3))
df
## face suit value
## ace clubs 1
## two clubs 2
## six clubs 3
Você precisará garantir que cada vetor tenha o mesmo comprimento (ou possa ter
com as regras de reciclagem do R; veja a Figura 2.4 , pois os quadros de dados não
podem combinar colunas de comprimentos diferentes).
No código anterior, nomeei os argumentos em [Link] face, suit,
e value, mas você pode nomeá-los como quiser. [Link] usará os nomes dos
seus argumentos para rotular as colunas do quadro de dados.

Nomes
Você também pode dar nomes a uma lista ou vetor ao criar
um desses objetos. Use a mesma sintaxe de [Link]:
list(face = "ace", suit = "hearts", value = 1)
c(face = "ace", suit = "hearts", value = "one")
Os nomes serão armazenados no namesatributo do objeto.

Se você observar o tipo de um dataframe, verá que se trata de uma lista. Na


verdade, cada dataframe é uma lista com classe [Link]. Você pode ver quais
tipos de objetos são agrupados por uma lista (ou dataframe) com a str função:
typeof(df)
## "list"
class(df)
## "[Link]"
str(df)
## '[Link]': 3 obs. of 3 variables:
## $ face : Factor w/ 3 levels "ace","six","two": 1 3 2
## $ suit : Factor w/ 1 level "clubs": 1 1 1
## $ value: num 1 2 3

Observe que o R salvou suas sequências de caracteres como fatores. Eu disse que o
R gosta de fatores! Não é um grande problema aqui, mas você pode evitar esse
comportamento adicionando o argumento stringsAsFactors = FALSEa [Link]:
df <- [Link](face = c("ace", "two", "six"),
suit = c("clubs", "clubs", "clubs"), value = c(1, 2, 3),
stringsAsFactors = FALSE)

Um dataframe é uma ótima maneira de construir um baralho completo. Você pode


transformar cada linha do dataframe em uma carta de baralho e cada coluna em um
tipo de valor — cada uma com seu próprio tipo de dado apropriado. O dataframe
ficaria mais ou menos assim:
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8
## seven spades 7
## six spades 6
## five spades 5
## four spades 4
## three spades 3
## two spades 2
## ace spades 1
## king clubs 13
## queen clubs 12
## jack clubs 11
## ten clubs 10
## . . . and so on .

Você poderia criar este quadro de dados com [Link], mas observe a digitação
envolvida! Você precisa escrever três vetores, cada um com 52 elementos:
deck <- [Link](
face = c("king", "queen", "jack", "ten", "nine", "eight", "seven", "six",
"five", "four", "three", "two", "ace", "king", "queen", "jack", "ten",
"nine", "eight", "seven", "six", "five", "four", "three", "two", "ace",
"king", "queen", "jack", "ten", "nine", "eight", "seven", "six", "five",
"four", "three", "two", "ace", "king", "queen", "jack", "ten", "nine",
"eight", "seven", "six", "five", "four", "three", "two", "ace"),
suit = c("spades", "spades", "spades", "spades", "spades", "spades",
"spades", "spades", "spades", "spades", "spades", "spades", "spades",
"clubs", "clubs", "clubs", "clubs", "clubs", "clubs", "clubs", "clubs",
"clubs", "clubs", "clubs", "clubs", "clubs", "diamonds", "diamonds",
"diamonds", "diamonds", "diamonds", "diamonds", "diamonds", "diamonds",
"diamonds", "diamonds", "diamonds", "diamonds", "diamonds", "hearts",
"hearts", "hearts", "hearts", "hearts", "hearts", "hearts", "hearts",
"hearts", "hearts", "hearts", "hearts", "hearts"),
value = c(13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 13, 12, 11,
10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
)
Evite digitar grandes conjuntos de dados manualmente sempre que possível. Digitar
pode levar a erros de digitação e outros erros, sem mencionar a LER. É sempre
melhor obter grandes conjuntos de dados como um arquivo de computador. Você
pode então pedir ao R para ler o arquivo e armazenar o conteúdo como um objeto.
Criei um arquivo para você carregar que contém um quadro de dados com
informações sobre cartas de baralho, então não se preocupe em digitar o código.
Em vez disso, concentre-se em carregar os dados em R.

5.9 Carregando dados


Você pode carregar o deckquadro de dados do arquivo [Link] . Reserve um
momento para baixar o arquivo antes de continuar lendo. Visite o site, clique em
"Baixar Zip", descompacte e abra a pasta baixada pelo seu navegador. O arquivo
[Link] estará lá.
[Link] é um arquivo de valores separados por vírgula, ou CSV, para abreviar. CSVs
são arquivos de texto simples, o que significa que você pode abri-los em um editor
de texto (assim como em muitos outros programas). Se você abrir [Link] , notará
que ele contém uma tabela de dados semelhante à tabela a seguir. Cada linha da
tabela é salva em sua própria linha, e uma vírgula é usada para separar as células
dentro de cada linha. Todo arquivo CSV compartilha este formato básico:

"face","suit,"value"
"king","spades",13
"queen","spades,12
"jack","spades,11
"ten","spades,10
"nine","spades,9
... and so on.

A maioria dos aplicativos de ciência de dados pode abrir arquivos de texto simples e
exportar dados como arquivos de texto simples. Isso torna os arquivos de texto
simples uma espécie de língua franca para a ciência de dados.
Para carregar um arquivo de texto simples no R, clique no ícone "Importar Conjunto
de Dados" no RStudio, mostrado na Figura 5.3 . Em seguida, selecione "Do arquivo
de texto".

Figura 5.3: Você pode importar dados de arquivos de texto simples com o Import Dataset
do RStudio.

O RStudio solicitará que você selecione o arquivo que deseja importar e, em


seguida, abrirá um assistente para ajudar na importação dos dados, como na
Figura 5.4 . Use o assistente para informar ao RStudio qual nome dar ao conjunto de
dados. Você também pode usar o assistente para informar ao RStudio qual caractere
o conjunto de dados usa como separador, qual caractere ele usa para representar
decimais (geralmente um ponto final nos Estados Unidos e uma vírgula na Europa) e
se o conjunto de dados vem ou não com uma linha de nomes de colunas (conhecida
como cabeçalho ). Para ajudar, o assistente mostra a aparência do arquivo bruto,
bem como a aparência dos dados carregados com base nas configurações de
entrada.
Você também pode desmarcar a caixa "Strings como fatores" no assistente.
Recomendo fazer isso. Se fizer isso, o R carregará todas as suas strings de caracteres
como strings de caracteres. Caso contrário, o R as converterá em fatores.

Figura 5.4: Assistente de importação do RStudio.

Quando tudo estiver correto, clique em Importar. O RStudio lerá os dados e os


salvará em um quadro de dados. O RStudio também abrirá um visualizador de
dados, para que você possa ver os novos dados em formato de planilha. Esta é uma
boa maneira de verificar se tudo ocorreu conforme o esperado. Se tudo funcionar
bem, seu arquivo deverá aparecer na aba Exibir do RStudio, como na Figura 5.5 .
Você pode examinar o quadro de dados no console com head(deck).
Dados on-line
Você pode carregar um arquivo de texto simples
diretamente da internet clicando na opção "Da URL da
Web..." em "Importar Conjunto de Dados". O arquivo
precisará ter sua própria URL, e você precisará estar conectado
Figura 5.5: Ao importar um conjunto de dados, o RStudio salva os dados em um quadro de dados e o
exibe em uma aba "Exibir". Você pode abrir qualquer quadro de dados em uma aba "Exibir" a
qualquer momento com a função "Exibir".

Agora é a sua vez. Baixe [Link] e importe-o para o RStudio. Certifique-se de salvar
a saída em um objeto R chamado deck: você o usará nos próximos capítulos. Se
tudo correr bem, as primeiras linhas do seu quadro de dados deverão ficar assim:

head(deck)
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8

Head e tail são duas funções que fornecem uma maneira


fácil de visualizar grandes conjuntos de dados. Head
retornará apenas as seis primeiras linhas do conjunto de
dados e tail retornará apenas as seis últimas linhas. Para ver
um número diferente de linhas, forneça head ou tails um
segundo argumento, o número de linhas que você gostaria de ver, por
exemplo, head(deck, 10).
O R pode abrir muitos tipos de arquivos — não apenas CSVs. Visite Carregar e Salvar
Dados em R para saber como abrir outros tipos comuns de arquivos em R.

5.10 Salvando dados


Antes de prosseguirmos, vamos salvar uma cópia de deck como um novo
arquivo .csv . Assim, você pode enviá-lo por e-mail para um colega, armazená-lo em
um pendrive ou abri-lo em outro programa. Você pode salvar qualquer quadro de
dados em R em um arquivo .csv com o comando [Link]. Para salvar deck,
execute:
[Link](deck, file = "[Link]", [Link] = FALSE)

O R transformará seu quadro de dados em um arquivo de texto simples com o


formato de valores separados por vírgula e salvará o arquivo no seu diretório de
trabalho. Para ver onde está seu diretório de trabalho, execute getwd(). Para
alterar a localização do seu diretório de trabalho, acesse Sessão > Definir Diretório
de Trabalho > Escolher Diretório na barra de menus do RStudio.
Você pode personalizar o processo de salvamento com [Link] amplo conjunto
de argumentos opcionais do (consulte ? [Link] para obter detalhes). No
entanto, há três argumentos que você deve usar sempre que executar [Link].
Primeiro, você deve fornecer [Link] o nome do quadro de dados que deseja
salvar. Em seguida, você deve fornecer um nome para o seu arquivo. O R
interpretará esse nome literalmente, portanto, certifique-se de fornecer uma
extensão.
Por fim, você deve adicionar o argumento [Link] = FALSE. Isso impedirá
que o R adicione uma coluna de números no início do seu quadro de dados. Esses
números identificarão suas linhas de 1 a 52, mas é improvável que qualquer
programa em que você abra [Link] entenda o sistema de nomes de linhas. Muito
provavelmente, o programa assumirá que os nomes das linhas são a primeira coluna
de dados no seu quadro de dados.
Na verdade, é exatamente isso que o R assumirá se você reabrir [Link] . Se você
salvar e abrir [Link] várias vezes em R, notará colunas duplicadas de números de
linhas se formando no início do seu quadro de dados. Não sei explicar por que o R
faz isso, mas posso explicar como evitá-lo: use [Link] = FALSE sempre que
salvar dados com [Link].
Para obter mais detalhes sobre como salvar arquivos, incluindo como compactar
arquivos salvos e como salvar arquivos em outros formatos, consulte Carregando e
salvando dados em R .
Bom trabalho. Agora você tem um baralho virtual para trabalhar. Descanse um
pouco e, quando voltar, começaremos a escrever algumas funções para usar no seu
baralho.

5.11 Resumo
Você pode salvar dados em R com cinco objetos diferentes, que permitem
armazenar diferentes tipos de valores em diferentes tipos de relacionamentos,
como na Figura 5.6 . Desses objetos, os data frames são de longe os mais úteis para
a ciência de dados. Os data frames armazenam um dos formatos de dados mais
comuns usados em ciência de dados: dados tabulares.

Figura 5.6: As estruturas de dados mais comuns do R são vetores, matrizes, arrays, listas e quadros
de dados.

Você pode carregar dados tabulares em um quadro de dados com o botão Importar
Conjunto de Dados do RStudio, desde que os dados sejam salvos como um arquivo
de texto simples. Este requisito não é tão limitante quanto parece. A maioria dos
programas de software pode exportar dados como um arquivo de texto simples.
Portanto, se você tiver um arquivo do Excel (por exemplo), poderá abri-lo no Excel e
exportar os dados como um CSV para usar com o R. Na verdade, abrir um arquivo
em seu programa original é uma boa prática. Arquivos do Excel usam metadados,
como planilhas e fórmulas, que ajudam o Excel a trabalhar com o arquivo.
O R pode tentar extrair dados brutos do arquivo, mas não será tão bom nisso
quanto o Microsoft Excel. Nenhum programa é melhor na conversão de arquivos do
Excel do que o Excel. Da mesma forma, nenhum programa é melhor na conversão de
arquivos SAS Xport do que o SAS, e assim por diante.
No entanto, você pode se deparar com um arquivo específico do programa, mas não
com o programa que o criou. Você não gostaria de comprar uma licença SAS
multimilionária só para abrir um arquivo SAS. Felizmente, o R pode abrir muitos
tipos de arquivos, incluindo arquivos de outros programas e bancos de dados. O R
ainda possui seus próprios formatos específicos de programa que podem ajudar a
economizar memória e tempo se você souber que trabalhará inteiramente em R. Se
quiser saber mais sobre todas as suas opções para carregar e salvar dados em R,
consulte Carregando e Salvando Dados em R.
A Notação R se baseará nas habilidades que você aprendeu neste capítulo. Aqui,
você aprendeu a armazenar dados em R. Na Notação R , você aprenderá a acessar
valores depois de armazenados. Você também escreverá duas funções que
permitirão que você comece a usar seu baralho: uma função de embaralhamento e
uma função de distribuição.

Notação 6 R
Agora que você tem um baralho, precisa de uma maneira de fazer coisas com ele.
Primeiro, você vai querer embaralhar o baralho de vez em quando. E depois, você
vai querer distribuir as cartas do baralho (uma carta de cada vez, a que estiver no
topo — não somos trapaceiros).
Para fazer isso, você precisará trabalhar com os valores individuais dentro do seu
quadro de dados, uma tarefa essencial para a ciência de dados. Por exemplo, para
distribuir uma carta do topo do seu baralho, você precisará escrever uma função
que selecione a primeira linha de valores no seu quadro de dados, como esta:
deal(deck)
## face suit value
## king spades 13

Você pode selecionar valores dentro de um objeto R com o sistema de notação do R.

6.1 Selecionando Valores


R possui um sistema de notação que permite extrair valores de objetos R. Para
extrair um valor ou conjunto de valores de um quadro de dados, escreva o nome do
quadro de dados seguido por um par de colchetes:
deck[ , ]

Entre os colchetes, haverá dois índices separados por uma vírgula. Os índices
informam ao R quais valores retornar. O R usará o primeiro índice para subdividir as
linhas do quadro de dados e o segundo índice para subdividir as colunas.
Você tem uma escolha quando se trata de escrever índices. Existem seis maneiras
diferentes de escrever um índice para R, e cada uma faz algo ligeiramente diferente.
Todas são muito simples e práticas, então vamos dar uma olhada em cada uma
delas. Você pode criar índices com:
 Números inteiros positivos
 Números inteiros negativos
 Zero
 Espaços em branco
 Valores lógicos
 Nomes
O mais simples de usar são os números inteiros positivos.

6.1.1 Inteiros Positivos


O R trata números inteiros positivos da mesma forma que a notação ij na álgebra
linear: deck[i, j] retornará o valor de deck que está na i-ésima linha e na j-ésima
coluna, Figura 6.1 . Observe que i e j só precisam ser números inteiros no sentido
matemático. Eles podem ser salvos como números em R.
head(deck)
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8
deck[1, 1]
## "king"
Para extrair mais de um valor, use um vetor de inteiros positivos. Por exemplo, você
pode retornar a primeira linha de deck with deck [1, c(1, 2, 3)] ou deck [1,
1:3]:
deck[1, c(1, 2, 3)]
## face suit value
## king spades 13

O R retornará os valores de deck que estão na primeira linha e na primeira,


segunda e terceira colunas. Observe que o R não removerá esses valores de deck.
O R fornecerá um novo conjunto de valores que são cópias dos valores originais.
Você pode então salvar esse novo conjunto em um objeto R com o operador de
atribuição do R:
new <- deck[1, c(1, 2, 3)]
new
## face suit value
## king spades 13

REPETIÇÃO
Se você repetir um número no seu índice, o R retornará o(s) valor(es)
correspondente(s) mais de uma vez no seu “subconjunto”. Este código
retornará a primeira linha de deck duas vezes:
deck[c(1, 1), c(1, 2, 3)]
## face suit value
## king spades 13
## king spades 13
Figura 6.1: R usa o sistema de notação ij da álgebra linear. Os comandos nesta figura retornarão os
valores sombreados

O sistema de notação do R não se limita a quadros de dados. Você pode usar a


mesma sintaxe para selecionar valores em qualquer objeto do R, desde que forneça
um índice para cada dimensão do objeto. Assim, por exemplo, você pode subdividir
um vetor (que tem uma dimensão) com um único índice:
vec <- c(6, 1, 3, 6, 10, 5)
vec[1:3]
## 6 1 3

A indexação começa em 1
Em algumas linguagens de programação, a indexação começa com 0. Isso
significa que 0 retorna o primeiro elemento de um vetor, 1 retorna o
segundo elemento e assim por diante.
Este não é o caso com R. A indexação em R se comporta exatamente
como a indexação em álgebra linear. O primeiro elemento é sempre
indexado por 1. Por que R é diferente? Talvez porque foi escrito para
matemáticos. Aqueles de nós que aprenderam indexação em um curso
de álgebra linear se perguntam por que os programadores de
computador começam com 0.

drop = FALSO
Se você selecionar duas ou mais colunas de um quadro de dados, R
retornará um novo quadro de dados:
deck[1:2, 1:2]
## face suit
## king spades
## queen spades
Entretanto, se você selecionar uma única coluna, R retornará um vetor:
deck[1:2, 1]
## "king" "queen"
Se preferir um quadro de dados, você pode adicionar o
argumento opcional drop = FALSE entre colchetes:
deck[1:2, 1, drop = FALSE]
## face
## king
## queen

Este método também funciona para selecionar uma única coluna de uma
matriz ou array.

6.1.2 Inteiros Negativos


Inteiros negativos fazem exatamente o oposto de inteiros positivos na indexação. R
retornará todos os elementos, exceto os elementos em um índice negativo. Por
exemplo, deck [-1, 1 : 3] retornará tudo, exceto a primeira linha de deck.
Deck [- (2:52), 1 : 3 ] retornará a primeira linha (e excluirá todo o resto):

deck[-(2:52), 1:3]
## face suit value
## king spades 13

Os números inteiros negativos são uma maneira mais eficiente de subconjuntos do


que os números inteiros positivos se você quiser incluir a maioria das linhas ou
colunas de um quadro de dados.
R retornará um erro se você tentar parear um inteiro negativo com um inteiro
positivo no mesmo índice:
deck[c(-1, 1), 1]
## Error in xj[i] : only 0's may be mixed with negative subscripts

No entanto, você pode usar números inteiros negativos e positivos para


subconjuntos de um objeto se usá-los em índices diferentes (por exemplo, se usar
um no índice de linhas e um no índice de colunas, como deck [ -1, 1] ).

6.1.3 Zero
O que aconteceria se você usasse zero como índice? Zero não é um número inteiro
positivo nem negativo, mas o R ainda o usará para fazer um tipo de subconjunto. O
R não retornará nada de uma dimensão quando você usar zero como índice. Isso
cria um objeto vazio:

deck[0, 0]
## data frame with 0 columns and 0 rows

Para ser honesto, indexar com zero não é muito útil.

6.1.4 Espaços em branco


Você pode usar um espaço em branco para dizer ao R para extrair todos os valores
de uma dimensão. Isso permite subdividir um objeto em uma dimensão, mas não
nas outras, o que é útil para extrair linhas ou colunas inteiras de um quadro de
dados:

deck[1, ]
## face suit value
## king spades 13

6.1.5 Valores Lógicos


Se você fornecer um vetor de TRUE s e FALSE s como índice, o R corresponderá
cada um TRUE a FALSE uma linha no seu quadro de dados (ou a uma coluna,
dependendo de onde você posicionar o índice). O R retornará cada linha que
corresponde a um TRUE, Figura 6.2 .
Pode ser útil imaginar o R lendo o quadro de dados e perguntando: "Devo retornar a
i-ésima linha da estrutura de dados?" e, em seguida, consultando o i-ésimo valor do
índice para obter a resposta. Para que esse sistema funcione, seu vetor deve ter o
mesmo comprimento da dimensão que você está tentando subdividir:]

deck[1, c(TRUE, TRUE, FALSE)]


## face suit
## king spades
rows <- c(TRUE, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
F, F, F, F, F, F, F, F, F, F, F, F, F, F)
deck[rows, ]
## face suit value
## king spades 13

Você pode usar vetores de VERDADEIROS e FALSO para informar ao R exatamente


quais valores você deseja extrair e quais não. O comando retornaria apenas os
números 1, 6 e 5.

Figura 6.2: Você pode usar vetores de VERDADEIROS e FALSO para informar ao R exatamente quais
valores você deseja extrair e quais não. O comando retornaria apenas os números 1, 6 e 5.

Este sistema pode parecer estranho — quem quer digitar tantos TRUE s e FALSE
s? — mas ele se tornará muito poderoso na modificação de valores .

6.1.6 Nomes
Por fim, você pode solicitar os elementos desejados pelo nome — se o seu objeto
tiver nomes (consulte Nomes ). Esta é uma maneira comum de extrair as colunas de
um quadro de dados, já que as colunas quase sempre têm nomes:

deck[1, c("face", "suit", "value")]


## face suit value
## king spades 13
# the entire value column
deck[ , "value"]
## 13 12 11 10 9 8 7 6 5 4 3 2 1 13 12 11 10 9 8
## 7 6 5 4 3 2 1 13 12 11 10 9 8 7 6 5 4 3 2
## 1 13 12 11 10 9 8 7 6 5 4 3 2 1

6.2 Distribuir uma carta


Agora que você conhece os conceitos básicos do sistema de notação do R, vamos
colocá-lo em prática.

Exercício 6.1 (Distribuir uma carta) Complete o código a seguir para criar uma
função que retorna a primeira linha de um quadro de dados:

deal <- function(cards) {


#?
}

Solução. Você pode usar qualquer um dos sistemas que retornam a primeira linha
do seu quadro de dados para escrever umadealfunção. Usarei números inteiros
positivos e espaços em branco porque acho que são fáceis de entender:
deal <- function(cards) {
cards[1, ]
}
A função faz exatamente o que você quer: distribui a carta do topo do seu conjunto
de dados. No entanto, a função se torna menos impressionante se você a executar
deal repetidamente:
deal(deck)
## face suit value
## king spades 13
deal(deck)
## face suit value
## king spades 13
deal(deck)
## face suit value
## king spades 13

deal sempre devolve o rei de espadas porque deck não sabe que distribuímos a
carta. Portanto, o rei de espadas permanece onde está, no topo do baralho, pronto
para ser distribuído novamente. Este é um problema difícil de resolver, e
abordaremos isso em Ambientes . Enquanto isso, você pode resolver o problema
embaralhando seu baralho após cada distribuição. Assim, uma nova carta estará
sempre no topo.
Embaralhar é um compromisso temporário: as probabilidades em jogo no seu
baralho não corresponderão às probabilidades que ocorrem quando você joga com
um único baralho de cartas. Por exemplo, ainda haverá a probabilidade de o rei de
espadas aparecer duas vezes seguidas. No entanto, as coisas não são tão ruins
quanto parecem. A maioria dos cassinos usa cinco ou seis baralhos por vez em jogos
de cartas para evitar a contagem de cartas. As probabilidades que você encontraria
nessas situações são muito próximas das que criaremos aqui.

6.3 Embaralhe o baralho


Ao embaralhar um baralho real, você reorganiza aleatoriamente a ordem das cartas.
No seu baralho virtual, cada carta é uma linha em um quadro de dados. Para
embaralhar o baralho, você precisa reordenar aleatoriamente as linhas no quadro
de dados. Isso é possível? Pode apostar que sim! E você já sabe tudo o que precisa
para fazer isso.
Isso pode parecer bobo, mas comece extraindo cada linha do seu quadro de dados:
deck2 <- deck[1 : 52, ]
head(deck2)
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8

O que você obtém? Um novo quadro de dados cuja ordem não mudou em nada. E se
você pedisse ao R para extrair as linhas em uma ordem diferente? Por exemplo,
você poderia pedir a linha 2, depois a linha 1 e, em seguida, o restante dos cartões:

deck3 <- deck [c (2, 1, 3:52), ]


head(deck3)
## face suit value
## queen spades 12
## king spades 13
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8

O R está em conformidade. Você receberá todas as linhas de volta, e elas virão na


ordem que você solicitou. Se quiser que as linhas venham em uma ordem aleatória,
você precisa ordenar os números inteiros de 1 a 52 em uma ordem aleatória e usar
os resultados como um índice de linha. Como você poderia gerar uma coleção tão
aleatória de números inteiros? Com nossa amigável samplefunção de vizinhança:
random <- sample(1:52, size = 52)
random
## 35 28 39 9 18 29 26 45 47 48 23 22 21 16 32 38 1 15 20
## 11 2 4 14 49 34 25 8 6 10 41 46 17 33 5 7 44 3 27
## 50 12 51 40 52 24 19 13 42 37 43 36 31 30
deck4 <- deck[random, ]
head(deck4)
## face suit value
## five diamonds 5
## queen diamonds 12
## ace diamonds 1
## five spades 5
## nine clubs 9
## jack diamonds 11

Agora o novo conjunto está realmente embaralhado. Você terminará quando


encapsular essas etapas em uma função.

Exercício 6.2 (Embaralhar um baralho) Use as ideias anteriores para escrever uma
shuffle função. Shuffle deve pegar um quadro de dados e retornar uma cópia
embaralhada do quadro de dados.
Solução. Sua shuffle função ficará parecida com a seguinte:
shuffle <- function(cards) {
random <- sample(1:52, size = 52)
cards[random, ]
}
Bom trabalho! Agora você pode embaralhar suas cartas entre cada rodada:

deal(deck)
## face suit value
## king spades 13

deck2 <- shuffle(deck)

deal(deck2)
## face suit value
## jack clubs 11

6.4 Cifrões e colchetes duplos


Dois tipos de objetos em R obedecem a um segundo sistema de notação opcional.
Você pode extrair valores de quadros de dados e listas com a $ sintaxe. Você
encontrará a $ sintaxe repetidamente como programador em R, então vamos
examinar como ela funciona.
Para selecionar uma coluna de um quadro de dados, escreva o nome do quadro de
dados e o nome da coluna separados por $. Observe que o nome da coluna não
deve estar entre aspas:

deck$value
## 13 12 11 10 9 8 7 6 5 4 3 2 1 13 12 11 10 9 8 7
## 6 5 4 3 2 1 13 12 11 10 9 8 7 6 5 4 3 2 1 13
## 12 11 10 9 8 7 6 5 4 3 2 1

O R retornará todos os valores da coluna como um vetor. Essa $ notação é


incrivelmente útil porque você frequentemente armazenará as variáveis dos seus
conjuntos de dados como colunas em um dataframe.
De tempos em tempos, você precisará executar uma função como mean ou
median nos valores de uma variável. Em R, essas funções esperam um vetor de
valores como entrada e deck$value entregam seus dados no formato correto:

mean(deck$value)
## 7
median(deck$value)
## 7

Você pode usar a mesma $notação com os elementos de uma lista, se eles tiverem
nomes. Essa notação também tem uma vantagem com listas. Se você subdividir uma
lista da maneira usual, o R retornará uma nova lista com os elementos que você
solicitou. Isso se aplica mesmo se você solicitar apenas um único elemento.
Para ver isso, faça uma lista:
lst <- list (numbers = c(1, 2), logical = TRUE, strings = c("a", "b", "c"))
lst
## $numbers
## [1] 1 2
## $logical
## [1] TRUE
## $strings
## [1] "a" "b" "c"

E então subdivida-o:
lst[1]
## $numbers
## [ 1 ] 1 2

O resultado é uma lista menor com um elemento. Esse elemento é o vetor c ( 1, 2


). Isso pode ser irritante, pois muitas funções do R não funcionam com listas. Por
exemplo, sum (lst [ 1 ] ) retornará um erro. Seria horrível se, depois de
armazenar um vetor em uma lista, você só pudesse recuperá-lo como uma lista:
Sum ( lst [ 1 ] )
## Error in sum(lst[1]) : invalid 'type' (list) of argument

Quando você usa a $ notação, R retornará os valores selecionados como eles estão,
sem nenhuma estrutura de lista ao redor deles:

lst$numbers
## 1 2

Você pode então alimentar imediatamente uma função com os resultados:


sum(lst$numbers)
## 3
Se os elementos da sua lista não tiverem nomes (ou você não quiser usá-los), você
pode usar dois colchetes, em vez de um, para subdividir a lista. Esta notação fará o
mesmo que a $ notação:
lst[[1]]
## 1 2

Em outras palavras, se você subdividir uma lista com a notação de colchetes


simples, o R retornará uma lista menor. Se você subdividir uma lista com a notação
de colchetes duplos, o R retornará apenas os valores que estavam dentro de um
elemento da lista. Você pode combinar esse recurso com qualquer um dos métodos
de indexação do R:
Lst ["numbers"]
## $numbers
## [1 ] 1 2
Lst [["numbers"]]
## 1 2

Essa diferença é sutil, mas importante. Na comunidade R, existe uma maneira


popular e útil de pensar sobre isso, a Figura 6.3 . Imagine que cada lista é um trem e
cada elemento é um vagão. Ao usar colchetes simples, o R seleciona vagões
individuais e os retorna como um novo trem. Cada vagão mantém seu conteúdo,
mas esse conteúdo ainda está dentro de um vagão (ou seja, uma lista). Ao usar
colchetes duplos, o R, na verdade, descarrega o vagão e devolve o conteúdo.

Figura 6.3: Pode ser útil pensar na sua lista como um trem. Use colchetes simples
para selecionar vagões de trem e colchetes duplos para selecionar o conteúdo
dentro de um vagão.
Nunca prenda
Nos primórdios do R, tornou-se popular usá-lo attach() em um
conjunto de dados depois de carregado. Não faça isso! Attach recria um
ambiente de computação semelhante ao usado em outros aplicativos de
estatística, como Stata e SPSS, que agradavam aos usuários de crossover.
No entanto, R não é Stata nem SPSS. O R é otimizado para usar o
ambiente de computação R, e sua execução attach() pode causar
confusão com algumas funções do R.
O que faz attach()? À primeira vista, attach economiza digitação. Se
você anexar o deck conjunto de dados, poderá se referir a cada uma de
suas variáveis pelo nome; em vez de digitar deck$face, você pode
simplesmente digitar face. Mas digitar não é ruim. Dá a você a chance
de ser explícito, e em programação de computadores, explícito é bom.
Anexar um conjunto de dados cria a possibilidade de o R confundir dois
nomes de variáveis. Se isso ocorrer dentro de uma função, você
provavelmente obterá resultados inutilizáveis e uma mensagem de erro
inútil para explicar o que aconteceu.

Agora que você é um especialista em recuperar valores armazenados em R, vamos


resumir o que você realizou.

6.5 Resumo
Você aprendeu como acessar valores armazenados em R. Você pode recuperar uma
cópia dos valores que estão dentro de um quadro de dados e usar as cópias para
novos cálculos.
Na verdade, você pode usar o sistema de notação do R para acessar valores em
qualquer objeto R. Para usá-lo, escreva o nome de um objeto seguido por colchetes
e índices. Se o seu objeto for unidimensional, como um vetor, você só precisa
fornecer um índice. Se for bidimensional, como um dataframe, você precisa
fornecer dois índices separados por uma vírgula. E, se for n -dimensional, você
precisa fornecer n índices, cada um separado por uma vírgula.
Em Modificando Valores , você levará esse sistema um passo adiante e aprenderá
como alterar os valores reais armazenados no seu quadro de dados. Tudo isso se
soma a algo especial: controle total dos seus dados.
Agora você pode armazenar seus dados no seu computador, recuperar valores
individuais quando quiser e usá-lo para realizar cálculos corretos com esses valores.
Parece básico? Pode ser, mas também é poderoso e essencial para uma ciência de
dados eficiente. Você não precisa mais memorizar tudo de cabeça, nem se
preocupar em fazer cálculos mentais errados. Esse controle de baixo nível sobre
seus dados também é um pré-requisito para programas em R mais eficientes, o
tema do Projeto 3: Caça-Niqueis.

7 Modificando Valores
Pronto para jogar algumas partidas com seu baralho virtual? Calma! O sistema de
pontos do seu baralho não se alinha bem com muitos jogos de cartas. Por exemplo,
na guerra e no pôquer, os ases geralmente valem mais do que os reis. Eles valeriam
14 pontos, não 1.
Nesta tarefa, você alterará o sistema de pontos do seu baralho três vezes para
corresponder a três jogos diferentes: guerra, copas e vinte-e-um. Cada um desses
jogos lhe ensinará algo diferente sobre como modificar os valores dentro de um
conjunto de dados. Comece fazendo uma cópia deck que você possa manipular.
Isso garantirá que você sempre tenha uma cópia original para deckusar (caso as
coisas deem errado):

deck2 <- deck

7.0.1 Alterando valores no local


Você pode usar o sistema de notação do R para modificar valores dentro de um
objeto R. Primeiro, descreva o valor (ou valores) que deseja modificar. Em seguida,
use o operador de atribuição <- para sobrescrever esses valores. O R atualizará os
valores selecionados no objeto original . Vamos colocar isso em prática com um
exemplo real:

vec <- c(0, 0, 0, 0, 0, 0)


vec
## 0 0 0 0 0 0

Veja como você pode selecionar o primeiro valor de vec:

vec[1]
## 0
E aqui está como você pode modificá-lo:

vec[1] <- 1000


vec
## 1000 0 0 0 0 0

Você pode substituir vários valores de uma só vez, desde que o número de novos
valores seja igual ao número de valores selecionados:
vec[c(1, 3, 5)] <- c(1, 1, 1)
vec
## 1 0 1 0 1 0

vec[4:6] <- vec[4:6] + 1


vec
## 1 0 1 1 2 1

Você também pode criar valores que ainda não existem no seu objeto. O R
expandirá o objeto para acomodar os novos valores:

vec[7] <- 0
vec
## 1 0 1 1 2 1 0

Isso fornece uma ótima maneira de adicionar novas variáveis ao seu conjunto de
dados:
deck2$new <- 1:52
head(deck2)
## face suit value new
## king spades 13 1
## queen spades 12 2
## jack spades 11 3
## ten spades 10 4
## nine spades 9 5
## eight spades 8 6
Você também pode remover colunas de um quadro de dados (e elementos de uma
lista) atribuindo a eles o símbolo NULL:
deck2$new <- NULL

head(deck2)
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8

No jogo de guerra, os ases são reis (figurativamente falando). Eles recebem o maior
valor de todas as cartas, que seria algo como 14. Todas as outras cartas recebem o
valor que já possuem em deck. Para jogar guerra, você só precisa alterar os valores
dos seus ases de 1 para 14.
Desde que você não tenha embaralhado seu baralho, você sabe exatamente onde
estão os ases. Eles aparecem a cada 13 cartas. Portanto, você pode descrevê-los
com o sistema de notação de R:

deck2 [c ( 13, 26, 39, 52), ]


## face suit value
## ace spades 1
## ace clubs 1
## ace diamonds 1
## ace hearts 1

Você pode destacar apenas os valores dos ases subdividindo a dimensão das colunas
de deck2. Ou, melhor ainda, você pode subdividir o vetor da coluna deck2$value:
deck2[c (13, 26, 39, 52), 3]
## 1 1 1 1
deck2$value[c (13, 26, 39, 52)]
## 1 1 1 1

Agora, tudo o que você precisa fazer é atribuir um novo conjunto de valores a esses
valores antigos. O conjunto de novos valores terá que ter o mesmo tamanho do
conjunto de valores que você está substituindo. Portanto, você pode salvar c (14,
14, 14, 14) nos valores ace, ou simplesmente salvar 14 e confiar nas regras de
reciclagem do R para expandir 14 para c (14, 14, 14, 14):

deck2$value[c (13, 26, 39, 52)] <- c (14, 14, 14, 14)
# or
deck2$value[c (13, 26, 39, 52)] <- 14

Observe que os valores mudam no local . Você não fica com uma cópia modificada
de deck2; os novos valores aparecerão dentro de deck2:
head(deck2, 13)
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8
## seven spades 7
## six spades 6
## five spades 5
## four spades 4
## three spades 3
## two spades 2
## ace spades 14
A mesma técnica funcionará independentemente de você armazenar seus dados em
um vetor, matriz, array, lista ou quadro de dados. Basta descrever os valores que
você deseja alterar com o sistema de notação do R e, em seguida, atribuir esses
valores com o operador de atribuição do R
As coisas funcionaram muito facilmente neste exemplo porque você sabia
exatamente onde cada ás estava. As cartas foram organizadas de forma ordenada e
um ás apareceu a cada 13 fileiras.
Mas e se o baralho tivesse sido embaralhado? Você poderia olhar todas as cartas e
anotar a localização dos ases, mas isso seria tedioso. Se o seu dataframe fosse
maior, talvez fosse impossível:

deck3 <- shuffle(deck)

Onde estão os ases agora?

head(deck3)
## face suit value
## queen clubs 12
## king clubs 13
## ace spades 1 # an ace
## nine clubs 9
## seven spades 7
## queen diamonds 12

Por que não pedir ao R para encontrar os ases para você? Você pode fazer isso com
a subdivisão lógica. A subdivisão lógica fornece uma maneira de realizar extração e
modificação direcionadas com objetos do R, uma espécie de missão de busca e
destruição dentro dos seus próprios conjuntos de dados.

7.0.2 Subconjunto lógico


Você se lembra do sistema de índices lógicos do R, logicals ? Recapitulando, você
pode selecionar valores com um vetor de TRUE s e FALSE s. O vetor deve ter o
mesmo comprimento que a dimensão que você deseja subdividir. O R retornará
todos os elementos que corresponderem a um VERDADEIRO:

vec
## 1 0 1 1 2 1 0

vec[c (FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)]


## 2
À primeira vista, esse sistema pode parecer impraticável. Quem quer digitar longos
vetores de VERDADEIROS e FALSOS? Ninguém. Mas você não precisa. Você pode
deixar um teste lógico criar um vetor de VERDADEIROS e FALSOS para você.

[Link] Testes Lógicos


Um teste lógico é uma comparação como "um é menor que dois?", 1 < 2 ou "três é
maior que quatro?", 3 > 4. R fornece sete operadores lógicos que você pode usar
para fazer comparações, mostrados na Tabela 7.1 .

Tabela 7.1: Operadores Lógicos de R

Operador Sintaxe Testes


> a>b A é maior que b?
>= a >= b A é maior ou igual a b?
< a<b A é menor que b?
<= a <= b A é menor ou igual a b?
== a == b A é igual a b?
!= a != b A não é igual a b?
%in% a %in% c ( a, b, c) A está no grupo c (a, b, c)?

Cada operador retorna a TRUE ou a FALSE. Se você usar um operador para


comparar vetores, o R fará comparações elemento a elemento, assim como faz com
os operadores aritméticos:
1>2
## FALSE
1 > c(0, 1, 2)
## TRUE FALSE FALSE
c(1, 2, 3) == c(3, 2, 1)
## FALSE TRUE FALSE

%in%é o único operador que não realiza a execução normal elemento a elemento.
%in% testa se o(s) valor(es) do lado esquerdo está(ão) no vetor do lado direito. Se
você fornecer um vetor do lado esquerdo, o não %in% emparelhará os valores da
esquerda com os valores da direita e, em seguida, realizará testes elemento a
elemento. Em vez disso, o testará independentemente se cada valor à esquerda está
em algum lugar do vetor à direita: %in%
1 %in% c (3, 4, 5)
## FALSE
C (1, 2) %in% c(3, 4, 5)
## FALSE FALSE
C (1, 2, 3) %in% c(3, 4, 5)
## FALSE FALSE TRUE
C (1, 2, 3, 4) %in% c(3, 4, 5)
## FALSE FALSE TRUE TRUE

Observe que você testa a igualdade com dois sinais de igual, = = , e não com um
único sinal de igual, = , que é outra maneira de escrever <-. É fácil esquecer e usar
a = b para testar se a é igual a b. Infelizmente, você terá uma surpresa
desagradável. R não retornará um TRUE ou FALSE, porque não precisa: a
retornará igual a b, porque você acabou de executar o equivalente a a <- b.

= é um operador de atribuição
Cuidado para não confundir = com = =. = faz a mesma coisa que <- : atribui um
valor a um objeto.

Você pode comparar quaisquer dois objetos R com um operador lógico; no entanto,
operadores lógicos fazem mais sentido se você comparar dois objetos do mesmo
tipo de dados. Se você comparar objetos de tipos de dados diferentes, o R usará
suas regras de coerção para forçar os objetos a serem do mesmo tipo antes de fazer
a comparação.

Exercício 7.1 (Quantos Ases?) Extraia a face coluna de deck2 e teste se cada valor
é igual a ace. Como desafio, use R para contar rapidamente quantas cartas são
iguais a ace.

Solução. Você pode extrair a face coluna com $ a notação de R:

deck2$face
## "king" "queen" "jack" "ten" "nine"
## "eight" "seven" "six" "five" "four"
## "three" "two" "ace" "king" "queen"
## "jack" "ten" "nine" "eight" "seven"
## "six" "five" "four" "three" "two"
## "ace" "king" "queen" "jack" "ten"
## "nine" "eight" "seven" "six" "five"
## "four" "three" "two" "ace" "king"
## "queen" "jack" "ten" "nine" "eight"
## "seven" "six" "five" "four" "three"
## "two" "ace"

Em seguida, você pode usar o = = operador para testar se cada valor é igual a
ace. No código a seguir, o R usará suas regras de reciclagem para comparar
individualmente cada valor de deck2$face. " ace" Observe que as aspas são
importantes. Se você as omitir, o R tentará encontrar um objeto chamado ace para
comparar com deck2$face:
deck2$face == "ace"
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE TRUE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE TRUE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE TRUE

Você pode usar sum para contar rapidamente o número de TRUE s no vetor
anterior. Lembre-se de que R converterá números lógicos para numéricos quando
você fizer cálculos com eles. R converterá TRUE s em uns e FALSE s em zeros.
Como resultado, sum contará o número de TRUE s:

sum(deck2$face == "ace")
## 4

Você pode usar este método para identificar e trocar os ases do seu baralho, mesmo
que tenha embaralhado as cartas. Primeiro, crie um teste lógico que identifique os
ases do seu baralho embaralhado:

deck3$face == "ace"

Em seguida, use o teste para destacar os valores de pontos ás. Como o teste retorna
um vetor lógico, você pode usá-lo como índice:

deck3$value[deck3$face == "ace"]
## 1 1 1 1

Por fim, use a atribuição para alterar os valores ace em deck3:

deck3$value[deck3$face == "ace"] <- 14

head(deck3)
## face suit value
## queen clubs 12
## king clubs 13
## ace spades 14 # an ace
## nine clubs 9
## seven spades 7
## queen diamonds 12

Para resumir, você pode usar um teste lógico para selecionar valores dentro de um
objeto.
A subdivisão lógica é uma técnica poderosa porque permite identificar, extrair e
modificar rapidamente valores individuais em seu conjunto de dados. Ao trabalhar
com subdivisão lógica, você não precisa saber onde um valor está presente em seu
conjunto de dados. Você só precisa saber como descrever o valor com um teste
lógico.
A subdivisão lógica é uma das coisas que R faz melhor. Na verdade, a subdivisão
lógica é um componente-chave da programação vetorizada, um estilo de codificação
que permite escrever código R rápido e eficiente, que estudaremos em Speed .
Vamos usar a subdivisão lógica em um novo jogo: copas. Em copas, cada carta vale
zero:

deck4 <- deck


deck4$value <- 0
head(deck4, 13)
## face suit value
## king spades 0
## queen spades 0
## jack spades 0
## ten spades 0
## nine spades 0
## eight spades 0
## seven spades 0
## six spades 0
## five spades 0
## four spades 0
## three spades 0
## two spades 0
## ace spades 0

Exceto as cartas do naipe de copas e a dama de espadas. Cada carta do naipe de


copas vale 1. Você consegue encontrar essas cartas e substituir seus valores?
Experimente.

Exercício 7.2 (Pontuação do baralho para copas) Atribua o valor de 1 para cada
carta deck4 que tenha um naipe de copas.
Solução. Para isso, primeiro escreva um teste que identifique as cartas do hearts
naipe:
deck4$suit == "hearts"
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE TRUE TRUE TRUE
## TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## TRUE TRUE TRUE

Em seguida, use seu teste para selecionar os valores dessas cartas:

deck4$value[deck4$suit == "hearts"]
## 0 0 0 0 0 0 0 0 0 0 0 0 0

Por fim, atribua um novo número a esses valores:

deck4$value[deck4$suit == "hearts"] <- 1

Agora todos os seus heartscartões foram atualizados:


deck4$value[deck4$suit == "hearts"]
## 1 1 1 1 1 1 1 1 1 1 1 1 1

Em copas, a rainha de espadas tem o valor mais incomum de todos: ela vale 13
pontos. Deveria ser simples alterar seu valor, mas ela é surpreendentemente difícil
de encontrar. Você pode encontrar todas as rainhas :

deck4[deck4$face == "queen", ]
## face suit value
## queen spades 0
## queen clubs 0
## queen diamonds 0
## queen hearts 1

Mas são três cartas a mais. Por outro lado, você pode encontrar todas as cartas de
espadas :

deck4[deck4$suit == "spades", ]
## face suit value
## king spades 0
## queen spades 0
## jack spades 0
## ten spades 0
## nine spades 0
## eight spades 0
## seven spades 0
## six spades 0
## five spades 0
## four spades 0
## three spades 0
## two spades 0
## ace spades 0

Mas são 12 cartas a mais. O que você realmente quer encontrar são todas as cartas
que tenham valor de face igual a dama e valor de naipe igual a espadas. Você pode
fazer isso com um operador booleano . Operadores booleanos combinam vários
testes lógicos em um único teste.

[Link] Operadores Booleanos


Operadores booleanos são coisas como and ( & ) e or ( | ). Eles reúnem os
resultados de vários testes lógicos em um único TRUE or FALSE. R tem seis
operadores booleanos, mostrados na Tabela 7.2 .

Tabela 7.2: Operadores booleanos

Operador Sintaxe Testes

& Cond1 & cond2 Ambas são cond1 verdadeiras


cond2?

| Cond1 | cond2 Um ou mais de cond1 e são


cond2 verdadeiros?

xor xor (cond1, cond2) É exatamente um dos cond1 e


cond2 verdadeiros?

! !cond1 É cond1 falso? (por exemplo, !


inverte os resultados de um teste
lógico)

any any (cond1, cond2, Algumas das condições é


cond3, ...) verdadeira?

all All (cond1. Cond2, Todas as condições são


cond3, ...) verdadeiras?
Para usar um operador booleano, coloque-o entre dois testes lógicos completos . O
R executará cada teste lógico e, em seguida, usará o operador booleano para
combinar os resultados em um único TRUE ou FALSE, Figura 7.1 .

O erro mais comum com operadores booleanos


É fácil esquecer de colocar um teste completo em ambos
os lados de um operador booleano. Em inglês, é eficiente
dizer " X é maior que dois e menor que nove?". Mas em R,
você precisa escrever o equivalente a " X é maior que dois e x é menor
que nove?". Isso é mostrado na Figura 7.1 .

Figura
7.1: O R avaliará as expressões de cada lado de um operador booleano separadamente e, em
seguida, combinará os resultados em um único VERDADEIRO ou FALSO. Se você não fornecer um
teste completo para cada lado do operador, o R retornará um erro.

Quando usados com vetores, os operadores booleanos seguirão a mesma execução


elemento a elemento dos operadores aritméticos e lógicos:
a <- c (1, 2, 3)
b <- c (1, 2, 3)
c <- c (1, 2, 4)
a == b
## TRUE TRUE TRUE
b == c
## TRUE TRUE FALSE
a == b & b == c
## TRUE TRUE FALSE

Você poderia usar um operador booleano para localizar a rainha de espadas no seu
baralho? Claro que pode. Você quer testar cada carta para ver se é uma rainha e
uma espada. Você pode escrever este teste em R com:

deck4$face == "queen" & deck4$suit == "spades"

## FALSE TRUE FALSE FALSE FALSE FALSE FALSE


## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## FALSE FALSE FALSE

Salvarei os resultados deste teste em um objeto próprio. Isso facilitará o trabalho


com os resultados:
queenOfSpades <- deck4$face == "queen" & deck4$suit == "spades"

Em seguida, você pode usar o teste como índice para selecionar o valor da rainha de
espadas. Certifique-se de que o teste realmente selecione o valor correto:

deck4[queenOfSpades, ]
## face suit value
## queen spades 0
deck4$value[queenOfSpades]
## 0

Agora que você encontrou a rainha de espadas, você pode atualizar seu valor:

deck4$value[queenOfSpades] <- 13
deck4[queenOfSpades, ]
## face suit value
## queen spades 13

Seu baralho agora está pronto para jogar copas.

Exercício 7.3 (Prática com Testes) Se você acha que domina os testes lógicos, tente
converter estas frases em testes escritos com código R. Para ajudar, defini alguns
objetos R após as frases que você pode usar para testar suas respostas:
 W é positivo?
 X é maior que 10 e menor que 20?
 O objeto y é a palavra fevereiro?
 Todos os valores em za são dias da semana?

w <- c ( -1, 0, 1)
x <- c ( 5, 15 )
y <- "February"
z <- c ("Monday", "Tuesday", "Friday")

Solução. Aqui estão algumas respostas modelo. Se você tiver dúvidas, releia como o
R avalia testes lógicos que usam valores booleanos:
w>0
10 < x & x < 20
y == "February"
all (z %in% c ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday"))
Vamos considerar um último jogo: o blackjack. No blackjack, cada carta numérica
tem um valor igual ao seu valor de face. Cada carta figurada (rei, rainha ou valete)
vale 10. Por fim, cada ás vale 11 ou 1, dependendo do resultado final do jogo.
Vamos começar com uma nova cópia de deck— dessa forma, os cartões numéricos
( twoaté ten) começarão com o valor correto:

deck5 <- deck

head(deck5, 13)
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8
## seven spades 7
## six spades 6
## five spades 5
## four spades 4
## three spades 3
## two spades 2
## ace spades 1

Você pode alterar o valor das cartas de figuras de uma só vez com % in %:

facecard <- deck5$face %in% c ("king", "queen", "jack")


deck5 [ facecard, ]
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## king clubs 13
## queen clubs 12
## jack clubs 11
## king diamonds 13
## queen diamonds 12
## jack diamonds 11
## king hearts 13
## queen hearts 12
## jack hearts 11

deck5$value[facecard] <- 10

head(deck5, 13)
## face suit value
## king spades 10
## queen spades 10
## jack spades 10
## ten spades 10
## nine spades 9
## eight spades 8
## seven spades 7
## six spades 6
## five spades 5
## four spades 4
## three spades 3
## two spades 2
## ace spades 1

Agora você só precisa corrigir os valores dos ases — ou não? É difícil decidir qual
valor atribuir aos ases, pois o valor exato deles muda de mão para mão. Ao final de
cada mão, um ás valerá 11 se a soma das cartas do jogador não exceder 21. Caso
contrário, o ás valerá 1. O valor real do ás dependerá das outras cartas na mão do
jogador. Este é um caso de informação ausente. No momento, você não tem
informações suficientes para atribuir um valor de pontos correto às cartas de ás.

7.0.3 Informações ausentes


Problemas com informações ausentes são frequentes em ciência de dados.
Geralmente, são mais simples: você não sabe um valor porque a medição foi
perdida, corrompida ou nunca foi feita. O R tem uma maneira de ajudar você a
gerenciar esses valores ausentes.
O NA caractere é um símbolo especial em R. Ele significa "não disponível" e pode
ser usado como um espaço reservado para informações ausentes. R tratará NA
exatamente como você deseja que informações ausentes sejam tratadas. Por
exemplo, qual resultado você esperaria se adicionasse 1 a uma informação ausente?

1 + NA
## NA

R retornará uma segunda informação faltante. Não seria correto dizer isso, 1 + NA
= 1 pois há uma boa chance de que a quantidade faltante não seja zero. Você não
tem informações suficientes para determinar o resultado.
E se você testasse se uma informação faltante é igual a 1?
NA == 1
## NA

Novamente, sua resposta seria algo como "Não sei se isso é igual a um", ou seja,
NA. Geralmente, NA s se propagam sempre que você os usa em uma operação ou
função R. Isso pode evitar que você cometa erros com base em dados ausentes.

[Link] [Link]
Valores ausentes podem ajudar a contornar lacunas em seus conjuntos de dados,
mas também podem criar alguns problemas frustrantes. Suponha, por exemplo, que
você tenha coletado 1.000 pass:[observações] e desejam calcular a média com a
função R. mean Se pelo menos um dos valores for NA, o resultado será NA:

c(NA, 1:50)
## NA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
## 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
## 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
mean(c ( NA, 1:50))
## NA
Compreensivelmente, você pode preferir um comportamento diferente. A maioria
das funções R vem com o argumento opcional, [Link], que significa NAremove. O R
ignorará NAs ao avaliar uma função se você adicionar o argumento [Link] =
TRUE:

mean(c(NA, 1:50), [Link] = TRUE)


## 25.5

[Link] é. na

Às vezes, você pode querer identificar o NA s no seu conjunto de dados com um


teste lógico, mas isso também cria um problema. Como você faria isso? Se algo for
um valor ausente, qualquer teste lógico que o utilize retornará um valor ausente,
até mesmo este teste:

NA == NA
## NA
O que significa que testes como esse não ajudarão você a encontrar valores
ausentes:
C (1, 2, 3, NA) == NA
## NA NA NA NA
Mas não se preocupe muito; o R fornece uma função especial que pode testar se um
valor é um NA. A função tem um nome sensato [Link]:
[Link] (NA)
## TRUE
vec <- c (1, 2, 3, NA)
[Link] (vec)
## FALSE FALSE FALSE TRUE

Vamos definir todos os valores dos seus ás como NA. Isso realizará duas coisas.
Primeiro, lembrará que você não sabe o valor final de cada ás. Segundo, evitará que
você marque acidentalmente uma mão com um ás antes de determinar o valor final
do ás.
Você pode definir seus valores de ás NA da mesma forma que os definiria para um
número:
deck5$value[deck5$face == "ace"] <- NA
head(deck5, 13)
## face suit value
## king spades 10
## queen spades 10
## jack spades 10
## ten spades 10
## nine spades 9
## eight spades 8
## seven spades 7
## six spades 6
## five spades 5
## four spades 4
## three spades 3
## two spades 2
## ace spades NA
Parabéns! Seu baralho agora está pronto para uma partida de blackjack.

7.0.4 Resumo
Você pode modificar valores no local dentro de um objeto R ao combinar a sintaxe
de notação de R com o operador de atribuição, <-. Isso permite que você atualize
seus dados e limpe seus conjuntos de dados.
Ao trabalhar com grandes conjuntos de dados, modificar e recuperar valores cria um
problema logístico por si só. Como pesquisar nos dados para encontrar os valores
que deseja modificar ou recuperar? Como usuário do R, você pode fazer isso com
subconjuntos lógicos. Crie um teste lógico com operadores lógicos e booleanos e,
em seguida, use o teste como um índice na notação de colchetes do R. O R
retornará os valores que você está procurando, mesmo que você não saiba onde
eles estão.
Recuperar valores individuais não será sua única preocupação como programador R.
Você também precisará recuperar conjuntos de dados inteiros; por exemplo, você
pode chamar um em uma função. Ambientes lhe ensinarão como o R pesquisa e
salva conjuntos de dados e outros objetos R em seu sistema de ambiente. Você
então usará esse conhecimento para corrigir as funções deal e .shuffle

8 Ambientes
Seu baralho agora está pronto para uma partida de vinte-e-um (ou copas ou guerra),
mas suas funções shuffle e deal estão à altura? Definitivamente não. Por
exemplo, deal distribua a mesma carta repetidamente:

deal(deck)
## face suit value
## king spades 13

deal(deck)
## face suit value
## king spades 13

deal(deck)
## face suit value
## king spades 13

E a shuffle função não embaralha propriamente deck (ela retorna uma cópia
do deck que foi embaralhado). Resumindo, ambas as funções usam deck, mas
nenhuma manipula deck— e gostaríamos que o fizessem.
Para corrigir essas funções, você precisará aprender como o R armazena, pesquisa e
manipula objetos como deck. O R faz tudo isso com a ajuda de um sistema de
ambiente.

8.1 Ambientes
Pense por um momento em como seu computador armazena arquivos. Cada arquivo
é salvo em uma pasta, e cada pasta é salva em outra pasta, formando um sistema de
arquivos hierárquico. Se o seu computador quiser abrir um arquivo, ele precisa
primeiro procurá-lo nesse sistema de arquivos.
Você pode ver seu sistema de arquivos abrindo uma janela do Finder. Por exemplo,
a Figura 8.1 mostra parte do sistema de arquivos do meu computador. Tenho várias
pastas. Dentro de uma delas, há uma subpasta chamada Documents , dentro dessa
subpasta, há uma subpasta chamada ggsubplot , dentro dessa pasta, há uma pasta
chamada inst , dentro dela, uma pasta chamada doc e, dentro dela, um arquivo
chamado [Link] .

Figura 8.1: Seu computador organiza os arquivos em uma hierarquia de pastas e subpastas. Para
visualizar um arquivo, você precisa descobrir onde ele está salvo no sistema de arquivos.

O R usa um sistema semelhante para salvar objetos. Cada objeto é salvo dentro de
um ambiente, um objeto em forma de lista que se assemelha a uma pasta no seu
computador. Cada ambiente é conectado a um ambiente pai , um ambiente de nível
superior, o que cria uma hierarquia de ambientes.
Você pode ver o sistema de ambientes do R com a parenvs função no pacote pryr
(a nota parenvs veio no pacote pryr quando este livro foi publicado pela primeira
vez). parenvs (all = TRUE) retornará uma lista dos ambientes que sua sessão R
está usando. A saída real variará de sessão para sessão, dependendo dos pacotes
que você carregou. Aqui está a saída da minha sessão atual:
library(pryr)
parenvs (all = TRUE)
## label name
## 1 <environment: R_GlobalEnv> ""
## 2 <environment: package:pryr> "package:pryr"
## 3 <environment: 0x7fff3321c388> "tools:rstudio"
## 4 <environment: package:stats> "package:stats"
## 5 <environment: package:graphics> "package:graphics"
## 6 <environment: package:grDevices> "package:grDevices"
## 7 <environment: package:utils> "package:utils"
## 8 <environment: package:datasets> "package:datasets"
## 9 <environment: package:methods> "package:methods"
## 10 <environment: 0x7fff3193dab0> "Autoloads"
## 11 <environment: base> ""
## 12 <environment: R_EmptyEnv> ""

É preciso um pouco de imaginação para interpretar essa saída, então vamos


visualizar os ambientes como um sistema de pastas, Figura 8.2 . Você pode imaginar
a árvore de ambientes assim. O ambiente de nível mais baixo é
nomeado R_GlobalEnv e salvo dentro de um ambiente chamado package:pryr,
que é salvo dentro do ambiente chamado 0x7fff3321c388, e assim por diante,
até chegar ao ambiente final, de nível mais alto, R_EmptyEnv. R_EmptyEnv é
o único ambiente R que não possui um ambiente pai.
Figura 8.2: O R armazena objetos R em uma árvore de ambiente que se assemelha ao sistema de
pastas do seu computador.

Lembre-se de que este exemplo é apenas uma metáfora. Os ambientes do R existem


na sua memória RAM, e não no seu sistema de arquivos. Além disso, os ambientes
do R não são tecnicamente salvos uns dentro dos outros. Cada ambiente está
conectado a um ambiente pai, o que facilita a busca na árvore de ambientes do R.
Mas essa conexão é unidirecional: não há como olhar para um ambiente e saber
quais são seus "filhos". Portanto, você não pode buscar na árvore de ambientes do
R. Em outros aspectos, porém, o sistema de ambientes do R funciona de forma
semelhante a um sistema de arquivos.

8.2 Trabalhando com Ambientes


O R vem com algumas funções auxiliares que você pode usar para explorar sua
árvore de ambientes. Primeiro, você pode se referir a qualquer um dos ambientes
em sua árvore com [Link]. [Link] Recebe um nome de
ambiente (como uma sequência de caracteres) e retorna o ambiente
correspondente:

[Link]("package:stats")
## <environment: package:stats>
## attr(,"name")
## [1] "package:stats"
## attr(,"path")
## [1]
"/Library/Frameworks/[Link]/Versions/3.0/Resources/library/st
ats"

Três ambientes na sua árvore também vêm com suas próprias funções de acesso.
São eles: o ambiente global ( R_GlobalEnv), o ambiente base ( base) e o
ambiente vazio ( R_EmptyEnv). Você pode referenciá-los com:

globalenv()
## <environment: R_GlobalEnv>
baseenv()
## <environment: base>
emptyenv()
##<environment: R_EmptyEnv>

Em seguida, você pode consultar o pai de um ambiente com [Link]:


[Link](globalenv())
## <environment: package:pryr>
## attr(,"name")
## [1] "package:pryr"
## attr(,"path")
## [1]
"/Library/Frameworks/[Link]/Versions/3.0/Resources/library/pr
yr"

Observe que o ambiente vazio é o único ambiente R sem um pai:

[Link](emptyenv())
## Error in [Link](emptyenv ( ) ) : the empty environment has
no parent

Você pode visualizar os objetos salvos em um ambiente com ls


ou [Link]. lsretornará apenas os nomes dos objetos, mas [Link] exibirá um pouco
sobre a estrutura de cada objeto:

ls(emptyenv())
## character(0)

ls(globalenv())
## "deal" "deck" "deck2" "deck3" "deck4" "deck5"
## "die" "gender" "hand" "lst" "mat" "mil"
## "new" "now" "shuffle" "vec"

O ambiente vazio está — sem surpresas — vazio; o ambiente base tem objetos
demais para listar aqui; e o ambiente global tem alguns rostos familiares. É onde o R
salvou todos os objetos que você criou até agora.

O painel de ambiente do RStudio exibe todos os objetos no seu


ambiente global.

Você pode usar a sintaxe do R $ para acessar um objeto em um ambiente


específico. Por exemplo, você pode acessar deck a partir do ambiente global:

head(globalenv()$deck, 3)
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
E você pode usar a assign função para salvar um objeto em um ambiente
específico. Primeiro, informe assign o nome do novo objeto (como uma sequência
de caracteres). Em seguida, informe assign o valor do novo objeto e, por fim, o
ambiente para salvá-lo:
assign("new", "Hello Global", envir = globalenv( ))

globalenv( )$new
## "Hello Global"

Observe que assign funciona de forma semelhante a <-. Se um objeto já existir


com o nome fornecido no ambiente especificado, assign o substituirá sem pedir
permissão. Isso é assign útil para atualizar objetos, mas pode gerar problemas.
Agora que você já pode explorar a árvore de ambientes do R, vamos examinar como
o R a utiliza. O R trabalha em estreita colaboração com a árvore de ambientes para
procurar objetos, armazenar objetos e avaliar funções. A forma como o R executa
cada uma dessas tarefas dependerá do ambiente ativo no momento.
8.2.1 O Ambiente Ativo
A qualquer momento, o R está trabalhando em estreita colaboração com um
único ambiente. O R armazenará novos objetos nesse ambiente (se você criar
algum) e o usará como ponto de partida para procurar objetos existentes (se você
chamar algum). Chamarei esse ambiente especial de ambiente ativo . O ambiente
ativo geralmente é o ambiente global, mas isso pode mudar quando você executa
uma função.

Você pode usar environment para ver o ambiente ativo atual:

environment()
<environment: R_GlobalEnv>

O ambiente global desempenha um papel especial em R. É o ambiente ativo para


cada comando executado na linha de comando. Como resultado, qualquer objeto
criado na linha de comando será salvo no ambiente global. Você pode pensar no
ambiente global como o seu espaço de trabalho do usuário.
Quando você chama um objeto na linha de comando, o R o procura primeiro no
ambiente global. Mas e se o objeto não estiver lá? Nesse caso, o R seguirá uma série
de regras para procurá-lo.

8.3 Regras de Escopo


O R segue um conjunto especial de regras para pesquisar objetos. Essas regras são
conhecidas como regras de escopo do R, e você já conheceu algumas delas:
1. R procura objetos no ambiente ativo atual.
2. Ao trabalhar na linha de comando, o ambiente ativo é o ambiente global.
Portanto, o R procura objetos que você chama na linha de comando no
ambiente global.
Aqui está uma terceira regra que explica como R encontra objetos que não estão no
ambiente ativo
3. Quando R não encontra um objeto em um ambiente, R procura no
ambiente pai do ambiente, depois no ambiente pai do ambiente pai e
assim por diante, até que R encontre o objeto ou alcance o ambiente
vazio.
Portanto, se você chamar um objeto na linha de comando, o R o procurará no
ambiente global. Se não o encontrar lá, o R procurará no ambiente pai do ambiente
global, depois no ambiente pai do ambiente pai e assim por diante, subindo na
árvore de ambientes até encontrar o objeto, como na Figura 8.3 . Se o R não
conseguir encontrar o objeto em nenhum ambiente, ele retornará um erro
informando que o objeto não foi encontrado.

Figura 8.3: O R buscará um objeto pelo nome no ambiente ativo, neste caso, o ambiente global. Se o
R não encontrar o objeto lá, ele buscará no ambiente pai do ambiente ativo, depois no ambiente pai
do ambiente pai, e assim por diante, até que o R encontre o objeto ou fique sem ambientes.

Lembre-se de que funções são um tipo de objeto em R. R armazenará


e pesquisará funções da mesma forma que armazena e pesquisa outros
objetos, pesquisando-os pelo nome na árvore de ambiente.

8.4 Atribuição
Ao atribuir um valor a um objeto, o R salva o valor no ambiente ativo com o nome
do objeto. Se um objeto com o mesmo nome já existir no ambiente ativo, o R o
substituirá.
Por exemplo, um objeto chamado new existe no ambiente global:

new
## "Hello Global"
Você pode salvar um novo objeto nomeado new no ambiente global com este
comando. O R substituirá o objeto antigo como resultado:
new <- "Hello Active"
new
## "Hello Active"

Esse arranjo cria um dilema para o R sempre que ele executa uma função. Muitas
funções salvam objetos temporários que as ajudam a realizar suas tarefas. Por
exemplo, a roll função do Projeto 1: Dados Ponderados salvou um objeto
chamado die e um objeto chamado dice:

roll <- function() {


die <- 1:6
dice <- sample(die, size = 2, replace = TRUE)
sum(dice)
}
O R deve salvar esses objetos temporários no ambiente ativo; mas, se fizer isso,
poderá sobrescrever objetos existentes. Os autores de funções não podem prever
com antecedência quais nomes já podem existir no seu ambiente ativo. Como o R
evita esse risco? Sempre que executa uma função, ele cria um novo ambiente ativo
para avaliá-la.

8.5 Avaliação
O R cria um novo ambiente cada vez que avalia uma função. O R usará o novo
ambiente como o ambiente ativo enquanto executa a função e, em seguida,
retornará ao ambiente de onde você chamou a função, trazendo o resultado da
função com ele. Vamos chamar esses novos ambientes de ambientes de tempo de
execução, pois o R os cria em tempo de execução para avaliar funções.
Usaremos a seguinte função para explorar os ambientes de execução do R.
Queremos saber como são os ambientes: quais são os ambientes pais e quais
objetos eles contêm? show_env A função foi projetada para nos informar:
show_env <- function(){
list([Link] = environment(),
parent = [Link](environment()),
objects = [Link](environment()))
}
show_env é em si uma função, então quando chamamos show_env ( ), R criará
um ambiente de execução para avaliar a função. Os resultados de show_env nos
dirão o nome do ambiente de execução, seu pai e quais objetos o ambiente de
execução contém:

show_env()
## $[Link]
## <environment: 0x7ff711d12e28>
##
## $parent
## <environment: R_GlobalEnv>
##
## $objects

Os resultados revelam que o R criou um novo ambiente chamado 0x7ff711d12e28


para execução show_env( ). O ambiente não continha objetos e seu pai era
o global environment. Portanto, para fins de execução show_env, a árvore de
ambientes do R se parecia com a Figura 8.4 .

Vamos correr show_env novamente:


show_env()
## $[Link]
## <environment: 0x7ff715f49808>
##
## $parent
## <environment: R_GlobalEnv>
##
## $objects

Desta vez, show_env foi executado em um novo ambiente, 0x7ff715f49808. O R


cria um novo ambiente cada vez que você executa uma função.
O 0x7ff715f49808 ambiente é exatamente igual ao 0x7ff711d12e28. Ele está
vazio e tem o mesmo ambiente global que seu pai.

Figura 8.4: R cria um novo ambiente para executar show_env. O ambiente é filho do ambiente
global.

Agora vamos considerar qual ambiente o R usará como pai do ambiente de tempo
de execução.
O R conectará o ambiente de execução de uma função ao ambiente em que a
função foi criada inicialmente . Esse ambiente desempenha um papel importante na
vida da função, pois todos os ambientes de execução da função o utilizarão como
um ambiente pai. Vamos chamar esse ambiente de ambiente de origem . Você pode
consultar o ambiente de origem de uma função executando environment na
função:

environment(show_env)
## <environment: R_GlobalEnv>

O ambiente de origem de show_env é o ambiente global porque o


criamos show_env na linha de comando, mas não precisa ser o ambiente global.
Por exemplo, o ambiente de parenvs é o pryr pacote:
environment(parenvs)
## <environment: namespace:pryr>

Em outras palavras, o pai de um ambiente de execução nem sempre será o


ambiente global; será qualquer ambiente em que a função foi criada primeiro.
Por fim, vamos analisar os objetos contidos em um ambiente de execução. No
momento, show_env os ambientes de execução de não contêm nenhum objeto,
mas isso é fácil de corrigir. Basta show_env criar alguns objetos em seu corpo de
código. O R armazenará quaisquer objetos criados por show_env em seu ambiente
de execução. Por quê? Porque o ambiente de execução será o ambiente ativo
quando esses objetos forem criados:

show_env <- function( ){


a <- 1
b <- 2
c <- 3
list([Link] = environment( ),
parent = [Link](environment( ) ),
objects = [Link](environment( ) ) )
}
Desta vez, quando executamos show_env, R armazena a, b, e c no ambiente de
execução:
show_env()
## $[Link]
## <environment: 0x7ff712312cd0>
##
## $parent
## <environment: R_GlobalEnv>
##
## $objects
## a : num 1
## b : num 2
## c : num 3
É assim que o R garante que uma função não sobrescreva nada que não deva. Todos
os objetos criados pela função são armazenados em um ambiente de execução
seguro e discreto.
O R também colocará um segundo tipo de objeto em um ambiente de execução. Se
uma função tiver argumentos, o R copiará cada argumento para o ambiente de
execução. O argumento aparecerá como um objeto que possui o nome do
argumento, mas o valor de qualquer entrada que o usuário tenha fornecido para o
argumento. Isso garante que uma função seja capaz de encontrar e usar cada um de
seus argumentos:

foo <- "take me to your runtime"


show_env <- function ( x = foo){
list ([Link] = environment(),
parent = [Link](environment( ) ),
objects = [Link](environment( ) ) )
}
show_env( )
## $[Link]
## <environment: 0x7ff712398958>
##
## $parent
## <environment: R_GlobalEnv>
##
## $objects
## x : chr "take me to your runtime"

Vamos juntar tudo isso para ver como o R avalia uma função. Antes de chamar uma
função, o R está trabalhando em um ambiente ativo; vamos chamar isso
de ambiente de chamada . É o ambiente a partir do qual o R chama a função.
Em seguida, você chama a função. O R responde configurando um novo ambiente de
execução. Este ambiente será um filho do ambiente de origem da função. O R
copiará cada um dos argumentos da função para o ambiente de execução e, em
seguida, tornará este o novo ambiente ativo.
Em seguida, o R executa o código no corpo da função. Se o código criar algum
objeto, o R o armazena no ambiente ativo, ou seja, o ambiente de execução. Se o
código chamar algum objeto, o R usará suas regras de escopo para procurá-lo. O R
pesquisará o ambiente de execução, depois o ambiente pai do ambiente de
execução (que será o ambiente de origem), depois o ambiente pai do ambiente de
origem e assim por diante. Observe que o ambiente de chamada pode não estar no
caminho de busca. Normalmente, uma função chamará apenas seus argumentos,
que o R consegue encontrar no ambiente de execução ativo.
Por fim, o R finaliza a execução da função. Ele alterna o ambiente ativo de volta para
o ambiente de chamada. Agora, o R executa quaisquer outros comandos na linha de
código que chamou a função. Portanto, se você salvar o resultado da função em um
objeto com <-, o novo objeto será armazenado no ambiente de chamada.
Recapitulando, o R armazena seus objetos em um sistema de ambiente. A qualquer
momento, o R está trabalhando em estreita colaboração com um único ambiente
ativo. Ele armazena novos objetos nesse ambiente e o utiliza como ponto de partida
na busca por objetos existentes. O ambiente ativo do R geralmente é o ambiente
global, mas o R ajustará o ambiente ativo para executar funções de forma segura,
como executar funções.
Como você pode usar esse conhecimento para corrigir as funções deal e ? shuffle
Primeiro, vamos começar com uma pergunta de aquecimento. Suponha que eu
redefina deal na linha de comando assim:

deal <- function() {


deck[1, ]
}
Observe que deal não aceita mais argumentos e chama o deck objeto, que reside
no ambiente global.

Exercício 8.1 (O acordo funcionará?) R será capaz de encontrar deck e retornar


uma resposta quando eu chamar a nova versão de deal, como deal( )?
Solução. Sim. deal Continuará funcionando da mesma forma que antes. O R será
executado deal em um ambiente de execução que é filho do ambiente global. Por
que será filho do ambiente global? Porque o ambiente global é o ambiente de
origem de deal (definimos deal no ambiente global):

environment(deal)
## <environment: R_GlobalEnv>

Ao dealchamar deck, o R precisará procurar o deck objeto. As regras de escopo do


R o levarão à versão de deckno ambiente global, como na Figura 8.5 . deal Como
resultado, funciona conforme o esperado:
deal( )
## face suit value
## king spades 13

Figura 8.5:
O R encontra o deck consultando o ambiente de execução pai do deal. O ambiente pai é o ambiente
global, o ambiente de origem do deal. Aqui, o R encontra a cópia do deck .

Agora vamos corrigir a deal função para remover as cartas que ela distribuiu
de deck. Lembre-se de que deal retorna a carta do topo de , deck mas não a
remove do baralho. Como resultado, deal sempre retorna a mesma carta:
deal()
## face suit value
## king spades 13
deal()
## face suit value
## king spades 13

Você conhece sintaxe R o suficiente para remover a carta do topo de deck. O


código a seguir salvará uma cópia original de deck e removerá a carta do topo:
DECK <- deck
deck <- deck[-1, ]

head(deck, 3)
## face suit value
## queen spades 12
## jack spades 11
## ten spades 10

Agora vamos adicionar o código a deal. Aqui deal salva (e depois retorna) a carta
do topo de deck. No meio tempo, ele remove a carta de deck… ou não?

deal <- function( ) {


card <- deck[ 1, ]
deck <- deck[ -1, ]
card
}

Este código não funcionará porque o R estará em um ambiente de execução quando


executar deck <- deck[ -1, ]. Em vez de sobrescrever a cópia global
de deck, deck [ -1, ], deal apenas criará uma cópia ligeiramente alterada
de deck em seu ambiente de execução, como na Figura 8.6 .
Figura 8.6: A função deal procura deck no ambiente global, mas salva deck[-1, ] no ambiente de
tempo de execução como um novo objeto chamado deck.

Exercício 8.2 (Sobrescrever baralho) Reescreva a deck <- deck[ -1, ] linha de
deal para atribuir deck [ -1, ] a um objeto nomeado deck no ambiente global.
Dica: considere aassign função.
Solução. Você pode atribuir um objeto a um ambiente específico com a assign
função:
deal <- function() {
card <- deck[1, ]
assign("deck", deck[-1, ], envir = globalenv())
card
}
Agora deal finalmente limparemos a cópia global de deck, e
poderemos dealcartões como faríamos na vida real:

deal()
## face suit value
## queen spades 12

deal()
## face suit value
## jack spades 11

deal()
## face suit value
## ten spades 10

Vamos voltar nossa atenção para a shuffle função:

shuffle <- function(cards) {


random <- sample(1:52, size = 52)
cards[random, ]
}

Shuffle (deck) não embaralha o deck objeto; ele retorna uma cópia
embaralhada do deck objeto:
head(deck, 3)
## face suit value
## nine spades 9
## eight spades 8
## seven spades 7

a <- shuffle(deck)

head(deck, 3)
## face suit value
## nine spades 9
## eight spades 8
## seven spades 7

head(a, 3)
## face suit value
## ace diamonds 1
## seven clubs 7
## two clubs 2

Esse comportamento agora é indesejável de duas maneiras. Primeiro, shuffle não


embaralha deck. Segundo, shuffle retorna uma cópia de deck, que pode estar
sem as cartas que foram distribuídas. Seria melhor se shuffle as cartas distribuídas
fossem devolvidas ao baralho e depois embaralhadas. É o que acontece quando
você embaralha um baralho na vida real.

Exercício 8.3 (Reescreva embaralhado) Reescrevaque reside no ambiente global


shuffle seja substituída, a cópia intacta de, que também reside no ambiente global.
A nova versão de não deve ter argumentos e não retornar nenhuma saída. Deck
DECK deck shuffle
Solução. Você pode atualizar shuffle da mesma forma que atualizou o deck. A
seguinte versão resolverá o problema:

shuffle <- function(){


random <- sample(1:52, size = 52)
assign("deck", DECK[random, ], envir = globalenv())
}

Como DECK vive no ambiente global, shuffle o ambiente de origem de shuffle


será capaz de encontrar DECK em tempo de execução. O R buscará DECK primeiro
no shuffle ambiente de execução de e, em seguida, no shuffle ambiente de
origem de — o ambiente global — que é onde DECK está armazenado.
A segunda linha de shuffle criará uma cópia reordenada de DECK e a salvará
como deck no ambiente global. Isso substituirá a versão anterior, não
embaralhada, de deck.

8.6 Encerramentos
Nosso sistema finalmente funciona. Por exemplo, você pode embaralhar as cartas e
depois distribuir uma mão de blackjack:
shuffle( )
deal( )
## face suit value
## queen hearts 12
deal( )
## face suit value
## eight hearts 8

Mas o sistema requer deck e DECK existe no ambiente global. Muitas coisas
acontecem nesse ambiente, e é possível que deck sejam modificadas ou apagadas
acidentalmente.
Seria melhor se pudéssemos armazenar deck em um local seguro e discreto, como
um desses ambientes seguros e discretos que o R cria para executar funções. Na
verdade, armazenar deck em um ambiente de execução não é uma má ideia.
Você poderia criar uma função que recebe deck como argumento e salva uma
cópia de deck como DECK. A função também poderia salvar suas próprias cópias
de deal e shuffle:
setup <- function(deck) {
DECK <- deck
DEAL <- function() {
card <- deck[1, ]
assign("deck", deck[-1, ], envir = globalenv())
card
}
SHUFFLE <- function(){
random <- sample(1:52, size = 52)
assign("deck", DECK[random, ], envir = globalenv())
}
}

Quando você executa setup, o R criará um ambiente de execução para armazenar


esses objetos. O ambiente será semelhante à Figura 8.7 .
Agora, todas essas coisas estão seguras e fora do caminho em um ambiente filho do
ambiente global. Isso as torna seguras, mas difíceis de usar. Vamos pedir setuppara
retornar DEAL e, SHUFFLE assim, podemos usá-las. A melhor maneira de fazer
isso é retornar as funções como uma lista:
setup <- function(deck) {
DECK <- deck
DEAL <- function( ) {
card <- deck[1, ]
assign("deck", deck[-1, ], envir = globalenv( ))
card
}
SHUFFLE <- function( ){
random <- sample(1:52, size = 52)
assign("deck", DECK[random, ], envir = globalenv( ))
}
list(deal = DEAL, shuffle = SHUFFLE)
}
cards <- setup(deck)

Figura 8.7: Executar o setup armazenará deck e DECK em um local discreto e criará as
funções DEAL e SHUFFLE. Cada um desses objetos será armazenado em um ambiente cujo
pai é o ambiente global.

Em seguida, você pode salvar cada um dos elementos da lista em um objeto


dedicado no ambiente global:

deal <- cards$deal


shuffle <- cards$shuffle

Agora você pode executar deal e shuffle como antes. Cada objeto contém o
mesmo
código do original deal e shuffle:

deal
## function() {
## card <- deck[1, ]
## assign("deck", deck[-1, ], envir = globalenv())
## card
## }
## <environment: 0x7ff7169c3390>

shuffle
## function(){
## random <- sample(1:52, size = 52)
## assign("deck", DECK[random, ], envir = globalenv())
## }
## <environment: 0x7ff7169c3390>

No entanto, as funções agora têm uma diferença importante. Seu ambiente de


origem não é mais o ambiente global (embora deal e shuffle estejam atualmente
salvos lá). Seu ambiente de origem é o ambiente de execução que o R criou quando
você executou setup. É onde o R criou DEAL e SHUFFLE, as funções copiadas
para o novo deal e shuffle, como mostrado em:
environment(deal)
## <environment: 0x7ff7169c3390>
environment(shuffle)
## <environment: 0x7ff7169c3390>

Por que isso importa? Porque agora, quando você executa deal or shuffle, o R
avaliará as funções em um ambiente de execução que usa 0x7ff7169c3390 como
pai. DECK e deck estará nesse ambiente pai, o que significa que deal e shuffle
poderá encontrá-las em tempo de execução. DECK e deck estará no caminho de
busca das funções,
mas ainda fora
do caminho em
todos os outros
aspectos,
como mostrado na
Figura 8 .8 .
Figura 8.8: Agora, distribuir e embaralhar serão executados em um ambiente que tem o
baralho protegido e o DECK em seu caminho de pesquisa.

Esse arranjo é chamado de fechamento . setup O ambiente de execução de


"envolve" as funções deal e shuffle. Ambas deal e shuffle podem trabalhar em
estreita colaboração com os objetos contidos no ambiente envolvente, mas quase
nenhuma outra função ou ambiente pode. O ambiente envolvente não está no
caminho de busca para nenhuma outra função ou ambiente R.
Você deve ter notado que deal e shuffle ainda atualiza o deck objeto no
ambiente global. Não se preocupe, estamos prestes a mudar isso. Queremos
que deal e shuffle trabalhe exclusivamente com os objetos no ambiente pai (que
os envolve) de seus ambientes de execução. Em vez de fazer com que cada função
faça referência ao ambiente global para atualizar deck, você pode fazer com que
elas façam referência ao ambiente pai em tempo de execução, como mostrado na
Figura 8.9 :

setup <- function(deck) {


DECK <- deck
DEAL <- function() {
card <- deck[1, ]
assign("deck", deck[-1, ], envir = [Link](environment()))
card
}
SHUFFLE <- function(){
random <- sample(1:52, size = 52)
assign("deck", DECK[random, ], envir = [Link](environment()))
}
list(deal = DEAL, shuffle = SHUFFLE)
}
cards <- setup(deck)
deal <- cards$deal
shuffle <- cards$shuffle

Figura 8.9: Quando você altera seu código, o deal e o shuffle passam de atualizar o ambiente global
(esquerda) para atualizar o ambiente pai (direita).

Finalmente temos um jogo de cartas independente. Você pode excluir (ou


modificar) a cópia global de deck quantos quiser e ainda jogar cartas. Deal
E shuffle usará a cópia original e protegida de deck:
rm(deck)
shuffle()
deal()
## face suit value
## ace hearts 1
deal()
## face suit value
## jack clubs 11
Vinte-e-um!

8.7 Resumo
O R salva seus objetos em um sistema de ambiente semelhante ao sistema de
arquivos do seu computador. Se você entender esse sistema, poderá prever como o
R procurará objetos. Se você chamar um objeto na linha de comando, o R procurará
o objeto no ambiente global e, em seguida, nos pais do ambiente global, subindo na
árvore de ambientes, um ambiente de cada vez.
O R usará um caminho de busca ligeiramente diferente quando você chamar um
objeto de dentro de uma função. Ao executar uma função, o R cria um novo
ambiente para executar comandos. Esse ambiente será um ambiente filho do
ambiente onde a função foi originalmente definida. Este pode ser o ambiente
global, mas também pode não ser. Você pode usar esse comportamento para criar
closures, que são funções vinculadas a objetos em ambientes protegidos.
À medida que você se familiariza com o sistema de ambiente do R, pode usá-lo para
produzir resultados elegantes, como fizemos aqui. No entanto, o verdadeiro valor
de entender o sistema de ambiente vem de saber como as funções do R funcionam.
Você pode usar esse conhecimento para descobrir o que está errado quando uma
função não funciona como esperado.

8.8 Conclusão do Projeto 2


Agora você tem controle total sobre os conjuntos de dados e valores que carrega no
R. Você pode armazenar dados como objetos R, recuperar e manipular valores de
dados quando quiser e pode até mesmo prever como o R armazenará e pesquisará
seus objetos na memória do seu computador.
Você pode não ter percebido ainda, mas sua expertise o torna um usuário avançado
de dados, ampliado por computador. Você pode usar o R para salvar e trabalhar
com conjuntos de dados maiores do que conseguiria lidar de outra forma. Até
agora, trabalhamos apenas com deck, um pequeno conjunto de dados; mas você
pode usar as mesmas técnicas para trabalhar com qualquer conjunto de dados que
caiba na memória do seu computador.
No entanto, armazenar dados não é a única tarefa logística que você enfrentará
como cientista de dados. Muitas vezes, você precisará executar tarefas com seus
dados que são tão complexas ou repetitivas que são difíceis de realizar sem um
computador.
Algumas coisas podem ser feitas com funções que já existem em R e seus pacotes,
mas outras não. Você será mais versátil como cientista de dados se puder escrever
seus próprios programas para computadores acompanharem. R pode ajudá-lo com
isso. Quando estiver pronto, o Projeto 3: Caça-Níqueis lhe ensinará as habilidades
mais úteis para escrever programas em R.

Projeto 3: Caça-níqueis
As máquinas caça-níqueis são o jogo mais popular nos cassinos modernos. Se você
nunca viu uma, uma máquina caça-níqueis se assemelha a um jogo de arcade com
uma alavanca na lateral. Por uma pequena taxa, você pode puxar a alavanca e a
máquina gerará uma combinação aleatória de três símbolos. Se a combinação
correta aparecer, você pode ganhar um prêmio, talvez até o jackpot.
As máquinas caça-níqueis geram lucros fantásticos para os cassinos porque
oferecem uma taxa de pagamento muito baixa. Em muitos jogos, como Blackjack e
Roleta, as probabilidades são apenas ligeiramente favoráveis ao cassino. A longo
prazo, o cassino devolve de 97 a 98 centavos em prêmios para cada dólar gasto por
um jogador nesses jogos. Com as máquinas caça-níqueis, é comum que um cassino
devolva apenas de 90 a 95 centavos — e o cassino fica com o restante. Se isso
parece dissimulado, lembre-se de que as máquinas caça-níqueis são um dos jogos
mais populares em um cassino; poucas pessoas parecem se importar. E se você
considerar que as loterias estaduais têm taxas de pagamento muito mais próximas
de 50 centavos por dólar, as máquinas caça-níqueis não parecem tão ruins.

Neste projeto, você construirá uma máquina caça-níqueis real e funcional,


modelada com base em terminais de loteria de vídeo reais de Manitoba, Canadá. Os
terminais foram motivo de escândalo na década de 1990. Você chegará ao cerne
desse escândalo escrevendo um programa que recria as máquinas caça-níqueis. Em
seguida, fará alguns cálculos e simulações que revelarão a verdadeira taxa de
pagamento das máquinas.
Este projeto lhe ensinará como escrever programas e executar simulações em R.
Você também aprenderá como:
 Use uma estratégia prática para projetar programas
 Use instruções if e else para dizer ao R o que fazer quando
 Crie tabelas de pesquisa para encontrar valores
 Use loops for, while, e repeat para automatizar operações repetitivas
 Use métodos S3, a versão de R da Programação Orientada a Objetos
 Medir a velocidade do código R
 Escreva código R rápido e vetorizado

9 Programas
Neste capítulo, você construirá uma máquina caça-níqueis real e funcional que
poderá jogar executando uma função R. Quando terminar, você poderá jogar assim:
play()
## 0 0 DD
## $0
play()
## 7 7 7
## $80

A play função precisará fazer duas coisas. Primeiro, gerar três símbolos
aleatoriamente; e, segundo, calcular um prêmio com base nesses símbolos.
O primeiro passo é fácil de simular. Você pode gerar três símbolos aleatoriamente
com a sample função — assim como você "rolou" dois dados aleatoriamente
no Projeto 1: Dados Ponderados . A função a seguir gera três símbolos a partir de
um grupo de símbolos comuns de caça-níqueis: ouros ( DD), setes ( 7), barras triplas
( BBB), barras duplas ( BB), barras simples ( B), cerejas ( C) e zeros ( 0). Os símbolos
são selecionados aleatoriamente, e cada símbolo aparece com uma probabilidade
diferente:

get_symbols <- function() {


wheel <- c("DD", "7", "BBB", "BB", "B", "C", "0")
sample(wheel, size = 3, replace = TRUE,
prob = c(0.03, 0.03, 0.06, 0.1, 0.25, 0.01, 0.52))
}
Você pode usar get_symbols para gerar os símbolos usados na sua máquina caça-
níqueis:

get_symbols()
## "BBB" "0" "C"
get_symbols()
## "0" "0" "0"
get_symbols()
## "7" "0" "B"

get_symbols utiliza as probabilidades observadas em um grupo de terminais de


vídeo loteria de Manitoba, Canadá. Essas máquinas caça-níqueis se tornaram
brevemente controversas na década de 1990, quando um repórter decidiu testar
sua taxa de pagamento. As máquinas pareciam pagar apenas 40 centavos por dólar,
embora o fabricante alegasse que pagariam 92 centavos por dólar. Os dados
originais coletados sobre as máquinas e uma descrição da controvérsia estão
disponíveis online em um artigo de periódico de W. John Braun . A controvérsia
diminuiu quando testes adicionais mostraram que o fabricante estava correto.
As máquinas caça-níqueis de Manitoba utilizam o complexo esquema de pagamento
mostrado na Tabela 9.1 . Um jogador ganhará um prêmio se obtiver:
 Três do mesmo tipo de símbolo (exceto três zeros)
 Três barras (de variedade mista)
 Uma ou mais cerejas
Caso contrário, o jogador não recebe nenhum prêmio.

O valor monetário do prêmio é determinado pela combinação exata de símbolos e é


ainda mais modificado pela presença de diamantes. Diamantes são tratados como
"curingas", o que significa que podem ser considerados qualquer outro símbolo se
isso aumentar o prêmio do jogador. Por exemplo, um jogador que rolar a ficha 7 7
DD ganharia um prêmio por obter três setes. Há uma exceção a essa regra, no
entanto: um diamante não pode ser considerado uma cereja a menos que o jogador
também obtenha uma cereja real. Isso evita que uma jogada ruim como , 0 DD 0
seja pontuada como 0 C 0.
Diamantes também são especiais de outra forma. Cada diamante que aparece em
uma combinação dobra o valor do prêmio final. Portanto, 7 7 DD na verdade, a
pontuação seria maior que 7 7 7 . Três setes renderiam US$ 80, mas dois setes e
um diamante renderiam US$ 160. Um sete e dois ouros seriam ainda melhores,
resultando em um prêmio dobrado duas vezes, ou US$ 320. Um jackpot ocorre
quando um jogador tira DD DD DD. Então, um jogador ganha US$ 100 dobrados
três vezes, o que dá US$ 800.

Para criar sua play função, você precisará escrever um programa que possa pegar a
saída get_symbols e calcular o prêmio correto com base na Tabela 9.1 .
Em R, os programas são salvos como scripts R ou como funções. Salvaremos seu
programa como uma função chamada score. Ao terminar, você poderá usar score
para calcular um prêmio como este:
score(c("DD", "DD", "DD"))
## 800
Depois disso, será fácil criar a máquina caça-níqueis completa, assim:
play <- function() {
symbols <- get_symbols()
print(symbols)
score(symbols)
}

Op rint comando imprime sua saída na janela do console, o que


ép rint uma maneira útil de exibir mensagens de dentro do corpo de
uma função.

Você pode notar que play chama uma nova função, print. Isso ajudará play a
exibir os três símbolos da máquina caça-níqueis, já que eles não são retornados pela
última linha da função. O print comando imprime sua saída na janela do console –
mesmo que o R o chame de dentro de uma função.
No Projeto 1: Dados Ponderados , eu o incentivei a escrever todo o seu código R em
um script R, um arquivo de texto onde você pode compor e salvar código. Essa dica
será muito importante à medida que você avança neste capítulo. Lembre-se de que
você pode abrir um script R no RStudio acessando a barra de menus e clicando em
Arquivo > Novo Arquivo > Script R.

9.1 Estratégia
Pontuar resultados em caça-níqueis é uma tarefa complexa que exige um algoritmo
complexo. Você pode facilitar essa e outras tarefas de programação usando uma
estratégia simples:
Divida tarefas complexas em subtarefas simples.
Use exemplos concretos.
Descreva suas soluções em inglês e depois converta-as para R.
Vamos começar vendo como você pode dividir um programa em subtarefas que
sejam simples de trabalhar. Um programa é um conjunto de instruções passo a
passo para o seu computador seguir. Juntas, essas instruções podem realizar algo
muito sofisticado. Separadas, cada etapa individual provavelmente será simples e
direta.
Você pode facilitar a codificação identificando as etapas ou subtarefas individuais do
seu programa. Você pode então trabalhar em cada subtarefa separadamente. Se
uma subtarefa parecer complicada, tente dividi-la novamente em subtarefas pares
ainda mais simples. Muitas vezes, você pode reduzir um programa em R a subtarefas
tão simples que cada uma delas pode ser executada com uma função preexistente.
Os programas R contêm dois tipos de subtarefas: etapas sequenciais e casos
paralelos.

9.1.1 Etapas sequenciais

Uma maneira de subdividir um programa é em uma série de etapas sequenciais.


A play função adota a abordagem mostrada na Figura 9.1 . Primeiro, ela gera três
símbolos (etapa 1), depois os exibe na janela do console (etapa 2) e, em seguida, os
pontua (etapa 3):
play <- function() {
# step 1: generate symbols
symbols <- get_symbols()
# step 2: display the symbols
print(symbols)
# step 3: score the symbols
score(symbols)
}

Para que o R execute etapas em sequência, coloque as etapas uma após a outra em
um script R ou corpo de função.

Figura 9.1: A função de reprodução usa uma série de etapas.

9.1.2 Casos Paralelos


Outra maneira de dividir uma tarefa é identificar grupos de casos semelhantes
dentro dela. Algumas tarefas exigem algoritmos diferentes para diferentes grupos
de entrada. Se você conseguir identificar esses grupos, poderá elaborar seus
algoritmos um de cada vez.
Por exemplo, score será necessário calcular o prêmio de uma maneira
se symbols contiver três de um tipo (nesse caso, score será necessário combinar
o símbolo comum a um prêmio). Score Será necessário calcular o prêmio de uma
segunda maneira se os símbolos forem todos barras (nesse caso, score pode-se
atribuir um prêmio de $ 5). E, finalmente, score será necessário calcular o prêmio
de uma terceira maneira se os símbolos não contiverem três de um tipo ou todas as
barras (nesse caso, score deve-se contar o número de cerejas presentes). Score
Nunca usará todos os três algoritmos ao mesmo tempo; sempre escolherá apenas
um algoritmo para executar com base na combinação de símbolos.
Diamantes complicam tudo isso porque podem ser tratados como curingas. Vamos
ignorar isso por enquanto e nos concentrar no caso mais simples, em que os
diamantes dobram o prêmio, mas não são curingas. Score pode dobrar o prêmio
conforme necessário após executar um dos seguintes algoritmos, conforme
mostrado na Figura 9.2 .
Adicionar os score casos às play etapas revela uma estratégia para o programa
completo da máquina caça-níqueis, conforme mostrado na Figura 9.3 .
Já resolvemos os primeiros passos desta estratégia. Nosso programa pode obter três
símbolos de caça-níqueis com a get_symbols função. Em seguida, ele pode exibir
os símbolos com a print função. Agora, vamos examinar como o programa pode
lidar com os casos de pontuação paralela

Figura 9.2:
A função de pontuação deve distinguir entre casos paralelos .
Figura 9.3: A simulação completa da máquina caça-níqueis envolverá subtarefas organizadas em
série e em paralelo.

9.2 Instruções if
Ligar casos em paralelo requer um pouco de estrutura; seu programa se depara com
uma bifurcação sempre que precisa escolher entre os casos. Você pode ajudar o
programa a navegar nessa bifurcação com uma if instrução.
Uma if instrução diz ao R para realizar uma determinada tarefa para um
determinado caso. Em inglês, você diria algo como: "Se isto for verdade, faça
aquilo". Em R, você diria:
if (this) {
that
}

O this objeto deve ser um teste lógico ou uma expressão R que seja avaliada como
um único TRUE ou FALSE. Se thisfor avaliado como TRUE, o R executará todo o
código que aparecer entre as chaves que seguem a if instrução (ou seja, entre
os símbolos {e ). Se for avaliado como , o R ignorará o código entre as chaves sem
executá-lo.} this FALSE
Por exemplo, você pode escrever uma if declaração que garanta que algum
objeto, num, seja positivo:
if (num < 0) {
num <- num * -1
}

Se num < 0 for TRUE, R será multiplicado num por menos um, o que resultará
em num positivo:
num <- -2
if (num < 0) {
num <- num * -1
}
num
## 2

Se num < 0 for FALSE, R não fará nada e num permanecerá como está —
positivo (ou zero):
num <- 4
if (num < 0) {
num <- num * -1
}
num
## 4

A condição de uma if instrução deve ser avaliada como um único TRUE ou FALSE.
Se a condição criar um vetor de TRUE s e FALSE s (o que é mais fácil de fazer do
que você imagina), sua if instrução exibirá uma mensagem de aviso e usará apenas
o primeiro elemento do vetor. Lembre-se de que você pode condensar vetores de
valores lógicos em um único TRUE ou FALSE com as funções any e all.
Você não precisa limitar suas if instruções a uma única linha de código; você pode
incluir quantas linhas quiser entre as chaves. Por exemplo, o código a seguir usa
muitas linhas para garantir que num seja positivo. As linhas adicionais imprimem
algumas instruções informativas se num começar como um número negativo. O R
pulará todo o bloco de código — print instruções e tudo — se num começar como
um número positivo:
num <- -1
if (num < 0) {
print("num is negative.")
print("Don't worry, I'll fix it.")
num <- num * -1
print("Now num is positive.")
}
## "num is negative."
## "Don't worry, I'll fix it."
## "Now num is positive."
num
## 1

Tente os seguintes testes para desenvolver sua compreensão das if afirmações.

Exercício 9.1 (Questionário A) O que isso retornará?


x <- 1
if (3 == 3) {
x <- 2
}
X

Solução. O código retornará o número 2. x começa como 1, e então R encontra a if


instrução. Como a condição é avaliada como TRUE, R executará x <- 2, alterando
o valor de x.

Exercício 9.2 (Questionário B) O que isso retornará?


x <- 1
if (TRUE) {
x <- 2
}
x
Solução. Este código também retornará o número 2. Funciona da mesma forma que
o código do Quiz A, exceto que a condição nesta instrução já é TRUE. R nem precisa
avaliá-la. Como resultado, o código dentro da if instrução será executado e x será
definido como 2.

Exercício 9.3 (Questionário C) O que isso retornará?


x <- 1
if (x == 1) {
x <- 2
if (x == 1) {
x <- 3
}
}
x

Solução. Mais uma vez, o código retornará o número 2. X começa como 1, e a


condição da primeira if instrução será avaliada como TRUE, o que faz com que R
execute o código no corpo da if instrução. Primeiro, R define x igual a 2, então R
avalia a segunda if instrução, que está no corpo da primeira. Desta vez, x == 1
será avaliada como FALSE porque x agora é igual a 2. Como resultado, R ignora x
<- 3 e sai de ambas if as instruções.

9.3 Instruções else


If As instruções dizem ao R o que fazer quando sua condição for verdadeira , mas
você também pode dizer ao R o que fazer quando a condição for falsa . else É uma
contrapartida if que estende uma if instrução para incluir um segundo caso. Em
inglês, você diria: "If this is true, do plan A; else do plan B" (Se isso for
verdade, faça o plano A; caso contrário, faça o plano B). Em R, você diria:
if (this) {
Plan A
} else {
Plan B
}
Quando this avaliado como TRUE, o R executará o código no primeiro conjunto de
chaves, mas não o código no segundo. Quando this avaliado como FALSE, o R
executará o código no segundo conjunto de chaves, mas não o primeiro. Você pode
usar esse arranjo para cobrir todos os casos possíveis. Por exemplo, você pode
escrever um código que arredonde um decimal para o inteiro mais próximo.

Comece com um decimal:


a <- 3.14

Em seguida, isole o componente decimal com trunc:


dec <- a - trunc(a)
dec
## 0.14

Trunc pega um número e retorna apenas a parte do número que


aparece à esquerda da casa decimal (ou seja, a parte inteira do
número).

a – trunc (a) é uma maneira conveniente de retornar a parte


decimal de a.

Em seguida, use uma if else árvore para arredondar o número (para cima ou para
baixo):
if (dec >= 0.5) {
a <- trunc(a) + 1
} else {
a <- trunc(a)
}
a
## 3
Se a sua situação tiver mais de dois casos mutuamente exclusivos, você pode
encadear várias instruções if and else adicionando uma nova if instrução
imediatamente após else. Por exemplo:

a <- 1
b <- 1
if (a > b) {
print("A wins!")
} else if (a < b) {
print("B wins!")
} else {
print("Tie.")
}
## "Tie."

O R analisará as if condições até que uma seja avaliada como TRUE, então
ignorará quaisquer cláusulas if and restantes else na árvore. Se nenhuma
condição for avaliada como TRUE, o R executará a else instrução final.
Se duas if instruções descrevem eventos mutuamente exclusivos, é melhor uni if -
las com um else if do que listá-las separadamente. Isso permite que o R ignore a
segunda if instrução sempre que a primeira retornar um TRUE, o que economiza
trabalho.
Você pode usar if e else para vincular as subtarefas na sua função de caça-níqueis.
Abra um novo script R e copie este código para ele. O código será o esqueleto da
nossa score função final. Compare-o com o fluxograma da score Figura 9.2 :

if ( # Case 1: all the same <1>) {


prize <- # look up the prize <3>
} else if ( # Case 2: all bars <2> ) {
prize <- # assign $5 <4>
} else {
# count cherries <5>
prize <- # calculate a prize <7>
}
# count diamonds <6>
# double the prize if necessary <8>
Nosso esqueleto está bastante incompleto; há muitas seções que são apenas
comentários de código em vez de código real. No entanto, reduzimos o programa a
oito subtarefas simples:
<1> - Teste se os símbolos são três do mesmo tipo.
<2> - Teste se todos os símbolos são barras.
<3> - Procure o prêmio para três do mesmo tipo com base no símbolo comum.
<4> - Atribua um prêmio de $ 5.
<5> - Conte o número de cerejas.
<6> - Conte o número de ouros.
<7> - Calcule um prêmio com base no número de cerejas.
<8> - Ajuste o prêmio para ouros.

Se desejar, você pode reorganizar seu fluxograma em torno dessas tarefas, como na
Figura 9.4 . O fluxograma descreverá a mesma estratégia, mas de forma mais
precisa. Usarei um losango para simbolizar uma if else decisão.

Figura 9.4: a pontuação pode navegar por três casos com duas decisões if else. Também podemos
dividir algumas de nossas tarefas em duas etapas.

Agora podemos trabalhar nas subtarefas uma de cada vez, adicionando código R à if
árvore à medida que avançamos. Cada subtarefa será fácil de resolver se você criar
um exemplo concreto para trabalhar e tentar descrever uma solução em inglês
antes de codificar em R.
A primeira subtarefa pede que você teste se os símbolos são trincas. Como você
deve começar a escrever o código para esta subtarefa?
Você sabe que a função final scoreserá algo como isto:
score <- function(symbols) {
# calculate a prize
prize
}

Seu argumento, symbols, será a saída de get_symbols, um vetor que contém


três cadeias de caracteres. Você poderia começar a escrever score como eu escrevi,
definindo um objeto chamado score e, em seguida, preenchendo lentamente o
corpo da função. No entanto, isso seria uma má ideia. A função final terá oito partes
separadas e não funcionará corretamente até que todas essas partes sejam escritas
(e funcionem corretamente). Isso significa que você teria que escrever a score
função inteira antes de poder testar qualquer uma das subtarefas. Se score não
funcionar — o que é muito provável — você não saberá qual subtarefa precisa ser
corrigida.
Você pode economizar tempo e dores de cabeça se concentrar em uma subtarefa de
cada vez. Para cada subtarefa, crie um exemplo concreto para testar seu código. Por
exemplo, você sabe que score precisará trabalhar em um vetor
chamado symbols que contém três sequências de caracteres. Se você criar um
vetor real chamado symbols, poderá executar o código de muitas das suas
subtarefas no vetor à medida que avança:

symbols <- c ("7", "7", "7")

Se um trecho de código não funcionar em symbols, você saberá que precisa


corrigi-lo antes de prosseguir. Você pode alterar o valor de symbols subtask para
subtask para garantir que seu código funcione em todas as situações:

symbols <- c("B", "BB", "BBB")


symbols <- c("C", "DD", "0")

Combine suas subtarefas em uma score função somente quando cada subtarefa
funcionar em um exemplo concreto. Se você seguir este plano, gastará mais tempo
usando suas funções e menos tempo tentando descobrir por que elas não
funcionam.
Depois de definir um exemplo concreto, tente descrever como você executará a
subtarefa em inglês. Quanto mais precisamente você descrever sua solução, mais
fácil será escrever seu código R.
Nossa primeira subtarefa nos pede para "testar se os símbolos são trincas". Esta
frase não me sugere nenhum código R útil. No entanto, eu poderia descrever um
teste mais preciso para trincas: três símbolos serão iguais se o primeiro símbolo for
igual ao segundo e o segundo símbolo for igual ao terceiro. Ou, ainda mais
precisamente:
Um vetor nomeado symbols conterá três do mesmo símbolo se o primeiro
elemento de symbols for igual ao segundo elemento de symbols e o segundo
elemento de symbols for igual ao terceiro elemento de symbols .

Exercício 9.4 (Escreva um Teste) Transforme a declaração anterior em um teste


lógico escrito em R. Use seu conhecimento de testes lógicos, operadores booleanos
e subconjuntos da Notação R. O teste deve funcionar com o vetor symbolse
retornar umTRUE if, e somente if, cada elemento em symbols for o mesmo.
Certifique-se de testar seu código em symbols.
Solução. Aqui estão algumas maneiras de testar se a solução symbols contém três
símbolos iguais. O primeiro método é semelhante à sugestão em inglês acima, mas
existem outras maneiras de fazer o mesmo teste. Não há resposta certa ou errada,
desde que sua solução funcione, o que é fácil de verificar porque você criou um
vetor chamado symbols:

symbols
## "7" "7" "7"
symbols[1] == symbols[2] & symbols[2] == symbols[3]
## TRUE
symbols[1] == symbols[2] & symbols[1] == symbols[3]
## TRUE
all(symbols == symbols[1])
## TRUE
À medida que seu vocabulário de funções do R se amplia, você pensará em mais
maneiras de realizar tarefas básicas. Um método que eu gosto para verificar trincas
é:
length(unique(symbols) == 1)

A unique função retorna todos os termos únicos que aparecem em um vetor. Se o


seu symbols vetor contiver três do mesmo tipo (ou seja, um termo único que
aparece três vezes), então unique (symbols) retornará um vetor de
comprimento 1.
Agora que você tem um teste funcional, você pode adicioná-lo ao seu script de caça-
níqueis:
same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]
if (same) {
prize <- # look up the prize
} else if ( # Case 2: all bars ) {
prize <- # assign $5
} else {
# count cherries
prize <- # calculate a prize
}
# count diamonds
# double the prize if necessary

&& e | | se comportam como & e, | mas podem, às vezes, ser mais


eficientes. Os operadores double não avaliarão o segundo teste em um
par de testes se o primeiro teste deixar o resultado claro.
Por exemplo, se symbols[ 1 ] não for igual symbols
[ 2 ] na próxima expressão, && não avaliará symbols [
2 ] = = symbols [ 3 ]; ele pode retornar
imediatamente a FALSE para toda a expressão (porque ambos FALSE
& TRUE e FALSE & FALSE são avaliados como FALSE ). Essa
eficiência pode acelerar seus programas; no entanto, operadores
double não são apropriados em todos os lugares. && e | | não são
vetorizados, o que significa que só podem lidar com um único teste
lógico de cada lado do operador.
O segundo caso de prêmio ocorre quando todos os símbolos são um tipo de barra,
por exemplo, B, BB, e BBB. Vamos começar criando um exemplo concreto para
trabalhar:
symbols <- c("B", "BBB", "BB")

Exercício 9.5 (Teste para Todas as Barras) Use os operadores lógicos e booleanos do
R para escrever um teste que determinará se um vetor nomeado symbols contém
apenas símbolos que são um tipo de barra. Verifique se o seu teste funciona com o
nosso symbols vetor de exemplo. Lembre-se de descrever como o teste deve
funcionar em inglês e, em seguida, converter a solução para R.
Solução. Como acontece com muitas coisas em R, existem várias maneiras de testar
se symbols contém todas as barras. Por exemplo, você poderia escrever um teste
bem longo que usa vários operadores booleanos, como este:

symbols[1] == "B" | symbols[1] == "BB" | symbols[1] == "BBB" &


symbols[2] == "B" | symbols[2] == "BB" | symbols[2] == "BBB" &
symbols[3] == "B" | symbols[3] == "BB" | symbols[3] == "BBB"
## TRUE

No entanto, esta não é uma solução muito eficiente, pois o R precisa executar nove
testes lógicos (e você precisa digitá-los). Muitas vezes, é possível substituir vários |
operadores por um único % in %. Além disso, você pode verificar se um teste é
verdadeiro para cada elemento de um vetor com all. Essas duas alterações
encurtam o código anterior para:
all(symbols %in% c("B", "BB", "BBB"))
## TRUE

Vamos adicionar este código ao nosso script:


same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]
bars <- symbols %in% c("B", "BB", "BBB")
if (same) {
prize <- # look up the prize
} else if (all(bars)) {
prize <- # assign $5
} else {
# count cherries
prize <- # calculate a prize
}
# count diamonds
# double the prize if necessary

Você deve ter notado que dividi este teste em duas etapas. bars Isso all (bars) é
apenas uma questão de preferência pessoal. Sempre que possível, gosto de escrever
meu código de forma que possa ser lido com nomes de funções e objetos que
transmitam o que eles fazem.
Você também deve ter notado que nosso teste para o Caso 2 capturará alguns
símbolos que deveriam estar no Caso 1 porque eles contêm três do mesmo tipo:

symbols <- c("B", "B", "B")


all(symbols %in% c("B", "BB", "BBB"))
## TRUE
Isso não será um problema, no entanto, porque conectamos nossos casos com else
if na if árvore. Assim que o R chegar a um caso que avalie TRUE, ele pulará o
restante da árvore. Pense desta forma: cada um else diz ao R para executar o
código seguinte apenas se nenhuma das condições anteriores tiver sido atendida .
Portanto, quando tivermos três barras do mesmo tipo, o R avaliará o código do Caso
1 e, em seguida, pulará o código do Caso 2 (e do Caso 3).
Nossa próxima subtarefa é atribuir um prêmio para symbols. Quando o symbols
vetor contém três símbolos iguais, o prêmio dependerá de qual símbolo houver três.
Se houver três DD s, o prêmio será de R$ 100; se houver três 7s, o prêmio será de R$
80; e assim por diante.
Isso sugere outra if árvore. Você poderia atribuir um prêmio com um código como
este:
if (same) {
symbol <- symbols[1]
if (symbol == "DD") {
prize <- 800
} else if (symbol == "7") {
prize <- 80
} else if (symbol == "BBB") {
prize <- 40
} else if (symbol == "BB") {
prize <- 5
} else if (symbol == "B") {
prize <- 10
} else if (symbol == "C") {
prize <- 10
} else if (symbol == "0") {
prize <- 0
}
}
Embora este código funcione, é um pouco longo para escrever e ler, e pode exigir
que o R execute vários testes lógicos antes de entregar o prêmio correto. Podemos
fazer melhor com um método diferente.

9.4 Tabelas de consulta


Muitas vezes, em R, a maneira mais simples de fazer algo envolve a subdivisão.
Como você poderia usar a subdivisão aqui? Como você sabe a relação exata entre os
símbolos e seus prêmios, pode criar um vetor que capture essa informação. Esse
vetor pode armazenar símbolos como nomes e valores de prêmios como elementos:
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
payouts
## DD 7 BBB BB B C 0
## 100 80 40 25 10 10 0

Agora você pode extrair o prêmio correto para qualquer símbolo subdividindo o
vetor com o nome do símbolo:
payouts["DD"]
## DD
## 100
payouts["B"]
## B
## 10

Se você quiser deixar o nome do símbolo ao criar um subconjunto, você pode


executar a unname função na saída:
unname(payouts["DD"])
## 100

unname retorna uma cópia de um objeto com o atributo names removido.

payouts é um tipo de tabela de consulta , um objeto R que você pode usar para
pesquisar valores. A subdivisão payouts fornece uma maneira simples de
encontrar o valor de um símbolo. Não requer muitas linhas de código e realiza a
mesma quantidade de trabalho, independentemente de o seu símbolo ser DD
ou 0. Você pode criar tabelas de consulta em R criando objetos nomeados que
podem ser subdivididos de maneiras inteligentes.
Infelizmente, nosso método não é totalmente automático; precisamos informar ao R
qual símbolo procurar em payouts. Ou será que não? O que aconteceria se você
subdividisse payouts por symbols [ 1 ] ? Experimente:
symbols <- c("7", "7", "7")
symbols[1]
## "7"
payouts[symbols[1]]
## 7
## 80
symbols <- c("C", "C", "C")
payouts[symbols[1]]
## C
## 10

Você não precisa saber o símbolo exato a ser pesquisado, pois pode dizer ao R para
pesquisar qualquer símbolo que esteja em symbols. Você pode encontrar esse
símbolo com symbols [ 1 ], symbols [ 2 ], ou symbols [ 3 ], pois cada um
contém o mesmo símbolo neste caso. Agora você tem uma maneira simples e
automatizada de calcular o prêmio quando symbols contém uma trinca. Vamos
adicioná-la ao nosso código e, em seguida, analisar o Caso 2:

same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]


bars <- symbols %in% c("B", "BB", "BBB")
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- # assign $5
} else {
# count cherries
prize <- # calculate a prize
}
# count diamonds
# double the prize if necessary

O caso 2 ocorre quando os símbolos são todos barras. Nesse caso, o prêmio será de
US$ 5, o que é fácil de atribuir:

same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]


bars <- symbols %in% c("B", "BB", "BBB")
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- 5
} else {
# count cherries
prize <- # calculate a prize
}
# count diamonds
# double the prize if necessary

Agora podemos trabalhar no último caso. Aqui, você precisa saber quantas cerejas
estão na caixa symbols antes de calcular o prêmio.

Exercício 9.6 (Encontre C's) Como você pode dizer quais elementos de um vetor
chamado symbols são aC? Crie um teste e experimente.

Desafio
Como você contaria o número de Cs em um vetor
chamado symbols? Lembre-se das regras de coerção de
R.

Solução. Como sempre, vamos trabalhar com um exemplo real:


symbols <- c("C", "DD", "C")

Uma maneira de testar as cerejas seria verificar quais símbolos, se houver, são C:
symbols == "C"
## TRUE FALSE TRUE

Seria ainda mais útil contar quantos símbolos são cerejas. Você pode fazer isso
com sum, que espera uma entrada numérica, não lógica. Sabendo disso, R
converterá s TRUE e FALSE s para 1s e 0 s antes de realizar a soma. Como
resultado, sum retornará o número de TRUEs, que também é o número de
cerejas:
sum(symbols == "C")
## 2
Você pode usar o mesmo método para contar o número de diamantes
em symbols:
sum(symbols == "DD")
## 1

Vamos adicionar essas duas subtarefas ao esqueleto do programa:

same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]


bars <- symbols %in% c("B", "BB", "BBB")
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- 5
} else {
cherries <- sum(symbols == "C")
prize <- # calculate a prize
}
diamonds <- sum(symbols == "DD")
# double the prize if necessary

Como o Caso 3 aparece mais abaixo na if árvore do que os Casos 1 e 2, o código no


Caso 3 só será aplicado a jogadores que não tiverem uma trinca ou todas as barras.
De acordo com o esquema de pagamento da máquina caça-níqueis, esses jogadores
ganharão US$ 5 se tiverem duas cerejas e US$ 2 se tiverem uma cereja. Se o jogador
não tiver cerejas, ele ganha um prêmio de US$ 0. Não precisamos nos preocupar
com três cerejas, pois esse resultado já está coberto no Caso 1.
Assim como no Caso 1, você poderia escrever uma if árvore que manipulasse cada
combinação de cerejas, mas, assim como no Caso 1, esta seria uma solução
ineficiente:
if (cherries == 2) {
prize <- 5
} else if (cherries == 1) {
prize <- 2
} else {}
prize <- 0
}

Mais uma vez, acredito que a melhor solução envolverá a subdivisão. Se você se
sentir ambicioso, pode tentar descobrir essa solução sozinho, mas aprenderá com a
mesma rapidez se trabalhar mentalmente na seguinte solução proposta.
Sabemos que nosso prêmio deve ser de R$ 0 se não tivermos cerejas, R$ 2 se
tivermos uma cereja e R$ 5 se tivermos duas cerejas. Você pode criar um vetor que
contenha essas informações. Esta será uma tabela de consulta bem simples:
c(0, 2, 5)

Agora, como no Caso 1, você pode subdividir o vetor para obter o prêmio correto.
Neste caso, os prêmios não são identificados pelo nome de um símbolo, mas pelo
número de cerejas presentes. Temos essa informação? Sim, ela está armazenada
em cherries. Podemos usar a subdivisão básica de inteiros para obter o prêmio
correto da tabela de consulta anterior, por exemplo, c ( 0, 2, 5 ) [ 1 ].
cherries não é exatamente adequado para subconjuntos inteiros, pois pode conter
um zero, mas isso é fácil de corrigir. Podemos subconjuntos com cherries + 1.
Agora, quando cherries é igual a zero, temos:

cherries + 1
## 1
c(0, 2, 5)[cherries + 1]
## 0

Quando cherries é igual a um, temos:


cherries + 1
## 2
c(0, 2, 5)[cherries + 1]
## 2

E quando cherries é igual a dois, temos:


cherries + 1
## 3
c(0, 2, 5)[cherries + 1]
## 5

Examine essas soluções até ter certeza de que elas retornam o prêmio correto para
cada número de cerejas. Em seguida, adicione o código ao seu script, da seguinte
maneira:

same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]


bars <- symbols %in% c("B", "BB", "BBB")
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- 5
} else {
cherries <- sum(symbols == "C")
prize <- c(0, 2, 5)[cherries + 1]
}
diamonds <- sum(symbols == "DD")
# double the prize if necessary

TABELAS DE CONSULTA VERSUS ÁRVORES IF


Esta é a segunda vez que criamos uma tabela de consulta para evitar a
criação de uma if árvore. Por que essa técnica é útil e por que ela
continua aparecendo? Muitas if árvores em R são essenciais. Elas
fornecem uma maneira útil de instruir o R a usar algoritmos diferentes
em casos diferentes. No entanto, if árvores não são apropriadas em
todos os lugares.
if Árvores têm algumas desvantagens. Primeiro, elas exigem que o R
execute vários testes à medida que avança na ifárvore, o que pode
gerar trabalho desnecessário. Segundo, como você verá em Speed ,
pode ser difícil usar if árvores em código vetorizado, um estilo de
código que aproveita os pontos fortes da programação do R para criar
programas rápidos. Tabelas de consulta não sofrem de nenhuma dessas
desvantagens.
Você não poderá substituir todas if as árvores por uma tabela de
consulta, nem deveria. No entanto, geralmente você pode usar tabelas
de consulta para evitar a atribuição de variáveis com if árvores. Como
regra geral, use uma if árvore se cada ramo da árvore executar um
código diferente . Use uma tabela de consulta se cada ramo da árvore
atribuir apenas um valor diferente .
Para converter uma if árvore em uma tabela de consulta, identifique os
valores a serem atribuídos e armazene-os em um vetor. Em seguida,
identifique os critérios de seleção usados nas condições da if árvore. Se
as condições usarem cadeias de caracteres, dê nomes aos seus vetores
e use subconjuntos baseados em nomes. Se as condições usarem
números inteiros, use subconjuntos baseados em números inteiros.

A subtarefa final é dobrar o prêmio uma vez para cada diamante presente. Isso
significa que o prêmio final será um múltiplo do prêmio atual. Por exemplo, se não
houver diamantes presentes, o prêmio será:
prize * 1 #1=2^0

Se houver um diamante presente, ele será:


prize * 2 #2=2^1

Se houver dois diamantes, será:


prize * 4 #4=2^2
E se houver três ouros, será:
prize * 8 #8=2^3

Você consegue pensar em uma maneira fácil de lidar com isso? Que tal algo
semelhante a estes exemplos?

Exercício 9.7 (Ajuste para Diamantes) Escreva um método para ajuste prize com
base em diamonds. Descreva uma solução em inglês primeiro e, em seguida,
escreva seu código.
Solução. Aqui está uma solução concisa inspirada no padrão anterior. O prêmio
ajustado será igual a:
prize * 2 ^ diamonds

o que nos dá nosso script final score:


same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]
bars <- symbols %in% c("B", "BB", "BBB")
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- 5
} else {
cherries <- sum(symbols == "C")
prize <- c(0, 2, 5)[cherries + 1]
}
diamonds <- sum(symbols == "DD")
prize * 2 ^ Diamonds
9.5 Comentários de código
Agora você tem um script de partitura funcional que pode ser salvo em uma função.
Antes de salvar seu script, considere adicionar comentários ao seu código com um #.
Comentários podem facilitar a compreensão do seu código, explicando por que ele
faz o que faz. Você também pode usar comentários para dividir programas longos
em partes que possam ser escaneadas. Por exemplo, eu incluiria três comentários
no score código:
# identify case
same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]
bars <- symbols %in% c("B", "BB", "BBB")
# get prize
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- 5
} else {
cherries <- sum(symbols == "C")
prize <- c(0, 2, 5)[cherries + 1]
}
# adjust for diamonds
diamonds <- sum(symbols == "DD")
prize * 2 ^ diamonds

Agora que cada parte do seu código funciona, você pode encapsulá-la em uma
função com os métodos que aprendeu em " Escrevendo suas próprias funções" . Use
a opção "Extrair função" do RStudio na barra de menus em "Código" ou use
a function função.
Certifique-se de que a última linha da função retorne um resultado (e retorna) e
identifique quaisquer argumentos usados pela sua função. Muitas vezes, os
exemplos concretos que você usou para testar seu código, como symbols, se
tornarão os argumentos da sua função. Execute o seguinte código para começar a
usar a score função:
score <- function (symbols) {
# identify case
same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]
bars <- symbols %in% c("B", "BB", "BBB")
# get prize
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- 5
} else {
cherries <- sum(symbols == "C")
prize <- c(0, 2, 5)[cherries + 1]
}
# adjust for diamonds
diamonds <- sum(symbols == "DD")
prize * 2 ^ diamonds
}

Depois de definir a scorefunção, play ela também funcionará:


play <- function() {
symbols <- get_symbols()
print(symbols)
score(symbols)
}
Agora é fácil jogar na máquina caça-níqueis:
play()
## "0" "BB" "B"
## 0
play()
## "DD" "0" "B"
## 0
play()
## "BB" "BB" "B"
## 25

9.6 Resumo
Um programa em R é um conjunto de instruções para o seu computador seguir,
organizado em uma sequência de etapas e casos. Isso pode fazer com que os
programas pareçam simples, mas não se deixe enganar: você pode criar resultados
complexos com a combinação certa de etapas (e casos) simples.
Como programador, você tem mais chances de ser enganado da maneira oposta.
Um programa pode parecer impossível de escrever quando você sabe que ele
precisa fazer algo impressionante. Não entre em pânico nessas situações. Divida a
tarefa em tarefas simples e, em seguida, divida-as novamente. Você pode visualizar
a relação entre as tarefas com um fluxograma, se isso ajudar. Em seguida, trabalhe
nas subtarefas, uma de cada vez. Descreva as soluções em inglês e, em seguida,
converta-as para código R. Teste cada solução com exemplos concretos à medida
que avança. Assim que cada uma das suas subtarefas funcionar, combine o código
em uma função que você possa compartilhar e reutilizar.
O R fornece ferramentas que podem ajudar você a fazer isso. Você pode gerenciar
casos com instruções if e else. Você pode criar uma tabela de consulta com objetos
e subconjuntos. Você pode adicionar comentários de código com #. E você pode
salvar seus programas como uma função com function.
Muitas vezes, as coisas dão errado quando as pessoas escrevem programas. Caberá
a você encontrar a fonte de quaisquer erros que ocorram e corrigi-los. Deve ser fácil
encontrar a fonte dos seus erros se você usar uma abordagem passo a passo para
escrever funções, escrevendo — e depois testando — um bit de cada vez. No
entanto, se a fonte de um erro não for clara para você, ou se você estiver
trabalhando com grandes blocos de código não testados, considere usar as
ferramentas de depuração integradas do R, descritas em Depurando Código R.
Os próximos dois capítulos apresentarão mais ferramentas que você pode usar em
seus programas. À medida que dominar essas ferramentas, você achará mais fácil
escrever programas em R que permitem fazer o que quiser com seus dados. No S3 ,
você aprenderá a usar o sistema S3 do R, uma mão invisível que molda muitas
partes do R. Você usará o sistema para criar uma classe personalizada para a saída
da sua máquina caça-níqueis e informará ao R como exibir objetos que possuem a
sua classe.

10 S3
Você deve ter notado que os resultados da sua máquina caça-níqueis não aparecem
como eu prometi. Sugeri que a máquina caça-níqueis exibisse os resultados assim:
play()
## 0 0 DD
## $ 0

Mas a máquina atual exibe seus resultados em um formato menos bonito:


play()
## "0" "0" "DD"
## 0

Além disso, a máquina caça-níqueis usa um hack para exibir símbolos


(chamamos print de "de dentro" play). Como resultado, os símbolos não
acompanham o resultado do seu prêmio se você salvá-lo:
one_play <- play()
## "B" "0" "B"
one_play
## 0

Você pode corrigir ambos os problemas com o sistema S3 do R.

10.1 O Sistema S3
S3 refere-se a um sistema de classes incorporado ao R. O sistema controla como o R
manipula objetos de diferentes classes. Certas funções do R pesquisam a classe S3
de um objeto e, em resposta, se comportam de maneira diferente.
A print função é assim. Ao imprimir um vetor numérico, print será exibido um
número:
num <- 1000000000
print(num)
## 1000000000

Mas se você fornecer esse número, a classe S3 POSIXct seguida


por POSIXt, print exibirá um horário:
class(num) <- c("POSIXct", "POSIXt")
print(num)
## "2001-09-08 19:46:40 CST"

Se você usa objetos com classes — e usa — você se deparará com o sistema S3 do R.
O comportamento do S3 pode parecer estranho no início, mas é fácil de prever
quando você se familiariza com ele.
O sistema S3 do R é construído em torno de três componentes: atributos
(especialmente o class atributo), funções genéricas e métodos.

10.2 Atributos
Em Atributos , você aprendeu que muitos objetos R vêm com atributos, informações
extras que recebem um nome e são anexadas ao objeto. Os atributos não afetam os
valores do objeto, mas permanecem como um tipo de metadados que o R pode usar
para lidar com o objeto. Por exemplo, um dataframe armazena os nomes de suas
linhas e colunas como atributos. Os dataframes também armazenam sua
classe, "[Link]", como um atributo.
Você pode ver os atributos de um objeto com attribute. Se você
executar atribute no deck quadro de dados criado no Projeto 2: Cartas de
Baralho , verá:

attributes(deck)
## $names
## [1] "face" "suit" "value"
##
## $class
## [1] "[Link]"
##
## $[Link]
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
## [20] 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
## [37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

O R vem com muitas funções auxiliares que permitem definir e acessar os atributos
mais comuns usados em R. Você já conheceu as funções names, dim, e , cada uma
delas trabalhando com um atributo com o mesmo nome. No entanto, o R também
possui , e muitas outras funções auxiliares baseadas em atributos. Você pode usar
qualquer uma destas funções para recuperar o valor de um atributo:class
[Link] levels
[Link](deck)
## [1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13"
## [14] "14" "15" "16" "17" "18" "19" "20" "21" "22" "23" "24" "25"
"26"
## [27] "27" "28" "29" "30" "31" "32" "33" "34" "35" "36" "37" "38"
"39"
## [40] "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" "50" "51"
"52"

ou para alterar o valor de um atributo:

[Link](deck) <- 101:152

ou dar a um objeto um atributo totalmente novo:

levels(deck) <- c("level 1", "level 2", "level 3")

attributes(deck)
## $names
## [1] "face" "suit" "value"
##
## $class
## [1] "[Link]"
##
## $[Link]
## [1] 101 102 103 104 105 106 107 108 109 110 111 112 113
114 115 116 117
## [18] 118 119 120 121 122 123 124 125 126 127 128 129 130
131 132 133 134
## [35] 135 136 137 138 139 140 141 142 143 144 145 146 147
148 149 150 151
## [52] 152
##
## $levels
## [1] "level 1" "level 2" "level 3"

O R é muito permissivo quando se trata de atributos. Ele permite que você adicione
quaisquer atributos que desejar a um objeto (e geralmente os ignora). O R só
reclama quando uma função precisa encontrar um atributo e ele não está lá.
Você pode adicionar qualquer atributo geral a um objeto com attr; você também
pode usar attr para consultar o valor de qualquer atributo de um objeto. Vamos ver
como isso funciona com one_play, o resultado de jogar nossa máquina caça-
níqueis uma vez:

one_play <- play( )


one_play
## 0

attributes(one_play)
## NULL

attr recebe dois argumentos: um objeto R e o nome de um atributo (como uma


sequência de caracteres). Para atribuir ao objeto R um atributo com o nome
especificado, salve um valor na saída de attr. Vamos atribuir one_play um
atributo chamado symbols, que contém um vetor de sequências de caracteres:
attr(one_play, "symbols") <- c ("B", "0", "B")

attributes(one_play)
## $symbols
## [1] "B" "0" "B"

Para consultar o valor de qualquer atributo, forneça attr um objeto R e o nome do


atributo que você gostaria de consultar:

attr(one_play, "symbols")
## "B" "0" "B"

Se você atribuir um atributo a um vetor atômico, como one_play, o R geralmente


exibirá o atributo abaixo dos valores do vetor. No entanto, se o atributo alterar a
classe do vetor, o R poderá exibir todas as informações do vetor de uma nova
maneira (como vimos com POSIXct objetos):
one_play
## [1] 0
## attr(,"symbols")
## [1] "B" "0" "B"

O R geralmente ignora os atributos de um objeto, a menos que você dê a eles um


nome que uma função R procure, como names ou class. Por exemplo, o R ignorará
o symbols atributo de one_play conforme você manipula one_play:
one_play + 1
## 1
## attr(,"symbols")
## "B" "0" "B"

Exercício 10.1 (Adicionar um Atributo) Modifique play para retornar um prêmio


que contém os símbolos associados a ele como um atributo chamado symbols.
Remova a chamada redundante para print (symbols):
play <- function() {
symbols <- get_symbols()
print(symbols)
score(symbols)
}

Solução. Você pode criar uma nova versão de play capturando a saída de score
(symbols) e atribuindo um atributo a ela. play Você pode então retornar a versão
aprimorada da saída:
play <- function( ) {
symbols <- get_symbols( )
prize <- score(symbols)
attr(prize, "symbols") <- symbols
prize
}

Agora, play retorna o prêmio e os símbolos associados a ele. Os resultados podem


não parecer bonitos, mas os símbolos permanecem com o prêmio quando o
copiamos para um novo objeto. Podemos começar a organizar a exibição em um
minuto:

play( )
## [1] 0
## attr(,"symbols")
## [1] "B" "BB" "0"

two_play <- play( )

two_play
## [1] 0
## attr(,"symbols")
## [1] "0" "B" "0"
Você também pode gerar um prêmio e definir seus atributos em uma única etapa
com a structure função . structure cria um objeto com um conjunto de
atributos. O primeiro argumento de structure deve ser um objeto R ou um conjunto
de valores, e os argumentos restantes devem ser nomeados atributos
para structure adicionar ao objeto. Você pode dar a esses argumentos qualquer
nome que desejar. Structure aicionará os atributos ao objeto com os nomes que
você fornecer como nomes de argumento:

play <- function( ) {


symbols <- get_symbols( )
structure(score(symbols), symbols = symbols)
}
three_play <- play( )
three_play
## 0
## attr(,"symbols")
## "0" "BB" "B"

Agora que sua play saída contém um symbols atributo, o que você pode fazer
com ele? Você pode escrever suas próprias funções que pesquisam e usam o
atributo. Por exemplo, a função a seguir pesquisará one_play o symbols atributo
e o usará para exibi-lo one_play de forma elegante. Usaremos essa função para
exibir os resultados dos nossos slots, então vamos dedicar um momento para
estudar o que ela faz:

slot_display <- function(prize){

# extract symbols
symbols <- attr(prize, "symbols")

# collapse symbols into single string


symbols <- paste(symbols, collapse = " ")
# combine symbol with prize as a character string
# \n is special escape sequence for a new line (i.e. return or enter)
string <- paste(symbols, prize, sep = "\n$")

# display character string in console without quotes


cat(string)
}
slot_display(one_play)
## B 0 B
## $0

A função espera que um objeto como one_play esse tenha um valor numérico e
um symbols atributo. A primeira linha da função buscará o valor do symbols
atributo e o salvará como um objeto chamado symbols. Vamos criar um symbols
objeto de exemplo para vermos o que o restante da função faz. Podemos
usar one_play o symbols atributo de para fazer o trabalho. Symbols será um
vetor de strings de três caracteres:

symbols <- attr(one_play, "symbols")

symbols
## "B" "0" "B"

Em seguida, slot_display usa paste para compactar as três strings em symbols


uma string de um único caractere .paste Compacta um vetor de strings de
caracteres em uma única string quando você fornece o colapse
argumento .paste Usará o valor de colapse para separar as strings anteriormente
distintas. Portanto, symbols torna-se B 0 B as três strings separadas por um
espaço:

symbols <- paste(symbols, collapse = " ")

symbols
## "B 0 B"

Nossa função, então, usa paste uma nova maneira de combinar symbols com o valor
de prize. paste Combina objetos separados em uma string de caracteres quando
você fornece um sep argumento. Por exemplo, aqui, paste combinaremos a string
em symbols, B 0 B, com o número em prize, 0. paste Usaremos o valor
do sep argumento para separar as entradas na nova string. Aqui, esse valor é \ n
$, então nosso resultado será semelhante a "B 0 B \ n $ 0 " :
prize <- one_play
string <- paste(symbols, prize, sep = "\n$")

string
## "B 0 B\n$0"

A última linha de slot_display chamadas cat na nova string cat é semelhante


a print; exibe sua entrada na linha de comando. No entanto, cat não coloca sua
saída entre aspas. cat Também substitui every \n por uma nova linha ou quebra de
linha. O resultado é o que vemos. Observe que se parece exatamente com o que
sugeri que nossa play saída deveria ser em Programas :
cat(string)
## B 0 B
## $0

Você pode usar slot_display para limpar manualmente a saída de play:


slot_display(play())
## C B 0
## $2

slot_display(play())
## 7 0 BB
## $0
Este método de limpeza da saída exige que você intervenha manualmente na sua
sessão R (para chamar slot_display). Há uma função que você pode usar para
limpar automaticamente a saída play sempre que ela for exibida. Esta função
é print, e é uma função genérica .

10.3 Funções Genéricas


O R usa print com mais frequência do que você imagina; ele chama print cada vez
que exibe um resultado na janela do console. Essa chamada acontece em segundo
plano, então você não percebe; mas a chamada explica como a saída chega à janela
do console (lembre-se de que print always imprime seu argumento na janela do
console). Essa print chamada também explica por que a saída de print sempre
corresponde ao que você vê ao exibir um objeto na linha de comando:

print(pi)
## 3.141593

pi
## 3.141593

print(head(deck))
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8

head(deck)
## face suit value
## king spades 13
## queen spades 12
## jack spades 11
## ten spades 10
## nine spades 9
## eight spades 8

print(play( ))
## 5
## attr (,"symbols")
## "B" "BB" "B"

play( )
## 5
## attr(,"symbols")
## "B" "BB" "B"

Você pode alterar a forma como o R exibe a saída do seu slot reescrevendo-a print
para que se pareça com slot_display. Assim, o R imprimiria a saída em nosso
formato organizado. No entanto, esse método teria efeitos colaterais negativos.
Você não quer que o R chame slot_display ao imprimir um quadro de dados, um
vetor numérico ou qualquer outro objeto.
Felizmente, print não é uma função normal; é uma função genérica . Isso significa
que printfoi escrita de uma forma que lhe permite fazer coisas diferentes em casos
diferentes. Você já viu esse comportamento em ação (embora talvez não tenha
percebido). Print fez uma coisa quando analisamos a versão não classificada
de num:

num <- 1000000000


print(num)
## 1000000000

e uma coisa diferente quando demos num uma aula:

class(num) <- c("POSIXct", "POSIXt")


print(num)
## "2001-09-08 19:46:40 CST"

Dê uma olhada no código print para ver como ele faz isso. Você pode imaginar que
print consulta o atributo class de sua entrada e, em seguida, usa uma árvore + if +
para escolher qual saída exibir. Se isso ocorreu com você, ótimo trabalho ! print Faz
algo muito semelhante, mas muito mais simples.

10.4 Métodos
Quando você chama print, print chama uma função especial, UseMethod:

print
## function (x, ...)
## UseMethod("print")
## <bytecode: 0x7ffee4c62f80>
## <environment: namespace:base>

UseMethod examina a classe da entrada que você fornece para o primeiro


argumento de print e, em seguida, passa todos os seus argumentos para uma nova
função projetada para lidar com essa classe de entrada. Por exemplo, quando você
fornece print um objeto POSIXct, , UseMethod passará todos print os
argumentos de para [Link]. O R então executará [Link] e
retornará os resultados:

[Link]
## function (x, ...)
## {
## [Link] <- getOption("[Link]", 9999L)
## if ([Link] < length(x)) {
## print(format(x[seq_len([Link])], usetz = TRUE), ...)
## cat(" [ reached getOption(\"[Link]\") -- omitted",
## length(x) - [Link], "entries ]\n")
## }
## else print(format(x, usetz = TRUE), ...)
## invisible(x)
## }
## <bytecode: 0x7fa948f3d008>
## <environment: namespace:base>

Se você fornecer print um objeto de fator, UseMethod passará todos print os


argumentos de para [Link]. R será executado [Link] e retornará os
resultados:
[Link]
## function (x, quote = FALSE, [Link] = NULL, width =
getOption("width"),
## ...)
## {
## ord <- [Link](x)
## if (length(x) == 0L)
## cat(if (ord)
## "ordered"
## ...
## drop <- n > maxl
## cat(if (drop)
## paste(format(n), ""), T0, paste(if (drop)
## c(lev[1L:max(1, maxl - 1)], "...", if (maxl > 1) lev[n])
## else lev, collapse = colsep), "\n", sep = "")
## }
## invisible(x)
## }
## <bytecode: 0x7fa94a64d470>
## <environment: namespace:base>

[Link] e [Link] são chamados métodos de print. Por si


só, [Link] e [Link] funcionam como funções comuns do R. No
entanto, cada um foi escrito especificamente para que UseMethod pudesse ser
chamado para lidar com uma classe específica de print entrada.
Observe que [Link] e [Link] fazem duas coisas diferentes (observe
também que eu abreviei o meio de [Link]— é uma função longa). É assim
que printconsegue fazer coisas diferentes em casos diferentes. print
calls UseMethod, que chama um método especializado baseado na classe
do print primeiro argumento de .
Você pode ver quais métodos existem para uma função genérica
chamando methods-a. Por exemplo, print tem quase 200 métodos (o que dá uma
ideia de quantas classes existem em R):

methods(print)
## [1] [Link]*
## [2] [Link]
## [3] [Link]*
## ...
## [176] [Link]*
## [177] [Link]*
## [178] [Link]*
##
## Nonvisible functions are asterisked

Este sistema de funções genéricas, métodos e despacho baseado em classes é


conhecido como S3 porque se originou na terceira versão do S, a linguagem de
programação que evoluiria para S-PLUS e R. Muitas funções comuns do R são
genéricas do S3 que funcionam com um conjunto de métodos de classe. Por
exemplo, summary e head também chamam UseMethod. Funções mais
básicas, como c, +, e outras -, < também se comportam como funções
genéricas, embora chamem .primitive em vez de UseMethod.
O sistema S3 permite que funções R se comportem de maneiras diferentes para
classes diferentes. Você pode usar o S3 para formatar a saída do seu slot. Primeiro,
dê à sua saída uma classe própria. Em seguida, escreva um método print para essa
classe. Para fazer isso de forma eficiente, você precisará saber um pouco sobre
como UseMethod a função selects a method usa.
10.4.1 Despacho de Método
UseMethod usa um sistema muito simples para combinar métodos com funções.
Cada método S3 tem um nome de duas partes. A primeira parte do nome se referirá
à função com a qual o método trabalha. A segunda parte se referirá à classe. Essas
duas partes serão separadas por um ponto. Assim, por exemplo, o método print que
trabalha com funções será chamado [Link]. O método summary que
trabalha com matrizes será chamado [Link]. E assim por diante.
Quando UseMethod precisa chamar um método, ele busca uma função R com o
nome correto no estilo S3. A função não precisa ser especial de forma alguma; ela só
precisa ter o nome correto.
Você pode participar deste sistema escrevendo sua própria função e dando a ela um
nome válido no estilo S3. Por exemplo, vamos criar one_play uma classe própria.
Não importa como você a chama; o R armazenará qualquer sequência de caracteres
no atributo class:

class(one_play) <- "slots"

Agora, vamos escrever um método print do S3 para a classe +slots+. O método não
precisa fazer nada de especial — nem precisa imprimir one_play. Mas precisa ser
nomeado [Link]; caso contrário UseMethod, não o encontrará. O método
também deve receber os mesmos argumentos que print; caso contrário, o R
apresentará um erro ao tentar passar os argumentos para [Link]:

args(print)
## function (x, ...)
## NULL

[Link] <- function(x, ...) {


cat("I'm using the [Link] method")
}

Nosso método funciona? Sim, e não só isso; o R usa o método print para exibir o
conteúdo de one_play. Esse método não é muito útil, então vou removê-lo. Você
terá a chance de escrever um melhor em um minuto:
print(one_play)
## I'm using the [Link] method

one_play
## I'm using the [Link] method

rm([Link])

Alguns objetos R têm múltiplas classes. Por exemplo, a saída de [Link] possui
duas classes. Qual classe será UseMethod usada para encontrar um método print?

now <- [Link]()


attributes(now)
## $class
## [1] "POSIXct" "POSIXt"

UseMethod primeiro procurará um método que corresponda à primeira classe


listada no vetor de classes do objeto. Se UseMethod não encontrar nenhum,
procurará o método que corresponda à segunda classe (e assim por diante, se
houver mais classes no vetor de classes de um objeto).
Se você fornecer print um objeto cuja classe ou classes não tenham um método
print, UseMethod chamará [Link], um método especial escrito para lidar
com casos gerais.
Vamos usar esse sistema para escrever um método de impressão melhor para a
saída da máquina caça-níqueis.

Exercício 10.2 (Criar um Método Print) Escreva um novo método print para a classe
slots. O método deve chamar slot_display para retornar uma saída de máquina
caça-níqueis bem formatada.
Que nome você deve usar para esse método?
Solução. É surpreendentemente fácil escrever um bom [Link] método porque
já fizemos todo o trabalho duro quando escrevemos slot_display. Por exemplo, o
método a seguir funcionará. Apenas certifique-se de que o método tenha o nome
"[Link] so" para UseMethod que possamos encontrá-lo e de que ele receba
os mesmos argumentos que "print so" UseMethod para que possamos passá-los
[Link] sem problemas:

[Link] <- function(x, ...) {


slot_display(x)
}

Agora o R usará automaticamente slot_display para exibir objetos da classe


+slots+ (e somente objetos da classe “slots”):
one_play
## B 0 B
## $0

Vamos garantir que cada peça de saída da máquina caça-níqueis tenha slots classe.

Exercício 10.3 (Adicionar uma classe) Modifique a play função para que ela atribua
slots ao class atributo de sua saída:

play <- function() {


symbols <- get_symbols()
structure(score(symbols), symbols = symbols)
}

Solução. Você pode definir o class atributo da saída ao mesmo tempo em que
define o atributo +symbols+. Basta adicionar class = "slots" à structure
chamada:

play <- function() {


symbols <- get_symbols()
structure(score(symbols), symbols = symbols, class = "slots")
}
Agora cada uma das nossas máquinas caça-níqueis terá a classe slots:
class(play())
## "slots"

Como resultado, R os exibirá no formato correto de caça-níqueis:


play()
## BB BB BBB
## $5

play()
## BB 0 0
## $0

10.5 Aulas
Você pode usar o sistema S3 para criar uma nova classe robusta de objetos em R.
Assim, o R tratará os objetos da sua classe de maneira consistente e sensata. Para
criar uma classe:
Escolha um nome para sua classe.
Atribua a cada instância da sua classe um atributo +class+.
Escreva métodos de classe para qualquer função genérica que
provavelmente usará objetos da sua classe.

Muitos pacotes R são baseados em classes que foram construídas de maneira


semelhante. Embora esse trabalho seja simples, pode não ser fácil. Por exemplo,
considere quantos métodos existem para classes predefinidas.
Você pode chamar methods uma classe com o class argumento, que recebe uma
string de caracteres. methods retornará todos os métodos escritos para a classe.
Observe que methods não será possível mostrar métodos que vêm em um pacote
R descarregado:

methods(class = "factor")
## [1] [.factor [[.factor
## [3] [[<-.factor [<-.factor
## [5] [Link] [Link]
## [7] [Link] [Link]
## [9] [Link] [Link]
## [11] [Link] [Link]
## [13] [Link] [Link]
## [15] [Link]<-.factor length<-.factor
## [17] levels<-.factor [Link]
## [19] [Link] [Link]*
## [21] [Link] [Link]*
## [23] [Link]* [Link]
## [25] [Link] [Link]
## [27] [Link]
##
## Nonvisible functions are asterisked

Esta saída indica quanto trabalho é necessário para criar uma classe robusta e bem-
comportada. Normalmente, você precisará escrever um class método para cada
operação básica do R.
Considere dois desafios que você enfrentará imediatamente. Primeiro, o R elimina
atributos (como class) ao combinar objetos em um vetor:

play1 <- play()


play1
## B BBB BBB
## $5

play2 <- play()


play2
## 0 B 0
## $0

c(play1, play2)
## [1] 5 0

Aqui, R para de usar [Link] para exibir o vetor porque o vetor c (play1,
play2) não tem mais um atributo “slots” +class+.
Em seguida, R removerá os atributos de um objeto (como class) quando você
subdividir o objeto:

play1[1]
## [1] 5

Você pode evitar esse comportamento escrevendo um [Link] método e [.slots


outro, mas as dificuldades surgirão rapidamente. Como você combinaria
os symbols atributos de várias peças em um vetor de atributos de símbolos? Como
você mudaria [Link] para lidar com vetores de saídas? Esses desafios estão
abertos para você explorar. No entanto, normalmente você não precisará tentar
esse tipo de programação em larga escala como cientista de dados.
No nosso caso, é muito útil deixar que slots os objetos revertam para valores de
prêmio único quando combinamos grupos deles em um vetor.

10.6 S3 e Depuração
O S3 pode ser irritante se você estiver tentando entender funções do R. É difícil
dizer o que uma função faz se o corpo do código contém uma chamada
para UseMethod. Agora que você sabe que UseMethod chama um método
específico da classe, pode pesquisar e examinar o método diretamente. Será uma
função cujo nome segue a <[Link]> sintaxe, ou
possivelmente <[Link]>. Você também pode usar a methods
função para ver quais métodos estão associados a uma função ou classe.

10.7 S4 e R5
O R também contém dois outros sistemas que criam comportamento específico de
classe. Eles são conhecidos como S4 e R5 (ou classes de referência). Cada um desses
sistemas é muito mais difícil de usar do que o S3 e, talvez por isso, mais raro. No
entanto, eles oferecem salvaguardas que o S3 não oferece. Se você quiser aprender
mais sobre esses sistemas, incluindo como escrever e usar suas próprias funções
genéricas, recomendo o livro Advanced R Programming, de Hadley Wickham.
10.8 Resumo
Valores não são o único lugar para armazenar informações em R, e funções não são
a única maneira de criar comportamentos únicos. Você também pode fazer ambas
as coisas com o sistema S3 do R. O sistema S3 fornece uma maneira simples de criar
comportamentos específicos de objetos em R. Em outras palavras, é a versão do R
da programação orientada a objetos (POO). O sistema é implementado por funções
genéricas. Essas funções examinam o atributo de classe de sua entrada e chamam
um método específico de classe para gerar a saída. Muitos métodos do S3 procuram
e usam informações adicionais armazenadas nos atributos de um objeto. Muitas
funções comuns do R são genéricas do S3.
O sistema S3 do R é mais útil para tarefas de ciência da computação do que para
tarefas de ciência de dados, mas entender o S3 pode ajudar você a solucionar
problemas no seu trabalho em R como cientista de dados.
Agora você já sabe bastante sobre como escrever código R que executa tarefas
personalizadas, mas como repeti-las? Como cientista de dados, você
frequentemente repetirá tarefas, às vezes milhares ou até milhões de vezes. Por
quê? Porque a repetição permite simular resultados e estimar
probabilidades. Loops mostrará como automatizar a repetição com Rs for e while
funções. Você usará isso for para simular várias jogadas em máquinas caça-níqueis
e calcular a taxa de pagamento da sua máquina.

11 Laços
Laços são o método do R para repetir uma tarefa, o que os torna uma ferramenta
útil para simulações de programação. Este capítulo ensinará como usar as
ferramentas de laço do R
Vamos usar a score função para resolver um problema do mundo real.
Sua máquina caça-níqueis foi modelada com base em máquinas reais que foram
acusadas de fraude. As máquinas pareciam pagar 40 centavos por dólar, mas o
fabricante alegou que pagavam 92 centavos por dólar. Você pode calcular a taxa de
pagamento exata da sua máquina com o score programa. A taxa de pagamento
será o valor esperado do prêmio da máquina caça-níqueis.

11.1 Valores Esperados


O valor esperado de um evento aleatório é um tipo de média ponderada; é a soma
de cada resultado possível do evento, ponderada pela probabilidade de que cada
resultado ocorra:
n
E(x)= ∑ (¿ x eu . P(x eu ))¿
eu=1

Você pode pensar no valor esperado como o prêmio médio que você observaria se
jogasse na máquina caça-níqueis um número infinito de vezes. Vamos usar a
fórmula para calcular alguns valores esperados simples. Em seguida, aplicaremos a
fórmula à sua máquina caça-níqueis

Você se lembra do dieque criou no Projeto 1: Dados Ponderados ?

die <- c(1, 2, 3, 4, 5, 6)

Cada vez que você lança o dado, ele retorna um valor selecionado aleatoriamente
(de um a seis). Você pode encontrar o valor esperado do lançamento do dado com a
fórmula:
n
E(morrer) = ∑ (morrer eu ¿ . P morrer eu )¿
eu=1

O morrereu s são os resultados possíveis do lançamento do dado: 1, 2, 3, 4, 5 e 6; e o


P(morrereu) As probabilidades associadas a cada um dos resultados. Se o seu dado
for honesto, cada resultado ocorrerá com a mesma probabilidade: 1/6. Portanto,
nossa equação se simplifica para:
n
E(morrer) = ∑ (morrer eu . P ( morrer eu ) )
eu=1

1 1 1 1 1 1
¿ 1 . +2 . +3 . +4 . +5 . +6 . =3 ,5
0 0 0 0 0 0

Portanto, o valor esperado de um dado honesto é 3,5. Você pode notar que este
também é o valor médio do dado. O valor esperado será igual à média se todos os
resultados tiverem a mesma chance de ocorrer.
Mas e se cada resultado tiver uma chance diferente de ocorrer? Por exemplo,
ponderamos nossos dados em Pacotes e Páginas de Ajuda para que cada dado
tirasse 1, 2, 3, 4 e 5 com probabilidade de 1/8 e 6 com probabilidade de 3/8. Você
pode usar a mesma fórmula para calcular o valor esperado nestas condições:

1 1 1 1 1 3
E(deue) =1. + 2. + 3 . + 4 . + 5 . + 6 . =4,125
8 8 8 8 8 8
Portanto, o valor esperado de um dado viciado não é igual ao valor médio de seus
resultados. Se você jogasse um dado viciado infinitas vezes, o resultado médio seria
4,125, o que é maior do que o esperado de um dado honesto.
Observe que fizemos as mesmas três coisas para calcular ambos os valores
esperados. Temos:
 Listou todos os resultados possíveis
 Determinou o valor de cada resultado (aqui apenas o valor do dado)
 Calculou a probabilidade de que cada resultado ocorresse
O valor esperado era então apenas a soma dos valores da etapa 2 multiplicada pelas
probabilidades da etapa 3.
Você pode usar estas etapas para calcular valores esperados mais sofisticados. Por
exemplo, você pode calcular o valor esperado de um par de dados ponderados.
Vamos fazer isso passo a passo.
Primeiro, liste todos os resultados possíveis. Um total de 36 resultados diferentes
podem aparecer ao rolar dois dados. Por exemplo, você pode rolar (1, 1), que
representa um no primeiro dado e um no segundo dado. Ou você pode rolar (1, 2),
um no primeiro dado e dois no segundo. E assim por diante. Listar essas
combinações pode ser tedioso, mas o R tem uma função que pode ajudar.

11.2 [Link]
A [Link] função em R fornece uma maneira rápida de escrever todas as
combinações dos elementos em n vetores. Por exemplo, você pode listar todas as
combinações de dois dados. Para isso, execute [Link] em duas cópias
de die:

rolls <- [Link](die, die)

[Link] retornará um quadro de dados que contém todas as maneiras de


parear um elemento do primeiro die vetor com um elemento do segundo die
vetor. Isso capturará todas as 36 combinações possíveis de valores:

rolls
## Var1 Var2
## 1 1 1
## 2 2 1
## 3 3 1
## ...
## 34 4 6
## 35 5 6
## 36 6 6

Você pode usar [Link] com mais de dois vetores, se desejar. Por exemplo,
você pode listar cada combinação de lançamento de três dados com [Link]
(die, die, die) e cada combinação de lançamento de quatro dados
com [Link] (die, die, die, die), e assim por diante. [Link]
sempre retornará um quadro de dados que contém cada combinação possível
de n elementos dos n vetores. Cada combinação conterá exatamente um elemento
de cada vetor.
Você pode determinar o valor de cada jogada depois de fazer sua lista de resultados.
Esta será a soma dos dois dados, que você pode calcular usando a execução
elemento a elemento de R:

rolls$value <- rolls$Var1 + rolls$Var2


head(rolls, 3)
## Var1 Var2 value
## 1 1 2
## 2 1 3
## 3 1 4

O R combinará os elementos de cada vetor antes de somá-los. Como resultado, cada


elemento de valuese referirá aos elementos de Var1 e Var2 que aparecem na
mesma linha.
Em seguida, você deve determinar a probabilidade de cada combinação aparecer.
Você pode calcular isso com uma regra básica de probabilidade:
A probabilidade de que n eventos aleatórios independentes ocorram é igual ao
produto das probabilidades de que cada evento aleatório ocorra .
Ou mais sucintamente:
P ( UM∧B∧C∧… )=P ( UM ) . P ( B ) . P (C ) . …

Portanto, a probabilidade de tirarmos um (1, 1) será igual à probabilidade de


tirarmos um no primeiro dado, 1/8, vezes a probabilidade de tirarmos um no
segundo dado, 1/8:

1 1 1
P ( 1∧1 )=P ( 1 ) . P ( 1 ) ¿ . =
8 8 64
E a probabilidade de obtermos um (1, 2) será:

1 1 1
P ( 1∧2 )=P ( 1 ) . P ( 2 ) ¿ . =
8 8 64

E assim por diante.


Deixe-me sugerir um processo de três etapas para calcular essas probabilidades em
R. Primeiro, podemos pesquisar as probabilidades de rolar os valores em Var1.
Faremos isso com a tabela de consulta a seguir:

prob <- c("1" = 1/8, "2" = 1/8, "3" = 1/8, "4" = 1/8, "5" = 1/8, "6" = 3/8)
prob
## 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375

Se você subdividir esta tabela por rolls$Var1, você obterá um vetor de


probabilidades perfeitamente vinculado aos valores de Var1:

rolls$Var1
## 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4
56

prob[rolls$Var1]
## 1 2 3 4 5 6 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375 0.125 0.125 0.125 0.125
0.125 0.375
## 1 2 3 4 5 6 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375 0.125 0.125 0.125 0.125
0.125 0.375
## 1 2 3 4 5 6 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375 0.125 0.125 0.125 0.125
0.125 0.375

rolls$prob1 <- prob[rolls$Var1]


head(rolls, 3)
## Var1 Var2 value prob1
## 1 1 2 0.125
## 2 1 3 0.125
## 3 1 4 0.125

Em segundo lugar, podemos procurar as probabilidades de rolar os valores


em Var2:
rolls$prob2 <- prob[rolls$Var2]

head(rolls, 3)
## Var1 Var2 value prob1 prob2
## 1 1 2 0.125 0.125
## 2 1 3 0.125 0.125
## 3 1 4 0.125 0.125

Terceiro, podemos calcular a probabilidade de rolar cada combinação


multiplicando prob1 por prob2:

rolls$prob <- rolls$prob1 * rolls$prob2


head(rolls, 3)
## Var1 Var2 value prob1 prob2 prob
## 1 1 2 0.125 0.125 0.015625
## 2 1 3 0.125 0.125 0.015625
## 3 1 4 0.125 0.125 0.015625

É fácil calcular o valor esperado agora que temos cada resultado, o valor de cada
resultado e a probabilidade de cada resultado. O valor esperado será a soma dos
valores dos dados multiplicados pelas probabilidades dos dados:

sum(rolls$value * rolls$prob)
## 8.25

Portanto, o valor esperado de rolar dois dados viciados é 8,25. Se você rolar um par
de dados viciados infinitas vezes, a soma média seria 8,25. (Se você estiver curioso,
o valor esperado de rolar um par de dados honestos é 7, o que explica por que o 7
desempenha um papel tão importante em jogos de dados como o craps.)
Agora que você se aqueceu, vamos usar nosso método para calcular o valor
esperado do prêmio da máquina caça-níqueis. Seguiremos os mesmos passos que
acabamos de seguir:
 Listaremos todos os resultados possíveis ao jogar na máquina. Esta
será uma lista de todas as combinações de três símbolos de caça-
níqueis.
 Calcularemos a probabilidade de obter cada combinação quando você
jogar na máquina.
 Determinaremos o prêmio que ganharemos para cada combinação.

Quando terminarmos, teremos um conjunto de dados parecido com este:

## Var1 Var2 Var3 prob1 prob2 prob3 prob prize


## DD DD DD 0.03 0.03 0.03 0.000027 800
## 7 DD DD 0.03 0.03 0.03 0.000027 0
## BBB DD DD 0.06 0.03 0.03 0.000054 0
## ... and so on.
O valor esperado será então a soma dos prêmios multiplicada pela sua
probabilidade de ocorrência:
n
E( prêmio )= ∑ ( prêmio eu . P( prêmioeu ¿)) ¿
eu=1

Pronto para começar?

Exercício 11.1 (Listar as combinações) Use [Link] para criar um quadro de


dados que contenha todas as combinações possíveis de três símbolos do wheel
vetor:

wheel <- c("DD", "7", "BBB", "BB", "B", "C", "0")

Certifique-se de adicionar o argumento stringsAsFactors = FALSE à


sua [Link] chamada; caso contrário, [Link] as combinações serão
salvas como fatores, uma escolha infeliz que interromperá a score função.
Solução. Para criar um quadro de dados de cada combinação de três símbolos, você
precisa executar [Link] e fornecer três cópias de wheel. O resultado será
um quadro de dados com 343 linhas, uma para cada combinação única de três
símbolos de slot:

combos <- [Link](wheel, wheel, wheel, stringsAsFactors = FALSE)


combos
## Var1 Var2 Var3
## 1 DD DD DD
## 2 7 DD DD
## 3 BBB DD DD
## 4 BB DD DD
## 5 B DD DD
## 6 C DD DD
## ...
## 341 B 0 0
## 342 C 0 0
## 343 0 0 0

Agora, vamos calcular a probabilidade de obter cada combinação. Você pode usar as
probabilidades contidas no prob argumento de get_symbols para fazer isso.
Essas probabilidades determinam a frequência com que cada símbolo é escolhido
quando sua máquina caça-níqueis gera símbolos. Elas foram calculadas após
observar 345 jogadas nos terminais de videoloteria de Manitoba. Os zeros têm a
maior chance de serem selecionados (0,52) e as cerejas, a menor (0,01):
get_symbols <- function() {
wheel <- c("DD", "7", "BBB", "BB", "B", "C", "0")
sample(wheel, size = 3, replace = TRUE,
prob = c(0.03, 0.03, 0.06, 0.1, 0.25, 0.01, 0.52)
}

Exercício 11.2 (Criar uma Tabela de Consulta) Isole as probabilidades anteriores em


uma tabela de consulta. Quais nomes você usará na sua tabela?
Solução. Seus nomes devem corresponder à entrada que você deseja consultar.
Nesse caso, a entrada serão as sequências de caracteres que aparecem em Var1,
Var2, e Var3. Portanto, sua tabela de consulta deve ficar assim:

prob <- c ("DD" = 0.03, "7" = 0.03, "BBB" = 0.06,


"BB" = 0.1, "B" = 0.25, "C" = 0.01, "0" = 0.52)
Agora vamos analisar nossas probabilidades.

Exercício 11.3 (Consulte as Probabilidades) Pesquise as probabilidades de obter os


valores em Var1. Em seguida, some-os a combos uma coluna chamada prob1.
Em seguida, faça o mesmo para Var2 (prob2) e Var3 (prob3).
Solução. Lembre-se de que você usa a notação de seleção do R para pesquisar
valores em uma tabela de consulta. Os valores resultantes serão vinculados ao
índice que você usar:
combos$prob1 <- prob[combos$Var1]
combos$prob2 <- prob[combos$Var2]
combos$prob3 <- prob[combos$Var3]

head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3
## DD DD DD 0.03 0.03 0.03
## 7 DD DD 0.03 0.03 0.03
## BBB DD DD 0.06 0.03 0.03

Agora, como devemos calcular a probabilidade total de cada combinação? Nossos


três símbolos de caça-níqueis são escolhidos independentemente, o que significa
que a mesma regra que rege nossas probabilidades nos dados rege nossas
probabilidades de símbolos:

P ( UM∧B∧C∧… )=P ( UM ) . P ( B ) . P (C ) . ….. P ( A∧B∧C∧… . )=P ( A ) P ( B ) P ( C ) … .

Exercício 11.4 (Calcular Probabilidades para Cada Combinação) Calcule as


probabilidades gerais para cada combinação. Salve-as como uma coluna nomeada
prob em combos e verifique seu trabalho.
Você pode verificar se o cálculo funcionou somando as probabilidades. As
probabilidades devem somar um, pois uma das combinações deve aparecer quando
você joga na máquina caça-níqueis. Em outras palavras, uma combinação aparecerá
com probabilidade de um. Você pode calcular as probabilidades de todas as
combinações possíveis de uma só vez com alguma execução por elemento:

combos$prob <- combos$prob1 * combos$prob2 * combos$prob3

head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3 prob
## DD DD DD 0.03 0.03 0.03 0.000027
## 7 DD DD 0.03 0.03 0.03 0.000027
## BBB DD DD 0.06 0.03 0.03 0.000054
A soma das probabilidades é um, o que sugere que nossa matemática está correta:
sum(combos$prob)
## 1

Você só precisa fazer mais uma coisa antes de calcular o valor esperado: determinar
o prêmio para cada combinação em combos. Você pode calcular o prêmio
com score. Por exemplo, podemos calcular o prêmio para a primeira linha
de combos assim:

symbols <- c(combos[1, 1], combos[1, 2], combos[1, 3])


## "DD" "DD" "DD"

score(symbols)
## 800

No entanto, existem 343 linhas, o que torna o trabalho tedioso se você planeja
calcular as pontuações manualmente. Será mais rápido automatizar essa tarefa e
deixar que o R faça isso por você, o que pode ser feito com um for loop.

11.3 para Loops


Um for loop repete um trecho de código várias vezes, uma para cada elemento em
um conjunto de entradas. for Os loops fornecem uma maneira de dizer ao R: "Faça
isso para cada valor daquilo". Na sintaxe do R, isso se parece com:

for (value in that) {


this
}

O that objeto deve ser um conjunto de objetos (geralmente um vetor de números


ou sequências de caracteres). O loop for executará o código que aparece entre
chaves uma vez para cada membro de that. Por exemplo, o loop for abaixo é
executado print ("one run") uma vez para cada elemento em um vetor de
sequências de caracteres:

for (value in c("My", "first", "for", "loop")) {


print("one run")
}
## "one run"
## "one run"
## "one run"
## "one run"

O value símbolo em um laço for atua como um argumento em uma função. O laço
for cria um objeto chamado value e atribui a ele um novo valor a cada execução do
laço. O código no seu laço pode acessar esse valor chamando o value objeto.

Quais valores o loop for atribuirá a value? Ele usará os elementos do conjunto em
que você executar o loop. for começa com o primeiro elemento e, em seguida,
atribui um elemento diferente a value cada execução do loop for, até que todos os
elementos tenham sido atribuídos a value. Por exemplo, o loop for abaixo será
executado print (value) quatro vezes e imprimirá um elemento de c ("My",
"second", "for", "loop") cada vez:

for (value in c("My", "second", "for", "loop")) {


print(value)
}
## "My"
## "second"
## "for"
## "loop"

Na primeira execução, o loop for substituiu "My" for value em print (value).
Na segunda execução, ele substituiu "second", e assim por diante até for ter sido
executado print (value) uma vez com cada elemento do conjunto:
Se você observar value depois que o loop é executado, verá que ele ainda contém
o valor do último elemento do conjunto:

value
## "loop"

Tenho usado o símbolo value nos meus laços for, mas não há nada de especial
nele. Você pode usar qualquer símbolo que desejar no seu laço para fazer a mesma
coisa, desde que o símbolo apareça antes, in entre parênteses, depois de for. Por
exemplo, você pode reescrever o laço anterior com qualquer um dos seguintes:

for (word in c("My", "second", "for", "loop")) {


print(word)
}
for (string in c("My", "second", "for", "loop")) {
print(string)
}
for (i in c("My", "second", "for", "loop")) {
print(i)
}

Escolha seus símbolos com cuidado


O R executará seu loop em qualquer ambiente de onde você o chamar.
Isso é ruim se o seu loop usar nomes de objetos que já existem no
ambiente. Seu loop substituirá os objetos existentes pelos objetos que
ele criar. Isso também se aplica ao símbolo de valor.

Os loops for são executados em conjuntos


Em muitas linguagens de programação, for os laços são projetados
para trabalhar com números inteiros, não com conjuntos. Você atribui
ao laço um valor inicial e um valor final, bem como um incremento para
avançar o valor entre os laços. O for laço então é executado até que o
valor do laço exceda o valor final.
Você pode recriar esse efeito em R fazendo com que um for loop seja
executado em um conjunto de inteiros, mas não perca de vista o fato
de que for os loops de R são executados em membros de um conjunto,
não em sequências de inteiros.

for Laços são muito úteis em programação porque ajudam a conectar um trecho de
código a cada elemento de um conjunto. Por exemplo, poderíamos usar um for laço
para executar score uma vez para cada linha em combos. No entanto, os laços do
R fortêm uma deficiência que você precisa saber antes de começar a usá-los: for
laços não retornam saída.
for Loops são como Las Vegas: o que acontece em um for loop permanece no for
loop. Se você quiser usar os produtos de um for loop, deve escrevê- for lo de forma
que ele salve sua própria saída conforme avança.
Nossos exemplos anteriores pareciam retornar uma saída, mas isso era enganoso.
Os exemplos funcionaram porque chamamos print, que sempre imprime seus
argumentos no console (mesmo se for chamado de uma função, um for loop ou
qualquer outro). Nossos for loops não retornarão nada se você remover a print
chamada:

for (value in c("My", "third", "for", "loop")) {


value
}
##

Para salvar a saída de um for loop, você deve escrevê-lo de forma que ele salve sua
própria saída enquanto é executado. Você pode fazer isso criando um vetor ou lista
vazios antes de executar o for loop. Em seguida, use o for loop para preencher o
vetor ou a lista. Quando o for loop terminar, você poderá acessar o vetor ou a lista,
que agora conterá todos os seus resultados.
Vamos ver isso em ação. O código a seguir cria um vetor vazio de comprimento 4:

chars <- vector(length = 4)


O próximo loop irá preenchê-lo com strings:

words <- c("My", "fourth", "for", "loop")

for (i in 1:4) {
chars[i] <- words[i]
}

chars
## "My" "fourth" "for" "loop"

Essa abordagem geralmente exigirá que você altere os conjuntos for nos quais
executa o loop. Em vez de executar em um conjunto de objetos, execute em um
conjunto de inteiros que você pode usar para indexar tanto o objeto quanto o vetor
de armazenamento. Essa abordagem é muito comum em R. Na prática, você verá
que usa for loops não tanto para executar código, mas para preencher vetores e
listas com os resultados do código.
Vamos usar um for loop para calcular o prêmio de cada linha em combos. Para
começar, crie uma nova coluna combo spara armazenar os resultados do for loop:
combos$prize <- NA
head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3 prob prize
## DD DD DD 0.03 0.03 0.03 0.000027 NA
## 7 DD DD 0.03 0.03 0.03 0.000027 NA
## BBB DD DD 0.06 0.03 0.03 0.000054 NA

O código cria uma nova coluna chamada prize e a preenche com NA s. R usa suas
regras de reciclagem para preencher cada valor da coluna com NA.

Exercício 11.5 (Construir um Laço) Construa um for laço que será executado score
em todas as 343 linhas de combos. O laço deve ser executadoscore nas três
primeiras entradas da _i_ésima linha de combos e deve armazenar os resultados
na _i_ésima entrada de combos$prize.
Solução. Você pode pontuar as linhas combos com:

for (i in 1:nrow(combos)) {
symbols <- c(combos[i, 1], combos[i, 2], combos[i, 3])
combos$prize[i] <- score(symbols)
}

Após executar o loop for, combos$prize conterá o prêmio correto para cada
linha. Este exercício também testa a score função; score parece funcionar
corretamente para todas as combinações de slots possíveis:
head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3 prob prize
## DD DD DD 0.03 0.03 0.03 0.000027 800
## 7 DD DD 0.03 0.03 0.03 0.000027 0
## BBB DD DD 0.06 0.03 0.03 0.000054 0

Agora estamos prontos para calcular o valor esperado do prêmio. O valor esperado
é a soma combos$prize ponderada por combos$prob. Esta também é a taxa
de pagamento da máquina caça-níqueis:

sum(combos$prize * combos$prob)
## 0.538014

Ah, não. O prêmio esperado é de cerca de 0,54, o que significa que nossa máquina
caça-níqueis paga apenas 54 centavos por dólar a longo prazo. Isso significa que o
fabricante das máquinas caça-níqueis de Manitoba estava mentindo?
Não, porque ignoramos uma característica importante da máquina caça-níqueis
quando escrevemos score: um diamante é um curinga. Você pode tratar um DD
como qualquer outro símbolo se ele aumentar seu prêmio, com uma exceção. Você
não pode fazer um DD a C menos que já tenha outro C em seus símbolos (seria
muito fácil se cada um DD lhe rendesse automaticamente $2).
A melhor coisa sobre DD s é que seus efeitos são cumulativos. Por exemplo,
considere a combinação B, DD, B. O não só DD conta como um B, o que lhe
renderia um prêmio de US$ 10; o DDtambém dobra o prêmio para US$ 20.
Adicionar esse comportamento ao nosso código é um pouco mais difícil do que o
que fizemos até agora, mas envolve todos os mesmos princípios. Você pode decidir
que sua máquina caça-níqueis não usa wilds e manter o código que temos. Nesse
caso, sua máquina caça-níqueis terá uma taxa de pagamento de cerca de 54%. Ou
você pode reescrever seu código para usar wilds. Se fizer isso, você descobrirá que
sua máquina caça-níqueis tem uma taxa de pagamento de 93%, um por cento a mais
do que o declarado pelo fabricante. Você pode calcular essa taxa com o mesmo
método que usamos nesta seção.

Exercício 11.6 (Desafio) Há muitas maneiras de modificar score que contariam


DDs como curinga. Se você quiser testar suas habilidades como programador R,
tente escrever sua própria versão de score que lida corretamente com diamantes.
Se você quiser um desafio mais modesto, estude o score código a seguir. Ele
considera diamantes selvagens de uma forma que considero elegante e sucinta. Veja
se consegue entender cada etapa do código e como ele chega ao seu resultado.
Solução. Aqui está uma versão do score que lida com diamantes selvagens:

score <- function(symbols) {


diamonds <- sum(symbols == "DD")
cherries <- sum(symbols == "C")

# identify case
# since diamonds are wild, only nondiamonds
# matter for three of a kind and all bars
slots <- symbols[symbols != "DD"]
same <- length(unique(slots)) == 1
bars <- slots %in% c("B", "BB", "BBB")

# assign prize
if (diamonds == 3) {
prize <- 100
} else if (same) {
payouts <- c("7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[slots[1]])
} else if (all(bars)) {
prize <- 5
} else if (cherries > 0) {
# diamonds count as cherries
# so long as there is one real cherry
prize <- c(0, 2, 5)[cherries + diamonds + 1]
} else {
prize <- 0
}

# double for each diamond


prize * 2^diamonds
}

Exercício 11.7 (Calcule o Valor Esperado) Calcule o valor esperado da máquina caça-
níqueis quando ela usar a nova score função. Você pode usar o combos quadro
de dados existente, mas precisará construir um for loop para recalcular
combos$prize.
Para atualizar o valor esperado, basta atualizar combos$prize:

for (i in 1:nrow(combos)) {
symbols <- c(combos[i, 1], combos[i, 2], combos[i, 3])
combos$prize[i] <- score(symbols)
}

Em seguida, recalcule o valor esperado:

sum(combos$prize * combos$prob)
## 0.934356
Este resultado confirma a afirmação do fabricante. Na verdade, as máquinas caça-
níqueis parecem mais generosas do que o fabricante afirma.

11.4 loops While


O R possui dois companheiros para o for laço: o while laço e o repeat laço.
Um while laço reexecuta um trecho enquanto uma determinada condição
permanece TRUE. Para criar um while laço, adicione while uma condição e um
trecho de código, como este:

while (condition) {
code
}

while executará novamente condition, que deve ser um teste lógico, no início de
cada loop. Se condition for avaliado como TRUE, while executará o código entre
chaves. Se condition for avaliado como FALSE, while finalizará o loop.
Por que pode condition mudar de TRUE para FALSE? Presumivelmente porque o
código dentro do seu loop mudou, independentemente de a condição ainda
ser TRUE. Se o código não tiver relação com a condição, um while loop será
executado até que você o interrompa. Portanto, tenha cuidado. Você pode
interromper um while loop pressionando Escape ou clicando no ícone de sinal de
parada na parte superior do painel do console do RStudio. O ícone aparecerá assim
que o loop começar a ser executado.
Assim como for os loops, while os loops não retornam um resultado, então você
deve pensar no que quer que o loop retorne e salvá-lo em um objeto durante o
loop.
Você pode usar while loops para fazer coisas que exigem um número variável de
iterações, como calcular quanto tempo leva para ficar sem dinheiro jogando caça-
níqueis (como a seguir). No entanto, na prática, while loops são muito menos
comuns do que for loops em R:

plays_till_broke <- function(start_with) {


cash <- start_with
n <- 0
while (cash > 0) {
cash <- cash - 1 + play()
n <- n + 1
}
n
}

plays_till_broke(100)
## 260

11.5 loops de repetição


repeat Os loops são ainda mais básicos que while os loops convencionais. Eles
repetirão um trecho de código até que você diga para eles pararem (pressionando
Escape) ou até que encontrem o comando break, que interromperá o loop.
Você pode usar um repeat loop para recriar plays_till_broke minha função que
simula quanto tempo leva para perder dinheiro ao jogar caça-níqueis:

plays_till_broke <- function(start_with) {


cash <- start_with
n <- 0
repeat {
cash <- cash - 1 + play()
n <- n + 1
if (cash <= 0) {
break
}
}
n
}

plays_till_broke(100)
## 237
11.6 Resumo
Você pode repetir tarefas em R com loops , e . Para usar for, while forneça a ele
um trecho de código para executar e um conjunto de objetos para
percorrer. executará o trecho de código uma vez para cada objeto. Se desejar salvar
a saída do seu loop, você pode atribuí-la a um objeto que exista fora do loop
.repeat for for
A repetição desempenha um papel importante na ciência de dados. É a base para a
simulação, bem como para estimativas de variância e probabilidade. Laços não são a
única maneira de criar repetição em R (considere, replicate por exemplo), mas são
uma das formas mais populares.
Infelizmente, os laços em R podem, às vezes, ser mais lentos do que os de outras
linguagens. Como resultado, os laços em R têm má reputação. Essa reputação não é
totalmente merecida, mas destaca uma questão importante. A velocidade é
essencial para a análise de dados. Quando seu código é executado rapidamente,
você pode trabalhar com dados maiores e fazer mais com eles antes de ficar sem
tempo ou poder computacional. A velocidade ensinará como escrever for laços
rápidos e código rápido em geral com R. Lá, você aprenderá a escrever código
vetorizado, um estilo de código extremamente rápido que aproveita todos os
pontos fortes de R.

12 Velocidades
Como cientista de dados, você precisa de velocidade. Você pode trabalhar com
dados maiores e realizar tarefas mais ambiciosas quando seu código é executado
rapidamente. Este capítulo mostrará uma maneira específica de escrever código
rápido em R. Você usará o método para simular 10 milhões de jogadas em sua
máquina caça-níqueis.

12.1 Código Vetorizado


Você pode escrever um trecho de código de muitas maneiras diferentes, mas o
código R mais rápido geralmente se beneficia de três coisas: testes lógicos,
subconjuntos e execução elemento a elemento. Essas são as coisas que o R faz
melhor. O código que usa essas coisas geralmente tem uma certa qualidade:
é vetorizado ; o código pode receber um vetor de valores como entrada e manipular
cada valor no vetor simultaneamente.
Para ver como é o código vetorizado, compare estes dois exemplos de uma função
de valor absoluto. Cada um pega um vetor de números e o transforma em um vetor
de valores absolutos (por exemplo, números positivos). O primeiro exemplo não é
vetorizado; abs_loop usa um for loop para manipular cada elemento do vetor, um
de cada vez:
abs_loop <- function(vec){
for (i in 1:length(vec)) {
if (vec[i] < 0) {
vec[i] <- -vec[i]
}
}
vec
}

O segundo exemplo, abs_set, é uma versão vetorizada de abs_loop. Ele usa


subconjuntos lógicos para manipular todos os números negativos no vetor
simultaneamente:

abs_sets <- function(vec){


negs <- vec < 0
vec[negs] <- vec[negs] * -1
vec
}

abs_set é muito mais rápido abs_loop porque depende de operações que o R faz
rapidamente: testes lógicos, subconjuntos e execução elemento a elemento.
Você pode usar a [Link] função para ver o quão rápido abs_set ela
é. [Link] pega uma expressão R, executa-a e então exibe quanto tempo
decorreu enquanto a expressão era executada.
Para comparar abs_loop e abs_set, primeiro faça um longo vetor de números
positivos e negativos . long conterá 10 milhões de valores:

long <- rep(c(-1, 1), 5000000)


rep repete um valor, ou vetor de valores, muitas vezes. Para usar rep,
forneça um vetor de valores e, em seguida, o número de vezes que o
vetor será repetido. R retornará os resultados como um novo vetor mais
longo.

Você pode então usar [Link] para medir quanto tempo leva para cada
função ser avaliada long:
[Link](abs_loop(long))
## user system elapsed
## 15.982 0.032 16.018

[Link](abs_sets(long))
## user system elapsed
## 0.529 0.063 0.592

Não confunda [Link] com [Link], que


retorna a hora atual.

As duas primeiras colunas da saída do [Link] relatório mostram quantos


segundos seu computador gastou executando a chamada no lado do usuário e no
lado do sistema do seu processo, uma dicotomia que varia de sistema operacional
para sistema operacional.
A última coluna exibe quantos segundos se passaram enquanto o R executava a
expressão. Os resultados mostram que abs_set o valor absoluto foi calculado 30
vezes mais rápido do que abs_loop quando aplicado a um vetor de 10 milhões de
números. Você pode esperar acelerações semelhantes ao escrever código
vetorizado.

Exercício 12.1 (Qual a velocidade de abs?) Muitas funções R preexistentes já são


vetorizadas e otimizadas para execução rápida. Você pode tornar seu código mais
rápido contando com essas funções sempre que possível. Por exemplo, o R vem com
uma função de valor absoluto integrada, abs. Verifique o quanto mais
rápido abscalcula o valor absoluto de long than abs_loop e abs_setdo.
Solução. Você pode medir a velocidade de abs com [Link]. Leva abs 0,05
segundos para calcular o valor absoluto de 10 milhões de números. Isso é 0,592 /
0,054 = 10,96 vezes mais rápido que abs_set e quase 300 vezes mais rápido que
abs_loop:

[Link](abs(long))
## user system elapsed
## 0.037 0.018 0.054

12.2 Como escrever código vetorizado


Código vetorizado é fácil de escrever em R porque a maioria das funções R já são
vetorizadas. Código baseado nessas funções pode ser facilmente vetorizado e,
portanto, rápido. Para criar código vetorizado:
1. Use funções vetorizadas para completar as etapas sequenciais do seu
programa.
2. Use subconjuntos lógicos para lidar com casos paralelos. Tente
manipular todos os elementos de um caso de uma só vez.

abs_loop e abs_set ilustram essas regras. As funções lidam com dois casos e
realizam uma etapa sequencial, Figura 12.1 . Se um número for positivo, as funções
o ignoram. Se um número for negativo, as funções o multiplicam por um negativo.

Figura 12.1: abs_loop usa um loop for para filtrar dados em um dos dois casos: números
negativos e números não negativos.

Você pode identificar todos os elementos de um vetor que se enquadram em um


caso com um teste lógico. O R executará o teste elemento a elemento e retornará
um TRUE para cada elemento pertencente ao caso. Por exemplo, vec < 0
identifica todos os valores de vec que pertencem ao caso negativo. Você pode usar
o mesmo teste lógico para extrair o conjunto de valores negativos com
subconjuntos lógicos:

vec <- c (1, -2, 3, -4, 5, -6, 7, -8, 9, -10)


vec < 0
## FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE
vec[vec < 0]
## -2 -4 -6 -8 -10

O plano da Figura 12.1 agora requer uma etapa sequencial: você deve multiplicar
cada um dos valores negativos por um negativo. Todos os operadores aritméticos do
R são vetorizados, então você pode usar
* para concluir esta etapa de forma vetorizada.
* multiplicará cada número em vec [vec < 0 ] por um negativo ao mesmo
tempo:

vec[vec < 0] * -1
## 2 4 6 8 10

Por fim, você pode usar o operador de atribuição do R, que também é vetorizado,
para salvar o novo conjunto sobre o antigo no vec objeto original. Como <- ele é
vetorizado, os elementos do novo conjunto serão pareados com os elementos do
antigo, em ordem, e então ocorrerá a atribuição elemento a elemento. Como
resultado, cada valor negativo será substituído por seu par positivo, como na
Figura 12.2 .
Figura 12.2: Use subconjuntos lógicos para modificar grupos de valores existentes. Os
operadores aritméticos e de atribuição do R são vetorizados, o que permite manipular e
atualizar vários valores simultaneamente.

Exercício 12.2 (Vetorizar uma Função) A função a seguir converte um vetor de


símbolos de slot em um vetor de novos símbolos de slot. Você consegue vetorizá-la?
Quão mais rápido a versão vetorizada funciona?

change_symbols <- function(vec){


for (i in 1:length(vec)){
if (vec[i] == "DD") {
vec[i] <- "joker"
} else if (vec[i] == "C") {
vec[i] <- "ace"
} else if (vec[i] == "7") {
vec[i] <- "king"
}else if (vec[i] == "B") {
vec[i] <- "queen"
} else if (vec[i] == "BB") {
vec[i] <- "jack"
} else if (vec[i] == "BBB") {
vec[i] <- "ten"
} else {
vec[i] <- "nine"
}
}
vec
}
vec <- c("DD", "C", "7", "B", "BB", "BBB", "0")

change_symbols(vec)
## "joker" "ace" "king" "queen" "jack" "ten" "nine"

many <- rep(vec, 1000000)

[Link](change_symbols(many))
## user system elapsed
## 30.057 0.031 30.079

Solução. change_symbols usa um for loop para classificar valores em sete casos
diferentes, conforme demonstrado na Figura 12.3 .
Para vetorizar change_symbols, crie um teste lógico que possa identificar cada
caso:

vec[vec == "DD"]
## "DD"

vec[vec == "C"]
## "C"

vec[vec == "7"]
## "7"

vec[vec == "B"]
## "B"

vec[vec == "BB"]
## "BB"

vec[vec == "BBB"]
## "BBB"
vec[vec == "0"]
## "0"

Figura 12.3: change_many faz algo diferente para cada um dos sete casos.

Em seguida, escreva um código que possa alterar os símbolos para cada caso:

vec[vec == "DD"] <- "joker"


vec[vec == "C"] <- "ace"
vec[vec == "7"] <- "king"
vec[vec == "B"] <- "queen"
vec[vec == "BB"] <- "jack"
vec[vec == "BBB"] <- "ten"
vec[vec == "0"] <- "nine"

Quando você combina isso em uma função, você tem uma versão vetorizada
que change_symbols roda cerca de 14 vezes mais rápido:

change_vec <- function (vec) {


vec[vec == "DD"] <- "joker"
vec[vec == "C"] <- "ace"
vec[vec == "7"] <- "king"
vec[vec == "B"] <- "queen"
vec[vec == "BB"] <- "jack"
vec[vec == "BBB"] <- "ten"
vec[vec == "0"] <- "nine"
vec
}
[Link](change_vec(many))
## user system elapsed
## 1.994 0.059 2.051

Ou, melhor ainda, use uma tabela de consulta. Tabelas de consulta são um método
vetorizado porque se baseiam nas operações de seleção vetorizada do R:

change_vec2 <- function(vec){


tb <- c("DD" = "joker", "C" = "ace", "7" = "king", "B" = "queen",
"BB" = "jack", "BBB" = "ten", "0" = "nine")
unname(tb[vec])
}
[Link](change_vec(many))
## user system elapsed
## 0.687 0.059 0.746

Aqui, uma tabela de consulta é 40 vezes mais rápida que a função original.

abs_loop e change_many ilustrar uma característica do código vetorizado: os


programadores frequentemente escrevem códigos mais lentos e não vetorizados,
confiando em for loops desnecessários, como o que está em change_many.
Acredito que isso seja resultado de um mal-entendido geral sobre R. for Os loops
não se comportam da mesma forma em R como em outras linguagens, o que
significa que você deve escrever código de forma diferente em R do que faria em
outras linguagens.
Ao escrever em linguagens como C e Fortran, você precisa compilar o código antes
que o computador possa executá-lo. Essa etapa de compilação otimiza o for uso da
memória do computador pelos loops no código, o que for os torna muito rápidos.
Como resultado, muitos programadores usam forloops com frequência ao escrever
em C e Fortran.
No entanto, ao escrever em R, você não compila seu código. Você pula esta etapa, o
que torna a programação em R uma experiência mais amigável. Infelizmente, isso
também significa que você não dá aos seus loops o aumento de velocidade que eles
receberiam em C ou Fortran. Como resultado, seus loops serão executados mais
lentamente do que as outras operações que estudamos: testes lógicos,
subconjuntos e execução elemento a elemento. Se você puder escrever seu código
com as operações mais rápidas em vez de um for loop, faça isso.
Independentemente da linguagem em que você escrever, você deve tentar usar os
recursos da linguagem que executa mais rápido.

se e para
Uma boa maneira de identificar forlaços que podem ser vetorizados é
procurar combinações de if e for. If Só pode ser aplicado a um valor
por vez, o que significa que é frequentemente usado em conjunto com
um forlaço. O for laço ajuda a aplicar if a um vetor inteiro de valores.
Essa combinação geralmente pode ser substituída por subconjuntos
lógicos, que farão a mesma coisa, mas serão executados muito mais
rapidamente.

Isso não significa que você nunca deve usar for loops em R. Ainda há muitos lugares
em R onde for os loops fazem sentido. for Os loops realizam uma tarefa básica que
você nem sempre pode recriar com código vetorizado. for Os loops também são
fáceis de entender e executam razoavelmente rápido em R, desde que você tome
algumas precauções.

12.3 Como escrever loops for rápidos em R


Você pode aumentar drasticamente a velocidade dos seus for loops fazendo duas
coisas para otimizá-los. Primeiro, faça o máximo possível fora do for loop. Cada
linha de código inserida dentro do for loop será executada muitas e muitas vezes.
Se uma linha de código precisar ser executada apenas uma vez, coloque-a fora do
loop para evitar repetições.
Em segundo lugar, certifique-se de que todos os objetos de armazenamento usados
com o loop sejam grandes o suficiente para conter todos os resultados do loop. Por
exemplo, os dois loops abaixo precisarão armazenar um milhão de valores. O
primeiro loop armazena seus valores em um objeto chamado output que começa
com um comprimento de um milhão :
[Link]({
output <- rep(NA, 1000000)
for (i in 1:1000000) {
output[i] <- i + 1
}
})
## user system elapsed
## 1.709 0.015 1.724

O segundo loop armazena seus valores em um objeto chamado output que começa
com um comprimento de 1. O R expandirá o objeto para um comprimento de 1
milhão à medida que executa o loop. O código neste loop é muito semelhante ao
código do primeiro loop, mas leva 37 minutos a mais para ser executado do que o
primeiro loop:

[Link]({
output <- NA
for (i in 1:1000000) {
output[i] <- i + 1
}
})
## user system elapsed
## 1689.537 560.951 2249.927

Os dois laços fazem a mesma coisa, então o que explica a diferença? No segundo
laço, R precisa aumentar o comprimento de output em um a cada execução do
laço. Para isso, R precisa encontrar um novo espaço na memória do seu computador
que possa conter o objeto maior. R deve então copiar o output vetor e apagar a
versão antiga de output antes de passar para a próxima execução do laço. Ao final
do laço, R terá reescrito output na memória do seu computador um milhão de
vezes.
No primeiro caso, o tamanho de output nunca muda; R pode definir um output
objeto na memória e usá-lo para cada execução do for loop.
Os autores do R usam linguagens de baixo nível como C e Fortran para
escrever funções básicas do R, muitas das quais utilizam for laços. Essas
funções são compiladas e otimizadas antes de se tornarem parte do R, o
que as torna bastante rápidas.
Sempre que você vir .Primitive, .Internal ou .Call escrito na
definição de uma função, pode ter certeza de que a função está
chamando código de outra linguagem. Você obterá todas as vantagens
de velocidade dessa linguagem usando a função.

12.4 Código Vetorizado na Prática


Para ver como o código vetorizado pode ajudar você como cientista de dados,
considere nosso projeto de caça-níqueis. Em Loops , você calculou a taxa de
pagamento exata para sua máquina caça-níqueis, mas poderia ter estimado essa
taxa com uma simulação. Se você jogasse na máquina caça-níqueis muitas e muitas
vezes, o prêmio médio de todas as jogadas seria uma boa estimativa da taxa de
pagamento real.
Este método de estimativa baseia-se na lei dos grandes números e é semelhante a
muitas simulações estatísticas. Para executar esta simulação, você pode usar
um for loop:

winnings <- vector(length = 1000000)


for (i in 1:1000000) {
winnings[i] <- play()
}

mean(winnings)
## 0.9366984

A taxa de pagamento estimada após 10 milhões de rodadas é de 0,937, o que é


muito próximo da taxa de pagamento real de 0,934. Observe que estou usando
a score função modificada que trata os diamantes como curingas.
Se você executar esta simulação, notará que ela demora um pouco para ser
executada. De fato, a simulação leva 342.308 segundos para ser executada, o que
equivale a cerca de 5,7 minutos. Isso não é particularmente impressionante, e você
pode melhorar usando código vetorizado:

[Link](for (i in 1:1000000) {
winnings[i] <- play()
})
## user system elapsed
## 342.041 0.355 342.308

A função atual score não é vetorizada. Ela recebe uma única combinação de slots e
usa uma if árvore para atribuir um prêmio a ela. Essa combinação de uma if árvore
com um for laço sugere que você poderia escrever um trecho de código vetorizado
que recebe muitas combinações de slots e, em seguida, usa subconjuntos lógicos
para operar em todas elas de uma só vez.
Por exemplo, você poderia reescrever get_symbols para gerar n combinações de
slots e retorná-las como uma matriz n x 3, como a seguinte. Cada linha da matriz
conterá uma combinação de slots a ser pontuada:

get_many_symbols <- function(n) {


wheel <- c("DD", "7", "BBB", "BB", "B", "C", "0")
vec <- sample(wheel, size = 3 * n, replace = TRUE,
prob = c(0.03, 0.03, 0.06, 0.1, 0.25, 0.01, 0.52))
matrix(vec, ncol = 3)
}

get_many_symbols(5)
## [,1 ] [ ,2 ] [, 3 ]
## [ 1, ] "B" "0" "B"
## [ 2, ] "0" "BB" "7"
## [ 3, ] "0" "0" "BBB"
## [ 4, ] "0" "0" "B"
## [ 5, ] "BBB" "0" "0"

Você também pode reescrever play para receber um parâmetro n, e retornar n


prêmios, em um quadro de dados:

play_many <- function(n) {


symb_mat <- get_many_symbols(n = n)
[Link](w1 = symb_mat[,1], w2 = symb_mat[,2],
w3 = symb_mat[,3], prize = score_many(symb_mat))
}

Esta nova função facilitará a simulação de um milhão, ou até mesmo 10 milhões de


jogadas na máquina caça-níqueis, que será o nosso objetivo. Quando terminarmos,
você poderá estimar a taxa de pagamento com:

# plays <- play_many(10000000))


# mean(plays$prize)

Agora você só precisa escrever score_many, uma versão vetorizada


(matixizada?) score que pega uma matriz n x 3 e retorna n prêmios. Será difícil
escrever esta função porque score ela já é bastante complicada. Eu não esperaria
que você se sentisse confiante para fazer isso sozinho até ter mais prática e
experiência do que conseguimos desenvolver aqui.
Se você quiser testar suas habilidades e escrever uma versão de score_many,
recomendo que use a função rowSums em seu código. Ela calcula a soma de cada
linha de números (ou lógicas) em uma matriz.
Se você quiser se testar de uma forma mais modesta, recomendo que estude a
seguinte score_many função do modelo até entender como cada parte funciona e
como as partes interagem para criar uma função vetorizada. Para isso, será útil criar
um exemplo concreto, como este:

symbols <- matrix(


c("DD", "DD", "DD",
"C", "DD", "0",
"B", "B", "B",
"B", "BB", "BBB",
"C", "C", "0",
"7", "DD", "DD"), nrow = 6, byrow = TRUE)

Symbols
## [,1 ] [,2 ] [,3 ]
## [ 1, ] "DD" "DD" "DD"
## [ 2,] "C" "DD" "0"
## [ 3,] "B" "B" "B"
## [ 4,] "B" "BB" "BBB"
## [ 5,] "C" "C" "0"
## [ 6,] "7" "DD" "DD"

Depois, você pode executar cada linha em score_many relação ao exemplo e


examinar os resultados à medida que avança.

Exercício 12.3 (Teste sua compreensão) Estude a score_many função modelo até
que você tenha certeza de que entendeu como ela funciona e consiga escrever uma
função semelhante.

Exercício 12.4 (Desafio Avançado) Em vez de examinar a resposta do modelo,


escreva sua própria versão vetorizada de score. Suponha que os dados estejam
armazenados em uma matriz n × 3, onde cada linha da matriz contém uma
combinação de espaços a serem pontuados.
Você pode usar a versão score que trata os diamantes como curingas ou a
versão score que não trata. No entanto, a resposta modelo usará a versão que
trata os diamantes como curingas.
Solution. score_many é uma versão vetorizada de score. Você pode usá-lo para
executar a simulação do início desta seção em pouco mais de 20 segundos. Isso é 17
vezes mais rápido do que usar umfor loop:
# symbols should be a matrix with a column for each slot machine window
score_many <- function(symbols) {

# Step 1: Assign base prize based on cherries and diamonds ---------


## Count the number of cherries and diamonds in each
combination
cherries <- rowSums(symbols == "C")
diamonds <- rowSums(symbols == "DD")

## Wild diamonds count as cherries


prize <- c(0, 2, 5)[cherries + diamonds + 1]

## ...but not if there are zero real cherries


### (cherries is coerced to FALSE where cherries == 0)
prize[!cherries] <- 0

# Step 2: Change prize for combinations that contain three of a kind


same <- symbols[, 1] == symbols[, 2] &
symbols[, 2] == symbols[, 3]
payoffs <- c("DD" = 100, "7" = 80, "BBB" = 40,
"BB" = 25, "B" = 10, "C" = 10, "0" = 0)
prize[same] <- payoffs[symbols[same, 1]]

# Step 3: Change prize for combinations that contain all bars ------
bars <- symbols == "B" | symbols == "BB" | symbols == "BBB"
all_bars <- bars[, 1] & bars[, 2] & bars[, 3] & !same
prize[all_bars] <- 5

# Step 4: Handle wilds ---------------------------------------------


## combos with two diamonds
two_wilds <- diamonds == 2

### Identify the nonwild symbol


one <- two_wilds & symbols[, 1] != symbols[, 2] &
symbols[, 2] == symbols[, 3]
two <- two_wilds & symbols[, 1] != symbols[, 2] &
symbols[, 1] == symbols[, 3]
three <- two_wilds & symbols[, 1] == symbols[, 2] &
symbols[, 2] != symbols[, 3]

### Treat as three of a kind


prize[one] <- payoffs[symbols[one, 1]]
prize[two] <- payoffs[symbols[two, 2]]
prize[three] <- payoffs[symbols[three, 3]]

## combos with one wild


one_wild <- diamonds == 1

### Treat as all bars (if appropriate)


wild_bars <- one_wild & (rowSums(bars) == 2)
prize[wild_bars] <- 5

### Treat as three of a kind (if appropriate)


one <- one_wild & symbols[, 1] == symbols[, 2]
two <- one_wild & symbols[, 2] == symbols[, 3]
three <- one_wild & symbols[, 3] == symbols[, 1]
prize[one] <- payoffs[symbols[one, 1]]
prize[two] <- payoffs[symbols[two, 2]]
prize[three] <- payoffs[symbols[three, 3]]

# Step 5: Double prize for every diamond in combo ------------------


unname(prize * 2^diamonds)
}

[Link](play_many(10000000))
## user system elapsed
## 20.942 1.433 22.367

12.4.1 Loops versus código vetorizado


Em muitas linguagens, for os laços são executados muito rapidamente. Como
resultado, os programadores aprendem a usar for laços sempre que possível ao
programar. Muitas vezes, esses programadores continuam a confiar em for laços
quando começam a programar em R, geralmente sem tomar as medidas simples
necessárias para otimizar for os laços de R. Esses programadores podem ficar
desiludidos com R quando seu código não funciona tão rápido quanto gostariam. Se
você acha que isso pode estar acontecendo com você, examine com que frequência
você está usando for laços e para que os está usando. Se você se pega usando for
laços para todas as tarefas, há uma boa chance de que você esteja "falando R com
sotaque de C". A solução é aprender a escrever e usar código vetorizado.
Isso não significa que for laços não tenham lugar em R. for Laços são um recurso
muito útil; eles podem fazer muitas coisas que código vetorizado não pode fazer.
Você também não deve se tornar escravo do código vetorizado. Às vezes, levaria
mais tempo reescrever o código em formato vetorizado do que deixar um for laço
rodar. Por exemplo, seria mais rápido deixar a simulação de slot rodar por 5,7
minutos ou reescrever score?

12.5 Resumo
Código rápido é um componente importante da ciência de dados porque você pode
fazer mais com código rápido do que com código lento. Você pode trabalhar com
conjuntos de dados maiores antes que restrições computacionais intervenham e
pode realizar mais cálculos antes que restrições de tempo interfiram. O código mais
rápido em R dependerá das coisas que R faz de melhor: testes lógicos, subconjuntos
e execução elemento a elemento. Chamei esse tipo de código de código vetorizado
porque o código escrito com essas operações receberá um vetor de valores como
entrada e operará em cada elemento do vetor ao mesmo tempo. A maior parte do
código escrito em R já é vetorizada.
Se você usar essas operações, mas seu código não parecer vetorizado, analise as
etapas sequenciais e os casos paralelos em seu programa. Certifique-se de ter usado
funções vetorizadas para lidar com as etapas e subconjuntos lógicos para lidar com
os casos. Esteja ciente, no entanto, de que algumas tarefas não podem ser
vetorizadas.

12.6 Conclusão do Projeto 3


Agora você escreveu seu primeiro programa em R, e é um programa do qual você
deve se orgulhar. play não é um hello world exercício simples, mas um
programa real que executa uma tarefa real de uma forma complicada.
Escrever novos programas em R sempre será desafiador, pois a programação
depende muito da sua criatividade, capacidade de resolução de problemas e
experiência em escrever programas semelhantes. No entanto, você pode usar as
sugestões deste capítulo para tornar até mesmo o programa mais complexo
administrável: divida as tarefas em etapas e casos simples, trabalhe com exemplos
concretos e descreva as possíveis soluções em inglês.
Este projeto completa a formação que você iniciou em Noções Básicas . Agora você
pode usar R para manipular dados, o que aumentou sua capacidade de analisá-los.
Você pode:
 Carregue e armazene dados no seu computador, não no papel ou na sua
mente
 Lembre-se e altere valores individuais com precisão, sem depender da
sua memória
 Instrua seu computador a executar tarefas tediosas ou complexas em seu
nome
Essas habilidades resolvem um importante problema logístico enfrentado por todo
cientista de dados: como armazenar e manipular dados sem cometer erros? No
entanto, este não é o único problema que você enfrentará como cientista de dados.
O próximo problema surgirá quando você tentar entender as informações contidas
em seus dados. É quase impossível identificar insights ou descobrir padrões em
dados brutos. Um terceiro problema surgirá quando você tentar usar seu conjunto
de dados para raciocinar sobre a realidade, o que inclui coisas não contidas nele. O
que exatamente seus dados implicam sobre coisas fora do conjunto de dados? Quão
certo você pode estar?
Eu me refiro a esses problemas como os problemas logísticos, táticos e estratégicos
da ciência de dados, como mostrado na Figura 12.4 . Você os enfrentará sempre que
tentar aprender com dados:
 Um problema logístico: - Como você pode armazenar e manipular dados sem
cometer erros?
 Um problema tático - Como você pode descobrir as informações contidas em
seus dados?
 Um problema estratégico - Como você pode usar os dados para tirar
conclusões sobre o mundo em geral?

Figura 12.4: Os três principais conjuntos de habilidades da ciência de dados: programação


de computadores, compreensão de dados e raciocínio científico.

Um cientista de dados completo precisará ser capaz de resolver cada um desses


problemas em diversas situações. Ao aprender a programar em R, você domina o
problema logístico, pré-requisito para a resolução dos problemas táticos e
estratégicos.
Se você quiser aprender a raciocinar com dados ou como transformar, visualizar e
explorar seus conjuntos de dados com ferramentas R, recomendo o livro R para
Ciência de Dados , o volume complementar deste livro. R para Ciência de
Dados ensina um fluxo de trabalho simples para transformar, visualizar e modelar
dados em R, além de como relatar resultados com o pacote R Markdown.

Instalando R e RStudio
Para começar a usar o R, você precisa adquirir sua própria cópia. Este apêndice
mostrará como baixar o R e o RStudio, um aplicativo que facilita o uso do R. Você
passará do download do R à abertura da sua primeira sessão do R.
Tanto o R quanto o RStudio são gratuitos e fáceis de baixar.
A.1 Como baixar e instalar o R
O R é mantido por uma equipe internacional de desenvolvedores que disponibiliza a
linguagem por meio da página web da The Comprehensive R Archive Network . O
topo da página fornece três links para download do R. Siga o link que descreve seu
sistema operacional: Windows, Mac ou Linux.

A.1.1 Janelas
Para instalar o R no Windows, clique no link "Baixar R para Windows". Em seguida,
clique no link "base". Em seguida, clique no primeiro link no topo da nova página.
Este link deve ser algo como "Baixar R 3.0.3 para Windows", exceto que a versão
3.0.3 será substituída pela versão mais atual do R. O link baixa um programa
instalador, que instala a versão mais atualizada do R para Windows. Execute este
programa e siga as etapas do assistente de instalação que aparece. O assistente
instalará o R nas pastas de arquivos de programas e criará um atalho no menu
Iniciar. Observe que você precisará ter todos os privilégios de administrador
apropriados para instalar novos softwares em sua máquina.

A.1.2 MAC
Para instalar o R em um Mac, clique no link "Baixar R para Mac". Em seguida, clique
no R-3.0.3link do pacote (ou no link do pacote para a versão mais recente do R). Um
instalador será baixado para guiá-lo pelo processo de instalação, que é muito fácil. O
instalador permite personalizar a instalação, mas os padrões serão adequados para
a maioria dos usuários. Nunca encontrei um motivo para alterá-los. Se o seu
computador exigir uma senha antes de instalar novos programas, você precisará
dela aqui

Binários versus fonte


O R pode ser instalado a partir de binários pré-compilados ou
compilado a partir do código-fonte em qualquer sistema operacional.
Para máquinas Windows e Mac, instalar o R a partir de binários é
extremamente fácil. O binário vem pré-carregado em seu próprio
instalador. Embora seja possível compilar o R a partir do código-fonte
nessas plataformas, o processo é muito mais complicado e não trará
muitos benefícios para a maioria dos usuários. Para sistemas Linux, o
oposto é verdadeiro. Binários pré-compilados podem ser encontrados
para alguns sistemas, mas é muito mais comum compilar o R a partir
de arquivos-fonte ao instalar no Linux. As páginas de download no site
do CRAN fornecem informações sobre como compilar o R a partir do
código-fonte para as plataformas Windows, Mac e Linux.

A.1.3 Linux
O R vem pré-instalado em muitos sistemas Linux, mas você precisará da versão mais
recente do R se a sua estiver desatualizada. O site do CRAN fornece arquivos para
compilar o R a partir do código-fonte em sistemas Debian, Red Hat, SUSE e Ubuntu
no link "Baixar R para Linux". Clique no link e siga o diretório até a versão do Linux
em que deseja instalar. O procedimento exato de instalação varia dependendo do
sistema Linux que você usa. O CRAN orienta o processo agrupando cada conjunto de
arquivos-fonte com a documentação ou arquivos README que explicam como
instalar no seu sistema.

32 bits versus 64 bits


O R está disponível em versões de 32 e 64 bits. Qual você deve usar? Na
maioria dos casos, não importa. Ambas as versões usam inteiros de 32
bits, o que significa que calculam números com a mesma precisão
numérica. A diferença está na maneira como cada versão gerencia a
memória. O R de 64 bits usa ponteiros de memória de 64
bits, e o R de 32 bits usa ponteiros de memória de 32 bits.
Isso significa que o R de 64 bits tem um espaço de memória
maior para usar (e pesquisar).
Como regra geral, compilações de 32 bits do R são mais rápidas do que
compilações de 64 bits, embora nem sempre. Por outro lado,
compilações de 64 bits podem lidar com arquivos e conjuntos de dados
maiores com menos problemas de gerenciamento de memória. Em
ambas as versões, o tamanho máximo permitido do vetor é de cerca de 2
bilhões de elementos. Se o seu sistema operacional não suporta
programas de 64 bits ou se sua RAM é inferior a 4 GB, o R de 32 bits é
ideal para você. Os instaladores para Windows e Mac instalarão
automaticamente ambas as versões se o seu sistema suportar o R de 64
bits.

A.2 Usando R
R não é um programa que você pode abrir e começar a usar, como o Microsoft Word
ou o Internet Explorer. Em vez disso, R é uma linguagem de computador, como C, C+
+ ou UNIX. Você usa R escrevendo comandos na linguagem R e pedindo ao seu
computador para interpretá-los. Antigamente, as pessoas executavam código R em
uma janela de terminal UNIX — como se fossem hackers em um filme dos anos
1980. Agora, quase todo mundo usa R com um aplicativo chamado RStudio, e eu
recomendo que você também faça o mesmo.

R e UNIX
Você ainda pode executar R em uma janela UNIX ou BASH digitando o
comando:
R
que abre um interpretador R. Você pode então fazer seu trabalho e
fechar o interpretador executando q()quando terminar.

A.3 RStudio
O RStudio é um aplicativo como o Microsoft Word — só que, em vez de ajudar você
a escrever em inglês, o RStudio ajuda você a escrever em R. Uso o RStudio em todo
o livro porque ele facilita muito o uso do R. Além disso, a interface do RStudio é a
mesma para Windows, Mac OS e Linux. Isso me ajudará a adaptar o livro à sua
experiência pessoal.
Você pode baixar o RStudio gratuitamente. Basta clicar no botão "Baixar RStudio" e
seguir as instruções simples a seguir. Depois de instalar o RStudio, você pode abri-lo
como qualquer outro programa no seu computador — geralmente clicando em um
ícone na área de trabalho.

As Interfaces gráficas do usuário (GUIs) do R


Usuários de Windows e Mac geralmente não programam a partir de uma
janela de terminal, então os downloads para Windows e Mac para R vêm
com um programa simples que abre uma janela semelhante a um terminal
para você executar código R. É isso que abre quando você clica no ícone R
no seu computador Windows ou Mac. Esses programas fazem um pouco
mais do que a janela de terminal básica, mas não muito. Você pode ouvir
as pessoas se referindo a eles como GUIs R do Windows ou Mac.

Ao abrir o RStudio, uma janela com três painéis é exibida, como na Figura A.1 . O
maior painel é uma janela de console. É aqui que você executará seu código R e verá
os resultados. A janela de console é exatamente o que você veria se executasse o R
em um console UNIX ou nas interfaces gráficas do Windows ou Mac. Todo o resto
que você vê é exclusivo do RStudio. Ocultos nos outros painéis estão um editor de
texto, uma janela gráfica, um depurador, um gerenciador de arquivos e muito mais.
Você aprenderá sobre esses painéis à medida que se tornarem úteis ao longo deste
livro.

Figura A.1: O
IDE RStudio para R.

Ainda preciso baixar o R?


Mesmo se você usar o RStudio, ainda precisará baixá-lo para o
seu computador. O RStudio ajuda você a usar a versão do R que
está instalada no seu computador, mas não vem com uma
versão própria do R.

A.4 Abertura R
Agora que você tem o R e o RStudio no seu computador, pode começar a usar o R
abrindo o programa RStudio. Abra o RStudio como faria com qualquer programa,
clicando em seu ícone ou digitando "RStudio" no prompt Executar do Windows.

Pacotes B R
Muitas das funções mais úteis do R não vêm pré-carregadas quando você inicia o R,
mas residem em pacotes que podem ser instalados sobre ele. Os pacotes do R são
semelhantes a bibliotecas em C, C++ e Javascript, pacotes em Python e gems em
Ruby. Um pacote do R reúne funções úteis, arquivos de ajuda e conjuntos de dados.
Você pode usar essas funções dentro do seu próprio código R assim que carregar o
pacote em que elas residem. Normalmente, o conteúdo de um pacote do R está
todo relacionado a um único tipo de tarefa, que o pacote ajuda a resolver. Os
pacotes do R permitirão que você aproveite os recursos mais úteis do R: sua grande
comunidade de criadores de pacotes (muitos dos quais são cientistas de dados
ativos) e suas rotinas pré-escritas para lidar com muitas tarefas comuns (e exóticas)
de ciência de dados.
BASE R
Você pode ouvir usuários de R (ou eu) se referindo ao "base R".
O que é o base R? É apenas o conjunto de funções R que é
carregado toda vez que você inicia o R. Essas funções fornecem
os fundamentos da linguagem, e você não precisa carregar um pacote
antes de usá-las.

B.1 Instalando Pacotes


Para usar um pacote R, você precisa primeiro instalá-lo no seu computador e depois
carregá-lo na sua sessão R atual. A maneira mais fácil de instalar um pacote R é com
a [Link]ção R. Abra o R e digite o seguinte na linha de comando:

[Link]("<package name>")

Isso procurará o pacote especificado na coleção de pacotes hospedada no site do


CRAN. Quando o R encontrar o pacote, ele o baixará para uma pasta de bibliotecas
no seu computador. O R poderá acessar o pacote aqui em sessões futuras do R sem
precisar reinstalá-lo. Qualquer pessoa pode escrever um pacote R e distribuí-lo
como quiser; no entanto, quase todos os pacotes R são publicados através do site
do CRAN. O CRAN testa cada pacote R antes de publicá-lo. Isso não elimina todos os
bugs dentro de um pacote, mas significa que você pode confiar que um pacote no
CRAN será executado na versão atual do R no seu sistema operacional.
Você pode instalar vários pacotes de uma só vez vinculando seus nomes à função
concatenate do R. cPor exemplo, para instalar os pacotes ggplot2, reshape2 e dplyr,
execute:

[Link](c("ggplot2", "reshape2", "dplyr"))

Se esta for a primeira vez que você instala um pacote, o R solicitará que você
escolha um espelho online para instalar. Os espelhos são listados por localização.
Seus downloads devem ser mais rápidos se você selecionar um espelho próximo a
você. Se quiser baixar um novo pacote, experimente primeiro o espelho Áustria.
Este é o repositório principal do CRAN, e novos pacotes podem levar alguns dias
para chegar a todos os outros espelhos.

B.2 Carregando Pacotes


Instalar um pacote não coloca suas funções imediatamente ao seu alcance. Apenas
as coloca no seu computador. Para usar um pacote R, você precisa carregá-lo na sua
sessão R com o comando:

library(<package name>)

Observe que as aspas desapareceram. Você pode usá-las se quiser, mas as aspas são
opcionais para o library comando. (Isso não se aplica ao [Link]
comando).
library disponibilizará todas as funções, conjuntos de dados e arquivos de ajuda do
pacote até que você feche sua sessão atual do R. Na próxima vez que iniciar uma
sessão do R, você terá que recarregar o pacote library se quiser usá-lo, mas não
precisará reinstalá-lo. Você só precisa instalar cada pacote uma vez. Depois disso,
uma cópia do pacote ficará na sua biblioteca do R. Para ver quais pacotes você
possui atualmente na sua biblioteca do R, execute:

library( )
library( ) Também mostra o caminho para a sua biblioteca R, que é a pasta que
contém seus pacotes R. Você pode notar muitos pacotes que não se lembra de ter
instalado. Isso ocorre porque o R baixa automaticamente um conjunto de pacotes
úteis quando você o instala pela primeira vez.

Instale pacotes de (quase) qualquer lugar


O devtoolspacote R facilita a instalação de pacotes de outros locais
além do site do CRAN. O devtools fornece funções
como install_github, install_gitorious, install_bitbucket e
install_url. Elas funcionam de forma semelhante
ao [Link], mas buscam pacotes R em novos
locais. install_github É especialmente útil porque muitos
desenvolvedores R disponibilizam versões de desenvolvimento de seus
pacotes no GitHub. A versão de desenvolvimento de um pacote conterá
uma prévia de novas funções e patches, mas pode não ser tão estável
ou livre de bugs quanto a versão do CRAN.
Por que o R exige que você se preocupe em instalar e carregar pacotes? Imagine um
R onde todos os pacotes já vêm pré-carregados, mas este seria um programa muito
grande e lento. Em 6 de maio de 2014, o site do CRAN hospedava 5.511 pacotes. É
mais fácil instalar e carregar apenas os pacotes que você deseja usar quando quiser.
Isso mantém sua cópia do R rápida, pois tem menos funções e páginas de ajuda para
pesquisar ao mesmo tempo. Essa configuração também traz outros benefícios. Por
exemplo, é possível atualizar sua cópia de um pacote do R sem atualizar toda a sua
cópia do R.
Qual é a melhor maneira de aprender sobre pacotes R?
É difícil usar um pacote R se você não sabe que ele existe. Você pode
acessar o site do CRAN e clicar no link "Pacotes" para ver uma lista de
pacotes disponíveis, mas terá que analisar milhares deles. Além disso,
muitos pacotes R fazem a mesma coisa.
Como saber qual pacote funciona melhor? A lista de
discussão R-packages é um bom ponto de partida. Ela
envia anúncios de novos pacotes e mantém um arquivo de anúncios
antigos. Blogs que agregam posts sobre R também podem fornecer
leads valiosos. Recomendo o R-bloggers . O RStudio mantém uma lista
de alguns dos pacotes R mais úteis na seção "Introdução" do site
[Link] . Por fim, o CRAN agrupa alguns dos pacotes
mais úteis — e mais respeitados — por área temática . Este é um
excelente lugar para aprender sobre os pacotes projetados para sua
área de atuação.
C Atualizando R e seus pacote
A Equipe de Desenvolvimento do Núcleo R aprimora continuamente a linguagem R,
identificando bugs, melhorando o desempenho e atualizando o R para que ele
funcione com novas tecnologias. Como resultado, novas versões do R são lançadas
diversas vezes ao ano. A maneira mais fácil de se manter atualizado com o R é
consultar periodicamente o site do CRAN . O site é atualizado a cada nova versão e a
disponibiliza para download. Você precisará instalar a nova versão. O processo é o
mesmo da instalação do R pela primeira vez.
Não se preocupe se não estiver interessado em se manter atualizado sobre as
novidades do R Core. O R muda apenas ligeiramente entre os lançamentos, e é
improvável que você perceba as diferenças. No entanto, atualizar para a versão
atual do R é um bom ponto de partida caso encontre um bug que não consiga
explicar.
O RStudio também aprimora constantemente seu produto. Você pode obter as
atualizações mais recentes simplesmente baixando-as do RStudio .

Pacotes C.1 R
Autores de pacotes ocasionalmente lançam novas versões de seus pacotes para
adicionar funções, corrigir bugs ou melhorar o desempenho. O [Link]
comando verifica se você possui a versão mais recente de um pacote e, caso
contrário, instala a versão mais recente. A sintaxe para [Link] segue a
de [Link]. Se você já possui ggplot2, reshape2 e dplyr em seu
computador, é uma boa ideia verificar se há atualizações antes de usá-los:

[Link](c("ggplot2", "reshape2", "dplyr"))

Você deve iniciar uma nova sessão do R após atualizar os pacotes. Se você tiver um
pacote carregado ao atualizá-lo, será necessário fechar a sessão do R e abrir uma
nova para começar a usar a versão atualizada do pacote.

D Carregando e salvando dados em R


Este apêndice mostrará como carregar e salvar dados no R a partir de arquivos de
texto simples, arquivos R e planilhas do Excel. Também mostrará os pacotes R que
você pode usar para carregar dados de bancos de dados e outros programas
comuns, como SAS e MATLAB.

D.1 Conjuntos de dados em base R


O R vem com muitos conjuntos de dados pré-carregados no datasets pacote, que
vem com o R básico. Esses conjuntos de dados não são muito interessantes, mas
oferecem a oportunidade de testar código ou fazer uma observação sem precisar
carregar um conjunto de dados de fora do R. Você pode ver uma lista de conjuntos
de dados do R, bem como uma breve descrição de cada um, executando:

help(package = "datasets")

Para usar um conjunto de dados, basta digitar seu nome. Cada conjunto de dados já
está pré-salvo como um objeto R. Por exemplo:

iris
## [Link] [Link] [Link] [Link]
Species
## 1 5.1 3.5 1.4 0.2
setosa
## 2 4.9 3.0 1.4 0.2
setosa
## 3 4.7 3.2 1.3 0.2
setosa
## 4 4.6 3.1 1.5 0.2
setosa
## 5 5.0 3.6 1.4 0.2
setosa
## 6 5.4 3.9 1.7 0.4
setosa

No entanto, os conjuntos de dados do R não substituem os seus próprios dados, que


você pode carregar no R a partir de uma ampla variedade de formatos de arquivo.
Mas antes de carregar qualquer arquivo de dados no R, você precisará determinar
onde está seu diretório de trabalho .

D.2 Diretório de Trabalho


Cada vez que você abre o R, ele se vincula a um diretório no seu computador, que o
R chama de diretório de trabalho. É aqui que o R procurará os arquivos quando você
tentar carregá-los e é onde o R salvará os arquivos quando você os salvar. A
localização do seu diretório de trabalho varia de acordo com o computador. Para
determinar qual diretório o R está usando como seu diretório de trabalho, execute:

getwd()
## "/Users/garrettgrolemund"

Você pode colocar os arquivos de dados diretamente na pasta que é o seu diretório
de trabalho ou pode movê-lo para onde estão os arquivos de dados. Você pode
mover o diretório de trabalho para qualquer pasta do seu computador com a
função setwd. Basta fornecer setwd o caminho do arquivo para o seu novo
diretório de trabalho. Eu prefiro definir meu diretório de trabalho como uma pasta
dedicada ao projeto em que estou trabalhando no momento. Dessa forma, posso
manter todos os meus dados, scripts, gráficos e relatórios no mesmo lugar. Por
exemplo:

setwd("~/Users/garrettgrolemund/Documents/Book_Project")

Se o caminho do arquivo não começar no seu diretório raiz, o R assumirá que ele
começa no seu diretório de trabalho atual.
Você também pode alterar seu diretório de trabalho clicando em Sessão > Definir
Diretório de Trabalho > Escolher Diretório na barra de menus do RStudio. As
interfaces gráficas do Windows e do Mac têm opções semelhantes. Se você iniciar o
R a partir de uma linha de comando UNIX (como em máquinas Linux), o diretório de
trabalho será o diretório em que você estava quando chamou o R.
Você pode ver quais arquivos estão no seu diretório de trabalho com [Link]( ).
Se você vir o arquivo que deseja abrir no seu diretório de trabalho, estará pronto
para prosseguir. A forma como você abre os arquivos no seu diretório de trabalho
dependerá do tipo de arquivo que você deseja abrir.
D.3 Arquivos de texto simples
Arquivos de texto simples são uma das formas mais comuns de salvar dados. Eles
são muito simples e podem ser lidos por diversos programas de computador — até
mesmo pelos editores de texto mais básicos. Por esse motivo, dados públicos
geralmente vêm como arquivos de texto simples. Por exemplo, o Census Bureau, a
Administração da Previdência Social e o Bureau of Labor Statistics disponibilizam
seus dados como arquivos de texto simples.
Veja como o conjunto de dados do Royal Flush do R Objects apareceria como um
arquivo de texto simples (adicionei uma coluna de valor):

"card", "suit", "value"


"ace", "spades", 14
"king", "spades", 13
"queen", "spades", 12
"jack", "spades", 11
"ten", "spades", 10

Um arquivo de texto simples armazena uma tabela de dados em um documento de


texto. Cada linha da tabela é salva em sua própria linha, e uma convenção simples é
usada para separar as células dentro de uma linha. Muitas vezes, as células são
separadas por uma vírgula, mas também podem ser separadas por uma tabulação,
uma barra vertical (ou seja, |) ou qualquer outro caractere. Cada arquivo usa apenas
um método de separação de células, o que minimiza a confusão. Dentro de cada
célula, os dados aparecem como você espera, como palavras e números.
Todos os arquivos de texto simples podem ser salvos com a extensão .txt (para
texto), mas às vezes um arquivo recebe uma extensão especial que indica como ele
separa as entradas das células de dados. Como as entradas no conjunto de dados
mencionado anteriormente são separadas por vírgula, este arquivo seria um
arquivo de valores separados por vírgula e normalmente seria salvo com a
extensão .csv .

D.3.1 [Link]
Para carregar um arquivo de texto simples, use [Link]. O primeiro argumento
de [Link] deve ser o nome do seu arquivo (se estiver no seu diretório de
trabalho) ou o caminho para o seu arquivo (se não estiver no seu diretório de
trabalho). Se o caminho do arquivo não começar com o seu diretório raiz, o R o
anexará ao final do caminho do arquivo que leva ao seu diretório de trabalho.
Você [Link] também pode fornecer outros argumentos. Os dois mais
importantes são sep e header.
Se o conjunto de dados do Royal Flush foi salvo como um arquivo
chamado [Link] no seu diretório de trabalho, você pode carregá-lo com:

poker <- [Link]("[Link]", sep = ",", header = TRUE)

D.3.1.1 setembro
Use sep para informar [Link] qual caractere seu arquivo usa para separar
entradas de dados. Para descobrir isso, talvez seja necessário abrir o arquivo em um
editor de texto e examiná-lo. Se você não especificar
um separgumento, [Link] o comando tentará separar células sempre que
houver espaços em branco, como tabulações ou espaços. O R não será capaz de
dizer se [Link] isso está correto ou não, portanto, confie nele por sua conta e
risco.

Cabeçalho D.3.1.2
Use header para informar [Link] se a primeira linha do arquivo contém
nomes de variáveis em vez de valores. Se a primeira linha do arquivo for um
conjunto de nomes de variáveis, você deve definir header = TRUE.

D.3.1.3 [Link]
Frequentemente, conjuntos de dados usam símbolos especiais para representar
informações ausentes. Se você sabe que seus dados usam um determinado símbolo
para representar entradas ausentes, você pode informar [Link] (e as funções
anteriores) qual é o símbolo com o [Link] argumento . , [Link] que
converterá todas as instâncias do símbolo de informação ausente para NA, que é o
símbolo de informação ausente do R (consulte Informações Ausentes ).
Por exemplo, seu conjunto de dados de pôquer continha valores ausentes
armazenados como um ., como este:
## "card","suit","value"
## "ace"," spades"," 14"
## "king"," spades"," 13"
## "queen", ".", "."
## "jack", ".", "."
## "ten", ".", "."
Você pode ler o conjunto de dados em R e converter os valores ausentes em NAs
conforme você executa o comando:

poker <- [Link]("[Link]", sep = ",", header = TRUE, [Link] = ".")

R salvaria uma versão pokerque se parece com isso:

## card suit value


## ace spades 14
## king spades 13
## queen <NA> NA
## jack <NA> NA
## ten <NA> NA

D.3.1.4 pular e nrow


Às vezes, um arquivo de texto simples vem com um texto introdutório que não faz
parte do conjunto de dados. Ou você pode decidir ler apenas parte de um conjunto
de dados. Você pode fazer isso com os argumentos skip e . Use para dizer ao R para
pular um número específico de linhas antes de começar a ler os valores do arquivo.
Use para dizer ao R para parar de ler os valores após ter lido um determinado
número de linhas. nrow skip nrow
Por exemplo, imagine que o arquivo completo do royal flush se parece com isso:

This data was collected by the National Poker Institute.


We accidentally repeated the last row of data.
"card", "suit", "value"
"ace", "spades", 14
"king", "spades", 13
"queen", "spades", 12
"jack", "spades", 11
"ten", "spades", 10
"ten", "spades", 10

Você pode ler apenas as seis linhas que deseja (cinco linhas mais um cabeçalho)
com:

[Link]("[Link]", sep = ",", header = TRUE, skip = 3, nrow = 5)


## card suit value
## 1 ace spades 14
## 2 king spades 13
## 3 queen spades 12
## 4 jack spades 11
## 5 ten spades 10

Observe que a linha de cabeçalho não conta para o total de linhas permitidas por
nrow.

D.3.1.5 strings As Factors


O R lê números exatamente como você esperaria, mas quando ele encontra
sequências de caracteres (por exemplo, letras e palavras), ele começa a agir de
forma estranha. O R quer converter cada sequência de caracteres em um fator. Este
é o comportamento padrão do R, mas acho que é um erro. Às vezes, os fatores são
úteis. Outras vezes, eles são claramente o tipo de dado errado para o trabalho. Além
disso, os fatores causam um comportamento estranho, especialmente quando você
deseja exibir dados. Esse comportamento pode ser surpreendente se você não
percebeu que o R converteu seus dados em fatores. Em geral, você terá uma
experiência R mais tranquila se não deixar o R criar fatores até que você os peça.
Felizmente, é fácil fazer isso.
Definir o argumento stringsAsFactors como FALSE garantirá que o R salve
quaisquer sequências de caracteres no seu conjunto de dados como sequências de
caracteres, e não como fatores. Para usar stringsAsFactors, você escreveria:

[Link]("[Link]", sep = ",", header = TRUE, stringsAsFactors = FALSE)

Se você for carregar mais de um arquivo de dados, poderá alterar o comportamento


de fatoração padrão no nível global com:

options(stringsAsFactors = FALSE)

Isso garantirá que todas as strings serão lidas como strings, não como fatores, até
que você encerre sua sessão R ou altere novamente o padrão global executando:

options(stringsAsFactors = TRUE)

D.3.2 A Família de Leitura


R também vem com alguns atalhos pré-empacotados para [Link], mostrados
na Tabela D.1 .

Tabela D.1: Funções de leitura do R. Você pode substituir qualquer um dos argumentos
padrão conforme necessário.

Função Padrões Usar

[Link] sep = " ", cabeçalho = Função de leitura de uso geral


Tabela D.1: Funções de leitura do R. Você pode substituir qualquer um dos argumentos
padrão conforme necessário.

Função Padrões Usar

FALSO

sep = “,”, cabeçalho = Arquivos de variáveis separadas por vírgula


[Link]
VERDADEIRO (CSV)

sep = “”, cabeçalho =


[Link] Arquivos delimitados por tabulação
TRUE

sep = “;”, cabeçalho = Arquivos CSV com formato decimal


read.csv2
TRUE, dec = “,” europeu

sep = “”, cabeçalho = Arquivos delimitados por tabulação com


read.delim2
TRUE, dec = “,” formato decimal europeu

O primeiro atalho, [Link], se comporta exatamente como [Link], mas define


automaticamente sep = ","e header = TRUE, o que pode economizar algum
tempo de digitação:

poker <- [Link]("[Link]")

[Link] define automaticamente sep para o caractere de tabulação, o que é


muito útil para ler arquivos delimitados por tabulação. Esses são arquivos em que
cada célula é separada por uma tabulação. [Link] Também definido header
= TRUE por padrão.
read.delim2 e read.csv2 existem para usuários europeus do R. Essas funções
informam ao R que os dados usam uma vírgula em vez de um ponto final para
indicar casas decimais. (Se você está se perguntando como isso funciona com
arquivos CSV, os arquivos CSV2 geralmente separam as células com um ponto e
vírgula, não uma vírgula.)
Importar conjunto de dados
Você também pode carregar arquivos de texto simples com o botão
Importar Conjunto de Dados do RStudio, conforme descrito
em Carregando Dados . A opção Importar Conjunto de Dados fornece
uma versão GUI do arquivo [Link].

D.3.3 [Link]
Um tipo de arquivo de texto simples desafia o padrão ao usar seu layout para
separar células de dados. Cada linha é colocada em sua própria linha (como em
outros arquivos de texto simples) e, em seguida, cada coluna começa com um
número específico de caracteres a partir do lado esquerdo do documento. Para isso,
um número arbitrário de espaços de caracteres é adicionado ao final de cada
entrada para posicionar corretamente a próxima entrada. Esses documentos são
conhecidos como arquivos de largura fixa e geralmente terminam com a
extensão .fwf .
Aqui está uma maneira de o conjunto de dados do Royal Flush parecer um arquivo
de largura fixa. Em cada linha, a entrada do naipe começa exatamente 10 caracteres
a partir do início da linha. Não importa quantos caracteres apareceram na primeira
célula de cada linha:

card suit value


ace spades 14
king spades 13
queen spades 12
jack spades 11
10 spades 10

Arquivos de largura fixa parecem agradáveis aos olhos humanos (mas não melhores
do que um arquivo delimitado por tabulação); no entanto, podem ser difíceis de
trabalhar. Talvez por isso, o R venha com uma função para ler arquivos de largura
fixa, mas nenhuma função para salvá-los. Infelizmente, as agências governamentais
dos EUA parecem gostar de arquivos de largura fixa, e você provavelmente
encontrará um ou mais deles ao longo da sua carreira.
Você pode ler arquivos de largura fixa no R com a função [Link]. A função
recebe os mesmos argumentos que [Link], mas requer um argumento
adicional, widths, que deve ser um vetor de números. Cada i-ésima entrada
do widthsvetor deve indicar a largura (em caracteres) da i-ésima coluna do
conjunto de dados.
Se os dados do royal flush de largura fixa mencionados acima foram salvos
como [Link] no seu diretório de trabalho, você pode lê-los com:

poker <- [Link]("[Link]", widths = c(10, 7, 6), header = TRUE)

D.3.4 Links Html


Muitos arquivos de dados são disponibilizados na internet em seus próprios
endereços web. Se você estiver conectado à internet, poderá abrir esses arquivos
diretamente no R com [Link], [Link], etc. Você pode passar um
endereço web no argumento do nome do arquivo para qualquer uma das funções
de leitura de dados do R. Como resultado, você pode ler o conjunto de dados de
pôquer de um endereço web como http:// …/[Link] com:

poker <- [Link]("[Link]

Obviamente esse não é um endereço real, mas aqui está algo que pode funcionar —
se você conseguir digitar!

Deck <-[Link]("[Link]
ee5dfc039fd581cb467cc69c226ea2524913c3d8/[Link]")

Certifique-se de que o endereço da web leve diretamente ao arquivo e não a uma


página da web que leve ao arquivo. Normalmente, ao visitar o endereço da web de
um arquivo de dados, o arquivo começará a ser baixado ou os dados brutos
aparecerão na janela do seu navegador.
Observe que sites que começam com _ [Link] são sites seguros, o que significa
que o R pode não conseguir acessar os dados fornecidos nesses links.

D.3.5 Salvando arquivos de texto simples


Depois que seus dados estiverem em R, você poderá salvá-los em qualquer formato
de arquivo compatível com R. Se desejar salvá-los como um arquivo de texto
simples, você pode usar a família de funções +write+. As três funções básicas de
gravação aparecem na Tabela D.2 . Use [Link] para salvar seus dados como um
arquivo .csv e [Link] para salvá-los como um documento delimitado por
tabulação ou um documento com separadores mais exóticos.

Tabela D.2: R salva conjuntos de dados em arquivos de texto simples com a


família de funções write

Formato de arquivo Função e sintaxe

[Link](r_object, file = filepath, [Link]


.csv
= FALSE)

.csv (com notação decimal write.csv2(r_object, file = filepath,


europeia) [Link] = FALSE)

[Link](r_object, file = filepath, sep = "\


delimitado por tabulação
t", [Link]=FALSE)

O primeiro argumento de cada função é o objeto R que contém seu conjunto de


dados. O file argumento é o nome do arquivo (incluindo a extensão) que você
deseja atribuir aos dados salvos. Por padrão, cada função salvará seus dados em seu
diretório de trabalho. No entanto, você pode fornecer um caminho de arquivo para
o argumento "file". O R fará isso salvando o arquivo no final do caminho. Se o
caminho do arquivo não começar com seu diretório raiz, o R o anexará ao final do
caminho do arquivo que leva ao seu diretório de trabalho.
Por exemplo, você pode salvar o quadro de dados de pôquer (hipotético) em um
subdiretório chamado data dentro do seu diretório de trabalho com o comando:

[Link](poker, "data/[Link]", [Link] = FALSE)


Lembre-se de que [Link] não [Link] é possível criar novos diretórios no
seu computador. Cada pasta no caminho do arquivo deve existir antes que você
tente salvar um arquivo com ela.
O [Link] argumento impede que o R salve os nomes das linhas do quadro de
dados como uma coluna no arquivo de texto simples. Você deve ter notado que o R
nomeia automaticamente cada linha em um quadro de dados com um número. Por
exemplo, cada linha em nosso quadro de dados de pôquer aparece com um número
ao lado:
poker
## card suit value
## 1 ace spades 14
## 2 king spades 13
## 3 queen spades 12
## 4 jack spades 11
## 5 10 spades 10

Esses números de linha são úteis, mas podem se acumular rapidamente se você
começar a salvá-los. O R adicionará um novo conjunto de números por padrão
sempre que você ler o arquivo novamente. Evite isso sempre definindo [Link] =
FALSE quando usar uma função na write família.

D.3.6 Compactando Arquivos


Para compactar um arquivo de texto simples, coloque o nome ou o caminho do
arquivo entre as funções bzfile, gzfile, ou xzfile. Por exemplo:

[Link](poker, file = bzfile("data/[Link].bz2"), [Link] = FALSE)

Cada uma dessas funções compactará a saída com um tipo diferente de formato de
compactação, mostrado na Tabela D.3 .

Tabela D.3: R vem com três funções auxiliares para compactar arquivos

Função Tipo de compressão

bzfile bzip2

gzfile gnu zip (gzip)


Tabela D.3: R vem com três funções auxiliares para compactar arquivos

Função Tipo de compressão

xzfile compressão xz

É uma boa ideia ajustar a extensão do seu arquivo para refletir a


compactação. read As funções do R abrirão arquivos de texto simples compactados
em qualquer um desses formatos. Por exemplo, você poderia ler um arquivo
compactado chamado [Link].bz2 com:

[Link]("[Link].bz2")
ou:
[Link]("data/[Link].bz2")

dependendo de onde o arquivo é salvo.

Arquivos D.4 R
O R fornece dois formatos de arquivo próprios para armazenar
dados: .RDS e .RData . Os arquivos RDS podem armazenar um único objeto R, e os
arquivos RData podem armazenar vários objetos R.
Você pode abrir um arquivo RDS com readRDS. Por exemplo, se os dados do Royal
Flush foram salvos como [Link] , você pode abri-los com:

poker <- readRDS("[Link]")

Abrir arquivos RData é ainda mais fácil. Basta executar a função loadcom o arquivo:

load("[Link]")

Não há necessidade de atribuir a saída a um objeto. Os objetos R no seu arquivo


RData serão carregados na sua sessão R com seus nomes originais. Arquivos RData
podem conter vários objetos R, portanto, carregar um pode incluir vários
objetos. load não informa quantos objetos está lendo, nem quais são seus nomes,
portanto, vale a pena conhecer um pouco sobre o arquivo RData antes de carregá-
lo.
Na pior das hipóteses, você pode ficar de olho no painel de ambiente do RStudio
enquanto carrega um arquivo RData. Ele exibe todos os objetos que você criou ou
carregou durante sua sessão R. Outro truque útil é colocar parênteses em torno do
comando de carregamento, como em (load("[Link]")). Isso fará com que
o R imprima os nomes de cada objeto carregado do arquivo.
Ambas as funções readRDS e load recebem um caminho de arquivo como
primeiro argumento, assim como as outras funções de leitura e escrita do R. Se o
seu arquivo estiver no seu diretório de trabalho, o caminho do arquivo será o nome
do arquivo.

D.4.1 Salvando arquivos R


Você pode salvar um objeto R, como um quadro de dados, como um arquivo RData
ou RDS. Arquivos RData podem armazenar vários objetos R simultaneamente, mas
os arquivos RDS são a melhor opção, pois promovem código reproduzível.
Para salvar dados como um objeto RData, use a save função . Para salvar dados
como um objeto RDS, use a saveRDS função . Em cada caso, o primeiro argumento
deve ser o nome do objeto R que você deseja salvar. Em seguida, você deve incluir
um argumento de arquivo que contenha o nome ou o caminho do arquivo no qual
deseja salvar o conjunto de dados.
Por exemplo, se você tiver três objetos R, a, b e c, você pode salvá-los todos no
mesmo arquivo RData e recarregá-los em outra sessão R:

a <- 1
b <- 2
c <- 3
save(a, b, c, file = "[Link]")
load("[Link]")

No entanto, se você esquecer os nomes dos seus objetos ou entregar o arquivo para
outra pessoa usar, será difícil determinar o que havia no arquivo — mesmo depois
que você (ou essa pessoa) o carregar. A interface do usuário para arquivos RDS é
muito mais clara. Você pode salvar apenas um objeto por arquivo, e quem o
carregar pode decidir como deseja chamar seus novos dados. Como bônus, você não
precisa se preocupar em load sobrescrever quaisquer objetos R que tenham o
mesmo nome dos objetos que você está carregando:

saveRDS(a, file = "[Link]")


a <- readRDS("[Link]")

Salvar seus dados como um arquivo R oferece algumas vantagens em relação a


salvá-los como um arquivo de texto simples. O R compacta o arquivo
automaticamente e também salva quaisquer metadados relacionados ao R
associados ao seu objeto. Isso pode ser útil se seus dados contiverem fatores, datas
e horas ou atributos de classe. Você não precisará analisar novamente essas
informações no R como faria se convertesse tudo para um arquivo de texto.
Por outro lado, arquivos R não podem ser lidos por muitos outros programas, o que
os torna ineficientes para compartilhamento. Eles também podem criar um
problema para armazenamento de longo prazo se você acha que não terá uma cópia
do R ao reabri-los.

D.5 Planilhas do Excel


O Microsoft Excel é um programa de planilhas popular que se tornou quase um
padrão no mundo dos negócios. É bem provável que você precise trabalhar com
uma planilha do Excel em R pelo menos uma vez na sua carreira. Você pode ler
planilhas em R e também salvar dados de R como uma planilha de diversas
maneiras.

D.5.1 Exportar do Excel


O melhor método para mover dados do Excel para o R é exportar a planilha do Excel
como um arquivo .csv ou .txt . Não só o R conseguirá ler o arquivo de texto, como
qualquer outro software de análise de dados. Arquivos de texto são a língua franca
do armazenamento de dados.
Exportar os dados também resolve outra dificuldade. O Excel usa formatos e
metadados proprietários que não são facilmente transferidos para o R. Por exemplo,
um único arquivo do Excel pode incluir várias planilhas, cada uma com suas próprias
colunas e macros. Quando o Excel exporta o arquivo como .csv ou .txt , ele garante
que esse formato seja transferido para um arquivo de texto simples da maneira
mais adequada. O R pode não conseguir gerenciar a conversão com a mesma
eficiência.
Para exportar dados do Excel, abra a planilha do Excel e acesse Salvar como no
menu do botão do Microsoft Office. Em seguida, escolha CSV na caixa Salvar como
tipo que aparece e salve os arquivos. Você poderá então ler o arquivo no R com
a [Link] função .

D.5.2 Copiar e Colar


Você também pode copiar partes de uma planilha do Excel e colá-las no R. Para
fazer isso, abra a planilha e selecione as células que deseja ler no R. Em seguida,
selecione Editar > Copiar na barra de menus — ou use um atalho de teclado — para
copiar as células para a área de transferência.
Na maioria dos sistemas operacionais, você pode ler os dados armazenados na sua
área de transferência no R com:

[Link]("clipboard")

Em Macs, você precisará usar:

[Link](pipe("pbpaste"))

Se as células contiverem valores com espaços, isso causará


interrupções [Link]. Você pode tentar outra read função (ou simplesmente
exportar formalmente os dados do Excel) antes de lê-los no R.

D.5.3 XLConnect
Muitos pacotes foram escritos para ajudar você a ler arquivos do Excel diretamente
no R. Infelizmente, muitos desses pacotes não funcionam em todos os sistemas
operacionais. Outros ficaram desatualizados devido ao formato de arquivo .xlsx . Um
pacote que funciona em todos os sistemas de arquivos (e recebe boas avaliações) é
o XLConnect. Para usá-lo, você precisará instalar e carregar o pacote:

[Link]("XLConnect")
library(XLConnect)
O XLConnect depende do Java para ser independente de plataforma. Portanto, ao
abrir o XLConnect pela primeira vez, o RStudio pode solicitar o download de um Java
Runtime Environment, caso você ainda não tenha um.

D.5.4 Leitura de planilhas


Você pode usar o XLConnect para ler em uma planilha do Excel com um processo de
uma ou duas etapas. Começarei com o processo de duas etapas. Primeiro, carregue
uma pasta de trabalho do Excel com loadWorkbook. loadWorkbook Ele pode
carregar arquivos .xls e .xlsx . Ele recebe um argumento: o caminho do arquivo
para a sua pasta de trabalho do Excel (este será o nome da pasta de trabalho se ela
estiver salva no seu diretório de trabalho):

wb <- loadWorkbook("[Link]")

Em seguida, leia uma planilha da pasta de trabalho com readWorksheet, que


recebe vários argumentos. O primeiro argumento deve ser um objeto da pasta de
trabalho criado com loadWorkbook. O próximo argumento, sheet, deve ser o
nome da planilha na pasta de trabalho que você gostaria de ler no R. Este será o
nome que aparecerá na aba inferior da planilha. Você também pode
fornecer sheet um número, que especifica a planilha que você deseja ler (um para
a primeira planilha, dois para a segunda e assim por diante).
readWorksheet então recebe quatro argumentos que especificam uma caixa
delimitadora de células para leitura: startRow, startCol, endRow e
endCol.
Use startRow e startCol para descrever a célula no canto superior esquerdo da
caixa delimitadora de células que você deseja ler. Use endRow e endCol para
especificar a célula no canto inferior direito da caixa delimitadora. Cada um desses
argumentos recebe um número. Se você não fornecer argumentos
delimitadores, readWorksheet lerá na região retangular de células na planilha
que parece conter dados. readWorksheet presumirá que esta região contém
uma linha de cabeçalho, mas você pode informar o contrário com header =
FALSE.

Então, para ler a primeira planilha de wb, você pode usar:

sheet1 <- readWorksheet(wb, sheet = 1, startRow = 0, startCol = 0,


endRow = 100, endCol = 3)

O R salvará a saída como um quadro de dados. Todos os


argumentos, readWorkbook exceto o primeiro, são vetorizados, permitindo que
você leia várias planilhas da mesma pasta de trabalho simultaneamente (ou várias
regiões de células de uma única planilha). Nesse caso, readWorksheet retornará
uma lista de quadros de dados.

Você pode combinar essas duas etapas com readWorksheetFromFile. Ele pega
o argumento de arquivo de loadWorkbooke o combina com os argumentos
de readWorksheet. Você pode usá-lo para ler uma ou mais planilhas diretamente
de um arquivo do Excel:

sheet1 <- readWorksheetFromFile("[Link]", sheet = 1, startRow = 0,


startCol = 0, endRow = 100, endCol = 3)

D.5.5 Escrevendo planilhas


Escrever em uma planilha do Excel é um processo de quatro etapas. Primeiro, você
precisa configurar um objeto de pasta de trabalho com loadWorkbook. Isso
funciona exatamente como antes, exceto que, se você não estiver usando um
arquivo do Excel existente, deverá adicionar o argumento create = TRUE. O
XLConnect criará uma pasta de trabalho em branco. Ao salvá-la, o XLConnect a
gravará no local de arquivo que você especificou aqui com loadWorkbook:

wb <- loadWorkbook("[Link]", create = TRUE)

Em seguida, você precisa criar uma planilha dentro do seu objeto de pasta de
trabalho com createSheet. Informe createSheet em qual pasta de trabalho a
planilha será colocada e qual será usada para a planilha.

createSheet(wb, "Sheet 1")

Em seguida, você pode salvar seu quadro de dados ou matriz na planilha


com writeWorksheet. O primeiro argumento de writeWorksheet, object, é a
pasta de trabalho na qual os dados serão gravados. O segundo argumento, data,
são os dados a serem gravados. O terceiro argumento, sheet, é o nome da planilha
na qual os dados serão gravados. Os dois argumentos seguintes, startRow
e startCol , informam ao R onde, na planilha, colocar a célula superior esquerda
dos novos dados. O valor padrão de cada argumento é 1. Por fim, você pode
usar headerpara informar ao R se os nomes das colunas devem ser gravados com os
dados:

writeWorksheet(wb, data = poker, sheet = "Sheet 1")

Após terminar de adicionar planilhas e dados à sua pasta de trabalho, você pode
salvá-la executando o comando saveWorkbook no objeto da pasta de trabalho. O
R salvará a pasta de trabalho no nome ou caminho de arquivo fornecido
em loadWorkbook. Se isso resultar em um arquivo Excel existente, o R o
substituirá. Se resultar em um novo arquivo, o R o criará.
Você também pode condensar essas etapas em uma única chamada
com writeWorksheetToFile, assim:

writeWorksheetToFile("[Link]", data = poker, sheet = "Sheet 1",


startRow = 1, startCol = 1)

O pacote XLConnect também permite realizar ações mais avançadas com planilhas
do Excel, como escrever em uma região nomeada da planilha, trabalhar com
fórmulas e atribuir estilos a células. Você pode ler sobre esses recursos no vinheta
do XLConnect, que pode ser acessado carregando o XLConnect e executando:

vignette("XLConnect")

D.6 Carregando arquivos de outros programas


Você deve seguir o mesmo conselho que dei para arquivos do Excel sempre que
quiser trabalhar com formatos de arquivo nativos de outros programas: abra o
arquivo no programa original e exporte os dados como um arquivo de texto simples,
geralmente um CSV. Isso garantirá a transcrição mais fiel dos dados no arquivo e
geralmente oferecerá mais opções para personalizar a forma como os dados são
transcritos.
Às vezes, porém, você pode obter um arquivo, mas não o programa de onde ele
veio. Como resultado, você não conseguirá abrir o arquivo em seu programa nativo
e exportá-lo como um arquivo de texto. Nesse caso, você pode usar uma das
funções da Tabela D.4 para abrir o arquivo. Essas funções geralmente vêm no
pacote do R. foreignCada uma tenta ler um formato de arquivo diferente com o
mínimo de problemas possível.

Tabela D.4: Várias funções tentarão ler os tipos de arquivo de outros programas de
análise de dados

Formato de arquivo Função Biblioteca

[Link]
ERSI ArcGIS arquivos de forma
e

Matlab readMat [Link]

minitab [Link] estrangeiro

SAS (conjunto de dados


[Link] estrangeiro
permanente)

SAS (formato XPORT) [Link] estrangeiro

SPSS [Link] estrangeiro

Stata [Link] estrangeiro

Systat [Link] estrangeiro

D.6.1 Conexão com bancos de dados


Você também pode usar R para se conectar a um banco de dados e ler dados.
Use o pacote RODBC para conectar-se a bancos de dados por meio de uma conexão
ODBC.
Use o pacote DBI para conectar-se a bancos de dados por meio de drivers
individuais. O pacote DBI fornece uma sintaxe comum para trabalhar com diferentes
bancos de dados. Você precisará baixar um pacote específico para o banco de dados
para usar em conjunto com o DBI. Esses pacotes fornecem a API para os drivers
nativos de diferentes programas de banco de dados. Para MySQL, use o RMySQL,
para SQLite, o RSQLite, para Oracle, o ROracle, para PostgreSQL, o RPostgreSQL, e
para bancos de dados que usam drivers baseados na API Java Database Connectivity
(JDBC), use o RJDBC. Depois de carregar o pacote de driver apropriado, você pode
usar os comandos fornecidos pelo DBI para acessar seu banco de dados.

E Depuração de código R

Este apêndice se refere a ambientes, ao


tópico Ambientes , e usa exemplos
de Programas e S3 . Você deve ler estes capítulos
primeiro para aproveitar ao máximo este apêndice.

O R vem com um conjunto simples de ferramentas de depuração que o RStudio


amplia. Você pode usar essas ferramentas para entender melhor o código que
produz um erro ou retorna um resultado inesperado. Normalmente, este será o seu
próprio código, mas você também pode examinar as funções do R ou de um de seus
pacotes.

Depurar código pode exigir tanta criatividade e perspicácia quanto escrever código.
Não há garantia de que você encontrará um bug ou conseguirá corrigi-lo quando o
encontrar. No entanto, você pode se ajudar usando as ferramentas de depuração do
R. Elas incluem as funções traceback, browser, debug, debugonce,
trace e recover.
O uso dessas ferramentas geralmente envolve um processo de duas etapas.
Primeiro, você localiza onde ocorreu o erro. Depois, tenta determinar por
que traceback ele ocorreu. Você pode executar a primeira etapa com a função do
R.

Rastreamento E.1
A traceback ferramenta identifica a localização de um erro. Muitas funções R
chamam outras funções R, que chamam outras funções, e assim por diante. Quando
ocorre um erro, pode não ficar claro qual dessas funções deu errado. Vejamos um
exemplo. As funções a seguir chamam uma à outra, e a última função gera um erro
(você verá o porquê em breve):
first <- function() second()
second <- function() third()
third <- function() fourth()
fourth <- function() fifth()
fifth <- function() bug()

Ao executar first, ele chamará second, que chamará third, que


chamará fourth, fifth que chamará bug, uma função que não existe. Veja como
isso ficará na linha de comando:

first()
## Error in fifth() : could not find function "bug"

O relatório de erros nos informa que o erro ocorreu quando o R tentou


executar fifth. Ele também nos informa a natureza do erro (não há nenhuma
função chamada bug). Aqui, é óbvio por que o R chama fifth, mas pode não ser tão
óbvio por que o R chama uma função quando um erro ocorre em campo.
Você pode ver o caminho das funções que o R chamou antes de ocorrer um erro
digitando traceback ( ) na linha de comando. Traceback retornará uma pilha
de chamadas, uma lista das funções que o R chamou na ordem em que as chamou.
A função de baixo será o comando que você digitou na linha de comando. A função
de cima será a função que causou o erro:

traceback()
## 5: fifth ( ) at #1
## 4: fourth ( ) at #1
## 3: third ( ) at #1
## 2: second ( ) at #1
## 1: first ( )

traceback sempre se referirá ao último erro encontrado. Se quiser ver um erro


menos recente, será necessário recriá-lo antes de executar traceback.
Como isso pode ajudar? Primeiro, traceback retorna uma lista de suspeitos. Uma
dessas funções causou o erro, e cada função é mais suspeita do que as abaixo dela.
É provável que o nosso bug tenha vindo de fifth (e veio), mas também é possível
que uma função anterior tenha feito algo estranho — como chamar fifth quando
não deveria.
Segundo, traceback pode mostrar se R saiu do caminho que você esperava. Se
isso aconteceu, observe a última função antes que as coisas dessem errado.
Terceiro, traceback pode revelar a extensão assustadora dos erros de recursão
infinita. Por exemplo, se você alterar fifth para que ele chame second, as funções
criarão um loop: second chamarão third, que chamará fourth, que
chamará fifth, que chamará second e iniciará o loop novamente. É mais fácil fazer
esse tipo de coisa na prática do que você imagina:

fifth <- function() second()

Ao chamar first( ), o R começará a executar as funções. Depois de um tempo, ele


perceberá que a função está se repetindo e retornará um erro. traceback Isso
mostrará exatamente o que o R estava fazendo:

first()
## Error: evaluation nested too deeply: infinite
recursion/options(expressions=)?

traceback()
## 5000: fourth() at #1
## 4999: third() at #1
## 4998: second() at #1
## 4997: fifth() at #1
## 4996: fourth() at #1
## 4995: third() at #1
## 4994: second() at #1
## 4993: fifth() at #1
## ...

Observe que há 5.000 linhas de saída neste arquivo traceback. Se você estiver
usando o RStudio, não verá o traceback de um erro de recursão infinita (usei a
interface gráfica do Mac para obter esta saída). O RStudio reprime o traceback para
erros de recursão infinita para evitar que grandes pilhas de chamadas empurrem o
histórico do console para fora do buffer de memória do R. Com o RStudio, você terá
que reconhecer o erro de recursão infinita por sua mensagem de erro. No entanto,
você ainda pode ver a imposição traceback executando as operações em um shell
UNIX ou nas interfaces gráficas do Windows ou Mac.
O RStudio facilita muito o uso traceback. Você nem precisa digitar o nome da
função. Sempre que ocorrer um erro, o RStudio o exibirá em uma caixa cinza com
duas opções. A primeira é "Mostrar Traceback", mostrada na Figura E.1 .

Figura E.1: Opção Mostrar Traceback do RStudio.

Se você clicar em "Mostrar Rastreamento", o RStudio expandirá a caixa cinza e


exibirá a traceback pilha de chamadas, como na Figura E.2 . A opção "Mostrar
Rastreamento" persistirá ao lado de uma mensagem de erro no seu console, mesmo
enquanto você escreve novos comandos. Isso significa que você pode voltar e
verificar as pilhas de chamadas para todos os erros — não apenas o erro mais
recente.
Imagine que você costumava traceback identificar uma função que acredita que
pode causar um bug. Agora, o que você deve fazer? Você deve tentar descobrir o
que a função fez para causar um erro durante a execução (se fez algo). Você pode
examinar como a função é executada com browser.

Figura E.2: Exibição Traceback do RStudio.


Navegador E.2
Você pode solicitar ao R que pause no meio da execução de uma função e devolva o
controle a você com browser. Isso permitirá que você insira novos comandos na
linha de comando. O ambiente ativo para esses comandos não será o ambiente
global (como de costume); será o ambiente de execução da função que você
pausou. Como resultado, você pode examinar os objetos que a função está usando,
consultar seus valores com as mesmas regras de escopo que a função usaria e
executar o código sob as mesmas condições em que a função o executaria. Esse
arranjo oferece a melhor chance de detectar a origem de bugs em uma função.
Para usar browser, adicione a chamada browser( ) ao corpo de uma função e
salve-a novamente. Por exemplo, se eu quisesse pausar no meio da scorefunção
em Programas , eu poderia adicionar browser()ao corpo de score e executar
novamente o seguinte código, que define score:

score <- function (symbols) {


# identify case
same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]
bars <- symbols %in% c("B", "BB", "BBB")
# get prize
if (same) {
payouts <- c("DD" = 100, "7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
prize <- unname(payouts[symbols[1]])
} else if (all(bars)) {
prize <- 5
} else {
cherries <- sum(symbols == "C")
prize <- c(0, 2, 5)[cherries + 1]
}
browser()
# adjust for diamonds
diamonds <- sum(symbols == "DD")
prize * 2 ^ diamonds
}

Agora, sempre que o R for executado score, ele atenderá à chamada browser( ).
Você pode ver isso com a play função em Programas . Se não tiver a play função à
mão, você pode acessá-la executando este código:

get_symbols <- function() {


wheel <- c("DD", "7", "BBB", "BB", "B", "C", "0")
sample(wheel, size = 3, replace = TRUE,
prob = c(0.03, 0.03, 0.06, 0.1, 0.25, 0.01, 0.52))
}
play <- function() {
symbols <- get_symbols()
structure(score(symbols), symbols = symbols, class = "slots")
}

Quando você executa play, play chamará get_symbols e então score.


Conforme o R trabalha em score, ele encontrará a chamada para browser e a
executará. Quando o R executa essa chamada, várias coisas acontecerão, como na
Figura E.3 . Primeiro, o R parará de executar score. Segundo, o prompt de
comando mudará para browser [ 1 ] > e o R me devolverá o controle; agora
posso digitar novos comandos no novo prompt de comando. Terceiro, três botões
aparecerão acima do painel do console: Avançar, Continuar e Parar. Quarto, o
RStudio exibirá o código-fonte de score no painel de scripts e destacará a linha que
contém browser ( ) . Quinto, a guia de ambientes mudará. Em vez de revelar os
objetos salvos no ambiente global, ela revelará os objetos salvos no ambiente de
tempo de execução de score (consulte Ambientes para obter uma explicação do
sistema de ambientes do R). Sexto, o RStudio abrirá um novo painel Traceback, que
mostra a pilha de chamadas que o RStudio usou para chegar a browser. A função
mais recente, score, será destacada.
Agora estou em um novo modo R, chamado modo navegador . O modo navegador
foi projetado para ajudar você a descobrir bugs, e a nova exibição do RStudio foi
projetada para ajudar você a navegar neste modo.
Qualquer comando executado no modo navegador será avaliado no contexto do
ambiente de execução da função que chamou browser. Esta será a função
destacada no novo painel Traceback. Aqui, essa função é score. Portanto,
enquanto estivermos no modo navegador, o ambiente ativo será score o ambiente
de execução de . Isso permite que você faça duas coisas.

Figura E.3: O RStudio atualiza sua exibição sempre que você entra no modo navegador para ajudar
você a navegar no modo .

Primeiro, você pode inspecionar os objetos que scoreutiliza. O painel Ambientes


atualizado mostra quais objetos score foram salvos em seu ambiente local. Você
pode inspecionar qualquer um deles digitando o nome no prompt do navegador.
Isso permite que você veja os valores das variáveis de tempo de execução que
normalmente não conseguiria acessar. Se um valor parecer claramente incorreto,
você pode estar perto de encontrar um bug:

Browse[1]> symbols
## [1] "B" "B" "0"
Browse[1]> same
## [1] FALSE

Em segundo lugar, você pode executar o código e ver os mesmos resultados


que score veria. Por exemplo, você pode executar as linhas restantes da score
função e verificar se elas fazem algo incomum. Você pode executar essas linhas
digitando-as no prompt de comando ou usar os três botões de navegação que agora
aparecem acima do prompt, como na Figura E.4 .
O primeiro botão, Avançar, executará a próxima linha de código em score. A linha
destacada no painel de scripts avançará uma linha para mostrar sua nova localização
na score função. Se a próxima linha iniciar um trecho de código, como um for loop
ou uma if árvore, o R executará o trecho inteiro e o destacará na janela de script.
O segundo botão, Continuar, executará todas as linhas restantes score e sairá do
modo de navegador.
O terceiro botão, Parar, sairá do modo navegador sem executar mais nenhuma linha
de score.

Figura E.4: Você pode navegar no modo navegador com os três botões na parte superior do painel
do console.

Você pode fazer o mesmo digitando os comandos n, c e Q no prompt do


navegador. Isso cria um incômodo: e se você quiser procurar um objeto
chamado n, c ou Q? Digitar o nome do objeto não funcionará; o R avançará,
continuará ou sairá do modo navegador. Em vez disso, você terá que procurar esses
objetos com os comandos get("n"), get("c") e get("Q") . cont é um sinônimo
de c no modo navegador e whereexibe a pilha de chamadas, então você terá que
procurar esses objetos com get também.
O modo navegador pode ajudar você a ver as coisas da perspectiva das suas
funções, mas não pode mostrar onde está o bug. No entanto, o modo navegador
pode ajudar você a testar hipóteses e investigar o comportamento das funções.
Geralmente, isso é tudo o que você precisa para identificar e corrigir um bug. O
modo navegador é a ferramenta básica de depuração do R. Cada uma das funções a
seguir fornece apenas uma maneira alternativa de entrar no modo navegador.
Depois de corrigir o bug, você deve salvar sua função pela terceira vez — desta vez
sem a browser ( ) chamada. Enquanto a chamada do navegador estiver lá, o R
pausará cada vez que você, ou outra função, chamar score.

E.3 Pontos de interrupção


Os pontos de interrupção do RStudio oferecem uma maneira gráfica de adicionar
uma browser instrução a uma função. Para usá-los, abra o script onde você definiu
uma função. Em seguida, clique à esquerda do número da linha de código no corpo
da função onde você gostaria de adicionar a instrução do navegador. Um ponto
vermelho vazado aparecerá para mostrar onde o ponto de interrupção ocorrerá. Em
seguida, execute o script clicando no botão "Source" na parte superior do painel
"Scripts". O ponto vazado se transformará em um ponto vermelho sólido para
indicar que a função tem um ponto de interrupção (veja a Figura E.5 ).

O R tratará o ponto de interrupção como uma browser instrução, entrando no


modo navegador ao encontrá-lo. Você pode remover um ponto de interrupção
clicando no ponto vermelho. O ponto desaparecerá e o ponto de interrupção será
removido.

Figura E.5: Os pontos de interrupção fornecem o equivalente gráfico de uma instrução do


navegador.

Pontos de interrupção browser são uma ótima maneira de depurar funções que
você definiu. Mas e se você quiser depurar uma função que já existe em R? Você
pode fazer isso com a debug função.

E.4 depuração
Você pode "adicionar" uma chamada de navegador ao início de uma função
preexistente com debug. Para isso, execute debug on na função. Por exemplo,
você pode executar debug on sample com:

debug(sample)

Depois disso, o R agirá como se houvesse uma browser( ) instrução na primeira


linha da função. Sempre que o R executar a função, ele entrará imediatamente no
modo navegador, permitindo que você percorra a função linha por linha. O R
continuará se comportando dessa forma até que você "remova" a instrução do
navegador com undebug:

undebug(sample)

Você pode verificar se uma função está em modo de "depuração"


com isdebugged. Isso retornará TRUE se você tiver executado debug a função,
mas ainda não tiver executado undebug:

isdebugged(sample)
## FALSE

Se isso for muito trabalhoso, você pode fazer o que eu faço e usar debugonceem vez
de debug. O R entrará no modo navegador na próxima vez que executar a função,
mas a depurará automaticamente depois. Se precisar navegar pela função
novamente, basta executá- debugonce la uma segunda vez.
Você pode recriar o comando debugonce no RStudio sempre que ocorrer um
erro. A opção "Reexecutar com depuração" aparecerá na caixa de erro cinza abaixo
de "Mostrar Traceback" (Figura E.1 ). Se você clicar nessa opção, o RStudio
executará o comando novamente como se você debugonce o tivesse executado
pela primeira vez. O R entrará imediatamente no modo navegador, permitindo que
você navegue pelo código. O comportamento do navegador ocorrerá apenas nesta
execução do código. Você não precisa se preocupar em chamar o
comando undebug quando terminar.

Traço E.5
Você pode adicionar a instrução do navegador mais adiante na função, e não logo
no início. trace. trace recebe o nome de uma função como uma sequência de
caracteres e, em seguida, uma expressão R para inserir na função. Você também
pode fornecer um at argumento que indique trace em qual linha da função colocar
a expressão. Portanto, para inserir uma chamada do navegador na quarta linha
de sample, você executaria:

trace("sample", browser, at = 4)

Você pode usar tracepara inserir outras funções R (não apenas browser) em uma
função, mas talvez precise pensar em um motivo inteligente para isso. Você também
pode executar trace uma função sem inserir nenhum código novo. O R
imprimirá trace:<the function> na linha de comando toda vez que executar a
função. Esta é uma ótima maneira de testar uma afirmação que fiz no S3 , de que o
R chama print toda vez que exibe algo na linha de comando:

trace(print)
first
## trace: print(function () second())
## function() second()
head(deck)
## trace: print
## face suit value
## 1 king spades 13
## 2 queen spades 12
## 3 jack spades 11
## 4 ten spades 10
## 5 nine spades 9
## 6 eight spades 8

Você pode reverter uma função ao normal após chamar trace nela com untrace:

untrace(sample)
untrace(print)

E.6 recuperar
A recover função oferece uma opção final para depuração. Ela combina a pilha de
chamadas de traceback com o modo navegador de browser. Você pode
usar recover, browser inserindo-o diretamente no corpo de uma função. Vamos
demonstrar recover com a fifth função:

fifth <- function() recover()

Quando o R executa recover, ele pausa e exibe a pilha de chamadas, mas isso não
é tudo. O R oferece a opção de abrir o modo navegador em qualquer uma das
funções que aparecem na pilha de chamadas. Irritantemente, a pilha de chamadas
será exibida de cabeça para baixo em comparação com traceback. A função mais
recente estará na parte inferior e a função original estará na parte superior:,

first()
##
## Enter a frame number, or 0 to exit
##
## 1: first()
## 2: #1: second()
## 3: #1: third()
## 4: #1: fourth()
## 5: #1: fifth()

Para entrar no modo de navegação, digite o número ao lado da função em cujo


ambiente de execução você deseja navegar. Se não desejar navegar em nenhuma
das funções, digite 0:

3
## Selection: 3
## Called from: fourth ( )
## Browse[ 1 ]>

Você pode então prosseguir normalmente. recoveroferece a oportunidade de


inspecionar variáveis em toda a sua pilha de chamadas e é uma ferramenta
poderosa para descobrir bugs. No entanto, adicionar informações recoverao corpo
de uma função R pode ser trabalhoso. A maioria dos usuários de R o utiliza como
uma opção global para lidar com erros.
Se você executar o código a seguir, o R chamará automaticamente recover ( )
sempre que ocorrer um erro:

options(error = recover)
Esse comportamento durará até você fechar sua sessão R ou reverter o
comportamento chamando:

options(error = NULL)

Você também pode gostar