Pandas: Séries Temporais Avançadas

Última atualização: 2026-08-26

Na Lição 16, você aprendeu sobre tipos de datetime. Esta lição aprofunda-se na análise prática de séries temporais — resample transforma dados diários em resumos mensais, shift/diff cria características de defasagem, e as três funções de janela (rolling/expanding/ewm) deixam tendências e volatilidade claras como cristal. Esta é a base fundamental para análise quantitativa e engenharia de características de ML.

⚠️ Nota: O código abaixo requer um ambiente Python local para ser executado.

1. What You Will Learn


2. Relatório Mensal da Cafeteria da Alice

(1) O Problema: Dados Diários São Muito Granulares — Necessidade de Agregação Mensal

Alice tem 90 dias de dados diários de vendas, e seu chefe quer um relatório mensal:

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'sales': np.random.poisson(50, 90) * 10,
    'customers': np.random.poisson(30, 90)
}, index=pd.date_range('2024-01-01', periods=90))
# Dados diários → precisa de resumo mensal
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.

(2) A Solução: Agregação em Uma Linha com resample

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.

: agregação mensal com resample (Dificuldade ⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'sales': np.random.poisson(50, 90) * 10,
    'customers': np.random.poisson(30, 90)
}, index=pd.date_range('2024-01-01', periods=90))

# Reamostre dados diários → mensais
monthly = df.resample('M').agg({
    'sales': 'sum',
    'customers': 'mean'
})
print(monthly)
#              sales  customers
# 2024-01-31   14680  29.55
# 2024-02-29   13520  31.10
# 2024-03-31   15430  30.45
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.

3. resample — Reamostragem

(1) Subamostragem e Superamostragem

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: reamostragem subamostragem/superamostragem (Dificuldade ⭐⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'price': np.random.uniform(100, 200, 60).round(2)
}, index=pd.date_range('2024-01-01', periods=60))

# Subamostragem: diário → semanal
weekly = df.resample('W').agg({
    'price': ['first', 'max', 'min', 'last']  # OHLC
})
print("OHLC Semanal:")
print(weekly.head())

# Subamostragem: diário → trimestral
quarterly = df.resample('Q').mean()

# Superamostragem: diário → horário (precisa de método de preenchimento)
hourly = df.resample('h').ffill()  # preenchimento progressivo
# Aviso: cria muitas linhas!
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(2) resample vs groupby

Recurso resample groupby
Base de agrupamento Frequência temporal Qualquer coluna
Funciona com DatetimeIndex Qualquer DataFrame
Superamostragem ✅ Preenche valores faltantes
Aliases de frequência D/W/M/Q/H
Essência groupby ao longo da dimensão temporal Agrupamento de uso geral

4. shift / diff / pct_change

(1) Defasagem e Diferença

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.

: shift/diff/pct_change (Dificuldade ⭐⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'sales': np.random.poisson(50, 10) * 10
}, index=pd.date_range('2024-01-01', periods=10))

# shift: move os dados para frente/para trás
df['sales_prev'] = df['sales'].shift(1)    # vendas de ontem
df['sales_next'] = df['sales'].shift(-1)   # vendas de amanhã

# diff: diferença com o valor anterior
df['sales_diff'] = df['sales'].diff()       # vendas - vendas_anteriores

# pct_change: variação percentual
df['sales_pct'] = df['sales'].pct_change() * 100  # % de variação

print(df.round(1))
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.
💡 Dica: A defasagem (shift) é a base da engenharia de características de séries temporais em ML — df['lag_1'] = df['value'].shift(1) usa o valor de ontem para prever hoje. diff extrai mudanças de tendência, e pct_change extrai taxas de crescimento.


5. rolling — Janela Deslizante

(1) Média Móvel

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: média móvel rolling (Dificuldade ⭐⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'temperature': np.round(np.random.normal(20, 5, 30), 1)
}, index=pd.date_range('2024-01-01', periods=30))

# 7-day moving average
df['ma_7'] = df['temperature'].rolling(window=7).mean()

# With min_periods (allow partial windows)
df['ma_7_flex'] = df['temperature'].rolling(window=7, min_periods=3).mean()

# Multiple aggregations
# Pandas 2.x: rolling.agg() returns a multi-column DataFrame and cannot be assigned to a single column directly; use join instead
rolling_agg = df['temperature'].rolling(7).agg(['mean', 'std', 'min', 'max'])
rolling_agg.columns = [f'temp_{stat}' for stat in ['mean', 'std', 'min', 'max']]
df = df.join(rolling_agg)

# Centered rolling (include future values — for smoothing, not forecasting)
df['ma_7_center'] = df['temperature'].rolling(7, center=True).mean()

print(df[['temperature', 'ma_7', 'ma_7_center']].head(10))
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(2) Janelas Baseadas em Tempo

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: janela rolling baseada em tempo (Dificuldade ⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# Irregular time series — row-based window is wrong
df = pd.DataFrame({
    'value': [10, 20, 15, 30, 25, 40, 35, 50, 45, 60]
}, index=pd.to_datetime([
    '2024-01-01', '2024-01-02', '2024-01-05',  # gap on 03-04
    '2024-01-06', '2024-01-07', '2024-01-15',  # gap on 08-14
    '2024-01-16', '2024-01-20', '2024-01-21', '2024-01-25'
]))

# Row-based window (wrong for irregular data)
print("Row-based 3:")
print(df['value'].rolling(3).mean())

# Time-based window (correct — 7 calendar days)
print("\nTime-based 7D:")
print(df['value'].rolling('7D').mean())
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Por favor, instale localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

6. expanding — Janela Cumulativa

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: estatísticas cumulativas com expanding (Dificuldade ⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'sales': np.random.poisson(50, 10) * 10
})

# Expanding: todos os valores até a posição atual
df['cumsum'] = df['sales'].expanding().sum()
df['cummean'] = df['sales'].expanding().mean()
df['cummax'] = df['sales'].expanding().max()

# Com min_periods
df['cummean_3'] = df['sales'].expanding(min_periods=3).mean()

print(df)
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado. Instale-o localmente (`pip install pandas`) e acompanhe. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

(1) Comparação rolling vs expanding vs ewm

Característica rolling expanding ewm
Janela Tamanho fixo Crescimento contínuo Infinita (pesos decaem)
Pesos Iguais Iguais Valores recentes pesam mais
Caso de uso Tendências locais Estatísticas cumulativas Tendências suavizadas
Parâmetros window min_periods span/com/halflife

7. ewm — Suavização Exponencial

▶ Exemplo

TEXT 📖 Somente leitura
> **Output:** Run in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.

: suavização exponencial ewm (Dificuldade ⭐⭐)

PYTHON
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    'price': np.cumsum(np.random.randn(30)) + 100
}, index=pd.date_range('2024-01-01', periods=30))

# Exponentially weighted moving average
df['ewm_5'] = df['price'].ewm(span=5).mean()
df['ewm_10'] = df['price'].ewm(span=10).mean()
df['ewm_20'] = df['price'].ewm(span=20).mean()

# Compare with simple moving average
df['sma_5'] = df['price'].rolling(5).mean()

print(df[['price', 'sma_5', 'ewm_5']].head(10).round(2))

# ewm parameters (only specify one):
# span=5     → similar to 5-period SMA but recent-heavy
# com=4      → center of mass (com = span-1)
# halflife=3 → weight halves every 3 periods
# alpha=0.3  → smoothing factor (0<α≤1)
TEXT 📖 Somente leitura
> **Output:** Run in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.

8. Exemplo Completo: Análise Diária de Gráfico de Candlestick de Ações

(5) ▶ Janela Deslizante Deslizando ao Longo do Eixo Temporal

100%
graph TB
    A[Time Series Data] --> B[rolling Window]
    B --> C[Window slides along the time axis]
    C --> D[Window 1: t1 to tN]
    C --> E[Window 2: t2 to t(N+1)]
    C --> F[Window 3: t3 to t(N+2)]
    D --> G[mean / std / max / min]
    E --> G
    F --> G
    G --> H[Smoothed trend / Volatility]
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga o código. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga o código. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

: pipeline completo de análise de séries temporais (Dificuldade ⭐⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# ============================================
# Exemplo abrangente: dados diários de ações
# resample → rolling → diff → ewm
# ============================================

# 1. Gerar dados diários de ações (60 dias úteis)
np.random.seed(42)
dates = pd.bdate_range('2024-01-01', periods=60)
price = 100 + np.cumsum(np.random.randn(60) * 2)
df = pd.DataFrame({
    'open': price + np.random.randn(60) * 0.5,
    'high': price + np.abs(np.random.randn(60)) * 1.5,
    'low': price - np.abs(np.random.randn(60)) * 1.5,
    'close': price + np.random.randn(60) * 0.5,
    'volume': np.random.randint(100000, 500000, 60)
}, index=dates)

# 2. Resample: diário → semanal
weekly = df.resample('W-FRI').agg({
    'open': 'first', 'high': 'max', 'low': 'min',
    'close': 'last', 'volume': 'sum'
})
print("=== OHLCV Semanal ===")
print(weekly.head(4).round(2))

# 3. Rolling: média móvel de 20 dias
df['ma_20'] = df['close'].rolling(20).mean()
df['vol_20'] = df['close'].rolling(20).std()

# 4. Retornos diários
df['returns'] = df['close'].pct_change()
df['log_returns'] = np.log(df['close'] / df['close'].shift(1))

# 5. EWM: suavização exponencial
df['ewm_12'] = df['close'].ewm(span=12).mean()
df['ewm_26'] = df['close'].ewm(span=26).mean()
df['macd'] = df['ewm_12'] - df['ewm_26']

# 6. Resumo
print(f"\n=== Estatísticas ===")
print(f"Período: {df.index[0].date()} a {df.index[-1].date()}")
print(f"Retorno total: {((df['close'].iloc[-1] / df['close'].iloc[0] - 1) * 100):.1f}%")
print(f"Volatilidade (20d): {df['vol_20'].iloc[-1]:.2f}")
print(f"Máximo drawdown: {((df['close'] / df['close'].cummax() - 1).min() * 100):.1f}%")
TEXT 📖 Somente leitura
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado. Por favor, instale-o localmente (`pip install pandas`) e siga o código. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.

❓ Perguntas Frequentes

P Qual é a diferença entre resample e groupby?
R resample é essencialmente groupby ao longo da dimensão tempo — agrupa por frequência (diária/semanal/mensal/trimestral) e só funciona com DatetimeIndex/PeriodIndex. groupby agrupa por qualquer coluna e não é limitado a tempo. resample suporta unicamente upsampling (frequência baixa → alta requer preenchimento), o que groupby não consegue fazer. Regra prática: use resample para agregações baseadas em tempo, groupby para todo o resto.
P Como escolho o tamanho da janela (rolling window)?
R Depende do cenário. Para tendências de curto prazo, use uma janela de 5-10; para médio prazo, 20-30 (mensal); para longo prazo, 50-200 (trimestral/anual). Nas finanças, as médias móveis de 20/50/200 dias são padrão. Janela muito pequena → muito ruído; grande demais → atraso excessivo. Recomendação: olhe primeiro a frequência dos seus dados e defina a janela para cobrir 1-3 "ciclos naturais".
P Qual é a diferença entre shift e diff?
R shift move os dados sem calcular nada; diff calcula a diferença. s.diff() = s - s.shift(1). shift é usado para criar features de lag (ML), enquanto diff extrai a quantidade de mudança (análise de tendência). pct_change calcula a taxa de mudança: s.pct_change() = (s - s.shift(1)) / s.shift(1).
P Qual é a diferença entre expanding e cumsum?
R expanding é uma versão generalizada de cumsum — suporta qualquer função de agregação (mean/std/max/min), enquanto cumsum só computa a soma cumulativa. s.expanding().sum() é equivalente a s.cumsum(). expanding é mais flexível, mas um pouco mais lento; para uso diário, cumsum é mais conciso.
P O que é ewm?
R EWM (Exponentially Weighted Moving) é uma média móvel ponderada exponencialmente — dados mais recentes recebem peso maior, e dados mais antigos decaem exponencialmente. span=5 comporta-se similarmente a uma SMA de 5 períodos, mas dá mais peso aos valores recentes. Ela responde a mudanças mais rápido que a SMA e é a base da análise técnica (MACD) e aprendizado online.
P Como preencho valores após upsampling com resample?
R Upsampling (frequência baixa → alta) produz muitos valores NaN. Opções de preenchimento: ffill() (preenchimento para frente, usa o valor anterior), bfill() (preenchimento para trás), interpolate() (interpolação), asfreq() (mantém NaN). Recomenda-se ffill (simples e conservador); para análise de séries temporais, use interpolate (resultados mais suaves).
P Posso usar center=True para previsão?
R De forma alguma! center=True faz a janela "olhar para o futuro" (inclui valores subsequentes), então é adequada apenas para suavizar dados históricos — nunca para previsão. Em cenários de tempo real ou previsão, você deve usar center=False (padrão), que olha apenas para valores passados. Esse erro causa vazamento de dados grave em análise quantitativa.

📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie um DataFrame de vendas diárias de 30 dias. Use resample('W') para agregar em dados semanais e rolling(7) para calcular uma média móvel de 7 dias.
  2. Intermediário (Dificuldade ⭐⭐): Crie 60 dias de preços de fechamento de ações. Calcule: shift(1) lag → diff() diferença diária → pct_change() retornos → rolling(20) volatilidade → ewm(span=12) suavização.
  3. Desafio (Dificuldade ⭐⭐⭐): Simule 90 dias de vendas diárias. Complete: resumo mensal/trimestral com resample → comparação de média móvel de 7/14/30 dias com rolling → média cumulativa expansiva com expanding → suavização com ewm → cálculo do maximum drawdown.

← Previous Lesson: Date and Time · Next Lesson: MultiIndex →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%