Projeto: análise de dados simples
LidoNesta aula, você vai construir um projeto completo de análise de dados simples em Python, cobrindo desde a leitura de arquivos CSV e JSON até a agregação e o cálculo de estatísticas básicas, com foco em boas práticas e saída formatada. Ao final, você terá um script funcional que pode ser adaptado para seus próprios conjuntos de dados.
Bem-vindo à aula 94 do nosso curso de Python! Hoje vamos colocar em prática tudo o que você aprendeu sobre manipulação de arquivos, estruturas de dados e estatística, construindo um projeto completo de análise de dados simples. O objetivo é criar um script que leia dados de um arquivo (CSV ou JSON), realize agregações, calcule estatísticas básicas e gere uma saída clara e organizada.
Este projeto é um marco importante porque integra várias habilidades: lidar com formatos de dados do mundo real, usar bibliotecas padrão do Python e pensar como um analista de dados. Vamos começar do zero, entendendo cada etapa, para que você possa adaptar o código aos seus próprios projetos futuros.
Lendo CSV/JSON
O primeiro passo em qualquer análise de dados é carregar os dados para dentro do programa. Python oferece módulos nativos para trabalhar com CSV e JSON, que são formatos extremamente comuns para armazenar dados tabulares e estruturados.
Para CSV, usamos o módulo csv. Ele permite ler e escrever arquivos onde os valores são separados por vírgulas (ou outros delimitadores). Para JSON, usamos o módulo json, que converte dados JSON em estruturas Python (listas, dicionários, etc.). Vamos ver como fazer isso de forma robusta, tratando possíveis erros de arquivo não encontrado ou formato inválido.
Exemplo de leitura de CSV:
import csv
def ler_csv(caminho):
"""Lê um arquivo CSV e retorna uma lista de dicionários."""
try:
with open(caminho, 'r', encoding='utf-8') as arquivo:
leitor = csv.DictReader(arquivo)
return list(leitor)
except FileNotFoundError:
print(f"Erro: arquivo não encontrado: {caminho}")
return []
except Exception as e:
print(f"Erro ao ler CSV: {e}")
return []Para JSON, a leitura é ainda mais simples:
import json
def ler_json(caminho):
"""Lê um arquivo JSON e retorna os dados (geralmente uma lista ou dicionário)."""
try:
with open(caminho, 'r', encoding='utf-8') as arquivo:
dados = json.load(arquivo)
return dados
except FileNotFoundError:
print(f"Erro: arquivo não encontrado: {caminho}")
return []
except json.JSONDecodeError as e:
print(f"Erro: JSON inválido: {e}")
return []
except Exception as e:
print(f"Erro ao ler JSON: {e}")
return []No nosso projeto, vamos criar uma função que detecta a extensão do arquivo e chama o leitor adequado. Isso torna o código mais flexível e reutilizável.
Agregação
Agregação é o processo de combinar dados para obter informações resumidas. Por exemplo, calcular a soma, a média, o valor máximo ou mínimo de uma coluna, ou agrupar dados por categorias. Em Python, podemos fazer isso manualmente com laços e dicionários, ou usar a biblioteca statistics para algumas funções.
Vamos supor que temos um conjunto de dados de vendas com campos como 'produto', 'quantidade' e 'preco'. Uma agregação comum é calcular o total de vendas por produto. Podemos fazer isso com um dicionário:
def agregar_por_produto(dados):
"""Agrega a quantidade e o total vendido por produto."""
total_por_produto = {}
for registro in dados:
produto = registro['produto']
quantidade = int(registro['quantidade'])
preco = float(registro['preco'])
total = quantidade * preco
if produto not in total_por_produto:
total_por_produto[produto] = {'quantidade': 0, 'total': 0.0}
total_por_produto[produto]['quantidade'] += quantidade
total_por_produto[produto]['total'] += total
return total_por_produtoOutra agregação útil é contar a frequência de valores em uma coluna, por exemplo, quantos registros existem para cada categoria. Isso pode ser feito com collections.Counter:
from collections import Counter
def contar_por_categoria(dados, campo):
"""Conta a frequência de valores em um campo específico."""
valores = [registro[campo] for registro in dados if campo in registro]
return Counter(valores)Essas funções fornecem a base para análises mais complexas. No projeto, vamos combinar várias agregações para responder perguntas sobre os dados.
Estatísticas básicas
Estatísticas básicas incluem média, mediana, moda, desvio padrão, variância, valores mínimos e máximos. Python tem o módulo statistics que implementa essas funções de forma eficiente.
Aqui estão algumas funções essenciais:
statistics.mean(dados)– média aritméticastatistics.median(dados)– medianastatistics.mode(dados)– moda (o valor mais frequente)statistics.stdev(dados)– desvio padrão amostralstatistics.variance(dados)– variância amostralmin(dados)emax(dados)– mínimos e máximos
Vamos criar uma função que calcula um resumo estatístico de uma lista de números:
import statistics
def resumo_estatistico(valores):
"""Retorna um dicionário com estatísticas básicas de uma lista de números."""
if not valores:
return {}
return {
'média': statistics.mean(valores),
'mediana': statistics.median(valores),
'moda': statistics.mode(valores) if len(valores) > 1 else valores[0],
'desvio_padrão': statistics.stdev(valores) if len(valores) > 1 else 0,
'mínimo': min(valores),
'máximo': max(valores),
'total': sum(valores)
}Note que statistics.mode pode lançar uma exceção se não houver uma moda única, então tratamos isso de forma simples. No projeto, vamos aplicar essas funções a colunas numéricas dos dados.
Saída
A saída dos resultados é tão importante quanto os cálculos. Uma boa saída deve ser clara, organizada e legível. Podemos usar print com formatação, criar tabelas simples com tabulate (biblioteca externa) ou gerar um relatório em texto.
Vamos criar funções para exibir os resultados de forma amigável. Por exemplo, para mostrar um dicionário de agregação:
def exibir_agregacao(agregado):
"""Exibe uma agregação no formato chave: valor."""
print("\n--- Agregação ---")
for chave, valor in agregado.items():
print(f"{chave}: {valor}")Para estatísticas, podemos criar uma saída mais detalhada:
def exibir_estatisticas(estatisticas):
"""Exibe um dicionário de estatísticas de forma legível."""
print("\n--- Estatísticas ---")
for nome, valor in estatisticas.items():
print(f"{nome}: {valor:.2f}" if isinstance(valor, float) else f"{nome}: {valor}")Além disso, podemos salvar a saída em um arquivo texto para reutilização. Usamos open no modo 'w' e escrevemos as linhas.
def salvar_relatorio(nome_arquivo, conteudo):
"""Salva um texto em um arquivo."""
with open(nome_arquivo, 'w', encoding='utf-8') as arquivo:
arquivo.write(conteudo)
print(f"Relatório salvo em {nome_arquivo}")No projeto final, vamos combinar tudo em um script principal que lê os dados, processa e exibe/salva os resultados.
Projeto completo
Agora vamos montar o projeto completo. Vamos criar um script que aceita um arquivo (CSV ou JSON) e realiza as seguintes tarefas:
- Ler os dados do arquivo.
- Exibir as primeiras linhas (para inspeção).
- Calcular estatísticas básicas de colunas numéricas.
- Agregar por uma coluna categórica (ex.: produto).
- Exibir os resultados e salvar em um relatório.
Vamos escrever o código passo a passo.
import csv
import json
import statistics
from collections import Counter
import os
def ler_csv(caminho):
try:
with open(caminho, 'r', encoding='utf-8') as f:
return list(csv.DictReader(f))
except FileNotFoundError:
print(f"Erro: arquivo não encontrado: {caminho}")
except Exception as e:
print(f"Erro ao ler CSV: {e}")
return []
def ler_json(caminho):
try:
with open(caminho, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f"Erro: arquivo não encontrado: {caminho}")
except json.JSONDecodeError as e:
print(f"Erro: JSON inválido: {e}")
except Exception as e:
print(f"Erro ao ler JSON: {e}")
return []
def carregar_dados(caminho):
"""Carrega dados de um arquivo CSV ou JSON."""
if not os.path.exists(caminho):
print(f"Erro: arquivo {caminho} não existe.")
return []
if caminho.endswith('.csv'):
return ler_csv(caminho)
elif caminho.endswith('.json'):
return ler_json(caminho)
else:
print("Formato não suportado. Use .csv ou .json")
return []
def resumo_estatistico(valores):
if not valores:
return {}
return {
'média': statistics.mean(valores),
'mediana': statistics.median(valores),
'moda': statistics.mode(valores) if len(valores) > 1 else valores[0],
'desvio_padrão': statistics.stdev(valores) if len(valores) > 1 else 0,
'mínimo': min(valores),
'máximo': max(valores),
'total': sum(valores)
}
def agregar_por_campo(dados, campo_agrupador, campo_valor, operacao='soma'):
"""Agrega valores de um campo numérico por um campo categórico."""
agregado = {}
for registro in dados:
grupo = registro.get(campo_agrupador)
valor = registro.get(campo_valor)
if grupo is None or valor is None:
continue
try:
valor = float(valor)
except ValueError:
continue
if grupo not in agregado:
agregado[grupo] = []
agregado[grupo].append(valor)
resultado = {}
for grupo, valores in agregado.items():
if operacao == 'soma':
resultado[grupo] = sum(valores)
elif operacao == 'media':
resultado[grupo] = sum(valores) / len(valores)
elif operacao == 'contagem':
resultado[grupo] = len(valores)
elif operacao == 'max':
resultado[grupo] = max(valores)
elif operacao == 'min':
resultado[grupo] = min(valores)
return resultado
def exibir_dados(dados, n=5):
"""Exibe as primeiras n linhas dos dados."""
if not dados:
print("Nenhum dado para exibir.")
return
print(f"Primeiras {n} linhas:")
for i, linha in enumerate(dados[:n]):
print(f"{i+1}: {linha}")
def exibir_estatisticas(estatisticas):
print("\n=== Estatísticas da coluna 'preco' ===")
for chave, valor in estatisticas.items():
if isinstance(valor, float):
print(f"{chave}: {valor:.2f}")
else:
print(f"{chave}: {valor}")
def exibir_agregacao(agregado):
print("\n=== Total de vendas por produto ===")
for produto, total in agregado.items():
print(f"{produto}: R$ {total:.2f}")
def principal(caminho_arquivo):
dados = carregar_dados(caminho_arquivo)
if not dados:
return
exibir_dados(dados)
# Supondo que temos colunas 'produto', 'quantidade', 'preco'
precos = []
for linha in dados:
try:
precos.append(float(linha['preco']))
except (KeyError, ValueError):
continue
estat = resumo_estatistico(precos)
exibir_estatisticas(estat)
agregado = agregar_por_campo(dados, 'produto', 'preco', 'soma')
# Para soma, precisamos multiplicar pela quantidade? Vamos fazer simples: soma apenas preco
# Mas para um exemplo real, faríamos quantidade*preco.
# Vamos ajustar: usar uma função dedicada.
exibir_agregacao(agregado)
# Salvar relatório
relatorio = f"Relatório de análise\nArquivo: {caminho_arquivo}\n"
relatorio += f"Média de preço: {estat['média']:.2f}\n"
relatorio += "\nAgregação por produto:\n"
for prod, total in agregado.items():
relatorio += f"{prod}: R$ {total:.2f}\n"
with open("relatorio.txt", "w", encoding='utf-8') as f:
f.write(relatorio)
print("\nRelatório salvo em relatorio.txt")
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("Uso: python analise.py ")
else:
principal(sys.argv[1]) Este código é um esqueleto; você pode adaptá-lo conforme seus dados. Note que a agregação de vendas por produto deve multiplicar quantidade por preço. Vamos corrigir isso nos exercícios.
Boas práticas
Ao trabalhar com análise de dados, algumas boas práticas são essenciais:
- Valide os dados antes de processar: verifique se os campos existem e são do tipo esperado.
- Trate erros com try/except para evitar que o programa quebre com dados inesperados.
- Documente suas funções com docstrings para facilitar a manutenção.
- Separe a lógica de leitura, processamento e saída em funções, tornando o código modular e testável.
- Use nomes de variáveis descritivos para tornar o código legível.
Essas práticas não só ajudam você agora, mas também em projetos maiores.
Conclusão
Nesta aula, você construiu um projeto completo de análise de dados simples em Python. Você aprendeu a ler arquivos CSV e JSON, realizar agregações, calcular estatísticas básicas e gerar saídas formatadas. Esse conhecimento é a base para ferramentas mais avançadas como pandas e NumPy. Continue praticando com seus próprios conjuntos de dados para dominar essas técnicas.
Exercícios
- Exercício 1: Escreva uma função que leia um arquivo CSV e retorne o número de linhas (ignorando o cabeçalho). Teste com um arquivo de exemplo.
def contar_linhas_csv(caminho):
import csv
with open(caminho, 'r', encoding='utf-8') as arquivo:
leitor = csv.reader(arquivo)
next(leitor) # pula cabeçalho
return sum(1 for _ in leitor)def mediana_manual(lista):
ordenada = sorted(lista)
n = len(ordenada)
if n % 2 == 1:
return ordenada[n//2]
else:
meio1 = ordenada[n//2 - 1]
meio2 = ordenada[n//2]
return (meio1 + meio2) / 2def contar_por_grupo(dados, campo):
contagem = {}
for item in dados:
chave = item.get(campo)
if chave is None:
continue
contagem[chave] = contagem.get(chave, 0) + 1
return contagemimport json
def principal():
with open('produtos.json', 'r', encoding='utf-8') as f:
produtos = json.load(f)
mais_caro = max(produtos, key=lambda p: p['preco'])
mais_barato = min(produtos, key=lambda p: p['preco'])
print(f"Mais caro: {mais_caro['nome']} - R$ {mais_caro['preco']:.2f}")
print(f"Mais barato: {mais_barato['nome']} - R$ {mais_barato['preco']:.2f}")
if __name__ == '__main__':
principal()def total_vendas_por_produto(dados):
totais = {}
for reg in dados:
produto = reg['produto']
qtd = int(reg['quantidade'])
preco = float(reg['preco'])
total = qtd * preco
totais[produto] = totais.get(produto, 0) + total
return totais
# Exemplo de uso:
dados = [
{'produto': 'A', 'quantidade': 2, 'preco': 10.0},
{'produto': 'B', 'quantidade': 1, 'preco': 20.0},
{'produto': 'A', 'quantidade': 3, 'preco': 10.0}
]
print(total_vendas_por_produto(dados)) # {'A': 50.0, 'B': 20.0}