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.

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

1. O Que Você Aprenderá


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

100%
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"]
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 ligeiramente dependendo da sua versão do pandas.

3. Pré-processamento de Dados

▶ 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 ligeiramente dependendo da sua versão do pandas.

: Pré-processamento de Séries Temporais (Dificuldade ⭐⭐)

PYTHON
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()}")
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 ligeiramente dependendo da sua versão do pandas.

4. Extração de Tendência

▶ 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 ligeiramente dependendo da sua versão do pandas.

: Tendência e Remoção de Tendência (Dificuldade ⭐⭐)

PYTHON
# ============================================
# 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}%")
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 ligeiramente dependendo da sua versão do pandas.

5. Análise de Sazonalidade

▶ 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 ligeiramente dependendo da sua versão do pandas.

: Índice Sazonal (Dificuldade ⭐⭐⭐)

PYTHON
# ============================================
# 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}")
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 ligeiramente dependendo da sua versão do pandas.

6. Previsão por 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-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 ⭐⭐⭐)

PYTHON
# ============================================
# 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}%")
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 ligeiramente dependendo da sua versão do pandas.

7. Avaliação da Previsão

▶ 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 ligeiramente dependendo da sua versão do pandas.

: Avaliação e Previsão do Próximo Mês (Dificuldade ⭐⭐)

PYTHON
# ============================================
# 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}")
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 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


📝 Exercícios

  1. 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.
  2. 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.
  3. 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 →

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%