Introdução ao Python para análise de dados
Introdução prática a Python para análise de dados, do zero ao pandas: variáveis, texto, coleções, decisões, laços, funções, tratamento de erros, DataFrames, limpeza, groupby, merge, a base Iris, uma mini-análise e gráficos, com exercícios e gabarito.
1. Visão geral
Uma apostila prática, do zero ao pandas. O texto explica cada conceito. Sempre que aparecer um bloco marcado Digite comigo, é a hora de escrever o código junto — linha por linha, sem pressa. Os blocos Saída no console mostram o que deve aparecer na tela. Os avisos de conceito, dica, atenção e desafio destacam o que merece cuidado. No fim há uma lista de exercícios com gabarito para praticar.
Python virou a linguagem mais usada no mundo dos dados por um motivo simples: ela é fácil de ler. Um programa em Python se parece bastante com uma receita escrita em português, com passos claros e poucos símbolos estranhos. Nesta disciplina você não vai virar programador — vai aprender o suficiente para conversar com os dados: carregar uma planilha, encontrar erros, limpar informação e responder perguntas de negócio.
A melhor forma de aprender a programar é programando. Por isso a apostila foi feita para ser digitada, não apenas lida. Cada bloco Digite comigo é um convite: abra o ambiente, escreva o código, execute e veja o resultado. Errar aqui é parte do processo — o computador reclama, você corrige e segue em frente.
O que é uma linguagem de programação
É um jeito de dar instruções ao computador usando palavras e regras bem definidas. O computador é obediente, mas literal: faz exatamente o que está escrito, na ordem em que está escrito. Boa parte de aprender a programar é aprender a ser preciso.
O que você vai aprender
- Variáveis, tipos, contas e comparações
- Como limpar e formatar texto (f-strings, fatiamento e métodos de string)
- Listas, compreensão de lista, dicionários e conjuntos
- Decisões com
if/elif/else, laçosfore funções (inclusivelambda) - Como tratar dados sujos com
try/except - pandas:
DataFrame, leitura de CSV, seleção, filtros, limpeza,apply,groupby, datas emerge - Uma análise com dados reais (base Iris) e gráficos com matplotlib
O que você vai precisar
- VS Code • Python 3 • pandas • matplotlib
2. Antes de começar: VS Code e erros
Onde escrever o código: VS Code
Vamos usar o Visual Studio Code (VS Code), um editor gratuito. A preparação é feita uma única vez:
- Instale o Python a partir de python.org (no Windows, marque a opção Add Python to PATH durante a instalação).
- Instale o VS Code e, dentro dele, a extensão Python da Microsoft (ícone de extensões na barra lateral).
- Crie uma pasta para a disciplina, abra-a no VS Code e crie um arquivo terminado em
.py— por exemplo,aula.py. - Abra o terminal integrado (menu Terminal → New Terminal) e instale as bibliotecas:
Terminal
pip install pandas matplotlib
Escreva o primeiro programa no arquivo. A função print() mostra algo na tela.
Digite comigo · aula.py
print("Olá, mundo dos dados!")
Para executar, clique no botão ▷ Run Python File (canto superior direito) ou digite no terminal:
Terminal
python aula.py
O resultado aparece no terminal.
Saída no console
Olá, mundo dos dados!
Quando algo dá errado
Mais cedo ou mais tarde o programa vai parar com uma mensagem em vermelho. Isso é normal e não quebra nada. A informação mais útil está quase sempre na última linha da mensagem, que resume o tipo do erro. Aprender a ler esse resumo economiza muito tempo.
Digite comigo
print(10 / 0)
Saída no console
ZeroDivisionError: division by zero
A última linha diz ZeroDivisionError: uma divisão por zero. O nome do erro já aponta o caminho da correção.
3. Variáveis, tipos e contas
Guardando informação: variáveis e tipos
Para trabalhar com um dado, precisamos guardá-lo com um nome. Esse "lugar com nome" é uma variável. O sinal de igual (=) não significa "é igual a": ele significa "guarde o valor da direita na caixa da esquerda".
Digite comigo
nome = "Maria"
idade = 34
ativo = True
print(nome, idade, ativo)
Todo valor tem um tipo. Os quatro básicos: texto (str), inteiro (int), decimal (float) e booleano (bool, apenas True ou False). A função type() revela o tipo — útil porque muitos erros vêm de um número guardado como texto.
Digite comigo
print(type("Maria")) # str
print(type(34)) # int
print(type(19.90)) # float
print(type(True)) # bool
Convertendo tipos
Quando um número chega como texto (lido de uma planilha, por exemplo), convertemos com int(), float() ou str().
Digite comigo
preco_texto = "19.90"
preco = float(preco_texto) # texto -> número decimal
print(preco * 2) # agora dá para calcular
Saída no console
39.8
Contas e comparações
Python funciona como uma calculadora. Duas operações merecem atenção: // (divisão inteira, só a parte inteira) e % (resto da divisão, ótimo para saber se um número é par ou múltiplo de outro).
Digite comigo
print(10 / 3) # 3.3333333333333335
print(10 // 3) # 3 (divisão inteira)
print(10 % 3) # 1 (resto)
print(round(10 / 3, 2)) # 3.33 (arredonda com 2 casas)
Comparações (>, <, ==, !=) sempre devolvem um booleano. Para combinar condições, use and (e), or (ou) e not (não).
Digite comigo
idade = 20
print(idade == 20) # True
print(idade >= 18 and idade < 65) # True: as DUAS condições valem
print(not (idade > 100)) # True
4. Trabalhando com texto
Boa parte do trabalho com dados é lidar com texto bagunçado: nomes com espaços a mais, e-mails em maiúsculas, valores com símbolos. Por isso vale dominar as strings.
Juntando texto: f-strings
A f-string monta uma frase misturando texto e variáveis: coloque um f antes das aspas e escreva as variáveis entre chaves. Ela também formata números.
Digite comigo
nome = "Maria"
valor = 1234.5
print(f"{nome} gastou R$ {valor:.2f}") # duas casas decimais
Saída no console
Maria gastou R$ 1234.50
Índice e fatiamento
Cada caractere de um texto tem uma posição, começando em zero. Podemos pegar um caractere pela posição ou uma "fatia" com [inicio:fim] (o fim não entra).
Digite comigo
cpf = "12345678900"
print(cpf[0]) # 1 (primeiro caractere)
print(cpf[0:3]) # 123 (posições 0, 1 e 2)
print(cpf[-2:]) # 00 (os dois últimos)
Métodos de texto
Métodos são "ferramentas embutidas" acionadas com um ponto. Os mais úteis na limpeza: .strip() (tira espaços das pontas), .lower() / .upper(), .replace(a, b) (troca trechos), .split(sep) (quebra em pedaços) e .title() (primeira letra maiúscula em cada palavra). Podemos encadear vários.
Digite comigo
nome = " maria SILVA "
limpo = nome.strip().title()
print(limpo) # "Maria Silva"
print(" ".join(limpo.split())) # "Maria Silva" (tira espaços duplos)
Saída no console
Maria Silva
Maria Silva
O truque " ".join(texto.split()) é um clássico: split() quebra o texto em palavras (descartando espaços extras) e join() remonta com um único espaço.
Digite comigo
valor_txt = "R$ 1.234,56"
limpo = valor_txt.replace("Rquot;, "").replace(".", "").replace(",", ".").strip()
valor = float(limpo)
print(valor + 10) # 1244.56
Por que padronizar texto
"Maria", "maria" e " MARIA " são, para o computador, três textos diferentes. Se você contar clientes chamados "Maria" sem padronizar antes, vai contar errado. Uniformizar o texto é o primeiro passo de quase toda análise séria.
5. Coleções: listas, dicionários e conjuntos
Listas
A lista é uma sequência ordenada, entre colchetes. Aceita índice e fatiamento (como as strings) e traz funções prontas: len(), sum(), max(), min(), além do método .append() para adicionar.
Digite comigo
precos = [19.90, 45.00, 12.50, 89.90]
print(len(precos)) # 4 (quantos itens)
print(sum(precos)) # 167.3 (soma)
print(max(precos)) # 89.9 (maior)
precos.append(30.00) # adiciona um item ao fim
print(precos[:2]) # [19.9, 45.0] (os dois primeiros)
Compreensão de lista
Uma forma compacta e muito usada de criar uma lista a partir de outra. Leia como uma frase: "para cada p em precos, faça p * 1.1".
Digite comigo
precos = [19.90, 45.00, 12.50, 89.90]
com_imposto = [p * 1.1 for p in precos] # aplica 10% a todos
caros = [p for p in precos if p > 40] # filtra os acima de 40
print(com_imposto)
print(caros)
Saída no console
[21.89, 49.50, 13.75, 98.89]
[45.0, 89.9]
Dicionários
O dicionário guarda pares chave → valor. Você acessa por um nome, não por posição. É ideal para representar um registro com vários atributos.
Digite comigo
cliente = {"nome": "Maria", "idade": 34, "cidade": "São Paulo"}
print(cliente["cidade"]) # São Paulo
cliente["email"] = "m@x.com" # adiciona um par novo
for chave, valor in cliente.items():
print(chave, "->", valor)
Dicionário, lista de dicionários e tabela
Guarde esta ponte: um dicionário parece uma linha de tabela (as chaves são os nomes das colunas). Uma lista de dicionários parece uma tabela inteira. É exatamente assim que o pandas vai organizar os dados adiante.
Digite comigo
tabela = [
{"nome": "Maria", "valor": 150.0},
{"nome": "João", "valor": 89.90},
{"nome": "Ana", "valor": 230.5},
]
print(tabela[1]["nome"]) # João
Conjuntos: valores únicos
O conjunto (set) guarda apenas valores distintos — perfeito para descobrir quantos valores diferentes existem ou detectar repetição.
Digite comigo
cidades = ["SP", "RJ", "SP", "MG", "RJ", "SP"]
print(set(cidades)) # {'SP', 'RJ', 'MG'}
print(len(set(cidades))) # 3 cidades distintas
6. Decisões e laços
Tomando decisões: if / elif / else
Programas ficam úteis quando decidem o que fazer conforme os dados. O bloco que roda quando a condição é verdadeira fica indentado (deslocado à direita). Com mais de duas possibilidades, usamos elif no meio; o Python testa de cima para baixo e para na primeira condição verdadeira.
Digite comigo
valor = 250
if valor >= 500:
faixa = "alto"
elif valor >= 100:
faixa = "medio"
else:
faixa = "baixo"
print(f"Cliente de ticket {faixa}")
O operador in verifica se um valor está numa coleção — muito prático para validar.
Digite comigo
uf = "SP"
validos = ["SP", "RJ", "MG", "PR", "RS"]
if uf in validos:
print("UF reconhecida")
else:
print("UF inválida")
Repetindo tarefas: laços
Para não repetir o mesmo comando dezenas de vezes, pedimos ao computador que repita. O laço for percorre uma coleção, item por item.
Digite comigo
precos = [19.90, 45.00, 12.50]
total = 0
for p in precos:
total = total + p # acumula
print(f"Total: R$ {total:.2f}")
O padrão acima (uma variável que vai somando) chama-se acumulador. Um primo dele é o contador, que soma 1 a cada item que satisfaz uma condição. Veja um exemplo mais completo: contar quantos e-mails de uma lista são inválidos (aqui, "sem @").
Digite comigo
emails = ["ana@x.com", "joao.com", "maria@y.com", "sem-arroba"]
invalidos = 0
for email in emails:
if "@" not in email:
invalidos = invalidos + 1
print(f"Inválido: {email}")
print(f"Total de inválidos: {invalidos}")
Saída no console
Inválido: joao.com
Inválido: sem-arroba
Total de inválidos: 2
Digite comigo
nomes = ["Maria", "João", "Ana"]
for i, nome in enumerate(nomes):
print(i, nome) # 0 Maria / 1 João / 2 Ana
7. Funções, lambda e try/except
Organizando o código: funções
Quando um trecho é útil e se repete, transforme-o numa função: um bloco com nome que recebe entradas (parâmetros), faz algo e return devolve um resultado. Uma função pode ter parâmetros com valor padrão e devolver mais de um valor.
Digite comigo
def resumo_vendas(valores, imposto=0.1):
total = sum(valores)
medio = total / len(valores)
total_com_imposto = total * (1 + imposto)
return total, medio, total_com_imposto
t, m, ti = resumo_vendas([100, 200, 300])
print(f"Total: {t} Médio: {m:.2f} Com imposto: {ti:.2f}")
Saída no console
Total: 600 Médio: 200.00 Com imposto: 660.00
O parâmetro imposto=0.1 tem valor padrão: se você não informar, ele vale 0,1. E o return devolveu três valores de uma vez, que capturamos em t, m e ti.
Por que usar funções
Funções evitam repetição e dão nome a uma ideia. Em vez de repetir a mesma conta em dez lugares, você a escreve uma vez, testa e chama pelo nome. Se precisar corrigir, corrige num lugar só. É a base para construir análises maiores sem se perder.
Funções em uma linha: lambda
Às vezes precisamos de uma função tão pequena que criar um def inteiro parece exagero. Para esses casos existe a lambda: uma função curta, sem nome, escrita em uma única linha. Leia lambda x: ... como "dado um x, devolva ...". Ela não usa a palavra return — o resultado é o que vem depois dos dois-pontos.
Exemplo 1 — dobrar um número. A lambda abaixo faz exatamente o mesmo que uma função tradicional, só que em uma linha.
Digite comigo
dobro = lambda x: x * 2
print(dobro(5)) # 10
# é equivalente a escrever:
def dobro_def(x):
return x * 2
print(dobro_def(5)) # 10
Exemplo 2 — decidir com uma condição. A lambda também aceita um teste if / else na mesma linha — útil para classificar um valor.
Digite comigo
situacao = lambda nota: "aprovado" if nota >= 70 else "reprovado"
print(situacao(85)) # aprovado
print(situacao(40)) # reprovado
Quando o dado está sujo: try / except
Dados reais vêm com surpresas: um campo que deveria ser número traz o texto "abc", e a conversão int("abc") quebra o programa. O bloco try / except permite "tentar" uma operação e, se ela falhar, tratar o erro em vez de parar tudo.
Digite comigo
valores = ["100", "abc", "250", ""]
for v in valores:
try:
numero = int(v)
print(numero * 2)
except ValueError:
print(f"'{v}' não é um número válido")
Saída no console
200
'abc' não é um número válido
500
'' não é um número válido
Robustez
Programas que lidam com dados precisam sobreviver ao inesperado. Em vez de confiar que todo valor está perfeito, prevemos a falha e decidimos o que fazer com ela — descartar, substituir por zero, registrar. Adiante, o pandas nos dará um atalho para isso (
errors="coerce"), mas a ideia é a mesma.
8. Bibliotecas e o DataFrame
Ampliando o Python: bibliotecas
O Python básico é enxuto de propósito. Tudo o que é mais especializado vem em bibliotecas que "importamos". O as cria um apelido curto: praticamente todo código de dados no mundo usa pd para o pandas e plt para o matplotlib.
Digite comigo
import pandas as pd
import matplotlib.pyplot as plt
pandas: a estrela da disciplina
O pandas transforma o Python numa ferramenta de análise. Uma planilha inteira vira uma variável que você consulta, filtra e resume com poucas linhas.
DataFrame: a tabela do pandas
A estrutura principal é o DataFrame: uma tabela com linhas e colunas nomeadas, como uma planilha. Cada coluna, isolada, é uma Series.
Digite comigo
import pandas as pd
dados = {
"cliente": ["Maria", "João", "Ana", "Bruno"],
"cidade": ["São Paulo", "Recife", "Curitiba", "Recife"],
"valor": [150.0, 89.90, 230.5, 60.0],
}
df = pd.DataFrame(dados)
print(df)
Saída no console
cliente cidade valor
0 Maria São Paulo 150.00
1 João Recife 89.90
2 Ana Curitiba 230.50
3 Bruno Recife 60.00
A numeração à esquerda é o índice, criado automaticamente para identificar cada linha.
Lendo dados de um arquivo
Na prática os dados vêm de um arquivo, quase sempre um CSV. A função read_csv() carrega tudo de uma vez. Atenção aos parâmetros — eles resolvem os problemas mais comuns com arquivos brasileiros.
Digite comigo
df = pd.read_csv("clientes.csv",
sep=";", # separador ; comum no Brasil
decimal=",", # vírgula como separador decimal
encoding="utf-8") # acentuação
Conhecendo os dados
A primeira coisa após carregar é olhar. Estes comandos respondem às perguntas iniciais de qualquer análise.
Digite comigo
df.head() # 5 primeiras linhas
df.shape # (linhas, colunas)
df.dtypes # tipo de cada coluna
df.info() # resumo: colunas, tipos e quantos valores preenchidos
df.describe() # estatísticas das colunas numéricas
df["cidade"].value_counts() # quantas vezes cada valor aparece
.info() e .value_counts()
O
.info()é seu diagnóstico de qualidade: mostra, coluna a coluna, o tipo e quantos valores estão preenchidos — denuncia na hora um preço guardado como texto ou linhas com campo vazio. Já o.value_counts()revela a distribuição de uma coluna categórica: ótimo para achar categorias escritas de formas diferentes ("SP", "sp", "S.P.").
9. pandas: selecionar, filtrar e limpar
Selecionando com loc e iloc
Para escolher linhas e colunas específicas, o pandas oferece dois seletores: loc trabalha por rótulo (nome da coluna, valor do índice) e iloc por posição (números).
Digite comigo
df["cidade"] # uma coluna inteira (uma Series)
df.loc[0, "cidade"] # valor da linha de índice 0, coluna cidade
df.iloc[0, 1] # mesma célula, por posição (linha 0, coluna 1)
df.loc[df["valor"] > 100, "cliente"] # clientes com valor acima de 100
Filtrando linhas
Escreva uma condição entre colchetes; o pandas mantém as linhas em que ela é verdadeira. Vários atalhos deixam o filtro mais legível: .isin(), .between() e o acessor de texto .str.contains().
Digite comigo
df[df["valor"] > 100] # valor acima de 100
df[df["cidade"].isin(["Recife", "Curitiba"])] # em uma lista de cidades
df[df["valor"].between(80, 200)] # faixa de valores
df[df["cliente"].str.contains("a")] # nome que contém "a"
# combinar condições: & para "e", | para "ou", cada uma entre parênteses
df[(df["valor"] > 100) & (df["cidade"] == "Curitiba")]
Criando e convertendo colunas
Uma coluna nova pode nascer de outras — o cálculo vale para todas as linhas de uma vez. E quando um número veio como texto (formato brasileiro, por exemplo), convertemos.
Digite comigo
# coluna nova a partir de outras
df["com_frete"] = df["valor"] + 15
# converter texto no formato brasileiro para número
serie = pd.Series(["1.234,56", "89,90", "abc"])
numeros = serie.str.replace(".", "", regex=False) # tira milhar
numeros = numeros.str.replace(",", ".", regex=False) # vírgula -> ponto
numeros = pd.to_numeric(numeros, errors="coerce") # texto ruim vira vazio
print(numeros)
Saída no console
0 1234.56
1 89.90
2 NaN
dtype: float64
O errors="coerce" é o atalho prometido: em vez de quebrar em "abc", o pandas troca o valor inválido por NaN (vazio) e segue.
Encontrando problemas: ausentes e duplicatas
Digite comigo
df.isnull().sum() # conta vazios em cada coluna
df["valor"].fillna(0) # preenche vazios com 0
df.dropna(subset=["valor"]) # remove linhas sem valor
df.duplicated().sum() # quantas linhas são duplicadas
df.drop_duplicates(subset="cliente") # remove clientes repetidos
Limpeza é análise
Cada número que sai de uma análise carrega a qualidade dos dados que entraram. Um relatório construído sobre uma tabela cheia de duplicatas e campos vazios dá uma resposta precisa e errada. A limpeza não é uma etapa chata antes do "trabalho de verdade" — ela é o trabalho de verdade.
10. pandas: apply, groupby, datas e merge
Transformando valores: apply e lambda
Às vezes queremos aplicar a cada valor de uma coluna uma regra que não é uma conta simples. O apply() faz isso, e a lambda é uma mini-função escrita na própria linha. Leia lambda v: ... como "dado um valor v, devolva ...".
Digite comigo
df["faixa"] = df["valor"].apply(lambda v: "alto" if v > 100 else "baixo")
print(df[["cliente", "valor", "faixa"]])
Saída no console
cliente valor faixa
0 Maria 150.00 alto
1 João 89.90 baixo
2 Ana 230.50 alto
3 Bruno 60.00 baixo
Agrupando e resumindo: groupby
Uma das operações mais poderosas. O groupby agrupa linhas por uma categoria e calcula um resumo por grupo — como a tabela dinâmica do Excel, em uma linha. Com .agg() calculamos várias medidas de uma vez e damos nome a cada uma.
Digite comigo
# ticket médio por cidade
df.groupby("cidade")["valor"].mean()
# várias medidas de uma vez
df.groupby("cidade").agg(
total=("valor", "sum"),
media=("valor", "mean"),
pedidos=("valor", "count"),
)
Saída no console
total media pedidos
cidade
Curitiba 230.5 230.50 1
Recife 149.9 74.95 2
São Paulo 150.0 150.00 1
Com groupby respondemos perguntas de negócio de verdade: qual cidade compra mais? Qual o ticket médio por categoria? Qual mês teve mais pedidos?
Ordenando e o Top-N
Para ver rankings, ordene com sort_values() e pegue as primeiras linhas com .head().
Digite comigo
# as 3 maiores compras
df.sort_values("valor", ascending=False).head(3)
Trabalhando com datas
Datas costumam chegar como texto. pd.to_datetime() as converte para um tipo de data de verdade, e o acessor .dt extrai partes (ano, mês, dia da semana).
Digite comigo
df["data"] = pd.to_datetime(df["data"]) # texto -> data
df["mes"] = df["data"].dt.month # extrai o mês
df["ano"] = df["data"].dt.year # extrai o ano
# total por mês:
df.groupby("mes")["valor"].sum()
Juntando duas tabelas: merge
Raramente tudo está numa tabela só. Um arquivo tem os pedidos; outro, os dados dos clientes. O merge combina os dois por uma coluna em comum (a "chave") — a mesma ideia do PROCV do Excel, e a base da modelagem dimensional que veremos adiante.
Digite comigo
pedidos = pd.DataFrame({
"id_cliente": [1, 2, 1, 3],
"valor": [100, 200, 50, 80],
})
clientes = pd.DataFrame({
"id_cliente": [1, 2, 3],
"nome": ["Ana", "Bruno", "Carla"],
"cidade": ["SP", "RJ", "MG"],
})
completo = pedidos.merge(clientes, on="id_cliente")
print(completo)
Saída no console
id_cliente valor nome cidade
0 1 100 Ana SP
1 1 50 Ana SP
2 2 200 Bruno RJ
3 3 80 Carla MG
Depois do merge, cada pedido carrega o nome e a cidade do cliente — e podemos agrupar por cidade, mesmo que essa informação estivesse em outra tabela.
11. Dados reais: a base Iris
Até aqui usamos tabelas pequenas escritas à mão. Vamos abrir um arquivo de verdade. A base Iris é uma das menores e mais conhecidas do mundo dos dados: apenas 150 linhas, cada uma descrevendo uma flor de íris. Foi organizada pelo estatístico Ronald Fisher em 1936 e desde então virou o "olá, mundo" da análise de dados — justamente por ser pequena, limpa e fácil de entender.
Cada flor pertence a uma de três espécies (setosa, versicolor e virginica), com 50 flores de cada. Para cada flor foram medidas quatro características, todas em centímetros. Duas dizem respeito à pétala (a parte colorida e chamativa da flor) e duas à sépala (a estrutura, em geral esverdeada, que protege o botão antes de a flor abrir e a sustenta por baixo). A ideia por trás da base é simples e poderosa: será que dá para descobrir a espécie de uma flor apenas olhando o tamanho das suas pétalas e sépalas?
O que é cada campo (dicionário de dados)
Antes de analisar qualquer tabela, vale montar seu dicionário de dados: uma lista que diz o que cada coluna significa. Para a base Iris, fica assim.
| Coluna | Significado |
|---|---|
| Id | Número que identifica cada flor (a linha da tabela). |
| SepalLengthCm | Comprimento da sépala, em centímetros. |
| SepalWidthCm | Largura da sépala, em centímetros. |
| PetalLengthCm | Comprimento da pétala, em centímetros. |
| PetalWidthCm | Largura da pétala, em centímetros. |
| Species | Espécie da flor: Iris-setosa, Iris-versicolor ou Iris-virginica. |
Baixe a base em kaggle.com/datasets/uciml/iris (arquivo Iris.csv) e salve-a na mesma pasta do seu script. Depois, carregue e dê uma primeira olhada.
Digite comigo
import pandas as pd
df = pd.read_csv("Iris.csv")
print(df.shape) # (150, 6): 150 linhas e 6 colunas
df.head()
Saída no console
Id SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species
0 1 5.1 3.5 1.4 0.2 Iris-setosa
1 2 4.9 3.0 1.4 0.2 Iris-setosa
2 3 4.7 3.2 1.3 0.2 Iris-setosa
3 4 4.6 3.1 1.5 0.2 Iris-setosa
4 5 5.0 3.6 1.4 0.2 Iris-setosa
Quantas flores de cada espécie existem? O value_counts() responde na hora.
Digite comigo
df["Species"].value_counts()
Saída no console
Species
Iris-setosa 50
Iris-versicolor 50
Iris-virginica 50
Name: count, dtype: int64
Agora a pergunta interessante: o tamanho da pétala muda conforme a espécie? Um groupby resolve — comprimento médio da pétala por espécie.
Digite comigo
df.groupby("Species")["PetalLengthCm"].mean().round(3)
Saída no console
Species
Iris-setosa 1.462
Iris-versicolor 4.260
Iris-virginica 5.552
Name: PetalLengthCm, dtype: float64
Repare como o comprimento médio da pétala separa bem as espécies: a setosa tem pétalas muito menores que as demais. Uma única linha de groupby já revelou um padrão real dos dados — exatamente o tipo de leitura que faremos o semestre inteiro, só que com bases maiores e brasileiras.
12. Mini-análise e gráficos
Juntando tudo: uma mini-análise
Vamos encadear o que aprendemos numa situação realista. Uma tabela de pedidos chega suja: tem uma linha duplicada, um valor no formato brasileiro e um valor inválido. A pergunta de negócio é simples — qual o nosso ticket médio? — mas a resposta muda completamente se ignorarmos a sujeira.
Digite comigo
import pandas as pd
dados = {
"pedido": [1, 2, 2, 3, 4],
"valor": ["100,00", "250,50", "250,50", "abc", "80,00"],
}
df = pd.DataFrame(dados)
print(df["valor"].dtype) # object -> é TEXTO, não dá para tirar média ainda
Agora limpamos, passo a passo: remover a duplicata, converter o formato brasileiro, descartar o valor inválido — e só então calcular.
Digite comigo
df = df.drop_duplicates(subset="pedido") # tira o pedido 2 repetido
df["valor"] = df["valor"].str.replace(",", ".", regex=False)
df["valor"] = pd.to_numeric(df["valor"], errors="coerce") # "abc" -> NaN
df = df.dropna(subset=["valor"]) # descarta o inválido
print(f"Pedidos válidos: {len(df)}")
print(f"Ticket médio: R$ {df['valor'].mean():.2f}")
Saída no console
Pedidos válidos: 3
Ticket médio: R$ 143.50
O momento "por que isso importa"
Sem limpar, essa tabela não permitiria sequer calcular a média (a coluna era texto). Uma tentativa apressada de "consertar na marra" contaria o pedido duplicado e distorceria o resultado. O mesmo dado, tratado com cuidado, dá uma resposta em que dá para confiar — e é disso que trata toda a disciplina.
Transformando números em gráficos
Uma tabela com centenas de linhas não conta uma história — um gráfico conta. O pandas se integra ao matplotlib, então dá para plotar direto do DataFrame.
Digite comigo
import matplotlib.pyplot as plt
total_cidade = df.groupby("cidade")["valor"].sum()
total_cidade.sort_values().plot(kind="barh") # barras horizontais
plt.title("Total vendido por cidade")
plt.xlabel("Valor (R$)")
plt.show()
13. Boas práticas e cola rápida
Você percorreu o caminho completo: do primeiro print() até juntar tabelas, agrupar e plotar. Não precisa decorar nada — a fluência vem com a prática. Alguns hábitos ajudam desde já:
- Execute em pequenos passos. Escreva uma ou duas linhas, execute, confira, e só então continue. Não escreva vinte linhas para descobrir o erro no fim.
- Leia a última linha do erro. Ela quase sempre diz o tipo do problema.
- Olhe os dados antes de analisá-los.
.head(),.info()e.describe()logo no início evitam conclusões erradas depois. - Nomeie bem as variáveis.
ticket_mediodiz muito mais quex.
Comandos que você mais vai usar
| Comando | O que faz |
|---|---|
print(x) |
mostra x na tela |
type(x) |
informa o tipo de x |
len(col) |
tamanho de uma coleção |
f"...{v:.2f}" |
texto com número (2 casas) |
[x for x in lista if ...] |
compreensão de lista com filtro |
import pandas as pd |
carrega o pandas |
pd.read_csv("a.csv", sep=";", decimal=",") |
lê CSV brasileiro |
df.head() / df.info() |
primeiras linhas / diagnóstico |
df["col"].value_counts() |
distribuição de uma coluna |
df.loc[lin, col] / df.iloc[i, j] |
selecionar por rótulo / posição |
df[df["v"] > 100] |
filtrar linhas por condição |
pd.to_numeric(s, errors="coerce") |
texto para número (sem quebrar) |
df.isnull().sum() |
conta valores ausentes |
df.drop_duplicates() |
remove duplicatas |
df["c"].apply(lambda v: ...) |
transforma cada valor |
df.groupby("c").agg(...) |
resumo por grupo |
a.merge(b, on="chave") |
junta duas tabelas |
pd.to_datetime(df["data"]) |
texto para data |
df.sort_values("v").head(3) |
ranking Top-N |
14. Exercícios
Resolva no VS Code, executando aos poucos. Não precisa acertar de primeira — o objetivo é praticar. Consulte a resposta só depois de tentar. Muitas vezes há mais de um caminho correto — se o seu resultado bate, está valendo.
Fundamentos
1. Crie variáveis nome (seu nome), idade e cidade e imprima uma frase usando uma f-string: "Meu nome é X, tenho Y anos e moro em Z."
2. Um produto custa R$ 49,90 e você compra 3 unidades. Calcule e imprima o total com duas casas decimais.
3. Dado n = 47, use o operador % para descobrir e imprimir se n é par ou ímpar (dica: um número é par quando n % 2 == 0).
Ver resposta
# 1
nome, idade, cidade = "Ana", 20, "Recife"
print(f"Meu nome é {nome}, tenho {idade} anos e moro em {cidade}.")
# 2
total = 49.90 * 3
print(f"R$ {total:.2f}") # R$ 149.70
# 3
n = 47
if n % 2 == 0:
print("par")
else:
print("ímpar") # ímpar
Texto
4. A partir de email = " Joao.SILVA@Email.COM ", produza a versão limpa "joao.silva@email.com" (sem espaços nas pontas e tudo minúsculo).
5. Dado cpf = "123.456.789-00", imprima apenas os números (sem pontos e traço).
6. Converta o texto "R$ 2.500,75" para o número 2500.75 e some 100 a ele.
Ver resposta
# 4
email = " Joao.SILVA@Email.COM "
print(email.strip().lower()) # joao.silva@email.com
# 5
cpf = "123.456.789-00"
print(cpf.replace(".", "").replace("-", "")) # 12345678900
# 6
txt = "R$ 2.500,75"
limpo = txt.replace("Rquot;, "").replace(".", "").replace(",", ".").strip()
print(float(limpo) + 100) # 2600.75
Coleções e laços
7. Dada precos = [30, 45, 12, 88, 60], imprima a soma, o maior e o menor valor.
8. Usando compreensão de lista, crie a lista dos preços acima de 40 a partir de precos.
9. Dada ufs = ["SP", "RJ", "SP", "MG", "RJ", "SP"], descubra quantas UFs distintas existem.
10. Percorra precos com um laço for e conte quantos valores são maiores que 50.
Ver resposta
precos = [30, 45, 12, 88, 60]
# 7
print(sum(precos), max(precos), min(precos)) # 235 88 12
# 8
print([p for p in precos if p > 40]) # [45, 88, 60]
# 9
ufs = ["SP", "RJ", "SP", "MG", "RJ", "SP"]
print(len(set(ufs))) # 3
# 10
qtd = 0
for p in precos:
if p > 50:
qtd = qtd + 1
print(qtd) # 2
Decisões e funções
11. Escreva uma função classifica(valor) que devolve "alto" se valor >= 200, "medio" se valor >= 100, e "baixo" caso contrário. Teste com 250, 150 e 30.
12. Escreva uma função email_valido(email) que devolve True se o texto contém "@" e ".", e False caso contrário.
13. Usando try / except, percorra ["10", "x", "25"] e imprima o dobro de cada valor que for um número válido, ignorando os demais.
Ver resposta
# 11
def classifica(valor):
if valor >= 200:
return "alto"
elif valor >= 100:
return "medio"
else:
return "baixo"
print(classifica(250), classifica(150), classifica(30)) # alto medio baixo
# 12
def email_valido(email):
return "@" in email and "." in email
print(email_valido("a@b.com"), email_valido("erro")) # True False
# 13
for v in ["10", "x", "25"]:
try:
print(int(v) * 2) # 20 e 50 (o "x" é ignorado)
except ValueError:
pass
pandas — básico
Use este DataFrame como ponto de partida:
Digite comigo
import pandas as pd
df = pd.DataFrame({
"cliente": ["Ana", "Bruno", "Carla", "Diego", "Ana"],
"cidade": ["SP", "RJ", "SP", "MG", "SP"],
"valor": [120.0, 80.0, 200.0, 50.0, 120.0],
})
14. Mostre as primeiras linhas e descubra quantas linhas e colunas o DataFrame tem.
15. Quantas vezes cada cidade aparece na coluna cidade?
16. Filtre e mostre apenas os clientes com valor acima de 100.
17. Remova as linhas duplicadas e diga quantas linhas sobraram.
Ver resposta
# 14
df.head()
print(df.shape) # (5, 3)
# 15
print(df["cidade"].value_counts()) # SP 3, RJ 1, MG 1
# 16
print(df[df["valor"] > 100]) # Ana, Carla, Ana
# 17
limpo = df.drop_duplicates()
print(len(limpo)) # 4 (uma linha "Ana/SP/120" era repetida)
pandas — análise
18. Calcule o ticket médio (média de valor) por cidade.
19. Crie uma coluna faixa com "alto" para valor >= 100 e "baixo" caso contrário (use apply com lambda).
20. Mostre as 2 maiores compras (Top-2 por valor).
21. Junte (merge) o DataFrame de pedidos abaixo com o de clientes pela coluna id_cliente e calcule o total gasto por cliente:
Digite comigo
pedidos = pd.DataFrame({"id_cliente": [1, 2, 1, 3],
"valor": [100, 200, 50, 80]})
clientes = pd.DataFrame({"id_cliente": [1, 2, 3],
"nome": ["Ana", "Bruno", "Carla"]})
Ver resposta
# 18
print(df.groupby("cidade")["valor"].mean())
# 19
df["faixa"] = df["valor"].apply(lambda v: "alto" if v >= 100 else "baixo")
# 20
print(df.sort_values("valor", ascending=False).head(2))
# 21
completo = pedidos.merge(clientes, on="id_cliente")
print(completo.groupby("nome")["valor"].sum()) # Ana 150, Bruno 200, Carla 80
Desafio
Análise ponta a ponta. Você recebe a tabela suja abaixo. Faça a limpeza completa (remover duplicata por pedido, converter o valor do formato brasileiro, descartar valores inválidos) e responda: quantos pedidos válidos restaram e qual o ticket médio?
Digite comigo
sujo = pd.DataFrame({
"pedido": [1, 2, 2, 3, 4, 5],
"valor": ["199,90", "50,00", "50,00", "??", "300,00", "80,50"],
})
Ver resposta
sujo = sujo.drop_duplicates(subset="pedido")
sujo["valor"] = sujo["valor"].str.replace(",", ".", regex=False)
sujo["valor"] = pd.to_numeric(sujo["valor"], errors="coerce")
sujo = sujo.dropna(subset=["valor"])
print(f"Pedidos válidos: {len(sujo)}") # 4
print(f"Ticket médio: R$ {sujo['valor'].mean():.2f}") # R$ 157.60
Bons estudos — e não esqueça: a melhor forma de aprender é digitando.