Capítulo 1, Ambiente
Por que o Pandas existe
Listas funcionam até o dia em que os dados ficam reais: faltam valores, aparecem repetições, as colunas misturam tipos. O Pandas existe para esse dia.
O problema das listas paralelas
Você já sabe guardar dados em listas do Python. Aqui estão três funcionários do jeito "das listas": uma lista para cada atributo, e a esperança de que elas nunca saiam de sincronia.
nomes = ["Ana", "Bruno", "Carla"]
idades = [24, 28, 26]
salarios = [4500, 6500, 5000]
print(sum(salarios) / len(salarios))
print([n for n, s in zip(nomes, salarios) if s > 4800])
5333.333333333333
['Bruno', 'Carla']
Funciona para três pessoas. Agora imagine 500 funcionários, com salários faltando, entradas repetidas e datas misturadas. O primeiro obstáculo é o dado ausente, que as listas não sabem representar:
salarios_com_falta = [4500, None, 5000]
try:
sum(salarios_com_falta)
except TypeError as erro:
print("TypeError:", erro)
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
Cada filtro exige um laço escrito à mão, agrupar "por departamento" exige a sua própria lógica de baldes, e nada disso escala além de um exemplo de brinquedo. O Pandas oferece estruturas de dados rápidas e flexíveis para dados estruturados do mundo real, com a mesma relação que uma planilha tem com uma folha de papel com números anotados.
Os mesmos dados no Pandas
import pandas as pd
df = pd.DataFrame({"nome": nomes, "idade": idades, "salario": salarios_com_falta})
print(df)
print(df["salario"].mean())
print(df[df["salario"] > 4800])
nome idade salario
0 Ana 24 4500.0
1 Bruno 28 NaN
2 Carla 26 5000.0
4750.0
nome idade salario
2 Carla 26 5000.0
Três coisas aconteceram sem esforço: o dado ausente virou NaN e foi ignorado na média (4750 é a média de 4500 e 5000), as colunas ficaram alinhadas (uma linha é um funcionário completo), e o filtro virou uma expressão, e não um laço.
Do NumPy ao Pandas
O Pandas é construído em cima do NumPy. O que ele acrescenta é, em essência, o que uma planilha tem e um array não tem:
| O NumPy dá | O Pandas acrescenta |
|---|---|
| Arrays numéricos rápidos | Rótulos nas linhas e nas colunas |
| Operações vetorizadas | Tipos diferentes por coluna |
| Layout de memória eficiente | Tratamento de dados ausentes, limpeza e agrupamento |
| Lista | Array NumPy | DataFrame | |
|---|---|---|---|
| Estrutura | Contêiner simples | Grade de formato fixo, um só tipo | Tabela rotulada, linhas e colunas |
| Tipos | Qualquer mistura | Um tipo para o array inteiro | Um tipo por coluna |
| Rótulos | Nenhum, só a posição | Nenhum, só a posição | Índice das linhas e nomes das colunas |
| Dados ausentes | Sem conceito | Difícil de representar | NaN nativo |
| Filtragem | Laços manuais | Indexação booleana | Indexação booleana e query() |
| Serve para | Dados pequenos e simples | Dados numéricos e uniformes | Planilhas e CSVs reais |
A anatomia: `Series` e `DataFrame`
Uma Series é uma coluna rotulada. Um DataFrame é uma tabela feita de colunas que compartilham o mesmo índice. As peças têm nome:
print(df.index)
print(df.columns.tolist())
print(df.loc[1])
print(df["salario"])
print(df.at[0, "nome"])
RangeIndex(start=0, stop=3, step=1)
['nome', 'idade', 'salario']
nome Bruno
idade 28
salario NaN
Name: 1, dtype: object
0 4500.0
1 NaN
2 5000.0
Name: salario, dtype: float64
Ana
| Peça | O que é | No exemplo |
|---|---|---|
| Índice | O rótulo de cada linha (por padrão, 0, 1, 2...) | df.index |
| Nomes das colunas | O rótulo de cada campo | df.columns |
| Linha | Um registro completo | df.loc[1] |
| Coluna | Um campo em todos os registros, e é uma Series | df["salario"] |
| Célula | Um valor, no cruzamento de uma linha e uma coluna | df.at[0, "nome"] |
E o NumPy continua ali por baixo. Qualquer coluna numérica entrega o seu array:
valores = df["salario"].to_numpy()
print(type(valores).__name__, valores.dtype)
ndarray float64
Repare no tipo float64: a coluna de salários era de inteiros, mas o dado ausente (NaN) só existe como decimal, e o Pandas promoveu a coluna inteira. O capítulo 3 explica essa regra.
O erro de quem vem do NumPy
Tratar um
DataFramecomo "um array do NumPy com passos extras". Os velhos hábitos atrapalham: confiar na posição em vez do rótulo, e esquecer que uma coluna pode guardar texto ao lado de uma coluna de números. O Pandas alinha por rótulo, e o capítulo 4 mostra o que isso muda.
Quando o Pandas não é a resposta
O Pandas carrega os dados na memória, inteiros. Para conjuntos que passam do tamanho da memória, as saídas são processar em partes (capítulo 32) ou usar ferramentas pensadas para isso, como o DuckDB ou o Polars, que este curso não cobre. Para cálculos puramente numéricos sobre uma grade uniforme, o NumPy continua sendo mais simples e mais rápido.
Exercício 1
Lista ou DataFrame?
Escreva contar_acima(nomes, valores, limite), que devolva quantos valores passam do limite ignorando os ausentes (None), primeiro com uma lista, e depois a mesma conta com uma Series. Confira que as duas dão o mesmo resultado.