Análise de Séries Temporais com Python: Statsmodels, Pandas e Prophet

Aprenda a analisar e prever séries temporais com Python usando Pandas, Statsmodels e Prophet, com exemplos práticos em dados econômicos brasileiros e previsão de demanda.

9 min de leitura Equipe python.dev.br

Prever o futuro a partir do passado é uma das tarefas mais úteis — e mais difíceis — da análise de dados. Vendas do próximo mês, demanda de energia elétrica no verão, preço de commodities, faturamento de uma loja, número de passageiros em um aeroporto: todos são exemplos de séries temporais, sequências de observações ordenadas no tempo. Python se tornou a linguagem dominante nesse campo porque combina manipulação eficiente de dados, bibliotecas estatísticas maduras e ferramentas de aprendizado de máquina em um mesmo ecossistema.

Este guia mostra, na prática, como analisar e prever séries temporais com Python. Vamos cobrir os fundamentos (tendência, sazonalidade, ruído), o fluxo de trabalho com Pandas, decomposição e modelos clássicos com Statsmodels, e previsão automatizada com Prophet. Os exemplos usam dados econômicos brasileiros, como a taxa Selic e o dólar, porque esse é o tipo de problema comum em empresas, bancos, fintechs e órgãos públicos do Brasil. Se você está começando na área de dados, leia antes o nosso guia de introdução ao Pandas e o de Python para ciência de dados.

O que é uma série temporal

Uma série temporal é qualquer conjunto de observações indexadas pelo tempo. A diferença em relação a dados tabulares comuns é que a ordem importa: o valor de hoje depende, de alguma forma, do valor de ontem. Isso quebra uma das premissas de muitos modelos de machine learning, que assumem observações independentes, e exige técnicas específicas.

Toda série temporal pode ser decomposta em três componentes principais:

  1. Tendência: movimento de longo prazo, ascendente ou descendente. Por exemplo, a inflação acumulada ao longo de anos.
  2. Sazonalidade: padrões que se repetem em intervalos fixos (diário, semanal, mensal, anual). O comércio varejista cresce no Natal; o consumo de energia sobe no verão.
  3. Ruído (resíduo): variação aleatória que não segue padrão algum e não pode ser prevista.

Modelos bons conseguem separar esses componentes e projetar a parte estrutural (tendência e sazonalidade), aceitando que o ruído é inevitável. O objetivo não é prever perfeitamente, mas reduzir a incerteza o suficiente para tomar decisões melhores.

Por que isso importa para negócios brasileiros? Uma cooperativa de crédito quer prever inadimplência; uma transportadora quer estimar volume de cargas em datas comemorativas; uma startup de energia solar quer dimensionar estoque de painéis. Em todos esses casos, a previsão de séries temporais traduz dados históricos em decisões de receita, custo e operação.

Preparando o ambiente

O caminho mais simples é usar o uv ou um ambiente virtual tradicional. As bibliotecas centrais deste guia são Pandas, NumPy, Matplotlib, Statsmodels e Prophet. Veja como instalar:

uv venv .venv
source .venv/bin/activate
uv pip install pandas numpy matplotlib statsmodels prophet

Se você ainda não usa o uv, leia o nosso guia sobre o gerenciador de pacotes uv para entender por que ele é mais rápido que o pip tradicional. Para visualização, recomendo também conhecer o Matplotlib, que usaremos nos gráficos deste artigo.

Carregando dados com Pandas

O Pandas é a base de qualquer análise de séries temporais em Python. O segredo está em converter a coluna de data em índice e garantir que a frequência esteja correta. Vamos usar um exemplo com a taxa Selic (dados fictícios estruturados como os da série do Banco Central):

import pandas as pd

df = pd.read_csv("selic_mensal.csv", parse_dates=["data"], index_col="data")
df = df.asfreq("MS")  # Month Start: frequência mensal
df["selic"] = df["selic"].interpolate()  # preenche buracos
print(df.head())
print(df.index.freq)  # deve mostrar <MonthBegin>

Dois detalhes são cruciais aqui. Primeiro, asfreq("MS") força a série a ter uma frequência definida; sem isso, muitas funções do Statsmodels e do Prophet não funcionam corretamente. Segundo, interpolate() preenche valores ausentes linearmente, o que é aceitável para pequenos buracos. Para buracos grandes, é melhor investigar a causa (falha na coleta, mudança de metodologia) antes de inventar dados.

Quando o dado vem de uma API, como a do Banco Central, convém validar a série antes de modelar. O nosso artigo sobre APIs públicas brasileiras com Python mostra como consumir diretamente o Sistema de Gerenciamento de Séries Temporais (SGS) do BC com HTTPX.

Visualização e decomposição

Antes de modelar, olhe para a série. A decomposição clássica separa tendência, sazonalidade e resíduo, e revela qual padrão domina:

from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt

result = seasonal_decompose(df["selic"], model="additive", period=12)
result.plot()
plt.tight_layout()
plt.show()

O parâmetro model pode ser "additive" (componentes somados) ou "multiplicative" (componentes multiplicados). Use o multiplicativo quando a amplitude da sazonalidade cresce junto com o nível da série — comum em vendas de empresas em expansão. O period define o ciclo sazonal: 12 para dados mensais anuais, 7 para dados diários semanais, 4 para trimestrais.

A inspeção visual responde perguntas que nenhum número responde sozinho: a série é estável ou explode? Há quebras estruturais (crises, mudanças de governo, pandemia)? A sazonalidade é forte ou sutil? Essas respostas guiam a escolha do modelo. Pular essa etapa é o erro mais comum de quem começa.

Estacionariedade e o teste de Dickey-Fuller

Muitos modelos clássicos, como o ARIMA, exigem que a série seja estacionária: média, variância e autocorrelação constantes no tempo. Séries econômicas raramente são. O teste de Dickey-Fuller aumentado (ADF) verifica isso estatisticamente:

from statsmodels.tsa.stattools import adfuller

teste = adfuller(df["selic"].dropna())
print(f" estatística ADF: {teste[0]:.4f}")
print(f"p-valor: {teste[1]:.4f}")

Um p-valor abaixo de 0,05 indica estacionariedade. Se a série não for estacionária, a correção padrão é diferenciar: subtrair de cada valor o valor anterior. Isso remove a tendência. A primeira diferença costuma bastar, mas às vezes é necessária a segunda. Diferenciar demais introduz ruído, então é um equilíbrio.

Modelos clássicos com Statsmodels

O Statsmodels oferece ARIMA e SARIMA, os modelos lineares mais usados para séries univariadas. O SARIMA estende o ARIMA com termos sazonais. A notação é SARIMA(p, d, q)(P, D, Q, s), onde p/d/q são os termos não sazonais, P/D/Q os sazonais e s o período:

from statsmodels.tsa.statespace.sarimax import SARIMAX

modelo = SARIMAX(
    df["selic"],
    order=(1, 1, 1),
    seasonal_order=(1, 1, 1, 12),
    enforce_stationarity=False,
)
resultado = modelo.fit(disp=False)
previsao = resultado.get_forecast(steps=12)
intervalo = previsao.conf_int()
print(previsao.predicted_mean.head())

A escolha dos parâmetros (p, d, q) é parte arte, parte método. As funções de autocorrelação (ACF) e autocorrelação parcial (PACF) do Statsmodels ajudam, e existem ferramentas como pmdarima que fazem a busca automática. Para tarefas de produção, porém, Prophet costuma ser mais simples e robusto, como veremos a seguir.

Previsão automatizada com Prophet

O Prophet, desenvolvido pelo Facebook (Meta), é um modelo aditivo generalizado que lida bem com sazonalidade múltipla, feriados e quebras estruturais. Ele brilha em séries comerciais com padrões fortes e exige pouco ajuste manual:

from prophet import Prophet

prophet_df = df.reset_index().rename(
    columns={"data": "ds", "selic": "y"}
)
modelo = Prophet(
    seasonality_mode="multiplicative",
    weekly_seasonality=False,
    daily_seasonality=False,
)
modelo.fit(prophet_df)
futuro = modelo.make_future_dataframe(periods=12, freq="MS")
previsao = modelo.predict(futuro)
modelo.plot(previsao)

A vantagem do Prophet é que ele lida com feriados brasileiros, outliers e mudanças de tendência sem exigir que você os modele explicitamente. Para feriados locais, basta passar um DataFrame com nome e data. O custo é menor controle estatístico: ele é uma caixa mais preta que o SARIMA. Para relatórios técnicos que precisam explicar cada coeficiente, Statsmodels ainda vence.

Avaliando a previsão

Nunca confie em uma previsão sem medir o erro. A prática padrão é dividir a série em treino e teste no tempo (nunca aleatoriamente, porque isso quebra a ordem). Métricas comuns são MAE (erro absoluto médio), RMSE (raiz do erro quadrático médio) e MAPE (erro percentual médio):

from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error

treino = df.iloc[:-12]
teste = df.iloc[-12:]
# ajuste no treino, preveja o período do teste, depois compare
mape = mean_absolute_percentage_error(teste["selic"], previsao_teste)
print(f"MAPE: {mape:.2%}")

Um MAPE abaixo de 10% é bom para séries econômicas; abaixo de 5% é excelente. Mas cuidado: métricas agregadas escondem erros em pontos específicos. Sempre plote a previsão contra o real, inclusive com o intervalo de confiança. O que mais mata previsões em produção não é o erro médio, mas o erro no pico — exatamente o momento (Black Friday, apagão, crise) em que a decisão é mais cara.

Armazenando o resultado

Depois de gerar a previsão, é comum persisti-la em um banco para alimentar dashboards. Para volumes pequenos e médios, o SQLite resolve; para dados volumosos com histórico longo, o PostgreSQL é mais robusto. O padrão é salvar a previsão com timestamp, valor previsto, limite inferior e limite superior, para que o dashboard mostre a faixa de incerteza e não apenas um número enganoso.

Para times que usam outras linguagens no backend, Python pode concentrar a camada analítica — ingestão, modelagem, previsão — enquanto serviços em Go servem os resultados em APIs de alta concorrência. O importante é isolar a previsão como um serviço agendado, que roda periodicamente e grava o resultado, em vez de calcular tudo sob demanda.

Cuidados em produção

Séries temporais em produção sofrem de problemas que não aparecem em tutoriais. O primeiro é o deslocamento de conceito (concept drift): o padrão que o modelo aprendeu muda com o tempo (uma pandemia, uma nova regulamentação, um concorrente). A solução é retreinar periodicamente e monitorar o erro em tempo real, com alertas quando ele ultrapassa um limite.

O segundo é o dado atrasado: a série depende de uma fonte externa que publica com defasagem. Uma previsão de inflação que usa dados do IBGE só pode rodar depois que o IBGE publica. Construa pipelines que tolerem essa defasagem e reprocessem automaticamente quando o dado chega. O nosso guia de pipelines ETL com Python mostra como orquestrar isso com Airflow ou Prefect.

O terceiro é a falsa confiança: um intervalo de 95% não significa que a previsão está certa em 95% dos casos no futuro. Significa isso apenas sob as hipóteses do modelo, que raramente se sustentam por muito tempo. Comunique incerteza com honestidade, especialmente para tomadores de decisão não técnicos.

Conclusão

Análise de séries temporais é uma das aplicações de Python com retorno mais direto para o negócio: transforma histórico em previsão, e previsão em decisão. O caminho do iniciante ao proficiente é relativamente curto porque as ferramentas são acessíveis: Pandas para preparar, Statsmodels para entender e modelar com rigor estatístico, Prophet para previsão rápida e robusta. A parte difícil não é o código, mas a disciplina de validar, monitorar e recalibrar continuamente.

Se você quer praticar, comece com dados públicos brasileiros — Selic, IPCA, câmbio, vendas do comércio varejista do IBGE — e tente prever os últimos 12 meses antes de vê-los. Compare MAPE entre SARIMA e Prophet. Quando conseguir explicar por que um vence o outro na sua série, você terá dominado o essencial. Para aprofundar a base estatística, vale estudar processos estocásticos e séries não lineares; para escalar, explore DeepAR, N-BEATS e modelos baseados em transformers que estão se tornando padrão em grandes empresas de dados.

E

Equipe python.dev.br

Contribuidor do Python Brasil — Aprenda Python em Português