(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Como detetar anomalias em séries temporais em Python

João Barros 24 de July de 2026 6 min de leitura

Este tutorial mostra como detetar anomalias em séries temporais em Python usando a abordagem simples e eficaz da média móvel e do desvio-padrão. É uma técnica útil quando queres rapidamente sinalizar picos ou quedas inesperadas em métricas como tráfego, vendas diárias ou leituras de sensores IoT. A vantagem deste método é que é explicável, rápido de executar e facilita iterações rápidas sobre parâmetros como a janela (window) e o limiar (k).

Pré-requisitos

  • Python 3.8+
  • pandas, numpy (pip install pandas numpy)
  • matplotlib para visualização (opcional)

Para reproduzir os exemplos garante que tens um ambiente com estas bibliotecas. Se tens dados horários, pensa em usar janelas como 24 ou 24*7; para dados diários, janelas de 7, 14 ou 30 são comuns. A escolha afeta a sensibilidade: janelas maiores tornam o detector menos sensível a flutuações de curto prazo.

Passo 1: Preparar dados para detetar anomalias em séries temporais

Os dados devem ter um carimbo temporal (timestamp) e uma coluna de valores. Vejamos um exemplo sintético com 200 observações diárias, onde introduzimos duas anomalias pontuais para validar o método. Também mostro como ler um ficheiro CSV com timestamps caso trabalhes com dados reais.

import numpy as np
import pandas as pd

# Exemplo sintético
np.random.seed(0)
dates = pd.date_range('2023-01-01', periods=200, freq='D')
values = 50 + np.cumsum(np.random.normal(0, 0.5, size=len(dates)))
# introduzir anomalias: pico positivo e negativo
values[30] += 8   # pico acentuado
values[120] -= 10 # queda acentuada

df = pd.DataFrame({'timestamp': dates, 'value': values})
df = df.set_index('timestamp')

# Se ler de CSV:
# df = pd.read_csv('dados.csv', parse_dates=['timestamp']).set_index('timestamp')

Neste exemplo temos 200 pontos; em cenários reais é comum ter milhares ou milhões de pontos. Testa primeiro com um subconjunto para ajustar parâmetros antes de aplicar ao dataset completo.

Passo 2: Calcular média móvel e desvio para detetar anomalias em séries temporais

Usamos uma janela rolling para obter a média móvel e o desvio-padrão. A janela deve refletir a escala temporal da variação normal: para dados diários, 7 captura flutuações semanais, 14 suaviza mais e 30 capta tendência mensal. O limiar k multiplica o desvio; k=3 é clássico (aproximadamente 99.7% para distribuição normal), k=2 é mais sensível.

window = 14  # exemplo: 14 dias
k = 3        # limiar: k * std (padrão é 3)

rolling_mean = df['value'].rolling(window=window, center=False).mean()
rolling_std = df['value'].rolling(window=window, center=False).std()

df['rolling_mean'] = rolling_mean
df['rolling_std'] = rolling_std

Nota: os primeiros window-1 pontos terão NaN nas métricas. Para produção podes usar .fillna(method='bfill') ou começar a deteção apenas após a janela estar preenchida.

Passo 3: Marcar anomalias simples com z-score rolante

Marcamos como anomalias os pontos fora de mean ± k * std. Esta regra funciona bem quando os resíduos aproximam-se de uma distribuição normal e não existe forte tendência não removida.

df['z_score'] = (df['value'] - df['rolling_mean']) / df['rolling_std']

def flag_anomaly(z, threshold=k):
    if pd.isna(z):
        return False
    return abs(z) > threshold

df['anomaly'] = df['z_score'].apply(lambda z: flag_anomaly(z, k))

Em termos práticos, com k=3 esperas um número pequeno de anomalias (por exemplo, <1% dos pontos) se os dados forem estáveis. Se obtiveres muitas anomalias, considera aumentar k ou aumentar a janela.

Passo 4: Lidar com tendência e sazonalidade antes de detetar anomalias

Se a série tem tendência forte ou sazonalidade, o método direto pode sinalizar esses efeitos como anomalias. Duas abordagens simples: (1) diferenciar a série para remover tendência de nível (diferenciação), ou (2) usar medidas robustas como rolling median e IQR para reduzir influência de outliers.

# Detrending simples por diferença
df['value_diff'] = df['value'].diff()
# Recalcular métricas sobre a série diferenciada (omitindo o primeiro NaN)
rolling_mean_diff = df['value_diff'].rolling(window=window).mean()
rolling_std_diff = df['value_diff'].rolling(window=window).std()

df['z_score_diff'] = (df['value_diff'] - rolling_mean_diff) / rolling_std_diff

df['anomaly_diff'] = df['z_score_diff'].abs() > k

Exemplo prático: numa série com tendência ascendente contínua, a diferenciação normalmente reduz deteções falsas. Outra alternativa é usar rolling median e o intervalo interquartil (IQR) para definir limites robustos se tens muitos outliers.

Passo 5: Visualizar para confirmar e ajustar parâmetros

Visualizar ajuda a ajustar window e k. Desenha a série, a média móvel e marca as anomalias detectadas. Ajusta até que as anomalias identificadas façam sentido para o contexto do negócio (por exemplo, picos de tráfego devido a campanhas são esperados).

import matplotlib.pyplot as plt

plt.figure(figsize=(10,4))
plt.plot(df.index, df['value'], label='value')
plt.plot(df.index, df['rolling_mean'], label=f'rolling_mean ({window})', alpha=0.8)
plt.scatter(df.index[df['anomaly']], df['value'][df['anomaly']], color='red', label='anomaly')
plt.legend()
plt.tight_layout()
plt.show()

Verificar o resultado

Confirma que as anomalias fazem sentido: conta-as, examina excertos e compara com a versão diferenciada. No nosso exemplo sintético, onde introduzimos duas anomalias pontuais (índices 30 e 120), espera-se que o método detecte essas duas quando a janela e k estiverem bem escolhidos. Se detectares falsos positivos, ajusta k ou usa uma abordagem robusta.

# Contagem e amostra de anomalias
print('Total anomalias (raw):', df['anomaly'].sum())
print(df[df['anomaly']].head())

# Comparar com abordagem diferenciada
print('Total anomalias (diff):', df['anomaly_diff'].sum())

# Guardar para análise
# df.to_csv('detected_anomalies.csv')

Conclusão

A técnica de média móvel + desvio-padrão é um bom ponto de partida para detetar anomalias em séries temporais em Python: é simples, explicável e rápida de correr em datasets moderados. Próximos passos possíveis incluem métodos robustos (rolling median + IQR), modelos estatísticos (ARIMA/ETS) para séries com padrão, ou algoritmos de machine learning (IsolationForest, autoencoders) para padrões mais complexos. Dica prática: começa por ajustar window e k, faz validação manual sobre amostras e visualiza sempre os resultados — muitas vezes 1 ou 2 iterações bastam para convergir para valores úteis para o teu caso.