Você está na página 1de 219

Python e R para

o Cientista de Dados Moderno


O MELHOR DE DOIS MUNDOS

Rick J. Scavetta
Boyan Angelov

Novatec
Authorized Portuguese translation of the English edition of Python and R for the Modern Data
Scientist, ISBN 9781492093404 © 2021 Boyan Angelov and Rick J. Scavetta. This translation is
published and sold by permission of O'Reilly Media, Inc., the owner of all rights to publish and
sell the same.
Tradução em português autorizada da edição em inglês da obra Python and R for the Modern
Data Scientist, ISBN 9781492093404 © 2021 Boyan Angelov and Rick J. Scavetta. Esta tradução é
publicada e vendida com a permissão da O'Reilly Media, Inc., detentora de todos os direitos para
publicação e venda desta obra.
© Novatec Editora Ltda. [2022].
Todos os direitos reservados e protegidos pela Lei 9.610 de 19/02/1998. É proibida a reprodução
desta obra, mesmo parcial, por qualquer processo, sem prévia autorização, por escrito, do autor e
da Editora.
Editor: Rubens Prates GRA20220905
Tradução: Aldir Coelho Corrêa da Silva
Revisão gramatical: Tássia Carvalho
ISBN do impresso: 978-65-86057-95-9
ISBN do ebook: 978-65-86057-96-6
Histórico de impressões:
Setembro/2022 Primeira edição
Novatec Editora Ltda.
Rua Luís Antônio dos Santos 110
02460-000 – São Paulo, SP – Brasil
Tel.: +55 11 2959-6529
Email: novatec@novatec.com.br
Site: https://novatec.com.br
Twitter: twitter.com/novateceditora
Facebook: facebook.com/novatec
LinkedIn: linkedin.com/in/novatec
GRA20220905
Aos meus pais por me darem o melhor início de vida possível.
À minha esposa por ser meu porto seguro.
Aos meus lhos por sonharem com um mundo melhor.
–Boyan Angelov

Para todos que estão prontos, dispostos e aptos


a ver o mundo de uma forma mais abrangente,
eliminando “a sombra” que existe em nós.
–Rick Scavetta
Download das imagens coloridas
A versão em cores das imagens do livro podem ser baixadas em
https://novatec.com.br/livros/python-r-para-cientista-dados-moderno
Sumário

Prefácio
Parte I Descoberta de uma nova linguagem
Capítulo 1 No começo
Origens do R
Origens do Python
Começa a guerra de linguagens
A batalha pela primazia na área da ciência de dados
Convergência para a cooperação e a criação de comunidade
Considerações nais

Parte II Bilinguismo I: Aprendendo uma nova linguagem


Capítulo 2 R para Pythonistas
Trabalhando com o R
Projetos e pacotes
Triunfo dos tibbles
Apresentação e exploração dos tipos de dados
Nomeando elementos (internos)
Listas
Fatos sobre os fatores
Como descobrir…o que quiser
Reexecutando reiterações
Considerações nais
Capítulo 3 Python para UseRs
Versões e builds
Ferramentas padrão
Ambientes virtuais
Instalação de pacotes
Notebooks
De que forma a linguagem Python se compara à linguagem R?
Importe um conjunto de dados
Examine os dados
Estruturas de dados e estatística descritiva
Estruturas de dados: de volta ao básico
Indexação e expressões lógicas
Plotagem
Estatística inferencial
Considerações nais

Parte III Bilinguismo II: O contexto moderno


Capítulo 4 Contexto dos formatos de dados
Pacotes externos versus básicos
Dados de imagem
Dados de texto
Dados de séries temporais
Base-R
Prophet
Dados espaciais
Considerações nais
Capítulo 5 Contexto dos uxos de trabalho
De nindo uxos de trabalho
Análise exploratória de dados
Visualizações estáticas
Visualizações interativas
Machine Learning
Engenharia de dados
Relatórios
Relatório estático
Relatório interativo
Considerações nais

Parte IV Bilinguismo III: Tornando-se sinérgico


Capítulo 6 Usando as duas linguagens sinergicamente
Falsa operabilidade
Interoperabilidade
Aprofundando-se
Passe objetos entre R e Python em um documento R Markdown
Chame Python em um documento R Markdown
Chame Python usando como fonte um script Python
Chame Python usando o REPL
Chame Python com entrada dinâmica em um documento interativo
Considerações nais
Capítulo 7 Estudo de caso da ciência de dados bilíngue
24 anos e 1,88 milhão de incêndios
Preparação e importação de dados
EDA e visualização de dados
Machine Learning
De nindo seu ambiente Python
Engenharia de características
Treinamento do modelo
Previsão e UI
Considerações nais
Apêndice Dicionário bilíngue de Python:R
Gerenciamento de pacotes
Operadores de atribuição
Tipos
Operadores aritméticos
Atributos
Palavras-chave
Funções e métodos
Estilo e convenções de nomeação
Objetos de armazenamento de dados análogos
Data Frames
Expressões lógicas
Indexação
Sobre os autores
Colofão
Prefácio

Por que escrevemos este livro


Queremos mostrar ao cientista de dados por que ser mais consciente,
informado e criterioso com relação às ferramentas usadas é uma
estratégia ideal para o aumento da produtividade. Tentando nos manter
éis a esse objetivo, não escrevemos um dicionário bilíngue (bem, não foi
só isso que zemos – você encontrará esse proveitoso recurso no
Apêndice). Discussões intermináveis sobre Python versus R (as chamadas
“guerras de linguagens”) já há muito tempo deixaram de ser produtivas.
Elas nos lembram do martelo de Maslow: “se a única ferramenta que você
tem é um martelo, tudo começa a parecer com um prego”. É uma visão de
mundo fantasiosa manifestada em termos absolutos, na qual uma única
ferramenta oferece uma solução para tudo. As situações do mundo real
dependem do contexto, e pro ssionais experientes sabem que as
ferramentas devem ser selecionadas de maneira apropriada. Queremos
demonstrar uma nova maneira de trabalhar que se bene cie de todas as
excelentes ferramentas de ciência de dados disponíveis,
independentemente da linguagem em que foram escritas. Logo, queremos
desenvolver não só como o cientista de dados moderno pensa, mas
também como ele trabalha.
Não escolhemos a palavra moderno para o título apenas para indicar que
nossa abordagem traz inovações. Ela nos permite adotar uma postura
mais exível na maneira de discutir as ferramentas. O que queremos dizer
com ciência de dados moderna? A ciência de dados moderna é:
Coletiva
Ela não existe isoladamente. Está integrada a redes mais amplas, como a
uma equipe ou organização. Evitamos jargões quando criam barreiras e
os adotamos quando constroem pontes (consulte “Interações técnicas”,
na página 10).
Simples
Queremos reduzir a complexidade em nossos métodos, códigos e
comunicações se ela não for necessária.
Acessível
É um processo de design aberto que pode ser avaliado, entendido e
otimizado.
Generalizável
Suas ferramentas e conceitos básicos são aplicáveis a muitas áreas.
Voltada para o exterior
Incorpora, recebe informações de e é in uenciada por desenvolvimentos
em outras áreas.
Ética e honesta
É orientada às pessoas. Leva em consideração práticas recomendadas
para a execução de um trabalho ético, assim como uma visão mais
ampla de suas consequências para as comunidades e a sociedade.
Evitamos exageros, modismos e tendências que só sirvam para a
obtenção de ganhos a curto prazo.
Independentemente de como a descrição do cargo de cientista de dados
evoluir nos próximos anos, esses princípios atemporais continuarão
fornecendo uma base sólida.

Interações técnicas
Aceitar que o mundo é extenso, diverso e complexo demais para uma
única ferramenta atender às demandas que enfrentamos apresenta um
desa o que pode ser superado mais apropriadamente de maneira direta e
antecipada.
Essa perspectiva ampliada resulta em um aumento nas interações técnicas.
Devemos considerar a linguagem de programação, pacotes, convenções de
nomenclatura, a arquitetura de arquivos do projeto, IDEs (integrated
development environments, ambientes de desenvolvimento integrado),
editores de texto, entre outros recursos, que atenderão melhor à situação.
A diversidade faz surgir complexidade e confusão.
Quanto mais diverso o ecossistema se tornar, mais importante será
considerar se nossas escolhas estão atuando como pontes ou barreiras.
Devemos sempre tentar fazer escolhas que construam pontes com nossos
colegas e comunidades e evitar as que criem barreiras que nos isolem e
nos tornem in exíveis. Há muito espaço para abrigar a diversidade de
escolhas que encontraremos. O desa o em cada situação será fazer
escolhas que encontrem um equilíbrio entre preferência pessoal e
acessibilidade da comunidade.
Esse desa o existe em todas as interações técnicas. Além da escolha da
ferramenta (uma habilidade “técnica”), ele também inclui a comunicação
(uma habilidade “comportamental”). O conteúdo, o estilo e o meio de
comunicação, para citar apenas algumas considerações, também agem
como pontes ou obstáculos para um público-alvo especí co.
Tornar-se bilíngue em Python e R é um passo em direção à construção de
pontes entre membros da comunidade mais ampla da ciência de dados.

Para quem é este livro


Este livro se destina a cientistas de dados no estágio intermediário de suas
carreiras. Como tal, ele não tenta ensinar ciência de dados. No entanto,
cientistas de dados iniciantes também se bene ciarão da leitura do livro
aprendendo o que é possível fazer em um contexto de ciência de dados
moderno antes de se decidir pela escolha de algum tópico, ferramenta ou
linguagem.
Nosso objetivo é conciliar as comunidades Python e R. Queremos nos
afastar de uma mentalidade tribal do tipo “nós contra eles” e construir
uma comunidade uni cada e produtiva. Logo, este livro é para os
cientistas de dados que entendem os benefícios de expandir seu conjunto
de habilidades e, portanto, suas perspectivas e o valor que seu trabalho
pode adicionar a todos os tipos de projetos de ciência de dados.
Seria negligência ignorar as poderosas ferramentas que estão disponíveis
para nós. Devemos car abertos para maneiras novas e produtivas de
atingir nossos objetivos de programação e encorajar nossos colegas a
saírem de sua zona de conforto.
Além disso, a Parte II e o Apêndice também servirão como referências
úteis para aqueles momentos em que você só precisar fazer um
mapeamento rápido entre algo que é familiar em uma linguagem e a outra
linguagem.

Pré-requisitos
Para aproveitar ao máximo este livro, presumimos que o leitor conheça
pelo menos uma das principais linguagens de programação da ciência de
dados, Python e R. Um leitor que conhecer uma linguagem que se
aproxime delas, como Julia ou Ruby, também poderá tirar bom proveito.
Um conhecimento básico das áreas gerais do trabalho de ciência de
dados, como a manipulação de dados (data munging), a visualização de
dados e o machine learning, é bené co, mas não é obrigatório, para a
apreciação dos exemplos, dos cenários de uxo de trabalho e do estudo
de caso.

Como este livro foi organizado


Organizamos o livro como se estivéssemos aprendendo um segundo
idioma na fase adulta.
Na Parte I, começaremos voltando no tempo até as origens das duas
linguagens e mostraremos como esse fato in uenciou o estado atual
abordando avanços importantes. Em nossa analogia com idiomas falados,
isso ajudará a fornecer algum contexto sobre por que temos
peculiaridades como verbos irregulares e diferentes terminações no plural.
A etimologia é interessante e nos ajuda a entender um idioma, como no
caso das aparentemente intermináveis formas de substantivos no plural da
língua alemã, mas certamente não é essencial para se falar o idioma.1 Se
quiser passar direto para as linguagens, pule para a Parte II.
A Parte II fornecerá um aprofundamento maior nos dialetos das duas
linguagens oferecendo uma perspectiva espelhada. Primeiro abordaremos
como um usuário de Python deve trabalhar com R e depois faremos o
inverso. Isso expandirá não só seu conjunto de habilidades, mas também
sua maneira de pensar conforme for entendendo como cada linguagem
opera.
Nessa parte, trataremos cada linguagem separadamente ao começarmos a
nos tornar bilíngues. Como quando alguém se torna bilíngue em idiomas
falados, temos de resistir a dois impulsos nocivos. O primeiro é acreditar
que algo é muito mais simples, elegante ou talvez “melhor” em nossa
língua materna. Se você pensa assim, parabéns, mas isso não leva alguém
a aprender um novo idioma, certo? Aprenderemos cada linguagem
isoladamente. Com certeza faremos comparações ao avançar, mas elas nos
ajudarão a lidar com a in uência trazida da língua materna.
O segundo impulso é tentar constantemente fazer uma tradução literal e
palavra por palavra entre os dois idiomas. Isso di culta pensar (ou até
mesmo sonhar) usando o novo idioma, e pode até mesmo tornar
impossível fazê-lo! Alguns exemplos que gosto de usar são os de
expressões como das schmeckt mir em alemão ou ho fame em italiano, que
são mal traduzidas como “tem gosto para mim” (tem um gosto bom) e
“tenho apetite” (estou com fome). O importante é que diferentes idiomas
permitem o uso de diferentes construções. Isso nos dá novas ferramentas
com as quais trabalhar e novas maneiras de pensar quando percebemos
que não podemos mapear tudo usando um padrão 1:1 para o que já
conhecemos. Considere esses capítulos como nossa primeira etapa para o
mapeamento entre o conhecimento que temos de um idioma e o outro
idioma.
A Parte III abordará o contexto moderno das aplicações de linguagens.
Isso inclui uma veri cação do amplo ecossistema de pacotes open source
assim como dos diversos métodos especí cos de uxos de trabalho. Essa
parte demonstrará quando uma linguagem é preferencial e por qual razão,
mesmo que nesse momento elas ainda sejam linguagens separadas. Isso o
ajudará a decidir que linguagem usar para partes de um projeto maior de
ciência de dados.
Nos idiomas falados, é comum que algo seja perdido na tradução. Existem
conceitos que funcionam melhor em um idioma especí co. Em alemão,
tanto mir ist heiß quanto ich bin heiß signi cam “I’m hot” (sou quente,
estou com calor) em inglês, mas quem fala alemão saberá distinguir o
calor proveniente da temperatura do calor corporal. Outras palavras,
como Schadenfreude, composta de “schaden” (dano) e “freude” (prazer),
que signi ca ter prazer com as di culdades de alguém, ou Kummerspeck,
composta de “kummer” (tristeza, pesar) e “speck” (bacon), que se refere
ao peso ganho devido à gula emocional, são tão perfeitas que não vale a
pena traduzi-las.
A Parte IV detalhará as interfaces modernas que existem entre as
linguagens. Primeiro, nós nos tornamos bilíngues, usando cada linguagem
isoladamente. Em seguida, identi camos como escolher uma linguagem
em vez da outra. Agora, examinaremos ferramentas que nos ajudarão a
migrar de scripts Python e R separados e interconectados para scripts
únicos integrando as duas linguagens no mesmo uxo de trabalho.
A diversão começará quando, além de bilíngue, você estiver trabalhando
dentro de uma comunidade bilíngue. Além de se comunicar em cada
linguagem independentemente, você também poderá combiná-las de
novas maneiras que apenas outros falantes bilíngues apreciarão e
entenderão. O bilinguismo não só dá acesso a uma nova comunidade
como também cria por si só outra comunidade. Para os puristas, essa
possibilidade é algo impensável, mas espero que ultrapassemos essa fase.
Os bilíngues apreciariam o aviso “O Ordnungsamt está monitorando
Bergmannkiez hoje”. O ideal é que você não deixe as palavras como estão
só porque se esqueceu da tradução, e sim porque elas são a melhor opção
para a situação. Não existe uma tradução exata para Orgnungsamt
(agência reguladora?) e Bergmannkiez ca nas vizinhanças de Berlim e
não deve ser mesmo traduzida. Podem existir palavras em um idioma que
transmitam melhor uma mensagem, como Mundschutzp icht, o uso
obrigatório de máscaras faciais durante a pandemia do coronavírus.
Para concluir, o Capítulo 7 apresenta um estudo de caso que descreve
como um projeto de ciência de dados moderno pode ser implementado
com base no material abordado neste livro. Nele, veremos todas as seções
anteriores unidas no mesmo uxo de trabalho.

Precisamos conversar
A área da ciência de dados está em contínua evolução e esperamos que
este livro o ajude a navegar facilmente entre Python e R. Queremos muito
saber o que você pensa, portanto nos diga como seu trabalho mudou!
Você pode entrar em contato conosco (em inglês) por meio do site que
serve como acompanhamento do livro (https://www.moderndata.design).
Nele você encontrará conteúdo adicional atualizado e um prático resumo
de referência bilíngue de Python/R.

Convenções usadas neste livro


As convenções tipográ cas a seguir foram usadas no livro:
Itálico
Indica novos termos, URLs, endereços de email, nomes de arquivo e
extensões de arquivo.
Largura constante
Usada para listagens de programa, assim como dentro de parágrafos
para indicar elementos de programa como nomes de variáveis ou
funções, bancos de dados, tipos de dados, variáveis de ambiente,
instruções e palavras-chave.
Largura constante em negrito
Mostra comandos ou algum outro texto que precisem ser digitados
literalmente pelo usuário.
Largura constante em itálico
Mostra texto que deve ser substituído por valores fornecidos pelo
usuário ou determinados pelo contexto.

Este elemento representa uma dica ou sugestão.

Este elemento representa uma nota geral.


Este elemento indica um aviso ou cuidado.

Usando exemplos de código


Há material complementar (exemplos de código, exercícios etc.) disponível
para download em https://github.com/moderndatadesign/PyR4MDS.
Se você tiver alguma dúvida ou problema técnico referente ao uso dos
exemplos de código, envie um email para bookquestions@oreilly.com.
Este livro foi escrito para ajudá-lo a realizar seu trabalho. Se o exemplo de
código estiver sendo oferecido com o livro, você poderá usá-lo em seus
programas e na sua documentação. Não é preciso entrar em contato
conosco para obter permissão a menos que esteja reproduzindo uma
parte signi cativa do código. Por exemplo, escrever um programa que use
vários trechos de código deste livro não requer permissão. Vender ou
distribuir exemplos dos livros da O’Reilly requer permissão. Responder a
uma pergunta citando este livro e referindo-se a exemplos de código não
requer permissão. Incorporar uma quantidade signi cativa de exemplos
de código do livro à documentação do seu produto requer permissão.
Apreciamos, mas geralmente não exigimos, quando nos é atribuída
autoria. Normalmente a atribuição de autoria inclui o título, o autor, a
editora e o ISBN. Por exemplo, “Python e R para o Cientista de Dados
Moderno, de Rick J. Scavetta e Boyan Angelov (O’Reilly). Copyright 2021
Boyan Angelov e Rick J. Scavetta, 978-1-492-09340-4”.
Se você achar que o uso que está fazendo dos exemplos de código não se
enquadra no uso ou na permissão legal mencionado anteriormente, que
à vontade para entrar em contato conosco em permissions@oreilly.com.

Como entrar em contato conosco


Envie comentários e dúvidas sobre este livro para:
novatec@novatec.com.br.
Temos uma página da web para este livro, na qual incluímos a lista de
erratas, exemplos e qualquer outra informação adicional.
• Página da edição em português
https://novatec.com.br/livros/python-r-para-cientista-dados-moderno
• Página da edição original, em inglês
https://oreil.ly/python-and-r-data-science
Para obter mais informações sobre livros da Novatec, acesse nosso site
em:
https://novatec.com.br

Agradecimentos
Os autores agradecem a contribuição de muitas pessoas que ajudaram a
tornar este livro possível.
Na O’Reilly, gostaríamos de agradecer à Michelle Smith, uma editora
sênior de aquisições de conteúdo com entusiasmo, conhecimento e visão
inigualáveis com quem tivemos a sorte de trabalhar. Agradecemos à
Angela Ru no, nossa editora de desenvolvimento de conteúdo, por nos
manter no caminho certo durante o processo de redação e por nos
motivar com uma parede repleta de heróis de ação e palavras
reconfortantes de encorajamento. Somos gratos à Katie Tozer, nossa
editora de produção, por sua paciência e pelo tratamento minucioso dado
ao nosso manuscrito. Queremos agradecer a Robert Romano e à equipe
de design da O’Reilly. Além de ajudarem no redesenho das guras, eles
também escolheram, por sugestão nossa, uma lula surpreendente,
imponente e muito impactante e colossal para a capa! Também
gostaríamos de agradecer a Chris Stone e à equipe de engenharia pela
ajuda técnica.
Queremos enviar um agradecimento especial para as incontáveis pessoas
que nunca são vistas e trabalham nos bastidores da O’Reilly. Apreciamos
o grande esforço necessário para que sejam disponibilizados conteúdos
relevantes e de excelência.
Também queremos agradecer a nossos revisores técnicos, que cederam
generosamente seu tempo e forneceram comentários esclarecedores
extraídos de sua experiência. Eric Pite e Ian Flores, da RStudio, nossos
colegas autores Noah Gift e George Mount, da O’Reilly, e o irrepreensível
autor Walter R. Paczkowski. Seus comentários foram bem recebidos e
melhoraram imensamente o livro.
Rick deseja agradecer aos seus alunos, tanto online quanto presenciais,
dos últimos 10 anos. Cada oportunidade de transmitir conhecimento e
informações reforçou a importância do ofício de ensinar e permitiu que
fossem feitas contribuições, mesmo que modestas, para o grandioso
avanço da ciência. Rick também agradece pelo dedicado suporte
administrativo que permite que ele mantenha um relacionamento ativo
com os principais cientistas em todo o mundo.
Para concluir, estendemos um agradecimento sincero não só aos
desenvolvedores de Python e R como também à ampla e interconectada
comunidade de desenvolvedores open source. Sua criatividade, dedicação
e paixão são surpreendentes. É difícil imaginar como seria o cenário da
ciência de dados sem os esforços coletivos de milhares de desenvolvedores
trabalhando em conjunto, cruzando todas as fronteiras e abrangendo
décadas. Grande parte do que incluímos neste livro não existiria sem suas
contribuições!

1 Etimologia é o estudo das origens e dos signi cados das palavras.


PARTE I

Descoberta de uma nova linguagem

Para começar o estudo, recapitularemos a história tanto do Python


quanto do R. Pela comparação das histórias de suas origens, você
entenderá melhor o estado atual de cada linguagem no cenário da ciência
de dados. Se já quiser começar a codi car, que à vontade e pule para a
Parte II.
CAPÍTULO 1

No começo

Rick J. Scavetta
Gostaríamos de iniciar com uma frase genial, como “Foi o melhor dos
tempos, foi o pior dos tempos…”, mas, na verdade, é o melhor dos tempos
– a ciência de dados está orescendo! Ao avançar, ela foi se fragmentando
em nichos, como ocorre com muitas disciplinas com o passar do tempo.
Essa maturidade é resultado de uma longa jornada que começou nos
primórdios da computação cientí ca. Acreditamos que conhecer as
histórias da origem de Python e R o ajudará a entender como elas diferem
no ambiente atual e, portanto, como aproveitá-las ao máximo.
Não queremos nos passar por historiadores da ciência, aquele seleto
grupo de acadêmicos que rastreiam as circunstâncias em que ocorreram
grandes descobertas e surgiram personalidades importantes. O que
podemos fazer é oferecer uma descrição dos momentos de destaque que
zeram surgir o Python e o R e de como isso nos levou à nossa situação
atual.

Origens do R
Sempre que penso no R, lembro-me da FUBU, uma empresa de streetwear
fundada nos idos de 1990. O nome é um acrônimo do qual gostei de
imediato: For Us, By Us (para nós, por nós). FUBU signi cava
comunidade; signi cava conhecer as necessidades e os desejos de seus
pares e se certi car de que eles estavam sendo bem atendidos. O R é
FUBU.1 Até o m deste capítulo, tenho certeza de que você também se
sentirá assim. A partir do momento em que entendemos que o R é FUBU,
tudo começa a fazer sentido.
Podemos encontrar as origens do R no agora lendário Bell Laboratories
em Nova Jersey. Em 1976, o desenvolvimento da linguagem de
programação estatística S estava sendo liderado por John Chambers. Um
ano depois, Chambers publicou Computational Methods for Data Analysis
(John Wiley & Sons) e seu colega John Tukey, também da Bell
Laboratories, publicou Exploratory Data Analysis (Addison-Wesley). Em
1983, Chambers et al. publicaram Graphical Methods for Data Analysis
(CRC Press). Esses livros forneceram a base para o desenvolvimento de
um sistema computacional que permitiria que um estatístico não só
explorasse, entendesse e analisasse seus dados, mas também comunicasse
seus resultados. Aqui temos uma equipe de estrelas do FUBU! Os
coautores do livro de Chambers foram os primos de Tukey, Paul A. Tuke e
William Cleveland. Os experimentos empíricos de Cleveland sobre
percepção, resumidos em dois livros esclarecedores, continuam até hoje a
embasar a área mais ampla da visualização de dados. Entre suas muitas
contribuições para a computação cientí ca e a estatística, Tukey
desenvolveu novas visualizações, como o geralmente mal compreendido
diagrama de caixa e bigodes (box and whiskers plot), e criou o método
LOESS (Locally Weighted Scatterplot Smoothing, Suavização de Grá co
de Dispersão Ponderada Localmente) para a suavização não paramétrica.
Começaremos com o S porque ele formou a base do que acabaria se
tornando o R. As poucas informações do parágrafo anterior explicam algo
sobre as bases do S – e do R. Em primeiro lugar, os estatísticos são
pessoas muito literais (daí S, de statisticians). Essa é uma característica
bastante útil. Em segundo lugar, eles queriam uma linguagem de
programação FUBU especí ca para a análise de dados. Não estavam
interessados em criar uma linguagem de programação generalista ou um
sistema operacional. Em terceiro lugar, esses livros iniciais sobre
estatística e visualização computacional são, resumindo, ótimos exemplos
de beleza pedagógica e exposição precisa.2 Eles envelheceram muito bem,
apesar da tecnologia obviamente datada. Eu diria que esses livros
plantaram a semente de como os estatísticos, e em particular a
comunidade R, lidariam com a comunicação técnica de maneira aberta,
clara e inclusiva. Acho que essa é uma característica marcante e
diferenciada da comunidade R que tem raízes profundas. Em quarto
lugar, a ênfase inicial dada a métodos grá cos mostra que o S já se
preocupava com o fornecimento de visualizações de dados exíveis e
e cientes, necessárias tanto para a compreensão dos dados quanto para a
comunicação dos resultados. Logo, o S tinha como objetivo realizar o que
fosse mais importante da maneira mais fácil possível, e de um modo
realmente FUBU.
A distribuição original do S era executada no Unix e estava disponível
gratuitamente. O S acabou sendo licenciado como uma implementação
chamada S-PLUS. Isso demandou outra implementação open source e
gratuita criada por Ross Ihaka e Robert Gentleman na Universidade de
Auckland em 1991. Eles chamaram essa implementação de R, com base
nas iniciais de seus nomes, como uma brincadeira com o nome S, e para
manter a tradição de nomear linguagens de programação usando uma
única letra. A primeira versão beta o cial estável do R v1.0.0 foi
disponibilizada em 29 de fevereiro de 2000. No período entre as duas
datas, ocorreram dois desenvolvimentos importantes. O CRAN
(Comprehensive R Archive Network) (https://oreil.ly/HIpY7), foi
estabelecido para hospedar e arquivar pacotes R em servidores
espelhados, e o R Core Team também foi estabelecido. Esse grupo de
voluntários (https://oreil.ly/Zjrvw) (que atualmente é composto de
20 membros) implementa o base-R, com a inclusão de documentos,
builds, testes e versões, mais a infraestrutura que torna tudo isso possível.
O interessante é que alguns dos membros originais continuam atuando,
entre eles John Chambers, Ross Ihaka e Robert Gentleman.
Muita coisa aconteceu desde o lançamento do R v1.0.0 em 2000, mas o
que contei até aqui deve lhe dar uma ideia do histórico único do R como
ferramenta FUBU de computação estatística. Antes de continuar com a
história do R, conheceremos a história do Python.

Origens do Python
Em 1991, enquanto Ross Ihaka e Robert Gentleman começavam a
trabalhar no que se tornaria o R, Guido van Rossum, um programador
holandês, lançou o Python. A perspectiva básica do Python é a de uma
pessoa que começou a resolver os problemas computacionais comuns
daquela época. Na verdade, van Rossum foi durante anos chamado
carinhosamente de BDFL (benevolent dictator for life, ditador
benevolente vitalício), um título ao qual ele renunciou quando deixou o
Python’s Steering Council em 2018.
Vimos que o S surgiu da necessidade de os estatísticos poderem executar a
análise de dados e o R devido à demanda por uma implementação open
source, mas, então, que problema foi resolvido pelo Python? Bem, não foi
o da análise de dados – isso veio muito depois. Quando o Python surgiu,
C e C++, duas linguagens de programação de baixo nível, eram
populares. O Python despontou lentamente como uma alternativa
interpretada de alto nível, principalmente depois que o Python v2 foi
lançado em 2000 (o mesmo ano em que o R v1.0.0 foi lançado). Python foi
criado com a nalidade explícita de ser, acima de tudo, uma linguagem de
programação amplamente adotada, fácil de usar e de aprender e com uma
sintaxe simples. E foi muito bem-sucedido nesse aspecto!
É por isso que, como você deve ter notado, ao contrário do R, Python é
visto em todos os lugares e é muito versátil. Ele pode ser visto no
desenvolvimento web, em jogos (gaming), na administração de sistemas,
em aplicações desktop, na ciência de dados e assim por diante. Na
verdade, o R pode ser usado não só na análise de dados, mas lembre-se,
ele é FUBU. Se o R é FUBU, o Python é um canivete suíço. Está em todos
os locais e todo mundo tem um, mas, ainda que forneça muitas
ferramentas, a maioria das pessoas usa apenas uma regularmente.
Embora os cientistas de dados que usam Python trabalhem em um
cenário amplo e variado, tendem a encontrar seu nicho e se especializar
nos pacotes e uxos de trabalho necessários para a execução de seu
trabalho em vez de explorar todas as facetas dessa linguagem generalista.
A ampla popularidade do Python dentro da ciência de dados não se deve
apenas aos recursos que ele fornece para essa área. Eu diria que o Python
fez sua entrada na ciência de dados parcialmente por causa dos recursos
que ele oferece como uma linguagem de uso geral. A nal, entrar aos
poucos já é meio caminho andado. Os analistas e cientistas de dados
achavam mais fácil compartilhar e implementar scripts com colegas
envolvidos na administração de sistemas e no desenvolvimento web
porque eles já sabiam como trabalhar com scripts Python. Isso
desempenhou um papel importante na ampla adoção do Python. Ele
conseguia se bene ciar da computação de alto desempenho e
implementar e cientemente algoritmos de deep learning (aprendizagem
profunda). O R era, e talvez ainda seja, uma linguagem de nicho,
semelhante a uma língua estrangeira que o universo mais amplo da
computação não entendeu.
Embora o Python v2 tenha sido lançado em 2000, a consolidação de um
pacote amplamente adotado para a manipulação de dados de array só
ocorreu em 2005, com o lançamento do NumPy. Nessa época, o SciPy, um
pacote que, desde 2001, fornecia algoritmos básicos para a ciência de
dados (relacionados à otimização, integração, equações diferenciais etc.),
começou a usar estruturas de dados do NumPy. O SciPy também fornece
estruturas de dados especializadas como as árvores k-dimensionais.
Quando o problema da existência de um pacote padrão para estruturas
de dados e algoritmos básicos foi resolvido, o Python começou sua
ascensão para a ampla adoção na computação cientí ca. Os pacotes de
baixo nível NumPy e SciPy formaram a base para pacotes de alto nível,
como o pandas em 2009, fornecendo ferramentas para a manipulação de
dados e para estruturas de dados como os dataframes. Isso também é
chamado de PyData stack e foi quando tudo começou realmente a
acontecer.

Começa a guerra de linguagens


No começo dos anos 2000 foi iniciado o que mais tarde seria conhecido
como as guerras de linguagens. À medida que a PyData stack ia tomando
forma, avanços realizados tanto em Python quanto em R começaram a
esquentar a competição. Quatro eventos se destacam.
Em primeiro lugar, em 2002 o BioConductor
(https://www.bioconductor.org) foi estabelecido como um novo repositório
de pacotes R e um framework para a manipulação dos orescentes (leia-se
a absoluta explosão de) dados biológicos em suas in nitas formas. Até
esse momento, os bioinformatas dependiam de ferramentas como o
MATLAB e o Perl (junto com ferramentas clássicas de linha de comando
e algumas ferramentas manuais de interface com a web). O MATLAB
ainda é o preferido em disciplinas especí cas, como a neurociência. No
entanto, o Perl foi, em grande parte, substituído pelo BioConductor. É
surpreendente o impacto que o BioConductor teve na bioinformática.
Além de fornecer um repositório de pacotes para a manipulação de
bancos de dados remotos de sequências genéticas, dados de expressão,
microarrays e assim por diante, ele também fornecia novas estruturas de
dados para o tratamento de sequências genéticas. O BioConductor
continua se expandindo e está profundamente integrado à comunidade
de bioinformática.
Em segundo lugar, em 2006 o pacote IPython foi lançado. Ele era uma
maneira inovadora de trabalhar com Python em um ambiente de
notebook interativo. Após várias doações a partir de 2012, o IPython
acabou evoluindo para o Projeto Jupyter (https://jupyter.org) em 2014, que
agora engloba o IDE JupyterLab. Os usuários costumam se esquecer de
que Jupyter é a abreviação de “Julia, Python, and R” porque ele é muito
orientado a Python. Os notebooks tornaram-se uma forma dominante
para o trabalho com ciência de dados em Python, e em 2018 a Google
lançou o Google Colab (https://oreil.ly/O1krw), uma ferramenta de
notebook online gratuita. Falaremos sobre isso no Capítulo 3.
Em terceiro lugar, em 2007, Hadley Wickham publicou sua tese de PhD,
que era composta de dois pacotes que mudariam o cenário da
linguagem R. O primeiro, reshape, fundou as bases do que
posteriormente seria formalizado como o Tidyverse
(https://www.tidyverse.org) (veremos mais sobre isso depois). Embora o
reshape já tenha há muito tempo se tornado obsoleto, foi o primeiro
vislumbre para o entendimento de como a estrutura de dados in uencia a
maneira de considerarmos e trabalharmos com nossos dados.3 O
segundo, ggplot2, é uma implementação do inspirador livro de Leland
Wilkinson et al., The Grammar of Graphics (Springer), e forneceu uma
geração de grá cos intuitiva e de alto nível que simpli cou muito as
ferramentas anteriormente existentes em R (falaremos sobre isso no
Capítulo 5).
Para concluir, o Python v3 foi lançado em 2008. Durante anos
convivemos com a dúvida de qual versão do Python deveríamos usar, a v2
ou a v3. Isso ocorria porque o Python v3 não é compatível com versões
anteriores.4 Felizmente, esse problema foi resolvido desde que o Python v2
foi descontinuado em 2020. O interessante é que ainda é possível comprar
um MacBook Pro novo após essa data com o Python 2 pré-instalado
porque scripts legados ainda dependem dele. Logo, o Python 2 continua
vivo.

A batalha pela primazia na área da ciência de dados


A essa altura, tanto o R quanto o Python tinham ferramentas habilitadas
para uma grande variedade de aplicações da ciência de dados. À medida
que as chamadas “guerras das linguagens” continuavam, outros
desenvolvimentos importantes levaram cada linguagem a encontrar seu
nicho.
Tanto o Python quanto o R vinham empacotados em builds especí cas.
Para o Python havia a distribuição Anaconda, que ainda é muito usada
(consulte o Capítulo 3). Para o R, a Revolution Analytics, uma
desenvolvedora de softwares de ciência de dados, produziu o Revolution
R Open. Embora sua build R não tenha sido amplamente adotada pela
comunidade, a empresa foi comprada pela Microsoft, o que indicou que
haveria um forte suporte corporativo da linguagem R.
Em 2011, a comunidade Python previu a expansão do machine learning
com o lançamento do pacote scikit-learn. Em 2016, esse evento foi seguido
pelo lançamento tanto do TensorFlow quanto do Keras para deep
learning, com uma saudável dose de suporte corporativo. Isso também
mostra o poder do Python como um interpretador de alto nível quando
executado em plataformas de alto desempenho. Por exemplo, há o
Amazon Web Services (AWS) Lambda para programação massiva
altamente concorrente, o Numba para computação de alto desempenho e
o já mencionado TensorFlow (https://www.tensor ow.org) para C++
altamente otimizado. Com sua ampla adoção fora da ciência de dados,
não é surpresa que o Python tenha conquistado uma reputação para a
implantação de modelos de uma maneira que o R não conseguiria.
O ano de 2011 também presenciou o lançamento do IDE RStudio
(https://rstudio.com) pela empresa de mesmo nome, e nos meses seguintes
a comunidade R começou a migrar para essa ferramenta. Nesse período,
usar R era, em muitos aspectos, o mesmo que usar o RStudio. Também é
importante observar a in uência que o RStudio teve na promoção do R
como uma linguagem de programação adequada para várias aplicações
centradas na ciência de dados.
Enquanto tudo isso acontecia, um segmento cada vez maior da
comunidade R passou a usar um conjunto de pacotes, muitos deles
criados ou supervisionados por Hadley Wickham, que começaram a
reformular e simpli car os uxos de trabalho típicos realizados com
dados. Grande parte do que esses pacotes faziam era padronizar a sintaxe
das funções R, assim como as estruturas de armazenamento de dados de
entrada e saída. O conjunto de pacotes começou a ser coloquialmente
chamado de “Hadleyverse”. Em uma palestra importante na conferência
useR! de 2016 na Universidade de Stanford, Wickham pôs um m nisso,
acendendo chamas digitais para queimar seu nome e cunhando o termo
“Tidyverse”. Desde que Wickham ingressou na RStudio, a empresa vem
desenvolvendo e promovendo ativamente o ecossistema Tidyverse, que
talvez tenha se tornado o dialeto dominante em R. Examinaremos isso
com mais detalhes no Capítulo 2.
Podemos considerar que o R contém pelo menos dois “paradigmas”, ou
“dialetos”. Eles podem ser combinados, mas cada um tem sua própria
natureza. O base-R é o que o R foi e, provavelmente, ainda é. O Tidyverse
reformula o base-R em um amplo e abrangente conjunto de pacotes e
funções que funcionam bem juntos, geralmente usando piping,5 e é
baseado principalmente em dataframes.6 Acredito que o BioConductor
forneça mais um dialeto, que é direcionado a uma disciplina especí ca, a
bioinformática. Certamente você descobrirá que alguns pacotes maiores
podem conter particularidades su cientes para também serem
considerados dialetos, mas deixemos isso de lado. Atualmente, o R está
no patamar em que alguns usuários só conhecem (ou aprendem) a
maneira do Tidyverse de fazer as coisas. A diferença entre o base R e o
Tidyverse pode parecer corriqueira, mas vi muitos aprendizes de R
tentando entender por que o Tidyverse existe. Isso está ocorrendo
parcialmente porque muitos códigos base R criados há anos ainda estão
em uso ativo e não podem ser ignorados. Embora os defensores do
Tidyverse argumentem que esses pacotes simpli cam muito a vida para o
iniciante, dialetos rivais podem causar uma confusão desnecessária.
Também podemos considerar que o Python contém dialetos distintos. A
instalação vanilla do Python é a mais simples e opera de maneira
diferente de um ambiente que importa a pilha PyData. Em geral, os
cientistas de dados trabalham dentro da pilha PyData; logo, há menos
confusão entre dialetos.

Convergência para a cooperação e a criação de comunidade


Durante algum tempo, parecia que a atitude predominante nas guerras de
linguagens era uma mentalidade nós contra eles. Um olhar de desdém
direcionado à tela do computador de outra pessoa. Parecia que o Python
ou o R acabaria desaparecendo do cenário da ciência de dados. Teríamos
uma monocultura! Alguns cientistas de dados ainda torcem para que isso
aconteça, mas supomos que você não seja um deles. E também houve
uma época em que parecia que o Python e o R estavam tentando imitar
um ao outro, apenas portando uxos de trabalho de modo que a
linguagem não importasse. Felizmente, esses esforços não prosperaram.
Tanto o Python quanto o R têm vantagens únicas; tentar imitar um ao
outro parece não levar isso em consideração.
Atualmente muitos cientistas de dados das comunidades Python e R
reconhecem que as duas linguagens são excelentes, úteis e
complementares. Para voltarmos a um aspecto importante do prefácio, a
comunidade de cientistas de dados convergiu para um ponto de
cooperação e construção de comunidade – para o benefício de todos os
envolvidos.
Estamos prontos para conceber uma nova comunidade de cientistas de
dados bilíngues. O desa o é que muitos usuários de cada linguagem não
sabem exatamente como elas se complementam ou quando usar cada
uma. Apareceram algumas soluções com o passar dos anos, mas falaremos
sobre isso na Parte IV.

Considerações nais
Agora você já deve ter uma boa ideia de aonde chegamos em 2021 e de
como isso ocorreu. Na próxima parte, introduziremos cada grupo de
usuários a uma nova linguagem.
Uma última observação: os usuários do Python chamam a si mesmos de
Pythonistas, que é um nome muito legal! Não há um equivalente em R, e
seus usuários também não têm um representante tão legal no mundo
animal, mas é isso que acontece quando se tem uma linguagem de uma
única letra. Normalmente os usuários do R são chamados de… adivinhe…
useRs! (usuáRios!) (O ponto de exclamação é opcional). Na verdade, a
conferência anual o cial chama-se useR! (aqui o ponto de exclamação é
obrigatório), e a editora Springer tem uma série de livros muito boa e de
lançamento contínuo com o mesmo nome. Usaremos esses nomes de
agora em diante.
A Figura 1.1 fornece um resumo de alguns dos principais eventos que
destacamos neste capítulo, além de outros momentos importantes de
interesse.
Figura 1.1: Linha do tempo de momentos importantes do Python e do R para
a ciência de dados.

1 Bem, na verdade seria mais algo como For Statisticians, By Statisticians (para estatísticos, por
estatísticos), mas FSBS não soa tão bem.
2 Com a possível exceção de Computational Methods for Data Analysis, que admito não ter lido.
3 Acredito que já podemos rastrear esse relacionamento nos primórdios do R, como evidenciado
pela notação de fórmula e dos diversos conjuntos de dados internos. No entanto, faltava um
framework consistente e intuitivo.
4 Isso chegou até mesmo a levar alguns desenvolvedores proeminentes a se declararem contra o
eventual desenvolvimento do Python 4.0. Será interessante acompanhar como o Python vai se
desenvolver!
5 Isto é, usando a saída de uma função como a entrada de outra.
6 Os usuários do Python podem não estar familiarizados com o termo base. Ele indica apenas a
funcionalidade interna da linguagem sem nenhuma instalação de pacote adicional. O base-R
está equipado para a análise de dados. Em Python, por padrão o cientista de dados precisaria
importar a pilha PyData.
PARTE II

Bilinguismo I: Aprendendo uma nova


linguagem

Nesta parte, introduzirei as duas linguagens básicas da ciência de dados:


Python e R. Diferentemente do que ocorre em outras introduções, espero
que o leitor tenha alguma familiaridade com uma linguagem antes de ser
introduzido à outra. Ou seja, espero que você já tenha alguma
experiência. Gostaria de poder pedir a você que deixasse sua bagagem na
porta, mas nesse caso ela precisa ser trazida; logo, não poderei fazê-lo. Em
vez disso, carregaremos a bagagem! É preciso reconhecer que o Python e
o R funcionam de maneira muito diferente e nem sempre há como fazer
um mapeamento 1:1. No entanto, isso não é problema!
Quando ensino R e Python para iniciantes sem nenhuma experiência,
cada lição é um elemento, um componente básico do todo. Os quatro
primeiros elementos são:
Funções
Como executar ações, isto é, os verbos.
Objetos
Como armazenar informações, isto é, os substantivos.
Expressões lógicas
Como fazer perguntas.
Indexação
Como encontrar informações.
Existem muitos níveis além desses quatro elementos, mas esses são os
essenciais e básicos. No momento em que você os conhecer bem, terá as
ferramentas para se aprofundar por conta própria. Meu objetivo é levá-lo
até esse ponto. Logo, os próximos capítulos não são introduções
completas a cada linguagem.
O Apêndice contém um dicionário bilíngue de Python:R para consulta
rápida. Ele o ajudará a traduzir um código com o qual você esteja
familiarizado para a nova linguagem ainda desconhecida.
Capítulo 2
Comece aqui se você for um Pythonista que deseja saber como pensa o
usuário de R (useR).
Capítulo 3
Comece aqui se você for um useR que deseja saber como pensa o
Pythonista.
Quando estiver familiarizado com sua nova linguagem, passe para a
Parte III para aprender quando é mais apropriado usar cada uma.
CAPÍTULO 2

R para Pythonistas

Rick J. Scavetta
Bem-vindo, bravo Pythonista, ao mundo useR!1 Neste capítulo, vou
apresentá-lo aos principais recursos do R e tentarei esclarecer algumas
partes confusas que você encontrará durante o percurso. Logo, será útil
mencionar o que não vamos fazer.
Em primeiro lugar, não estamos escrevendo para o cientista de dados
leigo. Se você deseja aprender R a partir do zero, existem muitos recursos
excelentes disponíveis – na verdade, recursos demais para podermos
mencionar. Recomendamos que você os examine e selecione os que
atenderem às suas necessidades e estilo de aprendizagem. Aqui,
exibiremos tópicos e questões que podem confundir o iniciante sem
experiência. Faremos alguns desvios de percurso para explicar os tópicos
que esperamos que ajudem especi camente o Pythonista amigável a se
adaptar mais facilmente ao R.
Em segundo lugar, esse não é um dicionário bilíngue; você encontrará um
no Apêndice, mas sem contexto ele não é útil. Aqui, queremos conduzi-lo
por uma jornada de exploRação e compReensão. Queremos que você se
habitue ao R para começar a pensar em R, tornando-se bilíngue. Logo,
para ajudar na apresentação, podemos introduzir alguns itens em um
momento posterior, o que não faríamos se estivéssemos escrevendo a
narrativa para um iniciante. No entanto, esperamos que você volte a este
capítulo quando precisar se lembrar de como executar tarefas familiares
em uma nova linguagem.
Em terceiro lugar, esse não é um guia de nitivo. Após aprender a
linguagem R, você terá ótimos momentos explorando-a com mais
cuidado para fazê-la atender às suas necessidades especí cas conforme
elas forem surgindo. Como mencionamos na primeira parte do livro, a
comunidade R é diversi cada, amigável, acolhedora – e útil! Estamos
convencidos de que é uma das culturas menos “tech-bro”2 existentes. Para
conhecer melhor a comunidade, você pode seguir #rstats no Twitter
(https://oreil.ly/YOfeZ).

Trabalhando com o R
Para acompanhar os exercícios deste capítulo, você pode acessar o R
online usando o RStudio Cloud ou instalar o R e o RStudio localmente.
O RStudio Cloud é uma plataforma que dá acesso a uma instância R (por
meio de um IDE RStudio) que nos permite fazer o upload de nossos
próprios dados e compartilhar projetos. Abordaremos os dois métodos
nos próximos parágrafos.
Para usar o RStudio Cloud, crie uma conta (https://rstudio.cloud) e acesse
nosso projeto disponível publicamente (https://oreil.ly/21Sr2). Certi que-
se de salvar uma cópia do projeto em seu espaço de trabalho para ter sua
própria cópia; você verá o link no título.
Sua sessão do RStudio deve car parecida com a da Figura 2.1. Abra ch02-
r4py/r4py.R e pronto! Você já pode acompanhar todos os exemplos. Para
executar comandos, pressione Ctrl + Enter (ou Command-Enter).
Figura 2.1: Nosso projeto no RStudio Cloud.
Para executar o R localmente, você o encontrará disponível com a
distribuição Anaconda, se a estiver usando; caso contrário, poderá
instalá-lo diretamente. Primeiro, baixe e instale o R especí co do seu
sistema operacional (https://www.r-project.org). O R v4.0 foi lançado em
junho de 2020 e, ao contrário do Pythonv3.x, é compatível com versões
anteriores, com algumas exceções. É preciso que você esteja executando
pelo menos o R 4.0.0: “Taking O Again”. Cada versão recebeu um nome
inspirado pelos quadrinhos Peanuts (a tirinha e a franquia de lmes
clássica estrelada por Charlie Brown, Snoopy e companhia), o que
considero um toque pessoal interessante. Em seguida, instale o IDE
RStudio Desktop (https://rstudio.com).
Para concluir, de na o projeto no qual irá trabalhar. Isso é um pouco
diferente de quando usamos um ambiente virtual, o que discutiremos
posteriormente. Existem duas maneiras de criar um projeto com arquivos
preexistentes.
Em primeiro lugar, se você estiver usando o Git, cará feliz em saber que
o RStudio também é um cliente Git GUI básico. No RStudio, selecione
File > “New project” > Version Control > Git e insira a URL de
repositório https://github.com/moderndatadesign/PyR4MDS. O nome do
diretório do projeto usará o nome do repositório automaticamente.
Selecione onde deseja armazenar o repositório e clique em Create Project.
Em segundo lugar, se você não estiver usando o Git, poderá baixar e
descompactar o repositório a partir de
https://github.com/moderndatadesign/PyR4MDS. No RStudio, selecione
File > Existing Directory e navegue até o diretório baixado. Um novo
arquivo de projeto R, *.Rproj, será criado nesse diretório.
Sua sessão do RStudio deve car com a aparência da Figura 2.2. Abra
ch02-r4py/r4py.R e pronto! Você já pode acompanhar todos os exemplos.
Para executar comandos, pressione Ctrl + Enter (ou Command-Enter).

Figura 2.2: Nosso projeto no RStudio.

Projetos e pacotes
Poderíamos começar a explorar o R usando um conjunto de dados
interno e entrando diretamente no Tidyverse (introduzido no Capítulo 1),
mas pre ro parar, tomar fôlego e começar nossa história pelo início.
Começaremos lendo um arquivo CSV simples. Para fazer isso, usaremos
um conjunto de dados que já está disponível em R no pacote ggplot2.
Para o que temos em mente, estamos mais interessados em como fazê-lo
em R do que com a análise real. Forneci o conjunto de dados como um
arquivo no repositório do livro
(https://github.com/moderndatadesign/PyR4MDS).
Se você de nir seu projeto corretamente, tudo que terá de fazer é executar
o comando a seguir. Se esse comando não funcionar, não se preocupe,
voltaremos a ele em breve.
diamonds <- read.csv("ch02-r4py/data/diamonds.csv")
Como em Python, as aspas simples ('') e duplas ("") são intercambiáveis,
embora o uso de aspas duplas seja preferencial.
Você já deve ter importado o arquivo e ele está disponível como um
objeto em seu ambiente global, onde cam os objetos de nidos pelo
usuário. A primeira coisa que notará é que o painel de ambiente
(environment) do RStudio está exibindo o objeto, já fornecendo algumas
informações resumidas (Figura 2.3).

Figura 2.3: Detalhamento de um data frame.


Esse detalhe simples e agradável é semelhante ao da extensão do Jupyter
Notebook para o VS Code (consulte o Capítulo 3), que também permite
visualizar o ambiente. Embora esse seja um recurso padrão no RStudio,
visualizar uma lista de objetos ao criar scripts em Python, ou em muitas
linguagens, não é típico. Clicar na pequena seta azul ao lado do nome do
objeto revelará uma descrição textual.
Clicar no nome o abrirá em um visualizador semelhante ao Excel
(consulte a Figura 2.4).

Figura 2.4: Data frame em visualização de tabela.


Visualizador do RStudio
O visualizador do RStudio é melhor que o do Excel, já que ele só
carrega na memória o que está sendo visto na tela. Você pode procurar
algum texto especí co e ltrar seus dados aqui; logo, é uma
ferramenta útil para a veri cação dos dados.
Embora esses recursos sejam ótimos, alguns useRs os consideram
mais próprios de uma GUI que de um IDE. Os pythonistas
concordam e alguns criticam a experiência de usuário do RStudio por
causa disso. Concordo em parte, já que vi que esse detalhe pode
encorajar práticas inadequadas. Por exemplo, para importar seu
conjunto de dados, você também poderia ter clicado em Import
Dataset. Isso pode ser conveniente se você tiver di culdades para
analisar a estrutura do arquivo, mas leva a ações não documentadas e
não reproduzíveis que são muito frustrantes já que os scripts/projetos
não serão independentes. O comando para a importação do arquivo
será executado no console e cará visível no painel de histórico, mas
não aparecerá no script a menos que você o copie explicitamente. Isso
resulta em objetos do ambiente não de nidos no script. No entanto,
lembre-se de que o RStudio não é o R. Você pode usar o R com outros
editores de texto (por exemplo a extensão ESS [Emacs Speaks Statistics] para o
Emacs (https://ess.r-project.org).
Se não for possível importar seus dados com os comandos anteriores, isso
terá ocorrido porque (i) o arquivo não existe nesse diretório ou (ii) você
está usando o diretório de trabalho errado, o que é mais provável. Você
pode car tentado a escrever algo inadequado como:
diamonds <- read.csv("ch02-r4py/data/diamonds.csv")
Você aprenderá a evitar o uso de caminhos embutidos em código quando
utilizar ambientes virtuais com Python. Usar caminhos relativos, como
zemos anteriormente, assegura que o diretório de arquivos contenha
todos os arquivos de dados necessários. O diretório de trabalho e o
projeto não são ambientes virtuais, mas são muito úteis; logo, iremos
examiná-los!
O diretório de trabalho é o primeiro lugar no qual o R procura um
arquivo. Quando você usar projetos R, a localização do diretório de
trabalho será onde quer que o arquivo *.Rproj estiver. Logo, ch02-r4py é
um subdiretório de nosso diretório de trabalho. Não importa o nome
dado ao diretório de trabalho ou onde ele se encontra. Você poderá mover
o projeto inteiro para qualquer local em seu computador e ele continuará
funcionando quando o arquivo *.Rproj for aberto no RStudio.

Se você não estiver usando projetos R, provavelmente seu diretório


de trabalho será o diretório home, exibido como project: (None) no
RStudio. Isso não é bom porque você terá de especi car o caminho
inteiro do seu arquivo em vez de especi car apenas os subdiretórios
do projeto. Você encontrará os comandos getwd() para acessar e setwd()
para de nir o diretório de trabalho em muitos tutorias
desatualizados. Não use esses comandos! Eles geram os mesmos
problemas de caminhos de arquivo completos embutidos em código.
Voltando ao nosso comando diamonds <- read.csv("ch02-
r4py/data/diamonds.csv"), você notará alguns problemas que
confundirão e/ou irritarão o Pythonista experiente. Três itens especí cos
se destacam.
Em primeiro lugar, observe que é comum, e até mesmo melhor, usar <-
como operador de atribuição em R. Você pode usar =, como em Python, e
na verdade vemos useRs proeminentes e experientes fazendo isso,
porém <- é mais explícito como atribuição a um objeto já que = também é
usado para atribuir valores a argumentos em chamadas de função, e todos
nós sabemos o quanto os Pythonistas gostam de ser explícitos!

Na verdade, o operador de atribuição <- é um operador legado


originário do teclado QWERTY pré-padronizado em que <- não
signi cava mova o cursor um espaço para a esquerda, indicando literalmente faça <-
aparecer.
Em segundo lugar, observe que o nome da função é read.csv(). No
entanto, isso não é um erro de digitação. csv() não é um método do
objeto read, como também não é uma função do módulo read. Essas
duas interpretações seriam perfeitamente aceitáveis se esse fosse um
comando Python. Em R, exceto em alguns casos relevantes, “.” não
signi ca nada de especial. Isso é um pouco irritante quando se está
acostumado com linguagens mais OOP (object-oriented programming,
programação orientada a objetos) nas quais “.” é um caractere especial.
Para concluir, você deve ter notado que não inicializamos nenhum pacote
para executar essa tarefa. As variantes da função read.*() fazem parte do
base-R. O interessante é que existem maneiras mais novas e convenientes
de ler arquivos se essas funções não atenderem às suas necessidades. Por
exemplo, a função read_csv() faz parte do pacote readr. Sabemos que
você não pode esperar para ver o que é esse “_”!
Geralmente, quando vemos funções simples com “.”, são funções antigas
do base-R criadas quando ninguém se preocupava com o fato de que seria
confuso haver “.” nos nomes. As funções dos pacotes mais novos do
Tidyverse (por exemplo, readr) tendem a usar “_” (consulte o Capítulo 1).
Elas fazem basicamente a mesma coisa, mas com alguns pequenos ajustes
que as tornam mais amigáveis para o usuário.
Veremos isso em ação com readr. Como em Python, você terá de instalar
o pacote. Normalmente a instalação é feita diretamente no console R; não
existe algo equivalente ao pip em R.
Use o comando a seguir:
install.packages("tidyverse")

No RStudio, você pode instalar pacotes usando a opção Packages do


painel inferior direito e clicando em Install. Digite tidyverse,
certi que-se de que a caixa Install All Dependencies esteja marcada e
clique em OK. Se você usar esse procedimento, não clique nas caixas
de seleção ao lado dos nomes dos pacotes instalados. Isso inicializará
o pacote, mas não o registrará em seu script.
Por padrão, esse comando instalará os pacotes e suas dependências a
partir do CRAN, o repositório de pacotes R o ciais. Os pacotes o ciais
passam pelo controle de qualidade e cam hospedados mundialmente em
servidores espelhados. Na primeira vez que você executar essa ação, será
solicitado a selecionar a partir de que mirror site3 deseja fazer a instalação.
Geralmente não importa que site é escolhido. Você verá um grande
volume de texto em vermelho à medida que os pacotes básicos do
Tidyverse e todas as suas dependências forem instalados. Essa é em
grande parte apenas uma maneira conveniente de fazer vários pacotes
úteis serem instalados ao mesmo tempo.
O problema mais comum da instalação de pacotes ocorre quando a
pessoa não tem permissão de gravação no diretório de pacotes. Isso
demandará que você crie uma biblioteca pessoal. É sempre possível saber
onde nossos pacotes estão instalados usando
.libPaths()
[1] "/Library/Frameworks/R.framework/Versions/4.0/Resources/library"
Se você tiver uma biblioteca pessoal, ela será exibida aqui na segunda
posição.

Ao contrário dos Pythonistas, que tendem a usar ambientes virtuais,


normalmente os useRs instalam um pacote uma única vez, tornando-
o disponível em todo o sistema. Após muitas tentativas malfadadas
de implementar uma solução para bibliotecas especí cas de projetos
em R, atualmente a favorita é o pacote renv (https://oreil.ly/GjXEw), isto é,
o R environment (ambiente R).
Como em Python, após a instalação de um pacote, ele precisa ser
inicializado em cada nova sessão do R. Quando dizemos inicializar, ou
carregar, um pacote, o que queremos dizer realmente é “usar a função
library() para carregar um pacote instalado e anexá-lo ao namespace,
isto é, ao ambiente global (global environment)”. Todos os seus pacotes
comporão a sua biblioteca (library), daí o nome library(). O conjunto
de pacotes básico do Tidyverse pode ser carregado com o uso de
library(tidyverse). Isso não é mistério e nem um problema, mas você
pode querer carregar apenas os pacotes que serão usados em vez de
encher seu ambiente desnecessariamente. Começaremos com readr, que
contém a função read_csv().
# R
library(readr)

A seguir temos uma opção equivalente:


# Equivalente em Python
import readr

Embora o R use a OOP, quase sempre ele opera em segundo plano; logo,
você nunca verá aliases estranhos para pacotes como:
import readr as rr

Esse é um conceito inexistente em R. Após você ter anexado o pacote,


todas as funções e conjuntos de dados desse pacote estarão disponíveis no
ambiente global.

Isso traz à mente outra função legada que você pode vir a encontrar.
De nitivamente você deve evitar attach() (e também sua
contrapartida detach()). Essa função permite anexar um objeto ao
ambiente global, de forma semelhante a como anexamos um pacote.
Logo, seria possível chamar elementos do objeto diretamente, sem
antes especi car o nome do objeto de maneira explícita, da mesma
forma que chamamos funções de um pacote sem precisar usar o
nome do pacote explicitamente. O motivo que fez esse recurso perder
o suporte foi que, se tivermos muitos objetos de dados para acessar,
nomes con itantes podem ser um problema (isto é, podem levar ao
mascaramento de objetos). Além disso, não é algo explícito.
Abordaremos outro problema do carregamento de pacotes antes de
continuar: você verá com frequência:
require(readr)
require() carregará um pacote instalado e também retornará
TRUE/FALSE dependendo do sucesso da operação. Isso é útil para
veri carmos se um pacote existe; logo, deve ser reservado para os casos
em que for necessário. Geralmente é melhor usar library().
Bem, leremos nosso conjunto de dados novamente, dessa vez usando
read_csv() para fazer algumas comparações simples entre os dois
métodos.
> diamonds_2 <- read_csv("R4Py/diamonds.csv")
Parsed with column specification:
cols(
carat = col_double(),
cut = col_character(),
color = col_character(),
clarity = col_character(),
depth = col_double(),
table = col_double(),
price = col_double(),
x = col_double(),
y = col_double(),
z = col_double()
)

Você deve ter notado que obtivemos uma descrição mais detalhada do
que ocorreu.
Como mencionamos anteriormente, as opções de design do Tidyverse
tendem a ser mais amigáveis para o usuário do que os processos mais
antigos que elas atualizam. Essa saída informa os nomes das colunas de
nossos dados tabulares e seus tipos (consulte a Tabela 2.2).
Observe também que a tendência atual em R é o uso de snake case,
underscores (“_”) entre as palavras e só letras minúsculas. Embora
normalmente a adoção de um guia de estilo para R seja baixa,
Advanced R, de Hadley Wickham (CRC Press), oferece boas sugestões. A
Google também tentou promover um guia de estilo para R
(https://oreil.ly/24XzZ), mas parece que a comunidade não é muito
rigorosa nesse aspecto. Isso contrasta com a estrita adoção do PEP 8 Style
Guide for Python Code, criado por Guido van Rossum e lançado quando
surgiu o Python.

Triunfo dos tibbles


Até agora, importamos nossos dados duas vezes, usando dois comandos
diferentes. Isso foi feito para você ver como o R funciona em segundo
plano e detectar alguns comportamentos típicos do Tidyverse versus o
pacote básico. Já mencionamos que você pode clicar no objeto no
Environment Viewer (visualizador de ambiente), o painel superior direito,
para examiná-lo, mas também é comum apenas exibi-lo no console. Você
poderia executar:
> print(diamonds)

No entanto, a função print() não é necessária exceto em casos


especí cos, como dentro de um loop for. Como em um Jupyter
Notebook, você pode simplesmente executar um comando com o nome
do objeto; por exemplo:
> diamonds

Ele exibirá o objeto no console. Não vamos reproduzi-lo aqui, mas, se


você executar o comando > diamonds, notará que a saída não é
adequada! Na verdade, é questionável por que a saída padrão permite que
tantas informações sejam exibidas no console no modo interativo. Agora
tente com o data frame que lemos usando read_csv():
> diamonds_2
# Um tibble: 53.940 x 10
carat cut color clarity depth table price x y z
<dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43
2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31
3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31
4 0.290 Premium I VS2 62.4 58 334 4.2 4.23 2.63
5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75
6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48
7 0.24 Very Good I VVS1 62.3 57 336 3.95 3.98 2.47
8 0.26 Very Good H SI1 61.9 55 337 4.07 4.11 2.53
9 0.22 Fair E VS2 65.1 61 337 3.87 3.78 2.49
10 0.23 Very Good H VS1 59.4 61 338 4 4.05 2.39
# … com mais 53.930 linhas

Realmente, a saída é melhor do que a da versão padrão do base-R. Temos


uma tabela pequena e organizada com os nomes das colunas em uma
linha e abaixo são exibidos códigos de três letras para os tipos de dados,
inseridos em colchetes (<>). Só vemos as 10 primeiras linhas e depois há
uma nota informando o que não estamos vendo. Se houvesse colunas
demais para nossa tela, elas seriam listadas na parte inferior. Faça um
teste; de na a saída do seu console para ser mais estreita e execute o
comando novamente:
# Um tibble: 53.940 x 10
carat cut color clarity
<dbl> <chr> <chr> <chr>
1 0.23 Ideal E SI2
2 0.21 Premium E SI1
3 0.23 Good E VS1
4 0.290 Premium I VS2
5 0.31 Good J SI2
6 0.24 Very G… J VVS2
7 0.24 Very G… I VVS1
8 0.26 Very G… H SI1
9 0.22 Fair E VS2
10 0.23 Very G… H VS1
# … com mais 53.930 linhas,
# e mais 6 variáveis:
# depth <dbl>, table <dbl>,
# price <dbl>, x <dbl>,
# y <dbl>, z <dbl>
O base-R já era muito bom para a EDA (exploratory data analysis, análise
exploratória de dados), mas essa é uma conveniência que avança mais
uma etapa. No entanto, o que aconteceu? É muito importante entender
isso, mas antes queremos realçar mais dois pontos interessantes.
Em primeiro lugar, observe que não precisamos carregar o pacote readr
inteiro para ganhar acesso à função read_csv(). Poderíamos ter deixado
library(readr) de fora e usado apenas:
> diamonds_2 <- readr::read_csv("R4Py/diamonds.csv")

O operador de dois pontos duplos, ::, é usado no acesso a funções a


partir de um pacote, o que seria semelhante a:
from pandas import read_csv

Você verá :: sendo usado quando os useRs souberem que só precisarão


de uma função muito especí ca de um pacote, ou se as funções de dois
pacotes puderem entrar em con ito, caso em que eles preferirão evitar
anexar um pacote inteiro ao seu namespace.
Em segundo lugar, essa é a primeira vez que vemos dados reais em R, e é
fácil observar que a numeração começa em 1! (E por que não começaria?).
Exibindo objetos na tela
Como um aparte para indicar a exibição de objetos na tela, com
frequência vemos uma expressão inteira inserida em parênteses. Isso
representa simplesmente a execução da expressão e a exibição do
objeto na tela:
(aa <- 8)
Quase sempre essa notação causa confusão com os comandos. Chame
o objeto explicitamente e só a use se for necessário:
aa <- 8
aa
Além disso, é mais fácil simplesmente desativar a linha de exibição
como um comentário (use Ctrl + Shift + C no RStudio) em vez de ser
preciso voltar e remover todos esses parênteses adicionais.
Certo, agora chegamos ao âmago da questão. Por que diamonds e
diamonds_2 parecem tão diferentes quando exibidos no console?
Responder a essa pergunta nos ajudará a entender melhor como o R
manipula objetos. Para responder, examinaremos as classes desses
objetos:
class(diamonds)
[1] "data.frame"

class(diamonds_2)
[1] "spec_tbl_df" "tbl_df" "tbl" "data.frame"

Você deve saber o que é data.frame por conhecer o pd.DataFrame (bem,


podemos simplesmente admitir que um pd.DataFrame é apenas uma
implementação em Python de um data.frame da linguagem R?). No
entanto, usar a função read_csv() do Tidyverse produziu um objeto com
três classes adicionais. As duas que queremos mencionar aqui são a
subclasse tbl_df e a classe tbl; ambas são usadas conjuntamente para a
de nição de um tibble (daí o uso de tbl), que tem a estrutura de data
frame tbl_df.
Os tibbles são um recurso básico do Tidyverse que oferecem mais
benefícios do que os objetos do base-R. Por exemplo, a exibição no
console. Você deve se lembrar de que chamar o nome de um objeto é
apenas um atalho para uma chamada à função print(), que tem um
método para a manipulação de data frames. Como já anexamos o pacote
readr, agora ela tem um método para a manipulação de objetos da classe
tbl_df.
Logo, aqui podemos ver os princípios da OOP operando em segundo
plano com a manipulação implícita de classes de objetos e a chamada aos
métodos apropriadas a uma classe especí ca. Conveniente! Confuso?
Implícito! Entendo por que os Pythonistas cam aborrecidos, mas, se
você não se preocupar, verá que pode simplesmente continuar
trabalhando sem ter problemas.

Apresentação e exploração dos tipos de dados


Examinaremos nossos dados com mais detalhes e veremos como o R
armazena e manipula dados. Um data frame é uma estrutura de dados
bidimensional heterogênea. Parece simples, mas mesmo assim vamos
detalhá-la um pouco mais (consulte a Tabela 2.1).
Tabela 2.1: Estruturas de dados comuns em R
Nome Número de dimensões Tipo de dado
Vetor 1 Homogêneo
Lista 1 Heterogêneo
Data frame 2 Heterogêneo
Matriz 2 Homogêneo
Array n Homogêneo

Os vetores são a forma mais básica de armazenamento de dados. Eles são


unidimensionais e homogêneos. Isso signi ca que há um elemento após o
outro e todos os elementos têm o mesmo tipo. É como um array do
NumPy composto somente de escalares. Não usamos o nome escalares em
R; eles são considerados apenas um vetor com um único elemento.
Existem muitos tipos em R, e quatro “tipos de vetor atômico de nidos
pelo usuário” são mais usados. O termo atômico já nos diz que eles não
poderiam ser mais básicos do que mostra a Tabela 2.2.
Tabela 2.2: Os quatro tipos de vetor atômico de nidos pelo usuário mais
comuns em R
Abreviação
Tipo Abreviação para o data frame Descrição
para o tibble
Logical logi <lgl> Binário TRUE/FALSE, T/F, 1/0
Integer int <int> Números inteiros de [-Inf,Inf]
Double num <dbl> Números reais de [-Inf,Inf]
Character chr <chr> Todos os caracteres alfanuméricos, incluindo espaços em branco

Os outros dois tipos, menos comuns, de vetor atômico de nidos pelo


usuário são raw e complex.
Os vetores são blocos de construção básicos. Existem alguns detalhes que
precisamos saber sobre os vetores, então vamos examiná-los de uma vez
antes de voltar à linha de frente da ciência de dados, o apreciado data
frame.
Os quatro tipos de vetor atômico de nidos pelo usuário listados na
Tabela 2.2 estão ordenados de acordo com níveis crescentes de conteúdo
de informação. Quando criamos um vetor, o R tenta encontrar o tipo de
conteúdo de informação mais baixo que possa englobar todas as
informações desse vetor. Por exemplo, logical:
> a <- c(TRUE, FALSE)
> typeof(a)
[1] "logical"

logical é o equivalente em R a bool, mas raramente é chamado de


booleano ou binário. Além disso, é bom ressaltar que T e F não são termos
reservados em R; logo, não são recomendados para vetores lógicos,
embora sejam válidos. Em vez deles, use TRUE e FALSE. Examinemos os
números:
> b <- c(1, 2)
> typeof(b)
[1] "double"

> c <- c(3.14, 6.8)


> typeof(c)
[1] "double"

O R faz a conversão automática entre double e integer quando


necessário. O cálculo é feito principalmente com o uso de precisão dupla,
o que é re etido na abreviação de double no data frame sendo exibida
como numeric. A menos que você precise restringir explicitamente um
número para que seja realmente um inteiro, pode usar numeric/double.
Se quiser restringir valores ao tipo integer, pode fazer sua coerção para
um tipo especí co usando uma das funções as.*() ou usar o su xo L
para especi car que um número deve ser um inteiro.
> b <- as.integer(c(1, 2))
> typeof(b)
[1] "integer"

> b <- c(1L, 2L)


> typeof(b)
[1] "integer"

Os caracteres são as versões do R para as strings. Você deve conhecê-los


como str em Python, que confusamente é uma função comum em R,
str(), que fornece a estrutura de um objeto. Os caracteres também
costumam ser chamados de strings em R, inclusive em argumentos e
nomes de pacotes, o que é uma inconsistência infeliz:
> d <- c("a", "b")
> typeof(d)
[1] "character"

Se juntarmos tudo isso em um data frame básico usando data.frame()


ou o mais recentemente desenvolvido tibble por meio da função
tibble(), obteremos:
my_tibble <- tibble(a = c(T, F),
b = c(1L, 2L),
c = c(3.14, 6.8),
d = c("a", "b"))
my_tibble
# Um tibble: 2 x 4
a b c d
<lgl> <int> <dbl> <chr>
1 TRUE 1 3.14 a
2 FALSE 2 6.8 b

Observe que obtivemos a saída amigável de print() já que se trata de um


tibble. Quando examinamos a estrutura, vemos algumas características
confusas:
> str(my_tibble)
tibble [2 × 4] (S3: tbl_df/tbl/data.frame)
$ a: logi [1:2] TRUE FALSE
$ b: int [1:2] 1 2
$ c: num [1:2] 3.14 6.8
$ d: chr [1:2] "a" "b"

str() é uma função clássica de pacote básico que fornece uma saída
simples. Sua saída é semelhante ao que você verá quando clicar na seta de
exibição ao lado do nome do objeto no painel de ambiente. A primeira
linha mostra a classe do objeto (que já vimos). S3 se refere ao sistema
OOP especí co que esse objeto usa, que nesse caso é o mais básico e
menos estrito.
Alternativamente, também podemos usar a função glimpse() do
Tidyverse, do pacote dplyr:
> library(dplyr)
> glimpse(my_tibble)
Rows: 2
Columns: 4
$ a <lgl> TRUE, FALSE
$ b <int> 1, 2
$ c <dbl> 3.14, 6.80
$ d <chr> "a", "b"

Observe que a Tabela 2.2 também exibe a abreviação num, que não
aparece na saída de glimpse(). Ela se refere à classe “numérica”,
indicando o tipo double (para números de ponto utuante com precisão
dupla) ou integer.
Os exemplos anteriores nos mostraram que um data.frame é uma
coleção bidimensional heterogênea de vetores unidimensionais
homogêneos, todos com o mesmo tamanho. Ainda veremos por que o R
exibe tantos cifrões (o que certamente não tem nenhuma relação com seu
salário!).

Nomeando elementos (internos)


Já mencionamos que o snake case é a tendência atual na nomeação de
objetos em R. No entanto, nomear as colunas de um data frame é algo
totalmente diferente porque herdaremos os nomes da primeira linha do
arquivo fonte (source le). Os data frames do base-R obtidos, por
exemplo, com o uso da família de funções read.*() ou criados
manualmente com o uso da função data.frame() não permitem
nenhum caractere inválido. Os caracteres inválidos incluem todos os
espaços em branco e todos os caracteres reservados em R:
• Operadores aritméticos (+, –, /, * etc.)
• Operadores lógicos (&, | etc.)
• Operadores relacionais (==, !=, >, < etc.)
• Colchetes, parênteses, chaves ([, (, {, < e seu fechamento)
Além disso, embora eles possam conter números, não podem começar
com números. Vejamos o que acontece:
# Versão do pacote básico
data.frame("Weight (g)" = 15,
"Group" = "trt1",
"5-day check" = TRUE)
Weight..g. Group X5.day.check
1 15 trt1 TRUE
Todos os caracteres inválidos foram substituídos por .! Sei o que você está
pensando. Parece que o R se diverte zombando de quem é obsessivo com
os princípios da OOP! Não bastasse isso, qualquer variável que comece
com um número agora é antecedida por um X.
Bem, o que acha de importar um arquivo sem cabeçalho?
> diamonds_base_nohead <- read.csv("ch02-r4py/data/diamonds_noheader.csv",
header = F)
> names(diamonds_base_nohead)
[1] "V1" "V2" "V3" "V4" "V5" "V6" "V7" "V8" "V9" "V10"

No base-R, se não tivermos cabeçalhos, os nomes fornecidos terão V de


“variável” seguido do número dessa coluna.
O mesmo arquivo lido com uma das funções da família readr::read_*
() ou criado manualmente com tibble() mantém os caracteres
inválidos! Isso parece trivial, mas na verdade é algo que merece uma
crítica séria no Tidyverse e no qual devemos prestar atenção se
manipularmos scripts de outras pessoas. Vejamos:
> tibble("Weight (g)" = 15,
+ "Group" = "trt1",
+ "5-day check" = TRUE)
# Um tibble: 1 x 3
`Weight (g)` Group `5-day check`
<dbl> <chr> <lgl>
1 15 trt1 TRUE

Notou os pares de acentos graves (backticks) das colunas Weight (g) e 5-


day check? Agora é preciso usá-los se quisermos escapar os caracteres
inválidos. Talvez eles criem comandos mais informativos, já que temos o
nome completo, mas é provável que você queira manter os nomes das
colunas não só informativos como também curtos. Dados de unidade
(por exemplo, g para o peso) são informações separadas que pertencem à
legenda do dataset.
E não é só isso. Os nomes fornecidos a conjuntos de dados sem
cabeçalhos também são diferentes:
> diamonds_tidy_nohead <- read_csv("ch02-r4py/data/diamonds_noheader.csv",
col_names = F)
> names(diamonds_tidy_nohead)
[1] "X1" "X2" "X3" "X4" "X5" "X6" "X7" "X8" "X9" "X10"

Em vez de V temos X! Isso nos faz lembrar de que o Tidyverse é


considerado um dialeto distinto no R. Se você herdar um script
totalmente em base R, terá problemas se começar a usar funções do
Tidyverse indiscriminadamente. É como pedir um Berliner em uma
padaria de Berlim!4

Listas
As listas são outra estrutura de dados comum, mas não são exatamente o
que esperaríamos de uma lista em Python; logo, a nomenclatura pode
confundir. Na verdade, já encontramos listas em nossa curta jornada pelo
universo do R. Estou me referindo ao fato de o data.frame ser uma classe
especí ca de tipo list. Sim, é isso mesmo que você leu.
> typeof(my_tibble)
[1] "list"

A Tabela 2.1 mostra que uma lista é um objeto heterogêneo


unidimensional. Isso signi ca que cada elemento desse objeto
unidimensional pode ter um tipo diferente. Na verdade, as listas contêm
não só vetores, mas outras listas, data frames, matrizes e assim por diante.
No caso de cada elemento ser um vetor de mesmo tamanho, acabaremos
obtendo dados tabulares que serão então da classe data.frame.
Conveniente, não é mesmo? Normalmente, vemos as listas como a saída
de testes estatísticos; vejamos.
O data frame PlantGrowth é um objeto interno do R. Ele contém duas
variáveis (isto é, elementos da lista, também conhecidos como colunas
nos dados tabulares): weight e group.
> glimpse(PlantGrowth)
Rows: 30
Columns: 2
$ weight <dbl> 4.17, 5.58, 5.18, 6.11, 4.50, 4.6...
$ group <fct> ctrl, ctrl, ctrl, ctrl, ctrl, ctr...

O conjunto de dados descreve o peso (weight) das plantas desidratadas


(em gramas, como exibido na legenda dos dados) em 30 observações (isto
é, plantas individuais, também chamadas de linhas nos dados tabulares),
cultivadas de acordo com uma das três condições descritas em groups:
ctrl, trt1 e trt2. A conveniente função glimpse() não mostra esses três
grupos, mas a clássica função str() mostra:
> str(PlantGrowth)
'data.frame': 30 obs. of 2 variables:
$ weight: num 4.17 5.58 5.18 6.11 4.5 4.61 5.17 4.53 5.33 5.14...
$ group : Factor w/ 3 levels "ctrl","trt1",..: 1 1 1 1 1 1 1 1 1 1...

Se você está preocupado com <fct> e Factor w/ 3 levels, calma –


falaremos sobre isso após terminarmos com as listas.
Certo, façamos alguns testes. Poderíamos querer de nir um modelo linear
para weight descrito por group:
pg_lm <- lm(weight ~ group, data = PlantGrowth)

lm() é uma função básica e exível para a de nição de modelos lineares


em R. Nosso modelo foi escrito em notação de fórmula, em que weight ~
group é y ~ x. Talvez você conheça o ~ como o símbolo padrão para
“descrito por” em estatística. A saída é um tipo list de classe lm:
> typeof(pg_lm)
[1] "list"
> class(pg_lm)
[1] "lm"

Aqui temos dois itens dos quais queremos que você se lembre.
Em primeiro lugar, você se lembra de que mencionamos que um data
frame é uma coleção de vetores de mesmo tamanho? Agora podemos ver
que isso signi ca apenas que ele é uma classe especial de tipo list, na
qual cada elemento é um vetor com o mesmo tamanho. Podemos acessar
um elemento nomeado dentro de uma lista usando a notação $:
> names(PlantGrowth)
[1] "weight" "group"
> PlantGrowth$weight
[1] 4.17 5.58 5.18 6.11 4.50 4.61 5.17 4.53 5.33 5.14 4.81 4.17 4.41 3.59
[15] 5.87 3.83 6.03 4.89 4.32 4.69 6.31 5.12 5.54 5.50 5.37 5.29 4.92 6.15
[29] 5.80 5.26

Observe a maneira como ele é exibido, ao longo de uma linha, e o início


de cada linha começa com [] com a posição de um índice. (Já
mencionamos que o R começa a indexação em 1, certo?). No RStudio,
você obterá uma lista de preenchimento automático com os nomes das
colunas após digitar $.
Também podemos acessar um elemento nomeado dentro de uma lista
usando a mesma notação:
> names(pg_lm)
[1] "coefficients" "residuals" "effects" "rank"
[5] "fitted.values" "assign" "qr" "df.residual"
[9] "contrasts" "xlevels" "call" "terms"
[13] "model"

É possível ver que uma lista é uma ótima maneira de armazenar os


resultados de um teste estatístico, já que temos vários tipos de saída
diferentes. Por exemplo:
> pg_lm$coefficients
(Intercept) grouptrt1 grouptrt2
5.032 -0.371 0.494

é um vetor numérico nomeado de três elementos. (Embora seus elementos


sejam nomeados, o operador $ não é válido para vetores atômicos, mas é
claro que temos mais alguns truques reservados – consulte a indexação
com [] em “Como descobrir …o que quiser”, na página 51.) Não
entramos em detalhes, mas você deve ter percebido que, de acordo com os
dados, esperamos que haja três coe cientes (estimativas) em nosso
modelo.
Considere residuals:
> pg_lm$residuals
1 2 3 4 5 6 7 8 9 10
-0.862 0.548 0.148 1.078 -0.532 -0.422 0.138 -0.502 0.298 0.108
11 12 13 14 15 16 17 18 19 20
0.149 -0.491 -0.251 -1.071 1.209 -0.831 1.369 0.229 -0.341 0.029
21 22 23 24 25 26 27 28 29 30
0.784 -0.406 0.014 -0.026 -0.156 -0.236 -0.606 0.624 0.274 -0.266
Os resíduos estão armazenados em um vetor numérico nomeado de
30 elementos (lembre-se de que tínhamos 30 observações). Logo, as listas
são bastante convenientes para o armazenamento de dados e você as verá
com frequência em R, embora haja um esforço concentrado para fazer o
Tidyverse ser mais direcionado aos data frames e suas variantes.
Em segundo lugar, você deve se lembrar de que mencionamos que quase
sempre o . (ponto) não tem nenhum signi cado especial. Bem, essa é
uma das exceções em que ele tem um signi cado. Provavelmente o uso
mais comum seja quando ele especi ca tudo na de nição de um modelo.
Aqui, já que além da coluna weight, PlantGrowth só tem mais uma
coluna, poderíamos ter escrito:
lm(weight ~ ., data = PlantGrowth)

Não é necessário, porque temos uma única variável independente, mas em


alguns casos é conveniente. O conjunto de dados ToothGrowth tem uma
de nição experimental semelhante, mas estaremos medindo o nível de
crescimento dos dentes sob duas condições: o uso de um suplemento
especí co (supp) e sua dosagem (dose).

Aqui vale uma nota sobre os tipos de variáveis. Quando usamos a


fórmula y ~ x, estamos dizendo que x é uma variável independente ou
previsora e y depende de x, ou é a resposta do previsor.
lm(len ~ ., data = ToothGrowth)
# é o mesmo que
lm(len ~ supp + dose, data = ToothGrowth)

No entanto, como sempre, ser explícito tem suas vantagens, como na


de nição de modelos mais precisos:
lm(len ~ supp * dose, data = ToothGrowth)

Consegue identi car a diferença entre as duas saídas? A especi cação de


interações é feita com *.5

Fatos sobre os fatores


O último tópico que precisamos esclarecer antes de continuar é o
fenômeno factor. Os fatores são semelhantes ao tipo category do
pandas em Python. Eles são uma classe útil e fascinante do R. Quase
sempre estão presentes e não há por que se preocupar, mas que alerta, já
que seu uso correto ou incorreto pode facilitar ou di cultar muito a sua
vida, respectivamente. Vamos examiná-los agora.
“Fator” é um termo muito usado em estatística. Poderíamos chamá-lo de
variável categórica como em Python, mas você também o verá sendo
chamado de variável qualitativa ou discreta, em livros e em pacotes R
especí cos, como o RColorBrewer e o ggplot2, respectivamente. Embora
todos esses termos se re ram ao mesmo tipo de variável, quando dizemos
“fator” em R, nos referimos a uma classe de tipo integer. É como quando
dizemos que o data.frame é uma classe de tipo list. Observe:
> typeof(PlantGrowth$group)
[1] "integer"
> class(PlantGrowth$group)
[1] "factor"

Você pode identi car facilmente um fator porque, tanto na saída de str()
(consulte “Listas”, na página 46) quanto na formatação simples dos
vetores, os níveis são declarados:
> PlantGrowth$group
[1] ctrl ctrl ctrl ctrl ctrl ctrl ctrl ctrl ctrl ctrl
[11] trt1 trt1 trt1 trt1 trt1 trt1 trt1 trt1 trt1 trt1
[21] trt2 trt2 trt2 trt2 trt2 trt2 trt2 trt2 trt2 trt2
Levels: ctrl trt1 trt2

Os níveis são os nomes dados pelos estatísticos para o que normalmente


chamamos de grupos. Outra característica é que, embora tenhamos
caracteres, eles não são inseridos em aspas! Isso é muito curioso porque
na verdade podemos tratá-los como caracteres, ainda que sejam de tipo
integer (consulte a Tabela 2.2). Pode ser interessante examinarmos a
estrutura interna de um objeto usando dput(). Aqui podemos ver que
temos um vetor integer c(1L...) e dois atributos, label e class:
> dput(PlantGrowth$group)
structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L,
3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L),
.Label = c("ctrl", "trt1", "trt2"),
class = "factor")

Os rótulos de nem os nomes de cada nível do fator e são mapeados para


os inteiros, com 1 sendo ctrl e assim por diante. Logo, quando fazemos
a exibição na tela só vemos os nomes, e não os inteiros. Isso costuma ser
aceito como um caso de uso herdado da época em que a memória era
cara e fazia sentido salvar um inteiro muitas vezes em vez de salvar um
vetor de caracteres possivelmente longo.
Até agora, o único tipo de fator que vimos estava descrevendo uma
variável nominal (uma variável categórica sem ordem), mas também
temos uma solução adequada para as variáveis ordinais. Veja esta variável
do conjunto de dados diamonds:
> diamonds$color
[1] E E E I J J I H E H ..
Levels: D < E < F < G < H < I < J

Os níveis têm uma ordem; logo, D vem antes de E e assim por diante.

Como descobrir…o que quiser


Até aqui vimos como o R armazena dados, e várias sutilezas que você terá
de memorizar, principalmente algo que possa confundir um Pythonista.
Passaremos para as expressões lógicas e a indexação, que é o mesmo que
dizer: como descobrir…o que quiser?
As expressões lógicas são combinações de operadores relacionais, que
fazem perguntas de comparação com respostas sim/não, e operadores
lógicos, que combinam essas perguntas de sim/não.
Começaremos com um vetor:
> diamonds$price > 18000
[1] FALSE FALSE FALSE FALSE FALSE FALSE
...

Esse código apenas pergunta quais de nossos diamantes são mais caros
que 18.000 dólares. Existem três elementos chave que sempre devemos
manter em mente aqui.
Em primeiro lugar, o tamanho do objeto menor, que nesse caso é o vetor
numérico não atribuído 18000 (com um elemento), será “reciclado” ao
longo de toda a extensão do vetor mais longo, nesse exemplo a coluna
price do data frame diamonds, com a notação $ (53.940 elementos). Em
Python chamamos isso de broadcasting, quando são usados arrays do
NumPy, e de vetorização como uma função distinta. Em R, chamamos os
dois casos de vetorização ou reciclagem de vetor.
Em segundo lugar, isso signi ca que o vetor da saída terá o mesmo
tamanho do vetor mais longo, nesse exemplo 53.940 elementos.
Em terceiro lugar, quando você encontrar um operador relacional ou
lógico, saberá que o vetor da saída sempre será um vetor lógico. (Lógico
como em TRUE/FALSE, e não como na lógica do Sr. Spock).
Se você quiser combinar perguntas, terá de combinar duas perguntas em
uma, como em quais seriam os diamantes mais caros e menores
(requintado, não?):
> diamonds$price > 18000 & diamonds$carat < 1.5
[1] FALSE FALSE FALSE FALSE FALSE FALSE
...
Observe que a ocorrência de todos os três elementos chave pode ser
con rmada. Quando apresentei os tipos de vetores atômicos, não
mencionei que o lógico também é de nido por 1 e 0. Isso signi ca que
podemos fazer cálculos com vetores lógicos, o que é muito conveniente.
Quantos diamantes pequenos e caros temos?
> sum(diamonds$price > 18000 & diamonds$carat < 1.5)
[1] 9
(Na verdade, isso não é su ciente.) Que proporção do meu conjunto de
dados eles representam? Apenas divida pelo número total de observações:
> sum(diamonds$price > 18000 & diamonds$carat < 1.5)/nrow(diamonds)
[1] 0.0001668521

É assim que fazemos e combinamos perguntas. Examinaremos a


indexação com o uso de []. Você já está familiarizado com o símbolo [],
mas acho que será mais fácil entendê-lo se o examinarmos diretamente
em R. Um resumo é fornecido na Tabela 2.3.
Tabela 2.3: Indexação
Uso Objeto de dados Resultado
xx[i] Vetor Vetor com apenas i elementos
xx[i] Lista, data frame, tibble Elemento i mantendo a estrutura original
xx[[i]] Lista, data frame, tibble Elemento i extraído de uma lista
xx[i,j] Data frame, tibble ou matriz As i linhas e j colunas de um data frame, tibble ou matriz
xx[i,j,k] Array As i linhas, j colunas e k dimensões de um array

i, j e k são três tipos de vetores diferentes que podem ser usados dentro
de []:
• Um vetor integer
• Um vetor lógico
• Um vetor de caracteres contendo nomes, se houver elementos
nomeados
Você deve conhecer isso pelo que temos em Python. No caso de vetores
integer e lógicos, eles podem ser vetores não atribuídos, ou ainda objetos
ou funções que gerem vetores integer ou lógicos. Os números não
precisam ser de tipo integer, embora números inteiros facilitem a situação.
Usar numeric/double faz o arredondamento para o próximo número
inteiro mais baixo, mas tente evitar usar números reais na indexação, a
menos que haja uma razão.
Começaremos com os inteiros. Faremos outro pequeno desvio aqui para
discutir o onipresente operador :, que não fará o que o seu cérebro
Pythonista acha que ele deveria fazer. Começaremos com um vetor de
caracteres interno, letters, que é semelhante a se tivéssemos a coluna de
um data frame, como em PlantGrowth$weight:
> letters[1] # O 1º elemento (a indexação começa em 1)
[1] "a"

É realmente um exemplo muito simples. Que tal contarmos para trás?


> letters[-4] # Tudo exceto o 4º elemento,
> # (*sem* o quarto elemento, contando para trás!)
[1] "a" "b" "c" "e" "f" "g" "h" ...

Não deu certo, - signi ca excluir um elemento, e não contar para trás,
mas foi uma boa tentativa. Também podemos excluir um intervalo de
valores:
> letters[-(1:20)] # Exclui os elementos 1 a 20
[1] "u" "v" "w" "x" "y" "z"

e, claro, indexar um intervalo de valores:


> letters[23:26] # Do 23º ao 26º elemento
[1] "w" "x" "y" "z"
E lembre-se, podemos combinar isso com qualquer coisa que nos forneça
um vetor integer. length() nos dirá quantos elementos temos em nossos
vetor, e lhs:rhs é a abreviação de seq(from = lhs, to = rhs, by =
1), que cria uma sequência de valores em etapas incrementais de nidas
por by, que nesse caso usa como padrão 1.
> # Do 23º ao último elemento
[1] "w" "x" "y" "z"

Isso signi ca que você sempre precisará de um lhs (left hand side, lado
esquerdo) e de um rhs (right hand side, lado direito) ao usar :. É uma
pena, mas a linha a seguir não funcionará:
> letters[23:] # erro

Usar [] inapropriadamente faz surgir uma mensagem de erro lendária e


misteriosa em R:
> df[1]
Error in df[1] : object of type 'closure' is not subsettable
> t[6]
Error in t[6] : object of type 'closure' is not subsettable

Sabe dizer onde erramos? df e t não são objetos de armazenamento de


dados de nidos que possamos indexar! São funções e, portanto, devem
ser seguidas de () onde forneceremos os argumentos. Os colchetes ([])
são sempre usados para criar subconjuntos, e essas funções (df() e t())
são de tipo closure, que não podem ser usadas em subconjuntos. Logo,
trata-se de uma mensagem de erro bem clara, e é um bom lembrete para
não darmos nomes ambíguos e curtos aos objetos ou para não
confundirmos funções com objetos de armazenamento de dados.
Até aqui tudo bem, mas você deve ter conhecimento de que o poder real
da indexação vem do uso de vetores lógicos para indexarmos elementos
TRUE especí cos, como no uso do tipo bool em Python. A maneira mais
comum de obtermos um vetor lógico para a indexação é usando uma
expressão lógica, como já discutimos. É exatamente isso que acontece
com o mascaramento no NumPy.
Então, quais seriam as cores desses vistosos diamantes?
> diamonds$color[diamonds$price > 18000 & diamonds$carat < 1.5]
[1] D D D D F D F F E
Levels: D < E < F < G < H < I < J

Aqui, estamos usando o preço (price) e o quilate (carat) para encontrar as


cores dos diamantes nos quais estamos interessados. Não é de
surpreender que sejam as cores de melhor classi cação. Você pode achar
tedioso ter de escrever diamonds$ repetidamente, mas podemos alegar
que isso torna tudo mais explícito, e é o que ocorre quando referenciamos
Series do pandas em Python. Já que estamos indexando um vetor,
obteremos um vetor como saída. Passemos para os data frames.
Poderíamos ter escrito o comando de indexação anterior como:
> diamonds[diamonds$price > 18000 & diamonds$carat < 1.5, "color"]
# Um tibble: 9 x 1
color
<ord>
1 D
2 D
3 D
4 D
5 F
6 D
7 F
8 F
9 E

Como era de se esperar, em [i,j], i sempre referencia as linhas


(observações) e j sempre referencia as colunas (variáveis). Observe que
também combinamos dois tipos de entrada diferentes, mas essa
abordagem funcionou porque eles estão em partes distintas da expressão.
Usamos um vetor lógico que tem o tamanho do número de observações
do data frame (graças à reciclagem do vetor) para obter todas as linhas
TRUE e depois usamos um vetor de caracteres para extrair um elemento
nomeado; lembre-se de que cada coluna de um data frame é um elemento
nomeado. Essa é uma formulação típica em R. A saída é um data frame,
na verdade um tibble porque usamos a indexação no data frame
diamonds e não em um vetor unidimensional especí co. Não devemos
nos demorar nesse tópico, mas é útil mencionar que, se não tivéssemos
um tibble, a indexação de uma única coluna (em j) retonaria um vetor:
> class(diamonds)
[1] "data.frame"
> diamonds[diamonds$price > 18000 & diamonds$carat < 1.5, "color"]
[1] D D D D F D F F E
Levels: D < E < F < G < H < I < J

Isso é realmente confuso e realça a necessidade de sempre conhecermos a


classe de nosso objeto de dados. O Tidyverse tenta resolver parte desse
problema mantendo data frames até mesmo em casos em que o base R
acaba fazendo a reversão para um vetor. As funções de indexação do
Tidyverse, mostradas a seguir, facilitam as coisas. (A versão abreviada do
pacote básico, subset(), funciona de forma semelhante, mas filter()
funciona melhor quando usada no contexto do Tidyverse.)
> diamonds %>%
+ filter(price > 18000, carat < 1.5) %>%
+ select(color)
# Um tibble: 9 x 1
color
<ord>
1 D
2 D
3 D
4 D
5 F
6 D
7 F
8 F
9 E

Introduzimos os princípios usados pelo Tidyverse na primeira parte do


livro, e agora estamos vendo-os em ação. O operador forward pipe ou
pipe, “%>%”, do código anterior permite desaninhar objetos e funções. Por
exemplo, poderíamos ter escrito:
> select(filter(diamonds, price > 18000, carat < 1.5), color)

Essa linha tem o formato de uma longa função aninhada que é muito
difícil de decifrar. Podemos pronunciar %>% como “e então” e, portanto,
ler o comando inteiro acima como “Pegue o conjunto de dados diamonds
e então ltre usando esses critérios e selecione apenas essas colunas”. Esse
recurso ajuda muito a ler e entender literalmente o código e é por isso que
dplyr é descrito como a gramática da análise de dados. Os objetos, como
os tibbles, são os substantivos, %>% é nossa pontuação, e as funções são os
verbos.
Embora geralmente o operador %>% seja usado para encadear funções do
Tidyverse, ele também pode ser empregado para desaninhar funções
aninhadas. Devido a esse uso ampliado, um operador forward pipe nativo,
“|>”, foi incluído no R v4.1 (lançado em 18 de maio de 2021, enquanto
nalizávamos este texto). Não está claro como os useRs o adotarão, dadas
a predominância de %>% e algumas pequenas diferenças entre os dois
operadores. Por enquanto, você pode continuar usando seguramente o
operador %>%.
Os cinco verbos mais importantes do dplyr estão listados na Tabela 2.4.
Tabela 2.4: Descrição das funções
Função Opera em Descrição
filter() linhas Usa um vetor lógico para reter apenas linhas TRUE
arrange() linhas Reordena as linhas de acordo com os valores de uma coluna especí ca
select() colunas Usa um nome ou uma função auxiliar para extrair apenas essas colunas
summarise() colunas Aplica funções de agregação a uma coluna
mutate() colunas Aplica funções de transformação a uma coluna

Já vimos filter() e select() em ação e agora examinaremos as


aplicação de funções com summarise() e mutate(). summarise() é
usada para aplicar uma função de agregação, que retorna um único valor,
como a média, mean(), ou o desvio-padrão, sd(). É comum vermos
summarise() sendo usada junto com a função group_by(). Em nossa
analogia com os elementos gramaticais, group_by() seria um advérbio;
ela modi ca como um verbo funciona. No exemplo a seguir, usamos
group_by() para adicionar um atributo Group ao nosso data frame e,
portanto, as funções aplicadas em summarise são especí cas do grupo. É
como o método .groupby() para pd.DataFrames!
> PlantGrowth %>%
+ group_by(group) %>%
+ summarise(avg = mean(weight),
+ stdev = sd(weight))
`summarise()` ungrouping output (override with `.groups` argument)
# Um tibble: 3 x 3
group avg stdev
<fct> <dbl> <dbl>
1 ctrl 5.03 0.583
2 trt1 4.66 0.794
3 trt2 5.53 0.443

mutate() é usada para aplicar uma função de transformação, que retorna


um número de saídas igual ao número de entradas. Nesses casos não é
incomum o uso tanto da sintaxe do Tidyverse quanto dos colchetes ([])
nativos em combinação com valores de índice especí cos. Por exemplo,
este conjunto de dados contém a área sob irrigação (milhares de hectares)
de diferentes regiões do mundo em quatro momentos diferentes no
tempo:
> irrigation <- read_csv("R4Py/irrigation.csv")
Parsed with column specification:
cols(
region = col_character(),
year = col_double(),
area = col_double()
)
> irrigation
# Um tibble: 16 x 3
region year area
<chr> <dbl> <dbl>
1 Africa 1980 9.3
2 Africa 1990 11
3 Africa 2000 13.2
4 Africa 2007 13.6
5 Europe 1980 18.8
6 Europe 1990 25.3
7 Europe 2000 26.7
8 Europe 2007 26.3
...

Poderíamos querer medir a alteração no fator multiplicador da área em


relação a 1980 para cada região:
irrigation %>%
group_by(region) %>%
mutate(area_std_1980 = area/area[year == 1980])
# Um tibble: 16 x 4
# Grupos: região [4]
region year area area_std_1980
<chr> <dbl> <dbl> <dbl>
1 Africa 1980 9.3 1
2 Africa 1990 11 1.18
3 Africa 2000 13.2 1.42
4 Africa 2007 13.6 1.46
5 Europe 1980 18.8 1
6 Europe 1990 25.3 1.35
7 Europe 2000 26.7 1.42
8 Europe 2007 26.3 1.40
...

Como com mutate(), podemos adicionar mais transformações, por


exemplo, a mudança percentual ao longo de cada momento no tempo:
> irrigation <- irrigation %>%
+ group_by(region) %>%
+ mutate(area_std_1980 = area/area[year == 1980],
+ area_per_change = c(0, diff(area)/area[-length(area)] * 100))
> irrigation
# Um tibble: 16 x 5
# Grupos: região [4]
region year area area_std_1980 area_per_change
<chr> <dbl> <dbl> <dbl> <dbl>
1 Africa 1980 9.3 1 0
2 Africa 1990 11 1.18 18.3
3 Africa 2000 13.2 1.42 20.0
4 Africa 2007 13.6 1.46 3.03
5 Europe 1980 18.8 1 0
6 Europe 1990 25.3 1.35 34.6
7 Europe 2000 26.7 1.42 5.53
8 Europe 2007 26.3 1.40 -1.50
...

Reexecutando reiterações
Observe que não precisamos de nenhum loop nos exemplos anteriores.
Intuitivamente você poderia querer aplicar um loop for para calcular
funções de agregação ou transformação para cada região, mas não é
necessário. Evitar loops for é como um passatempo em R, e pode ser feito
no pacote básico com a família de funções apply.
Já que a vetorização é tão fundamental para o R, existe algo como um
concurso não o cial de quem consegue escrever menos loops for.
Podemos imaginar os useRs em um mural com os dizeres: “Dias desde o
último loop for:” como os encontrados em fábricas relacionados a
acidentes.
Isso signi ca que existem alguns métodos muito antigos para a reiteração
de tarefas e certos métodos mais novos que tornam o processo mais
conveniente.
Os métodos antigos dependem da família de funções apply, listadas na
Tabela 2.5. Exceto por apply(), todos são pronunciados com a primeira
letra e depois apply, ou seja, é “t apply” e não “tapply”.
Tabela 2.5: Família apply do pacote básico
Função Uso
apply() Aplica uma função a cada linha ou coluna de uma matriz ou data frame
lapply() Aplica uma função a cada elemento de uma lista
sapply() Simpli ca a saída de lapply()
mapply() Versão multivariável de sapply()
tapply() Aplica uma função aos valores de nidos por um índice
emapply() Aplica uma função aos valores de um ambiente

Existem algumas tendências de rejeição do uso desses impulsionadores da


reiteração, mas você ainda verá muitos; logo, é útil conhecê-los. Isso o
ajudará a entender por que o Tidyverse surgiu. Como exemplo,
voltaremos às funções de agregação que aplicamos ao data frame
PlantGrowth anterior. Da família de funções apply, poderíamos ter
usado:
> tapply(PlantGrowth$weight, PlantGrowth$group, mean)
ctrl trt1 trt2
5.032 4.661 5.526
> tapply(PlantGrowth$weight, PlantGrowth$group, sd)
ctrl trt1 trt2
0.5830914 0.7936757 0.4425733

Esse código poderia ser lido como “pegue a coluna de peso do conjunto
de dados PlantGrowth, divida os valores de acordo com o rótulo da
coluna de grupo do conjunto de dados PlantGrowth, aplique a função de
média a cada grupo de valores e retorne um vetor nomeado”.
Percebe como será tedioso se você quiser adicionar mais funções? Os
vetores nomeados podem ser convenientes, mas não são uma maneira
comum de armazenar dados importantes.
Uma tentativa de simpli car esse processo foi implementada no plyr, o
precursor do dplyr. A palavra plyr deve ser pronunciada como plier
(alicate), da mesma maneira que a pequena ferramenta de mão
multifuncional. O pacote é usado desta forma:
library(plyr)

ddply(PlantGrowth, "group", summarize,


avg = mean(weight))

Esse recurso ainda é usado atualmente, mas foi em grande parte


substituído por uma versão do pacote orientada a data frames, daí o d de
dplyr (pronuncia-se d-plier):
library(dplyr)
PlantGrowth %>%
group_by(group) %>%
summarize(avg = mean(weight))

No entanto, na verdade poderíamos ter retornado um data frame com


outras funções muito antigas:
> aggregate(weight ~ group, PlantGrowth, mean)
group weight
1 ctrl 5.032
2 trt1 4.661
3 trt2 5.526

É uma ótima função, não é mesmo? Ela é muito antiga! Você ainda a verá
sendo usada. E por que não veria? Ao entendê-la, você verá que ela é
elegante e faz o que é necessário, ainda que só aplique uma única função.
Contudo, a tendência contínua de uso de um framework Tidyverse
uni cado, mais fácil de ler e possivelmente de aprender, signi ca que os
métodos antigos estão sendo colocados em segundo plano.
Essas funções existem desde os primórdios do R e intuitivamente re etem
o que os estatísticos fazem o tempo todo. Eles dividem dados em blocos,
de nidos por alguma propriedade (linhas, colunas, variáveis categóricas,
objetos), depois aplicam algum tipo de ação (diagramação, teste de
hipóteses, modelagem etc.) e, em seguida, combinam a saída de alguma
forma (usando um data frame, uma lista etc.). O processo costuma ser
chamado de dividir-aplicar-combinar. Perceber que esse processo se repetia
começou a deixar claro para a comunidade como eles deviam considerar
os dados e também como organizá-los. Foi dessa noção que nasceu a
ideia de dados “organizados” (tidy data).6
Como último exemplo das iterações, talvez você conheça a função map()
do Python. Uma função análoga pode ser encontrada no pacote purrr do
Tidyverse. Ela será conveniente se você quiser iterar pelas listas ou
elementos de um vetor, mas não faz parte do escopo deste livro.

Considerações nais
Em Python, geralmente ouvimos falar sobre a maneira Python de fazer as
coisas (“Pythônico”). Isso signi ca a sintaxe Python apropriada e o
melhor método de executar uma ação especí ca. Não há algo assim em R;
existem muitas maneiras de se fazer a mesma coisa e as pessoas usam
toda essa variedade! Além disso, elas costumam combinar dialetos.
Embora alguns dialetos sejam mais fáceis de ler do que outros, essa
hibridização pode di cultar o aprendizado da linguagem.
E também há os constantes ajustes de um Tidyverse em expansão.
Funções são marcadas como experimentais, em suspensão, em maturação,
estáveis, em questionamento, substituídas e arquivadas. Alie isso aos
relativamente descuidados padrões de gerenciamento de pacotes especí co
de projetos ou de uso de ambientes virtuais e podemos imaginar que haja
um nível cada vez maior de frustração.
O R celebrou o cialmente seu 20º aniversário em 2020, e suas raízes são
muito mais antigas. Mesmo assim, às vezes parece que ele está
vivenciando um crescimento repentino como o da adolescência. Está
tentando entender como cresceu tanto repentinamente e pode ser ao
mesmo tempo desajeitado e descolado. Combinar os diferentes dialetos
do R o ajudará a descobrir todo o seu potencial.

1 useR! é a conferência de R anual e também uma série de livros da editora Springer.


2 N.T.: Tech bro é alguém do sexo masculino que trabalha em tecnologia.
3 N.T.: Na internet, um mirror site é uma cópia exata de outro site.
4 Berliner (substantivo): em Berlim é um residente da cidade. Nos outros lugares é um saboroso
bolinho açucarado e recheado com creme.
5 Uma exposição detalhada das de nições de modelos não faz parte do escopo deste texto.
6 Se quiser ler mais sobre esse tópico, examine o artigo de Hadley Wickham (https://oreil.ly/LWXFa).
CAPÍTULO 3

Python para UseRs

Rick J. Scavetta
Bem-vindo, bravo useR, ao maravilhoso mundo do Pythonista! Para
muitos useRs, esse admirável mundo novo pode parecer mais variado – e,
portanto, mais inconsistente e confuso – do que o ambiente com o qual
eles estão acostumados em R. No entanto, não se preocupe com a
diversidade – celebre-a! Neste capítulo, vou ajudá-lo a navegar pela rica e
diversa selva Python, realçando várias rotas ( uxos de trabalho) que seus
colegas usuários de Python podem ter seguido e que talvez você decida
explorar posteriormente. Entretanto, tenha certeza de que acabará
encontrando o caminho que servirá melhor a você e ao seu ambiente de
trabalho; ele mudará com o tempo e pode não ser o descrito aqui. Como
deve ser feito em qualquer boa caminhada, use essa rota como guia e não
como um livro de regras.
Abordarei os aspectos básicos dos quatro elementos que mencionei na
introdução desta parte: funções, objetos, expressões lógicas e indexação.
Contudo, começarei respondendo a três perguntas:
Pergunta 1
Que versão e que build (distribuição) usar? Existem algumas versões e
builds diferentes de Python para escolhermos, ao contrário do que
acontece com o R.
Pergunta 2
Que ferramentas usar? A ampla variedade de IDEs, editores de texto e
notebooks, além das várias maneiras de implementar ambientes
virtuais, adiciona mais opções à seleção.
Pergunta 3
De que forma a linguagem Python se compara à linguagem R? Habituar-
se a um mundo baseado na OOP, com várias classes, métodos, funções e
palavras-chave apresenta outra barreira à compreensão.
Abordarei cada uma dessas perguntas individualmente. Meu objetivo é
deixá-lo su cientemente confortável para ler e escrever código Python de
modo a poder continuar sua jornada bilíngue nas partes III e IV. Não
pretendo fornecer uma introdução completa e profunda do uso de Python
para a ciência de dados. Se quiser algo assim, leia os livros da O’Reilly,
Python para Análise de Dados, de Wes McKinney, e Python Data Science
Handbook, de Jake VanderPlas; este capítulo o ajudará a apreciar ainda
mais esses livros.
Se estiver ansioso para começar a usar Python, você pode pular para a
seção sobre notebooks, “Notebooks”, na página 76, e visitar o Notebook
do Google Colab (https://oreil.ly/hLi6i) referente à lição sobre Python, ou
acessar o script deste capítulo no repositório do nosso livro no GitHub
(https://github.com/moderndatadesign/PyR4MDS).

Versões e builds
Embora existam algumas distribuições diferentes do R, a maioria dos
useRs usa o R básico (vanilla R) que pode ser obtido em r-project.org
(https://www.r-project.org).1 Para Python, existem pelo menos quatro
builds (também chamadas de distribuições) para escolher. Em cada caso,
você também deve considerar a versão do Python.
Em primeiro lugar, você notará que provavelmente já possui uma versão
do Python instalada no sistema. Em minha máquina, executando o
macOS Big Sur (v11.1), consigo ver a versão do Python usando o comando
de terminal a seguir:
$ python --version
Python 2.7.16

O interessante é que o macOS também vem com o python3:


$ python3 --version
Python 3.8.6
Essas são as instalações do Python que o macOS usa internamente; não é
preciso mexer nelas.
Em segundo lugar, há o vanilla Python – a versão mais simples e
diretamente da fonte do Python. Quando este texto foi escrito, a versão
corrente era a 3.9. A versão 2.x não é mais suportada, e você deve usar
a 3.x para futuros projetos de ciência de dados. Até ter certeza de que
todos os pacotes que usará são compatíveis com a versão mais recente, é
mais seguro utilizar a última atualização de menor porte, nesse caso a 3.8.
Na verdade, você pode ter várias versões menores em seu sistema.
Para instalar a versão especí ca que você deseja, visite o site do Python
(https://www.python.org) e siga as instruções na página de download.
A instalação varia dependendo do sistema. O guia o cial Python Setup
and Usage (https://oreil.ly/xZEhZ) é o recurso autorizado. Se você tiver
problemas na instalação, um bom ponto de partida é executar uma busca
literal na web (inclua em aspas duplas) da parte genérica da mensagem de
erro.
A Tabela 3.1 fornece outras fontes, mas advertimos que é melhor ir à fonte
principal.2
Tabela 3.1: Instalando o Python
Plataforma Site Alternativa
Linux python.org O Python 3 já vem instalado
macOS python.org Use brew install python3 no terminal
Windows python.org Instale o Python a partir da Windows Store

Em terceiro lugar, existem duas builds Conda comuns: Anaconda


(também conhecida como Conda) e Miniconda. O Conda oferece
gerenciamento de pacotes, de dependências e de ambiente para várias
linguagens de programação, incluindo Python e R, embora raramente seja
usado para o R. Essas builds open source incluem o Python, um conjunto
de pacotes útil para a ciência de dados, e uma coleção de IDEs (incluindo
o RStudio). Existe uma versão individual gratuita do Anaconda e várias
versões comerciais. Como o nome sugere, o Miniconda
(https://oreil.ly/oBi4M) é um instalador mínimo. Ele aparecerá novamente
na última parte do livro.
O site do Anaconda (https://oreil.ly/lQ6s6) tem instruções detalhadas para
a instalação. Você notará que o Anaconda pode não vir empacotado com
a última versão do Python. Por exemplo, quando este texto foi escrito, ele
vinha empacotado com o Python 3.8, e não com o 3.9. Essa é a razão para
preferirmos o vanilla Python 3.8, como mencionado anteriormente. O
Anaconda é uma build popular, mas, para o que queremos, usaremos o
vanilla Python a m de evitar os penduricalhos adicionais que nesse
momento só serviriam para nos distrair. Logo, não consideraremos essa
opção, porém citaremos algumas diferenças signi cativas quando
necessário para o caso de você preferir seguir essa rota.
Em quarto lugar, você pode decidir não usar uma instalação local do
Python e, em vez disso, usar a popular versão online fornecida pelos
Notebooks do Google Colab (https://oreil.ly/USncL).3 Existem outras
ferramentas de notebook online, mas não faz parte do escopo deste livro
detalhar todas. Os notebooks são semelhantes a documentos
R Markdown, porém baseados em JSON. Iremos abordá-los com mais
detalhes posteriormente.
É fácil deduzir que essa diversidade no estágio inicial pode resultar em
confusão em uma etapa posterior do processo quando surgirem
problemas especí cos da instalação. Seguindo adiante, presumirei que
você tem uma instalação do Python local ou baseada em nuvem pronta
para ser usada.

Ferramentas padrão
Como ocorre com o R, existem muitas maneiras de acessar o Python.
Alguns métodos comuns são o uso da linha de comando, IDEs, editores
de texto e notebooks. Para simpli car, não me concentrarei na execução
do Python na linha de comando. Se você conhece a execução de scripts na
linha de comando, esse é um território familiar. Se não conhece, cruzará
essa ponte quando a alcançar.
Os IDEs incluem o JupyterLab, o Spyder, o PyCharm e nosso apreciado
RStudio. Entre os IDEs nativos da nuvem temos o AWS Cloud9. Essas são
todas variações do mesmo tema, e pelo que sei normalmente não são as
prediletas dos Pythonistas, embora exista uma tendência para o uso de
ferramentas baseadas em nuvem. Pode parecer estranho que os IDEs não
sejam tão populares. Por que não usar um IDE se existir um ótimo
disponível? Acho que há duas respostas. Em primeiro lugar, nenhum IDE
conseguiu se posicionar como a opção predominante entre os Pythonistas
como ocorreu com o RStudio entre os useRs. Em segundo lugar, já que os
casos de uso de Python são tão variados, incluindo ele ser executado na
própria linha de comando, codi car com IDEs não era tão interessante
para muitos Pythonistas, principalmente se tivessem experiência em
codi cação e se sentissem confortáveis sem um IDE. Para mim, isso
reforça um pouco uma narrativa que diz que o Python é ao mesmo tempo
mais difícil e melhor do que o R. Desculpe, mas as duas assertivas estão
incorretas! Todavia, você pode car tentado a começar a usar o Python
com um IDE de aparência amigável. Aqui, defendemos a ideia de que os
editores de texto podem ser mais úteis a longo prazo. Voltaremos ao
RStudio na última parte do livro quando unirmos Python e R no mesmo
script. Por enquanto, tente resistir ao impulso de adotar um IDE como
padrão e procure desenvolvimentos em plataformas de nuvem que
possam guiar tendências futuras.
Os editores de texto são a ferramenta mais comum e aparentemente
preferida para a composição de scripts em Python puro. Existem vários
editores de texto fantásticos para escolhermos, com sua popularidade
crescendo e diminuindo ano após ano. Sublime
(https://www.sublimetext.com), Atom (https://atom.io), Visual Studio Code
(VS Code) (https://code.visualstudio.com) e até mesmo os clássicos editores
Vim (https://www.vim.org) e Emacs (https://oreil.ly/pGjVX), entre muitos
outros, são frequentemente usados. No entanto, o VS Code, um editor
open source desenvolvido e fortemente suportado pela Microsoft,
emergiu nos últimos anos como a opção preferencial. Como um mercado
de extensões, esse editor fornece suporte forte e fácil para várias
linguagens, incluindo Python e R.4 Logo, nos concentraremos no
VS Code. Seu primeiro exercício será obter e instalar o VS Code.
Na primeira vez que você abrir o VS Code, será saudado com a tela de
boas-vindas, onde poderá escolher seu tema (light, no nosso caso) e
instalar a extensão da linguagem Python imediatamente, como mostrado
na Figura 3.1.

Figura 3.1: Tela de boas-vindas do VS Code.


Quando você clicar no ícone de documento em branco no canto superior
esquerdo, será solicitado a abrir uma pasta ou clonar um repositório Git
(do GitHub, por exemplo), como mostrado na Figura 3.2. Se tiver clonado
o repositório do livro, use-o; caso contrário, use a pasta que quiser. É
como abrir um projeto no RStudio.
Você também verá os arquivos da pasta na barra lateral esquerda. Abra
ch03-py4r/py4r.py, como na Figura 3.3.
Figura 3.2: Abrindo uma pasta como espaço de trabalho.
Figura 3.3: Uma pasta aberta, o script Python e o guia da extensão.
Devido à extensão do arquivo, o VS Code detectou automaticamente que
você deseja usar um interpretador Python para esse documento. O
VS Code, como muitos outros editores de texto, pode executar código
diretamente a partir do documento se souber que interpretador usar. Você
será saudado com a página de boas-vindas da extensão e o rodapé azul
exibirá a versão do Python que está sendo usada. Lembre-se de que você
pode ter muitas versões instaladas em seu sistema. Aqui, estou usando
a v3.8.6.
O primeiro item da página de boas-vindas da extensão é “Create a Jupyter
Notebook” (Criar Jupyter Notebook). Chegaremos a ele em breve; por
enquanto, é preciso ressaltar que podemos usar o VS Code tanto para
scripts quanto para notebooks. Observe também que o primeiro bullet
point desse item informa que para abrir um notebook temos de executar
um comando na Paleta de Comandos (Command Palette). Você pode
acessar a Paleta de Comandos com o atalho de teclado Shift-Command-P
em um Mac (ou Shift + Ctrl + P em um PC).5 Volte ao nosso script e abra
a Paleta de Comandos. É nesse local que você executará todos os
comandos para facilitar sua vida como Pythonista. A Paleta de Comandos
é um recurso relativamente novo no RStudio, mas já há algum tempo é a
maneira padrão para a navegação em editores de texto. Cada extensão que
você instalar a partir do marketplace adicionará mais comandos que
poderão ser acessados aqui. Nosso primeiro comando será “Create New
Integrated Terminal (in Active Workspace)” (Figura 3.4).

Figura 3.4: Acessando a Paleta de Comandos.


Você pode obtê-lo começando a digitar o comando e depois deixando que
o preenchimento automático faça sua mágica. Certi que-se de selecionar
a opção “(in Active Workspace)”. Lembre-se, esse projeto é semelhante a
um do RStudio, logo queremos permanecer em nosso espaço de trabalho
ativo (Active Workspace).
Até agora escolhemos um editor de texto e temos nosso primeiro script
Python (vazio). Parece que estamos prontos para prosseguir – mas não é
bem assim! Precisamos abordar dois fatores cruciais que surgirão sempre
que você quiser criar um novo projeto Python:
• Ambiente (de desenvolvimento) virtual
• Instalação de pacotes
Ambientes virtuais
A maioria dos useRs está acostumada a usar projetos do RStudio, que
mantêm o diretório de trabalho vinculado ao diretório do projeto. Esses
projetos são convenientes porque não precisamos embutir caminhos no
código e somos encorajados a manter todos os dados e scripts no mesmo
diretório. Você já estará usando esse recurso quando abrir uma pasta
inteira em um espaço de trabalho do VS Code.
Uma desvantagem importante dos projetos do RStudio e dos espaços de
trabalho do VS Code é que eles não fornecem ambientes de
desenvolvimento portáteis e reproduzíveis! Muitos useRs têm uma única
instalação global de cada pacote (consulte .libPaths()) e raramente
especi cam a versão do R.
Sejamos francos, caro useR: se você ainda não teve problemas com
con itos de versões de pacotes, em algum momento terá. Você atualizou
um pacote globalmente e agora um script antigo tornou-se obsoleto
porque está chamando uma função descontinuada, porque está usando
os argumentos padrão de uma função que foi alterada ou por várias
outras razões relacionadas a con itos de versões de pacotes. Essa é uma
ocorrência muito comum em R e é uma prática realmente desastrosa em
casos de trabalhos demorados ou colaborativos. Houve muitas tentativas
de implementação de algum tipo de ambiente de desenvolvimento
controlado em R com o passar dos anos. A mais recente, que esperamos
que seja a solução que nalmente permanecerá, é o renv. Se você não tem
acompanhado os desenvolvimentos, visite o site do pacote do RStudio
(https://oreil.ly/xGVcr).
Há muito tempo os Pythonistas usam ambientes virtuais para manter a
compatibilidade futura de seus projetos, um sinal da abordagem
programming- rst das origens do Python. Aqui, um ambiente virtual é
simplesmente um subdiretório oculto dentro de uma pasta de projeto,
chamado, por exemplo, de .venv. O . (ponto) é o que o torna virtual. Você
tem muitos arquivos e diretórios ocultos em todo o seu computador, e em
sua grande maioria eles estão ocultos porque não há razão para mexer
neles. Dentro de .venv você encontrará os pacotes usados nesse projeto
especí co, e informações sobre que build Python esse projeto usa. Já que
agora cada projeto contém um ambiente virtual com todos os pacotes e as
versões apropriadas deles (!), você pode ter certeza de que o projeto
continuará funcionando inde nidamente, enquanto esse ambiente virtual
existir. Podemos prever os possíveis problemas de dependência entre
máquinas diferentes como na Figura 3.5, que realça o benefício de haver
uma única “fonte de verdade” no que diz respeito às versões dos pacotes.

Figura 3.5: Fontes de con ito em ambientes Python.


Como para tudo em Python, existem muitas maneiras de criar um
ambiente virtual. Podemos usar os pacotes venv ou virtualenv. Se você
estiver usando o Anaconda, empregará a alternativa Conda, que não
abordaremos aqui. Há algumas diferenças sutis entre o venv e o
virtualenv, mas nesse ponto da história elas são irrelevantes; usaremos o
venv. Em nossa nova janela de terminal, execute um dos comandos da
Tabela 3.2 dependendo de sua plataforma, como z na Figura 3.6.
Tabela 3.2: Criando (e ativando) um ambiente virtual com o venv
Plataforma Criar Ativar (a ativação automática do VS Code é preferencial)
macOS X e Linux python3 -m venv .venv source .venv/bin/activate
Windows py -3 -m venv .venv .venv\scripts\activate

Figura 3.6: Criando um novo ambiente virtual em nosso espaço de trabalho


ativo.
Após criar o ambiente virtual, você deve ativá-lo. Os comandos do
terminal para essa operação foram fornecidos na Tabela 3.2, porém é mais
conveniente deixar o VS Code fazer o que ele sabe fazer melhor. Ele
detectará automaticamente seu novo ambiente virtual e perguntará se
você deseja ativá-lo (consulte a Figura 3.7). Aceite! Observe que o
interpretador Python no canto inferior esquerdo também mencionará
explicitamente (.venv): venv e em sua pasta aberta você terá duas
pastas ocultas, .venv e .vscode (Figura 3.7).
Veremos a instalação de pacotes em breve; por enquanto, tente executar
nosso primeiro comando, “Hello, world!”. Digite o comando a seguir em
seu script:
#%%
print('Hello, world!')

Figura 3.7: Um ambiente virtual ativo.


Na verdade, print não é necessário, mas ele torna o que estamos
tentando fazer explícito. Parece muito com uma função R simples, certo?
A linha #%% também não é necessária, mas é um excelente recurso da
extensão Python no VS Code e é altamente recomendável! Digitar #%% nos
permite dividir um script longo em blocos executáveis. É semelhante aos
blocos do R Markdown, porém muito mais fácil e usado em scripts
Python simples. Para executar o comando, pressione Shift-Enter ou clique
em Run Cell, como mostrado na Figura 3.8.
Você será solicitado imediatamente a instalar o ipyKernel. Faça isso e
obterá a saída no novo painel superior direito, que pode ser visto na
Figura 3.9.
Pronto, agora chegamos aonde queríamos. Parece muito trabalho, mas
uma vez que você o executar várias vezes desenvolverá uma rotina e se
habituará!
Figura 3.8: Executando seu primeiro código em Python.
Figura 3.9: Visualizando a saída do comando no visualizador interativo do
Python.

Instalação de pacotes
Até agora instalamos uma versão do Python e acessamos um espaço de
trabalho, como em um projeto R, de dentro do VS Code. Também
criamos um ambiente virtual, que já estamos prontos para preencher com
nossos pacotes de ciência de dados favoritos. Se você seguiu a rota do
Conda, usou comandos diferentes, mas também está pronto para
prosseguir com os pacotes de ciência de dados mais comuns pré-
instalados. Parece bom, mas você pode descobrir que, quando tiver de
colaborar com outros desenvolvedores Python, por exemplo, engenheiros
de dados ou administradores de sistemas, provavelmente eles não estarão
usando o Anaconda. Achamos que também é possível chegar à essência
do Python sem todos os penduricalhos que o Anaconda fornece. Logo,
seguimos a rota vanilla.
Antes de examinar os pacotes, aprenderemos a terminologia necessária.
Em R, uma biblioteca é uma coleção de pacotes individuais. O mesmo
ocorre em Python, mas o uso de biblioteca e pacote não é tão rígido. Por
exemplo, o pandas, o pacote que fornece a classe de objetos
pd.DataFrame, é chamado tanto de biblioteca quanto de pacote no
próprio site. Essa mistura dos termos é comum entre os Pythonistas; logo,
se você for rigoroso com nomes, não deixe que isso o confunda ou
incomode. No entanto, no caso dos módulos é importante prestar atenção.
Um pacote é uma coleção de módulos. É útil saber disso já que podemos
carregar um pacote inteiro ou apenas um módulo especí co. Portanto,
geralmente: biblioteca > pacote > módulo.
Em R, você instalaria os pacotes a partir do CRAN com a função
install.packages() de dentro do próprio R. Em Python, existem dois
equivalentes ao CRAN: o PyPI (Python Package Installer, ou Instalador de
Pacotes Python), no caso de uso do vanilla Python, e o Conda, para
quando for usado o Anaconda ou o Miniconda (também veremos
posteriormente como instalar pacotes com o Google Colab diretamente
no notebook online). Para instalar pacotes a partir do PyPI usando o
vanilla Python, você terá de executar um comando no terminal. Lembre-
se de que deixamos aberta no VS Code nossa janela de terminal ativa.
Execute o comando pip install matplotlib no terminal para instalar
o pacote matplotlib em seu ambiente virtual, como mostrado na
Figura 3.10. pip é o instalador de pacotes para Python, que tem várias
versões.
Os pacotes que você instalará em praticamente qualquer ambiente virtual
são o NumPy, o pandas, o matplotlib, o seaborn e o SciPy. Você não
precisará instalar todos sempre, já que as dependências dos pacotes se
encarregarão disso. Se eles já estiverem instalados, o pip dará essa
informação e não instalará nada. As mensagens de erro mais comuns que
você verá aparecerão quando a versão do seu pacote for incompatível com
sua versão do Python. Para resolver isso, você pode usar um kernel (o
back-end de execução) Python diferente em seu projeto ou especi car a
versão exata do pacote que deseja instalar. Como no R, só é necessário
instalar o pacote uma vez, mas você precisará importá-lo (isto é, inicializá-
lo) sempre que ativar seu ambiente. Parece conveniente a instalação do
pacote ocorrer no terminal, separada da importação no script. Você já
deve ter visto muitas funções install.packages() perdidas em
scripts R, o que incomoda um pouco.

Figura 3.10: Instalando um pacote em um ambiente virtual usando a linha de


comando.
Existem mais dois pontos importantes que desejo mencionar. Em
primeiro lugar, veri que todos os pacotes instalados em seu ambiente, e
suas versões, no terminal com:
$ pip freeze

Em segundo lugar, envie essa saída para um arquivo chamado


requirements.txt executando o comando a seguir:
$ pip freeze > requirements.txt
Agora outros usuários poderão usar requirements.txt para instalar os
pacotes necessários usando o comando:
$ pip install -r requirements.txt
Notebooks
Se você seguiu o tutorial até aqui, está pronto para passar para a terceira
pergunta e começar a explorar a linguagem Python. No entanto, será útil
examinarmos os notebooks, então, continue lendo. Se teve problemas para
instalar o Python localmente, não se preocupe! É nos Jupyter Notebooks
que você poderá tomar fôlego, colocar seus problemas de instalação de
lado e começar de novo.
Os Jupyter Notebooks foram construídos tendo como base o IPython,
que surgiu em 2001. A plataforma Jupyter, que é a abreviação de JUlia,
PYThon e R, agora acomoda várias linguagens de programação e pode ser
usada no IDE JupyterLab ou em notebooks simples. Os notebooks
permitem escrever texto com o uso de marcação (markdown), adicionar
blocos de código e ver a saída inline. Lembram muito o R Markdown!
Bem, sim e não. Em segundo plano, um R Markdown é um arquivo de
texto simples que é renderizado como HTML, DOC ou PDF. Os
notebooks são exclusivamente HTML baseado em JSON e podem
manipular nativamente componentes interativos. Para os useRs, seria
como um R Markdown interativo com um runtime shiny por padrão. Isso
signi ca que não compomos um notebook como um arquivo de texto
simples, o que é uma diferença importante na consideração do potencial
de edição.
Geralmente a codi cação em Python é composta puramente de
notebooks. Por exemplo, se você se aventurar a usar plataformas na
nuvem que operem com big data para machine learning (ML), como o
AWS SageMaker, o Google AI Platform ou o Azure Machine Learning
Studio, começará com os notebooks. Como já vimos, eles são suportados
pelo VS Code. Outras versões online incluem as competições do Kaggle e
os Jupyter Notebooks publicados. Ainda outra variedade de notebooks
online pode ser encontrada no serviço Google Colab (consulte a
Figura 3.11). Ela permite produzir e distribuir notebooks online com um
back-end Python e é o que usaremos para explorar os notebooks.
Para se familiarizar com o trabalho em notebooks, use esse tutorial online
do Jupyter (https://jupyter.org/try). Apenas clique no painel Notebook
Basics (https://oreil.ly/QXfL6) e preste muita atenção nos atalhos do
teclado.
Abra o notebook do Google Colab relacionado a este capítulo
(https://oreil.ly/UVyP9) para acompanhar.

Figura 3.11: Exemplos para conhecer os notebooks Python com o uso do


Google Colab.

De que forma a linguagem Python se compara à linguagem R?


A essa altura você já deve ter optado por um dos dois caminhos possíveis.
Se instalou o Python localmente, deve ter:
1. Um diretório de projeto no qual armazenará seus arquivos de dados e
scripts.
2. Um ambiente virtual de nido dentro desse diretório.
3. Os pacotes típicos de ciência de dados instalados nesse ambiente
virtual.
Se decidiu seguir a rota do Google Colab, deve ter acessado o notebook
deste capítulo.
Agora é hora de iniciarmos nosso projeto importando os pacotes que
usaremos. Aqui, veremos novamente que existem várias maneiras de fazer
isso, mas a maioria delas é padrão. Vejamos então. No repositório do livro
(https://github.com/moderndatadesign/PyR4MDS), você encontrará um
script prático com os comandos descritos a seguir, mas também é possível
acompanhar no notebook do Google Colab.
Ao percorrer esses comandos, introduzirei novos termos – palavras-chave,
métodos e atributos – e discutirei o que eles representam no contexto do
Python.
Em primeiro lugar, podemos importar um pacote inteiro:
import math # Funções que vão além da matemática básica

Isso nos permitirá usar funções do pacote math. O pacote math já está
instalado, logo não precisamos usar pip, mas precisamos importar o
pacote.
Essa é a primeira vez que encontramos um aspecto comum e importante
da linguagem Python: as palavras-chave, que se comportam como as
palavras reservadas do R, mas são mais numerosas. Atualmente existem
35 palavras-chave em Python que podem ser divididas em grupos
distintos (consulte o Apêndice). Aqui import é uma palavra-chave de
importação. Como um useR acostumado com a programação funcional,
você usaria library(math) em R. Logo, poderia considerar as palavras-
chave como atalhos para as funções, o que em muitos casos elas são. Seria
semelhante aos operadores em R (por exemplo, <-, +, ==, & etc.), que na
verdade são apenas atalhos para as funções. Eles não são escritos no
formato clássico das funções, mas poderiam ser.
Resumindo, as palavras-chave são palavras reservadas que têm
signi cados muito especí cos. Nesse exemplo, import representa uma
função para a importação de todas as funções do pacote math. Muitas
palavras-chave agem dessa forma, mas não todas. Veremos alguns
exemplos em breve.
No entanto, agora que temos as funções do pacote math, tentaremos isto:
math.log(8, 2)

Aqui vemos que o . (ponto) tem um signi cado especí co: dentro do
pacote math, acesse a função log(). Os dois argumentos são o dígito e a
base. Logo, é possível ver por que o Tidyverse R tende a usar _ em vez da
notação com . (ponto) e por que a predominância de um . (ponto) sem
signi cado em muitas funções R frustra os usuários que vêm de
linguagens baseadas na OOP.
Em segundo lugar, podemos importar um pacote inteiro e fornecer para
ele um alias especí co, tipicamente padronizado:
import pandas as pd # Para data frame e manipulação
import numpy as np # Array e processamento numérico
import seaborn as sns # Diagramação de alto nível

Essa é a nossa segunda palavra-chave, as. Observe que ela não está
agindo como substituta de uma função a menos que nos lembremos de
que <- também é uma função. Se deixarmos nossa imaginação voar,
poderíamos considerar como sendo igual ao seguinte em R:
dp <- library(dplyr) # sem sentido, apenas uma ideia

Os useRs não fariam isso, mas esse é o comando análogo que chega mais
próximo do que eles fariam.6 A palavra-chave as é sempre usada com
import para fornecer um alias conveniente para o acesso às funções de
um pacote ou módulo.7 Logo, é uma maneira explícita de chamar a
função exata que queremos. Execute esta função para importar o
conjunto de dados para um trabalho futuro:
plant_growth = pd.read_csv('ch03-py4r/data/plant_growth.csv')

Notou o . novamente? O comando anterior é equivalente a este comando


em R:
plant_growth <- readr::read_csv("ch03-py4r/data/plant_growth.csv")

Em terceiro lugar, podemos importar um módulo especí co de um pacote:


from scipy import stats # por exemplo, para uso da função de teste t
Essa é nossa terceira palavra-chave, from. Ela permite acessar o pacote
SciPy e importar apenas o módulo stats.
Em quarto lugar, podemos importar um módulo especí co de um pacote,
fornecendo também um alias próprio tipicamente padronizado:
import matplotlib.pyplot as plt # Diagramação de baixo nível
import statsmodels.api as sm # Modelagem, por exemplo, ANOVA

Para concluir, também podemos importar apenas uma função especí ca


de um pacote:
from statsmodels.formula.api import ols # Para a regressão dos mínimos
quadrados ordinários

Importe um conjunto de dados


Vimos como importar um conjunto de dados usando uma função do
pacote pandas:
plant_growth = pd.read_csv('ch03-py4r/data/plant_growth.csv')

Examine os dados
É sempre boa prática examinarmos nossos dados antes de começar a
trabalhar com eles. Em R usaríamos algo como summary() e str(), ou
glimpse() se tivermos inicializado o dplyr. Vejamos como isso funciona
em Python.
plant_growth.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 30 entries, 0 to 29
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 weight 30 non-null float64
1 group 30 non-null object
dtypes: float64(1), object(1)
memory usage: 608.0+ bytes
plant_growth.describe()

weight
count 30.000000
mean 5.073000
std 0.701192
min 3.590000
25% 4.550000
50% 5.155000
75% 5.530000
max 6.310000
plant_growth.head()

weight group
0 4.17 ctrl
1 5.58 ctrl
2 5.18 ctrl
3 6.11 ctrl
4 4.50 ctrl

O que é isso?? É a primeira vez que encontramos essa nomenclatura, e


temos a sempre presente notação de ponto novamente! As funções
info(), describe() e head() são métodos do objeto plant_growth. Um
método é uma função que é chamada por um objeto. Como ocorre com
as outras funções, também podemos fornecer argumentos especí cos,
mas nesses casos usaremos o padrão.
Observe principalmente a saída do método info(). Aqui, estamos vendo
pela primeira vez que a indexação começa em 0 no Python, como ocorre
com muitas linguagens de programação – e por que não seria assim? Esse
é um aspecto importante da programação Pythônica. Veremos as
consequências disso posteriormente quando chegarmos à indexação.
A saída de .info() também mostra que temos um pd.DataFrame.
Examinaremos as diferentes classes de objetos em breve.
Que tal examinarmos a forma (isto é, as dimensões) e os nomes de
colunas do objeto plant_growth?
plant_growth.shape

(30, 2)
plant_growth.columns

Index(['weight', 'group'], dtype='object')

Nesse caso, estamos chamando atributos do objeto, logo eles não


receberam parênteses. Aqui vemos que qualquer objeto pode chamar
métodos e atributos permitidos, de acordo com sua classe. Você já deve
saber disso pelo que ocorre em R, quando a classe de um objeto permite
que este seja usado em funções especí cas nas quais existam métodos
disponíveis para ele. Em segundo plano, a mesma mágica está ocorrendo.
O R usa o padrão “primeiro as funções, depois a OOP”. É o que ocorre,
mas não precisamos nos preocupar tanto com isso na programação
funcional. Para você ter uma ideia de como funciona em R, considere um
conjunto de dados interno, sunspots. Ele é um objeto da classe ts (isto é,
time series, ou série temporal):
# em R
class(sunspots)
[1] "ts"
plot(sunspots)
# não exibido
Você pode encontrar os métodos da função plot usando:
# em R
methods(plot)

Você verá o método plot.ts(), que é o método que é realmente chamado


quando fornecemos um objeto da classe ts para a função plot().
Para concluir, talvez você não consiga ver esse conjunto de dados como é
possível ver com a opção de visualização do RStudio. Não se preocupe!
Você pode clicar no ícone de tabela do kernel interativo do Python e ver
tudo que existe em seu ambiente. Se você clicar no data frame, ele abrirá
uma visualização para você examiná-lo.

Estruturas de dados e estatística descritiva


Agora que sabemos lidar com os métodos e atributos, examinaremos
como você geraria alguns dados de estatística descritiva. Um
pd.DataFrame é muito semelhante a um data.frame ou tbl do R. É uma
tabela bidimensional na qual cada coluna é uma Series, da mesma
forma como as colunas são vetores de mesmo tamanho nos data frames
do R. Como ocorre com o pd.DataFrame, uma Series também tem
métodos e atributos. Lembre-se de que a coluna group é categórica. Por
enquanto, este comando deve fazer sentido para você:
plant_growth['group'].value_counts()
trt2 10
trt1 10
ctrl 10
Name: group, dtype: int64

Você deve conhecer os colchetes ([]) já que são usados no R; eles fazem a
indexação de acordo com o nome de uma coluna. Em seguida, o . pega
essa coluna individual e chama um método, .value_counts(), que nesse
caso conta o número de observações para cada valor.
E quanto a isto:
np.mean(plant_growth['weight'])

np está dizendo que usaremos uma função do pacote NumPy que


importamos anteriormente. Dentro dessa função, forneceremos valores
numéricos, a weight Series do data frame plant_growth.
Vejamos agora a síntese estatística. Consegue imaginar o que este método
fará?
# síntese estatística
plant_growth.groupby(['group']).describe()

Como a função group_by() do pacote dplyr, o método groupby()


permitirá que métodos downstream sejam aplicados em cada
subconjunto de acordo com uma variável categórica, nesse caso group
Series. O método describe() fornecerá uma suíte de síntese estatística
para cada subconjunto.
Esta versão é mais especí ca:
plant_growth.groupby(['group']).agg({'weight':['mean','std']})

É fácil perceber que o método .agg() signi ca agregação. As funções de


agregação (normalmente) retornam um único valor, e em R o
especi caríamos com o uso da função summarise().
A entrada do método .agg(), {'weight':['mean','std']}, é um
dicionário (classe dict). Ele pode ser considerado um par chave-valor,
de nido aqui com o uso de {}:
{'weight':['mean','std']}

Também poderíamos ter usado a função dict() com a mesma nalidade:


dict([('weight', ['mean','std'])])
Os dicionários são objetos de armazenamento de dados, fazem parte do
vanilla Python padrão e, como vemos aqui, são usados como argumentos
de métodos e funções. Isso é semelhante a como as listas do R são usadas
tanto para armazenamento de dados quanto como uma lista de
argumentos em circunstâncias especí cas. No entanto, um dicionário
seria mais como um array associativo, já que a indexação só ocorre pela
chave e não pelo número. Eu chegaria até mesmo a dizer que um
dicionário se parece mais com um ambiente do R, já que contém muitos
objetos, mas não há indexação, porém isso poderia ser um exagero.
Iremos nos aprofundar um pouco mais. Os comandos a seguir produzem
a mesma saída, mas em formatos diferentes!
# Produz Series do Pandas
plant_growth.groupby('group')['weight'].mean()
# Produz data frame do Pandas
plant_growth.groupby('group')[['weight']].mean()
Notou o símbolo [[]] versus []? Isso lembra uma diferença que
encontramos no R quando trabalhamos com data frames que não são
tibbles.

Estruturas de dados: de volta ao básico


Já vimos três tipos de objetos de armazenamento de dados comuns em
Python, pd.DataFrame, pandas Series e dict. Só dict é do vanilla
Python; logo, ante de prosseguirmos, quero examinar algumas das outras
estruturas básicas: as listas, as tuplas, e os arrays do NumPy. Estou
introduzindo-as com um atraso maior do que era de se esperar; isso
ocorreu porque eu queria começar com os data frames, que são intuitivos
e usados com frequência. Portanto, temos de nos certi car de abordar o
básico antes de concluir:
Em primeiro lugar, como em R, você verá quatro tipos de dados
principais em Python:
Tipo Nome Exemplo
bool Binário True e False
int Números inteiros 7,9,2,-4
float Números reais 3,14, 2,78, 6,45
str String Todos os caracteres alfanuméricos e especiais

Em seguida, você encontrará as listas, que são objetos unidimensionais.


Ao contrário do que ocorre nos vetores do R, cada elemento pode ser um
objeto diferente, como outra lista unidimensional. Aqui estão duas listas
simples:
cities = ['Munich', 'Paris', 'Amsterdam']
dist = [584, 1054, 653]

Observe que os colchetes ([]) de nem uma lista. Na verdade, já vimos


isso quando de nimos o dict anterior:
{'weight':['mean','std']}

Tanto [] quanto {} são válidos individualmente em Python e se


comportam de maneira diferente de como se comportam no R. No
entanto, lembre-se, usamos [] anteriormente para indexar o data frame, o
que é muito semelhante ao que ocorre no R.
plant_growth['weight']
Para concluir, temos as tuplas, que são como listas, exceto por serem
imutáveis, isto é, inalteráveis. Elas são de nidas com (), como em:
('Munich', 'Paris', 'Amsterdam')

As tuplas são muito usadas quando uma função retorna vários valores.
Como exemplo, a função divmod() retorna o resultado de uma divisão de
inteiros e o resto:
>>> divmod(10, 3)
(3, 1)

O resultado é uma tupla, mas podemos desempacotá-la e atribuir cada


saída a um objeto separado:
int, mod = divmod(10, 3)

Isso é muito conveniente na de nição de funções personalizadas. O


equivalente em R seria salvar a saída em uma lista.
UseRs astutos podem estar familiarizados com o operador de atribuição
múltipla %<-%, introduzido pelo pacote zeallot e popularizado pelo
framework Keras.
A última estrutura de dados que desejo mencionar é o array do NumPy.
Ele é muito parecido com uma lista unidimensional, mas permite a
vetorização, entre outras coisas. Por exemplo:
# Uma ilsta de distâncias
>>> dist
[584, 1054, 653]
# Alguma função de transformação
>>> dist * 2
[584, 1054, 653, 584, 1054, 653]

Isso é muito diferente do que um useR esperaria. Se estivéssemos


trabalhando em um array do NumPy, voltaríamos a um território
familiar:
# Cria um array do numpy
>>> dist_array = np.array(dist)
>>> dist_array * 2
array([1168, 2108, 1306])

Indexação e expressões lógicas


Agora que temos vários objetos, aprenderemos a indexá-los. Já vimos que
podemos usar [] e até mesmo [[]] como em R, mas existem algumas
diferenças interessantes. Lembre-se de que a indexação sempre começa
com 0 em Python! Além disso, observe que um dos operadores mais
comuns em R, :, faz uma reaparição em Python, mas de uma forma um
pouco diferente. Aqui ele é [início:fim]:
>>> dist_array
array([ 584, 1054, 653])
>>> dist_array[:2]
array([ 584, 1054])
>>> dist_array[1:]
array([1054, 653])

O operador : não precisa dos lados esquerdo e direito. Se um lado estiver


vazio, o índice começará ou prosseguirá até o m. O início é inclusivo e o
m, se especi cado, será exclusivo. Logo, :2 usa o índice 0 e 1, e 1: usa o
índice 1 até o último elemento, que não foi especi cado e, portanto, será
incluído.
Para data frames bidimensionais, temos os métodos do pandas .iloc
(“index location”, localização por índice) e .loc (“location”, localização):
# Linhas: 0ª e 2ª
>>> plant_growth.iloc[[0,2]]
weight group
0 4.17 ctrl
2 5.18 ctrl

# Linhas: 0ª a 5ª, exclusive


# Cols: 1ª
>>> plant_growth.iloc[:5, 0]
0 4.17
1 5.58
2 5.18
3 6.11
4 4.50

Para .loc(), podemos introduzir expressões lógicas, isto é, combinações


de operadores relacionais e lógicos para fazer e combinar perguntas
True/False.
>>> plant_growth.loc[(plant_growth.weight <= 4)]
weight group
13 3.59 trt1
15 3.83 trt1

Para ver mais detalhes sobre a indexação e as expressões lógicas, consulte


as notas do Apêndice.

Plotagem
Examinaremos algumas visualizações de dados de peso (weight) descrito
por group. Já que o pacote ggplot2 do R será recomendado para
visualizações de dados elegantes e exíveis na Parte III, deixei de fora os
diagramas dos comandos a seguir. No entanto, é útil vermos a abordagem
do seaborn. Aqui temos um diagrama de caixa:
sns.boxplot(x='group', y='weight', data=plant_growth)
plt.show()
# não exibido

Apenas os pontos:
sns.catplot(x="group", y="weight", data=plant_growth)
plt.show()

E apenas a média e seus desvios-padrão:


sns.catplot(x="group", y="weight", data=plant_growth, kind="point")
plt.show()

Observe que estamos usado o pacote seaborn (alias sns) para


visualizações de dados e depois usando a função show() do matplotlib
para exibir a visualização na tela.

Estatística inferencial
Neste conjunto de dados, temos uma de nição especial por haver três
grupos e estarmos interessados em duas comparações especí cas de dois
grupos. Podemos fazer isso estabelecendo um modelo linear:
# alinha um modelo linear
# especifica o modelo
model = ols("weight ~ group", plant_growth)

# alinha o modelo
results = model.fit()

Podemos obter os coe cientes do modelo diretamente:


# extrai os coeficientes
results.params.Intercept
results.params["group[T.trt1]"]
results.params["group[T.trt2]"]

Para concluir, examinaremos um resumo de nosso modelo:


# Explora os resultados do modelo
results.summary()

Concluiremos usando um teste estatístico típico para esse tipo de dado:


um ANOVA unidimensional. Repare que estamos usando nosso modelo,
results, que alinhamos anteriormente:
# ANOVA
# calcula a anova (análise de variância)
aov_table = sm.stats.anova_lm(results, typ=2)

# explora os resultados da anova


aov_table

Se quisermos fazer todas as comparações entre pares, podemos recorrer ao


teste post hoc HSD (honestly signi cant di erences, diferenças
honestamente signi cativas) de Tukey:
from statsmodels.stats.multicomp import pairwise_tukeyhsd
print(pairwise_tukeyhsd(plant_growth['weight'], plant_growth['group']))

Nesse caso, estamos começando com a biblioteca statsmodel, acessamos o


pacote stats package e seu módulo multicomp, e extraímos daí apenas a
função especí ca pairwise_tukeyhsd() para a importarmos. Na segunda
linha, executamos a função com uma variável contínua como primeiro
argumento e a variável de agrupamento como segundo argumento.

Considerações nais
Em R, houve uma convergência para práticas e uxos de trabalho comuns
desde aproximadamente 2016. Em Python, existe bem mais diversidade
para abraçarmos desde que começamos. Essa diversidade pode parecer
assustadora, mas é apenas um re exo do histórico da origem do Python e
dos casos de uso do mundo real.
Se você é um useR acostumado com o universo da programação
funcional, entender os métodos da OOP também pode parecer
assustador, mas, quando superar esse desa o, você poderá começar a
explorar o poder do Python onde ele realmente brilha, que é o tópico da
Parte III.

1 Na verdade, raramente as outras builds são mencionadas em companhia apropriada.


2 Você precisará instalar o Homebrew se quiser seguir essa rota para o macOS.
3 Você precisará de uma conta do Google para acessar esse recurso gratuito.
4 Novamente, embora o R seja suportado, raramente os useRs trabalham no VS Code.
5 UseRs astutos podem ter notado que uma Paleta de Comandos, chamada com os mesmos
atalhos de teclado, foi adicionada ao RStudio v1.4 no m de 2020.
6 No entanto, lembre-se disso para quando começarmos a usar Python e R juntos porque veremos
algo muito semelhante.
7 Voltando a log(), em vez de math.log(), é mais provável que você use np.log(), já que essa
função aceita uma variedade mais ampla de tipos de entrada.
PARTE III

Bilinguismo II: O contexto moderno

Nesta parte, você colocará a mão na massa e conhecerá aplicações das


duas linguagens em um contexto moderno, relacionado ao ecossistema
open source e a uxos de trabalho úteis.
Essas são as duas dimensões que precisamos abranger para ter uma visão
coerente. Examinando ambas, você terá um cenário claro de quando e
onde usar qual linguagem, qual pacote open source e qual uxo de
trabalho.
Capítulo 4
Nesse capítulo, examinaremos como os diferentes formatos de dados
(isto é, imagem ou texto) são processados por diferentes pacotes e quais
são os melhores.
Capítulo 5
Esse capítulo abordará os uxos de trabalho modernos mais e cazes
(isto é, machine learning e visualização) que geram um trabalho
produtivo tanto em R quanto em Python.
CAPÍTULO 4

Contexto dos formatos de dados

Boyan Angelov
Neste capítulo examinaremos ferramentas em Python e R para a
importação e o processamento de dados em vários formatos.
Abordaremos uma seleção de pacotes, comparando-os e realçando as
propriedades que os tornam e cazes. No m dessa investigação, você
poderá selecionar os pacotes con antemente. Cada seção ilustra os
recursos da ferramenta com um estudo de caso pequeno e especí co,
baseado nas tarefas que um cientista de dados encontra diariamente. Se
você estiver fazendo a transição de seu trabalho de uma linguagem para
outra ou apenas quiser descobrir como começar a usar rapidamente
pacotes especí cos de um contexto e com boa manutenção, este capítulo o
guiará.
Antes de começarmos, lembre-se de que o ecossistema open source está
sempre mudando. Novos desenvolvimentos, como os modelos
transformer (https://oreil.ly/PLaGE) e a XAI (explainable arti cial
intelligence, inteligência arti cial explicável) (https://oreil.ly/0sRHV),
parecem surgir quase toda semana. Geralmente eles têm como objetivo
diminuir a curva de aprendizagem e aumentar a produtividade do
desenvolvedor. Essa explosão de diversidade também se aplica aos pacotes
relacionados, resultando em um uxo quase constante de ferramentas
novas e (esperamos) melhores. Se você tiver um problema muito
especí co, provavelmente haverá um pacote já disponível para a sua
situação; logo, não é preciso reinventar a roda. A seleção de ferramentas
pode ser complexa, mas ao mesmo tempo essa diversidade de opções
pode melhorar a qualidade e a velocidade de seu trabalho de ciência de
dados.
Pode parecer que a seleção de pacotes deste capítulo tem uma visão
limitada; portanto, é essencial esclarecer nossos critérios de seleção. Que
qualidades devemos procurar em uma boa ferramenta?
Deve ser open source.
Existe uma grande quantidade de ferramentas comerciais úteis
disponíveis, mas temos certeza de que as ferramentas open source
apresentam uma vantagem maior. Elas tendem a ser mais fáceis de
estender, também facilitam entender como funcionam seus mecanismos
internos e são mais populares.
Deve ter todos os recursos (feature-complete).
O pacote deve incluir um conjunto abrangente de funções que ajudem o
usuário a executar seu trabalho sem precisar recorrer a outras
ferramentas.
Deve ter boa manutenção.
Uma das desvantagens do uso de um OSS (open source software,
software open source) é que às vezes os pacotes têm um ciclo de vida
curto e sua manutenção é abandonada (o chamado “abandonware”).
Queremos usar pacotes que tenham manutenção ativa para podermos
ter certeza de que estarão atualizados.
Começaremos com uma de nição. O que é um formato de dados?
Existem várias respostas disponíveis (https://oreil.ly/M67LQ). Possíveis
candidatos seriam o tipo de dado, o formato de gravação e o formato de
arquivo. O tipo de dado está relacionado aos dados armazenados em
bancos de dados ou aos tipos das linguagens de programação (por
exemplo, integer, oat ou string). O formato de gravação é como os dados
são armazenados em uma mídia física, como um CD ou DVD. E, para
concluir, o que estamos procurando, o formato do arquivo, isto é, como as
informações são preparadas para ns de computação.
Dada essa de nição, você ainda pode estar se perguntando por que
dediquei um capítulo inteiro apenas aos formatos de arquivo. Você pode
ter sido exposto a eles em outro contexto, como ao salvar um conjunto de
slides do PowerPoint com uma extensão .ppt ou .pptx (e querer saber qual
é melhor). O problema aqui vai muito além da compatibilidade básica da
ferramenta. A maneira como as informações são armazenadas in uencia
todas as etapas posteriores do processo de ciência de dados. Por exemplo,
se nosso objetivo nal fosse executar uma análise avançada e as
informações estivessem armazenadas em um formato de texto,
precisaríamos prestar atenção em fatores como a codi cação dos
caracteres (um problema conhecido, principalmente em Python).1 Para
esses dados serem processados de maneira e caz, também precisariam
passar por várias etapas, como a tokenização (https://oreil.ly/ek4s9) e a
remoção de palavras vazias (stop words) (https://oreil.ly/zojPz).2 Essas
mesmas etapas não são aplicáveis a dados de imagem, ainda que
tivéssemos o mesmo objetivo nal em mente, por exemplo, a classi cação.
Nesse caso, outras técnicas de processamento são mais adequadas, como
o redimensionamento e o escalonamento. Essas diferenças nos pipelines
de processamento de dados são mostradas na Figura 4.1. Resumindo: o
formato dos dados é o fator mais signi cativo que in uencia o que
podemos ou não fazer com eles.

Usamos a palavra pipeline pela primeira vez nesse contexto; logo,


aproveitaremos a oportunidade para de ni-la. Você já deve ter ouvido
a expressão “os dados são o novo petróleo”. Essa expressão vai além
de uma simples estratégia de marketing e representa uma maneira
útil de considerar os dados. É surpreendente como há tantos
paralelos entre como o petróleo e os dados são processados. Podemos
visualizar isso da seguinte forma: os dados iniciais que a empresa
coleta chegam na sua forma mais bruta – provavelmente de uso
inicial limitado. Em seguida, eles passam por uma sequência de
etapas, chamada processamento de dados, antes de serem usados em
alguma aplicação (isto é, para o treinamento de um modelo de ML
ou a alimentação de um painel). No processamento de petróleo, isso
se chama re namento e enriquecimento – tornar os dados usáveis
para um m empresarial. Os pipelines transportam os diferentes
tipos de petróleo (bruto, re nado) pelo sistema até seu estado nal. O
mesmo termo pode ser usado na ciência e na engenharia de dados
para descrever a infraestrutura e a tecnologia necessárias para
processar e distribuir dados.

Figura 4.1: Diferença entre pipelines comuns de formatos de dados (o


sombreado mais claro indica as etapas compartilhadas entre os uxos de
trabalho).
A infraestrutura e o desempenho também precisam ser levados em
consideração no trabalho com um formato de dado especí co. Por
exemplo, para dados de imagem, você precisará de mais disponibilidade
de armazenamento. Para dados de séries temporais, talvez precise usar um
banco de dados especí co, como o In uxDB (https://oreil.ly/2crIr). E, para
concluir, em termos de desempenho, geralmente a classi cação de
imagens é resolvida com o uso de métodos de deep learning baseados em
CNNs (convolutional neural networks, redes neurais convolucionais), que
podem requerer uma GPU (graphics processing unit, unidade de
processamento grá co). Sem ela, o treinamento de modelos pode ser
muito lento e tornar-se um gargalo tanto para o seu trabalho de
desenvolvimento quanto para uma possível implantação de produção.
Agora que abordamos as razões para considerarmos cuidadosamente que
pacotes usar, examinaremos os formatos de dados possíveis. Essa visão
geral está sendo apresentada na Tabela 4.1 (é bom ressaltar que as
ferramentas foram projetadas principalmente para conjuntos de dados de
tamanho pequeno a médio). É claro que estamos vendo apenas uma
pequena parte do que existe, e há algumas omissões óbvias (como áudio e
vídeo). Aqui, nos concentraremos nos formatos de dados mais
amplamente usados.
Tabela 4.1: Visão geral dos formatos de dados e dos pacotes Python e R
populares usados para processar os dados armazenados neles
Tipo de dado Pacote Python Pacote R
Tabular pandas readr, rio
Imagem open-cv, scikit-image, PIL magickr, imager, EBImage
Texto nltk, spaCy tidytext, stringr
Série temporal prophet, sktime prophet, ts, zoo
Espacial gdal, geopandas, pysal rgdal, sp, sf, raster

Essa tabela não é de forma alguma de nitiva, e temos certeza de que


novas ferramentas aparecerão em breve, mas esses são os recursos que
atendem aos nossos critérios de seleção. Vamos colocá-los em ação nas
seções a seguir e veremos quais são os melhores para a tarefa!

Pacotes externos versus básicos


Nos capítulos 2 e 3, introduzimos os pacotes muito cedo no processo de
aprendizagem. Em Python usamos pandas logo no início e zemos a
transição para o Tidyverse em R com relativa rapidez. Isso nos permitiu
ser produtivos com maior velocidade do que se nos aventurássemos pelos
recursos arcaicos das linguagens, dos quais provavelmente, como
iniciante, você não precisará.3 Um utilitário de linguagem de programação
é de nido pela disponibilidade e pela qualidade de seus pacotes de
terceiros, e não pelos recursos básicos da própria linguagem.
Além disso, não há muita coisa que você possa fazer apenas com o base R
(como veremos em alguns dos próximos exemplos), mas bene ciar-se do
ecossistema open source é uma habilidade fundamental para aumentar
sua produtividade e evitar reinventar a roda.
Volte e aprenda o básico
Existe um perigo no uso excessivo de pacotes de terceiros e você
precisa car alerta para quando for a hora certa de voltar ao básico.
Caso contrário, pode ser vítima de uma falsa sensação de segurança e
tornar-se dependente das redes de proteção fornecidas por ferramentas
como o pandas. Isso pode levar a di culdades na superação de
desa os especí cos do mundo real.
Agora veremos como esse pacote versus o conceito da linguagem básica se
saem na prática ao detalharmos um tópico com o qual já estamos
familiarizados: dados tabulares.4 Existem pelo menos duas maneiras de
fazer isso em Python. Em primeiro lugar, usando pandas:
import pandas as pd

data = pd.read_csv("dataset.csv")
Em segundo lugar, com o módulo interno csv:
import csv

with open("dataset.csv", "r") as file:❶


reader = csv.reader(file, delimiter=",")
for row in reader:❷
print(row)

❶ Observe que é preciso especi car o modo do arquivo


(https://oreil.ly/KxuG2), nesse caso "r" (que signi ca “read”). Isso é
para assegurar que o arquivo não seja sobreposto por acidente,
fazendo alusão a uma linguagem orientada a um uso mais genérico.
❷ Usar um loop para ler um arquivo pode parecer estranho para um
iniciante, mas torna o processo explícito.
Esse exemplo mostra que a função pd.read_csv() do pandas fornece
uma maneira mais concisa, conveniente e intuitiva de importar dados. Ela
também é menos explícita do que o vanilla Python e não essencial.
pd.read_csv() é, na verdade, um wrapper de conveniência de
funcionalidade existente – o que é bom para nós!
Aqui vemos que os pacotes servem a duas nalidades. Em primeiro lugar,
como era de se esperar, eles fornecem nova funcionalidade. Em segundo
lugar, também são wrappers de conveniência para funções padrão
existentes, o que facilita nossas vidas.
Isso é demonstrado com brilhantismo no pacote rio do R.5 rio signi ca “R
input/output” (entrada/saída do R) e ele faz exatamente o que sugere.6
Aqui, a função única import() usa a extensão de nome do arquivo para
selecionar a melhor função de uma coleção de pacotes de importação. Isso
funciona no Excel, SPSS, stata, SAS e em muitos outros formatos comuns.
Outro pacote do Tidyverse R chamado vroom permite a rápida
importação de dados tabulares e pode ler um diretório inteiro em um
único comando, com o uso de funções map() ou loops for.
Para concluir, o pacote data.table, que costuma ser negligenciado devido à
promoção do Tidyverse, fornece a excepcional função fread(), que pode
importar arquivos muito grandes a uma velocidade maior do que o
base R ou o readr oferece.
A utilidade de aprender como usar pacotes de terceiros torna-se mais
óbvia quando tentamos executar tarefas mais complexas, como veremos
ao processar outros formatos de dados.
Ciência de dados a partir do zero
Criar software a partir do zero é uma ótima maneira de entender
como as coisas funcionam em segundo plano. É uma etapa
recomendada na aprendizagem, principalmente depois que nos
acostumamos com as ferramentas em níveis mais altos de abstração
(como no scikit-learn). Um ótimo material de leitura sobre esse tópico
é fornecido no livro de Joel Grus, Data Science do Zero (O’Reilly, 2019).
Agora que conhecemos as vantagens dos pacotes, demonstrarei alguns de
seus recursos. Para isso, trabalharemos em vários casos de uso diferentes
do mundo real, listados na Tabela 4.2. Não nos concentraremos em
detalhes minuciosos da implementação e, em vez disso, abordaremos os
elementos que expõem seus benefícios (e de ciências) para as tarefas a
serem executadas. Já que este capítulo dará ênfase aos formatos de dados,
e o Capítulo 5 é sobre uxos de trabalho, todos esses estudos de caso são
sobre processamento de dados.

Por razões pedagógicas omitimos partes do código. Se quiser segui-


lo, o código executável está disponível no repositório do livro
(https://github.com/moderndatadesign/PyR4MDS).
Tabela 4.2: Visão geral dos diferentes casos de uso
Formato dos dados Caso de uso
imagem Detecção de piscinas e carros
texto Processamento de críticas de produtos da Amazon
série temporal Temperaturas diárias na Austrália
espacial Dados de distribuição da espécie africana Loxodonta

Mais informações sobre como baixar e processar esses dados estão


disponíveis no repositório
(https://github.com/moderndatadesign/PyR4MDS) o cial do livro.

Dados de imagem
As imagens trazem um conjunto de desa os único para os cientistas de
dados. Demonstraremos a metodologia ótima abordando o desa o do
processamento de imagens aéreas – uma área de importância crescente na
agricultura, na conservação da biodiversidade, no planejamento urbano, e
na pesquisa de mudanças climáticas. Nosso pequeno caso de uso utiliza
dados do Kaggle coletados para ajudar na detecção de piscinas e carros.
Para obter mais informações sobre o conjunto de dados, você pode usar a
URL da Tabela 4.2.
Como mencionamos no começo do capítulo, a nalidade de etapas
posteriores do processo in uencia muito o processamento dos dados. Já
que dados aéreos são usados com frequência no treinamento de
algoritmos de machine learning, daremos ênfase às tarefas preparatórias.
O pacote OpenCV (https://opencv.org) é uma das maneiras mais comuns
de trabalhar com dados de imagem em Python. Ele contém todas as
ferramentas necessárias para o carregamento, a manipulação e o
armazenamento de imagens. O “CV” do nome signi ca computer vision –
a área de machine learning que se dedica aos dados de imagem. Outra
ferramenta útil que usaremos é o scikit-image. Como seu nome sugere, ele
está intimamente relacionado com o scikit-learn (https://oreil.ly/ZCR55).7
Aqui estão as etapas de nossa tarefa (consulte a Tabela 4.2):
1. Redimensionar a imagem para um tamanho especí co.
2. Converter a imagem para preto e branco.
3. Aumentar os dados girando a imagem.
Para um algoritmo de ML aprender com sucesso a partir dos dados, a
entrada tem de ser limpa (data munging, manipulação de dados),
padronizada (escalonamento) e ltrada (engenharia de recursos).8
Suponhamos que você coletasse um conjunto de dados de imagens (por
exemplo, fazendo o scraping de dados do Google Images).9
Eles diferirão de alguma forma – como no tamanho e/ou na cor. As
etapas 1 e 2 de nossa lista de tarefas nos ajudarão a lidar com isso. A
etapa 3 é útil para aplicações de ML. O desempenho (isto é, a precisão da
classi cação, ou a AUC [área sob a curva]) dos algoritmos de ML depende
em grande parte da quantidade de dados de treinamento, que geralmente
ocorre em pequeno volume. Para resolver esse problema, sem recorrer à
obtenção de mais dados,10 os cientistas de dados descobriram que
manusear os dados já disponíveis, como girá-los e recortá-los, pode
introduzir novos pontos de dados. Esses novos pontos de dados podem
então ser usados para treinar o modelo novamente e melhorar o
desempenho. Esse processo é conhecido formalmente como aumento de
dados (data augmentation).11
Já falamos demais – vamos importar os dados! Veri que o código
completo no repositório (https://github.com/moderndatadesign/PyR4MDS)
do livro se quiser fazer o acompanhamento. Os resultados estão na
Figura 4.2.
import cv2❶
single_image = cv2.imread("img_01.jpg")

plt.imshow(single_image)
plt.show()
❶ Usar cv2 pode parecer confuso, já que o pacote se chama OpenCV.
cv2 é usado como uma abreviação. O mesmo padrão de nomeação é
utilizado para o scikit-image, em que a instrução de importação é
abreviada para skimage.
Em que tipo de objeto cv2 armazenou os dados? Podemos veri car com
type:
print(type(single_image))
numpy.ndarray

Figura 4.2: Diagrama bruto da imagem em Python com o matplotlib.


Aqui podemos observar um recurso importante que apresenta vantagens
no uso do Python para tarefas de CV, ao contrário do que ocorre com o R.
A imagem é armazenada diretamente como um array multidimensional
do NumPy (nd signi ca n-dimensões), o que a torna acessível para outras
ferramentas disponíveis no ecossistema mais amplo do Python. Já que
esse recurso é baseado na pilha PyData, ele tem um bom suporte. Isso
ocorre com o R? Examinemos o pacote magick:
library(magick)
single_image <- image_read('img_01.jpg')
class(single_image)
[1] "magick-image"

A classe magick-image só pode ser acessada por funções do pacote


magick, ou outras ferramentas intimamente relacionadas, mas não pelos
poderosos métodos do base-R (como os mostrados no Capítulo 2, com a
notável exceção de plot()). As diferenças nas abordagens de vários
pacotes open source ao suportarem uns aos outros são ilustradas na
Figura 4.3, e esse é um tópico comum em todos os exemplos deste
capítulo.

Há pela menos uma exceção a essa regra – o pacote EBImage, que faz
parte do BioConductor (https://bioconductor.org). Usando-o você pode ter
acesso à imagem em sua forma bruta de array e então utilizar
também outras ferramentas. A desvantagem é que ele faz parte de um
pacote especí co de uma área, e pode não ser fácil saber como
funcionaria em um pipeline CV padrão.
Observe que, na etapa anterior (na qual carregamos a imagem bruta em
Python), também usamos uma das ferramentas de diagramação mais
populares, o matplotlib (a visualização de dados será abordada no
Capítulo 5); logo, nos bene ciamos novamente desse padrão melhor de
design.

Figura 4.3: Os dois tipos de hierarquias de design de pacotes como são


usados durante um ciclo de vida de dados (de baixo para cima).
Agora que sabemos que os dados de imagem estão armazenados como
um ndarray do NumPy, podemos usar os métodos do NumPy. Qual é o
tamanho da imagem? Para saber isso podemos tentar usar o método
.shape de ndarray:
print(single_image.shape)
224 224 3

Funcionou! Os dois primeiros valores da saída correspondem


respectivamente à altura e à largura da imagem e o terceiro é o número
de canais da imagem – nesse caso, três ((r)ed, (g)reen e (b)lue). Agora
daremos prosseguimento e distribuiremos a primeira etapa de
padronização – o redimensionamento da imagem. Aqui usaremos cv2
pela primeira vez:
single_image = cv2.resize(single_image,(150, 150))
print(single_image.shape)
(150, 150, 3)

Se você ganhar experiência no trabalho com essas ferramentas


básicas nas duas linguagens, poderá testar suas ideias rapidamente,
mesmo sem saber se esses métodos existem. Se as ferramentas que
você usar tiverem sido bem projetadas (como no melhor design da
Figura 4.3), elas devem funcionar como esperado!
Perfeito, funcionou como em um passe de mágica! A próxima etapa é
converter a imagem para preto e branco. Para fazer isso, também
usaremos cv2:
gray_image = cv2.cvtColor(single_image, cv2.COLOR_RGB2GRAY)
print(gray_image.shape)
(150, 150)

As cores são esverdeadas em vez de cinza. Essa opção padrão seleciona


um esquema de cores que torna o contraste mais facilmente discernível
para um olho humano do que preto e branco. Se você examinar a forma
do ndarray do NumPy, verá que o número do canal desapareceu – há
apenas um agora. Concluiremos nossa tarefa, executando uma etapa
simples de aumento dos dados e invertendo a imagem horizontalmente.
Estamos nos bene ciando novamente do fato de os dados estarem
armazenados como um array do NumPy. Usaremos uma função
diretamente a partir do NumPy, sem precisar das outras bibliotecas de CV
(OpenCV ou scikit-image):
flipped_image = np.fliplr(gray_image)

Os resultados são mostrados na Figura 4.4.

Figura 4.4: Diagrama de uma imagem invertida com o uso de funções do


NumPy.
Podemos usar o scikit-image para tarefas adicionais de manipulação de
imagem como a rotação, e até mesmo esse pacote diferente funcionará
como esperado em nosso formato de dados:
from skimage import transform
rotated_image = transform.rotate(single_image, angle=45)

As etapas de padronização e aumento dos dados que executamos ilustram


como o design de pacote menos complexo (Figura 4.3) nos torna mais
produtivos. Podemos reforçar esse ponto mostrando um exemplo negativo
da terceira etapa, dessa vez em R. Para fazer isso, teremos de usar o pacote
adimpro:
library(adimpro)
rotate.image(single_image, angle = 90, compress=NULL)
Sempre que carregamos mais um pacote, diminuímos a qualidade, a
legibilidade e a reusabilidade de nosso código. Esse problema pode
ocorrer principalmente devido a possíveis bugs desconhecidos, uma curva
de aprendizagem mais íngreme ou a possível falta de uma documentação
consistente e completa para esse pacote de terceiros. Uma veri cação
rápida do status do adimpro no CRAN (https://oreil.ly/Hr1f3) revela que a
última vez que ele foi atualizado foi em novembro de 2019.12 É por isso que
é preferível usar ferramentas como o OpenCV, que opera com dados de
imagem bene ciando-se da pilha PyData.13
Um design de pacote menos complexo, modular e su cientemente
abstrato ajuda a tornar os cientistas de dados produtivos e felizes ao
usarem suas ferramentas. Eles cam livres para se dedicar ao trabalho
propriamente dito em vez de ter de lidar com uma documentação
complexa ou vários pacotes abandonware. Essas considerações tornam o
Python um vencedor inconteste na importação e no processamento de
dados de imagem, mas é isso que ocorre com os outros formatos?

Dados de texto
Geralmente a análise de dados de texto é usada de maneira
intercambiável com o termo NLP (natural language processing,
processamento de linguagem natural). Essa, por sua vez, é uma subárea do
ML. Logo, não é de surpreender que ferramentas baseadas em Python
também a dominem. A natureza de uso inerentemente intensivo de
computação do trabalho com dados de texto é uma boa razão para isso.
Outra seria que lidar com conjuntos de dados maiores pode ser um
desa o mais difícil em R do que em Python (esse tópico será abordado
com mais detalhes no Capítulo 5).14 E trata-se de um problema de
big data. A quantidade de dados de texto tem proliferado nos últimos
anos com o surgimento de serviços nas grandes corporações de internet e
mídias sociais como o Twitter e o Facebook. Essas organizações também
têm investido pesadamente na tecnologia e em ferramentas open source
relacionadas, devido ao fato de que grande parte dos dados disponíveis
para elas está no formato de texto.
Como no caso dos dados de imagem, começaremos projetando uma
tarefa NLP padrão. Ela deve conter os elementos mais básicos de um
pipeline NLP. Para o conjunto de dados, selecionamos um de textos de
críticas de produtos da Amazon (Tabela 4.2) e temos de prepará-lo para
um caso de uso de análise avançado, como de classi cação de texto,
análise de sentimentos ou modelagem de tópicos. As etapas necessárias
para a conclusão são as seguintes:
1. Tokenizar os dados.
2. Remover palavras vazias.
3. Marcar as PoS (parts of speech, partes da fala).
Também examinaremos métodos mais avançados (como a incorporação
de palavras) no spaCy para demonstrar do que os pacotes Python são
capazes e, ao mesmo tempo, fornecer alguns exemplos de R para
comparação.
Quais seriam as ferramentas mais comuns em Python? Geralmente a
mais popular é chamada de canivete suíço do NLP – o NLTK (Natural
Language Toolkit).15 Ele contém uma boa seleção de ferramentas que
abrangem o pipeline inteiro. Também fornece uma excelente
documentação e uma curva de aprendizagem relativamente baixa para
sua API.
Para um cientista de dados, uma das primeiras etapas de um projeto é
examinar os dados brutos. Aqui está o exemplo de uma crítica, com seu
tipo de dado:
example_review = reviews["reviewText"].sample()
print(example_review)
print(type(example_review))
I just recently purchased her ''Paint The Sky With Stars''
CD and was so impressed that I bought 3 of her previously
released CD's and plan to buy all her music. She is
truely talented and her music is very unique with a
combination of modern classical and pop with a hint of
an Angelic tone. I still recommend you buy this CD. Anybody
who has an appreciation for music will certainly enjoy her music.

str
Essa parte é importante – os dados estão armazenados em um tipo de
dado básico em Python – str (string). Semelhante ao que ocorreu com os
dados de imagem que foram armazenados como um array
multidimensional do NumPy, várias ferramentas poderão ter acesso a ele.
Por exemplo, suponhamos que fôssemos usar uma ferramenta que
pesquisasse e substituísse e cientemente partes de uma string, como o
ashtext (https://oreil.ly/JyYW6). Nesse caso, poderíamos usá-la aqui sem
problemas de formatação e a necessidade de fazer a coerção do tipo de
dado.16
Agora podemos executar a primeira etapa de nosso pequeno estudo de
caso – a tokenização. Ela dividirá as críticas em componentes, como em
palavras ou frases:
sentences = nltk.sent_tokenize(example_review)
print(sentences)
["I just recently purchased her ''Paint The Sky With Stars''
CD and was so impressed that I bought 3 of her
previously released CD's and plan to buy all her music.",
'She is truely talented and her music is very unique with
a combination of modern classical and pop with a hint of an Angelic tone.',
'I still recommend you buy this CD.',
'Anybody who has an appreciation for music will certainly enjoy her music.']

Muito fácil! Para ns de ilustração, seria muito difícil tentar executar essa
tarefa relativamente simples em R, com algumas funções do pacote
tidytext?
tidy_reviews <- amazon_reviews %>%
unnest_tokens(word, reviewText) %>%
mutate(word = lemmatize_strings(word, dictionary = lexicon::hash_lemmas))

Esse é um dos métodos mais bem documentados que podemos usar. Um


problema dessa abordagem é que ela depende muito do conceito de
“dados organizados” (tidy data) e também do conceito de encadeamento
em pipeline do dplyr (abordamos as duas situações no Capítulo 2). Esses
conceitos são especí cos do R, e para usar o tidytext com sucesso, antes
você precisaria conhecê-los, em vez de passar diretamente para o
processamento de seus dados. O segundo problema é a saída desse
procedimento – um novo data.frame contendo os dados em uma coluna
processada. Embora no m das contas possa ser disso que precisamos, é
algo que pula algumas etapas intermediárias e está a várias camadas de
abstração acima do que zemos com o nltk. Diminuir a abstração e
trabalhar de maneira mais modular (como processando em primeiro
lugar antes um único arquivo de texto) nos faz car em conformidade
com as melhores práticas de desenvolvimento de software, como o
princípio DRY (“Do not repeat yourself”, não se repita) e a separação de
conceitos (separation of concerns).
A segunda etapa de nosso pequeno pipeline de processamento de dados
NLP é remover as palavras vazias:17
tidy_reviews <- tidy_reviews %>%
anti_join(stop_words)

Esse código apresenta os mesmos problemas, além de uma função nova e


confusa – anti_join. Iremos compará-lo com a etapa simples de
compreensão de lista (veja mais informações sobre isso no Capítulo 3) do
nltk:
english_stop_words = set(stopwords.words("english"))
cleaned_words = [word for word in words if word not in english_stop_words]

english_stop_words é apenas uma lista, e a única coisa que estamos


fazendo é percorrer com um loop cada palavra de outra lista (words),
removendo-a se ela estiver presente nas duas listas. Isso é mais fácil de
entender. Não há a dependência de conceitos ou funções avançados que
não estejam diretamente relacionados. Esse código também está no nível
certo de abstração. Pequenos blocos de códigos podem ser usados de
maneira mais exível como partes de uma função maior de pipeline de
processamento de texto. Uma metafunção de processamento semelhante
em R poderia car saturada – com execução lenta e difícil de ler.
Embora o nltk permita a execução dessas tarefas básicas, agora teremos
de examinar um pacote mais avançado – o spaCy. Vamos usá-lo na
terceira e última etapa de nosso estudo de caso – a marcação de PoS
(partes da fala):18
import spacy

nlp = spacy.load("en_core_web_sm") ❶
doc = nlp(example_review) ❷
print(type(doc))
spacy.tokens.doc.Doc

❶ Aqui estamos carregando com uma única função toda a


funcionalidade avançada necessária.
❷ Pegamos um único exemplo de crítica e o fornecemos para um
modelo do spaCy, o que resultou no tipo spacy.tokens.doc.Doc e
não em um str. Agora esse objeto pode ser usado para todos os tipos
de operações:
for token in doc:
print(token.text, token.pos_)

Os dados já foram tokenizados no carregamento. E não é só isso – todas


as marcações de PoS já foram criadas!
As etapas de processamento de dados que abordamos são relativamente
básicas. Que tal vermos alguns métodos mais novos e avançados de NLP?
Podemos examinar as incorporações de palavras, por exemplo. Esse é um
dos métodos mais avançados de vetorização de texto,19 no qual cada vetor
representa o signi cado de uma palavra com base em seu contexto. Para
fazer isso, podemos usar o mesmo objeto nlp do código do spaCy:
for token in doc:
print(token.text, token.has_vector, token.vector_norm, token.is_oov)
for token in doc:...
I True 21.885008 True
just True 22.404057 True
recently True 23.668447 True
purchased True 23.86188 True
her True 21.763712 True
' True 18.825636 True

É uma surpresa bem-vinda vermos que esses recursos já existem em um


dos pacotes de NLP mais populares do Python. É possível ver que quase
não há alternativa em R (ou até mesmo em outras linguagens) para esse
nível de métodos de NLP. Muitas soluções análogas em R dependem do
código wrapper de um back-end Python (o que pode tornar inútil o uso
da linguagem R).20 Esse padrão é visto com frequência no livro,
principalmente no Capítulo 5. O mesmo ocorre com outros métodos
avançados, como os modelos transformer.21
No segundo round, o Python é novamente o vencedor. Os recursos do
nltk, do spaCy e de outros pacotes associados o tornam uma excelente
opção para o trabalho com NLP!

Dados de séries temporais


O formato de série temporal é usado para armazenar dados com uma
dimensão temporal associada. Ele pode ser utilizado para um caso tão
simples como o da venda de um xampu em uma mercearia local com um
timestamp, ou para milhões de pontos de dados de uma rede de sensores
que meça a umidade em um campo de cultivo.

Existem algumas exceções à predominância do R para a análise de


dados de séries temporais. Os desenvolvimentos recentes nos
métodos de deep learning, principalmente as redes LSTM (long
short-term memory, memória longa de curto prazo), mostraram ser
muito bem-sucedidos para a previsão de séries temporais. Como no
caso de outros métodos de deep learning (veremos mais sobre isso no
Capítulo 5), essa é uma área mais bem suportada por ferramentas
Python.
Base-R
Existem alguns pacotes diferentes que os useRs podem empregar para
analisar dados de séries temporais, incluindo o xts e o zoo, mas nos
concentraremos nas funções do base-R como ponto de partida. Depois,
examinaremos um pacote mais moderno para ilustrar uma
funcionalidade mais avançada: o pacote Prophet do Facebook
(https://oreil.ly/WNOyF).
Dados meteorológicos estão amplamente disponíveis e são relativamente
fáceis de interpretar; logo, em nosso estudo de caso, analisaremos a
temperatura mínima diária na Austrália (Tabela 4.2). Para fazer uma
análise de série temporal, precisamos executar as etapas a seguir:
1. Carregar os dados em um formato apropriado.
2. Diagramar os dados.
3. Remover ruídos e efeitos sazonais e extrair tendências.
Em seguida, poderíamos prosseguir para uma análise mais avançada.
Suponhamos que tivéssemos carregado os dados a partir de um arquivo
.csv para um objeto data.frame em R. Não há nada de especial aqui.
Mesmo assim, ao contrário do que ocorre com a maioria dos pacotes
Python, o R requer a coerção de dados para um tipo de objeto especí co.
Nesse caso, precisamos transformar o data.frame em um ts (que
representa série temporal).
df_ts <- ts(ts_data_raw$Temp, start=c(1981, 01, 01),
end=c(1990, 12, 31), frequency=365)
class(df_ts)

Por que isso seria melhor do que o pandas? Bem, depois de conseguir
converter os dados brutos em um pd.DataFrame de série temporal, você
ainda encontrará um novo conceito – a indexação do data frame (consulte
a Figura 4.5). Para ser e ciente na manipulação dos dados, antes você
precisa entender como esse conceito funciona!
O conceito de indexação pode ser confuso, logo examinaremos agora qual
é a alternativa em R e se ela é melhor. Já existem algumas coisas úteis que
podemos fazer com o objeto de série temporal df_ts.

Figura 4.5: Índice de série temporal no pandas.


Ele também será um bom ponto de partida quando você estiver
trabalhando com pacotes de séries temporais mais avançados porque a
coerção de um objeto ts para o xts ou o zoo não deve lançar erros (esse é
mais um exemplo do bom design de objetos que abordamos na
Figura 4.3). A primeira coisa que você pode tentar fazer é diagramar (com
plot) o objeto, o que com frequência gera bons resultados em R:
plot(df_ts)
A chamada à função plot não usa apenas uma função padrão que
pode diagramar todos os tipos de objetos diferentes em R (isso era o
que seria esperado). Ela chama um método especí co que está
associado ao objeto de dados (mais informações sobre a diferença
entre funções e métodos estão disponíveis no Capítulo 2). Existe
muita complexidade oculta por trás dessa simples chamada de
função!
Os resultados de plot(df_ts) mostrados na Figura 4.6 são úteis. As datas
do eixo x são reconhecidas e um grá co de linhas foi selecionado em vez
do grá co de pontos padrão.

Figura 4.6: Grá co de um objeto de série temporal (ts) no base-R.


O problema predominante na análise de dados de séries temporais (e na
maioria dos dados de ML) é a manipulação do ruído. A diferença entre
esse formato de dados e os outros é que existem algumas fontes de ruído
diferentes, e os diferentes padrões podem ser limpos. Isso é feito com uma
técnica chamada decomposição, para a qual temos a função interna de
nome apropriado decompose:
decomposed_ts <- decompose(df_ts)
plot(decomposed_ts)

Os resultados podem ser vistos na Figura 4.7.


Figura 4.7: Diagrama de série temporal decomposta no base-R.
Podemos ver qual é o ruído aleatório e também qual é o padrão sazonal e
geral. Obtivemos tudo isso com uma única chamada de função no base R!
Em Python, precisaríamos usar o pacote statsmodels para obter o mesmo.

Prophet
Existe outro exemplo de pacote interessante para a análise de dados de
séries temporais. Ele foi desenvolvido simultaneamente tanto para R
quanto para Python (semelhante ao ocorrido com a ferramenta de ML
explicável lime): trata-se do pacote Prophet do Facebook
(https://oreil.ly/WNOyF). Esse exemplo pode nos ajudar a comparar as
diferenças no design de APIs. O Prophet é um pacote cuja principal
vantagem está na exibilidade de o usuário de uma área conseguir fazer
ajustes de acordo com suas necessidades especí cas, na facilidade de uso
da API e na ênfase dada à preparação para a produção. Esses fatores o
tornam uma boa opção para a prototipagem de trabalhos com séries
temporais e para o seu uso em um produto de dados. Façamos uma
veri cação; nossos dados foram armazenados como um pd.DataFrame
em df:
from fbprophet import Prophet
m = Prophet()
m.fit(df)❶
future = m.make_future_dataframe(periods=365) ❷
future.tail()

❶ Aqui vemos novamente o mesmo padrão de API fit, tomado


emprestado do scikit-learn.
❷ Essa etapa cria um novo pd.DataFrame vazio que armazenará nossas
previsões posteriormente.
library(prophet)

m <- prophet(df)

future <- make_future_dataframe(m, periods = 365)


tail(future)

Os dois códigos são muito simples e contêm o mesmo número de etapas –


esse é um ótimo exemplo de um design de API consistente (veremos mais
sobre isso no Capítulo 5).

É uma ideia útil e interessante oferecer uma experiência de usuário


consistente em várias linguagens, mas não achamos que ela será
amplamente implementada. Poucas organizações possuem os
recursos necessários para a execução desse trabalho, o que pode ser
limitante, já que comprometimentos têm de ser feitos quando se trata
de opções de design de software.
A essa altura, você já deve ter entendido que conhecer as duas linguagens
lhe dará uma vantagem signi cativa no trabalho diário. Se você só puder
acessar o ecossistema de pacotes Python, provavelmente tentará encontrar
ferramentas semelhantes para a análise de séries temporais e perderá as
oportunidades que o base R e os pacotes R relacionados fornecem.

Dados espaciais
A análise de dados espaciais é uma das áreas mais promissoras do
machine learning moderno e tem uma rica história. Novas ferramentas
foram desenvolvidas nos últimos anos, mas por muito tempo o R levou
vantagem, apesar de alguns avanços recentes em Python. Como nas seções
anteriores, examinaremos um exemplo prático para ver os pacotes em
ação.

Existem vários formatos de dados espaciais disponíveis. Nesta


subseção, nos concentraremos na análise de dados raster. Para outros
formatos existem algumas ferramentas interessantes disponíveis em
Python, como o GeoPandas (https://geopandas.org), mas ele não faz parte
do escopo deste capítulo.
Nossa tarefa será processar dados de ocorrência (observações do animal
no ambiente selvagem com marcação do local) e ambientais do Loxodonta
africana (elefante africano) para torná-los adequados para previsões
espaciais. Esse processamento de dados é típico da SDM (species
distribution modeling, modelagem de distribuição de espécies), na qual as
previsões são usadas para a construção de mapas de adequação de hábitat
empregados para a conservação. Esse estudo de caso é mais avançado do
que os anteriores e muitas das etapas ocultam alguma complexidade
onde os pacotes fazem o trabalho pesado. As etapas são as seguintes:
1. Obter dados raster ambientais.
2. Recortar o raster para que se ajuste à área de interesse.
3. Lidar com a autocorrelação espacial com métodos de amostragem.
Para resolver esse problema, como primeira etapa precisamos processar os
dados raster.22 Eles são, de certa forma, muito semelhantes aos dados de
imagem padrão, mas com diferenças nas etapas de processamento. Para
isso, o R tem o excelente pacote raster disponível (a alternativa seria o gdal
do Python e o rgdal do R, mas achamos que eles são mais difíceis de
usar).
library(raster)
climate_variables <- getData(name = "worldclim", var = "bio", res = 10)

Os dados raster nos permitem baixar a maioria dos conjuntos de dados


ambientais e espaciais úteis comuns, incluindo os dados bioclimáticos:23
e <- extent(xmin, xmax, ymin, ymax)
coords_absence <- dismo::randomPoints(climate_variables, 10000, ext = e)
points_absence <- sp::SpatialPoints(coords_absence,
proj4string = climate_variables@crs)
env_absence <- raster::extract(climate_variables, points_absence)

Aqui usamos a útil função extent para recortar (cortar) os dados raster –
só estamos interessados em uma subseção de todas as camadas
ambientais existentes ao redor dos dados de ocorrência. Usamos as
coordenadas de latitude e longitude para desenhar esse retângulo. Como
próxima etapa, para gerar um problema de classi cação, estamos criando
uma amostragem aleatória dos pontos de dados a partir dos dados raster
(as chamadas pseudoausências). Você poderia considerá-los os 0s de sua
tarefa de classi cação, e as ocorrências (observações) os 1s – a variável
alvo. Em seguida, convertemos as pseudoausências em SpatialPoints e,
para concluir, também extraímos seus dados climáticos. Na função
SpatialPoints, é possível ver que especi camos o sistema de projeção
geográ ca, um dos conceitos básicos da análise de dados espaciais.
Um dos problemas mais comuns do trabalho em ML são as correlações
dentro dos dados. A suposição básica que fazemos para considerar que
um conjunto de dados está correto é a de que as observações individuais
dos dados devem ser independentes umas das outras para obtermos
resultados estatísticos precisos. Esse problema está sempre presente nos
dados espaciais devido à sua natureza. Ele é chamado de autocorrelação
espacial. Existem vários pacotes disponíveis para a criação de amostragens
dos dados que tentam reduzir esse risco. Um deles é o ENMeval:
library(ENMeval)
check1 <- get.checkerboard1(occs, envs, bg, aggregation.factor=5)

A função get.checkerboard1 cria uma amostragem dos dados de


maneira uniformemente distribuída, semelhante a se pegássemos pontos
iguais de cada quadrado de um tabuleiro preto e branco de xadrez. Agora
podemos pegar os dados dessa nova amostra e treinar com sucesso um
modelo de ML sem nos preocupar com a autocorrelação espacial. Como
última etapa, podemos pegar essas previsões e criar um mapa de
adequação de hábitat, mostrado na Figura 4.8.
raster_prediction <- predict(predictors, model)
plot(raster_prediction)
Figura 4.8: Diagrama de uma previsão de objeto raster em R, resultando em
um mapa de adequação de hábitat.
Quando você estiver trabalhando com dados raster espaciais, o melhor
design de pacote é o fornecido pelo R. Ferramentas fundamentais como o
raster fornecem uma base consistente para ferramentas mais avançadas
especí cas de aplicações como o ENMeval e o dismo, sem precisarmos
nos preocupar com uma transformação complexa ou uma coerção de
tipos propensa a erros.

Considerações nais
Neste capítulo examinamos os diferentes formatos de dados mais comuns
e os melhores pacotes para o seu processamento de modo a carem
prontos para tarefas avançadas. Em cada estudo de caso, demonstramos
um bom design de pacote e como isso pode tornar um cientista de dados
mais produtivo. Vimos que para tarefas mais direcionadas ao ML, como a
CV e o NLP, Python está fornecendo a melhor experiência de usuário e
uma curva de aprendizagem mais baixa. Por outro lado, para uma
previsão mais voltada para séries temporais e para a análise espacial, o R
leva vantagem. Essas opções de escolha são mostradas na Figura 4.9.
Figura 4.9: Árvore de decisão para a seleção de pacotes.
O que as melhores ferramentas têm em comum é um design de pacote
melhor (Figura 4.3). Você deve sempre usar a melhor ferramenta para a
tarefa e prestar atenção na complexidade, na documentação e no
desempenho das ferramentas que usar!

1 Para ver uma explicação mais detalhada sobre esse assunto, consulte o guia do RealPython.com.
2 Normalmente isso é chamado de linhagem de dados.
3 Quem mais não aprendeu o que if __name__ == "__main__" faz em Python?
4 Uma tabela com os dados, armazenados em um único arquivo.
5 Não devemos nos esquecer do readr, que foi discutido no Capítulo 2.
6 Mencionamos que os estatísticos são muito literais, lembra-se?
7 Essa consistência é um tópico comum nos capítulos da Parte IV e também é abordada no
Capítulo 5.
8 Lembre-se – entra lixo, sai lixo.
9 Usar código para percorrer o conteúdo de uma página web, baixando-o e armazenando-o em um
formato legível por máquinas.
10 A obtenção de mais dados pode ser cara, ou até mesmo impossível em alguns casos.
11 Se quiser aprender mais sobre o aumento de dados de imagens, examine esse tutorial.
12 Veri cação feita quando este texto foi escrito.
13 A pilha PyData está relacionada com o NumPy, SciPy, pandas, IPython e matplotlib e não deve
ser confundida com a conferência de mesmo nome.
14 A comunidade R também se apressou e melhorou as ferramentas recentemente, mas é possível
que ainda não tenha alcançado o nível dos equivalentes no Python.
15 Para saber mais sobre o NLTK, consulte Natural Language Processing with Python, de Steven
Bird, Ewan Klein e Edward Loper (O’Reilly), um dos livros mais acessíveis sobre o trabalho com
dados de texto.
16 A coerção de tipos de dados é a conversão de um tipo de dado em outro.
17 Essa é uma etapa comum no NLP. Alguns exemplos de palavras vazias são “the”, “a” e “this”.
Elas têm de ser removidas, já que raramente oferecem informações úteis para algoritmos de ML.
18 Processo de rotular palavras com as PoS às quais elas pertencem.
19 Converter texto em números para ingestão por um algoritmo de ML.
20 Como na tentativa de criar incorporações personalizadas. Consulte o blog do RStudio
(https://oreil.ly/waD3o) para obter mais informações.
21 Você pode ler mais sobre isso no blog do RStudio (https://oreil.ly/rUaWz).
22 Dados representando células, em que o valor da célula representa alguma informação.
23 Características ambientais determinadas pelos ecologistas para serem altamente preditivas das
distribuições de espécies, isto é, umidade e temperatura.
CAPÍTULO 5

Contexto dos uxos de trabalho

Boyan Angelov
Uma fonte de frustração comum para os cientistas de dados é a discussão
de seu trabalho com colegas de áreas adjacentes. Vejamos o exemplo de
alguém que tem trabalhado principalmente no desenvolvimento de
modelos de ML, conversando sobre seu trabalho com um colega da
equipe de BI (business intelligence, inteligência empresarial), que dá
ênfase a relatórios. Geralmente, essa discussão deixa as duas partes
desconfortáveis devido a uma perceptível falta de conhecimento sobre
suas áreas de trabalho (sobre os uxos de trabalho associados) – apesar
de compartilharem uma função de mesmo nome. O pro ssional de ML
pode não saber o que é D3.js, gramática dos grá cos etc. Por outro lado, o
cientista de dados de BI pode se sentir inseguro por não saber como
construir uma API implantável. Os sentimentos que podem a orar de
uma situação desse tipo têm sido chamados de síndrome do impostor, na
qual surgem dúvidas sobre a competência do pro ssional. Essa situação é
um subproduto do grande volume de aplicações possíveis na ciência de
dados. Raramente uma pessoa tem o mesmo nível de familiaridade com
mais do que algumas subáreas. A exibilidade ainda é frequentemente
exigida nessa área em rápida evolução.
Essa complexidade é a razão da ênfase dada neste capítulo ao uxo de
trabalho. Abordaremos os principais uxos de trabalho da ciência de
dados e como os diferentes ecossistemas das linguagens os suportam.
Semelhante ao que ocorreu no Capítulo 4, no m do capítulo, você terá
tudo que é necessário para tomar decisões embasadas relacionadas aos
seus uxos de trabalho.
De nindo uxos de trabalho
Faremos uma pausa para de nir um uxo de trabalho:
Um uxo de trabalho é uma coleção completa de ferramentas e
frameworks para a execução de todas as tarefas requeridas pelas
competências de um cargo especí co.
Para esse exemplo, suponhamos que você fosse um engenheiro de ML.
Suas tarefas diárias poderiam incluir ferramentas para a obtenção de
dados, para o seu processamento e para o treinamento de um modelo
com os dados, e frameworks de implantação. Coletivamente, essas
ferramentas representam o uxo de trabalho do engenheiro de ML. Uma
visão geral dos uxos de trabalho de dados para essa e outras funções e
de suas ferramentas de suporte é apresentada na Tabela 5.1.
Tabela 5.1: Fluxos de trabalho comuns na ciência de dados e suas
ferramentas de apoio
Método Pacote Python Pacote R
Manipulação de dados (data munging)(a) pandas dplyr
EDA matplotlib, seaborn, pandas ggplot2, base R, Lea et
Machine learning scikit-learn mlr, tidymodels, caret
Deep learning Keras, TensorFlow, PyTorch Keras, TensorFlow, torch
Engenharia de dados(b) Flask, BentoML, FastAPI plumber
Relatórios Jupyter, Streamlit R Markdown, Shiny
(a) Data munging (ou wrangling) é um tópico fundamental na ciência de dados que já foi
abordado no Capítulo 2.
(b) A engenharia de dados vai muito além da implantação de modelos, mas decidimos nos
concentrar nesse subconjunto para ilustrar os recursos do Python.
Omitimos algumas áreas na esperança de que as listadas sejam as mais
comuns e críticas. Os uxos de trabalho selecionados estão relacionados
uns com os outros, como apresentado na Figura 5.1. Esse diagrama tem
uma aparência semelhante à do framework CRISP-DM
(https://oreil.ly/19361), que mostra todas as etapas signi cativas de um
projeto de ciência de dados típico. Cada etapa do diagrama tem um uxo
de trabalho separado associado a ela, geralmente atribuído a uma pessoa
ou a uma equipe.
Agora que de nimos um uxo de trabalho, quais são as propriedades
de nidoras de um que seja “bom”? Podemos criar uma lista de veri cação
com os três principais fatores a serem considerados:
1. É estabelecido. Ele foi amplamente adotado pela comunidade (e
também por diferentes áreas de aplicação, como a visão
computacional ou o processamento de linguagem natural).
2. É suportado por um ecossistema e uma comunidade open source
com boa manutenção. Um uxo de trabalho que dependa
pesadamente de aplicações de código-fonte fechado (closed-source) e
comerciais (como o MATLAB) não é considerado aceitável.
3. É adequado à sobreposição de tarefas da função. Os melhores uxos
de trabalho são semelhantes às peças do Lego – seu design modular e
extensibilidade podem suportar diversas pilhas de tecnologia.
Com o cenário geral e as de nições fora do caminho, podemos nos
aprofundar nos diferentes uxos de trabalho e em como eles são
suportados em R e Python!

Figura 5.1: Meta uxo de trabalho da ciência e da engenharia de dados.

Análise exploratória de dados


É difícil analisar números. Examinar linhas de dados contendo milhões
deles é ainda mais desa ador. Qualquer pessoa que lide com dados
enfrenta esse desa o diariamente. Essa necessidade levou a
desenvolvimentos consideráveis nas ferramentas de DV (data
visualization, visualização de dados). Uma tendência recente na área foi a
explosão de ferramentas analíticas de autoatendimento, como o Tableau
(https://www.tableau.com), o Alteryx (https://www.alteryx.com) e o
Microsoft Power BI (https://oreil.ly/5FMc2). Elas são muito úteis, mas o
universo open source tem várias alternativas disponíveis, geralmente
rivalizando com ou até mesmo indo além dos recursos de suas
contrapartidas comerciais (exceto, em alguns casos, na facilidade de uso).
Coletivamente essas ferramentas representam o uxo de trabalho da EDA.
Quando usar uma GUI para a EDA
Muitos cientistas de dados não veem com bons olhos usar uma GUI
em seu trabalho diário. Eles preferem a exibilidade e a utilidade das
ferramentas de linha de comando. No entanto, uma área em que o uso
de uma GUI faz muito sentido (por razões de produtividade) é a EDA.
Pode ser demorado gerar vários diagramas, principalmente no começo
de um projeto de ciência de dados. Geralmente, é preciso criar
dezenas, quando não centenas deles. Imagine escrever o código de
cada um (mesmo se você melhorar a organização de seu código
refatorando-o em funções). Para alguns conjuntos de dados maiores,
pode ser muito mais fácil usar uma GUI, como a do AWS QuickSight
ou a do Google Data Studio. Usando uma GUI, o cientista de dados
pode gerar vários diagramas rapidamente e só então escrever o código
dos que atenderem aos requisitos após a triagem. Existem algumas
ferramentas de GUI open source muito boas, por exemplo, o Orange
(https://orange.biolab.si).
A EDA é uma etapa fundamental no começo da análise de qualquer fonte
de dados. Ela costuma ser executada diretamente após o carregamento
dos dados, no estágio em que há uma necessidade signi cativa de
conhecimento do negócio. Isso explica por que é uma etapa essencial.
Você deve conhecer o paradigma entra lixo, sai lixo – a qualidade de
qualquer projeto de dados depende da qualidade dos dados de entrada e
do conhecimento que eles transmitem sobre a área. A EDA permite que os
uxos de trabalho posteriores (como o de ML) sejam bem-sucedidos,
assegurando que tanto os dados quanto as suposições feitas com eles
estejam corretos e tenham qualidade su ciente.
Para a EDA, o R tem ferramentas melhores que as do Python disponíveis.
Como discutimos nos capítulos 1 e 2, o R é uma linguagem criada por
estatísticos para estatísticos (lembra-se do termo FUBU que vimos no
Capítulo 2?), e há décadas a visualização (diagramação) de dados é muito
importante na estatística. O Python fez alguns avanços nos últimos anos,
mas ainda é visto como estando atrás (é só examinar o exemplo de
diagrama do matplotlib para perceber que esse pacote também permite a
criação de belos diagramas rapidamente, porém ainda perde para os
recursos do ggplot).1 Já elogiamos bastante o R; vejamos por que ele é
ótimo para a EDA!

Visualizações estáticas
Você já deve estar familiarizado com os recursos do base-R relacionados à
DV que vimos no Capítulo 4, principalmente os de diagramação de séries
temporais. Aqui daremos um passo à frente e discutiremos um dos
pacotes R mais famosos – o ggplot2. Ele é uma das principais razões para
os Pythonistas quererem mudar para o R.2 O que torna o ggplot2 tão
bem-sucedido no trabalho com a EDA é que ele é baseado em uma
metodologia bem planejada – a gramática dos grá cos, que foi
desenvolvida por L. Wilkinson. O pacote ggplot2 foi desenvolvido por
Hadley Wickham.3
O que é a gramática dos grá cos? O artigo original
(https://oreil.ly/BFQQc) que a inspirou chama-se “A Layered Grammar of
Graphics” (Uma gramática dos grá cos em camadas) e a palavra “layered”
é a chave. Tudo que vemos em um diagrama contribui para a formação de
uma pilha ou sistema maior. Por exemplo, os eixos e as grades formam
uma camada separada em comparação com as linhas, barras e pontos.
Esses últimos elementos constituem a camada de dados. A pilha completa
de camadas forma o resultado – um ggplot completo. Esse padrão de
design modular permite grande exibilidade e fornece uma nova maneira
de pensarmos em visualização de dados. A lógica existente por trás da
gramática dos grá cos é ilustrada pela Figura 5.2.
Figura 5.2: A gramática dos grá cos baseada em camadas.
Para ilustrar os diferentes procedimentos de um uxo de trabalho comum
da EDA, usaremos o conjunto de dados starwars (disponível no pacote
dplyr).4 Esse conjunto de dados contém informações sobre os personagens
do lme Guerra nas Estrelas, como seu gênero, altura e espécie. Vejamos
então!
library(ggplot2)
library(dplyr)

data("starwars") ❶
❶ ] Esse código tornará o conjunto de dados visível no ambiente do
RStudio, mas isso não é obrigatório.
Como primeira etapa, criaremos um diagrama básico:
ggplot(starwars, aes(hair_color)) +
geom_bar()
Ele representa gra camente as contagens da variável de cor do cabelo.
Aqui, vemos um operador familiar, +, usado de maneira não
convencional. Usamos + no ggplot2 para adicionar camadas acima umas
das outras. Vamos nos basear nisso para um caso mais complexo. Observe
que omitimos uma etapa de ltragem do código (há um valor discrepante
– Jabba the Hutt):5
ggplot(starwars, aes(x = height, y = mass, fill = gender)) + ❶
geom_point(shape = 21, size = 5) + ❷
theme_light() + ❸
geom_smooth(method = "lm") +❹
labs(x = "Height (cm)", y = "Weight (cm)",
title = "StarWars profiles ",
subtitle = "Mass vs Height Comparison",
caption = "Source: The Star Wars API")❺
❶ Especi ca que dados e características usar.
❷ Seleciona um diagrama de pontos (o mais adequado para dados
contínuos).
❸ Usa um tema (theme) interno – uma coleção de estilos de camada
especí cos.
❹ Ajusta um modelo linear e mostra os resultados como uma camada
no diagrama.
❺ Adiciona rótulos para o título e os eixos.
Os resultados dessa operação de diagramação são mostrados na
Figura 5.3. Com apenas algumas linhas de código, criamos um belo
diagrama, que pode ser estendido ainda mais.
Agora que abordamos as visualizações estáticas, veremos como torná-las
mais interessantes adicionando interatividade!
Figura 5.3: Diagrama avançado do ggplot2.

Visualizações interativas
A interatividade pode ser de grande ajuda nos diagramas exploratórios.
Dois excelentes pacotes do R se destacam: Lea et
(https://rstudio.github.io/lea et) e plotly (https://plotly.com).

Geralmente a interatividade em Python e R é baseada em uma base


de código (codebase) JavaScript subjacente. Pacotes como o Lea et e
o plotly facilitam esse trabalho fornecendo uma interface de nível
mais alto. Pacotes de baixo nível para grá cos interativos, como o D3.js
(https://d3js.org), podem ser difíceis para um novato usar. Logo,
recomendamos usar um framework de alto nível, como o dimple.js
(http://dimplejs.org).
Diferentes conjuntos de dados requerem métodos de visualização
diferentes. Abordamos o caso de um conjunto de dados tabular padrão
(starwars), mas que tal ver algo diferente? Tentaremos visualizar dados
com uma dimensão espacial e a usaremos para mostrar os excelentes
recursos do R para a produção de diagramas interativos. Para fazer isso,
selecionei o conjunto de dados Shared Cars Locations
(https://oreil.ly/FCKLG). Ele fornece as localizações de veículos
compartilhados em Tel Aviv, Israel. Podemos mostrá-las em um mapa?
library(leaflet)
leaflet(data = shared_cars_data[1:20, ]) %>%
addTiles() %>%
addMarkers(lng = longitude, lat = latitude)

Nesse caso, criamos um subconjunto dos dados usando apenas as


20 primeiras linhas (para tornar a visualização menos cheia). A função
addTiles fornece o plano de fundo do mapa, com os nomes das ruas e
cidades.6 A próxima etapa é adicionar os marcadores que especi cam as
localizações dos carros com o uso de addMarkers. O resultado dessa
operação relativamente simples é mostrado na Figura 5.4.

Figura 5.4: Diagrama de mapa interativo com o lea et.


Como ocorre com as melhores ferramentas de ciência de dados, pacotes
como o Lea et ocultam muita complexidade. Eles fazem grande parte do
trabalho pesado necessário para a visualização avançada e permitem que
o cientista de dados se concentre no que ele faz melhor – dedicar-se aos
dados. Existem muitos outros recursos avançados disponíveis no Lea et,
e recomendamos que o usuário motivado os explore.

Como o subtítulo do nosso livro sugere, estamos sempre tentando


aproveitar o melhor de dois mundos. Logo, uma maneira fácil de
fazê-lo é usando o comando ggplotly do pacote plotly passando para
ele um diagrama do ggplot2. Isso tornará o diagrama interativo!
Esperamos que esta seção tenha esclarecido por que o uxo de trabalho
de EDA torna o uso do R e de ferramentas como o ggplot2 e o Lea et as
melhores opções. Só vimos uma pequena parte do que é possível fazer, e
se alguém quiser se aprofundar nos aspectos da visualização de dados,
existem muitos recursos excelentes disponíveis.

Machine Learning
Atualmente, a ciência de dados é quase um sinônimo de machine
learning (ML). Embora existam muitos uxos de trabalho diferentes
necessários em um projeto de ciência de dados (Figura 5.1), geralmente o
ML é que chama a atenção dos aspirantes a cientistas de dados. Isso
ocorre em parte devido a uma disseminação cada vez maior nos últimos
anos provocada pela disponibilidade de grandes quantidades de dados e
de recursos computacionais melhores (como CPUs e GPus melhores), e
pela necessidade de previsões e automação nas empresas modernas.
Quando essa área surgiu, ela tinha um nome diferente – aprendizagem
estatística (statistical learning). Como já mencionado, historicamente a
estatística é a principal área atendida pelo R. Logo, existiam ferramentas
boas disponíveis desde o início para o trabalho com ML nessa linguagem.
No entanto, ultimamente isso mudou e na maioria das vezes as
ferramentas do Python superam às de seu concorrente estatístico.
Se rastreássemos o sucesso do ecossistema de ML do Python,
chegaríamos a um pacote especí co: o scikit-learn (https://oreil.ly/ZCR55).
Desde suas primeiras versões, a equipe de desenvolvimento básico se
dedicou a projetar uma API acessível e fácil de usar. Eles a
complementaram com uma das documentações mais completas e
acessíveis disponíveis no universo open source. Além de ser uma
documentação de referência, ela também contém ótimos tutoriais sobre
várias aplicações de ML especí cas modernas como o trabalho com dados
de texto (https://oreil.ly/ZXsrG). O scikit-learn dá acesso direto a quase
todos os algoritmos de ML comuns.7
Examinaremos provas de por que o scikit-learn é tão bom para ML. Em
primeiro lugar, podemos demonstrar as importações de modelos:
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LinearRegression

Aqui já podemos ver como esses modelos foram projetados de maneira


consistente – como os livros de uma biblioteca bem organizada; tudo está
no lugar certo. Os algoritmos de ML do scikit-learn estão agrupados de
acordo com suas semelhanças. Nesse exemplo, métodos baseados em
árvore como DecisionTreeClassifier pertencem ao módulo tree. Por
outro lado, algoritmos lineares podem ser encontrados em linear_mode
(isto é, se você quiser executar um modelo LASSO [least absolute
shrinkage and selection operator, operador de encolhimento e seleção
menos absoluto], poderá, como era de se esperar, encontrá-lo em
linear_model.Lasso). Esse design hierárquico facilita para os
pro ssionais poderem se dedicar à criação do código em vez de precisar
pesquisar a documentação, já que qualquer mecanismo com
preenchimento automático encontrará o modelo relevante.

Discutimos os módulos no Capítulo 3, mas esse é um conceito que


vale a pena repetir, já que ele é confuso para alguns usuários do R.
Os módulos em Python são apenas coleções de scripts organizados
(baseados em algumas semelhanças, como “data_processing”), o que
permite que eles sejam importados para nossas aplicações,
melhorando a legibilidade e tornando a base de código mais
organizada.
Em seguida, precisamos preparar os dados para modelagem. Um
elemento essencial de qualquer projeto de ML é a divisão dos dados em
conjuntos de treinamento e teste. Embora os pacotes mais novos do R,
como o mlr, também tenham melhorado nisso, o scikit-learn tem funções
melhores (em termos tanto de consistência quanto de sintaxe)
disponíveis:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.33,
random_state=42)

Suponhamos que tivéssemos sido consistentes nas etapas anteriores e


seguido a convenção tradicional do ML. Nesse caso, teríamos o objeto X
para armazenar nossas características e ‘y’ para os rótulos (no caso de um
problema de aprendizagem supervisionada).8 Os dados serão então
divididos aleatoriamente. A maneira o cial de se fazer isso no pacote mlr
do R é:
train_set = sample(task$nrow, 0.8 * task$nrow)
test_set = setdiff(seq_len(task$nrow), train_set)

Isso pode ser mais difícil de entender, há pouca documentação sobre


como executar uma divisão mais avançada, como por estrati cação, e
outro pacote pode ser necessário, aumentando a curva de aprendizagem e
a carga cognitiva do cientista de dados. Por outro lado, o scikit-learn
fornece uma função útil em StratifiedShuffleSplit. Os recursos
aumentam ainda mais quando começamos a executar realmente a
modelagem:
model = RandomForestClassifier()
model.fit(X_train, y_train)
predictions = model.predict(X_test)

Só precisamos dessas três linhas de código para inicializar o modelo com


parâmetros padrão, ajustá-lo (treiná-lo) no conjunto de dados de
treinamento e fazer previsões no conjunto de teste. Esse padrão é
consistente entre os projetos (exceto pela inicialização do modelo, onde
selecionamos o algoritmo que preferimos e seus parâmetros – e é claro
que eles são diferentes). Uma comparação visual entre vários pacotes
diferentes (de outros desenvolvedores e para outras nalidades) é
mostrada na Figura 5.6. Para concluir, calcularemos algumas métricas de
desempenho; muitas delas facilmente disponíveis:
from sklearn import metrics
acc = metrics.accuracy_score(predictions, y_test)
conf_matrix = metrics.confusion_matrix(predictions, y_test)
classif_report = metrics.classification_report(predictions, y_test)

O módulo metrics contém tudo que é necessário para a veri cação do


desempenho de nosso modelo com uma API simples e previsível. O
padrão ajustar e prever que vimos anteriormente tem sido tão in uente
no universo open source que foi amplamente adotado por outros pacotes,
como o Yellowbrick (um pacote para a visualização do desempenho de
modelos):
from yellowbrick.regressor import ResidualsPlot

visualizer = ResidualsPlot(regr)

visualizer.fit(X_train, y_train)
visualizer.score(X_test, y_test)
visualizer.show()

Existem muitas visualizações disponíveis no Yellowbrick (Figura 5.5),


todas obtidas com um procedimento semelhante. A consistência e a
facilidade de uso estão entre as razões mais relevantes que levam os
usuários a quererem usar Python para ML. Ele permite que o usuário se
concentre na tarefa, e não na criação de código e na veri cação de
tediosas páginas de documentação. Ocorreram alterações nos pacotes R
nos últimos anos visando à redução dessas de ciências. Entre esses
pacotes estão incluídos principalmente o mlr e o tidymodels. Eles não são
muito usados, mas esse padrão pode mudar no futuro. Há um fator
adicional a ser considerado aqui, que é semelhante à interoperabilidade
do ecossistema que vimos no Capítulo 4. O scikit-learn funciona muito
bem com outras ferramentas em Python que são necessárias para o
desenvolvimento e a implantação de modelos de ML. Essas ferramentas
incluem as conexões de banco de dados, pacotes computacionais de alto
desempenho, frameworks de teste e frameworks de implantação. Escrever
o código de ML no scikit-learn permitirá que o cientista de dados seja
uma parte mais produtiva de uma equipe de dados (imagine a expressão
no rosto de seus colegas engenheiros de dados quando você entregar um
modelo do mlr para eles implantarem).
Figura 5.5: Diferentes diagramas de regressão que podemos ver no
Yellowbrick.

Figura 5.6: Visão geral da consistência das APIs no ecossistema de ML do


Python.
Deep Learning
Não abordaremos a DL (deep learning, aprendizagem profunda) com
detalhes aqui porque grande parte da base lógica do scikit-learn (e do
Python em geral) também se aplica a ela. Mesmo assim, devido à sua
crescente importância na ciência de dados moderna, merece alguns
comentários adicionais.
O uxo de trabalho de DL tem sido suportado em grande parte por
dois frameworks open source concorrentes: o TensorFlow
(https://www.tensor ow.org) (do Google) e o PyTorch (https://pytorch.org) (do
Facebook). Existe mais um framework, que acabou sendo incluído no
TensorFlow, chamado Keras (https://keras.io). Ele fornece uma API com um
nível mais alto de abstração para as funções do TensorFlow,
diminuindo a curva de aprendizagem. Ocorreram dois
desenvolvimentos dignos de nota no ecossistema R relacionados a
esses frameworks de DL. O TensorFlow e o Keras foram portados com
o uso do pacote reticulate (https://rstudio.github.io/reticulate) (vamos abordá-lo no
Capítulo 6), que chama o Python em segundo plano. Por outro lado, o
PyTorch foi recriado elmente com base no libtorch, seu back-end C++
no pacote torch (https://torch.mlverse.org).
Devido a esses detalhes, recomendamos o uso das ferramentas Python
para um uxo de trabalho de DL, baseado no Keras e TensorFlow, mas
não para o torch, para o qual já existe uma base de código R.
Para concluir esta seção, resumiremos os principais pontos do uxo de
trabalho de ML e de por que as ferramentas Python dão um suporte
melhor a ele:
1. O foco mudou para previsões em tempo real e automação.
2. O uxo de trabalho de ML do Python fornece uma API mais
consistente e fácil de usar.
3. O Python é considerado uma linguagem de natureza mais
aglutinadora, ideal para a combinação de diferentes componentes de
software (isto é, front-tend/back-end e bancos de dados).9
Na próxima seção, nos aprofundaremos na terceira parte dessa lista e
demonstraremos o uxo de trabalho de engenharia de dados
recomendado.

Engenharia de dados
Apesar dos avanços obtidos nas ferramentas de ML nos últimos anos, a
taxa de conclusão desses projetos nas empresas permanece baixa. Uma
razão frequentemente associada a isso é a falta de suporte à DE (data
engineering, engenharia de dados). Para aplicar ML e análise avançada, as
empresas precisam da base infraestrutural fornecida pelos engenheiros de
dados, incluindo os bancos de dados, os pipelines de processamento de
dados, teste e ferramentas de implantação. É claro que isso forma um
cargo diferente – o engenheiro de dados. Mesmo assim, os cientistas de
dados precisam interagir com essas tecnologias (e às vezes implementá-
las) para assegurar que os projetos de ciência de dados sejam concluídos
com sucesso.
Embora a DE seja uma área imensa, nos concentraremos em um
subconjunto nesta seção. Selecionamos a implantação de modelos, já que
ela é o uxo de trabalho de DE mais comum do qual um cientista de
dados pode ter de participar. Então, o que é implantação de ML? Quase
sempre, isso signi ca criar uma API (application programming interface,
interface de programação de aplicações) e torná-la disponível para outras
aplicações, internamente ou externamente (para os clientes, chama-se
“expor” uma API, para ser “consumida”). Normalmente os modelos de
ML são implantados por meio de uma interface REST (representational
state transfer, transferência representacional de estado).10
A implantação de modelos de ML, em comparação com os outros tópicos
deste capítulo, requer interação com muitas tecnologias diferentes não
diretamente relacionadas à ciência de dados. Elas incluem frameworks
web, CSS, HTML, JavaScript, servidores de nuvem, balanceadores de
carga e outros componentes. Logo, não é de surpreender que as
ferramentas Python sejam predominantes aqui – como mencionamos,
trata-se de uma ótima linguagem aglutinadora.11

O uxo de trabalho de implantação de modelos requer que o código


seja executado em outras máquinas em vez de no local onde o
cientista de dados executa seu trabalho diário. Isso resolve
imediatamente o problema “funciona na minha máquina”. Existem
várias maneiras de lidar com o gerenciamento de diferentes
ambientes consistentemente, que vão da simples à complexa. Uma
maneira simples de fazer isso é usando um arquivo requirements.txt, no
qual todas as dependências são especi cadas. Uma opção mais
complexa, que geralmente é usada em implantações críticas de larga
escala, usa soluções de contêiner como o Docker (https://www.docker.com).
Esse gerenciamento de dependências é muito mais fácil de obter em
Python do que em R.
Uma das ferramentas mais populares para a criação de uma API é o Flask
(https://oreil.ly/6S7ag) da linguagem Python – que é um microframework.
Ele fornece uma interface minimalista que é fácil de estender com outras
ferramentas, como as que fornecem autenticação de usuário ou um design
melhor. Como introdução, percorreremos um pequeno exemplo.
Precisaríamos de uma instalação do Python com algumas con gurações
adicionais como um ambiente virtual e uma GUI para consultar a API.12
Comecemos!

Recentemente surgiram concorrentes do Flask. Eles servem à mesma


nalidade, mas dão mais ênfase ao ML. Dois exemplos populares são
o BentoML (https://www.bentoml.ai) e o FastAPI (https://FastAPI.tiangolo.com).
Esses frameworks fornecem algumas opções adicionais que facilitam
a implantação do ML. Lembre-se de que inicialmente o Flask foi
construído para APIs de desenvolvimento web, e as necessidades de
um projeto de ML podem ser diferentes.
Construiremos uma API para prever preços na área da habitação.13 É
sempre prudente começar com o objetivo nal em mente e como
queremos que esse modelo preditivo seja usado por uma aplicação
externa ou um usuário nal. Nesse caso, podemos supor que nossa API
será integrada a um portal de aluguéis de casas online.
Para sermos breves, omitiremos a parte de treinamento do modelo.
Suponhamos que você tenha seguido um desenvolvimento de modelo
tradicional do scikit-learn. Os resultados do modelo preditivo estarão
armazenados em um .pkl (objeto Pickle, a maneira padrão de o Python
armazenar objetos em disco). Esse processo chama-se serialização e temos
de executá-lo para usar o modelo na API posteriormente:
import pickle

# parte de preparação e treinamento do modelo


# ...

# serialização do modelo
outfile = open("models/regr.pkl", "wb")
pickle.dump(regr, outfile)
outfile.close()

print("Model trained & stored!")

Podemos salvar esse código em um script chamado train_model.py. Ao


executarmos com python train_model.py, o modelo do objeto pickle
será produzido e salvo. A Figura 5.7 fornece uma visão geral de como os
diferentes componentes interagem.

Figura 5.7: Exemplo de arquitetura de uma API de ML.


Em nosso exemplo, a API fornece uma única funcionalidade – a
habilidade de prever o preço de moradias em um conjunto de dados.
Geralmente, no mundo real, a mesma aplicação poderia ter de fazer
outras coisas. Isso é organizado com a criação de diferentes
endpoints. Por exemplo, poderia haver um endpoint que acionasse
um script de preparação de dados e um separado de inferência.
Agora usaremos o Flask:
import pickle
import numpy as np
from ast import literal_eval ❶
from flask import Flask, request, jsonify

app = Flask(__name__) ❷
infile = open("models/regr.pkl", "rb") ❸
regr = pickle.load(infile)
infile.close()

@app.route('/')❹
def predict(methods=["GET"]):
payload = request.json["data"]
input_data = np.array(literal_eval(payload)).reshape(1, -1)
prediction = regr.predict(input_data) ❺
return jsonify({
"prediction": round(float(prediction), 3) ❻
})

if __name__ == '__main__':
app.run(debug=True)

❶ Usamos essa função para especi car que o objeto string payload é na
verdade um dicionário.
❷ Aqui criamos um objeto que contém a aplicação.
❸ Nessas linhas carregamos o modelo serializado.
❹ Este decorador Python cria um endpoint.
❺ Nesta etapa, o modelo serializado é usado para inferência.
❻ Os resultados da inferência são retornados em um formato JSON.
Esse código será adicionado a um arquivo app.py. Quando você executar
esse script, a linha de comando exibirá uma URL local. Poderemos então
usar uma ferramenta como o Postman para consultá-la.14 Examine a
Figura 5.8 para ver como essa consulta funciona. E aí está – construímos
uma API de ML!

Figura 5.8: Consultando a API de ML com o Postman.


Implantação na nuvem
Após você terminar de escrever e testar o código da API de ML, a
próxima fase seria implantá-la. É claro que você poderia usar seu
computador como um servidor e expô-la na internet, mas sabemos
que isso não é muito escalável (é preciso manter a máquina ligada, e
ela pode car sem recursos). Uma das alterações signi cativas vistas
nos últimos anos em termos de ferramentas de DE foi o advento da
computação em nuvem. Plataformas de nuvem como a AWS ou o GCP
(Google Cloud Provider) fornecem ótimas oportunidades e implantam
aplicações. Sua API do Flask pode ser implantada por meio de um
serviço de nuvem como o AWS Elastic Beanstalk (https://oreil.ly/emrdf) ou o
Google App Engine (https://oreil.ly/bgPHa).
Devido à natureza “aglutinadora” dos pacotes Python, eles são
predominantes no uxo de trabalho de DE. Se um cientista de dados
puder escrever essas aplicações por conta própria em Python, o sucesso do
projeto de dados inteiro estará garantido.

Relatórios
Qualquer cientista de dados sabe (talvez até mais do que gostaria) como a
comunicação é vital para seu trabalho diário. Ela também costuma ser
uma habilidade subestimada; logo, nunca é demais enfatizar sua
importância. Bem, o que seria mais importante do que um dos produtos
essenciais de um projeto de ciência de dados – relatar seus resultados?
Existem vários métodos de apresentação de relatórios disponíveis. O caso
de uso mais típico para um cientista de dados é a criação de um
documento, ou de um conjunto de slides, contendo os resultados da
análise que ele executou em um conjunto de dados. Geralmente se trata
de uma coleção de visualizações com um texto associado e uma narrativa
consistente (isto é, percorrendo os diferentes estágios do ciclo de vida de
um projeto – importação, limpeza e visualização dos dados). Há outras
situações em que o relatório tem de ser exibido com frequência e
atualizado em tempo real, caso em que ele é chamado de painel. E, para
concluir, alguns relatórios permitem que o usuário nal os examine mais
interativamente. Veremos esses três tipos de relatórios nas próximas
subseções.

Relatório estático
A popularização da linguagem de marcação (markdown) ajudou os
cientistas de dados a se concentrarem em escrever código e a pensarem em
tarefas relacionadas em vez de precisarem se preocupar com a ferramenta
propriamente dita. Uma versão dessa linguagem, o RMD (R Markdown),
é amplamente empregada na comunidade R. Isso permite o uso do
conceito de programação literária, em que o código é combinado à análise.
O IDE RStudio fornece ainda mais funcionalidades com ferramentas
como os R Notebooks (https://oreil.ly/3STa2). Veja como é fácil escrever
um relatório RMD:
# Analisando Guerra nas Estrelas
First we start by importing the data.

```{r}
library(dplyr)

data(starwars)
```

Then we can have a look at the result.

Esse arquivo.rmd pode então ser compilado (com knit) em um arquivo


.pdf ou .html (melhor para diagramas interativos), criando um elegante
relatório. Existem templates adicionais para a criação até mesmo de slides,
painéis e sites a partir de um arquivo RMD. Examine a Figura 5.9 para ver
tudo isso em ação.

Figura 5.9: Edição do R Markdown dentro do RStudio.


Como ocorre com tudo no universo open source, cientistas de dados do
mundo todo deram contribuições para o maior desenvolvimento do
RMD, permitindo que os usuários criem de um relatório de estilo
personalizado a um blog gerado dinamicamente.

A alternativa ao RMD amplamente adotada no universo Python é o


Jupyter Notebook (https://jupyter.org) (e sua versão mais recente: o
Jupyter Lab (https://oreil.ly/VgfmD)). O “r” de Jupyter representa a
linguagem R, e com certeza é possível usá-la, mas achamos que os
notebooks do RMD no RStudio fornecem uma interface melhor, pelo
menos para o trabalho com o R.

Relatório interativo
E se quiséssemos deixar que os destinatários de nossos relatórios também
zessem algo? Se permitirmos o uso de alguma interatividade, nossos
usuários nais poderão responder a perguntas por conta própria sem
precisar que alteremos o código e geremos novamente os grá cos. Existem
várias ferramentas disponíveis,15 mas a maioria delas é ofuscada pela
facilidade de uso e os recursos do pacote shiny do R.16
Usar essa ferramenta requer uma maneira um pouco diferente de escrever
código R, mas você criará aplicações fantásticas quando se acostumar
com ela. Percorreremos um exemplo básico, porém prático. As aplicações
shiny são compostas de dois elementos fundamentais: a UI (user
interface, interface de usuário) e a lógica do servidor. Geralmente eles
cam separados em dois arquivos. Para simpli car, usaremos o layout de
arquivo único e usaremos duas funções para a aplicação:
library(shiny)

ui <- fluidPage( ❶
titlePanel("StarWars Characters"),

sidebarLayout(
sidebarPanel(
numericInput("height", "Minimum Height:", 0, min = 1, max = 1000),

numericInput("weight", "Minimum Weight:", 0, min = 1, max = 1000),
hr(),
helpText("Data from dplyr package.")
),

mainPanel(
plotOutput("distPlot") ❸
)
)
)

❶ Essa função especi ca que queremos um layout “ uido” que torne a


aplicação responsiva – fácil de ler em vários dispositivos, como os
smartphones.
❷ Adiciona a entrada dinâmica para o usuário.
❸ Adiciona uma área dedicada para a saída.
O objeto ui contém todas as partes do front-end da aplicação. A
computação ocorre realmente na função a seguir; adicionaremos a função
ggplot da seção de DV:
server <- function(input, output) { ❶
output$distPlot <- renderPlot({ ❷
starwars_filtered <- starwars %>%
filter(height > input$height & mass > input$weight)❸
ggplot(starwars_filtered, aes(x = height, y = mass, fill = gender)) +
geom_point(pch = 21, size = 5) +
theme_light() +
geom_smooth(method = "lm") +
labs(x = "Height", y = "Mass",
title = "StarWars Characters Mass vs Height Comparison",
subtitle = "Each dot represents a separate character",
caption = "Data Source: starwars (dplyr)")❹
})
}

❶ O servidor precisa de duas coisas: entrada e saída.


❷ No nosso caso existe apenas uma saída.
❸ Podemos adicionar qualquer tipo de computação feita com o R aqui,
como em um script R comum.
❹ O item mais recente (nesse caso, um diagrama) é retornado para
exibição no front-end.
A computação ocorre nessa função. Para nalizar, precisamos passar essas
duas funções para shinyApp para vê-la em ação. Essa etapa inicia o back-
end shiny, que suporta as entradas provenientes da função de ui com as
computações de server. Os resultados são mostrados na Figura 5.10.
shinyApp(ui = ui, server = server)
Figura 5.10: Relatório interativo com o shiny.
Uma diferença de nossa aplicação shiny que pode torná-la mais difícil de
usar do que nossos arquivos com marcação é que você precisaria
hospedar a aplicação em uma máquina remota. Para usar um .rmd
comum para os arquivos, você terá de compilar o arquivo em um PDF e,
então, compartilhá-lo. A maneira de implantar essas aplicações não faz
parte do escopo deste livro.
Criar relatórios é um componente pequeno, porém vital, do trabalho de
ciência de dados. É assim que seu trabalho será mostrado para o mundo
externo, seja seu gerente ou outro departamento. Mesmo se você tiver feito
um ótimo trabalho em sua análise, ela será julgada por sua habilidade em
comunicar o processo e os resultados. Ferramentas de programação
literária como o RMD e os relatórios interativos mais avançados do shiny
podem ajudar a criar relatórios de ponta. No último capítulo deste livro, o
Capítulo 7, forneceremos um bom exemplo disso na prática.

Considerações nais
Neste capítulo, percorremos os uxos de trabalho essenciais de um
projeto de ciência de dados em R e Python. Em termos de EDA e de
relatórios, o R pode ser coroado rei. Pacotes como o ggplot2 não têm
concorrentes na comunidade da ciência de dados, e o shiny permite o uso
de maneiras novas e fascinantes de apresentação de resultados de ciência
de dados para stakeholders e colegas. Nos universos do ML e da DE, a
natureza aglutinadora do Python fornece ótimas opções, permitindo que
os cientistas de dados modernos se concentrem no trabalho, e não nas
ferramentas.

1 É um pouco injusto apresentar o matplotlib como a única alternativa viável do Python. O pacote
seaborn (https://seaborn.pydata.org) também permite a criação de belos diagramas rapidamente,
mas mesmo assim não chega ao nível dos recursos do ggplot. Versões mais novas do pandas
também têm recursos de diagramação; logo, é preciso prestar atenção nisso.
2 Foram feitas tentativas de recriar esse pacote em Python, como a do ggplot
(https://oreil.ly/z4AoA), mas até agora elas não empolgaram a comunidade.
3 Ele criou vários outros pacotes, e em certos aspectos alterou quase sozinho a maneira de as
pessoas usarem o R em um contexto moderno. Consulte o Capítulo 2 e The Grammar of
Graphics, de Leland Wilkinson et. al. (Springer), para obter mais informações sobre seus pacotes.
4 Mais informações sobre o conjunto de dados estão disponíveis na Documentação de Pacotes R
(https://oreil.ly/JXsJg).
5 Sabia que seu nome real é Jabba Desilijic Tiure?
6 Examine a documentação o cial para ver diferentes estilos de mapa.
7 Uma visão geral deles está disponível no scikit-learn.
8 Para os leitores iniciantes em ML, a aprendizagem supervisionada está relacionada a tarefas de
previsão em que um alvo está disponível (o rótulo), em comparação com a aprendizagem não
supervisionada na qual ele inexiste, e a tarefa de previsão se dá na descoberta de grupos nos
dados.
9 Para ver a complexidade das arquiteturas de ML, consulte o documento MLOps do Google.
10 Para saber mais sobre o que é REST, consulte a página da Wikipedia (https://oreil.ly/OPaFE).
11 A alternativa do R para o Flask é o plumber. O IDE RStudio fornece uma interface amigável
para o uso dessa ferramenta, mas, mesmo assim, ela ainda ca para trás nas opções e na adoção
pela comunidade de ML.
12 Para sermos breves, não nos aprofundaremos na instalação de ambientes virtuais aqui. Pedimos
ao leitor dedicado que se informe sobre as ferramentas virtualenv (https://oreil.ly/oqn0Y) e renv
(https://oreil.ly/7szIG), abordadas no Capítulo 3.
13 O conjunto de dados é “Boston Housing”, disponível no scikit-learn (https://oreil.ly/xpTI6).
14 Se você é uma pessoa mais acostumada à linha de comando, tente usar o curl.
15 Existe uma ferramenta nova e avançada em Python, chamada Streamlit
(https://www.streamlit.io), mas ela ainda não se popularizou ou foi adotada.
16 Para se maravilhar com o que é possível fazer no shiny, examine a galeria de casos de uso no site
do RStudio (https://oreil.ly/DVbDd).
PARTE IV

Bilinguismo III: Tornando-se sinérgico

Até agora neste livro exploramos as duas linguagens em casos bem


isolados. Aprendemos a conhecer uma a partir da outra, e em que
formatos de dados e uxos de trabalho elas se saem melhor. Em todos
esses casos, a separação era clara – para algumas tarefas, R é melhor, e
para outras sua contrapartida de uso geral Python é uma opção mais
adequada.
Capítulo 6
Nesse capítulo, seguiremos uma perspectiva diferente – que pode ser a
precursora de uma nova maneira de trabalhar com linguagens de
programação no futuro.
Capítulo 7
Já que esse é o último capítulo do livro, é um momento apropriado para
aplicarmos todos os conceitos que aprendemos até aqui. Faremos isso
percorrendo um estudo de caso de ciência de dados bilingue do mundo
real.
CAPÍTULO 6

Usando as duas linguagens


sinergicamente

Rick J. Scavetta
A interoperabilidade, a possibilidade de diferentes linguagens de
programação operarem em conjunto, é um pilar da computação. O ideal é
que os objetos possam ser compartilhados diretamente entre as duas
linguagens. Como era de se esperar, isso pode ser problemático por várias
razões, como o uso da memória e estruturas de armazenamento de dados
incompatíveis, para citar apenas duas. Embora tenham sido feitas muitas
tentativas de se implementar essa abordagem de maneira menos
traumática entre Python e R, só nos últimos anos é que um kit
razoavelmente funcional foi nalizado. Discutirei isso em
“Interoperabilidade”, na página 139. No entanto, seria útil antes voltarmos
ao básico. Além de nos dar um contexto para apreciarmos a
interoperabilidade sem problemas posteriormente, essa decisão fornecerá
uma solução básica que talvez já atenda às suas necessidades. Contudo, se
você já quiser começar a lidar com a interoperabilidade, pode pular a
próxima seção.

Falsa operabilidade
O tipo mais básico de interoperabilidade, às vezes chamada de cross-talk,
na verdade é uma falsa operabilidade. Executamos scripts prede nidos
entre linguagens, passando informações entre elas usando arquivos como
intermediários. Imagine a situação a seguir, que diagramamos na Figura
6.1.
Figura 6.1: Exemplo de cross-talk para facilitar a interoperabilidade.
Em R, após fazer algum trabalho necessário em um objeto, por exemplo,
PlantGrowth, executaríamos:
# (As interessantes e complicadas etapas anteriores foram omitidas)

# Grava um data.frame de interesse em um arquivo ...


rio::export(PlantGrowth, "pg.csv")

# ... que é então processado por um script Python


system('~/.venv/bin/python3 myScript_2.py < "pg.csv"')
A função system() executa um comando do sistema, fornecido como um
argumento de caracteres. O comando é composto de quatro partes.
Em primeiro lugar, ~/.venv/bin/python3 é a localização do executável
Python dentro de nosso ambiente virtual, supondo que você tenha criado
um. Também poderíamos ter incluído esse caminho na primeira linha
shebang do script como #!/.venv/bin/env python3. Isso assegura que o
script seja executado no ambiente em que foi criado. Consulte “Ambientes
virtuais”, na página 69, se achar estranho.
Em segundo lugar, myScript_2.py é o nome do arquivo Python que
contém os comandos que queremos executar.
Em terceiro lugar, < permite redirecionar stdin do rhs (lado direito) para
o arquivo no lhs (lado esquerdo). 1
Em quarto lugar, “pg.csv” é a stdin. Você deve se lembrar de que existem
três canais, ou uxos, padrão para funções de linha de comando: stdin
para a entrada padrão, stdout para a saída padrão e stderr para o erro
padrão. Aqui, stdin está embutida no código. É uma string de caracteres
que corresponde a um arquivo: “pg.csv”, que foi exportado no comando
anterior. Embutir elementos no código deve ser evitado e temos certeza de
que você pode imaginar muitas maneiras de tornar a entrada dinâmica.
No entanto, não é isso que importa agora; o importante é fornecer
alguma entrada para um script Python.
Estamos executando um script Python que pega stdin de dentro de um
script R, e a própria stdin é um produto do script R. Examinemos os
componentes mínimos desse script Python:
import sys
import pandas as pd

# importa o arquivo especificado pela entrada padrão


myFile = pd.read_csv(sys.stdin)

# (Um código muito complexo e Pythônico foi omitido)

# Grava as quatro primeiras linhas em um arquivo


myFile.head(4).to_csv("pg_small.csv")

Antes de mais nada, precisamos do módulo sys para manipular stdin


(sys.stdin). Importamos o arquivo representado por sys.stdin usando
o pandas e, depois de nosso script Python fazer sua mágica, exportamos
outra saída usando o método to_csv().
Temos várias coisas erradas nesse método e iremos examiná-las em breve.
O importante é que ele funciona, e às vezes é exatamente disso que
precisamos. Quando eu trabalhava em um laboratório de pesquisa, com
frequência tinha de fornecer resultados para os colegas rapidamente. E era
realmente assim, já que culturas celulares muito caras morreriam e o
trabalho de uma semana seria perdido se os resultados não estivessem
prontos. O pré-processamento de dados proprietários brutos e o acesso a
um servidor seguro não deixavam meus colegas executarem scripts R
automatizados. A solução que vislumbrei foi, em primeiro lugar, processar
os dados proprietários gerados por máquina com um software
especializado para a tarefa. Em seguida, pude usar o serviço Automator
do macOS para executar um script Perl nessa saída, que agora era minha
stdin. Esse script Perl chamava então um script R que produzia um
arquivo de diagrama com todas as informações relevantes claramente
exibidas no título. Não era a solução mais aberta ou elegante, mas
funcionou e obtive meus diagramas com um único clique do mouse em
meio segundo sem nenhum site ou login adicional. Deu tudo certo, então
qual é o problema?
Bem, há vários problemas. Consideraremos três.
Em primeiro lugar, pensando bem, eu poderia ter executado o uxo de
trabalho inteiro em R (com exceção do pré-processamento dos dados
proprietários). É necessário considerar a simpli cação do uxo de
trabalho e haver uma boa razão para o uso de mais de uma linguagem.
Decidir quando e por que combinar Python e R é algo que temos
discutido no decorrer deste livro.
Em segundo lugar, há muitas peças soltas. Temos vários arquivos e
estamos até mesmo produzindo arquivos intermediários adicionais. Isso
aumenta as chances de haver erro e confusão. Não é algo terrível, mas o
melhor é tomar cuidado e manter tudo organizado.
Em terceiro lugar, esse uxo de trabalho funcionará bem quando
pudermos exportar um data.frame R como um arquivo CSV, o que o
pandas pode importar facilmente. Para estruturas de dados mais
complexas, você pode exportar um ou mais objetos R como um arquivo
de formato RDATA ou RDS. O pacote Python pyreadr fornece funções
para a importação desses arquivos e dá acesso a cada objeto armazenado
em um dict.
O cross-talk é ótimo, mas a interoperabilidade real resolve os problemas
desse processo com muita facilidade. Existem dois frameworks
amplamente usados; a escolha de qual usar vai depender da linguagem
que você estiver usando como ponto de partida.
Interoperabilidade
Se você estiver usando principalmente R e quiser acesso ao Python, usar o
pacote R reticulate é a maneira de fazê-lo. Inversamente, se estiver usando
principalmente Python e quiser acesso ao R, então o módulo Python rpy2
é a ferramenta certa. Isso está resumido nas tabelas 6.1 e 6.2.2 Em cada
tabela, leia cada linha como uma frase começando com os cabeçalhos das
colunas.
Tabela 6.1: Interoperabilidade fornecida pelo reticulate
Acesse Usando o comando
Funções Python em R, pd <- library(pandas); pd$read_csv()
Objetos Python em R, py$objName
Objetos R em Python, r.objName

Tabela 6.2: Interoperabilidade fornecida pelo rpy2 quando escrevemos em


Python
Acesse Usando o comando
Funções R em Python, import rpy2.robjects.lib.ggplot2 as ggplot2
Pacotes R em Python, r_cluster = importr('cluster')
Objetos R em Python, foo_py = robjects.r['foo_r']

Os comandos das tabelas 6.1 e 6.2 revelam como acessar todas as


variedades de objetos de uma linguagem diretamente a partir da outra. E
também podemos chamar as funções diretamente. Isso é um avanço, já
que não temos de forçar uma linguagem a executar tarefas nas quais ela
não é boa e signi ca que não precisamos reinventar a roda, introduzindo
redundância entre as linguagens. Quando este texto foi escrito, não era
possível acessar funções R de dentro do Python no reticulate. Você pode
tentar usar o reticulate para essa tarefa, mas seria mais fácil passar um
objeto de volta para o R e executar os comandos R nativamente.
O reticulate apareceu pela primeira vez no CRAN em 2017, e recentemente
sua popularidade aumentou devido ao seu amadurecimento. Esse pacote
foi desenvolvido pela RStudio, tendo sido integrado ao IDE de mesmo
nome, o que é muito conveniente. No entanto, quando este texto foi
escrito, havia recursos problemáticos (bugs?) que demandavam algum
cuidado (consulte a caixa de aviso a seguir). Uma boa etapa inicial seria
assegurar-se de usar a última versão pública do RStudio e a última versão
do pacote reticulate e de qualquer pacote associado, como o knitr.

O reticulate tem um bom suporte e é su cientemente estável para ser


usado na produção. No entanto, você pode encontrar problemas
dependendo das versões do seu sistema e do software. Já que essa
ferramenta combina tecnologias, ela também pode ser difícil de
depurar, e a documentação ainda é um pouco escassa. Fique
atualizado com as novas versões à medida que forem lançadas. Se
encontrar problemas em sua máquina local, acesse nosso projeto no
RStudio Cloud (https://rstudio.cloud/project/2534578).
Nesta seção começaremos com dois scripts, listados na Tabela 6.3. Você os
encontrará na pasta deste capítulo no repositório do livro
(https://github.com/moderndatadesign/PyR4MDS).
Tabela 6.3: Trabalhando com o reticulate
Arquivo Descrição
0 - Setup.Rmd Instalação do reticulate e de ambientes virtuais
1 - Activate.R Ativação de um ambiente virtual Python

Começaremos com o script R 0 - Setup.R. Certi que-se de instalar o


reticulate e inicializá-lo em seu ambiente:
library(reticulate)

Em primeiro lugar, precisamos especi car que build do Python usaremos.


Você pode deixar o R usar o padrão do seu sistema ou de nir a build
especí ca do Python que deseja usar acessando Tools > “Project options”
e selecionando o ícone do Python (consulte a Figura 6.2).
Figura 6.2: Selecionando a versão e a build do Python que serão usadas.
Veri caremos a versão que estamos usando:
reticulate::py_config()
python: /usr/local/bin/python3.8
libpython: /Library/Frameworks/Python.framework/Versions/3.8...
pythonhome: /Library/Frameworks/Python.framework/Versions/3.8...
version: 3.8.6 (v3.8.6:db455296be, Sep 23 2020, 13:31:39) ...
numpy: [NOT FOUND]
sys: [builtin module]
Na verdade, não precisamos usar o RStudio para de nir a versão do
Python. Esse é apenas um recurso de conveniência. Poderíamos ter
executado:
use_python("/usr/local/bin/python3.8", required = TRUE)

Observe que essa função faz apenas uma sugestão e não resulta em erro se
a build desejada não for encontrada, a menos que o argumento required
seja con gurado com TRUE.
Antes de continuar, queremos estabelecer um ambiente virtual. Se você
estiver no Windows, terá de usar um ambiente conda, que veremos em
breve. Caso contrário, use o comando a seguir para criar um ambiente
virtual chamado modern_data:
virtualenv_create("modern_data")

Anteriormente, quando usamos o pacote venv em Python, o ambiente


virtual foi armazenado como um diretório oculto (normalmente chamado
de .venv no diretório do projeto). Onde estão os ambientes virtuais
Python agora? Podemos ver com o comando a seguir:
virtualenv_root()
[1] "~/.virtualenvs"

Estão todos armazenados em uma pasta oculta no diretório root.


Podemos ver todos os nossos ambientes virtuais usando este comando:
virtualenv_list()

Como ocorre com os pacotes de ciência de dados mais populares, há


um resumo de referência disponível para reticulate. Você pode baixá-
lo diretamente do RStudio
(https://raw.githubusercontent.com/rstudio/cheatsheets/master/reticulate.pdf).
Isso é diferente do que vimos com os ambientes virtuais em Python, que
são armazenados dentro do diretório do projeto. No entanto, é
conveniente, já que podemos reutilizar facilmente um bom ambiente em
muitos projetos.
Observe que, para remover um ambiente virtual, precisamos passar o
caminho propriamente dito:
virtualenv_remove("~/modern_data")

A próxima etapa é instalar os pacotes apropriados:


virtualenv_install("modern_data", "pandas")

Alternativamente, você pode usar a função purrr::map() do Tidyverse


para instalar muitos pacotes:
library(tidyverse)
c("scikit-learn", "pandas", "seaborn") %>%
map(~ virtualenv_install("modern_data", .))

Se você estiver no Windows, use os comandos a seguir:


# Para usuários do windows:
# Instala uma versão mínima do Conda
install_miniconda()

# Lista seus ambientes virtuais do Conda


conda_list()

# Cria um novo ambiente virtual


conda_create("modern_data")

# Instala apenas um...


conda_install("modern_data", "scikit-learn")

#...ou vários pacotes:


library(tidyverse)
c("scikit-learn", "pandas", "seaborn") %>%
map(~ conda_install("modern_data", .))

A última etapa é ativar nosso ambiente virtual. Essa parece ser uma área
que está em rápido desenvolvimento. Diferentes mensagens de erro, ou
nenhuma, serão produzidas dependendo de suas versões do reticulate e
do RStudio, tornando-os mais difíceis de depurar. Pela minha experiência,
a melhor opção é (i) certi car-se de que todos os seus pacotes R, assim
como o RStudio, estejam atualizados e (ii) reiniciar o R antes de ativar seu
ambiente virtual. Você pode fazer isso no menu Session > Restart R do
RStudio, com o atalho de teclado Shift-Command/Ctrl + F10, ou
executando o comando .rs.restartR(). Também pode fechar e reiniciar
literalmente o RStudio. Isso assegurará que não haja nenhuma build
Python em uso ativo e poderemos estabelecer uma a partir do zero. Logo,
temos um script R para instalação, onde criaremos um ambiente virtual e
instalaremos os pacotes, e outro com nossa análise real, onde
carregaremos o reticulate e ativaremos nosso ambiente virtual.
library(reticulate)
use_virtualenv("modern_data", required = TRUE)

# Alternativamente, para o miniconda:


# use_miniconda("modern_data")

E, para concluir, podemos con rmar que build estamos usando:


py_config()
Você deve ver a saída a seguir. O importante é veri car se o caminho de
seu ambiente virtual está sendo informado na primeira linha:
/.virtualenvs/modern_data/bin/python.
python: /Users/user_name/.virtualenvs/modern_data/bin/python
libpython: /Library/Frameworks/Python.framework/Versions/3.8...
pythonhome: /Users/user_name/.virtualenvs/modern_data...
version: 3.8.6 (v3.8.6:db455296be, Sep 23 2020, 13:31:39)
numpy: /Users/user_name/.virtualenvs/modern_data/lib/python3.8/...
numpy_version: 1.20.1

Se aparecer algo como /usr/local/bin/python3.8, então o RStudio


ainda está sendo direcionado para usar a versão do Python que você
de niu no começo do capítulo, e não um ambiente virtual. Isso pode
servir, mas é preferível usar um ambiente virtual.

Aprofundando-se
Já criamos um ambiente virtual, instalamos alguns pacotes nele,
reiniciamos o R e ativamos o ambiente virtual. Essas etapas são
abordadas nos scripts 0 - Setup.R e 1 - Activate.R. No restante desta
seção discutirei maneiras de passar informações entre R e Python, o que
resumi na Tabela 6.4.
Tabela 6.4: Interoperabilidade fornecida pelo reticulate
Arquivo Descrição
2 - Passing objects.Rmd Passa objetos entre R e Python em um documento R Markdown
3 - Using functions.Rmd Chama Python em um documento R Markdown
4 - Calling scripts.Rmd Chama Python usando como fonte um script Python
5 - Interactive mode.R Chama Python usando um console REPL Python
6 - Interactive document.Rmd Chama Python com entrada dinâmica em um documento interativo

Por que “reticulate”? A píton-reticulada é uma espécie encontrada no


sudeste da Ásia. Elas são as cobras e os répteis mais longos do
mundo. O nome da espécie, Malayopython reticulatus, vem do latim e
signi ca “como uma rede” ou reticulado e é uma referência ao padrão
de cores complexo.
Considerarei os cenários da Tabela 6.1 nas próximas subseções. Para
acompanhar esses exemplos, certi que-se de ter seguido as instruções de
instalação e ativação encontradas em 0 - Setup.R e 1 - Activate.R –
os dois arquivos estão no repositório de códigos do livro
(https://github.com/moderndatadesign/PyR4MDS). É preciso que você esteja
com o ambiente virtual modern_data e a lista de pacotes anterior
instalados. Se estiver usando o Miniconda, certi que-se de empregar o
comando correto fornecido em cada arquivo para ativar seu ambiente
virtual.

Passe objetos entre R e Python em um documento R Markdown


Os comandos a seguir podem ser encontrados no arquivo 2 - Passing
objects.Rmd. Para acessar um objeto R em Python, use o objeto r, e para
acessar um objeto Python em R, use o objeto py. Considere os blocos a
seguir, que se encontram em um documento R Markdown:
```{python}
a = 3.14
a
```
```{r}
py$a
```

O objeto python a é acessado no objeto R py com o uso da notação $. Na


direção oposta:
```{r}
b <- 42
b
```
```{python}
r.b
```

Em Python, chame o objeto r e use a notação . para acessar objetos R


pelo nome. Esses são escalares, ou vetores simples, mas é claro que
podemos passar itens mais complexos diretamente entre as duas
linguagens. reticulate se encarregará da conversão do objeto para nós.
Considere o caso a seguir:
```{r}
# Um data frame interno
head(PlantGrowth)
```
```{python}
r.PlantGrowth.head()
```

Um data.frame R é acessado como um pd.DataFrame Python. No


entanto, se você não estiver com o pandas instalado, verá um objeto dict,
um dicionário Python.
Um ndarray do NumPy Python é convertido para uma matriz do R:3
```{python eval = TRUE}
from sklearn.datasets import load_iris

iris = load_iris()
iris.data[:6]
```

Um ndarray do NumPy Python como uma matriz do R:


```{r eval = TRUE}
head(py$iris$data)
```

Observe como a notação . do Python, usada em iris.data, pode ser


acessada automaticamente com o uso da notação $ do R: py$iris$data.
Isso também ocorre para objetos, métodos e atributos aninhados, como
ocorreria em Python.

Chame Python em um documento R Markdown


Os comandos a seguir podem ser encontrados no arquivo 3 - Using
functions.Rmd. Continuaremos usando o clássico conjunto de dados iris
que acessamos em Python na seção anterior. Dentro de um documento R
Markdown, acessaremos uma função Python, que nos permitirá acessar o
classi cador treinado de máquina de vetores de suporte para prever a
classi cação com novos valores. Esse é um uxo de trabalho simples de
machine learning que não tem como objetivo produzir um modelo útil. O
importante é demonstrar como acessar um modelo a partir de Python em
R.
A con guração do modelo inteira é de nida aqui:
```{python}
# importa módulos
from sklearn import datasets
from sklearn.svm import SVC

# carrega os dados:
iris = datasets.load_iris()

# Cria uma instância da classe SVC, _Support Vector Classification_.


clf = SVC()

# Treina o modelo chamando o método fit nos dados de destino, usando os nomes
de destino
clf.fit(iris.data, iris.target_names[iris.target])

# Prevê a classe dos novos valores, aqui dos três primeiros


clf.predict(iris.data[:3])
```

O método clf.predict() recebe um ndarray como entrada e retorna a


classi cação nomeada. Para acessar essa função em R, podemos usar
novamente o objeto py, como em py$clf$predict(). O conjunto de
dados iris do R é um data.frame, no qual a quinta coluna é a
classi cação. Devemos fazer a conversão para um objeto Python usando
r_to_py(), nesse caso excluindo a quinta coluna.
```{r}
py$clf$predict(r_to_py(iris[-5]))
```

Chame Python usando como fonte um script Python


Os comandos a seguir podem ser encontrados nos arquivos 4 - Calling
scripts.Rmd e 4b - Calling scripts.R. Nesse cenário, executaremos um script
Python inteiro e acessaremos todos os objetos e funções disponíveis nele.
Para fazê-lo, podemos chamar:
source_python("SVC_iris.py")

Esse código funcionará bem tanto em um documento R Markdown


quanto em um script.
Embora essa abordagem se pareça muito com a da seção anterior, há uma
diferença importante. Ambientes Python ativados dessa maneira
fornecem as funções e os objetos diretamente. Logo, podemos chamar:
clf$predict(r_to_py(iris[-5]))

Isso é conveniente, mas também é confuso. Além de a sintaxe ter mudado,


ou seja, não precisamos usar py$, os objetos carregados no ambiente R
podem entrar em con ito. Os objetos Python mascararão os objetos R,
logo tenha muito cuidado com con itos de nomeação! Você notará que
em SVC_iris.py renomeamos o conjunto de dados iris do Python
como iris_py para evitar problemas ao chamar iris em R.

Chame Python usando o REPL


Os comandos a seguir podem ser encontrados no arquivo 5 - Interactive
mode.R. Nesse cenário, iniciaremos um console REPL do Python, usando
este comando:
repl_python()

REPL é a abreviação de read-eval-print loop. É um recurso comum


em muitas linguagens com o qual o usuário pode fazer experimentos
de maneira interativa, em vez de escrever um script que precise ser
executado.
Isso permitirá que você execute comandos Python diretamente em um
interpretador. Por exemplo, tente executar os comandos que vimos no
último caso:
from sklearn import datasets
from sklearn.svm import SVC
iris = datasets.load_iris()
clf = SVC()
clf.fit(iris.data, iris.target_names[iris.target])
clf.predict(iris.data[:3])

Podemos sair do interpretador executando o comando exit do Python:


exit

Como vimos, as funções e os objetos desse ambiente Python podem ser


acessados em R. Essa é uma programação realmente interativa porque
estamos executando comandos diretamente no console. Embora
tenhamos apresentado esse cenário a título de completude, na verdade
repl_python() não deve ser usada na prática diária. Ela é a função que
chamamos quando um bloco do R Markdown usa um kernel Python.
Logo, você pode fazer isso, mas tenha cuidado! É uma ação que apresenta
um problema considerável na reprodutibilidade e automação, mas pode
ser útil para a veri cação rápida de alguns comandos.

Chame Python com entrada dinâmica em um documento interativo


Os comandos a seguir podem ser encontrados no arquivo 6 - Interactive
document.Rmd.
Já vimos toda a funcionalidade básica do reticulate. Aqui iremos além e
mostraremos uma maneira muito simples de introduzir interatividade
usando um runtime shiny em um documento R Markdown. Para ver a
interatividade, certi que-se de ter o pacote shiny instalado e de renderizar
o documento em HTML. No RStudio, você pode fazer isso clicando no
botão Run Document quando o arquivo for aberto.
Em primeiro lugar, precisamos especi car esse novo ambiente de runtime
no cabeçalho de nosso documento:
---
title: "Python & R for the Modern Data Scientist"
subtitle: "A bilingual case study"
runtime: shiny
---

O código Python a seguir, que já vimos, é executado em um bloco


Python:
```{python}
from sklearn import datasets
from sklearn.svm import SVC
iris = datasets.load_iris()

clf = SVC()
clf.fit(iris.data, iris.target_names[iris.target])
```

Nos dois últimos blocos, podemos usar funções do pacote shiny para
produzir uma interface de usuário. Ela será composta tanto da entrada
quanto da saída.
Em primeiro lugar, a entrada. Produzimos um controle deslizante (slider)
para cada um dos quatro recursos usando a função sliderInput() como
mostrado a seguir para sl. Os controles deslizantes de sw, pl e pw são
semelhantes e podem ser encontrados no script do estudo de caso.
sliderInput("sl", label = "Sepal length:",
min = 4.3, max = 7.9, value = 4.5, step = 0.1)

Agora, a saída. Chamamos os valores dos controles deslizantes como


elementos nomeados (sl, sw, pl e pw) em um objeto de lista R chamado
input (input$sl). Esses valores serão usados como entradas da função
predict() de Python. A saída do Python será atribuída à previsão do
objeto R:
prediction <- renderText({
py$clf$predict(
r_to_py(
data.frame(
sl = input$sl,
sw = input$sw,
pl = input$pl,
pw = input$pw)
)
)
})

Para concluir, chamamos o objeto R prediction como um comando


inline, `r prediction`, para exibir o resultado na tela como uma frase.

Considerações nais
Neste capítulo, abordamos os componentes básicos do pacote reticulate,
progredindo da instalação essencial aos aspectos básicos e nalmente a
uma implementação simples, porém poderosa, que demonstrou os pontos
fortes do R, do Python e do reticulate. Usando esse conhecimento,
passaremos para um estudo de caso maior no último capítulo.

1 Lembre-se de que rhs é o right-hand side e lhs é o left-hand side na chamada de operadores, nesse
caso <.
2 Nessas tabelas estamos diferenciando funções e objetos. Lembre-se de que as funções são apenas
objetos, mas não precisamos nos preocupar com esses detalhes no momento.
3 Consulte o Apêndice para ver um resumo das estruturas de dados.
CAPÍTULO 7

Estudo de caso da ciência de dados


bilíngue

Rick J. Scavetta
Boyan Angelov
Neste último capítulo, nosso objetivo é apresentar um estudo de caso que
forneça uma amostra de todos os conceitos e ferramentas que exibimos
no decorrer deste livro. Embora a ciência de dados ofereça uma enorme
diversidade de métodos e aplicações, normalmente usamos apenas um kit
de ferramentas básico em nosso trabalho diário. Logo, é improvável que
você venha a fazer uso de todas as ferramentas apresentadas no livro (ou
nesse estudo de caso). No entanto, isso não é problema! Esperamos que
você dê atenção às partes do estudo de caso que forem mais relevantes
para o seu trabalho e que se sinta inspirado a ser um cientista de dados
bilíngue e moderno.

24 anos e 1,88 milhão de incêndios


Nosso estudo de caso se concentrará no conjunto de dados US Wild res.1
Esse conjunto de dados, lançado pelo USDA (US Department of
Agriculture, Departamento de Agricultura dos Estados Unidos), contém
1,88 milhão de registros de incêndios georreferenciados. Coletivamente,
esses incêndios resultaram na perda de 140 milhões de acres de oresta ao
longo de 24 anos. Se você deseja executar o código deste capítulo, baixe o
conjunto de dados do SQLite diretamente a partir do site do USDA
(https://doi.org/10.2737/RDS-2013-0009.4) ou a partir do Kaggle
(https://oreil.ly/jyCsp) e insira-o dentro do diretório ch07/data.
Existem 39 características, além de outra variável, de forma, no formato
bruto. Muitas delas são identi cadores únicos ou representações
categóricas e contínuas redundantes. Logo, para simpli car nosso estudo
de caso, daremos ênfase a algumas características, listadas na Tabela 7.1.
Tabela 7.1: A tabela Fires contém 39 características que descrevem mais de
1,88 milhão de incêndios nos Estados Unidos de 1992 a 2015
Variável Descrição
STAT_CAUSE_DESCR Causa do incêndio (a variável alvo)
OWNER_CODE Código do principal proprietário da terra
DISCOVERY_DOY Dia do ano em que foi feita a descoberta ou a con rmação do incêndio
FIRE_SIZE Estimativa do tamanho nal do incêndio (acres)
LATITUDE Latitude (NAD83) do incêndio
LONGITUDE Longitude (NAD83) do incêndio

Desenvolveremos um modelo de classi cação para prever a causa de um


incêndio (STAT_CAUSE_CODE) usando as outras cinco características. O
alvo e o modelo são secundários; esse não é um estudo de caso de ML.
Logo, não daremos ênfase a detalhes como validação cruzada ou ajuste de
hiperparâmetros.2 Também nos limitaremos às observações de 2015 e
excluiremos o Havaí e o Alasca para reduzir o conjunto de dados a um
tamanho mais gerenciável. O produto nal de nosso estudo de caso será a
geração de um documento interativo que nos permitirá inserir novos
valores preditores, como mostrado na Figura 7.1.3
Antes de começar, seria bom fazermos uma pausa para considerar a
linhagem de dados – de dados brutos a produto nal. Responder às
perguntas a seguir ajudará a nos orientar.
1. Qual é o produto nal?
2. Como ele será usado e por quem?
3. Podemos dividir o projeto em partes componentes?
4. Como cada componente será construído? Isto é, será em Python ou
em R? Que pacotes adicionais podem ser necessários?
5. Como essas partes componentes operarão em conjunto?
Responder a essas perguntas nos permitirá traçar uma rota dos dados
brutos ao produto nal, provavelmente evitando gargalos durante o
percurso. Para a pergunta 1, já declaramos que queremos construir um
documento interativo. Quanto à segunda pergunta, para simpli car,
presumiremos que o objetivo é podermos inserir facilmente novos valores
para as características e ver a previsão do modelo.
As perguntas 3–5 são as que consideramos neste livro. Na pergunta 3,
imaginamos as partes como uma série de etapas para nosso uxo de
trabalho geral. A pergunta 4 foi abordada nos capítulos 4 e 5. Resumimos
essas etapas na Tabela 7.2.
Tabela 7.2: Etapas de nosso estudo de caso e suas respectivas linguagens
Componente/Etapa Linguagem Pacotes adicionais?
1. Importação de dados R RSQLite, DBI
2. EDA e visualização de dados R ggplot2, GGally, visdat, naniar
3. Engenharia de características Python scikit-learn
4. Machine learning Python scikit-learn
5. Mapeamento R Lea et
6. Interface web interativa R runtime shiny em um R Markdown

Para concluir, a pergunta 5 nos pede para considerar a arquitetura do


projeto. O diagrama apresentado na Figura 7.1 mostra como as etapas da
Tabela 7.2 estarão associadas.

Figura 7.1: Arquitetura do projeto de nosso estudo de caso.


Agora que sabemos para onde vamos, selecionaremos nossas ferramentas
e reuniremos todos os componentes em um todo uni cado.
Preparamos esse estudo de caso usando exclusivamente o IDE
RStudio. Como discutimos no Capítulo 6, quando escrevemos código
em R e acessamos funções Python, essa é a maneira de agir. A razão
são as funcionalidades internas para a execução de blocos de código
Python dentro do R Markdown, os recursos dos painéis Environment
e Plot e, por m, as ferramentas fornecidas pelo shiny.

Preparação e importação de dados


Podemos ver em nosso diagrama que o produto nal será um documento
R Markdown interativo. Logo, começaremos como no Capítulo 5. Nosso
cabeçalho YAML conterá pelo menos o seguinte:4
---
title: "R & Python Case Study"
author: "Python & R for the modern data scientist"
runtime: shiny
---

Para termos uma formatação melhor, excluiremos dos próximos


exemplos os caracteres que especi cam um bloco do R Markdown. É
claro que, se você está acompanhando, terá de adicioná-los.
Já que os dados estão armazenados em um banco de dados SQLite, temos
de usar alguns pacote adicionais além dos que já vimos. Nosso primeiro
bloco de código é:
library(tidyverse)
library(RSQLite) # SQLite
library(DBI) # R Database Interface

Em nosso segundo bloco de código, nos conectaremos com o banco de


dados e listaremos todas as 33 tabelas disponíveis:
# Estabelece conexão com um banco de dados RSQLite na memória
con <- dbConnect(SQLite(), "ch07/data/FPA_FOD_20170508.sqlite")

# Exibe todas as tabelas


dbListTables(con)
Criar um objeto de conexão (con) é uma prática padrão no
estabelecimento de acesso programático a bancos de dados. Ao contrário
do R, o Python tem suporte interno para a abertura desses arquivos com
o pacote sqlite3. Isso é preferível ao R, já que não precisamos instalar e
carregar dois pacotes adicionais. No entanto, o R é uma linguagem básica
para as etapas iniciais, logo também poderíamos importar os dados em R
desde o princípio.
Nossos dados estão armazenados na tabela Fires. Já que conhecemos as
colunas que queremos acessar, podemos especi cá-las na importação.
Também é importante lembrar-se de fechar as conexões ao trabalhar com
bancos de dados remotos ou compartilhados porque isso pode impedir
que outros usuários acessem o banco de dados e causem problemas.5
fires <- dbGetQuery(con, "
SELECT
STAT_CAUSE_DESCR, OWNER_CODE, DISCOVERY_DOY,
FIRE_SIZE, LATITUDE, LONGITUDE
FROM Fires
WHERE (FIRE_YEAR=2015 AND STATE != 'AK' AND STATE !=
'HI');")
dbDisconnect(con)

dim(fires)

Já limitamos o tamanho do nosso conjunto de dados nessa primeira


etapa de importação. É uma pena descartar tantos dados. Mesmo assim,
zemos isso porque dados mais antigos, principalmente em aplicações
meteorológicas, tendem a ser menos representativos da situação atual ou
de um futuro próximo. Previsões baseadas em dados antigos podem ser
inerentemente tendenciosas. Ao limitar o tamanho do conjunto de dados,
também reduzimos a quantidade de memória usada, melhorando o
desempenho.

Geralmente, no caso de conjuntos de dados muito grandes (que não


caibam totalmente ou em parte na memória da máquina), podemos
usar um comando de ingestão para selecionar apenas uma
amostragem, como LIMIT 1000.
Podemos gerar uma visualização dos dados usando a função
dplyr::glimpse() do tidyverse:
glimpse(fires)
Rows: 73,688
Columns: 6
$ STAT_CAUSE_DESCR <chr> "Lightning", "Lightning", "Lightning", "Lightning"…
$ OWNER_CODE <dbl> 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 5, 8, 5,…
$ DISCOVERY_DOY <int> 226, 232, 195, 226, 272, 181, 146, 219, 191, 192, …
$ FIRE_SIZE <dbl> 0.10, 6313.00, 0.25, 0.10, 0.10, 0.25, 0.10, 0.10,…
$ LATITUDE <dbl> 45.93417, 45.51528, 45.72722, 45.45556, 44.41667, …
$ LONGITUDE <dbl> -113.0208, -113.2453, -112.9439, -113.7497, -112.8…

EDA e visualização de dados


Já que o conjunto de dados ainda está relativamente grande, devemos
pensar com cuidado na melhor estratégia de visualização de dados. Nosso
primeiro impulso seria criar um mapa, já que temos as coordenadas de
latitude e longitude. Ele pode ser fornecido para o ggplot2 diretamente
como coordenadas dos eixos x e y desta forma:
g <- ggplot(fires, aes(x = LONGITUDE,
y = LATITUDE,
size = FIRE_SIZE,
color = factor(OWNER_CODE))) +
geom_point(alpha = 0.15, shape = 16) +
scale_size(range = c(0.5, 10)) +
theme_classic() +
theme(legend.position = "bottom",
panel.background = element_rect(fill = "grey10"))

g
Mapeando OWNER_CODE na estética colorida (Figura 7.2), podemos ver a
forte correlação em alguns estados. Podemos prever que isso terá um
efeito signi cativo sobre o desempenho do nosso modelo.
Figura 7.2: Representando os tamanhos de incêndios individuais.
No fragmento de código anterior, atribuímos o diagrama ao objeto g. Isso
não é obrigatório, mas foi o que zemos nesse caso para demonstrar o
poder do método baseado em camadas do ggplot2. Podemos adicionar
uma camada facet_wrap() a esse diagrama e dividi-lo em 13 facetas, ou
pequenos múltiplos, uma para cada tipo de STAT_CAUSE_DESCR (Figura
7.3):
g +
facet_wrap(facets = vars(STAT_CAUSE_DESCR), nrow = 4)
Figura 7.3: Dividindo o diagrama de incêndios em facetas, baseadas na causa
do incêndio.
Isso nos permite saber que algumas causas são abundantes enquanto
outras são raras, uma observação que veremos novamente em breve de
maneira diferente. Também podemos começar a avaliar qualquer
associação mais forte entre, por exemplo, região, código do proprietário e
causa de um incêndio.
Voltando ao conjunto de dados em sua totalidade, uma maneira fácil de
obter uma visão geral abrangente seria usando um pairs plot (diagrama
de pares), às vezes chamado de splom (ou scatter plot matrix se for
composto puramente de dados numéricos). O pacote GGally fornece uma
excelente função, ggpairs(), que produz uma matriz de diagramas
(Figura 7.4).6 Cada diagrama bivariado em pares é mostrado como
diagramas de densidade univariada ou histogramas na diagonal. No
triângulo superior, a correlação entre características contínuas está
disponível:
library(GGally)
fires %>%
ggpairs()

Figura 7.4: Um pairs plot.


Essa visualização rica em informações demanda algum tempo para ser
processada. Ela é útil como um diagrama exploratório, na EDA, mas não
necessariamente como um diagrama explicativo para o relatório de
resultados. Consegue identi car algum padrão incomum? Em primeiro
lugar, STAT_CAUSE_DESCR parece desbalanceado, o que signi ca que existe
uma diferença signi cativa entre o número de observações por classe.
Além disso, OWNER_CODE parece ser bimodal (tem dois picos). Essas
propriedades podem afetar negativamente nossa análise, dependendo do
modelo que escolhermos. Em segundo lugar, todas as correlações parecem
ser relativamente baixas, o que facilita nosso trabalho (já que dados
correlacionados não são bons para ML). Mesmo assim, já sabemos que há
uma forte associação entre a localização (LATITUDE e LONGITUDE) e o
código do proprietário que vimos em nosso diagrama anterior. Logo,
devemos descon ar dessas correlações. O esperado é que esse problema
fosse detectado na engenharia de características. Em terceiro lugar,
FIRE_SIZE tem uma distribuição muito incomum. Parece que esse
diagrama está vazio, apenas com os eixos x e y presentes. Vemos um
diagrama de densidade com um pico muito alto e estreito no intervalo
mais baixo e uma inclinação positiva extremamente longa. Podemos gerar
rapidamente um diagrama de densidade transformado para log10 (Figura
7.5):
ggplot(fires, aes(FIRE_SIZE)) +
geom_density() +
scale_x_log10()

Para o estudo de caso, manteremos uma quantidade mínima de


tarefas, mas talvez haja mais alguns detalhes interessantes para
visualizar que poderiam contar uma história para o usuário nal. Por
exemplo, observe que o conjunto de dados tem uma dimensão
temporal. Seria interessante ver como a quantidade (e a qualidade)
dos incêndios orestais tem mudado com o tempo. Deixaremos isso
para o usuário motivado a explorar com o excelente pacote
gganimate.
Geralmente a visualização de dados interativa é usada sem uma nalidade
especial. Até mesmo para os pacotes mais populares, a documentação
mostra apenas o uso básico. Em nosso caso, já que temos tantos pontos
de dados em uma con guração espacial, e queremos um produto nal
que seja acessível, criar um mapa interativo é uma opção óbvia. Como no
Capítulo 5, usaremos o Lea et (Figura 7.6):
library(leaflet)

leaflet() %>%
addTiles() %>%
addMarkers(lng = df$LONGITUDE, lat = df$LATITUDE,
clusterOptions = markerClusterOptions()
)

Figura 7.5: Diagrama de densidade da característica FIRE_SIZE com


transformação logarítmica.
Figura 7.6: Mapa interativo mostrando as localizações de incêndios orestais.
Observe como o uso de clusterOptions permite apresentar
simultaneamente todos os dados sem sobrecarregar o usuário ou reduzir
a visibilidade. Isso satisfaz nossa curiosidade sobre boas visualizações na
EDA. Existem muitos métodos estatísticos que podemos aplicar, mas
passaremos para o machine learning em Python.

Machine Learning
Já temos alguma ideia dos fatores que podem in uenciar a causa de um
incêndio. Examinaremos a construção de um modelo de machine
learning com o uso do scikit-learn em Python.7
Achamos que é melhor executar o ML em Python como vimos no
Capítulo 5. Usaremos um algoritmo de oresta aleatória. Há várias razões
para essa escolha:
1. É um algoritmo estabelecido.
2. É relativamente fácil de entender.
3. Não requer escalonamento de características antes do treinamento.
Existem outras razões para acharmos o algoritmo bom, como por
trabalhar bem com dados ausentes e ter explicabilidade imediata.

De nindo seu ambiente Python


Como discutido no Capítulo 6, existem algumas maneiras de acessar o
Python usando o pacote reticulate. A escolha depende das circunstâncias,
que mostramos na arquitetura de nosso projeto. Aqui, passaremos nosso
data.frame R para um ambiente virtual Python. Se você seguiu as etapas
do Capítulo 6, está com o ambiente virtual modern_data instalado. Já
instalamos alguns pacotes nesse ambiente. Recapitulando, executamos os
comandos a seguir:
library(reticulate)

# Cria um novo virtualenv


virtualenv_create("modern_data")

# Instala pacotes Python nesse virtualenv


library(tidyverse)
c("scikit-learn", "pandas", "seaborn") %>%
purrr::map(~ virtualenv_install("modern_data", .))

Se você não tiver o virtualenv modern_data ou estiver usando o Windows,


recorra às etapas dos arquivos 0 – setup.R e 1 – activate.R discutidas no
Capítulo 6. Pode ser aconselhável reiniciar o R nesse momento para
assegurar que você consiga ativar seu ambiente virtual usando o comando
a seguir:
# Ativa o ambiente virtual
use_virtualenv("modern_data", required = TRUE)

# se estiver usando o miniconda (windows)


# use_condaenv("modern_data")

Incluiremos todas as etapas do Python em um único script; você pode


encontrar esse script no repositório do livro
(https://github.com/moderndatadesign/PyR4MDS) em ml.py. Em primeiro
lugar, importaremos os módulos necessários:
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn import metrics

Engenharia de características
Agora trataremos das características do conjunto de dados que podem ser
informativas para um analista de dados, mas que na melhor das hipóteses
serão inúteis para o treinamento do modelo, e na pior das hipóteses
podem reduzir sua precisão. Isso se chama adicionar ruído ao conjunto de
dados, e queremos evitar esse problema. Essa é a nalidade da engenharia
de características. Selecionaremos apenas as características das quais
precisamos, como especi cado na Tabela 7.1. Também usamos convenções
padrão do ML ao armazenar as características em X e o alvo em y:
features = ["OWNER_CODE", "DISCOVERY_DOY", "FIRE_SIZE", "LATITUDE",
"LONGITUDE"]
X = df[features]
y = df["STAT_CAUSE_DESCR"]

Aqui, criamos uma instância de LabelEncoder. Iremos usá-la para


codi car uma característica categórica como numérica. Em nosso caso,
ela será a aplicada ao alvo:
le = LabelEncoder()
y = le.fit_transform(y)

Nessa etapa, dividimos o conjunto de dados em um conjunto de


treinamento e em um de teste (observe que também estamos usando o
útil parâmetro stratify para assegurar que a função de divisão crie
amostras uniformes de nossas classes desbalanceadas):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33,
random_state=42,
stratify=y)

Treinamento do modelo
Para aplicar o classi cador de oresta aleatória, criaremos uma instância
de RandomForestClassifier. Como no Capítulo 5, usamos o paradigma
fit/predict e armazenamos os valores previstos em preds:
clf = RandomForestClassifier()

clf.fit(X_train, y_train)

preds = clf.predict(X_test)

Na última etapa, atribuiremos a matriz de confusão e a pontuação de


precisão aos objetos:
conmat = metrics.confusion_matrix(y_test, preds)
acc = metrics.accuracy_score(y_test, preds)

Após concluirmos nosso script, podemos usá-lo como fonte para o R:


source_python("ml.py")

Depois de executar esse comando, teremos acesso a todos os objetos


Python diretamente em nosso ambiente. A precisão é de 0,58, o que não é
um fenômeno, mas certamente é melhor do que se fosse aleatória!

Quando usarmos a função source_python a partir do reticulate,


poderemos aumentar signi cativamente nossa produtividade,
principalmente se estivermos trabalhando em uma equipe bilíngue.
Imagine um cenário em que um colaborador construísse a parte
referente ao ML em Python e você precisasse incluir o trabalho dele
no seu. Seria necessário apenas executar o sourcing sem ser preciso se
preocupar com gravar tudo. Esse cenário também é plausível para um
pro ssional que entre em uma nova empresa ou projeto e herde um
código Python que precise ser usado imediatamente.
Se quisermos nos bene ciar de ggplot para examinar a matriz de
confusão, antes precisamos fazer a conversão para um data.frame R. O
valor (value) será então o numero de observações de cada caso, que
mapeamos para size, e alteramos shape para 1 (um círculo). O resultado
é mostrado na Figura 7.7:
library(ggplot2)
py$conmat %>%
as.data.frame.table(responseName = "value") %>%
ggplot(aes(Var1, Var2, size = value)) +
geom_point(shape = 1)
Não é surpresa termos alguns grupos com ocorrência muito alta já que
sabíamos que nossos dados estavam desbalanceados. O que faremos
agora com esses adequados código e saída Python? No m do Capítulo 6,
vimos uma maneira simples e e caz de criar um documento interativo
(lembre-se do que você aprendeu no Capítulo 5) usando um R Markdown
com runtime shiny. Implementaremos o mesmo conceito aqui.

Figura 7.7: Diagrama da matriz de confusão do classi cador.

Previsão e UI
Após o estabelecimento de um modelo Python, é prática geral testá-lo
com entradas ctícias. Isso nos permitirá veri car se nosso modelo pode
manipular os dados de entrada corretos e é prática padrão na engenharia
de características antes de conectá-lo a entradas reais do usuário. Para
fazer isso, criaremos cinco sliderInputs para as cinco características de
nosso modelo. Aqui, embutimos os valores mínimo e máximo em código
para simpli car, mas é claro que eles poderiam ser dinâmicos:
sliderInput("OWNER_CODE", "Owner code:",
min = 1, max = 15, value = 1)
sliderInput("DISCOVERY_DOY", "Day of the year:",
min = 1, max = 365, value = 36)
sliderInput("FIRE_SIZE", "Number of bins (log10):",
min = -4, max = 6, value = 1)
sliderInput("LATITUDE", "Latitude:",
min = 17.965571, max = 48.9992, value = 30)
sliderInput("LONGITUDE", "Longitude:",
min = -124.6615, max = -65.321389, value = 30)

Como zemos no m do Capítulo 6, acessaremos esses valores na lista


interna input e usaremos uma função do pacote shiny para renderizar a
saída apropriada (Figura 7.8).
prediction <- renderText({
input_df <- data.frame(OWNER_CODE = input$OWNER_CODE,
DISCOVERY_DOY = input$DISCOVERY_DOY,
FIRE_SIZE = input$FIRE_SIZE,
LATITUDE = input$LATITUDE,
LONGITUDE = input$LONGITUDE)

clf$predict(r_to_py(input_df))
})

Esses elementos responderão dinamicamente a alterações feitas na


entrada do usuário. É exatamente disso que precisamos em nosso
trabalho, já que esse é um produto interativo, e não estático. É possível ver
todos os diferentes blocos de código que usamos na preparação para esse
projeto. Eles devem demandar pouca alteração, destacando-se a
habilidade de capturar a entrada do usuário na parte de inferência. Isso
pode ser feito com o acesso ao objeto input.

Considerações nais
Nesse estudo de caso, demonstramos como você pode aproveitar o
melhor de dois mundos e combinar as excelentes ferramentas que os
cientistas de dados modernos têm à sua disposição para criar experiências
de usuário notáveis, que encantem visualmente e baseiem a tomada de
decisões. Esse é um exemplo básico desse sistema elegante, e temos
certeza de que, ao mostrarmos o que é possível fazer, vocês – nossos
leitores – criarão os produtos de ciência de dados do futuro!

Figura 7.8: Resultado de nosso estudo de caso.

1 Short, Karen C. 2017. Spatial wild re occurrence data for the United States, 1992-2015,
FPA_FOD_20170508. 4th Edition. Fort Collins, CO: Forest Service Research Data Archive.
https://doi.org/10.2737/RDS-2013-0009.4.
2 Deixaremos o desenvolvimento completo de um modelo de classi cação robusto para os leitores
motivados. Na verdade, você também pode car interessado em uma regressão que preveja o
tamanho nal do incêndio em acres. Os leitores curiosos notarão que alguns notebooks
interessantes estão disponíveis no Kaggle para ajudá-los a começar.
3 Isso está longe de retratar o desenvolvimento, a hospedagem e a implantação de modelos de ML
robustos, o que, de qualquer forma, não é o objetivo deste livro.
4 Alguns leitores podem não estar familiarizados com essa linguagem. Normalmente ela é usada
para especi car opções de con guração como código, como nesse caso.
5 Essa parte também pode ser executada apropriadamente dentro do R com o uso de pacotes
como o dbplyr ou do painel Connections do RStudio.
6 Esse pacote é usado para estender a funcionalidade do ggplot2 para conjuntos de dados
transformados.
7 Essa não é uma exposição completa de todos os métodos ou otimizações possíveis, já que
estamos dando ênfase à construção de um uxo de trabalho bilíngue, e não explorando técnicas
de machine learning detalhadamente. Os leitores podem consultar a documentação o cial do
scikit-learn para ver uma orientação adicional, na seção habilmente chamada de “Choosing the
right estimator” (Escolhendo o estimador certo) (https://oreil.ly/yVysu).
APÊNDICE

Dicionário bilíngue de Python:R

O dicionário a seguir tem como objetivo ser usado como uma referência
rápida para a conversão de comandos entre Python e R. Visite o
repositório do livro (https://github.com/moderndatadesign/PyR4MDS) para
acessar outros recursos.
Correções e acréscimos são bem-vindos. Entre em contato com o Rick no
LinkedIn (https://linkedin.com/in/rick-scavetta/) ou faça um comentário no
repositório. Para acessar um resumo baixável, visite o site do livro
(https://moderndata.design).
Exceto por algumas expressões de linha de comando para inserção no
terminal, que contêm uma observação explícita, as expressões foram
fornecidas em R ou Python.

Gerenciamento de pacotes
Tabela A.1: Instalando um único pacote

install.packages("tidyverse") # Linha de comando


pip install pandas

Tabela A.2: Instalando versões especí cas dos pacotes

devtools::install_version( # Linha de comando


"ggmap", pip install pandas==1.1.0
version = "3.5.2"
)

Tabela A.3: Instalando vários pacotes


install.packages(c("sf", # Linha de comando
"ggmap")) pip install pandas scikit-learn seaborn
Grava uma lista de todos os pacotes (e versões) que estão em uso em
requirements.txt
# Linha de comando
pip freeze > requirements.txt
Usa requirements.txt como entrada para instalar pacotes em um novo
ambiente:
# Linha de comando
pip install -r requirements.txt

Tabela A.4: Carregando pacotes

# Múltiplas chamadas a library() # Pacote completo


library(MASS) import math
library(nlme) from sklearn import * # Menos recomendado,
library(psych) # use um alias
library(sf)
# Pacote completo com alias
# Instala se ainda não estiver import pandas as pd
disponível:
if (!require(readr)) { # Módulo
install.packages("readr") from sklearn import datasets
library(readr)
} # Módulo com alias
import statsmodels.api as sm
# Verifica, instala se necessário e
# carrega um ou vários pacotes: # Função
pacman::p_load(MASS, nlme, psych, from statsmodels.formula.api import ols
sf) # Para regressão dos mínimos quadrados
ordinários

Operadores de atribuição
Tabela A.5: Operadores de atribuição típicos em Ra
Operador Direção Ambiente Nome Comentário
<- RHS Atual Operador de atribuição Preferido; comum e não ambíguo
para (direcionado para a
LHS esquerda)
= RHS Atual Operador de atribuição Menos adequado; comum, mas é facilmente confundido com ==
para (direcionado para a (equivalência) e = (atribuição a argumento de função); sem
LHS esquerda) superatribuição como resultado
Operador Direção Ambiente Nome Comentário
-> LHS Atual Operador de atribuição Menos adequado; incomum, facilmente ignorado, e inesperado.
para (direcionado para a Geralmente usado no m de uma longa cadeia de funções do
RHS direita) dplyr/tidyverse; melhor usar %<%.
a
RHS signi ca right-hand side (lado direito) e LHS é left-hand side (lado esquerdo).

Tabela A.6: Operador de atribuição típico em Python


Operador Direção Ambiente Nome Comentário
= RHS para LHS Atual Operador de atribuição simples Preferido; use seguindo as regras de escopo do ambiente

Tabela A.7: Operadores de superatribuição em R


Operador Direção Ambiente Nome Comentário
<<- RHS para Pai Operador de superatribuição (direcionado para a Comum; use seguindo as regras de escopo do
LHS esquerda) ambiente
->> LHS para Pai Operador de superatribuição (direcionado para a Menos comum
RHS direita)

Esses operadores são preferenciais principalmente quando do uso de uma


cadeia de funções do dplyr/tidyverse.
Tabela A.8: Casos especiais em R
Operador Direção Ambiente Nome Comentário
|> LHS para Atual Forward pipe nativo Faz a atribuição ao primeiro argumento da função downstream
RHS
%>% LHS para Atual Forward pipe ou pipe Faz a atribuição ao primeiro argumento da função downstream, pacote
RHS (coloquialmente) magrittr
%$% LHS para Atual Pipe de exposição Expõe os elementos nomeados para a função downstream, pacote
RHS magrittr
%<>% RHS Atual Pipe de atribuição Faz a atribuição ao primeiro argumento da função downstream e
para LHS atribui a saída no local, pacote magrittr
%<-% RHS Atual Múltipla atribuição Faz a atribuição a vários objetos, pacote zeallot
para LHS

Tabela A.9: Casos especiais e incrementos em Python


Operador Direção Ambiente Nome Comentário
+= RHS para Atual Atribuição de Soma um valor e a variável e atribui o resultado a essa variável.
LHS incremento
-= RHS para Atual Atribuição de Subtrai um valor da variável e atribui o resultado a essa variável.
LHS decremento
Operador Direção Ambiente Nome Comentário
*= RHS para Atual Atribuição de Multiplica a variável por um valor e atribui o resultado a essa variável.
LHS multiplicação
/= RHS para Atual Atribuição de divisão Divide a variável por um valor e atribui o resultado a essa variável.
LHS
**= RHS para Atual Atribuição de potência Eleva a variável a uma potência especi cada e atribui o resultado a essa
LHS variável.
%= RHS para Atual Atribuição de módulo Calcula o módulo da divisão entre a variável e um valor e atribui o
LHS resultado a essa variável.
//= RHS para Atual Atribuição de divisão Divide o piso da variável por um valor e atribui o resultado a essa
LHS do piso variável.

Tipos
Tabela A.10: Os quatro tipos de vetor atômico de nidos pelo usuário mais
comuns em R
Abreviação do data Abreviação do
Tipo Descrição Exemplo
frame tibble
Lógico logi <lgl> Dados binários TRUE/FALSE, T/F,
1/0
Integer int <int> Número inteiros de –∞, ∞ 7, 9, 2, –4
Double num <dbl> Números reais de –∞, ∞ 3.14, 2.78, 6.45
Caractere chr <chr> Todos os caracteres alfanuméricos, incluindo os "Apple", "Dog"
espaços em branco

Tabela A.11: Os quatro tipos de nidos pelo usuário mais comuns em Python
Tipo Abreviação Descrição Exemplo
Boolean bool Dados binários True/False
Integer int Números inteiros de –∞, ∞ 7, 9, 2, –4
Float float Número reais de –∞, ∞ 3.14, 2.78, 6.45
String str Todos os caracteres alfanuméricos, incluindo os espaços em branco "Apple", "Dog"

Operadores aritméticos
Tabela A.12: Operadores aritméticos comuns
Descrição Operador R Operador Python
Adição + +
Descrição Operador R Operador Python
Subtração – –
Multiplicação * *
Divisão ( oat) / /
Exponenciação ^ ou ** **
Divisão de inteiros (piso) %/% //
Módulo %% %

Atributos
Tabela A.13: Atributos de classe

# Lista atributos # Definição de uma classe


attributes(df) class Food:
name = 'toast'
# Funções de acesso
dim(df) # Instância de uma classe
names(df) breakfast = Food()
class(df)
comment(df) # Atributo da classe
# herdado pela instância
# Adiciona comentário breakfast.name
comment(df) <- "new info"
# Definindo um atributo
# Adiciona atributo personalizado breakfast.name = 'museli'
attr(df, "custom") <- "alt info" # setattr(breakfast, 'name', 'museli')
attributes(df)$custom

Palavras-chave
Tabela A.14: Palavras reservadas e palavras-chave

?reserved # Palavras-chave Python


import keyword
if, else, repeat, while, function, print(keyword.kwlist)
for, in, next, break, TRUE, FALSE,
NULL, Inf, NaN, NA, NA_integer_, ## ['False', 'None', 'True', 'and',
NA_real_, NA_complex_, NA_character_, 'as', 'assert', 'async', 'await',
... (..1, ..2 etc.) 'break', 'class', 'continue', 'def',
'del', 'elif', 'else', 'except',
'finally', 'for', 'from', 'global',
'if', 'import', 'in', 'is', 'lambda',
'nonlocal', 'not', 'or', 'pass',
'raise', 'return', 'try', 'while',
'with', 'yield']

Funções e métodos
Tabela A.15: De nindo funções nomeadas
# Definição básica # Definição simples
myFunc <- function (x, ...) { def my_func(x):
x * 10 return(x * 10)
}
my_func(4)
myFunc(4)
## 40
## [1] 40
# Múltiplos argumentos nomeados, passados como
# Múltiplos argumentos não uma tupla
nomeados def my_func(*x):
myFunc <- function (...) { return(x[2])
sum(...)
} my_func(100, 40, 60)

myFunc(100,40,60) ## 60

## [1] 200 # Múltiplos argumentos desconhecidos, salvos como


um dict
def my_func(**numb):
print("x: ",numb["x"])
print("y: ",numb["y"])

my_func(x = 40, y = 100)

## x: 40
## y: 100

# Usando doc strings


def my_func(**numb):
"""Exemplo de função
que recebe múltiplos argumentos desconhecidos.
"""

print("x: ",numb["x"])
print("y: ",numb["y"])

# Acessa doc strings com dunder


my_func.__doc__

'Exemplo de função
que recebe múltiplos argumentos desconhecidos.'
Estilo e convenções de nomeação
Geralmente o estilo em R é de nido de maneira menos restrita do que em
Python. No entanto, consulte o Advanced R style guide
(https://oreil.ly/BOf3M), de Hadley Wickham (CRC Press), ou o guia de
estilo R do Google (https://oreil.ly/ZyXjb) para ver sugestões.
Para Python, consulte o guia de estilo PEP 8 (https://oreil.ly/UqWqs).
Tabela A.16: Estilo e convenções de nomeação em R e Python
Indentação e Nomeando
Indentação e espaçamento Nomeando um script
espaçamento um script
Geralmente o espaço em branco está Atualmente a tendência é O espaço em branco, principalmente a Tipo:
relacionado a estilo e não tem efeito sobre a usar snake case minúsculo: indentação, faz parte da execução em Funções e
execução. Adicione um espaço ao redor dos underscores (“_”) entre as Python. Use quatro espaços em vez de variáveis
operadores e use tabulação ou indentação em palavras e somente letras tabulação (isso pode ser de nido em Estilo: Snake
linhas sucessivas de comandos longos. minúsculas. Exemplo: seu editor de texto). case
my_data <- 1:6 minúsculo
Exemplo:
func,
my_func,
var,
my_var,
x

Tabela A.17: Ao de nir classes


Tipo Estilo Exemplo
Classe Camel case capitalizado Recipe, MyClass
Método Snake case minúsculo class_method, method
Constante Snake case todo em maiúsculas CONS, MY_CONS, LONG_NAME_CONSTANT

Tabela A.18: Em pacotes


Tipo Estilo Exemplo
Pacotes e módulos Snake case minúsculo mypackage, module.py, my_module.py

Tabela A.19: Convenções de nomeação com _


Nomeação Signi cado
_var Convenção usada para mostrar que uma variável se destina ao uso interno dentro de uma função ou método
var_ Convenção usada para evitar con itos de nomeação com palavras-chave Python
__var Aciona a mutilação de nomes (name mangling) quando usada em um contexto de classe para impedir con itos de
herança. Imposto pelo interpretador Python
__var__ Variáveis dunder (“double underscore”). Métodos especiais de nidos pela linguagem Python. Evite esse
esquema de nomeação para atributos que você mesmo criar
_ Nomeação de uma variável temporária ou irrelevante, por exemplo, em um loop for

Objetos de armazenamento de dados análogos


Tabela A.20: Objetos Python análogos a objetos R comuns
Estrutura Estrutura(s) análoga(s)
Vetor (unidimensional homogêneo) ndarray, mas também scalars, e list e tuple homogêneos
Vetor, matrix ou array (homogêneo) Array (ndarray) n-dimensional do NumPy
Lista não nomeada (heterogênea) List
Lista nomeada (heterogênea) Dicionário dict, porém sem ordem
Ambiente (nomeado, mas com elementos desordenados) Dicionário dict
Variável/coluna em um data.frame Series do pandas (pd.Series)
data.frame bidimensional Data frame do pandas (pd.DataFrame)

Tabela A.21: Objetos R análogos a objetos Python comuns


Estrutura Estrutura(s) análoga(s)
scalar Vetor longo de um elemento
list (homogênea) Vetor, mas como se não houvesse vetorização
list (heterogênea) list não nomeada
tuple (imutável, homogênea) Vetor, list como saída separada de uma função
Dicionário dict, um par chave-valor list nomeada ou, melhor, environment
Array (ndarray) n-dimensional do NumPy Vetor, matrix ou array
Series do pandas (pd.Series) Vetor, variável/coluna em um data.frame
Data frame do pandas (pd.DataFrame) data.frame bidimensional

Tabela A.22: Unidimensionais, homogêneos

# Vetores # Listas
cities_R <- c("Munich", "Paris", cities = ['Munich', 'Paris',
"Amsterdam") 'Amsterdam']
dist_R <- c(584, 1054, 653) dist = [584, 1054, 653]
Tabela A.23: Pares chave-valor unidimensionais heterogêneos (listas em R,
dicionários em Python)

# Uma lista de data frames # listas


cities_list <- list( city_l = ['Munich', 'Paris', 'Amsterdam']
Munich = data.frame(dist = 584,
pop = 1484226, dist_l = [584, 1054, 653]
area = 310.43,
country = "DE"), pop_l = [1484226, 2175601, 1558755]
Paris = data.frame(dist = 1054,
pop = 2175601, area_l = [310.43, 105.4, 219.32]
area = 105.4,
country = "FR"), country_l = ['DE', 'FR', 'NL']
Amsterdam = data.frame(dist = 653,
pop = 1558755, import numpy as np
area = 219.32,
country = "NL")) # Arrays do NumPy
city_a = np.array(['Munich', 'Paris',
# Como um objeto list 'Amsterdam'])
cities_list[1] city_a

## $Munich ## array(['Munich', 'Paris', 'Amsterdam'],


## dist pop area country dtype='<U9')
## 1 584 1484226 310.43 DE
pop_a = np.array([1484226, 2175601,
cities_list["Munich"] 1558755])
pop_a
## $Munich
## dist pop area country ## array([1484226, 2175601, 1558755])
## 1 584 1484226 310.43 DE
# Dicionários
# Como um objeto data.frame yy = {'city': ['Munich', 'Paris',
cities_list[[1]] 'Amsterdam'],
'dist': [584, 1054, 653],
## dist pop area country 'pop': [1484226, 2175601, 1558755],
## 1 584 1484226 310.43 DE 'area': [310.43, 105.4, 219.32],
'country': ['DE', 'FR', 'NL']}
cities_list$Munich yy

## dist pop area country ## {'city': ['Munich', 'Paris',


## 1 584 1484226 310.43 DE 'Amsterdam'],
## 'dist': [584, 1054, 653], 'pop':
# Uma lista de dados heterogêneos [1484226,
lm_list <- lm(weight ~ group, data = ## 2175601, 1558755], 'area': [310.43,
PlantGrowth) 105.4,
## 219.32], 'country': ['DE', 'FR', 'NL']}
# length(lm_list)
# names(lm_list)

Data Frames
Tabela A.24: Data frames em Python

# classe pd.DataFrame
import pandas as pd

# De um dicionário, yy
yy_df = pd.DataFrame(yy)
yy_df
## city dist pop area country
## 0 Munich 584 1484226 310.43 DE
## 1 Paris 1054 2175601 105.40 FR
## 2 Amsterdam 653 1558755 219.32 NL
# De nomes
# de listas
list_names = ['city', 'dist', 'pop', 'area', 'country']

# as colunas são uma lista de listas


list_cols = [city_l, dist_l, pop_l, area_l, country_l]
list_cols

## [['Munich', 'Paris', 'Amsterdam'], [584, 1054, 653], [1484226...

# Uma lista de tuplas zipada


zip_list = list(zip(list_cols, list_names))
zip_list

# zip_dict = dict(zip_list)
# zip_df = pd.DataFrame(zip_dict)
# zip_df

# zip_df = pd.DataFrame(zip_list)
# zip_df

## [(['Munich', 'Paris', 'Amsterdam'], 'city'), ([584, 1054, 653], 'dist')...


# Mais fácil
# Importa a biblioteca pandas
import pandas as pd

# inicializa a lista de listas


list_rows = [['Munich', 584, 1484226, 310.43, 'DE'],
['Paris', 1054, 2175601, 105.40, 'FR'],
['Amsterdam', 653, 1558755, 219.32, 'NL']]

# Cria o data frame pandas


df = pd.DataFrame(list_rows, columns = list_names)

# exibe o data frame.


df

## city dist pop area country


## 0 Munich 584 1484226 310.43 DE
## 1 Paris 1054 2175601 105.40 FR
## 2 Amsterdam 653 1558755 219.32 NL

Tabela A.25: Arrays multidimensionais

# array arr = np.array([[[ 1, 2],


arr_r <- array(c(1:4, [ 3, 4]],
seq(10, 40, 10), [[ 10, 20],
seq(100, 400, 100)), [30, 40]],
dim = c(2,2,3) ) [[100, 200],
[300, 400]]])
arr_r arr

## , , 1 ## array([[[ 1, 2],
## ## [ 3, 4]],
## [,1] [,2] ##
## [1,] 1 3 ## [[ 10, 20],
## [2,] 2 4 ## [ 30, 40]],
## ##
## , , 2 ## [[100, 200],
## ## [300, 400]]])
## [,1] [,2]
## [1,] 10 30 arr.sum(axis=0)
## [2,] 20 40
## ## array([[111, 222],
## , , 3 ## [333, 444]])
arr.sum(axis=1)
##
## [,1] [,2]
## array([[ 4, 6],
## [1,] 100 300
## [ 40, 60],
## [2,] 200 400
## [400, 600]])
rowSums(arr_r, dims = 2)
arr.sum(axis=2)
## [,1] [,2]
## array([[ 3, 7],
## [1,] 111 333
## [ 30, 70],
## [2,] 222 444
## [300, 700]])
rowSums(arr_r, dims = 1)

## [1] 444 666

colSums(arr_r, dims = 1)

## [,1] [,2] [,3]


## [1,] 3 30 300
## [2,] 7 70 700

colSums(arr_r, dims = 2)

## [1] 10 100 1000

Tabela A.26: Data frames bidimensionais, heterogêneos, tabulares em R

# classe data.frame de vetores


cities_df <- data.frame(city = c("Munich", "Paris", "Amsterdam"),
dist = c(584, 1054, 653),
pop = c(1484226, 2175601, 1558755),
area = c(310.43, 105.4, 219.32),
country = c("DE", "FR", "NL"))

cities_df

## city dist pop area country


## 1 Munich 584 1484226 310.43 DE
## 2 Paris 1054 2175601 105.40 FR
## 3 Amsterdam 653 1558755 219.32 NL

Expressões lógicas
Tabela A.27: Operadores relacionais
Descrição Operador R Operador Python
Equivalência == ==
Não equivalência != !=
Maior que (ou igual a) > (>=) > (>=)
Menor que (ou igual a) < (<=) < (<=)
Negação !x not()

Tabela A.28: Operadores relacionais

xx <- 1:10 # Operador relacional em Python


a = np.array([23, 6, 7, 9, 12])
xx == 6 a > 10

## [1] FALSE FALSE FALSE FALSE ## array([ True, False, False, False,
FALSE True])
TRUE FALSE FALSE FALSE FALSE

xx != 6
## [1] TRUE TRUE TRUE TRUE TRUE
FALSE TRUE TRUE TRUE TRUE

xx >= 6

## [1] FALSE FALSE FALSE FALSE


FALSE
TRUE TRUE TRUE TRUE TRUE

xx < 6

## [1] TRUE TRUE TRUE TRUE TRUE


FALSE FALSE FALSE FALSE FALSE

Tabela A.29: Operadores lógicos


Descrição Operador R Operador Python
AND &, && &, and
OR |, || |, or
WITHIN y %in% x in, not in
identidade identical() is, is not
Tabela A.30: Operadores lógicos

xx <- 1:6 # Operador lógico em Python


# x = range(6)
# fim de uma distribuição # x = [*x]
xx < 3 | xx > 4 # x
# type(x)
## [1] TRUE TRUE FALSE FALSE TRUE TRUE import numpy as np
x = np.array(range(6))
# Intervalo de uma distribuição # type(x)
xx > 3 & xx < 4 # fim de uma distribuição
# x < 3 or x > 4
## [1] FALSE FALSE FALSE FALSE FALSE FALSE [i for i in x if i < 3 or i > 4]

# Intervalo de uma distribuição


# x > 3 and x < 4

## [0, 1, 2, 5]

[i for i in x if i >= 3 and i <= 4]

## [3, 4]

Tabela A.31: Identidade

x <- c("Caracas", "Bogotá", "Quito") x = ['Caracas', 'Bogotá', 'Quito']


y <- c("Bern", "Berlin", "Brussels") y = ['Bern', 'Berlin', 'Brussels']
z <- c("Caracas", "Bogotá", "Quito") z = ['Caracas', 'Bogotá', 'Quito']

# Os objetos são idênticos? x == y


identical(x, y)
## False
## [1] FALSE
x == z
identical(x, z)
## True
## [1] TRUE
# Algum é True
# Algum é TRUE import numpy as np
any(x == "Quito") x = np.array(x)
np.any(x == "Caracas")
## [1] TRUE
## True
# Todos são TRUE
all(str_detect(y, "^B")) # Todos são True

## [1] TRUE np.all(x == "Caracas")

## False

Indexação
Tabela A.32: Procurando objetos idênticos(a)
Dimensões Uso Descrição Dimensões Uso Descrição
1 x[index] Isola conteúdos, 1 x[index] Isola
mantém contêiner conteúdos,
mantém
contêiner
1 x[-index] Extrai um conteúdo, 1 x[-index] Isola
descarta contêiner conteúdos
na direção
inversa,
mantém
contêiner
1 x[[index]] Isola conteúdos, 1 x[index_1:index_2] Fatia
remove item,
mantém contêiner
2 x[row_index, Isola conteúdos, 1 x[index_1:index_2:stride] Fatia com
col_index] mantém contêiner um
intervalo
2 x[col_index] Atalho para colunas 1 x[index_1:index_2:-1] Fatia com
inversão
2 x[[index]] Extrai um conteúdo, 2 x.loc[index_1:index_2] Localização
descarta contêiner
n x[row_index, Isola conteúdos, 2 x.iloc[index_1: Índice
col_index, mantém contêiner index_2:stride]
dim_index]
(a)
Em que index, row_index, col_index e dim_index são vetores de tipo inteiro, de caracteres ou
lógico.

Tabela A.33: Unidimensional

xx <- LETTERS[6:16] cities = ['Toronto', 'Santiago',


xx[4] 'Berlin', 'Singapore', 'Kampala', 'New Delhi']

## [1] "I" cities[0]

xx[[4]] ## 'Toronto'

## [1] "I" cities[-1]

cities_list[2] ## 'New Delhi'

## $Paris cities[1:2]
## dist pop area country
## 1 1054 2175601 105.4 FR ## ['Santiago']

cities[:2]

## ['Toronto', 'Santiago']

Tabela A.34: Bidimensional R


# classe data.frame de vetores
cities_df <- data.frame(city = c("Munich", "Paris", "Amsterdam"),
dist = c(584, 1054, 653),
pop = c(1484226, 2175601, 1558755),
area = c(310.43, 105.4, 219.32),
country = c("DE", "FR", "NL"))

cities_df[2] # data frame

## dist
## 1 584
## 2 1054
## 3 653
cities_df[,2] # vetor

## [1] 584 1054 653

cities_df[[2]] # vetor

## [1] 584 1054 653

cities_df[2:3] # data frame

## dist pop
## 1 584 1484226
## 2 1054 2175601
## 3 653 1558755
cities_df[,2:3] # data frame

## dist pop
## 1 584 1484226
## 2 1054 2175601
## 3 653 1558755

cities_tbl <- tibble(city = c("Munich", "Paris", "Amsterdam"),


dist = c(584, 1054, 653),
pop = c(1484226, 2175601, 1558755),
area = c(310.43, 105.4, 219.32),
country = c("DE", "FR", "NL"))

cities_tbl[2] # data frame

## # Um tibble: 3 x 1
## dist
## <dbl>
## 1 584
## 2 1054
## 3 653

cities_tbl[,2] # data frame

## # Um tibble: 3 x 1
## dist
## <dbl>
## 1 584
## 2 1054
## 3 653

cities_tbl[[2]] # vetor

## [1] 584 1054 653

cities_tbl[2:3] # data frame

## # Um tibble: 3 x 2
## dist pop
## <dbl> <dbl>
## 1 584 1484226
## 2 1054 2175601
## 3 653 1558755

cities_tbl[,2:3] # data frame

## # Um tibble: 3 x 2
## dist pop
## <dbl> <dbl>
## 1 584 1484226
## 2 1054 2175601
## 3 653 1558755

Tabela A.35 – Bidimensional Python

df

## city dist pop area country


## 0 Munich 584 1484226 310.43 DE
## 1 Paris 1054 2175601 105.40 FR
## 2 Amsterdam 653 1558755 219.32 NL

df[1:]

## city dist pop area country


## 1 Paris 1054 2175601 105.40 FR
## 2 Amsterdam 653 1558755 219.32 NL

# posição
df.iloc[0, 1]

## 584

df.iat[0, 1]

## 584

# Rótulo
df.loc[1:, 'city']

## 1 Paris
## 2 Amsterdam
## Name: city, dtype: object
data = {'Country': ['Belgium', 'India', 'Brazil'],
'Capital': ['Brussels', 'New Delhi', 'Brasilia'],
'Population': [11190846, 1303171035, 207847528]}

df_2 = pd.DataFrame(data,columns=['Country', 'Capital', 'Population'])

df_2

## Country Capital Population


## 0 Belgium Brussels 11190846
## 1 India New Delhi 1303171035
## 2 Brazil Brasilia 207847528

df[1:]
# df.iloc([0], [0])

## city dist pop area country


## 1 Paris 1054 2175601 105.40 FR
## 2 Amsterdam 653 1558755 219.32 NL

Tabela A.36: N-dimensional


cities_array <- c(1:16) # Indexação Python n-dimensional
dim(cities_array) <- c(4,2,2) arr
cities_array
## array([[[ 1, 2],
## , , 1 ## [ 3, 4]],
## ##
## [,1] [,2] ## [[ 10, 20],
## [1,] 1 5 ## [ 30, 40]],
## [2,] 2 6 ##
## [3,] 3 7 ## [[100, 200],
## [4,] 4 8 ## [300, 400]]])
##
## , , 2 arr[1,1,1]
##
## [,1] [,2] ## 40
## [1,] 9 13
## [2,] 10 14 arr[:,1,1]
## [3,] 11 15
## [4,] 12 16 ## array([ 4, 40, 400])

cities_array[1,2,2] arr[1,:,1]

## [1] 13 ## array([20, 40])

cities_array[1,2,] arr[1,1,:]

## [1] 5 13 ## array([30, 40])

cities_array[,2,1]

## [1] 5 6 7 8
Sobre os autores

Rick J. Scavetta trabalha como treinador independente de workshops,


cientista de dados freelancer e cofundador desde 2012. Usando o nome
Scavetta Academy, Rick tem presença próxima e recorrente nos principais
institutos de pesquisa da Alemanha. Seus cursos online foram assistidos
por mais de 200 mil alunos desde 2016 e ele também fez contribuições
para cursos avançados de ciência de dados para a O’Reilly e a Manning.
Atualmente Rick atua como conselheiro técnico de currículos para a
Misk Academy na Arábia Saudita e lidera o desenvolvimento de seu
programa de ciência de dados.
Boyan Angelov é um estrategista e consultor de dados com uma década
de experiência em vários ambientes acadêmicos e industriais, abordando
tópicos como bioinformática, testes clínicos, HRTech e consultoria em
gestão. Além disso, faz contribuições para projetos cientí cos open source
de XAI e dá palestras regularmente em conferências e meetups.
Colofão

O animal da capa de Python e R para o Cientista de Dados Moderno é uma


lula (Loligo forbesii), um cefalópode normalmente encontrado em todo o
Oceano Atlântico e ao longo da Europa Ocidental e do Leste da África.
No inverno, as lulas procuram as temperaturas estáveis das profundezas
do oceano. No verão, elas podem ser encontradas em qualquer lugar entre
10 mil e 500 mil metros de profundidade.
As lulas têm oito tentáculos abaixo de seus grandes olhos e dois braços
retráteis que se esticam sobre a cabeça com ventosas nas extremidades.
Acima dos olhos, duas grandes barbatanas se abrem como um diamante.
Suas barbatanas avermelhadas perdem a cor quando lançadas para trás, e
as guelras que cam atrás da cabeça são usada para propulsão, o que
ajuda a lula a evitar predadores. As lulas caçam peixes pequenos e outros
cefalópodes, incluindo outras lulas. Elas podem crescer até
aproximadamente 35,5 centímetros durante seu tempo de vida de 1 a 2
anos.
Muitos dos animais das capas da O’Reilly estão em perigo; todos eles são
importantes para o nosso planeta.
A ilustração da capa é de Susan Thompson, baseada em uma gravura em
preto e branco da iStock.

Você também pode gostar