Aprenda Programação Prática com R
Aprenda 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.
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.
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.
>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
>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"
>
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
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:
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 *:
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
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.4: R repetirá um vetor curto para fazer operações elemento a elemento com dois vetores de
comprimentos desiguais.
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
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
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
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.
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.
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?
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
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.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)
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.
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.
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
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.
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)
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.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.
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
...
é.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"
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
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.
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"
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")
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
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
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.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
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.
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
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.
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)
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.
"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.
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
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
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.
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
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.
deck[-(2:52), 1:3]
## face suit value
## king spades 13
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
deck[1, ]
## face suit value
## king spades 13
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:
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:
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.
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:
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
deal(deck2)
## face suit value
## jack clubs 11
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
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
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
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.
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):
vec[1]
## 0
E aqui está como você pode modificá-lo:
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
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:
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:
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.
vec
## 1 0 1 1 2 1 0
%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.
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
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:
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
deck4$value[deck4$suit == "hearts"]
## 0 0 0 0 0 0 0 0 0 0 0 0 0
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.
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.
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:
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
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:
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 %:
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.
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:
[Link] é. na
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> ""
[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>
[Link](emptyenv())
## Error in [Link](emptyenv ( ) ) : the empty environment has
no parent
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.
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"
environment()
<environment: R_GlobalEnv>
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.
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:
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
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>
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:
environment(deal)
## <environment: R_GlobalEnv>
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
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?
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
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
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())
}
}
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.
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>
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.
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).
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.
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.
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()
## "BBB" "0" "C"
get_symbols()
## "0" "0" "0"
get_symbols()
## "7" "0" "B"
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)
}
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.
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.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
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 :
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
}
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 .
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)
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:
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
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:
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
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:
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:
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.
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
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
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:
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
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
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
}
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
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
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"
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:
attributes(one_play)
## NULL
attributes(one_play)
## $symbols
## [1] "B" "0" "B"
attr(one_play, "symbols")
## "B" "0" "B"
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
}
play( )
## [1] 0
## attr(,"symbols")
## [1] "B" "BB" "0"
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:
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:
# extract symbols
symbols <- attr(prize, "symbols")
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
## "B" "0" "B"
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"
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 .
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:
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>
[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>
methods(print)
## [1] [Link]*
## [2] [Link]
## [3] [Link]*
## ...
## [176] [Link]*
## [177] [Link]*
## [178] [Link]*
##
## Nonvisible functions are asterisked
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
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?
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:
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:
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()
## 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.
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:
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
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.
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
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
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
## 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:
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
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
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
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
É 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.
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)
}
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
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:
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.
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:
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 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:
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:
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.
# 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
}
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)
}
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.
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(100)
## 260
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.
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:
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
[Link](abs(long))
## user system elapsed
## 0.037 0.018 0.054
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.
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.
change_symbols(vec)
## "joker" "ace" "king" "queen" "jack" "ten" "nine"
[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:
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:
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:
Aqui, uma tabela de consulta é 40 vezes mais rápida que a função original.
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.
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.
mean(winnings)
## 0.9366984
[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(5)
## [,1 ] [ ,2 ] [, 3 ]
## [ 1, ] "B" "0" "B"
## [ 2, ] "0" "BB" "7"
## [ 3, ] "0" "0" "BBB"
## [ 4, ] "0" "0" "B"
## [ 5, ] "BBB" "0" "0"
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"
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.
# 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
[Link](play_many(10000000))
## user system elapsed
## 20.942 1.433 22.367
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.
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
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.
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.
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.
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.
[Link]("<package name>")
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.
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.
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:
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.
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
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):
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:
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:
Você pode ler apenas as seis linhas que deseja (cinco linhas mais um cabeçalho)
com:
Observe que a linha de cabeçalho não conta para o total de linhas permitidas por
nrow.
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)
Tabela D.1: Funções de leitura do R. Você pode substituir qualquer um dos argumentos
padrão conforme necessário.
FALSO
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:
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:
Obviamente esse não é um endereço real, mas aqui está algo que pode funcionar —
se você conseguir digitar!
Deck <-[Link]("[Link]
ee5dfc039fd581cb467cc69c226ea2524913c3d8/[Link]")
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.
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
bzfile bzip2
xzfile compressão xz
[Link]("[Link].bz2")
ou:
[Link]("data/[Link].bz2")
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:
Abrir arquivos RData é ainda mais fácil. Basta executar a função loadcom o arquivo:
load("[Link]")
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:
[Link]("clipboard")
[Link](pipe("pbpaste"))
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.
wb <- loadWorkbook("[Link]")
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:
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.
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:
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")
Tabela D.4: Várias funções tentarão ler os tipos de arquivo de outros programas de
análise de dados
[Link]
ERSI ArcGIS arquivos de forma
e
E Depuração de código R
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()
first()
## Error in fifth() : could not find function "bug"
traceback()
## 5: fifth ( ) at #1
## 4: fourth ( ) at #1
## 3: third ( ) at #1
## 2: second ( ) at #1
## 1: first ( )
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 .
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:
Figura E.3: O RStudio atualiza sua exibição sempre que você entra no modo navegador para ajudar
você a navegar no modo .
Browse[1]> symbols
## [1] "B" "B" "0"
Browse[1]> same
## [1] FALSE
Figura E.4: Você pode navegar no modo navegador com os três botões na parte superior do painel
do console.
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)
undebug(sample)
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:
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()
3
## Selection: 3
## Called from: fourth ( )
## Browse[ 1 ]>
options(error = recover)
Esse comportamento durará até você fechar sua sessão R ou reverter o
comportamento chamando:
options(error = NULL)