Voltar ao Glossario
Glossario Python

Pandas: O que É e Como Funciona | Python Brasil

Pandas é a biblioteca Python para análise de dados. Aprenda DataFrame, Series, groupby, merge, tratamento de dados faltantes e muito mais.

O que é Pandas?

O Pandas é a biblioteca mais popular do Python para análise e manipulação de dados. Criada por Wes McKinney em 2008, ela oferece estruturas de dados poderosas e ferramentas para trabalhar com dados tabulares de forma rápida e intuitiva.

O nome vem de “Panel Data”, um termo de econometria para conjuntos de dados multidimensionais. Hoje, o Pandas é indispensável para qualquer cientista de dados, analista ou engenheiro que trabalha com Python.

Series: a estrutura unidimensional

A Series é o bloco fundamental do Pandas: um array unidimensional com rótulos (índice) em cada elemento.

import pandas as pd
import numpy as np

# Criando Series de diferentes formas
s1 = pd.Series([10, 20, 30, 40])           # Indice automático (0, 1, 2, 3)
s2 = pd.Series([10, 20, 30], index=['a', 'b', 'c'])  # Indice personalizado
s3 = pd.Series({'jan': 100, 'fev': 200, 'mar': 150}) # A partir de dicionário

# Operações em Series
print(s2 * 2)         # Multiplica todos os valores por 2
print(s2[s2 > 15])    # Filtragem booleana
print(s2.mean())      # Média: 20.0
print(s2.describe())  # Estatísticas descritivas

# Alinhamento automático por índice
a = pd.Series({'x': 1, 'y': 2, 'z': 3})
b = pd.Series({'x': 10, 'y': 20, 'w': 30})
print(a + b)
# w     NaN
# x    11.0
# y    22.0
# z     NaN
# 'z' e 'w' não têm par, resultam em NaN

Criando DataFrames de diferentes fontes

import pandas as pd

# A partir de dicionário (forma mais comum)
df_dict = pd.DataFrame({
    'nome': ['Ana', 'Bruno', 'Carla'],
    'idade': [28, 34, 25],
    'salario': [8500.0, 12000.0, 7200.0]
})

# A partir de lista de dicionários
registros = [
    {'produto': 'Notebook', 'preco': 3500, 'estoque': 10},
    {'produto': 'Mouse', 'preco': 80, 'estoque': 150},
    {'produto': 'Teclado', 'preco': 200, 'estoque': 75},
]
df_lista = pd.DataFrame(registros)

# Lendo arquivos externos
# df_csv   = pd.read_csv('dados.csv', encoding='utf-8', sep=';')
# df_excel = pd.read_excel('planilha.xlsx', sheet_name='Vendas')
# df_json  = pd.read_json('dados.json')
# df_sql   = pd.read_sql('SELECT * FROM clientes', conexao)

# Inspecionando o DataFrame
print(df_dict.head(3))     # Primeiras 3 linhas
print(df_dict.tail(2))     # Últimas 2 linhas
print(df_dict.info())      # Tipos de dados e valores não-nulos
print(df_dict.describe())  # Estatísticas descritivas das colunas numéricas
print(df_dict.shape)       # (3, 3) - linhas e colunas
print(df_dict.dtypes)      # Tipo de cada coluna

Indexação: loc, iloc, at e iat

O Pandas oferece quatro formas principais de indexar dados, cada uma com seu propósito:

import pandas as pd

df = pd.DataFrame({
    'nome': ['Ana', 'Bruno', 'Carla', 'Diego'],
    'idade': [28, 34, 25, 31],
    'cidade': ['SP', 'RJ', 'PR', 'MG'],
    'salario': [8500, 12000, 7200, 9800]
}, index=['a', 'b', 'c', 'd'])

# loc: indexação por RÓTULO (label-based)
print(df.loc['a'])                     # Linha com rótulo 'a'
print(df.loc['a', 'nome'])             # Valor específico: 'Ana'
print(df.loc['a':'c', 'nome':'idade']) # Intervalo de rótulos
print(df.loc[df['idade'] > 30])        # Filtragem booleana com loc

# iloc: indexação por POSIÇÃO (position-based)
print(df.iloc[0])          # Primeira linha (posição 0)
print(df.iloc[0, 1])       # Linha 0, coluna 1 → 28
print(df.iloc[1:3, 0:2])   # Linhas 1-2, colunas 0-1

# at e iat: acesso ESCALAR (mais rápido para valores únicos)
print(df.at['a', 'nome'])    # 'Ana'   - por rótulo
print(df.iat[0, 0])          # 'Ana'   - por posição

# Adicionando e removendo colunas
df['bonus'] = df['salario'] * 0.1              # Nova coluna calculada
df.drop(columns=['bonus'], inplace=True)       # Remove coluna
df_sem_linha = df.drop(index=['a'])            # Remove linha

GroupBy com múltiplas agregações

import pandas as pd
import numpy as np

dados = pd.DataFrame({
    'departamento': ['TI', 'TI', 'RH', 'RH', 'TI', 'RH'],
    'cargo': ['Jr', 'Sr', 'Jr', 'Sr', 'Pl', 'Pl'],
    'salario': [4000, 9000, 3500, 7000, 6500, 5500],
    'anos_empresa': [1, 5, 2, 8, 3, 4]
})

# Agregação simples
media_dept = dados.groupby('departamento')['salario'].mean()

# Múltiplas agregações com agg
resumo = dados.groupby('departamento').agg(
    salario_medio=('salario', 'mean'),
    salario_maximo=('salario', 'max'),
    salario_minimo=('salario', 'min'),
    total_funcionarios=('salario', 'count'),
    media_anos=('anos_empresa', 'mean')
)
print(resumo)

# Agrupamento por múltiplas colunas
por_dept_cargo = dados.groupby(['departamento', 'cargo'])['salario'].mean()

# transform: mantém o mesmo tamanho do DataFrame original
dados['media_dept'] = dados.groupby('departamento')['salario'].transform('mean')
dados['acima_media'] = dados['salario'] > dados['media_dept']
print(dados)

Merge, Join e Concat

import pandas as pd

clientes = pd.DataFrame({
    'id': [1, 2, 3, 4],
    'nome': ['Ana', 'Bruno', 'Carla', 'Diego']
})

pedidos = pd.DataFrame({
    'pedido_id': [101, 102, 103, 104],
    'cliente_id': [1, 2, 1, 5],     # cliente 5 não existe em clientes
    'valor': [150.0, 89.0, 220.0, 75.0]
})

# INNER JOIN: apenas registros com correspondência em ambos os lados
inner = pd.merge(clientes, pedidos,
                 left_on='id', right_on='cliente_id', how='inner')

# LEFT JOIN: todos os clientes, mesmo sem pedidos
left = pd.merge(clientes, pedidos,
                left_on='id', right_on='cliente_id', how='left')

# OUTER JOIN: todos os registros de ambas as tabelas
outer = pd.merge(clientes, pedidos,
                 left_on='id', right_on='cliente_id', how='outer')

print(f"Inner: {len(inner)} linhas")  # 3 linhas
print(f"Left:  {len(left)} linhas")   # 4 linhas (todos os clientes)

# concat: empilhar DataFrames
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})
empilhado = pd.concat([df1, df2], ignore_index=True)      # Empilha linhas
lado_a_lado = pd.concat([df1, df2], axis=1)                # Lado a lado

Tratamento de dados faltantes

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'A': [1, np.nan, 3, np.nan, 5],
    'B': [np.nan, 2, 3, 4, 5],
    'C': [1, 2, np.nan, 4, 5]
})

# Detectando valores faltantes
print(df.isnull().sum())          # Contagem de NaN por coluna
print(df.isnull().sum().sum())    # Total de NaN no DataFrame
print(df.notna().all())           # Colunas sem nenhum NaN

# dropna: removendo linhas ou colunas com NaN
df_sem_nan = df.dropna()                    # Remove qualquer linha com NaN
df_limpo = df.dropna(thresh=2)             # Mantém linhas com ao menos 2 valores válidos
df_cols = df.dropna(axis=1)               # Remove colunas com NaN

# fillna: preenchendo valores faltantes
df_zero = df.fillna(0)                      # Substitui por zero
df_media = df.fillna(df.mean())             # Substitui pela média da coluna
df_forward = df.fillna(method='ffill')      # Propaga o último valor válido
df_backward = df.fillna(method='bfill')     # Propaga o próximo valor válido

# interpolate: interpolação entre valores
df_interp = df.interpolate(method='linear') # Interpolação linear entre vizinhos

# Combinando estratégias por coluna
df['A'] = df['A'].fillna(df['A'].median())
df['B'] = df['B'].interpolate()
df['C'] = df['C'].fillna('desconhecido')

Apply, Map e Replace

import pandas as pd

df = pd.DataFrame({
    'nome': ['ana silva', 'BRUNO COSTA', 'Carla Souza'],
    'salario': [8500, 12000, 7200],
    'nota': [7.5, 9.2, 6.8]
})

# apply: aplica uma função a linhas ou colunas
df['nome_formatado'] = df['nome'].apply(str.title)
df['salario_com_bonus'] = df['salario'].apply(lambda x: x * 1.1 if x < 10000 else x)

# apply em linhas inteiras (axis=1)
def classificar_nota(row):
    if row['nota'] >= 7:
        return 'Aprovado'
    return 'Reprovado'

df['status'] = df.apply(classificar_nota, axis=1)

# map: substitui valores em uma Series (útil para mapeamentos)
mapa_nivel = {8500: 'Junior', 12000: 'Senior', 7200: 'Junior'}
df['nivel'] = df['salario'].map(mapa_nivel)

# replace: substitui valores específicos
df['status'] = df['status'].replace({'Aprovado': 1, 'Reprovado': 0})

print(df)

Pandas vs SQL: comparação de operações

SQLPandas
SELECT col FROM tb WHERE col > 5df.loc[df['col'] > 5, 'col']
GROUP BY col HAVING COUNT(*) > 2df.groupby('col').filter(lambda x: len(x) > 2)
ORDER BY col DESCdf.sort_values('col', ascending=False)
JOINpd.merge(df1, df2, on='col')
LIMIT 10df.head(10)

Dicas de performance

import pandas as pd
import numpy as np

# 1. Use dtype 'category' para colunas com poucos valores únicos
df = pd.DataFrame({'status': ['ativo', 'inativo', 'ativo'] * 10000})
df['status'] = df['status'].astype('category')  # Reduz memória drasticamente

# 2. query() é mais legível e às vezes mais rápido que filtragem booleana
df_filtrado = df.query("status == 'ativo'")

# 3. Evite loops - use vetorização
# RUIM:
# for i, row in df.iterrows():
#     df.at[i, 'nova_col'] = row['valor'] * 2

# BOM:
df['nova_col'] = df['valor'] * 2  # Se 'valor' existir

# 4. Especifique dtypes ao ler CSV para reduzir uso de memória
# df = pd.read_csv('dados.csv', dtype={'id': 'int32', 'status': 'category'})

Quando usar Pandas

Use Pandas quando precisar explorar e analisar dados tabulares interativamente, ler e escrever dados em formatos como CSV, Excel ou SQL, realizar operacoes de limpeza e transformacao de dados, ou preparar dados para modelos de machine learning. Para grandes volumes de dados que nao cabem na memoria RAM, considere alternativas como Polars, Dask ou PySpark.

Termos Relacionados