Pandas: referência¶
Comandos e padrões para manipulação de dados com Pandas.
Instalação¶
Importação¶
Criando DataFrames¶
# De dicionário
df = pd.DataFrame({
'nome': ['Ana', 'Bruno', 'Carla'],
'idade': [25, 30, 28],
'cidade': ['SP', 'RJ', 'BH']
})
# De lista de dicionários
df = pd.DataFrame([
{'nome': 'Ana', 'idade': 25},
{'nome': 'Bruno', 'idade': 30}
])
# De NumPy array
df = pd.DataFrame(
np.random.randn(5, 3),
columns=['A', 'B', 'C']
)
Lendo dados¶
# CSV
df = pd.read_csv('dados.csv')
df = pd.read_csv('dados.csv', sep=';', encoding='utf-8')
df = pd.read_csv('dados.csv', usecols=['col1', 'col2'])
df = pd.read_csv('dados.csv', dtype={'id': str})
df = pd.read_csv('dados.csv', parse_dates=['data'])
df = pd.read_csv('dados.csv', nrows=1000) # primeiras N linhas
# Excel
df = pd.read_excel('dados.xlsx', sheet_name='Plan1')
# JSON
df = pd.read_json('dados.json')
df = pd.read_json('dados.json', lines=True) # JSON Lines
# Parquet
df = pd.read_parquet('dados.parquet')
# SQL
from sqlalchemy import create_engine
engine = create_engine('postgresql://user:pass@host/db')
df = pd.read_sql('SELECT * FROM tabela', engine)
df = pd.read_sql_table('tabela', engine)
Salvando dados¶
# CSV
df.to_csv('saida.csv', index=False)
df.to_csv('saida.csv', index=False, sep=';')
# Excel
df.to_excel('saida.xlsx', index=False, sheet_name='Dados')
# JSON
df.to_json('saida.json', orient='records')
df.to_json('saida.json', orient='records', lines=True)
# Parquet
df.to_parquet('saida.parquet', index=False)
# SQL
df.to_sql('tabela', engine, if_exists='replace', index=False)
df.to_sql('tabela', engine, if_exists='append', index=False)
Explorando dados¶
df.head() # primeiras 5 linhas
df.head(10) # primeiras 10 linhas
df.tail() # últimas 5 linhas
df.sample(5) # 5 linhas aleatórias
df.shape # (linhas, colunas)
df.columns # nomes das colunas
df.dtypes # tipos de cada coluna
df.info() # resumo (tipos, não-nulos, memória)
df.describe() # estatísticas numéricas
df['coluna'].value_counts() # contagem de valores
df['coluna'].value_counts(normalize=True) # percentual
df['coluna'].unique() # valores únicos
df['coluna'].nunique() # quantidade de únicos
df.isnull().sum() # nulos por coluna
df.duplicated().sum() # linhas duplicadas
Selecionando dados¶
# Colunas
df['coluna'] # Series
df[['col1', 'col2']] # DataFrame
# Linhas por índice
df.loc[0] # linha pelo label
df.loc[0:5] # linhas 0 a 5 (inclusive)
df.loc[0:5, 'coluna'] # linhas e coluna
df.iloc[0] # linha pela posição
df.iloc[0:5] # linhas 0 a 4
df.iloc[0:5, 0:2] # linhas e colunas por posição
# Filtros
df[df['idade'] > 25]
df[df['cidade'] == 'SP']
df[df['cidade'].isin(['SP', 'RJ'])]
df[df['nome'].str.contains('Ana')]
df[df['coluna'].notna()]
df[(df['idade'] > 25) & (df['cidade'] == 'SP')]
df[(df['idade'] > 25) | (df['cidade'] == 'RJ')]
# Query (mais legível)
df.query('idade > 25')
df.query('cidade == "SP"')
df.query('idade > 25 and cidade == "SP"')
Modificando dados¶
# Adicionar coluna
df['nova'] = df['col1'] + df['col2']
df['constante'] = 100
df['calculada'] = df['valor'] * 1.1
# Renomear colunas
df.rename(columns={'old': 'new'}, inplace=True)
df.columns = ['col1', 'col2', 'col3']
# Alterar tipos
df['coluna'] = df['coluna'].astype(int)
df['coluna'] = df['coluna'].astype(str)
df['data'] = pd.to_datetime(df['data'])
df['categoria'] = df['categoria'].astype('category')
# Alterar valores
df.loc[df['idade'] > 30, 'categoria'] = 'senior'
df['coluna'] = df['coluna'].replace({'old': 'new'})
df['coluna'] = df['coluna'].str.upper()
df['coluna'] = df['coluna'].str.strip()
# Apply
df['nova'] = df['coluna'].apply(lambda x: x * 2)
df['nova'] = df.apply(lambda row: row['a'] + row['b'], axis=1)
# Map
df['status_texto'] = df['status'].map({0: 'Inativo', 1: 'Ativo'})
# Dropar colunas
df.drop(columns=['col1', 'col2'], inplace=True)
# Dropar linhas
df.drop(index=[0, 1, 2], inplace=True)
Lidando com nulos¶
# Verificar
df.isnull()
df.isnull().sum()
df.isnull().any()
# Remover
df.dropna() # remove linhas com qualquer nulo
df.dropna(subset=['col1']) # remove se col1 for nulo
df.dropna(how='all') # remove se todas forem nulas
# Preencher
df.fillna(0)
df.fillna({'col1': 0, 'col2': 'N/A'})
df['coluna'].fillna(df['coluna'].mean())
df['coluna'].fillna(method='ffill') # forward fill
df['coluna'].fillna(method='bfill') # backward fill
Duplicados¶
# Verificar
df.duplicated()
df.duplicated(subset=['col1', 'col2'])
# Remover
df.drop_duplicates()
df.drop_duplicates(subset=['col1'], keep='first')
df.drop_duplicates(subset=['col1'], keep='last')
Ordenação¶
df.sort_values('coluna')
df.sort_values('coluna', ascending=False)
df.sort_values(['col1', 'col2'], ascending=[True, False])
df.sort_index()
# Rank
df['rank'] = df['valor'].rank()
df['rank'] = df['valor'].rank(ascending=False)
df['rank'] = df.groupby('grupo')['valor'].rank()
Agregações¶
# Básicas
df['coluna'].sum()
df['coluna'].mean()
df['coluna'].median()
df['coluna'].min()
df['coluna'].max()
df['coluna'].std()
df['coluna'].count()
# Múltiplas
df.agg({'col1': 'sum', 'col2': 'mean'})
df.agg({'col1': ['sum', 'mean'], 'col2': 'count'})
GroupBy¶
# Básico
df.groupby('categoria')['valor'].sum()
df.groupby('categoria')['valor'].mean()
df.groupby('categoria').size()
# Múltiplas colunas
df.groupby(['cat1', 'cat2'])['valor'].sum()
# Múltiplas agregações
df.groupby('categoria').agg({
'valor': 'sum',
'quantidade': 'mean',
'id': 'count'
})
# Named aggregations
df.groupby('categoria').agg(
total=('valor', 'sum'),
media=('valor', 'mean'),
contagem=('id', 'count')
)
# Transform (mantém shape original)
df['valor_normalizado'] = df.groupby('grupo')['valor'].transform(
lambda x: (x - x.mean()) / x.std()
)
# Filter
df.groupby('grupo').filter(lambda x: x['valor'].sum() > 1000)
Pivot e Melt¶
# Pivot (long to wide)
df.pivot(index='data', columns='produto', values='vendas')
# Pivot table (com agregação)
df.pivot_table(
index='data',
columns='produto',
values='vendas',
aggfunc='sum',
fill_value=0
)
# Melt (wide to long)
df.melt(
id_vars=['data'],
value_vars=['prod_a', 'prod_b'],
var_name='produto',
value_name='vendas'
)
Joins¶
# Merge (SQL-like)
pd.merge(df1, df2, on='id')
pd.merge(df1, df2, on='id', how='left')
pd.merge(df1, df2, on='id', how='right')
pd.merge(df1, df2, on='id', how='outer')
pd.merge(df1, df2, left_on='id1', right_on='id2')
pd.merge(df1, df2, on=['col1', 'col2'])
# Join (por índice)
df1.join(df2, how='left')
# Concat
pd.concat([df1, df2]) # empilhar linhas
pd.concat([df1, df2], ignore_index=True)
pd.concat([df1, df2], axis=1) # lado a lado
Datas¶
# Converter
df['data'] = pd.to_datetime(df['data'])
df['data'] = pd.to_datetime(df['data'], format='%d/%m/%Y')
# Extrair componentes
df['ano'] = df['data'].dt.year
df['mes'] = df['data'].dt.month
df['dia'] = df['data'].dt.day
df['dia_semana'] = df['data'].dt.dayofweek
df['nome_dia'] = df['data'].dt.day_name()
df['trimestre'] = df['data'].dt.quarter
# Aritmética
df['data'] + pd.Timedelta(days=7)
df['data'] - pd.Timedelta(hours=1)
(df['data_fim'] - df['data_inicio']).dt.days
# Filtrar
df[df['data'] > '2025-01-01']
df[df['data'].between('2025-01-01', '2025-12-31')]
# Resample (séries temporais)
df.set_index('data').resample('M')['valor'].sum() # mensal
df.set_index('data').resample('W')['valor'].mean() # semanal
df.set_index('data').resample('D')['valor'].sum() # diário
Strings¶
# Acessar via .str
df['nome'].str.lower()
df['nome'].str.upper()
df['nome'].str.title()
df['nome'].str.strip()
df['nome'].str.replace('old', 'new')
df['nome'].str.contains('texto')
df['nome'].str.startswith('A')
df['nome'].str.endswith('a')
df['nome'].str.len()
df['nome'].str.split(',')
df['nome'].str.split(',').str[0] # primeiro elemento
# Regex
df['nome'].str.extract(r'(\d+)')
df['nome'].str.findall(r'\d+')
df['nome'].str.replace(r'\s+', ' ', regex=True)
Window functions¶
# Rolling (janela móvel)
df['media_movel'] = df['valor'].rolling(window=7).mean()
df['soma_movel'] = df['valor'].rolling(window=7).sum()
# Expanding (acumulado)
df['acumulado'] = df['valor'].expanding().sum()
df['max_ate_aqui'] = df['valor'].expanding().max()
# Shift (lag/lead)
df['valor_anterior'] = df['valor'].shift(1) # lag
df['valor_proximo'] = df['valor'].shift(-1) # lead
# Diff
df['variacao'] = df['valor'].diff()
# Pct change
df['variacao_pct'] = df['valor'].pct_change()
Categorias¶
# Criar categoria
df['categoria'] = df['categoria'].astype('category')
# Ordenar categorias
df['tamanho'] = pd.Categorical(
df['tamanho'],
categories=['P', 'M', 'G', 'GG'],
ordered=True
)
# Cut (bins)
df['faixa'] = pd.cut(df['idade'], bins=[0, 18, 30, 50, 100])
df['faixa'] = pd.cut(
df['idade'],
bins=[0, 18, 30, 50, 100],
labels=['Menor', 'Jovem', 'Adulto', 'Senior']
)
# Qcut (quantis)
df['quartil'] = pd.qcut(df['valor'], q=4)
df['quartil'] = pd.qcut(df['valor'], q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])
Performance¶
# Usar tipos eficientes
df['inteiro'] = df['inteiro'].astype('int32')
df['categoria'] = df['categoria'].astype('category')
# Ler só colunas necessárias
df = pd.read_csv('dados.csv', usecols=['col1', 'col2'])
# Processar em chunks
chunks = pd.read_csv('grande.csv', chunksize=10000)
for chunk in chunks:
processar(chunk)
# Usar query() para filtros complexos
df.query('col1 > 100 and col2 == "A"') # mais rápido que &
# Vetorizar operações (evitar apply quando possível)
# Ruim:
df['nova'] = df['col'].apply(lambda x: x * 2)
# Bom:
df['nova'] = df['col'] * 2
Method chaining¶
# Encadear operações
resultado = (
df
.query('idade > 18')
.assign(idade_meses=lambda x: x['idade'] * 12)
.groupby('cidade')
.agg(
total=('valor', 'sum'),
media_idade=('idade', 'mean')
)
.sort_values('total', ascending=False)
.head(10)
)