Pandas: Projeto: Séries Temporais
Última atualização: 2026-08-26
A previsão de séries temporais não é apenas sobre modelagem — antes de recorrer a Machine Learning, compreender apenas a tendência e a sazonalidade pode produzir previsões de linha de base sólidas. Nesta lição, usamos a previsão de vendas da cafeteria da Alice como cenário, percorrendo todo o pipeline desde o pré-processamento até a previsão básica com resample + rolling + shift. Esta é uma aplicação prática das ferramentas da Lição 17 e um prelúdio para as próximas lições de ML.
1. O Que Você Aprenderá
- ❶ Pré-processamento de séries temporais
- ❷ Extração de tendência e sazonalidade
- ❸ Previsão com média móvel
- ❹ Decomposição sazonal
- ❺ Avaliação da previsão (MAE/MAPE)
2. Contexto do Projeto: Previsão de Vendas de Cafeteria
(1) Tarefa
Alice quer prever a receita da cafeteria para o próximo mês — os dados históricos têm uma tendência (crescimento constante mês a mês) e sazonalidade (mais bebidas quentes no inverno).
(2) Pipeline de Análise
graph TB
A["1. Pré-processamento<br>resample+fillna"] --> B["2. Extração de Tendência<br>rolling+diff"]
B --> C["3. Análise de Sazonalidade<br>groupby mês"]
C --> D["4. Previsão por Média Móvel<br>rolling mean"]
D --> E["5. Avaliação<br>MAE/MAPE"]
E --> F["6. Previsão Próximo Mês"]
> **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 ligeiramente dependendo da sua versão do pandas.
3. Pré-processamento de Dados
▶ Exemplo
> **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 ligeiramente dependendo da sua versão do pandas.
: Pré-processamento de Séries Temporais (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
# ============================================
# Passo 1: Gerar & Pré-processar
# ============================================
np.random.seed(42)
# Gerar 365 dias de vendas com tendência + sazonalidade + ruído
dates = pd.date_range('2023-01-01', periods=365)
trend = np.linspace(300, 450, 365) # tendência de alta
seasonality = 50 * np.sin(2 * np.pi * np.arange(365) / 365 * 2) # ciclo semestral
winter_effect = np.where(dates.month.isin([11, 12, 1, 2]), 30, 0) # impulso no inverno
noise = np.random.normal(0, 20, 365)
daily_sales = pd.DataFrame({
'sales': np.round(trend + seasonality + winter_effect + noise, 0),
'customers': np.round((trend + seasonality + winter_effect + noise) / 8, 0)
}, index=dates)
# Injetar dias faltantes
missing_idx = np.random.choice(365, 15, replace=False)
daily_sales.iloc[missing_idx] = np.nan
# Pré-processar: preencher faltantes → garantir frequência diária
daily_sales = daily_sales.asfreq('D') # garantir que não faltem datas
daily_sales['sales'] = daily_sales['sales'].interpolate(method='time')
daily_sales['customers'] = daily_sales['customers'].interpolate(method='time')
print(f"Período: {daily_sales.index[0].date()} a {daily_sales.index[-1].date()}")
print(f"Faltantes após preenchimento: {daily_sales.isnull().sum().sum()}")
print(f"\nEstatísticas de vendas diárias:\n{daily_sales['sales'].describe()}")
> **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 ligeiramente dependendo da sua versão do pandas.
4. Extração de Tendência
▶ Exemplo
> **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 ligeiramente dependendo da sua versão do pandas.
: Tendência e Remoção de Tendência (Dificuldade ⭐⭐)
# ============================================
# Passo 2: Extração de Tendência
# ============================================
# Média móvel de 30 dias = tendência
daily_sales['trend'] = daily_sales['sales'].rolling(30, center=True).mean()
# Removido a tendência = real - tendência (mostra sazonalidade + ruído)
daily_sales['detrended'] = daily_sales['sales'] - daily_sales['trend']
# Taxa de crescimento mês a mês
monthly = daily_sales['sales'].resample('M').sum()
monthly_growth = monthly.pct_change() * 100
print("=== Vendas Mensais ===")
print(monthly.tail(6))
print(f"\nCrescimento mensal médio: {monthly_growth.mean():.1f}%")
print(f"Crescimento anual total: {((monthly.iloc[-1] / monthly.iloc[0]) - 1) * 100:.1f}%")
> **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 ligeiramente dependendo da sua versão do pandas.
5. Análise de Sazonalidade
▶ Exemplo
> **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 ligeiramente dependendo da sua versão do pandas.
: Índice Sazonal (Dificuldade ⭐⭐⭐)
# ============================================
# Passo 3: Análise de Sazonalidade
# ============================================
# Média mensal (de todos os anos)
daily_sales['month'] = daily_sales.index.month
monthly_avg = daily_sales.groupby('month')['sales'].mean()
# Índice sazonal = média mensal / média geral
overall_avg = daily_sales['sales'].mean()
seasonal_index = (monthly_avg / overall_avg).round(3)
print("=== Índice Sazonal ===")
for m, idx in seasonal_index.items():
label = "↑" if idx > 1.05 else ("↓" if idx < 0.95 else "→")
print(f" Mês {m:2d}: {idx:.3f} {label}")
# Padrão do dia da semana
daily_sales['dayofweek'] = daily_sales.index.dayofweek
dow_avg = daily_sales.groupby('dayofweek')['sales'].mean()
dow_names = ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb', 'Dom']
print(f"\n=== Média por Dia da Semana ===")
for i, name in enumerate(dow_names):
print(f" {name}: ${dow_avg[i]:.0f}")
> **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 ligeiramente dependendo da sua versão do pandas.
6. Previsão por Média Móvel
▶ Exemplo
> **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 ligeiramente dependendo da sua versão do pandas.
: Previsão com Média Móvel (Dificuldade ⭐⭐⭐)
# ============================================
# Passo 4: Previsão por Média Móvel
# ============================================
# Divisão treino/teste: últimos 30 dias como teste
train = daily_sales.iloc[:-30]
test = daily_sales.iloc[-30:]
# Previsão simples por média móvel (usa a média dos últimos N dias)
def ma_forecast(train_series, window, horizon):
"""Prever os próximos 'horizon' dias usando média móvel"""
last_ma = train_series.rolling(window).mean().iloc[-1]
return pd.Series([last_ma] * horizon,
index=pd.date_range(train.index[-1] + pd.Timedelta(days=1),
periods=horizon))
# Testar diferentes janelas
for w in [7, 14, 30]:
forecast = ma_forecast(train['sales'], w, 30)
mae = (forecast - test['sales']).abs().mean()
mape = ((forecast - test['sales']) / test['sales']).abs().mean() * 100
print(f"MA({w:2d}): MAE=${mae:.0f}, MAPE={mape:.1f}%")
# Previsão com ajuste sazonal
# previsão = previsão_tendência × índice_sazonal
last_trend = train['sales'].rolling(30).mean().iloc[-1]
monthly_trend_growth = train['sales'].rolling(30).mean().diff(30).mean()
forecast_months = test.index.month
seasonal_forecast = pd.Series(
[(last_trend + monthly_trend_growth * i) * seasonal_index[m]
for i, m in enumerate(forecast_months)],
index=test.index
)
mae_seasonal = (seasonal_forecast - test['sales']).abs().mean()
mape_seasonal = ((seasonal_forecast - test['sales']) / test['sales']).abs().mean() * 100
print(f"\nAjuste sazonal: MAE=${mae_seasonal:.0f}, MAPE={mape_seasonal:.1f}%")
> **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 ligeiramente dependendo da sua versão do pandas.
7. Avaliação da Previsão
▶ Exemplo
> **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 ligeiramente dependendo da sua versão do pandas.
: Avaliação e Previsão do Próximo Mês (Dificuldade ⭐⭐)
# ============================================
# Passo 5-6: Avaliação & Previsão Próximo Mês
# ============================================
# Métricas de avaliação
def evaluate(actual, forecast, name="Modelo"):
mae = (forecast - actual).abs().mean()
rmse = ((forecast - actual) ** 2).mean() ** 0.5
mape = ((forecast - actual) / actual).abs().mean() * 100
print(f"{name}: MAE=${mae:.0f}, RMSE=${rmse:.0f}, MAPE={mape:.1f}%")
return mae, rmse, mape
# Avaliar melhor modelo
print("=== Avaliação do Modelo ===")
evaluate(test['sales'], ma_forecast(train['sales'], 14, 30), "MA(14)")
evaluate(test['sales'], seasonal_forecast, "Ajuste sazonal")
# Previsão próximo mês (Janeiro 2024)
next_month_days = pd.date_range('2024-01-01', periods=31)
last_trend_full = daily_sales['sales'].rolling(30).mean().iloc[-1]
jan_index = seasonal_index[1]
next_month_forecast = last_trend_full * jan_index
print(f"\n=== Previsão para Janeiro de 2024 ===")
print(f" Previsão diária: ${next_month_forecast:.0f}")
print(f" Previsão mensal: ${next_month_forecast * 31:,.0f}")
print(f" Baseado na tendência: ${last_trend_full:.0f} × índice sazonal: {jan_index:.3f}")
> **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 ligeiramente dependendo da sua versão do pandas.
❓ Perguntas Frequentes
P: Como escolho a janela da média móvel? R: Alinhar a janela ao ciclo natural dos dados é a aposta mais segura. Para dados diários, um ciclo semanal significa janela=7; um ciclo mensal significa janela=30. Uma janela pequena demais captura muito ruído; uma janela grande demais introduz um atraso sério. Comece com uma janela igual ao período natural, depois tente alguns valores acima e abaixo, comparando o MAPE. Nesta lição, MA(14) normalmente oferece um equilíbrio melhor que MA(7) ou MA(30).
P: Como separo a tendência da sazonalidade? R: Tendência = média móvel de longo prazo (30+ dias); sazonalidade = a razão da média de cada mês pela média geral. A remoção de tendência significa subtrair a tendência dos valores brutos (modelo aditivo) ou dividir os valores brutos pela tendência (modelo multiplicativo). Dados de varejo geralmente exigem um modelo multiplicativo (as flutuações sazonais escalam com o nível), enquanto dados de temperatura se encaixam em um modelo aditivo.
P: Qual MAPE é considerado bom? R: MAPE < 10% é alta acurácia, 10-20% é bom, 20-50% é aceitável, e > 50% é não confiável. Mas o contexto importa — para vendas diárias altamente voláteis, um MAPE de 20% já é decente, e após agregar para totais mensais, o MAPE deve cair abaixo de 10%. Quanto mais longe você prevê, menos confiável se torna — uma previsão de 7 dias é muito mais confiável que uma de 30 dias.
P: Até que ponto consigo prever com confiança? R: Regra de ouro: o horizonte de previsão confiável é aproximadamente 1/3 a 1/2 do comprimento dos seus dados históricos. Com um ano de histórico, você pode prever com confiança no máximo 4-6 meses. Médias móveis só podem projetar uma continuação "plana" — elas não podem prever pontos de virada. Mudanças de tendência ou eventos inesperados invalidarão completamente a previsão. A previsão é uma ferramenta de apoio à decisão, não uma bola de cristal.
P: Como isso difere da previsão com ML? R: A previsão por média móvel assume que "o futuro é uma continuação do passado" — ela só pode capturar tendência e sazonalidade. Os métodos de ML (ARIMA/LSTM/Prophet) podem modelar padrões muito mais complexos (tendências não lineares, interações multivariadas, efeitos de feriados). Esta lição serve como a linha de base de ML — se uma média móvel simples alcançar 15% de MAPE, um modelo de ML deve ficar abaixo de 10% para valer a complexidade adicional.
P: Como o índice sazonal é calculado? R: Divida a média de cada mês pela média geral. Por exemplo, se a média de janeiro é 420 e a média geral é 380, o índice sazonal é 420/380 = 1,105 (janeiro fica 10,5% acima da média). Um índice > 1 indica alta temporada; < 1 indica baixa temporada. Você precisa de pelo menos 2 anos de dados para um índice sazonal confiável (janeiro pode variar de ano para ano).
P: Devo usar interpolate ou ffill para valores faltantes? R: Para séries temporais, recomenda-se interpolate — interpolar entre valores adjacentes produz resultados mais suaves e preserva a tendência. ffill carrega o último valor conhecido para frente, criando um efeito de escada durante as lacunas (descontinuidade da tendência). Para lacunas grandes, use interpolate(método='time') para considerar a distância temporal; para lacunas pequenas, a interpolação linear é suficiente.
📖 Resumo
- Pré-processamento: asfreq garante frequência consistente, depois interpolate preenche valores faltantes
- Tendência: média móvel rolling(30); diff revela a taxa de mudança
- Sazonalidade: média mensal / média geral = índice sazonal
- Previsão: média móvel (simples) → tendência × índice sazonal (melhorado)
- Avaliação: MAE (erro absoluto) / RMSE (raiz do erro quadrático médio) / MAPE (porcentagem)
- Horizonte de previsão confiável ≈ 1/3 do comprimento dos dados históricos; a média móvel serve como a linha de base para ML
📝 Exercícios
- Básico (Dificuldade ⭐): Gere 180 dias de dados de vendas (com uma tendência), agregue para totais mensais com resample('M'), e calcule a taxa de crescimento mês a mês.
- Intermediário (Dificuldade ⭐⭐): Gere 365 dias de dados com sazonalidade, extraia uma linha de tendência de 30 dias, compute o índice sazonal de 12 meses, e identifique as temporadas alta e baixa.
- Desafio (Dificuldade ⭐⭐⭐): Complete um projeto de previsão completo: pré-processamento → tendência + sazonalidade → previsões MA(7/14/30) → previsão com ajuste sazonal → avaliação MAPE → saída da previsão para o próximo mês.
← Anterior: Projeto - Análise de Dados · Próximo: Projeto - Análise Abrangente →