Full text
25-310908.0 CDD-005.133 Dados Internacionais de Catalogação na Publicação (CIP) (Câmara Brasileira do Livro, SP, Brasil) Algoritmos e programação na linguagem Python [livro eletrônico] : (do básico à análise de dados com aplicações práticas) / Raimundo Fagner Costa...[et al.] ; coordenação Tiago Francisco Andrade Diocesano...[et al.]. -- 2. ed. -- Manaus, AM : Ed. dos Autores, 2025. PDF Outros autores: Eduardo Palhares Júnior, Alexandre Lopes Martiniano, Wenndisson da Silva Souza, Nivaldo Rodrigues e Silva. Outros coordenadores: Jaidson Brandão da Costa, Elcivan dos Santos Silva, Martinho Correia Barros, Adelino Maia Galvão Filho. Bibliografia. ISBN 978-65-01-76116-9 1. Algoritmos 2. Dados - Análise 3. Linguagem de programação (Computadores) 4. Python (Linguagem de programação para computadores) I. Palhares Júnior, Eduardo. II. Martiniano, Alexandre Lopes. III. Souza, Wenndisson da Silva. IV. Silva, Nivaldo Rodrigues e. V. Diocesano, Tiago Francisco Andrade. VI. Costa, Jaidson Brandão da. VII. Silva, Elcivan dos Santos. VIII. Barros, Martinho Correia. IX. Galvão Filho, Adelino Maia. Índices para catálogo sistemático: 1. Python : Linguagem de programação : Computadores : Processamento de dados 005.133 Aline Graziele Benitez - Bibliotecária - CRB-1/3129 DOI: 10.5281/zenodo.15163262
Expediente do IFAM MINISTÉRIO DA EDUCAÇÃO SECRETARIA DE EDUCAÇÃO PROFISSIONAL E TECNOLÓGICA INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DO AMAZONAS Reitor Jaime Cavalcante Alves Pró-Reitor de Administração Fábio Teixeira Lima Pró-Reitor de Gestão de Pessoas Leandro Amorim Damasceno Pró-Reitora de Ensino Rosângela Santos da Silva Pró-Reitora de Extensão Maria Francisca Morais de Lima Pró-Reitor de Pesquisa, Pós-Graduação e Inovação Paulo Henrique Rocha Aride Diretor Geral do Campus Manaus Distrito Industrial Nivaldo Rodrigues e Silva
Expediente do Projeto CITHA MINISTÉRIO DA EDUCAÇÃO SECRETARIA DE EDUCAÇÃO PROFISSIONAL E TECNOLÓGICA INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DO AMAZONAS Gestores Nivaldo Rodrigues e Silva Samirames da Silva Fleury Alyson de Jesus dos Santos Maria Cassiana Andrade Braga Adanilton Rabelo de Andrade Coordenadores Tiago Francisco Andrade Diocesano Jaidson Brandão da Costa Elcivan dos Santos Silva Martinho Correia Barros Adelino Maia Galvão Filho Expediente de Produção Autores Raimundo Fagner Costa Eduardo Palhares Júnior Alexandre Lopes Martiniano Wenndisson da Silva Souza Nivaldo Rodrigues e Silva Avaliação Pedagógica Samirames da Silva Fleury Diagramadores Eduardo Palhares Jr. Wenndisson da Silva Souza Fabio Serra Ribeiro Couto Revisores de Texto Alyson de Jesus dos Santos
Algoritmos e Programação na Linguagem Python (do básico à análise de dados com aplicações práticas) Autores Raimundo Fagner Costa Eduardo Palhares Júnior Alexandre Lopes Martiniano Wenndisson da Silva Souza Nivaldo Rodrigues e Silva Prefácio por Nivaldo Rodrigues e Silva Revisão Alyson de Jesus dos Santos 2ª Edição Manaus - AM 2025
Prefácio Este e-book é um guia completo sobre programação de computadores, com foco na linguagem Python. Reconhecida por sua simplicidade e versatilidade, Python é uma das linguagens de programação mais populares do mundo. Sua curva de aprendizado acessível e sua ampla aplicabilidade – que abrange desde automação de tarefas até inteligência artificial – tornam-na indispensável para quem deseja explorar o universo da tecnologia. Estruturado de forma clara e interativa, este material convida o leitor a iniciar sua jornada na programação, oferecendo uma base sólida para o desenvolvimento de códigos e a resolução de desafios práticos. O módulo 4, em especial, exemplifica essa proposta ao demonstrar como aplicar Python na solução de problemas cotidianos. A organização e análise de dados ganham destaque nesse contexto, com o uso de ferramentas e bibliotecas que auxiliam na tomada de decisões de forma eficiente e fundamentada. Mais do que um recurso acadêmico, este e-book explora aplicações práticas voltadas para a região amazônica, mostrando como a programação pode contribuir para resolver desafios locais. Por meio da otimização de tempo e recursos, o material evidencia o potencial transformador da tecnologia em cenários de alta complexidade e impacto social. Ao longo das páginas, os leitores serão incentivados a refletir sobre os conceitos fundamentais da lógica de programação e a experimentar casos práticos que conectam teoria e prática. Este e-book não apenas ensina, mas inspira uma mentalidade inovadora e orientada para resultados, mostrando que a programação é uma ferramenta poderosa para enfrentar os desafios do século XXI e promover soluções sustentáveis e transformadoras.
Projeto de Capacitação e Interiorização em Tecnologias Habilitadoras na Amazônia - CITHA O projeto CITHA surge com o objetivo de fortalecer a economia da Amazônia por meio do incentivo ao empreendedorismo local e do desenvolvimento sustentável. Sua proposta é capacitar profissionais e impulsionar a criação de startups voltadas para a bioeconomia, além de apoiar cooperativas locais na melhoria de seus processos produtivos. A implementação de tecnologias inovadoras é uma das estratégias centrais do projeto, visando oferecer soluções eficientes que atendam às necessidades regionais, como a otimização dos recursos naturais e a melhoria da infraestrutura local. Ao longo de sua execução, o projeto se compromete a integrar os diversos stakeholders, como governos, empresas, ONGs e comunidades, por meio da capacitação da mão de obra local. O objetivo é formar um capital intelectual qualificado, capaz de apoiar uma governança eficiente, promover a inovação e assegurar a sustentabilidade. O CITHA dedica-se à criação de processos internos que incentivem o desenvolvimento de novos métodos e tecnologias, adaptáveis às particularidades do território amazônico. Em síntese, o projeto CITHA visa criar um ciclo de desenvolvimento que não só incentive o empreendedorismo, mas também promova a modernização das estruturas locais, elevando a qualidade de vida das populações da Amazônia. Focado em áreas como bioeconomia, inovação e transferência de tecnologia, o projeto busca estabelecer um ecossistema mais forte e autossustentável, capaz de responder eficientemente às demandas do mercado e da sociedade.
Lista de Expressões para Enriquecimento de Conteúdo Este material foi cuidadosamente estruturado para apoiar sua jornada de aprendizado. Ao longo dos capítulos, você encontrará diversas chamadas sinalizadas por ícones especiais, que ajudarão a destacar pontos-chave e enriquecer sua compreensão. Durante a diagramação, esses ícones serão inseridos conforme as indicações dos autores, guiando você para diferentes tipos de conteúdo e atividades que potencializam seu estudo. Fique Alerta! Destaque para conceitos, expressões e trechos fundamentais que merecem sua atenção especial para a compreensão do conteúdo. Iniciando o diálogo... Espaço para reflexão crítica. Aqui você será convidado(a) a problematizar os temas abordados, relacionando-os com sua experiência e buscando conexões relevantes para aprofundar seu aprendizado. Conhecendo um pouco mais! Indicação de fontes complementares, como livros, entrevistas, vídeos, aplicativos, links e outros recursos para ampliar seu conhecimento sobre o tema. Caso Prático Aplicação direta do conteúdo em exemplos concretos, para facilitar a fixação e demonstrar a utilidade do que foi aprendido. Copie e Teste! Trechos de código prontos para serem copiados e executados, para que você possa experimentar, validar e explorar na prática os conceitos estudados. Saída Esperada Apresenta o resultado esperado após a execução de um bloco de código. Serve como um gabarito para que você possa verificar se sua implementação está funcionando corretamente.
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Testando o Python Após instalar o python, podemos testar se o mesmo está funcionando, ou seja, se foi instalado corretamente. Para isso vá no Menu Iniciar do Windows e digite: CMD e abra o Prompt de Comando. Figura 1.6: Abrindo o prompt de comando - CMD No Prompt de Comando, ou terminal, digite o comando python, conforme ilustra a imagem abaixo, se aparecer uma mensagem semelhante à apresentada na tela a seguir, é porque deu tudo certo. Figura 1.7: Abrindo o prompt de comando - CMD Instalando o IDE A forma mais comum para programar em Python é através do uso de um ambiente de programação, também conhecido como ambiente integral de desenvolvimento (Integrated Development Environment – IDE). Mas o que é o IDE? A sigla IDE significa Integrated Development Environment - Ambiente de Desenvolvimento Integrado, em tradução livre é um programa que reúne uma série de ferramentas que facilitam a vida do programador. 15
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Dentre as principais IDEs no mercado para se programar em Python temos: • Pycharm • Atom • VS Code • Jupyter notebook • Sublime Text • Spyder Neste material usaremos o IDE PyCharm, abaixo seguem as instruções de instalação e como podemos criar um projeto em Python. Para instalar o PyCharm basta acessar o link apresentado na próxima seção. Acesse e o link abaixo para baixar o editor https://www.jetbrains.com/pych arm/download/#section=windows Figura 1.8: Localização do link de download no site oficial Após realizar o download, realize os passos conforme mostrados a seguir: Passo 1: Ao executar o arquivo baixado, será mostrado na tela presente na imagem abaixo clique nas opções destacadas na cor vermelho. Figura 1.9: Tela inicial do instalador do Pycharm 16
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Passo 2: Na próxima tela deixe as opções padrões. E Clique em «Next » Figura 1.10: Seleção do diretório de instalação do Pycharm Passo 3: Por fim clique em « Install» Figura 1.11: Seleção do diretório de atalhos do Pycharm 17
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Criando um projeto no PyCharm Para criarmos um projeto, abra o programa Pycharm e siga as seguintes etapas: Passo 1: No Menu File > Acesse opção New Project Figura 1.12: Criação de um novo projeto no Pycharm Passo 2: Defina o local e nome do seu projeto e clique em «Create» Figura 1.13: Definição do diretório do novo projeto 18
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Passo 3: Após criar o projeto será apresentado a seguinte tela. Figura 1.14: Interface do novo projeto em Pycharm Passo 4: Para criar um arquivo python na pasta do projeto, clique com o botão direito do Mouse na pasta do projeto e vá na opção New Python File, após isso, coloque um nome para o arquivo. Figura 1.15: Criação de um arquivo Python dentro do novo projeto 19
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Figura 1.16: Interface para selecionar o nome do arquivo Python Após isso, seu arquivo será mostrado no IDE, conforme apresentado abaixo. Figura 1.17: Interface do projeto com o arquivo Python 1.1.3 Uma Alternativa na Nuvem: Jupyter Notebooks e Google Colab Até agora, discutimos a instalação de um ambiente de desenvolvimento tradicional em seu computador. Essa é uma abordagem robusta e amplamente utilizada. No entanto, o mundo da programação e da ciência de dados foi transformado por uma ferramenta que funciona de maneira diferente, mais interativa e visual. 20
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON O que é um Jupyter Notebook? Imagine o caderno de campo de um pesquisador, ele não contém apenas anotações, mas também desenhos, cálculos, amostras coladas e observações. É um documento vivo que conta a história de uma descoberta. O Jupyter Notebook é a versão digital e interativa desse caderno. Jupyter Notebook é um ambiente de programação que permite criar e compartilhar documentos que contêm, em um só lugar, código, texto e visualizações. Sua estrutura é baseada em dois tipos principais de células: •Células de Código: Contêm código Python que você pode rodar individualmente. O resultado da execução (textos, tabelas, gráficos) aparece imediatamente abaixo da célula. •Células de Texto: Permitem que você escreva explicações, anotações, títulos e links usando uma linguagem simples chamada Markdown para formatar o texto. Essa estrutura torna o Jupyter ideal para a análise de dados exploratória, pois permite que você execute o código passo a passo, documente seu raciocínio e visualize os resultados em tempo real, contando a história por trás dos dados. Google Colaboratory (Colab): O Jupyter na Nuvem E se você pudesse ter acesso a esse caderno de campo digital de qualquer lugar, sem precisar instalar nada, usando apenas seu navegador de internet? Essa é a revolução que o Google Colab proporciona. Figura 1.18: Interface do Google Colaboratory O Colab é, essencialmente, a versão do Jupyter Notebook oferecida gratuitamente pelo Google que roda direto do navegador web. Ele elimina as barreiras de entrada para a programação, tornando-a muito mais acessível e democrática. Suas principais vantagens são: •Zero Configuração: Não é necessário instalar Python e suas bibliotecas básicas. Tudo funciona diretamente no seu navegador, conectado a um computador do Google. •Colaboração Fácil: Você pode compartilhar seus notebooks com outras pessoas, da mesma forma que compartilha um Google Docs, permitindo o trabalho em equipe. 21
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON •Recursos Gratuitos: O Colab oferece acesso gratuito a recursos computacionais poderosos, como GPUs, que são essenciais para tarefas mais avançadas de Machine Learning e Inteligência Artificial. Como pode ser observado na figura 1.18, é possível explorar os blocos de texto para criar vizualizações completamente customizadas, incluindo equações L A T EX, figuras e vídeos. Além disso, em projetos muito grandes a estrutura de índices ajuda muito a navegar pelo arquivo e realizar ostestes de forma segmentada. 1.2 Variáveis, Tipos de Dados e Operadores 1.2.1 Variáveis e Atribuição Na programação de computadores, conforme discutido por Mueller (2020), a entrada, o processamento e a saída de dados podem manipular vários tipos de dados, tais como números letras - caracteres alfanuméricos - e booleanos. Da mesma forma, na linguagem Python existem diferentes tipos de dados, podendo ter dados nos formatos numéricos, textuais e booleanos. Dentro os principais tipos de dados, podemos destacar: •int - Representa os números inteiros: 7, 10, 2022,70 •float - Representa os números reais: 3.2, 1.7, 10.4 •str - Representa as strings: “Instituto Federal do Amazonas”, “CITHA”, “olá mundo” •bool - Representa os valores booleanos: Verdadeiro (True) ou Falso (False) No Python, a função Type pode ser utilizada para mostrar o tipo de um dado. A função Type recebe um argumento e retorna seu tipo mostrado em tela. Caso Prático Crie um script PYTHON para testar a declaração variáveis e imprimir os tipos de cada uma. Copie e Teste! import numpy as np import matplotlib.pyplot as plt #DECLARANDO VARIÁVEIS ano = 2024 nomeCurso = "PYTHON" preco = 100.0 ativo = True print(type(ano)) print(type(nomeCurso)) print(type(preco)) print(type(ativo)) 22
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Saída Esperada <class 'int'> <class 'str'> <class 'float'> <class 'bool'> Ao executar o código, podemos verificar a saída do terminal com o tipo de cada variável, sendo a variável ano do tipo int (numero inteiro), a variável nomeCurso do tipo str (string ou palavra), e a varável preco do tipo float (numero decimal ou ponto flutuante) e a variável ativo é bool (booleana ou lógica). Sempre que executamos um código ou algoritmo, surge a necessidade de armazenarmos dados temporários na memória, e para isso temos o que chamamos de variáveis. Todas as variáveis utilizadas em algoritmos devem ser declaradas antes de serem utilizadas. Isto se faz necessário para permitir que o compilador reserve um espaço na memória para as mesmas. Segundo Ramalho (2015), variáveis atuam como containers que armazenam dados na memória volátil, para serem acessadas durante a execução de um programa. Como o Python é uma linguagem de tipagem dinâmica (variável) e forte (valor), a declaração de variáveis em Python é implícita. Em Python as variáveis podem ser dos tipos: Texto (String), Numérica (Inteiro ou Real) e lógicas (Booleanas - bool). Para declarar uma variável é utilizado o operador = (Igual) que atribui valores às variáveis. Após ter declarado as variáveis, podemos acessá-las a qualquer momento para fazer alguma operação, como cálculos, comparações ou qualquer outro tipo de manipulação. Copie e Teste! #DECLARANDO VARIÁVEIS ano = 2024 nomeCurso = "PYTHON" preco = 100.0 ativo = True print("ANO:", ano) print("NOME CURSO:", nomeCurso) print("PREÇO:", preco) print("ATIVO:", ativo) Saída Esperada ANO:2024 NOME CURSO: PYTHON PREÇO: 100.0 ATIVO: True Ao se criar novas variáveis, é recomendado seguir algumas regras, também conhecido como conjunto de boas práticas, conforme apresentado abaixo: • Nomes de variáveis devem começar com letras minúsculas ou sublinhado, 23
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON • Não se deve declarar variáveis com caracteres especiais (@,#,$,%,&) • A linguagem Python é case sensitive, isto é, diferencia letras maiúsculas e minúsculas 1.2.2 Analisando dados de entrada Ao ler dados do teclado, o Python faz a leitura no formato de Str ( texto ), sendo assim se quisermos trabalhar com números devemos converter o valor lido para o tipo desejado. Copie e Teste! #DECLARANDO VARIÁVEIS idadeTexto = input("Digite sua idade: ") idadeNumero = int(input("Digite sua idade: ")) print(type(idadeTexto)) print(type(idadeNumero)) Saída Esperada Digite sua idade: 100 Digite sua idade: 100 <class 'str'> <class 'int'> Observe que apesar de praticamente iguais são duas variáveis diferentes com tipos diferentes, já que o comando int(input("Digite sua idade: ")) converte o texto em um número do tipo float. Fique Alerta! A conversão de dados é importante principalmente quando vamos realizar operações matemáticas com números. Como o Python trata os dados de forma rigorosa, ele não permite, por exemplo, somar um número a um texto. Veja dois cenários práticos onde a conversão é indispensável: •Leitura de Entradas do Usuário: Quando um programa solicita a idade de uma pessoa com a função input(), o Python captura esse dado como uma string (texto). Se o usuário digitar ”25”, o programa armazena o texto ”25”. Tentar realizar uma operação como idade + 1 resultaria em um erro. É necessário converter explicitamente a string ”25”para o inteiro 25 usando a função int() para que o cálculo seja viável. •Cálculos Financeiros e Científicos: Imagine um sistema que calcula o preço final de um produto. A quantidade pode ser um número inteiro (int), mas o preço unitário é um número de ponto flutuante (float). Para garantir a precisão do resultado, pode ser necessário converter o valor da quantidade para float antes da multiplicação, assegurando que o resultado final mantenha as casas decimais corretas, algo essencial em contextos financeiros.” 24
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON 1.3.3 Estruturas de Repetição (for e while) Muitas das tarefas mais poderosas da programação envolvem a automação de processos repetitivos. Imagine ter que processar cada um dos mil itens de um estoque ou calcular a média de notas para cada aluno de uma turma. Escrever o código para cada item individualmente seria impraticável. É para resolver esse tipo de problema que existem as estruturas de repetição, também conhecidas como laços ou loops. O domínio dessas estruturas é um passo essencial no aprendizado de algoritmos e lógica, sendo um pilar na introdução à programação com Python (Menezes, 2019). Elas nos dão o poder de definir um bloco de código e instruir o computador a executá-lo múltiplas vezes, seja um número fixo de vezes ou enquanto uma determinada condição for verdadeira. Em Python, existem duas principais estruturas para realizar essa tarefa: • O laço for, utilizado para iterar sobre os elementos de uma sequência (como uma lista ou uma tupla). É a escolha ideal quando se sabe previamente o conjunto de itens a ser percorrido. • O laço while, que executa um bloco de código repetidamente enquanto uma condição específica for avaliada como verdadeira. É perfeito para situações em que o número de repetições não é conhecido de antemão. Laço FOR O for permite percorrer os itens de uma coleção (Lista, String, Tuplas, Dicionários) e, para cada um deles, executar um bloco de código. O for é utilizado para percorrer ou iterar sobre uma sequência de dados, executando um conjunto de instruções em cada item. A sintaxe básica do for pode ser escrita da seguinte forma: f or < v a r i a v e l _ d e _ i t e r a c a o > in <sequencia > : # Bloco de código que será executado para cada item # A < v a r i a v e l _ d e _ i t e r a c a o > guarda o item a t u a l da sequ ência Veja o exemplo onde temos uma lista, uma estrutura de dados que já veremos com mais detalhes em breve. Será utilizado o laço de repetição FOR para percorrer cada elemento dessa lista e imprimir seu valor na tela de saída. Copie e Teste! lista = [10,20,30,40,50] for item in lista: print(item) Saída Esperada 10 20 30 40 50 31
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Caso Prático Imagine que você tem uma lista de números inteiros, e deseja dobrar o valor de cada elemento e adicioná-lo a uma nova lista. Nesse caso, podemos criar uma nova lista vazia, e para cada elemento da nossa lista, dobramos o seu valor e o adicionamos à nova lista. Copie e Teste! lista = [10,20,30,40,50] listaDobro = [] for item in lista: listaDobro.append(item * 2) print("========Saída===========================") print("LISTA: ", lista) print("LISTA COM OS VALORES DOBRADOS: ",listaDobro) print("=========================================") Saída Esperada ========Saída=========================== LISTA: [10, 20, 30, 40, 50] LISTA COM OS VALORES DOBRADOS: [20, 40, 60, 80, 100] ========================================= A saída confirma que o laço for percorreu cada item da lista original. Para cada elemento a operação matemática (item * 2) foi executada e o resultado foi adicionado à listaDobro com o método .append(). Caso Prático Imagine que você tem uma lista de supermercado e deseja imprimir todos os elementos dessa lista no seguinte formato “PRODUTO: NOME”. Copie e Teste! listaSupermercado = ["Arroz","Farinha","Açúcar"] for produto in listaSupermercado: print("PRODUTO :", produto) Saída Esperada PRODUTO: Arroz PRODUTO: Farinha PRODUTO: Açucar 32
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Conhecendo um pouco mais! Material Extra Conheça um pouco mais a estrutura de repetição FOR em Python. . https://www.hashtagtreinamentos.com/estrutur as-de-repeticao-python?gad_source=1&gclid=Cjw KCAiAxea5BhBeEiwAh4t5KzH-d_Snz95z0Cs2p9iHtLl RVVfvFgpjE74lRQGpGaN2d66jOFqS1RoCljIQAvD_BwE ou aponte a câmera do seu smartphone para o Qr Code ao lado A função RANGE() é uma função nativa do Python que retorna uma sequência de números, começando em 0 por padrão e incrementa em 1 (por padrão) e termina em um número especificado. Em outras palavras, a função RANGE() cria uma lista e retorna essa lista que podemos usar em uma estrutura de repetição para executar uma tarefa inúmeras vezes. Caso Prático Imagine que você queira executar uma tarefa 5 vezes, utilizando for, mas você não tem uma lista para percorrer. Você pode fazer isso usando o RANGE(). Copie e Teste! for iin range(5): print("Executando alguma coisa...") Saída Esperada Executando alguma coisa... Executando alguma coisa... Executando alguma coisa... Executando alguma coisa... Executando alguma coisa... Conhecendo um pouco mais! Material Extra Conheça um pouco mais a estrutura de repetição FOR com a função RANGE(). https://www.youtube.com/watch?v=55rOjj6kEck ou aponte a câmera do seu smartphone para o Qr Code ao lado 33
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Laço WHILE Utilizando o while, podemos executar de forma repetida um conjunto de instruções enquanto uma condição for verdadeira. No exemplo à seguir, a mensagem executando... é impressa na tela até que a condição contador < 6 seja satisfeita. Copie e Teste! contador = 1 while contador <= 5: print("Executando ",contador, "Vez") contador += 1 Saída Esperada Executando 1 Vez Executando 2 Vez Executando 3 Vez Executando 4 Vez Executando 5 Vez Observe que toda vez que o laço de repetição é executado, o contador é incrementado, ou seja, uma unidade é somada ao contador. Isso é importante, pois temos que garantir que uma hora a condição será falsa para finalizar a repetição. Sendo assim, o laço de repetição será executado 5 vezes. 1.3.4 Aplicando seus Conhecimentos 1. Escreva um programa que leia a idade de uma pessoa, e informe se a pessoa é maior ou menor de idade. 2. Escreva um programa que, dados 2 números diferentes (a e b), encontre o menor deles. 3. Leia dois números do teclado e efetue a soma desses números. Caso o valor somado seja maior que 20, este deverá ser impresso na tela, somando-se a ele mais 8, caso o valor somado seja menor ou igual a 20, este deverá ser apresentado subtraindo-se 5. 4. Faça um programa que pergunte a idade e peso para uma pessoa e diga se ela pode doar sangue ou não. Para doar sangue é necessário • Pesar mais de 50 kg • Ter entre 16 e 69 anos 5. Faça um programa que pede duas notas de um aluno. Em seguida ele deve calcular a média do aluno e dar o seguinte resultado. • A mensagem ”Aprovado”, se a média alcançada for maior ou igual a sete • A mensagem ”Reprovado”, se a média for menor do que sete 34
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON 6. Utilize laços de repetição para elaborar um código cuja entrada sejam 5 números inteiros, e a saída retorne a quantidade de números pares e a quantidade de números ímpares. • Use variáveis para acumular a quantidade de números pares e a quantidade de números ímpares. • Use o operador Módulo ( % ) para saber se um número é par. Exemplo: 7 % 2 = 1 (Número ímpar), 8 % 2 = 0 ( Número par). 7. Utilize laços de repetição combinandos com a função RANGE() 1.4 Estruturas de Dados Essenciais Até este ponto, focamos em como armazenar valores individuais em variáveis, como um único número ou uma string. No entanto, a maioria dos problemas do mundo real exige o gerenciamento de conjuntos de dados: uma lista de alunos, as características de um produto ou as coordenadas de um mapa. Armazenar cada um desses valores em uma variável separada seria ineficiente e impraticável. É para resolver essa questão que a programação nos oferece as estruturas de dados. Conforme demonstrado em obras práticas como a de Matthes (2023), essas estruturas são a base para a construção de projetos complexos, permitindo-nos modelar coleções de informações de forma organizada e acessível. Uma estrutura de dados, portanto, é um formato especializado para organizar, processar e armazenar múltiplos elementos de forma coesa. A escolha da estrutura correta é uma decisão fundamental no desenvolvimento de um software. Como aponta Ramalho (2015), entender as características e o uso idiomático de cada estrutura de dados em Python é o que diferencia um programador iniciante de um programador fluente, impactando diretamente a performance e a clareza do código. Python oferece diversas estruturas de dados nativas, cada uma com características e casos de uso específicos. Nesta seção, exploraremos as quatro mais essenciais: •Listas: Coleções ordenadas e mutáveis de elementos, ideais para quando você precisa de uma sequência flexível que pode ser alterada. •Tuplas: Similares às listas, são coleções ordenadas, porém imutáveis, usadas para garantir a integridade dos dados. •Dicionários: Estruturas que organizam dados em pares de chave-valor, permitindo uma associação lógica entre informações. •Conjuntos (Sets): Coleções não ordenadas que armazenam apenas elementos únicos, sendo extremamente eficientes para operações matemáticas de conjuntos. 1.4.1 Listas: Coleções Ordenadas e Mutáveis Listas são estruturas de dados capazes de armazenar múltiplos elementos. Uma lista é uma coleção de elementos, podendo ser do tipo String, números ou até objetos (Matthes, 2023). Para se declarar uma lista em Python basta colocar os elementos separados por vírgulas dentro de colchetes [ ]. Veja um exemplo abaixo, na qual temos uma lista, representando uma lista de supermercado com vários elementos separados por vírgulas. 35
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Copie e Teste! #Criando uma lista lista = ["Arroz","Feijão","Açúcar","Farinha","Óleo"] #Imprimindo a lista print(lista) Saída Esperada ["Arroz","Feijão", "Açúcar", "Farinha", "Óleo"] Acessando elementos de uma lista Para acessar um elemento de uma lista podemos fazer isso pelo seu índice, ou posição na lista. Lembrando que essa contagem começa a partir do zero. Observe o exemplo abaixo, onde é impresso na tela o item “Arroz”e “Feijão” que estão nas posições 0 e 1 respectivamente. Copie e Teste! #Criando uma lista lista = ["Arroz","Feijão","Açúcar","Farinha","Óleo"] #Acessando um elemento da lista pelo índice print("Item :", lista[0]) print("Item :", lista[1]) Saída Esperada Item: Arroz Item: Feijão Alterando elementos de uma lista Podemos alterar o valor de um elemento da lista, para isso usamos o operador “=” para um determinado elemento, observe que obtemos um elemento pela posição. Observe no exemplo abaixo que mudamos o elemento da primeira posição que era “Arroz” para “Leite”. Copie e Teste! #Criando uma lista lista = ["Arroz","Feijão","Açúcar"] #Acessando o elemento pelo índice e atualizando seu valor lista[0] = "Leite" 36
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Saída Esperada lista = ["Leite", "Feijão", "Açucar"] Removendo elementos de uma lista Uma lista é uma estrutura de dados mutável, ou seja, pode ter seus valores alterados. Podemos remover um item utilizando o valor que o item representa dentro da lista através do operador remove(), ou ainda, baseado na posição que ele ocupa através do operador del. Copie e Teste! #Criando uma lista lista = ["Arroz","Feijão","Açúcar","Farinha","Óleo"] print("LISTA INICIAL: ", lista) #Removendo um elemento da lista pelo índice del lista[0] print("LISTA FINAL: ", lista) Saída Esperada LISTA INICIAL: ["Arroz", "Feijão", "Açúcar", "Farinha", " Óleo"] LISTA FINAL: ["Feijão", "Açúcar", "Farinha", "Óleo"] Foi utilizado o operador del para remover o primeiro elemento da lista, identificado pela posição 0. Como pode ser observado, a lista o primeiro elemento desaparece e a lista final passa a ter apenas 4 elementos. Adicionando elementos de uma lista Podemos também adicionar elementos a uma lista através do método append(), o qual adiciona o elemento no final da lista. Para ilustrar seu comportamento temos o exemplo abaixo de uma lista de amigos inicialemnte vazia. Copie e Teste! #Criando a lista vazia amigos = [] #Adicionando elementos na lista amigos.append("JOÃO") amigos.append("MARIA") amigos.append("ANA") #Imprimindo lista de amigos print("LISTA DE AMIGOS :", amigos) 37
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Saída Esperada LISTA DE AMIGOS: ["JOÃO", "MARIA", "ANA"] Ordenando Listas Também é possível ordenar os elementos dentro listas, de forma crescente ou decrescente. No caso de listas cusjo elementos são números, é possível ordenador do maior para o menor e vice-versa, enquanto que listas que envolvem string é possível organizar em ordem alfabética direta e invertida. Para ordenar uma lista é utilizado o método sort(). Copie e Teste! #Criando uma lista amigos = [] amigos.append("JOÃO") amigos.append("MARIA") amigos.append("ANA") #Imprimindo a lista print("LISTA DE AMIGOS :", amigos) #Ordenando a lista amigos.sort() #Imprimindo a lista ordenada de forma crescente print("LISTA DE AMIGOS ORDENADA :", amigos) Saída Esperada LISTA DE AMIGOS: ["JOÃO", "MARIA", "ANA"] LISTA DE AMIGOS ORDENADA: ["ANA", "JOÃO", "MARIA"] 1.4.2 Tuplas: Coleções Ordenadas e Imutáveis Uma tupla em PYTHON é uma estrutura de dados similar à uma lista, na qual temos uma coleção de elementos indexados. Porém, diferentemente das listas, uma tupla é uma estrutura imutável, ou seja, após ser criada, não podemos alterar seus elementos, nem adicionar ou remover elementos da mesma. A sintaxe para se criar uma tupla em PYTHON é similar as listas, com os elementos separados por virgula mas limitados por parenteses ( ), conforme o exemplo à seguir: Copie e Teste! minhaTupla = ("André","João","Maria") print(minhaTupla) 38
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Saída Esperada ("André", "João", "Maria") Acessando elementos de uma tupla Assim como na estrutura de lista, ao se usar tuplas, podemos acessar seus elementos através da posição. No exemplo abaixo será realizado o acesso ao primeiro elemento da tupla, lembrando que a contagem no Python sempre começa do índice0. Copie e Teste! minhaTupla = ("André","João","Maria") print("Primeiro elemento da tupla :", minhaTupla[0]) Saída Esperada Primero elemento da tupla: André Tentando alterar elementos de uma tupla A principal característica que define uma tupla é a sua imutabilidade. Uma vez criada, ela se torna uma coleção de dados ”somente leitura”: não podemos alterar, adicionar ou remover seus elementos. Essa propriedade é uma garantia de segurança, assegurando que dados importantes não sejam modificados acidentalmente em outra parte do programa. Mas o que acontece quando tentamos quebrar essa regra? O interpretador Python nos impedirá com um erro claro. O exemplo a seguir tenta deliberadamente modificar o segundo elemento da tupla de ”João”para ”José”. Copie e Teste! minhaTupla = ("André","João","Maria") minhaTupla[1] = "José" Saída Esperada Traceback (most recent call last): File "C:\Users\PC\PycharmProjects\CITHA\TUPLAS\Tuplas.py", line 4, in <module> minhaTupla[1] = "Jose" ~~~~~~~~~~~^^^ TypeError: 'tuple' object does not support item assignment 39
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON A ”Saída Esperada”nos mostra um Traceback, que é o relatório de erro do Python: 1. File "...": Informa o arquivo e a linha exata onde o erro ocorreu (linha 4). 2. minhaTupla[1] = "Jose": Mostra o código problemático. 3. TypeError: 'tuple' object does not support item assignment: Esta é a mensagem mais importante. Ela nos diz que houve um ”Erro de Tipo”(TypeError) porque objetos do tipo ’tupla’ (’tuple’ object) não suportam atribuição de item (does not support item assignment), ou seja, não permitem que seus elementos sejam alterados. Este erro não é uma falha, mas sim o Python aplicando corretamente e explicando a regra de imutabilidade das tuplas. Fique Alerta! Pense nas tuplas como uma forma de ”proteger”seus dados. Por serem imutáveis, uma vez que você cria uma tupla, seus elementos não podem ser alterados, adicionados ou removidos. Isso oferece uma garantia de segurança, tornando-as a escolha perfeita para armazenar informações que devem permanecer constantes e íntegras durante toda a execução do programa, como coordenadas geográficas ou dados de configuração. 1.4.3 Dicionários: Estruturas Chave-Valor O Dicionário é uma uma estrutura de dados mais completa, e representa coleções de dados que contém na sua estrutura um conjunto de pares chave/valor, nos quais cada chave individual tem um valor associado. Para se criar um dicionário utiliza-se uma sintaxe um pouco mais elaborada do que as listas e tuplas. Os elementos do dicionário continuam sendo separados por vírgula, mas agora são delimitados por chaves . Além disso, cada elemento do dicionário é composto por 2 subelementos separados por dois pontos fazendo a separação entre chave e valor. Figura 1.20: Sintaxe de um dicionário Fonte: Elaborada pelos autores O exemplo represetando pela figura 1.20 ilustra a estrutura de um dicionário onde temos uma variável chamada produto, que possuí algumas características que são as chaves. A chave Nome possuí o valor Feijão, enquanto que a chave Quantidade possuí o valor 10. 40
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON (b) Crie uma função que receba o dicionário e o nome de um aluno, e retorne a média de suas notas. (c) Ao final, imprima a média de um aluno específico. 7. Gerenciador de Inscrições em Eventos: Imagine que você está organizando dois eventos: um Workshop de Python e uma Palestra de IA. Crie dois conjuntos para armazenar os nomes dos participantes inscritos em cada evento. Seu programa deve ser capaz de responder: (a) Quais participantes estão inscritos em ambos os eventos? (Interseção) (b) Quais são todos os participantes únicos, somando os dois eventos? (União) (c) Quais participantes se inscreveram apenas no Workshop de Python? (Diferença) 8. Verificação de Ingredientes Únicos: Um chef de cozinha tem uma lista de ingredientes para uma receita, mas alguns podem estar repetidos: ingredientes=["farin ha","açúcar","ovo","leite","açúcar","fermento","ovo"]. Use um conjunto para descobrir quantos ingredientes únicos a receita possui e imprima a lista de ingredientes únicos. 1.5 Considerações do Módulo 1 Conhecendo um pouco mais! Acessando os Materiais Práticos do Curso Agora que você concluiu os fundamentos, é hora de colocar a mão na massa. Para facilitar seus estudos e a execução dos exercícios, todos os exemplos e conjuntos de dados (datasets) utilizados no livro estão disponíveis em nosso repositório oficial do projeto no GitHub. https://github.com/CITHA-AM/Python Sugerimos duas maneiras de utilizar esses materiais: Usando os Notebooks no Google Colab: A forma mais simples e interativa de acompanhar o curso é através dos notebooks (.ipynb). A grande vantagem desta abordagem é que os notebooks já estão configurados para carregar os datasets necessários diretamente da internet, sem a necessidade de downloads. Basta abri-los no Google Colab e começar a executar o código. Trabalhando Localmente com os Scripts: Se você prefere trabalhar em um ambiente local (como o PyCharm), a melhor abordagem é baixar o conteúdo do repositório. A maneira mais fácil é ir à página principal, clicar no botão verde <> Code e selecionar Download ZIP. Fique Alerta! Atenção: Para que os scripts (.py) funcionem corretamente, o arquivo de script e seu respectivo dataset (ex: dados.csv) devem estar na mesma pasta. Por isso, ao baixar o ZIP, mantenha a estrutura de pastas original. 47
CAPÍTULO 1. FUNDAMENTOS DA PROGRAMAÇÃO COM PYTHON Parabéns por concluir o primeiro módulo! Ao longo desta jornada inicial, você construiu uma base sólida que é o alicerce de toda a programação em Python. Partimos da sintaxe e dos conceitos mais básicos da linguagem, aprendemos a manipular dados individuais com variáveis, tipos e operadores e, em seguida, descobrimos como controlar o fluxo de um programa com estruturas condicionais e laços de repetição. Por fim, exploramos as estruturas de dados essenciais — listas, tuplas, dicionários e conjuntos — que nos permitem organizar e gerenciar coleções de informações de maneira eficiente. É crucial entender que esses conceitos não são apenas comandos isolados; eles são o alfabeto e a gramática da lógica de programação. Com as ferramentas que você adquiriu neste módulo, você já é capaz de escrever programas completos que resolvem problemas reais, automatizam tarefas e processam dados de forma estruturada. O passo mais importante agora é a prática contínua. Desafie-se com novos problemas, modifique os exemplos do livro e não tenha medo de experimentar. A programação é uma habilidade que se fortalece com a aplicação. Agora que você já sabe como construir a lógica interna de um programa, o próximo módulo o levará a um novo patamar de organização e poder. Vamos explorar como encapsular blocos de código em funções, tornando nossos programas mais limpos, reutilizáveis e fáceis de manter. Além disso, aprenderemos a interagir com o mundo exterior, lendo e escrevendo em arquivos, o que permitirá que nossos programas salvem seus resultados e trabalhem com dados de fontes externas. Continue com essa dedicação, pois os conceitos a seguir expandirão enormemente o que você é capaz de criar. 48
Capítulo 2 Modularização e Persistência de Dados Iniciando o diálogo... No módulo anterior, você aprendeu os blocos de construção da programação. Agora, vamos usá-los para criar estruturas mais inteligentes e duradouras. Este capítulo foca em duas ideias poderosas: organizar seu código em blocos reutilizáveis com Funções e ensinar seu programa a lembrar informações salvando-as em Arquivos. Dominar isso é o que transforma scripts simples em aplicações robustas e funcionais. Suponha que voce pretende construir uma casa, não existe um manual específico com milhões de passos sequenciais. O projeto é dividido em tarefas especializadas, como ”fazer a fundação”ou ”instalar a parte elétrica”. Na programação, fazemos o mesmo usando Funções. Cada função é como uma equipe especializada que realiza uma tarefa específica, o que torna nosso código mais limpo, organizado e reutilizável. Em vez de reescrever o mesmo código várias vezes, você simplesmente ”chama”a função sempre que precisar. Da mesma forma, uma casa não tem utilidade se tudo dentro dela some quando você sai. Nossos programas precisam de persistência de dados para serem úteis, ou seja, a capacidade de salvar informações permanentemente, mesmo após o término da execução. Vamos explorar como ler e escrever desde simples arquivos de texto (.txt) até formatos de dados estruturados como CSV eJSON, que são essenciais para a troca de informações na tecnologia hoje. Neste capítulo, você dará o próximo passo para se tornar um programador fluente, aprendendo a construir programas que não são apenas lógicos, mas também bem-estruturados e com memória. 2.1 Funções: Organizando seu Código No início deste capítulo, comparamos a criação de um programa à construção de uma casa. Vimos que seria impraticável seguir um único manual gigante com milhões de passos sequenciais. Em vez disso, o trabalho é dividido em partes lógicas e gerenciáveis: a equipe da fundação, os eletricistas, os encanadores. Cada equipe é especialista em uma tarefa, e seu trabalho pode ser solicitado sempre que necessário. Esta é a essência da modularização, e a principal ferramenta para aplicá-la em Python são as funções. Pense em uma função como uma ”receita”ou um ”plano de ação”nomeado que realiza uma tarefa específica e bem definida. Em vez de reescrever todos os passos para ”calcular a média de notas de um aluno”toda vez que for preciso, você pode criar uma única função 49
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS chamada calcular_media(). A partir daí, sempre que necessitar dessa operação, você simplesmente ”chama”a função pelo nome, e ela executa a tarefa para você. 2.1.1 A Necessidade de Abstração Até agora, nossos programas seguiram uma lógica linear. Se precisássemos realizar a mesma operação em três partes diferentes do código, a solução mais óbvia seria copiar e colar o mesmo bloco de instruções. Imagine que você está desenvolvendo um sistema para monitorar a saúde de uma área de reflorestamento na Amazônia. Você precisa coletar dados de um igarapé próximo a partir de três sensores diferentes: • umidade do solo • temperatura do ar • qualidade da água Para cada sensor, você precisa ler o dado, verificar se ele está dentro de uma faixa aceitável e, caso não esteja, registrar um alerta. Sem o conceito de funções, seu código poderia se parecer com isto: Copie e Teste! # --- Bloco para o Sensor de Umidade --- umidade = 45.7 print(f"Lendo dado de umidade: {umidade}%") if not (30 <= umidade <= 70): print("ALERTA: Umidade do solo fora do padrão!") else: print("Umidade do solo OK.") print("-" * 20) # --- Bloco para o Sensor de Temperatura --- temperatura = 31.5 print(f"Lendo dado de temperatura: {temperatura}°C") if not (22 <= temperatura <= 30): print("ALERTA: Temperatura do ar fora do padrão!") else: print("Temperatura do ar OK.") print("-" * 20) # --- Bloco para o Sensor de pH da Água --- ph_agua = 5.8 print(f"Lendo dado de pH da água: {ph_agua}") if not (6.5 <= ph_agua <= 8.5): print("ALERTA: pH da água fora do padrão!") else: print("pH da água OK.") print("-" * 20) 50
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Saída Esperada Lendo dado de umidade: 45.7\% Umidade do solo OK. -------------------- Lendo dado de temperatura: 31.5°C ALERTA: Temperatura do ar fora do padrão! -------------------- Lendo dado de pH da água: 5.8 ALERTA: pH da água fora do padrão! -------------------- Agora, imagine que você descobre um erro na lógica ou decide mudar a forma como os alertas são exibidos. Você teria que encontrar e corrigir todas as cópias espalhadas pelo seu programa. Esse método, além de cansativo, é uma fonte perigosa de erros e vai contra um princípio fundamental da programação: DRY - Don’t Repeat Yourself Figura 2.1: Exemplo do que evitar utilizando o conceito DRY Fonte: Peakd, 2019 Repetir código é considerado uma má prática, pois torna os programas difíceis de ler, depurar e manter. As funções são a nossa principal ferramenta para seguir o princípio DRY e praticar a abstração. Abstração é a ideia de esconder os detalhes complexos de uma implementação por trás de uma interface simples. Quando você usa a função print(), por exemplo, não precisa saber como ela interage com o sistema operacional para fazer os caracteres aparecerem na tela; você 51
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS apenas confia que ela fará seu trabalho. Da mesma forma, ao criar suas próprias funções, você encapsula a lógica — o ”como fazer”— dentro de um bloco nomeado, focando apenas no ”o que a função faz”. Como discutido por Menezes (2019), o domínio dessas estruturas é um passo essencial no aprendizado de algoritmos e lógica. Fique Alerta! Abstração não é sobre remover complexidade, é sobre organizá-la. A complexidade de verificar um sensor ainda existe, mas ela está contida dentro de uma função, como uma ferramenta em uma caixa. Você não precisa ver todas as engrenagens da ferramenta para usá-la, apenas saber qual botão apertar. Caso Prático Vamos criar uma função chamada verificar_sensor. Ela vai receber o nome do sensor, seu valor, e os limites mínimo e máximo aceitáveis. A função fará todo o trabalho de verificação e impressão. Copie e Teste! def verificar_sensor(nome_sensor, valor, unidade, min_aceitavel, max_aceitavel): """ Verifica o dado de um sensor e imprime seu status. """ print(f"Lendo dado de {nome_sensor}: {valor}{unidade}") if not (min_aceitavel <= valor <= max_aceitavel): print(f"ALERTA: {nome_sensor} fora do padrao!") else: print(f"{nome_sensor} OK.") print("-" * 20) # Agora, usamos nossa funcao para cada sensor verificar_sensor("Umidade do solo", 45.7, "%", 30, 70) verificar_sensor("Temperatura do ar", 31.5, " C", 22, 30) verificar_sensor("pH da agua", 5.8, "", 6.5, 8.5) Saída Esperada Lendo dado de Umidade do solo: 45.7\% Umidade do solo OK. -------------------- Lendo dado de Temperatura do ar: 31.5°C ALERTA: Temperatura do ar fora do padrão! -------------------- Lendo dado de pH da água: 5.8 pH da água OK. -------------------- 52
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Observe a clareza do segundo código. A lógica de verificação está definida em um único lugar. Se precisarmos alterar a mensagem de alerta, mudamos apenas dentro da função verificar_sensor, e a mudança se aplicará a todos os sensores automaticamente. Isso torna nosso código mais: •Legível: É fácil entender o que o programa faz: ele verifica três sensores. •Reutilizável: Podemos usar a mesma função para um quarto, quinto, ou centésimo sensor sem escrever novo código. •Manutenível: Corrigir um erro ou fazer uma melhoria exige a edição de um único local. Dominar as funções transforma os algoritmos, tornando-os imensamente mais limpos, organizados e eficientes. Para usá-las corretamente, porém, precisamos entender suas regras e componentes. 2.1.2 Parâmetros e Retorno de Valores: A Comunicação das Funções No tópico anterior, comparamos funções a equipes de especialistas prontas para realizar uma tarefa. Mas como passamos as instruções corretas para essa equipe? Se temos uma função para calcular a média de notas, como informamos a ela quais notas devem ser usadas? E depois que a equipe termina o trabalho, como ela nos entrega o resultado? É aqui que entram dois conceitos fundamentais: parâmetros eretorno de valores. •Parâmetros são os canais de entrada de uma função. São as informações que enviamos para dentro da função para que ela possa trabalhar. Pense neles como os ingredientes que você entrega a um chef de cozinha. •Retorno de Valores é o canal de saída. É o resultado que a função nos devolve após ter concluído sua tarefa. É o prato pronto que o chef entrega ao final. Dominar essa comunicação é essencial para criar funções flexíveis e poderosas, capazes de se adaptar a diferentes dados e cenários. Vamos explorar como essa troca de informações acontece. A Anatomia de uma Função: Definindo Parâmetros Quando definimos uma função, declaramos os parâmetros que ela espera receber dentro dos parênteses. Esses parâmetros funcionam como variáveis locais, ou seja, só existem dentro da função. Revisitando o exemplo do monitoramento ambiental, criamos a função verificar_sensor que recebia cinco informações, onde cada uma delas era um parâmetro: Copie e Teste! def verificar_sensor(nome_sensor, valor, unidade, min_aceitavel, max_aceitavel): # O código da função usa esses parâmetros print(f"Lendo dado de {nome_sensor}: {valor}{unidade}") # ... resto do código ... 53
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Quando chamamos a função, os valores que passamos para ela são chamados de argumentos. Esses argumentos são atribuídos aos parâmetros na ordem em que são passados. Copie e Teste! # "Temperatura do ar", 31.5, "°C", 22, 30 são os ARGUMENTOS verificar_sensor("Temperatura do ar", 31.5, "°C", 22, 30) Nesse caso, dentro da função verificar_sensor: •nome_sensor recebe "Temperatura do ar" •valor recebe 31.5 •unidade recebe "°C" •min_aceitavel recebe 22 •max_aceitavel recebe 30 Essa forma de passar argumentos, baseada na ordem, é a mais comum e é chamada de argumentos posicionais. Tipos de Argumentos: Flexibilizando a Chamada Python nos oferece diferentes maneiras de passar argumentos para uma função, o que torna nosso código mais claro e flexível. 1. Argumentos Posicionais Como vimos, são os argumentos passados na mesma ordem em que os parâmetros foram definidos. A posição de cada argumento determina a qual parâmetro ele será atribuído. Caso Prático Você precisa criar uma função que descreva uma espécie de árvore da Amazônia, informando seu nome popular, nome científico e altura média. A ordem das informações é importante. Copie e Teste! def descrever_especie(nome_popular, nome_cientifico, altura_media) : """ Descreve uma espécie de árvore com base em seus dados. """ print(f"--- Ficha da Espécie ---") print(f"Nome Popular: {nome_popular}") print(f"Nome Científico: {nome_cientifico}") print(f"Altura Média: {altura_media} metros") # Chamando a função com argumentos posicionais descrever_especie("Sumaúma","Ceiba pentandra", 40) 54
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Saída Esperada --- Ficha da Espécie --- Nome Popular: Sumaúma Nome Científico: Ceiba pentandra Altura Média: 40 metros Fique Alerta! Com argumentos posicionais, a ordem é crucial! Se você trocar os argumentos de lugar, a função ainda funcionará, mas o resultado estará logicamente incorreto. # Chamada com ordem incorreta descrever_especie("Ceiba pentandra", 40, "Sumaúma") Saída Incorreta --- Ficha da Espécie --- Nome Popular: Ceiba pentandra Nome Científico: 40 Altura Média: Sumaúma metros 2. Argumentos Nomeados (Keyword Arguments) Para evitar a confusão da ordem, Python permite que você especifique explicitamente a qual parâmetro cada argumento pertence. Isso é feito usando o nome do parâmetro seguido de um sinal de igual (=). A grande vantagem é que a ordem dos argumentos nomeados não importa, tornando a chamada da função muito mais legível e segura. Copie e Teste! def descrever_especie(nome_popular, nome_cientifico, altura_media) : """ Descreve uma espécie de árvore com base em seus dados. """ print(f"--- Ficha da Espécie ---") print(f"Nome Popular: {nome_popular}") print(f"Nome Científico: {nome_cientifico}") print(f"Altura Média: {altura_media} metros\n") # Usando argumentos nomeados, a ordem não importa print("Chamada 1 (ordem misturada):") descrever_especie(altura_media=25, nome_popular="Açaí", nome_cientifico="Euterpe oleracea") # Você também pode misturar posicionais e nomeados # Desde que os posicionais venham primeiro! print("Chamada 2 (mista):") descrever_especie("Castanheira", altura_media=50, nome_cientifico= "Bertholletia excelsa") 55
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Saída Esperada Chamada 1 (ordem misturada): --- Ficha da Espécie --- Nome Popular: Açaí Nome Científico: Euterpe oleracea Altura Média: 25 metros Chamada 2 (mista): --- Ficha da Espécie --- Nome Popular: Castanheira Nome Científico: Bertholletia excelsa Altura Média: 50 metros 3. Valores Padrão para Parâmetros Para evitar ter que digitar a mesma informação repetidamente, podemos definir um valor padrão. Isso é feito diretamente na definição da função (def), atribuindo um valor ao parâmetro. Caso Prático Construa uma função para registrar a medição do nível de um rio. O local padrão é ”Próximo à comunidade ribeirinha”, mas deve ser possível alterá-lo. Copie e Teste! def registrar_nivel_rio(nivel_metros, local="Próximo à comunidade ribeirinha"): """ Registra a medição do nível de um rio em um local específico. """ print(f"Medição registrada:") print(f" -> Nível: {nivel_metros} metros") print(f" -> Local: {local}\n") # Chamada 1: Usando o local padrão print("--- Medição Padrão ---") registrar_nivel_rio(3.5) # Chamada 2: Especificando um local diferente print("--- Medição em Ponto Específico ---") registrar_nivel_rio(3.7, local="Foz do igarapé") Saída Esperada --- Medição Padrão --- Medição registrada: 56
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS # --- Programa Principal --- estoque_carbono_floresta = estimar_carbono_floresta(1200) print(f"Estoque de carbono na floresta: {estoque_carbono_floresta} toneladas.\n") estoque_carbono_solo = estimar_carbono_solo(350) print(f"Estoque de carbono no solo: {estoque_carbono_solo} toneladas.") Saída Esperada -> Acessando a variável global FATOR_CARBONO de dentro da função. Estoque de carbono na floresta: 600.0 toneladas. -> Outra função acessando a mesma variável global. Estoque de carbono no solo: 175.0 toneladas. O Cuidado ao Tentar Modificar Variáveis Globais Até aqui, tudo parece simples: as funções podem ler variáveis globais. Mas o que acontece se uma função tentar alterar ou modificar o valor de uma variável global? Por padrão, Python protege o escopo global. Se você tentar atribuir um novo valor a uma variável dentro de uma função que tem o mesmo nome de uma variável global, o Python, em vez de alterar a global, criará uma nova variável local com aquele nome. A variável global original permanecerá intacta. Este comportamento, conhecido como ”sombreamento”(shadowing), pode causar muita confusão. Copie e Teste! contador_alertas = 0 # Global def registrar_alerta(tipo_alerta): print(f"\n ALERTA REGISTRADO: {tipo_alerta}") # Aqui, Python cria uma NOVA variável 'contador_alertas' que é LOCAL contador_alertas = 1 print(f"-> Contagem local de alertas nesta função: { contador_alertas}") print(f"Contagem total de alertas ANTES da chamada: { contador_alertas}") registrar_alerta("Nível do rio muito alto") print(f"Contagem total de alertas DEPOIS da chamada: { contador_alertas}") 63
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Saída Esperada Contagem total de alertas ANTES da chamada: 0 ALERTA REGISTRADO: Nível do rio muito alto -> Contagem local de alertas nesta função: 1 Contagem total de alertas DEPOIS da chamada: 0 Note que o contador global não foi alterado! A função criou sua própria variável local e a modificou, deixando a global intacta. A Palavra-Chave global Como, então, podemos realmente modificar uma variável global de dentro de uma função? Para isso, usamos a palavra-chave global. Ela avisa ao Python: ”Ei, nesta função, quando eu me referir a esta variável, estou falando daquela que existe no escopo global, não crie uma nova.” Copie e Teste! contador_alertas = 0 # Global def registrar_alerta_global(tipo_alerta): global contador_alertas # Avisando ao Python: use a variável global! print(f"\n ALERTA REGISTRADO: {tipo_alerta}") contador_alertas = contador_alertas + 1 print(f"-> Contagem de alertas atualizada: {contador_alertas}" ) print(f"Contagem inicial: {contador_alertas}") registrar_alerta_global("Temperatura elevada") registrar_alerta_global("Desmatamento detectado") print(f"Contagem final: {contador_alertas}") Saída Esperada Contagem inicial: 0 ALERTA REGISTRADO: Temperatura elevada -> Contagem de alertas atualizada: 1 ALERTA REGISTRADO: Desmatamento detectado -> Contagem de alertas atualizada: 2 Contagem final: 2 64
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Fique Alerta! Use a palavra-chave global com extremo cuidado. Modificar variáveis globais de dentro de muitas funções pode tornar seu código muito difícil de entender e depurar. Se uma variável global muda de valor, você terá que investigar todas as funções que a utilizam para descobrir qual delas fez a alteração. Em geral, a melhor prática é preferir a comunicação via parâmetros e return. Se uma função precisa de um valor, passe-o como parâmetro. Se ela produz um resultado, retorneo. Esta abordagem, discutida em textos clássicos como Lutz (2013), mantém suas funções independentes e seu código mais organizado e previsível. 2.1.4 Boas Práticas: Docstrings e Sugestões de Tipo (Type Hints) Até agora, você aprendeu a construir funções, que são as ”equipes de especialistas”do nosso código. Mas como garantimos que outra pessoa (ou até mesmo você, daqui a alguns meses) entenda exatamente o que sua equipe faz, quais ”ferramentas”ela precisa e o que ela ”entrega”no final? E como podemos deixar essas instruções tão claras que até o próprio editor de código nos ajude a evitar erros? Nesta seção, vamos refinar nossas habilidades e aprender a escrever funções como um profissional. Abordaremos duas práticas essenciais que, embora não alterem o que a função faz, transformam radicalmente a clareza, a segurança e a manutenibilidade do nosso código: as Docstrings e as Sugestões de Tipo (Type Hints). Docstrings: O Manual de Instruções da sua Função Imagine que você criou uma ferramenta incrível, como um dispositivo que mede a pureza da água de um rio. Se você entregar essa ferramenta a um colega sem nenhum manual, ele provavelmente não saberá como usá-la, para que serve cada botão ou como interpretar os resultados. O mesmo acontece com nossas funções. Uma docstring (documentation string, ou ”string de documentação”) é o manual de instruções oficial de uma função em Python. É um texto, delimitado por aspas triplas ("ou '''), que fica na primeira linha logo após a definição da função. Seu propósito é explicar, de forma clara e concisa, o que a função faz. Fique Alerta! Uma docstring não é um comentário (#). Comentários são feitos para explicar como uma parte específica do código funciona. Docstrings explicam o quê a função como um todo faz, para quem vai usá-la sem precisar ler todo o seu código interno. Além disso, ferramentas automáticas e a função nativa help() utilizam as docstrings. Embora você possa escrever qualquer coisa em uma docstring, a comunidade Python adota padrões para que elas sejam fáceis de ler e processar por ferramentas. Um formato comum e legível inclui: 1. Resumo de uma linha: O que a função faz, de forma direta. 2. Descrição detalhada (opcional): Mais detalhes sobre o comportamento da função. 65
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS 3. Parâmetros (Argumentos): Uma lista de cada parâmetro, seu tipo esperado e o que ele representa. 4. Retorno: O que a função retorna, seu tipo e o que esse valor significa. Caso Prático Uma ONG de conservação precisa de uma função para estimar o estoque de carbono de uma árvore, uma métrica vital para projetos de reflorestamento na Amazônia. A função deve receber a espécie da árvore e o diâmetro do seu tronco em centímetros, e retornar uma estimativa da biomassa em quilogramas. Vamos criar essa função e documentá-la corretamente. Copie e Teste! def estimar_biomassa_arvore(especie, diametro_cm): """Estima a biomassa de uma árvore com base em sua espécie e diâmetro. Utiliza um fator de conversão simplificado que varia conforme a espécie para calcular a biomassa acima do solo. Não é um modelo científico preciso, mas serve como exemplo. Args: especie (str): O nome popular da espécie da árvore. diametro_cm (float): O diâmetro do tronco medido à altura do peito (DAP), em centímetros. Returns: float: A biomassa estimada da árvore em quilogramas (kg)." "" if especie == "Sumaúma": fator = 0.65 else: fator = 0.55 # Fator genérico para outras espécies # Fórmula simplificada: biomassa = fator * (diametro^2) biomassa_kg = fator * (diametro_cm ** 2) return biomassa_kg # Agora, vamos ver como acessar nossa documentação help(estimar_biomassa_arvore) Saída Esperada Help on function estimar_biomassa_arvore in module __main__: estimar_biomassa_arvore(especie, diametro_cm) Estima a biomassa de uma árvore com base em sua espécie e diâmetro. Esta função utiliza um fator de conversão simplificado que varia conforme a espécie para calcular a biomassa acima do solo. Não é um modelo científico 66
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS preciso, mas serve como exemplo. Args: especie (str): O nome popular da espécie da árvore. diametro_cm (float): O diâmetro do tronco medido à altura do peito (DAP), em centímetros. Returns: float: A biomassa estimada da árvore em quilogramas (kg) Type Hints: Deixando seu Código Mais Claro e Seguro Se as docstrings são o manual de instruções, as sugestões de tipo (type hints) são como uma lista de ingredientes clara e específica. Em vez de dizer ”adicione açúcar”, a receita diz ”adicione 1 xícara de açúcar refinado”. Essa precisão extra ajuda a evitar erros e torna o processo muito mais claro. Type hints são uma forma moderna, introduzida nas versões mais recentes do Python, de indicar os tipos de dados esperados para os parâmetros de uma função e para o valor que ela retorna. A sintaxe é simples: •parametro: tipo para parâmetros. •-> tipo_retorno para o valor de retorno, colocado antes dos dois-pontos da função. Vamos aprimorar nossa função estimar_biomassa_arvore com type hints. Copie e Teste! def estimar_biomassa_arvore_com_tipos(especie: str, diametro_cm: float) -> float: """Estima a biomassa de uma árvore com base em sua espécie e diâmetro. Args: especie (str): O nome popular da espécie da árvore diametro_cm (float): O diâmetro do tronco medido à altura do peito (DAP), em centímetros. Returns: float: A biomassa estimada da árvore em quilogramas (kg)"" " if especie == "Sumaúma": fator = 0.65 else: fator = 0.55 biomassa_kg = fator * (diametro_cm ** 2) return biomassa_kg 67
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS # Chamada correta biomassa_castanheira = estimar_biomassa_arvore_com_tipos(" Castanheira", 120.5) print(f"Biomassa da Castanheira: {biomassa_castanheira:.2f} kg") # O que acontece se passarmos os tipos errados? # O código AINDA RODA, mas um verificador de tipos acusaria um erro aqui! biomassa_errada = estimar_biomassa_arvore_com_tipos(150.0, "Açaí") print(f"Resultado da chamada incorreta: {biomassa_errada}") Saída Esperada Biomassa da Castanheira: 7986.14 kg Traceback (most recent call last): File "<python-input-0>", line 24, in <module> biomassa_errada = estimar_biomassa_arvore_com_tipos (150.0, "Açaí") ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ File "<python-input-0>", line 15, in estimar_biomassa_arvore_com_tipos biomassa_kg = fator * (diametro_cm ** 2) ~~~~~~~~~~~~^^~~ TypeError: unsupported operand type(s) for ** or pow(): 'str ' and 'int' Podemos verificar que o primeiro caso rodou corretamente, mas o segundo caso ele acusa um erro na função biomassa_errada. Então ele descreve que essa linha utiliza a função biomassa_kg a qual não está conseguindo fazer a operação diametro_cm ** 2, através da marcação ~~~~^^~~. Analisando o TypeError, verificamos que a falha está na operação de potencia (** indicada pela marcação ^^), afinal estamos tentando elevar ao quadrado uma string. Saída Esperada teste_tipos.py:9: error: Argument 1 to " estimar_biomassa_arvore" has incompatible type "float"; expected "str" [arg-type] teste_tipos.py:9: error: Argument 2 to " estimar_biomassa_arvore" has incompatible type "str"; expected "float" [arg-type] Found 2 errors in 1 file (checked 1 source file) Verificando o Type Hint, é possível perceber que há incompatibilidade relacionada ao tipo das variáveis em ambos os argumentos da função estimar_biomassa_arvore. 68
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Os benefícios dos type hints são imensos, especialmente em projetos maiores, conforme ressaltado por autores como Luciano Ramalho em sua obra ”Python Fluente”(Ramalho, 2015): •Legibilidade: Fica instantaneamente claro que tipo de dado a função espera e retorna. •Detecção de Erros: Editores de código modernos (IDEs) e ferramentas externas como omypy usam esses ”hints”para verificar seu código antes de você executá-lo, alertando sobre possíveis erros de tipo. •Autocompletar Inteligente: O editor de código consegue identificar que a variável biomassa_castanheira é um float, então ele irá sugerir métodos aplicáveis a números, e não a strings. •Facilita a Colaboração: Quando outra pessoa for usar sua função, ela saberá exatamente que tipo de dado deve passar. Tipos Mais Complexos E se sua função trabalha com listas ou dicionários? O módulo typing do Python nos dá acesso a tipos mais específicos. Caso Prático Um sistema de monitoramento ambiental coleta dados de vários sensores e os armazena em uma lista de dicionários. Cada dicionário contém o nome do sensor e seu último valor medido. Precisamos de uma função que processe essa lista e retorne o nome do sensor com a maior leitura. Copie e Teste! # Importamos os tipos que precisamos do módulo typing from typing import List, Dict, Optional def encontrar_sensor_com_maior_valor(leituras: List[Dict[str, float]]) -> Optional[str]: """Analisa uma lista de leituras de sensores e encontra o sensor com maior valor. Args: leituras: Uma lista onde cada item é um dicionário. Cada dicionário deve ter uma chave 'sensor' (str ) e 'valor' (float). Returns: O nome (str) do sensor que registrou o maior valor. Retorna None se a lista de leituras estiver vazia. """ if not leituras: return None # Retorna None para indicar que nenhum sensor foi encontrado maior_leitura = -1.0 sensor_destaque = "" 69
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS for leitura in leituras: # Usamos .get() para acessar as chaves de forma segura nome_sensor = leitura.get('sensor','Desconhecido') valor = leitura.get('valor', -1.0) if valor > maior_leitura: maior_leitura = valor sensor_destaque = nome_sensor return sensor_destaque # Dados coletados de um igarapé dados_sensores = [ {'sensor':'pH','valor': 6.8}, {'sensor':'Turbidez','valor': 4.5}, {'sensor':'Oxigênio Dissolvido','valor': 7.2} ] sensor_alerta = encontrar_sensor_com_maior_valor(dados_sensores) print(f"Sensor com maior leitura: {sensor_alerta}") # Testando com uma lista vazia sensor_vazio = encontrar_sensor_com_maior_valor([]) print(f"Resultado para lista vazia: {sensor_vazio}") Saída Esperada Sensor com maior leitura: Oxigênio Dissolvido Resultado para lista vazia: None Neste exemplo, List[Dict[str, float]] informa que esperamos uma lista do tipo (List) onde cada elemento é um dicionário (Dict) com chaves do tipo string (str) e valores do tipo float (float). Optional[str] no retorno indica que a função pode retornar uma string ou None, tornando o código mais seguro e explícito sobre seus possíveis resultados. A adoção dessas práticas segundo Lutz (2013), é um passo fundamental para escrever código robusto e de fácil manutenção. 2.1.5 Funções Anônimas (Lambda) Até agora, todas as funções que criamos tinham um nome, como calcular_media ou verificar_sensor. Nós as definimos com def porque planejávamos reutilizá-las em vários pontos do nosso programa. Mas e se precisarmos de uma função muito simples, para uma tarefa rápida e que só será usada uma única vez? Seria como construir uma ferramenta complexa e permanente para apertar um único parafuso. Para essas situações o Python nos oferece uma sintaxe mais direta e compacta, as funções anônimas mais conhecidas como funções lambda. Pense em uma função lambda como um ”ajudante temporário”. Em vez de contratar um especialista em tempo integral (uma função def), você chama um assistente para uma tarefa 70
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS única e imediata. Essas funções não têm nome (”anônimas”) e são definidas em uma única linha de código, o que as torna perfeitas para operações pequenas e pontuais. A Sintaxe de uma Função Lambda A estrutura de uma função lambda é notavelmente simples e foi projetada para ser concisa, se resumindo a uma única linha: lambda argumentos: expressao •lambda: É a palavra-chave que sinaliza ao Python a criação de uma função anônima. •argumentos: São os parâmetros que a função recebe, exatamente como em uma função def, mas sem a necessidade de parênteses. Se houver mais de um, eles são separados por vírgulas (ex: lambda x, y:). •:: O caractere de dois-pontos separa os argumentos da operação que será realizada. •expressao: Esta é a parte crucial, uma única expressão (uma operação, um cálculo) que a função executa. O resultado desta expressão é automaticamente retornado, sem a necessidade da palavra-chave return. Caso Prático Em seu projeto de monitoramento na Amazônia, você frequentemente precisa converter o diâmetro de uma árvore para o seu raio. Implemente uma solução que compare o paradigma tradicional (def) com o paradigma funcional (lambda). Copie e Teste! # Usando uma função def tradicional def calcular_raio(diametro): return diametro / 2 # A mesma lógica com uma função lambda calcular_raio_lambda = lambda diametro: diametro / 2 # Vamos testar as duas diametro_arvore = 90 # em cm print(f"Raio (calculado com def): {calcular_raio(diametro_arvore)} cm") print(f"Raio (calculado com lambda): {calcular_raio_lambda( diametro_arvore)} cm") Saída Esperada Raio (calculado com def): 45.0 cm Raio (calculado com lambda): 45.0 cm 71
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Talvez possa parecer que o fato de atribuímos a função lambda a uma certa variável (calcular_raio_lambda), faria com que ela deixasse de ser ”anônima”. Tecnicamente sim, no entanto, este não é o uso mais comum ou poderoso das lambdas. A verdadeira força delas aparece quando as usamos como argumentos para outras funções de ordem superior. Onde as Lambdas Brilham: Funções como Argumentos O cenário mais comum para o uso de funções lambda é quando uma função precisa receber outra função como parâmetro. Funções como sort(),map() efilter() em Python operam sobre coleções de dados e, muitas vezes, precisam de uma pequena ”instrução”ou ”regra”para saber como devem realizar sua tarefa. Caso Prático Uma equipe de pesquisadores coletou dados sobre a turbidez da água em diferentes pontos de um rio. Os dados estão em uma lista de dicionários e a equipe precisa ordenar essa lista, não em ordem alfabética pelo nome do local, mas sim pelo valor da turbidez, do menor para o maior. Copie e Teste! dados_coleta = [ {'local':'Foz do Igarapé','turbidez': 4.8}, {'local':'Comunidade Ribeirinha','turbidez': 2.1}, {'local':'Reserva Florestal','turbidez': 1.5}, {'local':'Ponto de Descarte','turbidez': 7.9} ] # Usando lambda como a "chave" de ordenação para o método sort() dados_coleta.sort(key=lambda item: item['turbidez']) print("Dados ordenados por turbidez da água:") for ponto in dados_coleta: print(ponto) Saída Esperada Dados ordenados por turbidez da água: {'local': 'Reserva Florestal', 'turbidez': 1.5} {'local': 'Comunidade Ribeirinha', 'turbidez': 2.1} {'local': 'Foz do Igarapé', 'turbidez': 4.8} {'local': 'Ponto de Descarte', 'turbidez': 7.9} No exemplo acima, o parâmetro key do método sort() espera uma função. Essa função receberá cada elemento da lista (cada dicionário, que chamamos de item) e deverá retornar o valor que será usado para a ordenação. A função utilizada nesse exemplo tem exatamente esse papel, já que de forma anônima e em uma única linha, lambda item: item['turbidez'] instrui o sort a olhar para o valor da chave 'turbidez' em cada 72
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Saída Esperada Saída Esperada (em Windows) O caminho construído é: dados\relatorio_final.csv Caminho mais complexo: ProjetoCITHA\dados\sensores\log.txt Saída Esperada (em Linux/macOS) O caminho construído é: dados/relatorio_final.csv Caminho mais complexo: ProjetoCITHA/dados/sensores/log.txt Com o entendimento sólido de arquivos e caminhos, agora estamos prontos para a parte prática: abrir esses arquivos para ler as informações que eles contêm e escrever nossos próprios dados para guardá-los de forma permanente. 2.2.2 Lendo e Escrevendo em Arquivos de Texto Até agora, nossos programas funcionaram como uma conversa: as informações que criamos com variáveis e listas existem apenas enquanto o programa está em execução. Quando ele termina, tudo é esquecido. Mas e se quiséssemos criar um diário de campo para anotar as espécies de aves que observamos, ou registrar os dados de um sensor de qualidade da água de um igarapé ao longo de vários dias? Para isso, nosso programa precisa de uma ”memória”permanente. Ele precisa aprender a escrever em um caderno. No mundo digital, esse caderno é um arquivo de texto. Nesta seção, vamos aprender a habilidade fundamental de salvar nossos dados em arquivos .txt e, depois, lê-los de volta. Esta é a forma mais direta de persistência de dados, transformando nossos programas de meros executores de tarefas em verdadeiros guardiões de informação. Figura 2.4: Fluxo de ações envolvendo arquivos de texto em Python Fonte: Ravish Kumar, 2024 Abrindo e Escrevendo em um Arquivo: O Bloco with open A maneira mais segura e recomendada de trabalhar com arquivos em Python é usando o bloco with open(). Pense nele como um assistente cuidadoso: ele abre o arquivo para você, permite que você faça o que precisa (ler ou escrever) e, o mais importante, garante que o arquivo seja fechado corretamente no final, mesmo que ocorra um erro. 79
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Para escrever em um arquivo, usamos o modo de abertura 'w' (do inglês, write). Este modo diz ao Python para criar um novo arquivo (ou apagar completamente um existente) para que possamos escrever nele. Caso Prático Vamos criar nosso primeiro registro permanente. Imagine que estamos instalando uma nova estação de monitoramento ambiental em uma reserva. Nossa primeira tarefa é criar um arquivo de log para registrar o início das operações. Copie e Teste! # O nome do arquivo que queremos criar nome_arquivo = "log_estacao_1.txt" # Usamos 'with open' para abrir o arquivo em modo de escrita ('w') # A variável 'arquivo' nos dá acesso para interagir com o arquivo with open(nome_arquivo, 'w')as arquivo: # O método .write() escreve uma string no arquivo arquivo.write("Registro de operacoes da Estacao de Monitoramento Alpha.\n") arquivo.write("Dados coletados a partir de 24/07/2025.") print(f"Arquivo '{nome_arquivo}' criado com sucesso!") Saída Esperada Arquivo 'log_estacao_1.txt' criado com sucesso! Após executar este código, um novo arquivo chamado log_estacao_1.txt aparecerá na mesma pasta do seu script Python. Se você abri-lo, verá o seguinte conteúdo: Saída Esperada Registro de operacoes da Estacao de Monitoramento Alpha. Dados coletados a partir de 24/07/2025. O método .write() por padrão não pula linha quando realizamos uma nova chamada da função print(), portanto, foi necessário adicionar a quebra de linha manualmente através do caractere \n ao final da primeira string. Fique Alerta! Cuidado: O modo 'w' é destrutivo! Se o arquivo log_estacao_1.txt já existisse, seu conteúdo antigo seria completamente apagado e substituído pelo novo. Sempre tenha certeza de que deseja sobrescrever um arquivo antes de usar o modo 'w'. 80
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Adicionando Informações: O Modo de Anexação ('a') Sobrescrever um arquivo nem sempre é o que queremos. Em um diário de campo, por exemplo, não apagamos as anotações antigas para escrever as novas; nós as adicionamos no final. Para este comportamento, usamos o modo de anexação, ou append, representado pela letra 'a'. Caso Prático Nossa equipe de biólogos fez novas observações de campo e precisa adicioná-las a um diário. O arquivo diario_fauna.txt já existe e contém registros anteriores. Precisamos adicionar as novas observações sem apagar as antigas. Copie e Teste! # Lista de novas observacoes novas_observacoes = [ "Observado um bando de araras-canga (Ara macao).", "Rastros de onca-pintada (Panthera onca) perto do igarape.", "Ninho de uirapuru-verdadeiro (Cyphorhinus arada) encontrado", "Fui picado..." ] # Abrindo o arquivo em modo de anexacao ('a') with open("diario_fauna.txt",'a')as diario: # Adicionando um separador para organizar diario.write("\n--- Novas Observacoes ---\n") # Percorrendo a lista e escrevendo cada nova observacao for obs in novas_observacoes: diario.write(obs + "\n") print("Diario de campo atualizado!") Se o arquivo diario_fauna.txt continha ”Registro inicial.”antes da execução, após executar o script, seu conteúdo será: Saída Esperada Registro inicial. --- Novas Observacoes --- Observado um bando de araras-canga (Ara macao). Rastros de onca-pintada (Panthera onca) perto do igarape. Ninho de uirapuru-verdadeiro (Cyphorhinus arada) encontrado. Fui picado... O modo 'a' é considerada uma escolha segura para adicionar informações a um arquivo de log ou a qualquer registro contínuo, já que os dados são incluídos de forma incremental, garantindo que os dados históricos sejam preservados (Matthes, 2023). 81
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Lendo o Conteúdo de um Arquivo Agora que sabemos como guardar informações, precisamos aprender a recuperá-las. Para isso, abrimos o arquivo em modo de leitura, ou read, representado pela letra 'r'. Este é o modo padrão, mas é uma boa prática especificá-lo para maior clareza. Existem algumas maneiras de ler o conteúdo de um arquivo. 1. Lendo o Arquivo Inteiro: .read() O método .read() lê todo o conteúdo do arquivo e o retorna como uma única string. Útil para arquivos pequenos, mas pode consumir muita memória se o arquivo for muito grande. Copie e Teste! # Lendo o conteudo completo do nosso diario with open("diario_fauna.txt",'r')as diario: conteudo_completo = diario.read() print("--- Conteudo completo do Diario ---") print(conteudo_completo) Saída Esperada --- Conteudo completo do Diario --- Registro inicial. --- Novas Observacoes --- Observado um bando de araras-canga (Ara macao). Rastros de onca-pintada (Panthera onca) perto do igarape. Ninho de uirapuru-verdadeiro (Cyphorhinus arada) encontrado. Fui picado... 2. Lendo Linha por Linha: A Abordagem Mais Eficiente A forma mais comum e eficiente em Python para ler um arquivo é iterar sobre ele linha por linha com um laço for. Esta abordagem é ideal para arquivos grandes, pois lê apenas uma linha para a memória de cada vez. Mas o que isso realmente significa na prática? Imagine que um sensor de monitoramento na Amazônia gera um arquivo de log com milhões de registros de temperatura, um por linha. Se tentássemos usar um método como .read() para carregar esse arquivo inteiro, poderíamos facilmente esgotar a memória do computador, fazendo o programa travar. Ao usar um laço for, o Python adota uma estratégia de leitura preguiçosa (lazy reading). Ele não carrega o arquivo todo de uma vez. Em vez disso, a cada passagem do laço, ele vai até o disco, busca a próxima linha, a entrega para o seu código processar e, em seguida, a descarta da memória para buscar a próxima. Isso garante que seu programa possa processar arquivos de qualquer tamanho — de alguns kilobytes a muitos gigabytes — com a mesma eficiência e baixo uso de memória, tornando seu código mais robusto e escalável. 82
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Caso Prático Um sensor de temperatura nos enviou um arquivo temperaturas.txt com uma leitura por linha. Precisamos ler este arquivo e identificar qualquer temperatura acima de 35°C, que indica uma possível onda de calor na floresta. Copie e Teste! # Conteudo do arquivo temperaturas.txt (imagine que ele ja existe) : # 32.5 # 34.1 # 36.2 # 33.8 # 35.5 print("Analisando temperaturas em busca de alertas:") with open("temperaturas.txt",'r')as f: for linha in f: # .strip() remove espacos em branco e novas linhas ('\n') temperatura_str = linha.strip() # Convertemos a string para um numero float temperatura_float = float(temperatura_str) if temperatura_float > 35.0: print(f"ALERTA: Temperatura elevada detectada! -> { temperatura_float} C") Saída Esperada Analisando temperaturas em busca de alertas: ALERTA: Temperatura elevada detectada! -> 36.2 C ALERTA: Temperatura elevada detectada! -> 35.5 C Esta abordagem combina leitura de arquivo com a lógica de programação que já conhecemos (laços, condicionais, conversão de tipo), mostrando como a manipulação de arquivos se integra perfeitamente ao nosso fluxo de trabalho para resolver problemas práticos e significativos. 2.2.3 Trabalhando com Dados Estruturados: CSV No tópico anterior, aprendemos a usar arquivos de texto como um caderno de anotações, perfeito para salvar um diário de campo ou registros sequenciais. Mas, e se nossos dados tiverem uma estrutura mais organizada, como uma tabela? Imagine um pesquisador na Amazônia que coleta dados sobre árvores: para cada uma, ele anota a espécie, a altura, o diâmetro do tronco e as coordenadas geográficas. Tentar salvar isso em um arquivo .txt simples seria confuso e difícil de processar depois. 83
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS É para isso que servem os arquivos CSV (Comma-Separated Values, ou Valores Separados por Vírgula). Pense neles não como um caderno, mas como uma planilha digital. São arquivos de texto com uma regra simples: cada linha representa uma fileira da tabela e as vírgulas separam os valores de cada coluna. Este formato é universal, legível tanto por humanos quanto por máquinas, e é o padrão para exportar dados de programas como Excel e Google Sheets. Nesta seção, vamos aprender a dominar este formato com o módulo nativo do Python: csv. Um arquivo CSV é a forma mais comum de armazenar dados tabulares. A sua estrutura é direta: a primeira linha geralmente contém os cabeçalhos (os nomes das colunas), e as linhas seguintes contêm os dados, onde cada valor é separado por um delimitador, que, por padrão, é uma vírgula. A B C 1 local ph turbidez 2Encontro das Aguas 6.8 5.2 3Comunidade Sao Jose 7.1 3.1 4Frente a Manaus 6.5 8.9 5Reserva Ducke 6.9 2.5 A 1 local,ph,turbidez 2Encontro das Aguas,6.8,5.2 3Comunidade Sao Jose,7.1,3.1 4Frente a Manaus,6.5,8.9 5Reserva Ducke,6.9,2.5 Figura 2.5: Representação visual de dados tabulares (esquerda) e seu formato correspondente em arquivo CSV (direita). Para interagir com esses arquivos de forma eficiente e segura, Python nos oferece o módulo csv, que ajuda a lida com diversas particularidades do formato de forma simples e otimizada, facilitando a vizualização e manipulação dos dados. Lendo Arquivos CSV com csv.reader A forma mais fundamental de ler um arquivo CSV é com o csv.reader. Ele processa o arquivo linha por linha e, para cada uma, nos devolve uma lista de strings, onde cada string é um valor daquela coluna. Caso Prático Uma equipe de monitoramento ambiental nos forneceu o arquivo para consulta nomeado como qualidade_agua.csv, contendo medições de pH e turbidez da água coletada em diferentes pontos do Rio Negro. Nossa primeira tarefa é ler e exibir esses dados. Antes de começar a trabalhar com o arquivo qualidade_agua.csv, é considerada uma boa prática tentar verificar se o conteúdo do arquivo está integro: Saída Esperada local,ph,turbidez Encontro das Aguas,6.8,5.2 Comunidade Sao Jose,7.1,3.1 Frente a Manaus,6.5,8.9 Reserva Ducke,6.9,2.5 84
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Fique Alerta! Quando trabalhamos com grandes volumes de dados, alguns editores de arquivos podem ter dificuldades de abrir arquivos muito grandes, consumindo muita memória RAM e causando lentidão e até travamentos. Por outro lado, IDE modernas podem ser menos simples e intuitivas do que ferramentas de edição de dados tradicionais, mas são mais robustas, resilientes e escaláveis para aplicação em projetos. Agora que já verificamos o conteúdo do arquivo, vamos usar o csv.reader para manipular e processr seu conteúdo: Copie e Teste! import csv nome_arquivo = 'qualidade_agua.csv' print(f"Lendo dados do arquivo: {nome_arquivo}\n") # O argumento newline='' é importante para evitar linhas em branco inesperadas with open(nome_arquivo, mode='r', newline='', encoding='utf-8')as arquivo_csv: # Criamos um objeto leitor leitor_csv = csv.reader(arquivo_csv) # Iteramos sobre cada linha do leitor for linha in leitor_csv: print(linha) Saída Esperada Lendo dados do arquivo: qualidade_agua.csv ['local', 'ph', 'turbidez'] ['Encontro das Aguas', '6.8', '5.2'] ['Comunidade Sao Jose', '7.1', '3.1'] ['Frente a Manaus', '6.5', '8.9'] ['Reserva Ducke', '6.9', '2.5'] Note que o csv.reader nos devolveu cada linha como uma lista de strings. Embora funcional, essa abordagem tem uma fraqueza: para acessar um dado, precisamos usar seu índice (ex: linha[1] para o pH). Isso torna o código pouco legível e frágil. E se alguém alterar a ordem das colunas no arquivo? Nosso código quebraria. A Melhor Abordagem: Lendo com csv.DictReader Para resolver a fragilidade dos índices, o Python nos oferece uma ferramenta muito mais poderosa e ”Pythônica”: o csv.DictReader. Em vez de uma lista, ele lê cada linha e a transforma em um dicionário, onde as chaves são os nomes do cabeçalho. 85
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Caso Prático Utilizando o mesmo arquivo qualidade_agua.csv, nossa nova tarefa é analisar os dados e emitir um alerta para qualquer local cujo nível de turbidez da água seja superior a 5.0, o que pode indicar poluição ou excesso de sedimentos. Copie e Teste! import csv nome_arquivo = 'qualidade_agua.csv' print("Analisando niveis de turbidez...\n") with open(nome_arquivo, mode='r', newline='', encoding='utf-8')as arquivo_csv: # Criamos um leitor que transforma cada linha em um dicionario leitor_dict = csv.DictReader(arquivo_csv) for linha in leitor_dict: # Acessamos os dados pela chave (nome da coluna) local = linha['local'] turbidez = float(linha['turbidez']) # Nao esqueca de converter para numero! if turbidez > 5.0: print(f"ALERTA: Turbidez elevada em '{local}'. Valor: {turbidez}") Saída Esperada Analisando niveis de turbidez... ALERTA: Turbidez elevada em 'Encontro das Aguas'. Valor: 5.2 ALERTA: Turbidez elevada em 'Frente a Manaus'. Valor: 8.9 O código agora é muito mais legível e robusto. Acessar linha['turbidez'] é mais claro que linha[2], e se a ordem das colunas no arquivo mudar, nosso código continuará funcionando perfeitamente. Como aponta Matthes (2023), usar as ferramentas certas para a estrutura de dados correta é uma marca de um programador eficaz. Escrevendo em Arquivos CSV Assim como é possível ler, também é possível escrever dados em arquivos CSV. Isso é extremamente útil para salvar os resultados de nossas análises ou para criar conjuntos de dados que podem ser facilmente abertos em outros programas. Para isso, utilizamos o método csv.writer para facilitar essa escrita e manipulação dos arquivos. 86
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Caso Prático Após uma expedição, nossa equipe catalogou várias espécies de peixes da bacia amazônica. Os dados estão em uma lista de listas dentro do nosso programa. Precisamos salvar esses dados em um arquivo chamado peixes_catalogados.csv. Copie e Teste! import csv # Nossos dados: a primeira lista é o cabeçalho dados_peixes = [ ['nome_popular','nome_cientifico','risco_extincao'], ['Pirarucu','Arapaima gigas','Vulneravel'], ['Tambaqui','Colossoma macropomum','Quase Ameacado'], ['Tucunare','Cichla ocellaris','Pouco Preocupante'] ] nome_arquivo = 'peixes_catalogados.csv' # Abrimos o arquivo em modo de escrita ('w') with open(nome_arquivo, mode='w', newline='', encoding='utf-8')as arquivo_csv: escritor_csv = csv.writer(arquivo_csv) # .writerows() escreve todas as listas de uma vez escritor_csv.writerows(dados_peixes) print(f"Arquivo '{nome_arquivo}' salvo com sucesso!") Se você abrir o arquivo peixes_catalogados.csv vai encontrar um arquivo CSV perfeitamente formatado, pronto para ser utilizado em uma planilha ou outra análise. Fique Alerta! Assim como open com o modo 'w' para arquivos de texto, usar csv.writer em um arquivo existente irá sobrescrevê-lo completamente. Se você precisar adicionar novas linhas a um CSV existente, abra o arquivo em modo de anexação ('a'). Dominar a manipulação de arquivos CSV com o módulo csv abre um mundo de possibilidades para a análise de dados. É a ponte entre a lógica interna dos seus programas em Python e o vasto ecossistema de dados tabulares utilizados em ciência, negócios e tecnologia. 2.2.4 Trabalhando com Dados Estruturados: JSON No tópico anterior, exploramos os arquivos CSV, que são perfeitos para organizar dados em formato de tabela, como uma planilha. Mas e se nossos dados não se encaixarem bem em linhas e colunas? E se precisarmos de uma estrutura mais flexível, com informações aninhadas, como uma ficha de catalogação de uma espécie que contém uma lista de locais onde foi encontrada e um dicionário de suas características? 87
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS É para resolver esse desafio que utilizamos o formato JSON (JavaScript Object Notation). Se um CSV é como uma planilha, o JSON é como um documento de texto super organizado ou um cartão de contato digital, onde cada informação tem um rótulo claro e podemos guardar listas dentro de listas ou informações dentro de outras. Embora o nome venha da linguagem JavaScript, o JSON é um formato de texto completamente independente de linguagem e se tornou o padrão universal para a troca de dados na internet, sendo a base para o funcionamento de praticamente todos os aplicativos e sites modernos que você usa. Figura 2.6: Arte que ilustra a conversão entre JSON e Python Fonte: Mehedi Hasan, 2024 O que é JSON e por que é tão importante? JSON é uma forma leve e legível de representar dados estruturados. Sua grande vantagem é que sua estrutura se alinha quase perfeitamente com duas das estruturas de dados mais importantes do Python: os dicionários e as listas. As regras do JSON são simples: • Dados são organizados em pares de chave-valor, assim como nos dicionários do Python. As chaves são sempre strings entre aspas duplas. • Coleções de pares chave-valor formam um objeto (equivalente a um dicionário Python), delimitado por chaves {}. • Listas ordenadas de valores formam um array (equivalente a uma lista Python), delimitado por colchetes []. • Os valores podem ser strings, números, booleanos (true/false), arrays ou até mesmo outros objetos, permitindo a criação de estruturas de dados complexas e aninhadas. Essa flexibilidade torna o JSON a linguagem preferida para as APIs (Application Programming Interfaces), que são as pontes de comunicação que permitem que diferentes sistemas troquem informações. Quando seu aplicativo de clima mostra a previsão para Manaus, ele provavelmente recebeu esses dados de um servidor em formato JSON. 88
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS 8. Análise de Produção de Açaí: Você recebeu um arquivo producao_acai.csv com dados da produção de açaí (em toneladas) de uma cooperativa. Sua tarefa é ler o arquivo e calcular a produção total e a média mensal. Conteúdo de producao_acai.csv (crie este arquivo para testar): Saída Esperada mes,producao_toneladas Janeiro,15.5 Fevereiro,12.8 Março,18.2 Abril,17.9 Seu programa deve ler o arquivo (usando csv.DictReader), somar todos os valores da coluna producao_toneladas e calcular a média. Ao final, imprima os resultados. 2.3.4 Exercícios com Arquivos JSON 9. Salvando Configurações de um Projeto: As configurações de um sistema de monitoramento são armazenadas em um dicionário Python. Crie um programa que salve esse dicionário em um arquivo config.json de forma bem formatada. Copie e Teste! configuracoes = { "id_projeto":"CITHA-MON-01", "local":"Reserva de Desenvolvimento Sustentável Mamirauá ", "ativo": True, "sensores_instalados": ["Temperatura","Umidade"," Qualidade do Ar"], "limites_alerta": { "temperatura_max": 38.0, "umidade_min": 60.0 } } Dica: Use json.dump() com os argumentos indent=4 para a formatação adequada eensure_ascii=False para garantir que caracteres especiais que não sejam ASCII (”á”por exemplo) sejam salvos corretamente. 10. Lendo Ficha de Espécie em JSON: Um biólogo enviou a ficha de uma espécie em formato JSON. Crie um programa que leia o arquivo especie.json e imprima uma frase resumindo as informações. Conteúdo de especie.json (crie este arquivo para testar): 95
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS Saída Esperada { "nome_popular": "Boto-cor-de-rosa", "nome_cientifico": "Inia geoffrensis", "habitat": "Rios de água doce da bacia amazônica", "status_conservacao": "Em perigo" } Saída esperada: ”O Boto-cor-de-rosa (Inia geoffrensis), que vive em Rios de água doce da bacia amazônica, está com o status de conservação: Em perigo.” 2.4 Considerações do Módulo 2 Conhecendo um pouco mais! Materiais Práticos do Módulo 2 Lembre-se que todos os scripts, datasets e o notebook para este módulo estão disponíveis em nosso repositório no GitHub. Para Instruções detalhadas sobre como utilizar os materiais, consulte a subseção 1.5 Considerações do Módulo 1 •Scripts e Datasets do Módulo 2: https://github.com/CITHA-AM/Python/tree/main/Modulo%202 •Notebook do Módulo 2 (Colab): https://colab.research.google.com/github/CITHA-AM/Python /blob/main/Modulo%202.ipynb Parabéns! Ao concluir o Módulo 2, você deu um dos passos mais significativos na sua jornada como desenvolvedor: a transição de escrever instruções sequenciais para construir aplicações verdadeiramente estruturadas e com memória. Neste capítulo, mergulhamos em dois pilares que sustentam a programação robusta. O primeiro foi a modularização, onde as funções se revelaram nossas principais ferramentas. Aprendemos a encapsular a complexidade em blocos de código nomeados, reutilizáveis e fáceis de manter, seguindo o princípio fundamental de Don’t Repeat Yourself (DRY). Você agora entende como gerenciar a comunicação entre diferentes partes do seu código por meio de parâmetros e valores de retorno, como as variáveis se comportam em escopos locais e globais e a importância de criar funções claras e bem documentadas com docstrings e type hints. O segundo pilar foi a persistência de dados. Vimos como quebrar as barreiras da memória volátil, ensinando nossos programas a ler e a escrever em arquivos. Essa habilidade permite que suas aplicações salvem resultados, carreguem configurações e processem informações de fontes externas, tornando-as infinitamente mais úteis e dinâmicas. Você explorou desde o manuseio de simples arquivos de texto (.txt), ideais para logs e registros, até formatos estruturados como CSV e JSON, que são a espinha dorsal da troca de dados no mundo da tecnologia atual. Ao dominar a organização do código com funções e a manipulação de arquivos, você não está mais apenas criando scripts que executam e são esquecidos. Você agora é capaz 96
CAPÍTULO 2. MODULARIZAÇÃO E PERSISTÊNCIA DE DADOS de desenvolver programas que podem crescer em complexidade de forma organizada e que ”lembram”informações entre execuções. Com essa base sólida, você está perfeitamente preparado para o próximo desafio. No Módulo 3, utilizaremos essas habilidades para explorar um dos campos mais fascinantes da programação: a Análise de Dados. Vamos aprender a usar bibliotecas poderosas como Pandas e Matplotlib para extrair, manipular e visualizar dados de forma eficiente, transformando informações brutas em insights valiosos. Continue com a dedicação, pois o que você aprendeu aqui será a base para todas as análises que faremos a seguir. 97
Capítulo 3 Análise de Dados com Pandas e Matplotlib Iniciando o diálogo... Parabéns por chegar até aqui! Nos módulos anteriores, você aprendeu a construir programas estruturados com funções e a dar ”memória”a eles, salvando e lendo informações em arquivos. Você construiu a base e as paredes da sua casa. Agora, vamos aprender a entender o que acontece dentro dela, transformando os dados brutos que coletamos em conhecimento valioso. Este capítulo é a sua porta de entrada para o universo da Análise de Dados, uma das áreas mais impactantes da tecnologia. Usaremos duas das ferramentas mais poderosas do ecossistema Python, as bibliotecas Pandas eMatplotlib, para aprender a questionar, limpar, analisar e, o mais importante, contar as histórias que os dados escondem. Imagine que você é um(a) biólogo(a) retornando de uma expedição de campo na Amazônia com dezenas de cadernos de anotações. Esses cadernos estão repletos de dados brutos: medições da qualidade da água, contagem de espécies, coordenadas geográficas, datas e horas. Essa pilha de anotações — nossos arquivos de dados — é valiosíssima, mas, em seu estado atual, é caótica e difícil de interpretar. Apenas ter os dados não é o suficiente; o verdadeiro poder está em extrair insights deles. É exatamente aqui que a nossa jornada neste capítulo começa. Se os dados brutos são uma floresta densa e inexplorada, a biblioteca Pandas é o seu kit de exploração completo. Ela é a ferramenta que nos permite organizar esse caos. Com o Pandas, podemos pegar todas aquelas anotações espalhadas e organizá-las em uma tabela limpa e estruturada, chamada DataFrame. Pense no DataFrame como um mapa interativo da sua pesquisa. Com ele, você pode: •Inspecionar seus dados: Ter uma visão geral do terreno que você está explorando. •Filtrar e selecionar: Focar em uma área específica do mapa, como analisar apenas os dados de uma espécie ou de um local. •Limpar e corrigir: Lidar com anotações borradas ou informações faltantes, garantindo a precisão do seu mapa. •Analisar e agregar: Calcular estatísticas e agrupar informações para descobrir padrões ocultos, como o mês de maior incidência de uma certa espécie de ave. 98
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Dominar o Pandas é o que transforma um amontoado de dados em uma fonte organizada e confiável de informações. É uma habilidade tão fundamental que seu criador, Wes McKinney, escreveu uma das obras de referência na área, consolidando o Pandas como a ferramenta padrão para manipulação de dados em Python (McKinney, 2018). Contudo, um mapa cheio de números e coordenadas ainda não conta uma história de forma eficaz. Para compartilhar suas descobertas com o mundo, você precisa de algo mais visual. É aqui que entra a nossa segunda ferramenta poderosa: Matplotlib. Se o Pandas é o seu kit de exploração e cartografia, Matplotlib é o seu estúdio de arte e comunicação. Ele permite que você transforme suas tabelas de dados e análises em gráficos e visualizações claras e impactantes. Com Matplotlib, você pode: • Criar um gráfico de linhas para mostrar como a temperatura de um rio variou ao longo do ano. • Gerar um gráfico de barras para comparar a população de diferentes espécies em uma reserva. • Customizar cada detalhe, desde cores e legendas até títulos, para contar a história mais precisa e convincente possível. A visualização de dados não é apenas sobre criar imagens bonitas; é uma ferramenta essencial de análise que nos permite identificar tendências, anomalias e padrões que seriam quase impossíveis de perceber apenas olhando para números. É a ponte entre a sua análise técnica e a compreensão humana. Neste capítulo, você aprenderá a usar essas duas bibliotecas em conjunto. Primeiro, usaremos o Pandas para carregar, limpar e analisar conjuntos de dados do mundo real. Em seguida, usaremos o Matplotlib para dar vida a essas análises, criando visualizações que comunicam nossas descobertas de forma clara e profissional. Ao final, você estará equipado(a) não apenas para programar, mas para extrair significado de dados, uma habilidade essencial para enfrentar os desafios do século XXI. 3.1 Introdução à Biblioteca Pandas No capítulo anterior, aprendemos a interagir com arquivos CSV e JSON, o que nos permitiu salvar e carregar dados de forma estruturada. No entanto, usar apenas as listas e dicionários do Python para analisar esses dados pode ser como tentar explorar a Amazônia com um mapa de papel e uma bússola: é possível, mas é trabalhoso, lento e fácil de se perder. Para navegar por grandes volumes de informação de forma eficiente, precisamos de uma ferramenta mais poderosa e especializada. É aqui que entra a biblioteca Pandas. Pense nela como um sistema de GPS de última geração para seus dados. Ela foi criada especificamente para tornar a manipulação e a análise de dados não apenas mais fáceis, mas também incrivelmente rápidas. Dominar o Pandas é o que transforma um amontoado de dados em uma fonte organizada e confiável de informações. É uma habilidade tão fundamental que seu criador, Wes McKinney, escreveu uma das obras de referência na área, consolidando o Pandas como a ferramenta padrão para manipulação de dados em Python. 99
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Figura 3.1: Arte que ilustra a abrangência da biblioteca Pandas Fonte: Adaptado de Guilherme.py, 2021 Nesta seção, vamos dar os primeiros passos com essa ferramenta essencial. Começaremos entendendo por que bibliotecas como o Pandas são tão importantes e como elas aceleram nosso trabalho. Em seguida, conheceremos suas duas estruturas de dados fundamentais, o DataFrame (nossa tabela de dados superpoderosa) e a Series (a coluna que forma a tabela). Por fim, veremos como é surpreendentemente simples usar o Pandas para carregar os dados dos arquivos CSV que aprendemos a criar, trazendo-os para dentro do nosso ambiente de análise e preparando o terreno para a exploração. 3.1.1 O Poder das Bibliotecas: Por que usar Pandas? No início da sua jornada com Python, você aprendeu a usar as ferramentas que vêm na ”caixa de ferramentas”padrão da linguagem: listas, dicionários, laços for e funções. Essas ferramentas são fantásticas e versáteis, mas, para certas tarefas especializadas, elas podem não ser as mais eficientes. Imagine que você é um pesquisador monitorando a qualidade da água do Rio Negro e coletou dados em um arquivo CSV como este: dados_rio.csv local,ph,turbidez,temperatura_c Encontro das Águas,6.8,5.2,28.5 Comunidade São José,7.1,3.1,29.1 Frente a Manaus,6.5,8.9,30.2 Reserva Ducke,6.9,2.5,27.8 Sua primeira tarefa é simples: calcular a temperatura média da água. Como faríamos isso usando apenas o que aprendemos até agora? Caso Prático Vamos tentar calcular a temperatura média da água usando as ferramentas padrão do Python, para entender as dificuldades que encontramos. 100
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Copie e Teste! import csv # Lista para guardar apenas os valores de temperatura temperaturas = [] with open('dados_rio.csv', mode='r', encoding='utf-8')as arquivo: leitor_csv = csv.reader(arquivo) # Pula a primeira linha (o cabeçalho) next(leitor_csv) # Itera sobre as linhas para extrair a temperatura for linha in leitor_csv: # A temperatura está na 4ª coluna (índice 3) temperatura_str = linha[3] temperaturas.append(float(temperatura_str)) # Agora, calculamos a média manualmente media = sum(temperaturas) / len(temperaturas) print(f"A temperatura média da água é: {media:.2f}°C") Saída Esperada A temperatura média da água é: 28.90°C Este código funciona, mas observe os desafios: •Muita preparação manual: Tivemos que abrir o arquivo, criar um leitor, pular o cabeçalho manualmente, iterar linha por linha, extrair o valor de uma coluna específica usando seu índice (linha[3]), converter para float e só então calcular a média. •Código frágil: E se alguém adicionasse uma nova coluna antes da temperatura? O índice 3estaria errado e nosso código quebraria ou, pior, calcularia a média de uma coluna errada sem nos avisar. •Ineficiência: Para tarefas mais complexas, como encontrar o local com a maior turbidez ou agrupar dados por região, nosso código se tornaria rapidamente longo, complicado e lento, especialmente com milhares ou milhões de linhas. É aqui que o conceito de bibliotecas entra em jogo. Uma biblioteca em programação é um conjunto de códigos e funções pré-escritos que resolvem problemas comuns. Em vez de construir tudo do zero, você ”importa”uma biblioteca especializada e usa suas ferramentas otimizadas. OPandas é a biblioteca definitiva para análise de dados em Python. Ele foi projetado para lidar com dados tabulares (como os do nosso CSV) de forma intuitiva e extremamente eficiente. A principal estrutura de dados que o Pandas nos oferece é o DataFrame. 101
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Pense em um DataFrame como uma versão superpoderosa de uma planilha do Excel ou do Google Sheets, diretamente no seu código Python. É uma tabela com linhas e colunas, mas com habilidades extraordinárias: •Colunas nomeadas: Você acessa os dados pelo nome da coluna ao invés de um índice numérico frágil (ex: dados['temperatura_c']). •Tipos de dados inteligentes: O Pandas detecta automaticamente se uma coluna contém números, textos ou datas, otimizando o armazenamento e os cálculos. •Funções prontas: Quer a média de uma coluna? Basta usar .mean(). A mediana? .median(). O valor máximo? .max(). Tudo de forma direta e intuitiva. Conhecendo um pouco mais! O nome ”Pandas”não vem do animal, mas sim do termo econométrico ”panel data” (dados em painel), que se refere a conjuntos de dados que combinam observações ao longo do tempo para diferentes indivíduos. Isso reflete a origem da biblioteca, criada por Wes McKinney para facilitar a análise de dados financeiros. Agora, vamos refazer nossa tarefa de calcular a temperatura média, mas desta vez usando o poder do Pandas. Copie e Teste! import pandas as pd # 1. Ler o arquivo CSV diretamente para um DataFrame # O 'pd' é o apelido padrão para o pandas df = pd.read_csv('dados_rio.csv') # 2. Calcular a média da coluna 'temperatura_c' media_pandas = df['temperatura_c'].mean() print(f"A temperatura média (calculada com Pandas) é: { media_pandas:.2f}°C") Saída Esperada A temperatura média (calculada com Pandas) é: 28.90°C Veja a diferença! Em apenas duas linhas de código, o Pandas: 1. Abriu o arquivo. 2. Identificou o cabeçalho e nomeou as colunas. 3. Carregou todos os dados em uma estrutura organizada (o DataFrame df). 4. Calculou a média da coluna que especificamos pelo nome. 102
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB O código não é apenas mais curto, é imensamente mais legível, robusto e eficiente. É por isso que usamos bibliotecas: para nos apoiarmos no trabalho de especialistas e focarmos no que realmente importa, que é resolver nosso problema e extrair insights dos dados. Fique Alerta! Para usar uma biblioteca como o Pandas, primeiro você precisa instalá-la no seu ambiente Python. Se você estiver usando uma distribuição como o Anaconda, ele já vem incluído. Caso contrário, você pode instalá-lo facilmente abrindo seu terminal ou prompt de comando e digitando: pip install pandas Com o Pandas, a análise de dados deixa de ser uma tarefa de programação complexa e se torna uma exploração interativa. Nas próximas seções, vamos mergulhar nas estruturas de DataFrame eSeries e aprender a usar as ferramentas que o Pandas oferece para filtrar, limpar e entender as histórias que nossos dados amazônicos têm a nos contar. 3.1.2 Series e DataFrames: As Estruturas Fundamentais No tópico anterior, comparamos a biblioteca Pandas a um GPS de última geração para nossos dados. Agora, vamos conhecer os dois componentes que formam o coração desse sistema: a Series e o DataFrame. Pense neles como os blocos de construção fundamentais de qualquer análise. Se você estivesse construindo uma casa de LEGOs, a Series seria um único tipo de bloco, como um tijolo 2x1, e o DataFrame seria a estrutura completa que você monta com esses blocos, como uma parede ou a casa inteira. Series 1 (especies_peixes) Índice Valores Lago A 120 Lago B 85 Lago C 98 + Series 2 (ph_agua) Índice Valores Lago A 6.5 Lago B 6.8 Lago C 6.2 + Series 3 (turbidez_ntu) Índice Valores Lago A 4.5 Lago B 3.1 Lago C 5.8 Pandas DataFrame Índice especies_peixes ph_agua turbidez_ntu Lago A 120 6.5 4.5 Lago B 85 6.8 3.1 Lago C 98 6.2 5.8 Figura 3.2: Diagrama da formação de um DataFrame a partir de Series (usando tabular). Entender como essas duas estruturas funcionam e se relacionam é o passo mais importante para se tornar fluente em Pandas. Elas são a base sobre a qual todas as operações de limpeza, manipulação e análise de dados são construídas. ASeries: A Coluna Inteligente A estrutura mais básica do Pandas é a Series. A maneira mais fácil de imaginá-la é como uma única coluna de uma planilha ou uma lista Python com superpoderes. 103
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Uma Series é um objeto unidimensional, semelhante a um vetor, que pode armazenar qualquer tipo de dado (números inteiros, floats, strings, etc.). Sua principal característica, que a diferencia de uma simples lista, é a presença de um índice (index). O índice é um conjunto de rótulos que nos permite acessar cada elemento da Series de forma rápida e intuitiva. Caso Prático Imagine que uma equipe de pesquisadores do Instituto Nacional de Pesquisas da Amazônia (INPA) contou a quantidade de espécies de peixes em três lagos diferentes perto de Manaus. Podemos representar esses dados em uma Series. Copie e Teste! import pandas as pd # Criando uma Series a partir de uma lista Python dados_peixes = [120, 85, 98] lagos = ["Lago do Puraquequara","Lago do Janauari","Lago do Tarumã-Açu"] # Criando a Series com índices personalizados s_peixes = pd.Series(data=dados_peixes, index=lagos) print("Dados da Contagem de Espécies de Peixes:") print(s_peixes) Saída Esperada Dados da Contagem de Espécies de Peixes: Lago do Puraquequara 120 Lago do Janauari 85 Lago do Tarumã-Açu 98 dtype: int64 Observe a saída: não é apenas uma lista de números. À esquerda, temos os rótulos do índice (os nomes dos lagos) e, à direita, os valores correspondentes. No final, dtype: int64 nos informa que o Pandas identificou os dados como números inteiros. Essa estrutura de índice + valor é o que torna a Series tão poderosa para a análise de dados. Fique Alerta! O índice é o coração da Series. Enquanto em uma lista Python você acessa os elementos apenas por sua posição numérica (0, 1, 2...), em uma Series você pode usar rótulos explícitos, como nomes de locais ou datas. Isso torna o código muito mais legível e menos propenso a erros. 104
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB 3.2.1 Primeiros Passos: Inspecionando seu DataFrame No tópico anterior, você abriu a porta para o mundo da análise de dados ao carregar seu primeiro arquivo em um DataFrame. Foi um passo gigantesco! Agora que os dados estão ”dentro de casa”, o que fazemos com eles? É como um detetive que acaba de receber uma caixa de evidências: o primeiro passo não é tirar conclusões, mas sim inspecionar cuidadosamente cada item para entender com o que está lidando. Esta etapa de inspeção inicial é, talvez, a mais importante de toda a sua jornada. Antes de calcular médias, encontrar o maior valor ou criar um gráfico, você precisa ”sentir”seus dados. Quantas informações você tem? Elas estão completas? O Python as entendeu da maneira correta? Responder a essas perguntas evita erros e garante que sua análise seja construída sobre uma base sólida e confiável. Figura 3.3: Arte que ilustra o conceito de inspeção de dados Fonte: Gerada por Google Gemini 2.5 Pro, 2025 Felizmente, o Pandas nos oferece um kit de ferramentas de investigação de primeira linha. Com apenas alguns comandos simples, podemos obter um resumo completo do nosso conjunto de dados. Vamos aprender a usar as cinco ferramentas essenciais de todo analista de dados: .head(),.tail(),.shape,.columns e, a mais poderosa de todas, .info(). Caso Prático Monitoramento de Peixes no Rio Amazonas Imagine que uma equipe de pesquisadores do Instituto Nacional de Pesquisas da Amazônia (INPA) coletou dados sobre peixes em diferentes pontos ao longo do Rio Amazonas. Eles registraram o local, a espécie do peixe, a quantidade de peixes contados e o pH da água. Os dados foram salvos no arquivo coleta_peixes.csv. 111
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Saída Esperada Conteúdo de coleta_peixes.csv local,especie,quantidade,ph_agua Encontro das Águas,Tambaqui,45,6.8 Frente a Manaus,Tucunaré,22,6.5 Lago Janauari,Pirarucu,10,7.1 Parintins,Tambaqui,56, Frente a Manaus,Jaraqui,89,6.6 Lago Janauari,Tucunaré,15,7.0 Encontro das Águas,Pirarucu,,6.9 Após dar uma observada geral nos dados, nosso primeiro passo em qualquer análise é carregar esses dados em um DataFrame do Pandas. Copie e Teste! import pandas as pd # Carregando os dados do arquivo CSV para o nosso DataFrame df_peixes = pd.read_csv('coleta_peixes.csv') # Exibindo o DataFrame completo para confirmar que carregou print(df_peixes) Saída Esperada local especie quantidade ph_agua 0 Encontro das Águas Tambaqui 45.0 6.8 1 Frente a Manaus Tucunaré 22.0 6.5 2 Lago Janauari Pirarucu 10.0 7.1 3 Parintins Tambaqui 56.0 NaN 4 Frente a Manaus Jaraqui 89.0 6.6 5 Lago Janauari Tucunaré 15.0 7.0 6 Encontro das Águas Pirarucu NaN 6.9 Dando uma Espiada: .head() e.tail() Quando lidamos com arquivos que podem ter milhares ou milhões de linhas, imprimir o DataFrame inteiro é impraticável. A primeira coisa que um analista de dados faz é ”espiar”o começo e o fim do seu conjunto de dados. • O método .head() mostra as primeiras 5 linhas por padrão. É como olhar os primeiros itens que estão no topo da caixa de evidências. • O método .tail() mostra as últimas 5 linhas. É útil para ver se o final do arquivo foi lido corretamente. 112
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Copie e Teste! # Mostrando as 3 primeiras linhas print("--- Primeiras 3 linhas com .head(3) ---") print(df_peixes.head(3)) # Mostrando as 2 últimas linhas print("\n--- Últimas 2 linhas com .tail(2) ---") print(df_peixes.tail(2)) Saída Esperada --- Primeiras 3 linhas com .head(3) --- local especie quantidade ph_agua 0 Encontro das Águas Tambaqui 45.0 6.8 1 Frente a Manaus Tucunaré 22.0 6.5 2 Lago Janauari Pirarucu 10.0 7.1 --- Últimas 2 linhas com .tail(2) --- local especie quantidade ph_agua 5 Lago Janauari Tucunaré 15.0 7.0 6 Encontro das Águas Pirarucu NaN 6.9 Essa primeira olhada já nos dá uma boa ideia da estrutura dos dados e até revela algo interessante no final: um valor NaN (Not a Number), que é como o Pandas representa dados ausentes. Já encontramos nossa primeira pista! Qual o Tamanho do Desafio? Conhecendo as Dimensões com .shape A próxima pergunta é: ”Com quantos dados estamos lidando?”. O atributo .shape nos dá a resposta exata. Ele não é um método (por isso não usa parênteses ()), mas sim uma propriedade do DataFrame que retorna uma tupla com o formato (número de linhas, número de colunas). Copie e Teste! # Obtendo as dimensões do nosso DataFrame dimensoes = df_peixes.shape print(f"O DataFrame tem {dimensoes[0]} linhas (observações) e { dimensoes[1]} colunas (variáveis).") Saída Esperada O DataFrame tem 7 linhas (observações) e 4 colunas ( variáveis). 113
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Saber o .shape é fundamental para entender a escala do seu problema. Sete linhas é um conjunto pequeno, mas em um projeto real, você poderia ver (700000, 15), indicando um desafio muito maior. A Ficha Técnica Completa: O Poder do .info() Se .head() é uma espiada e .shape mede o tamanho, o método .info() é o raioX completo do seu DataFrame. Ele fornece um resumo técnico conciso e extremamente útil, sendo o comando mais importante para a inspeção inicial. Copie e Teste! # Obtendo um resumo completo do DataFrame print("--- Resumo técnico do DataFrame com .info() ---") df_peixes.info() Saída Esperada --- Resumo técnico do DataFrame com .info() --- <class 'pandas.core.frame.DataFrame'> RangeIndex: 7 entries, 0 to 6 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 local 7 non-null object 1 especie 7 non-null object 2 quantidade 6 non-null float64 3 ph_agua 6 non-null float64 dtypes: float64(2), object(2) memory usage: 356.0+ bytes Esta saída é uma mina de ouro de informações. Vamos analisá-la parte por parte: 1. RangeIndex: 7 entries, 0 to 6: Confirma que temos 7 linhas, com índices que vão de 0 a 6. 2. Data columns (total 4 columns): Confirma que temos 4 colunas. 3. A Tabela de Colunas: Esta é a parte mais importante. •Column: O nome de cada coluna. •Non-Null Count: O número de valores que não são nulos. Esta é a nossa principal ferramenta para detectar dados ausentes! Observe que as respectivas colunas quantidade eph_agua possuem ”6 non-null”, enquanto o total de linhas é 7. Isso confirma que cada uma delas tem um valor faltando. •Dtype: O tipo de dado que o Pandas atribuiu a cada coluna. object geralmente significa que a coluna contém strings (texto). float64 indica que a coluna contém números com casas decimais. 114
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Fique Alerta! O método .info() é seu melhor amigo no início de qualquer análise. Ele responde a três perguntas cruciais de uma só vez: 1. Quais são as minhas colunas? 2. Existem dados faltando? (Compare ”Non-Null Count”com o total de ”entries”) 3. Os tipos de dados estão corretos? (Números as vezes podem ser lidos como texto) Com esses comandos, você realizou uma inspeção inicial completa. Em poucos minutos, você entendeu o tamanho, a estrutura, os tipos de dados e até identificou problemas de dados ausentes em seu conjunto de dados. Como enfatiza Wes McKinney, o criador do Pandas, dedicar tempo para entender a estrutura dos seus dados no início economiza um tempo imenso e evita erros no futuro (McKinney, 2018). Agora que sabemos com o que estamos lidando, estamos prontos para o próximo passo: aprender a focar em partes específicas dos nossos dados, selecionando e filtrando as informações que são mais relevantes para nossa investigação. 3.2.2 Seleção e Filtragem de Dados No tópico anterior, você aprendeu a carregar seus dados e a realizar uma primeira inspeção, como um detetive examinando a caixa de evidências. Agora que temos uma visão geral do nosso DataFrame, é hora de aprofundar a investigação. Raramente trabalhamos com todos os dados de uma vez. Na maioria das vezes, estamos interessados em responder a perguntas específicas, como: • ”Qual a quantidade de Pirarucus avistados no Lago Janauari?” • ”Quais locais apresentaram um pH da água abaixo do nível ideal de 6.5?” • ”Quais são os dados apenas das colunas especie elocal?” Para responder a essas e outras perguntas, precisamos aprender a ”fatiar”e ”peneirar”o DataFrame, selecionando apenas as partes que nos interessam. Esta seção é dedicada a ensinar você a usar as ferramentas de seleção e filtragem do Pandas. Pense nelas como a lupa e as pinças do detetive: ferramentas de precisão para focar nos detalhes mais importantes e isolar as evidências cruciais. Dominar a seleção e a filtragem é o que transforma a análise de dados de uma simples observação para uma investigação direcionada, permitindo que você extraia respostas claras de conjuntos de dados complexos. A Lupa do Detetive: Selecionando Colunas A tarefa mais comum em uma análise é focar em uma ou mais variáveis (colunas) específicas. Em nosso DataFrame de peixes, por exemplo, podemos querer analisar apenas as espécies, ignorando temporariamente as medições de pH. O Pandas torna essa tarefa extremamente simples e intuitiva. 115
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Selecionando uma Única Coluna: Para selecionar uma única coluna, basta usar colchetes [] com o nome da coluna entre aspas, como se estivéssemos acessando um valor em um dicionário. Caso Prático Vamos continuar nossa análise com os dados de monitoramento de peixes. Nossa primeira tarefa é isolar e visualizar apenas a coluna que contém as espécies coletadas. Copie e Teste! import pandas as pd # Recriando nosso DataFrame para o exemplo dados = { 'local': ['Encontro das Águas','Frente a Manaus','Lago Janauari','Parintins','Frente a Manaus','Lago Janauari',' Encontro das Águas'], 'especie': ['Tambaqui','Tucunaré','Pirarucu','Tambaqui',' Jaraqui','Tucunaré','Pirarucu'], 'quantidade': [45.0, 22.0, 10.0, 56.0, 89.0, 15.0, None], 'ph_agua': [6.8, 6.5, 7.1, None, 6.6, 7.0, 6.9] } df_peixes = pd.DataFrame(dados) # Selecionando a coluna 'especie' coluna_especies = df_peixes['especie'] print("--- Conteúdo da Coluna 'especie' ---") print(coluna_especies) print("\nTipo do objeto retornado:") print(type(coluna_especies)) Saída Esperada --- Conteúdo da Coluna 'especie' --- 0 Tambaqui 1 Tucunaré 2 Pirarucu 3 Tambaqui 4 Jaraqui 5 Tucunaré 6 Pirarucu Name: especie, dtype: object Tipo do objeto retornado: <class 'pandas.core.series.Series'> 116
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Fique Alerta! Observe que, ao selecionar uma única coluna, o Pandas não nos devolve um DataFrame, mas sim uma Series. Isso reforça a ideia de que um DataFrame é, essencialmente, uma coleção de Series que compartilham o mesmo índice. Selecionando Múltiplas Colunas Para selecionar várias colunas ao mesmo tempo, como local equantidade, utilizamos uma lista de nomes de colunas dentro dos colchetes. Copie e Teste! # Para selecionar múltiplas colunas, usamos uma lista de nomes colunas_selecionadas = df_peixes[['local','quantidade']] print("--- DataFrame com as colunas 'local' e 'quantidade' ---") print(colunas_selecionadas) print("\nTipo do objeto retornado:") print(type(colunas_selecionadas)) Saída Esperada --- DataFrame com as colunas 'local' e 'quantidade' --- local quantidade 0 Encontro das Águas 45.0 1 Frente a Manaus 22.0 2 Lago Janauari 10.0 3 Parintins 56.0 4 Frente a Manaus 89.0 5 Lago Janauari 15.0 6 Encontro das Águas NaN Tipo do objeto retornado: <class 'pandas.core.frame.DataFrame'> Desta vez, como selecionamos mais de uma coluna, o resultado é um novo DataFrame, menor e mais focado. Essa técnica é fundamental para criar visualizações ou análises que dependem apenas de um subconjunto das suas variáveis. Pinças de Precisão: Selecionando Linhas com .loc e.iloc O Pandas nos oferece duas ”pinças”para essa tarefa, , cada uma com sua especialidade: 1. .loc (Seleção por Rótulo): Utiliza os nomes (rótulos) do índice para encontrar as linhas. É como encontrar um livro na estante pelo seu título. 2. .iloc (Seleção por Posição Inteira): Utiliza a posição numérica (índice inteiro) das linhas, começando do 0. É como pegar o ”terceiro livro da prateleira de cima”, independentemente do seu título. 117
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Seleção por Rótulo com .loc O método .loc é a principal forma de selecionar dados quando os rótulos do seu índice são significativos (como datas, nomes de locais, etc.). No nosso df_peixes, o índice padrão é numérico (0, 1, 2...), então os rótulos são os próprios números. Caso Prático Vamos refazer nosso DataFrame de monitoramento de lagos, desta vez usando os nomes dos lagos como índice, e depois usar o .loc para selecionar os dados de um lago específico. Copie e Teste! import pandas as pd dados_lagos = { 'especies_peixes': [120, 85, 98], 'ph_agua': [6.5, 6.8, 6.2], 'turbidez_ntu': [4.5, 3.1, 5.8] } nomes_lagos = ["Lago do Puraquequara","Lago do Janauari","Lago do Tarumã-Açu"] df_lagos = pd.DataFrame(dados_lagos, index=nomes_lagos) print("--- DataFrame com Índice Nominal ---") print(df_lagos) # Usando .loc para selecionar a linha com o rótulo "Lago do Janauari" dados_janauari = df_lagos.loc["Lago do Janauari"] print("\n--- Dados apenas do Lago do Janauari ---") print(dados_janauari) Saída Esperada --- DataFrame com Índice Nominal --- especies_peixes ph_agua turbidez_ntu Lago do Puraquequara 120 6.5 4.5 Lago do Janauari 85 6.8 3.1 Lago do Tarumã-Açu 98 6.2 5.8 --- Dados apenas do Lago do Janauari --- especies_peixes 85.0 ph_agua 6.8 turbidez_ntu 3.1 Name: Lago do Janauari, dtype: float64 Seleção por Posição com .iloc O método .iloc funciona de forma similar, mas ignora completamente os rótulos e foca apenas na posição numérica. É extremamente útil quando 118
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB você não conhece os rótulos do índice ou quer simplesmente pegar ”a primeira linha”ou ”as últimas cinco linhas”. Copie e Teste! # Selecionando a primeira linha (posição 0) do df_peixes primeira_linha = df_lagos.iloc[0] print("--- Primeira Linha do DataFrame (posição 0) ---") print(primeira_linha) # Selecionando a terceira linha (posição 2) terceira_linha = df_lagos.iloc[2] print("\n--- Terceira Linha do DataFrame (posição 2) ---") print(terceira_linha) # Selecionando um intervalo de linhas (da posição 1 à 2) intervalo_linhas = df_lagos.iloc[1:4] print("\n--- Linhas da Posição 1 até a 2 ---") print(intervalo_linhas) Saída Esperada --- Primeira Linha do DataFrame (posição 0) --- especies_peixes 120.0 ph_agua 6.5 turbidez_ntu 4.5 Name: Lago do Puraquequara, dtype: float64 --- Terceira Linha do DataFrame (posição 2) --- especies_peixes 98.0 ph_agua 6.2 turbidez_ntu 5.8 Name: Lago do Tarumã-Açu, dtype: float64 --- Linhas da Posição 1 até a 3 --- especies_peixes ph_agua turbidez_ntu 1 Lago do Janauari 85 6.8 3.1 2 Lago do Tarumã-Açu 98 6.2 5.8 Observe que o dataframe não possuí a posição 4, portanto, ele executa somente até a posição 2 que se refere ao índice máximo que ele alcança. A Peneira Fina: Filtragem por Condições A seleção de colunas e linhas é poderosa, mas a verdadeira mágica da análise de dados acontece quando começamos a filtrar nossos dados com base em condições. É aqui que passamos de ”me mostre esta linha”para ”me mostre todas as linhas onde algo interessante acontece”. 119
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Essa técnica, conhecida como indexação booleana ou máscara booleana, funciona em dois passos: 1. Criar uma condição: Fazemos uma pergunta sobre uma coluna que resulta em True ou False para cada linha. (Ex: df_peixes['quantidade'] > 50). 2. Aplicar a máscara: Usamos essa série de True/False para filtrar o DataFrame, que nos retorna apenas as linhas onde a condição foi True. Filtragem com uma Única Condição A filtragem por condição única envolve aplicar uma regra lógica (como >ou ==) a uma coluna. Essa operação gera uma “máscara” de valores True eFalse, que é usada para selecionar do DataFrame apenas as linhas onde a condição é verdadeira. Caso Prático A equipe de pesquisa quer identificar todas as coletas onde a contagem de peixes foi alta, definida como mais de 50 indivíduos. Precisamos filtrar nosso DataFrame para mostrar apenas esses registros. Copie e Teste! # Passo 1: Criar a máscara booleana # Isso cria uma Series de True/False mascara_alta_quantidade = df_peixes['quantidade'] > 50 print("--- Máscara Booleana (Quantidade > 50) ---") print(mascara_alta_quantidade) # Passo 2: Aplicar a máscara ao DataFrame coletas_grandes = df_peixes[mascara_alta_quantidade] print("\n--- Coletas com mais de 50 indivíduos ---") print(coletas_grandes) Saída Esperada --- Máscara Booleana (Quantidade > 50) --- 0 False 1 False 2 False 3 True 4 True 5 False 6 False Name: quantidade, dtype: bool --- Coletas com mais de 50 indivíduos --- local especie quantidade ph_agua 3 Parintins Tambaqui 56.0 NaN 4 Frente a Manaus Jaraqui 89.0 6.6 120
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Criando Novas Colunas a Partir de Operações Uma das tarefas mais poderosas e comuns na análise de dados é a criação de novas colunas baseadas em informações que já existem. Isso nos permite derivar novos insights e variáveis que não estavam presentes nos dados originais. Felizmente, o Pandas torna essa operação tão intuitiva quanto uma simples atribuição de variável. Caso Prático Uma equipe de monitoramento florestal coletou dados sobre diferentes parcelas de uma área de reflorestamento na Amazônia. O arquivo de dados contém o número de árvores e a área de cada parcela em hectares. Para avaliar o sucesso do reflorestamento, a equipe precisa calcular a densidade de árvores (árvores por hectare) para cada parcela, uma informação que não está no conjunto de dados original. Copie e Teste! import pandas as pd # Criando um DataFrame com os dados da pesquisa de campo dados_floresta = { 'id': ['A1','A2','B1','B2'], 'quantidade': [450, 510, 480, 530], 'area': [2.0, 2.2, 2.1, 2.3] } df_floresta = pd.DataFrame(dados_floresta) print("--- DataFrame Original ---") print(df_floresta) # Criando a nova coluna 'densidade_arvores' # A operação é feita para todas as linhas de uma só vez! df_floresta['densidade'] = df_floresta['quantidade'] / df_floresta ['area'] print("\n--- DataFrame com a Nova Coluna de Densidade ---") print(df_floresta) Saída Esperada --- DataFrame Original --- id quantidade area 0 A1 450 2.0 1 A2 510 2.2 2 B1 480 2.1 3 B2 530 2.3 --- DataFrame com a Nova Coluna de Densidade --- id quantidade area densidade 127
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB 0 A1 450 2.0 225.000000 1 A2 510 2.2 231.818182 2 B1 480 2.1 228.571429 3 B2 530 2.3 230.434783 Observe como a nova coluna densidade_arvores foi adicionada ao final do DataFrame. O Pandas aplicou a divisão linha por linha automaticamente, uma funcionalidade conhecida como vetorização, que é extremamente eficiente e uma das grandes vantagens de usar bibliotecas como o Pandas em vez de laços for manuais. Renomeando Colunas para Maior Clareza Os dados que recebemos nem sempre vêm com nomes de colunas claros. Eles podem ser abreviados (como qtd), em outro idioma ou conter caracteres que dificultam o acesso. Renomear colunas é um passo fundamental para tornar seu DataFrame e seu código mais legíveis e fáceis de manter. Para isso, usamos o método .rename(). Caso Prático Um sensor automático de qualidade da água gera um arquivo com os nomes das colunas em inglês e de forma abreviada: 'location','ph_val', e 'turb_ntu'. Para preparar os dados para um relatório em português, precisamos traduzir e tornar esses nomes mais descritivos. Copie e Teste! import pandas as pd # DataFrame com nomes de colunas pouco claros dados_sensor = { 'location': ['Encontro das Águas','Lago Janauari'], 'ph_val': [6.8, 7.1], 'turb_ntu': [5.2, 3.1] } df_sensor = pd.DataFrame(dados_sensor) # Usamos .rename() com um dicionário para mapear os nomes antigos para os novos df_sensor_renomeado = df_sensor.rename(columns={ 'location':'local_coleta', 'ph_val':'valor_ph', 'turb_ntu':'turbidez_ntu' }) print("--- DataFrame Original ---") print(df_sensor) print("\n--- DataFrame com Colunas Renomeadas ---") print(df_sensor_renomeado) 128
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Saída Esperada --- DataFrame Original --- location ph_val turb_ntu 0 Encontro das Águas 6.8 5.2 1 Lago Janauari 7.1 3.1 --- DataFrame com Colunas Renomeadas --- local_coleta valor_ph turbidez_ntu 0 Encontro das Águas 6.8 5.2 1 Lago Janauari 7.1 3.1 Fique Alerta! Por padrão, o método .rename() não modifica o DataFrame original; ele retorna uma cópia com as alterações. Por isso, salvamos o resultado em uma nova variável (df_sensor_renomeado). Uma maneira de modificar o DataFrame original diretamente é configurar a função com o parâmetro inplace=True. No entanto, trabalhar com cópias costuma ser uma prática mais segura, especialmente para iniciantes. Removendo Colunas Desnecessárias Muitas vezes, os conjuntos de dados contêm colunas que não são relevantes para a nossa análise específica. Remover essas colunas simplifica o DataFrame, economiza memória e nos ajuda a focar no que realmente importa. A maneira mais comum e segura de fazer isso é com o método .drop(). Caso Prático Nosso DataFrame de monitoramento de peixes contém um código interno de coleta ('cod_coleta') e a data no formato timestamp Unix ('timestamp'), que não serão usados na nossa análise de biodiversidade. Vamos remover essas colunas para simplificar nossa tabela. Copie e Teste! import pandas as pd # DataFrame com colunas a serem removidas dados_completos = { 'cod_coleta': ['C01','C02','C03'], 'local': ['Parintins','Maués','Itacoatiara'], 'especie': ['Tambaqui','Pirarucu','Tucunaré'], 'timestamp': [1672531200, 1672617600, 1672704000] } df_completo = pd.DataFrame(dados_completos) # Removendo uma lista de colunas com o método .drop() 129
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB # O argumento 'axis=1' informa ao Pandas que queremos remover colunas, não linhas df_simplificado = df_completo.drop(columns=['cod_coleta',' timestamp']) print("--- DataFrame Original ---") print(df_completo) print("\n--- DataFrame Após Remover Colunas ---") print(df_simplificado) Saída Esperada --- DataFrame Original --- cod_coleta local especie timestamp 0 C01 Parintins Tambaqui 1672531200 1 C02 Maués Pirarucu 1672617600 2 C03 Itacoatiara Tucunaré 1672704000 --- DataFrame Após Remover Colunas --- local especie 0 Parintins Tambaqui 1 Maués Pirarucu 2 Itacoatiara Tucunaré O método .drop() é a ferramenta padrão para remoção, pois é explícito e flexível. Dominar a criação, renomeação e remoção de colunas, é essencial para o processo de data munging ou preparação de dados, que constitui a maior parte do trabalho de um analista (McKinney, 2018). 3.3 Análise e Agregação de Dados Nos tópicos anteriores, você aprendeu a carregar, inspecionar e limpar seus dados. Foi como um explorador que, após uma longa expedição, organiza todas as amostras e anotações em uma mesa de laboratório. Agora que tudo está em ordem, começa a fase mais emocionante e desafiadora da descoberta, a análise das informações. Ter dados limpos e organizados é essencial, mas o verdadeiro valor está nas histórias que eles podem contar e nas perguntas que podem responder. Esta seção é dedicada a transformar seu DataFrame de uma simples tabela de informações em uma fonte rica de conhecimento. Pense nesta etapa como o momento em que o explorador começa a conectar os pontos: calcular a média de altitude onde uma espécie de planta foi encontrada ou agrupar todas as amostras de uma mesma região para entender suas características comuns. Para realizar essa tarefa, vamos focar em duas das técnicas mais poderosas e fundamentais do Pandas: •Cálculos Estatísticos Básicos: Aprenderemos a extrair rapidamente as principais métricas de nossos dados. Utilizando funções simples como .mean(),.median() e 130
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB .sum(), você poderá responder a perguntas como ”Qual foi a temperatura média registrada?”ou ”Qual a quantidade total de peixes coletados?”. Esses cálculos são o primeiro passo para entender as tendências centrais, dispersões e a magnitude dos seus dados. •Agrupamento de Dados (groupby): Esta é, talvez, uma das funcionalidades mais poderosas do Pandas. A técnica de groupby nos permite ”dividir”nosso conjunto de dados em grupos com base em uma categoria e, em seguida, aplicar uma análise a cada grupo separadamente. É a ferramenta perfeita para responder a perguntas comparativas, como ”Qual local teve a maior média de turbidez na água?”ou ”Qual a produção total de açaí por estado?”. Dominar essas técnicas de agregação é o que permite ir além da simples observação dos dados e começar a extrair conclusões significativas, revelando padrões e tendências que estavam escondidos sob a superfície (McKinney, 2018). Vamos começar a transformar nossos números em verdadeiros insights. 3.3.1 Cálculos Estatísticos Básicos: O Primeiro Raio-X dos Seus Dados Depois de limpar um conjunto de dados, o primeiro passo de um analista é obter um resumo numérico. A estatística descritiva nos ajuda a responder perguntas fundamentais de forma rápida. O Pandas transforma essa tarefa, que seria complexa, em operações de uma única linha. Vamos usar nosso DataFrame de monitoramento de peixes, já limpo, para explorar essas ferramentas. Caso Prático Vamos continuar nossa análise com os dados de peixes, agora focando nas colunas numéricas quantidade eph_agua para extrair nossas primeiras estatísticas. Copie e Teste! import pandas as pd import numpy as np # Recriando o DataFrame já com os dados ausentes tratados dados = { 'local': ['Encontro das Águas','Frente a Manaus','Lago Janauari','Parintins','Frente a Manaus','Lago Janauari',' Encontro das Águas'], 'especie': ['Tambaqui','Tucunaré','Pirarucu','Tambaqui',' Jaraqui','Tucunaré','Pirarucu'], 'quantidade': [45.0, 22.0, 10.0, 56.0, 89.0, 15.0, 0.0], # NaN preenchido com 0 'ph_agua': [6.8, 6.5, 7.1, 6.82, 6.6, 7.0, 6.9] # NaN preenchido com a média } df_peixes = pd.DataFrame(dados) # 1. Qual a quantidade total de peixes avistados? .sum() total_peixes = df_peixes['quantidade'].sum() 131
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB print(f"Total de peixes avistados: {total_peixes}") # 2. Qual foi a média de pH da água? .mean() media_ph = df_peixes['ph_agua'].mean() print(f"Média do pH da água: {media_ph:.2f}") # 3. Qual o valor mediano da contagem de peixes? .median() mediana_peixes = df_peixes['quantidade'].median() print(f"Mediana da quantidade de peixes: {mediana_peixes}") # 4. Qual foi a maior e a menor quantidade de peixes registrada? . max() e .min() max_peixes = df_peixes['quantidade'].max() min_peixes = df_peixes['quantidade'].min() print(f"Maior contagem em um local: {max_peixes}") print(f"Menor contagem em um local: {min_peixes}") # 5. Quantas medições de pH foram realizadas? .count() contagem_medicoes = df_peixes['ph_agua'].count() print(f"Total de medições de pH: {contagem_medicoes}") Saída Esperada Total de peixes avistados: 237.0 Média do pH da água: 6.82 Mediana da quantidade de peixes: 22.0 Maior contagem em um local: 89.0 Menor contagem em um local: 0.0 Total de medições de pH: 7 Conhecendo um pouco mais! Sugestão de Imagem 1: Inserir aqui um diagrama simples e intuitivo. Deveria mostrar uma série de números e indicar visualmente o que são a média (o ponto de equilíbrio), a mediana (o valor do meio), o mínimo e o máximo. Fique Alerta! Média vs. Mediana Amédia (.mean()) é a soma de todos os valores dividida pela quantidade de valores. Ela é muito útil, mas pode ser enganosa se houver valores extremos (outliers). Por exemplo, na lista [10, 20, 30, 40, 1000], a média é 220, um valor que não representa bem a maioria dos dados. Amediana (.median()) é o valor que está exatamente no meio da lista de dados, após ela ser ordenada. Na mesma lista, a mediana é 30, que é um valor muito mais representativo da ”tendência central”dos dados. Escolher entre média e mediana é uma decisão importante na análise de dados. 132
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB A Ferramenta Definitiva: Resumo Estatístico com .describe() Calcular cada métrica individualmente é útil, mas o que aconteceria se você quisesse um resumo rápido de todas as colunas numéricas de uma só vez? O Pandas nos oferece uma função incrivelmente poderosa para isso. Com um único comando, o método .describe() calcula as principais estatísticas descritivas para todas as colunas numéricas do seu DataFrame. •count: Contagem de valores não nulos. •mean: A média. •std: O desvio padrão. •min: O valor mínimo. •25%: O primeiro quartil. •50%: A mediana (o segundo quartil). •75%: O terceiro quartil. •max: O valor máximo. Caso Prático Vamos aplicar o .describe() ao nosso DataFrame df_peixes para obter um panorama completo das nossas medições de campo com um único comando. Copie e Teste! # Gerando o resumo estatístico completo resumo_estatistico = df_peixes.describe() print("--- Resumo Estatístico do DataFrame ---") print(resumo_estatistico) Saída Esperada --- Resumo Estatístico do DataFrame --- quantidade ph_agua count 7.000000 7.000000 mean 33.857143 6.817143 std 31.144823 0.222389 min 0.000000 6.500000 25% 12.500000 6.700000 50% 22.000000 6.820000 75% 50.500000 6.950000 max 89.000000 7.100000 Em segundos, temos uma visão completa. Podemos ver, por exemplo, que a contagem de peixes (quantidade) varia muito (o desvio padrão, std, é alto, quase igual à média), enquanto o pH da água (ph_agua) é bem mais estável (desvio padrão baixo). Essas ferramentas de cálculo estatístico são a base da análise exploratória de dados. Elas nos permitem passar da simples observação para a quantificação, o primeiro passo para descobrir os padrões escondidos em nossos dados. Agora que você sabe como resumir seus dados, o próximo passo é aprender a dividi-los em grupos para fazer comparações ainda mais ricas e detalhadas. 133
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB 3.3.2 Agrupamento de Dados (groupby) Nos tópicos anteriores, você aprendeu a carregar, inspecionar e limpar seus dados. Foi como um explorador que, após uma longa expedição, organiza todas as suas amostras e anotações em uma mesa de laboratório. Agora que tudo está em ordem, começa a fase mais emocionante da descoberta, a análise das informações para encontrar padrões. Ter dados limpos e organizados é essencial, mas o verdadeiro valor está nas histórias que eles podem contar e nas perguntas que podem responder. Até agora, nossos cálculos como .mean() ou .sum() foram aplicados ao conjunto de dados como um todo. Mas, na maioria das vezes, nossas perguntas são mais específicas e comparativas: • ”Qual a produção média de açaí por estado?” • ”Qual a quantidade total de peixes avistados para cada espécie?” • ”Qual local de coleta apresentou a maior média de pH na água?” Para responder a esse tipo de pergunta, precisamos de uma ferramenta que nos permita dividir nossos dados em grupos e analisar cada um deles separadamente. Pense nesta etapa como o momento em que o explorador começa a conectar os pontos, agrupar todas as amostras de uma mesma região para entender suas características comuns. A Estratégia ”Dividir-Aplicar-Combinar”(Split-Apply-Combine) O poder do .groupby() pode ser entendido por uma estratégia de três passos conhecida como ”Dividir-Aplicar-Combinar”(Split-Apply-Combine). Imagine que você tem uma cesta cheia de frutas amazônicas misturadas: açaí, cupuaçu e tucumã. Sua tarefa é descobrir o peso médio de cada tipo de fruta. Como você faria isso? 1. Dividir (Split): Você primeiro separa as frutas em três cestas menores, uma para cada tipo: uma só com açaí, outra só com cupuaçu e a terceira só com tucumã. 2. Aplicar (Apply): Em seguida, você pega cada cesta individualmente e aplica uma operação: você pesa todas as frutas daquela cesta e calcula o peso médio. Você faz isso para a cesta de açaí, depois para a de cupuaçu e, por fim, para a de tucumã. 3. Combinar (Combine): Finalmente, você pega os resultados de cada cesta (o peso médio de cada fruta) e os combina em uma única tabela-resumo, mostrando o resultado final da sua análise. Figura 3.4: Diagrama de representação da estratégia Dividir-Aplicar-Combinar Fonte: Michele Lozada, 2024 134
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Agrupando por uma Única Coluna A forma mais comum de agrupamento é baseada nos valores de uma única coluna categórica. Vamos voltar ao nosso exemplo de monitoramento de peixes para ver isso na prática. Caso Prático Utilizando nosso DataFrame df_peixes, já limpo e com os dados ausentes tratados, queremos descobrir a quantidade total de indivíduos contados para cada espécie. Isso nos ajudará a entender quais espécies foram mais abundantes em nossas coletas. Copie e Teste! import pandas as pd import numpy as np # Recriando o DataFrame já com os dados ausentes tratados dados = { 'local': ['Encontro das Águas','Frente a Manaus','Lago Janauari','Parintins','Frente a Manaus','Lago Janauari',' Encontro das Águas'], 'especie': ['Tambaqui','Tucunaré','Pirarucu','Tambaqui',' Jaraqui','Tucunaré','Pirarucu'], 'quantidade': [45.0, 22.0, 10.0, 56.0, 89.0, 15.0, 0.0], # NaN preenchido com 0 'ph_agua': [6.8, 6.5, 7.1, 6.82, 6.6, 7.0, 6.9] # NaN preenchido com a média } df_peixes = pd.DataFrame(dados) # 1. Dividir: Agrupamos o DataFrame pela coluna 'especie' # 2. Aplicar: Selecionamos a coluna 'quantidade' e aplicamos a soma (.sum()) # 3. Combinar: O Pandas combina os resultados em uma nova Series contagem_por_especie = df_peixes.groupby('especie')['quantidade']. sum() print("--- Contagem Total de Peixes por Espécie ---") print(contagem_por_especie) Saída Esperada --- Contagem Total de Peixes por Espécie --- especie Jaraqui 89.0 Pirarucu 10.0 Tambaqui 101.0 Tucunaré 37.0 Name: quantidade, dtype: float64 135
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB O resultado é uma nova Series onde o índice são as espécies (as categorias pelas quais agrupamos) e os valores são o resultado da soma. Agora, podemos ver claramente que o Tambaqui foi a espécie com o maior número total de indivíduos contados em todas as coletas. Conhecendo um pouco mais! Quando você executa df.groupby('coluna'), o Pandas não retorna imediatamente um DataFrame, mas sim um objeto especial chamado DataFrameGroupBy. Pense nele como uma representação intermediária dos seus dados, já divididos em grupos, mas esperando por uma instrução do que fazer a seguir (o passo ”Aplicar”). É somente quando você aplica uma função de agregação (como .sum(),.mean(),.count(), etc.) que o cálculo é realizado e o resultado final é combinado e exibido. Aplicando Múltiplas Agregações com .agg() E se quisermos mais do que apenas a soma? Muitas vezes, para cada grupo, nos interessa calcular várias estatísticas de uma só vez. Para isso, usamos o método .agg() (de aggregate), que nos permite aplicar uma lista de funções de agregação a cada grupo. Caso Prático Para cada espécie de peixe, a equipe de pesquisa quer um resumo mais completo. Eles precisam saber não apenas a soma (sum), mas também a contagem média de indivíduos por coleta (mean) e o maior número de indivíduos encontrados em uma única coleta (max). Copie e Teste! # Agrupamos por 'especie', selecionamos 'quantidade' e aplicamos . agg() resumo_especies = df_peixes.groupby('especie')['quantidade'].agg([ 'sum','mean','max']) print("--- Resumo Agregado por Espécie ---") print(resumo_especies) Saída Esperada --- Resumo Agregado por Espécie --- sum mean max especie Jaraqui 89.0 89.00 89.0 Pirarucu 10.0 5.00 10.0 Tambaqui 101.0 50.50 56.0 Tucunaré 37.0 18.50 22.0 Desta vez, o resultado é um DataFrame! As linhas ainda são as espécies, mas agora as colunas são as funções de agregação que pedimos. Essa tabela nos dá uma visão muito mais rica. Por exemplo, enquanto o Jaraqui teve o maior número de avistamentos em uma única 136
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Figura 3.7: Gráfico de barras agrupado por espécie Fique Alerta! Quando os nomes das categorias (os rótulos do eixo X) são muito longos, eles podem se sobrepor e dificultar a leitura. Nesses casos, além de rotacionar os rótulos (plt.xticks(rotation=45)), uma ótima alternativa é usar um gráfico de barras horizontais. Basta trocar kind='bar' por kind='barh'. Isso dá mais espaço para os nomes das categorias e pode deixar seu gráfico muito mais limpo. Dominar esses dois tipos de gráficos essenciais já lhe dá o poder de visualizar uma vasta gama de análises de dados. No próximo tópico, vamos aprofundar nossas habilidades, aprendendo a customizar cada detalhe dos nossos gráficos para torná-los ainda mais profissionais e informativos. 3.4.2 Customizando suas Visualizações: Da Análise à Narrativa Nos tópicos anteriores, aprendemos a criar gráficos básicos. Esse é o primeiro passo. Agora, vamos transformar esses rascunhos em ferramentas de comunicação profissionais. Um gráfico padrão mostra os dados; um gráfico customizado conta uma história, guia o leitor através das suas descobertas e destaca os insights mais importantes. Nesta seção, vamos aprofundar nossas habilidades com a Matplotlib. Usaremos um exemplo contínuo para adicionar camadas de informação e contexto, e depois exploraremos como comparar dados e escolher a melhor forma visual para a sua análise. 143
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Aprimorando um Gráfico: Um Estudo de Caso do Nível do Rio Vamos usar nosso exemplo do nível do rio para mostrar um fluxo de trabalho completo, indo de um gráfico simples a uma visualização rica em informações. Caso Prático Uma estação de monitoramento registrou o nível médio de um rio na Amazônia a cada mês. Nosso objetivo é transformar esses dados brutos em uma ferramenta visual de monitoramento de risco de enchentes, que seja clara para a comunidade ribeirinha e para a defesa civil, respeitando alguns critérios. Copie e Teste! import pandas as pd import matplotlib.pyplot as plt # --- DADOS --- dados_rio = { 'mes': ['Jan','Fev','Mar','Abr','Mai','Jun', 'Jul','Ago','Set','Out','Nov','Dez'], 'nivel_m': [18.5, 22.1, 25.7, 28.2, 29.5, 29.1, 27.3, 24.6, 21.0, 18.9, 17.8, 18.1] } df_rio = pd.DataFrame(dados_rio) # --- PASSO 1: Usando um Estilo Profissional --- # Esta única linha melhora drasticamente a aparência do gráfico. plt.style.use('ggplot') # --- PASSO 2: Criação da Figura e do Gráfico Base --- # `plt.subplots` cria a "tela" (fig) e os "eixos" (ax) separados. fig, ax = plt.subplots(figsize=(10, 6)) ax.plot(df_rio['mes'], df_rio['nivel_m'], marker='o', linestyle='- ', color='royalblue', label='Nível Médio do Rio') # --- PASSO 3: Adicionando Contexto com Linhas de Referência --- # `axhline` desenha uma linha para marcar um limiar importante. ax.axhline(y=28.0, color='red', linestyle='--', linewidth=2, label ='Nível de Alerta de Enchente') # --- PASSO 4: Adicionando Narrativa com Anotações --- # `annotate` permite adicionar texto e setas para destacar pontos. pico_mes_index = 4 # Índice do mês de Maio pico_valor = 29.5 ax.annotate('Pico da Cheia', xy=(pico_mes_index, pico_valor), xytext=(pico_mes_index - 2, pico_valor - 0.5), arrowprops=dict( facecolor='black', shrink=0.05, width=1, headwidth=8), fontsize =12, fontweight='bold') 144
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB # --- PASSO 5: Títulos, Rótulos e Legendas --- ax.set_title('Monitoramento do Nível do Rio na Bacia Amazônica (2024)', fontsize=16) ax.set_ylabel('Nível (metros)', fontsize=12) ax.set_xlabel('Mês', fontsize=12) plt.xticks(rotation=45) # Ativa a legenda para identificar as linhas plotadas com 'label' ax.legend() # Ajustes finais para garantir que nada seja cortado plt.tight_layout() plt.show() Ao seguir esses passos, transformamos um gráfico de linha básico em uma ferramenta de análise completa. Ele não apenas mostra os dados, mas também: •Tem aparência profissional (graças ao plt.style.use). •Fornece contexto crucial (a linha de alerta de enchente). •Conta uma história, destacando o evento mais importante (o pico da cheia). Figura 3.8: Gráfico de linhas com customizações específicas para uma certa finalidade Comparando Gráficos Lado a Lado com Subplots E se quisermos investigar a causa da cheia do rio? A suspeita principal na Amazônia é a chuva. Para visualizar essa relação de causa e efeito, a melhor abordagem é colocar os gráficos de chuva e de nível do rio juntos na mesma figura, usando subplots. 145
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Caso Prático Um hidrólogo quer demonstrar visualmente como o ciclo de chuvas impacta o nível do rio, destacando o atraso entre o pico da chuva e o pico da cheia. Para isso, precisamos criar uma única figura com dois gráficos: um de barras para a precipitação mensal e um de linha para o nível do rio. Copie e Teste! import pandas as pd import matplotlib.pyplot as plt plt.style.use('seaborn-v0_8-whitegrid') # --- DADOS --- dados = { 'mes': ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago' ,'Set','Out','Nov','Dez'], 'nivel_m': [18.5, 22.1, 25.7, 28.2, 29.5, 29.1, 27.3, 24.6, 21.0, 18.9, 17.8, 18.1], 'precipitacao_mm': [310, 300, 320, 250, 180, 90, 60, 40, 70, 120, 150, 210] } df = pd.DataFrame(dados) # --- CRIAÇÃO DOS SUBPLOTS --- # Figura com 2 linhas e 1 coluna. 'sharex=True' alinha os meses. fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(10, 8), sharex =True) # Adiciona um título geral para a figura inteira fig.suptitle('Análise Sazonal: Chuva vs. Nível do Rio', fontsize =16, fontweight='bold') # PLOT 1: Gráfico de Barras da Chuva (no 1º eixo: axes[0]) axes[0].bar(df['mes'], df['precipitacao_mm'], color='royalblue', label='Chuva') axes[0].set_ylabel('Precipitação (mm)') axes[0].legend() # PLOT 2: Gráfico de Linha do Nível do Rio (no 2º eixo: axes[1]) axes[1].plot(df['mes'], df['nivel_m'], color='darkorange', marker= 'o', label='Nível do Rio') axes[1].set_ylabel('Nível (metros)') axes[1].set_xlabel('Mês') axes[1].legend() # Ajustes finais plt.tight_layout(rect=[0, 0, 1, 0.96]) # Espaço para o subtitle plt.show() 146
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Figura 3.9: Gráficos concatenados através do subplot A História que os Subplots Contam Ao colocar os gráficos juntos, a hipótese do hidrólogo fica visualmente clara: 1. Causa (Chuva): O gráfico de barras superior mostra que o período mais chuvoso se concentra entre Janeiro e Abril. 2. Efeito (Nível do Rio): O gráfico de linha inferior mostra que o rio continua a subir mesmo depois que as chuvas diminuem, atingindo seu pico em Maio/Junho. 3. A Descoberta (O Atraso): A visualização confirma o ”lag”ou atraso. Leva tempo para que a água das chuvas que caem em toda a bacia hidrográfica chegue ao rio principal e cause o pico da cheia. Esta é a verdadeira força da customização avançada: não se trata apenas de fazer um gráfico, mas de projetar uma visualização que responda a uma pergunta específica e revele padrões complexos. Alternativas Visuais para Dados Categóricos Vamos verificar como os mesmos dados podem ser apresentados de formas alternativa para contar histórias diferentes, usando nosso exemplo da contagem de peixes df_peixes. 147
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Barras Horizontais (barh): Para Clareza em Rótulos Longos Quando os nomes das categorias são longos, um gráfico de barras horizontais é a melhor escolha para evitar a sobreposição de texto e facilitar a leitura. Nesse exemplo vamos agrupar pela média de peixes (ao invés do total) em diferentes locais (ao invés da espécie). Copie e Teste! # 1. Agrupar os dados para obter a média por local e ordenar as barras de forma decrescente (maior para o menor) contagem_por_local = df_peixes.groupby('local')['quantidade'].mean ().sort_values() # 2. Criar o gráfico de barras horizontal com os dados contagem_por_local.plot(kind='barh', color=['skyblue','salmon',' lightgreen','gold']) # 3. Customizar e exibir o gráfico plt.title('Média de Peixes por Local') plt.xlabel('Quantidade Média Observada') plt.ylabel('Local') plt.grid(axis='y', linestyle='--', alpha=0.7) plt.tight_layout() plt.show() Figura 3.10: Gráficos de barras horizontais agrupado por local 148
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Gráfico de Pizza (pie): Para Mostrar Proporções de um Todo Se o objetivo é verificar a porcentagem de cada categoria em relação ao total, um gráfico de pizza pode ser uma alternativa mais indicada. Copie e Teste! contagem_por_local.plot( kind='pie',# Estilo do gráfico y='quantidade',# Quais dados serão usados nas porcentagens autopct='%1.1f%%',# Mostra as porcentagens figsize=(6, 6), # Tamanho do gráfico legend=False, # Nesse caso é desnecessário labels=contagem_por_local.index, # Rótulos dos locais colors=['skyblue','salmon','lightgreen','gold'] ) plt.title('Composição Relativa da Média de Peixes Coletados') plt.ylabel('')# Remove o rótulo 'quantidade' que é desnecessário plt.show() Figura 3.11: Gráficos de pizza agrupado por local 149
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB Fique Alerta! Use gráficos de pizza com moderação. Eles funcionam bem para poucas categorias (geralmente menos de 6). Quando há muitas fatias, a comparação se torna difícil e um gráfico de barras é quase sempre uma escolha melhor e mais clara. Dominar essas técnicas de customização eleva seu trabalho de simples plotagem de dados para uma verdadeira comunicação visual de insights, uma habilidade essencial para qualquer analista de dados. 3.4.3 Gráficos Estatísticos Nos tópicos anteriores, aprendemos a usar gráficos de linhas para ver tendências e gráficos de barras para comparar categorias. Essas são ferramentas fantásticas, como um mapa e uma régua para nossas expedições de dados. No entanto, para entender a história completa, um explorador precisa de ferramentas mais avançadas, como uma lupa para ver a composição do solo ou um binóculo para enxergar padrões à distância. É aqui que entram os gráficos estatísticos. Eles não mostram apenas os dados; eles revelam a estrutura interna das informações: sua distribuição, sua dispersão e as relações entre diferentes variáveis. Se os gráficos de barras e linhas respondem ”quanto?”e ”quando?”, os gráficos estatísticos respondem ”com que frequência?”, ”quão espalhados?”e ”eles estão conectados?”. Nesta seção, vamos aprofundar nossa análise com as seguintes ferramentas: •Histograma: Para entender a frequência e a distribuição dos nossos dados. •Boxplot: Para resumir a dispersão dos dados e comparar grupos de forma eficaz. •Diagrama de Dispersão (Scatter Plot): Para investigar a relação entre duas variáveis numéricas. Conjunto de Dados: A Safra de Castanha-do-Pará Caso Prático Imagine que somos analistas de dados em uma cooperativa de produtores de castanhado-pará (Bertholletia excelsa) na Amazônia. A cooperativa coletou dados de centenas de árvores para entender melhor o que influencia uma boa colheita. Os dados estão no arquivo safra_castanha.csv e contêm as seguintes informações para cada árvore: •id_arvore: Um identificador único para cada castanheira. •regiao: A região de coleta (ex: ’Reserva Tapajós’, ’Floresta Jari’). •diametro_cm: O diâmetro do tronco da árvore em centímetros, uma medida que ajuda a estimar a idade e a saúde da árvore. •producao_kg: A quantidade total de castanhas coletadas daquela árvore na última safra, em quilogramas. Nosso objetivo é usar gráficos estatísticos para transformar essa tabela de números em insights que ajudem a cooperativa a melhorar suas práticas de manejo sustentável. 150
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB O primeiro passo para iniciar nossa exploração e análise é carregar e inspecionar nossos dados referentes a safra de castanha-do-pará. Copie e Teste! import pandas as pd import matplotlib.pyplot as plt import numpy as np # Para garantir que nossos exemplos sejam reprodutíveis, vamos criar os dados np.random.seed(42) # Semente para reprodutibilidade dados = { 'id_arvore':range(1, 101), 'regiao': np.random.choice(['Reserva Tapajós','Floresta Jari' ], 100), 'diametro_cm': np.random.normal(loc=120, scale=25, size=100), 'producao_kg': np.nan # Vamos preencher com base no diâmetro } df_safra = pd.DataFrame(dados) # Criando uma relação realista entre diâmetro e produção com um pouco de ruído df_safra['producao_kg'] = (df_safra['diametro_cm'] * 0.45) + np. random.normal(loc=0, scale=10, size=100) # Garantindo que não haja produção negativa df_safra['producao_kg'] = df_safra['producao_kg'].clip(lower=5) print("--- Primeiras 5 linhas do nosso conjunto de dados ---") print(df_safra.head()) print("\n--- Informações gerais do DataFrame ---") df_safra.info() Saída Esperada --- Primeiras 5 linhas do nosso conjunto de dados --- id_arvore regiao diametro_cm producao_kg 0 1 Reserva Tapajós 132.410131 62.887648 1 2 Floresta Jari 114.721473 67.877897 2 3 Reserva Tapajós 128.535815 59.623193 3 4 Reserva Tapajós 155.132333 74.288219 4 5 Reserva Tapajós 111.230291 43.053155 --- Informações gerais do DataFrame --- <class 'pandas.core.frame.DataFrame'> RangeIndex: 100 entries, 0 to 99 Data columns (total 4 columns): 151
CAPÍTULO 3. ANÁLISE DE DADOS COM PANDAS E MATPLOTLIB # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_arvore 100 non-null int64 1 regiao 100 non-null object 2 diametro_cm 100 non-null float64 3 producao_kg 100 non-null float64 dtypes: float64(2), int64(1), object(1) memory usage: 3.2+ KB Histograma: A Frequência dos Dados O primeiro passo para entender uma variável numérica é olhar sua distribuição. Um histograma é a ferramenta perfeita para isso. Ele agrupa os dados em ”faixas”(ou bins) de igual tamanho e conta quantos valores caem em cada faixa. O resultado é um gráfico de barras que mostra onde os dados estão mais concentrados. Caso Prático A cooperativa quer saber qual é a faixa de produção mais comum entre as castanheiras. Existem muitas árvores de baixa produção e poucas de alta produção, ou a maioria se concentra em um valor médio? Copie e Teste! import matplotlib.pyplot as plt plt.style.use('seaborn-v0_8-whitegrid') # Criando a figura e os eixos fig, ax = plt.subplots(figsize=(10, 6)) # Plotando o histograma da coluna 'producao_kg' com 20 intervalos ax.hist(df_safra['producao_kg'], bins=20, color='forestgreen', edgecolor='black') # Customizando o gráfico ax.set_title('Distribuição da Produção de Castanha-do-Pará', fontsize=16) ax.set_xlabel('Produção por Árvore (kg)', fontsize=12) ax.set_ylabel('Frequência (Nº de Árvores)', fontsize=12) plt.show() O histograma 3.12 nos mostra uma imagem interessante que se assemelha a um sino, conhecida como distribuição normal ou ”curva de sino”. Isso nos diz que: • A maioria das árvores tem uma produção concentrada em torno do centro do gráfico (provavelmente entre 40 e 60 kg). • Árvores com produção muito baixa ou muito alta são menos frequentes, como mostram as barras mais curtas nas extremidades. 152