Decomposição STL

Qualquer série temporal pode ser entendida como a soma de algumas peças mais simples: uma tendência subjacente, um padrão sazonal que se repete, e o que resta sem explicação. A decomposição STL (Seasonal-Trend decomposition using Loess) é a forma padrão e robusta de dividir uma série exatamente nesses três componentes. Este post se apoia nos componentes introduzidos em o que é uma série temporal?, e se combina naturalmente com a verificação de estacionariedade, já que remover a tendência e a sazonalidade costuma ser o primeiro passo rumo a uma série estacionária.

O modelo de decomposição aditiva

Cada observação é simplesmente a soma de suas três peças subjacentes:

\[y_t = T_t + S_t + R_t\]

em que \(T_t\) é o componente de tendência (o nível subjacente de evolução lenta), \(S_t\) é o componente sazonal (um padrão que se repete em um período fixo e conhecido, por exemplo a cada 12 meses), e \(R_t\) é o resíduo (o que a tendência e o componente sazonal não explicam).

Existe também uma alternativa multiplicativa, \(y_t = T_t \times S_t \times R_t\), usada quando as oscilações sazonais crescem proporcionalmente ao nível da série, em vez de permanecer em um tamanho absoluto constante. Ela é equivalente a aplicar uma decomposição aditiva em \(\log(y_t)\).

Decomposição clássica: a referência mais simples

A abordagem mais antiga, no estilo decompose(), funciona em duas etapas: estima a tendência com uma média móvel cuja janela é igual ao período sazonal, subtrai essa tendência, e então calcula a média dos valores sem tendência dentro de cada posição sazonal (por exemplo, calcular a média de todos os janeiros) para obter um único componente sazonal fixo e repetitivo. O que resta depois de remover ambos é o resíduo.

Isso tem duas fraquezas reais, que a STL corrige:

  • A estimativa de tendência perde o primeiro e o último meio período de observações, já que uma média móvel centrada precisa de dados em ambos os lados, então não pode ser calculada exatamente nas bordas da série.
  • O componente sazonal é forçado a ser perfeitamente idêntico em todo ciclo; ele não pode mudar gradualmente ao longo do tempo. Um padrão sazonal real que vai derivando lentamente, por exemplo vendas de verão se tornando cada vez mais dominantes ano após ano, não pode ser capturado de forma alguma.

STL: o que a torna diferente

A STL substitui a média móvel da tendência e a média sazonal fixa por suavização de regressão local (LOESS) para ambos os componentes, aplicada iterativamente. Isso oferece duas vantagens concretas sobre a decomposição clássica:

  • O componente sazonal pode mudar de forma lentamente ao longo do tempo, em vez de ficar congelado em um único ciclo repetitivo.
  • A STL é robusta a outliers: uma observação excepcionalmente extrema pode receber menos peso, de forma que não arraste toda a estimativa de tendência ou sazonal para fora do curso, acabando isolada no resíduo em vez disso.

Exemplo resolvido: decompondo 6 anos de vendas mensais

Simular uma série com uma tendência conhecida e uma forma sazonal conhecida embutidas permite verificar que a STL de fato recupera ambas as peças, e quão pouco sobra no resíduo.

Decompondo 6 anos de vendas mensais simuladas

Dados simulados de vendas mensais, 72 meses (6 anos), com uma tendência ascendente, um ciclo sazonal anual claro, e ruído:

set.seed(3)
n_months <- 72
t <- 1:n_months
trend <- 500 + 8*t
seasonal <- 150*sin(2*pi*t/12) + 60*cos(2*pi*t/12)
noise <- rnorm(n_months, 0, 40)
y <- trend + seasonal + noise
ts_y <- ts(y, frequency = 12, start = c(2019, 1))

fit_stl <- stl(ts_y, s.window = "periodic")

Valores reais do primeiro ano (12 meses) e os componentes STL resultantes:

Mês \(y_t\) (vendas) Tendência \(T_t\) Sazonal \(S_t\) Resíduo \(R_t\)
1 596,48 497,10 119,52 -20,14
2 664,20 505,96 152,40 5,84
3 684,35 514,82 157,62 11,91
4 585,82 523,43 97,48 -35,09
5 570,87 532,03 31,88 6,96
6 489,20 540,49 -87,32 36,04
7 432,46 548,94 -110,15 -6,34
8 448,76 557,41 -142,95 34,30
9 373,25 565,88 -164,35 -28,29
10 530,79 573,61 -91,43 48,61
11 535,17 581,35 -20,49 -25,69
12 610,75 588,52 57,77 -35,55

A identidade aditiva se mantém exatamente para o mês 1: \(497{,}10 + 119{,}52 - 20{,}14 = 596{,}48\).

Ao longo dos 72 meses completos, a tendência sobe de cerca de 497 para 1072, o componente sazonal varia de aproximadamente -164 a +158 (um ciclo anual claro, com pico por volta de março e vale por volta de setembro), e o resíduo tem desvio padrão de 31,1. A tendência e o componente sazonal juntos explicam cerca de 97,6% da variância total da série (variância do resíduo de 965,8 sobre uma variância total de 39.782,2); apenas 2,4% permanece como ruído não explicado, confirmando que a decomposição capturou bem os dois padrões sistemáticos.

Example icon

Quatro painéis empilhados mostrando a série observada, a tendência, o componente sazonal e o resíduo de uma decomposição STL de vendas mensais simuladas

A tendência (verde) sobe de forma suave e constante. O componente sazonal (laranja) se repete a cada 12 meses com uma forma estável. O resíduo (cinza) não apresenta nenhum padrão visível, exatamente o que uma boa decomposição deve deixar para trás.

⚠️ Um resíduo grande ou com padrão significa que a decomposição deixou algo sem explicar

O resíduo deve parecer ruído sem estrutura: sem tendência visível, sem padrão repetitivo, sem agrupamentos de resíduos excepcionalmente grandes. Se o resíduo ainda mostra um padrão claro, uma deriva lenta que restou, ou oscilações maiores em certos meses todos os anos, isso geralmente significa que o período sazonal foi mal especificado, que há um segundo ciclo sazonal que o modelo não está capturando (por exemplo, tanto um padrão semanal QUANTO um anual em dados diários), ou que há uma quebra estrutural, uma mudança súbita de regime, que uma decomposição suave simplesmente não pode representar.

Sempre plote o resíduo e inspecione-o. Não confie apenas na aparência razoável dos painéis de tendência e sazonal.

Escolhendo a janela sazonal (s.window)

O argumento s.window controla o quanto o componente sazonal pode mudar ao longo do tempo. s.window = "periodic", usado no exemplo acima, força um padrão sazonal perfeitamente fixo e invariável, equivalente à etapa sazonal da decomposição clássica. É um padrão bom e seguro quando não há motivo para esperar que o próprio padrão sazonal evolua.

Um valor numérico ímpar, por exemplo s.window = 7, em vez disso permite que a forma sazonal derive lentamente ao longo do tempo, útil para séries em que o padrão sazonal realmente muda entre os anos.

Executando em R

Toda a decomposição acima, desde a construção do objeto ts até a extração de cada componente, se reduz a algumas chamadas de função:

ts_y <- ts(y, frequency = 12, start = c(2019, 1))

# STL decomposition
fit_stl <- stl(ts_y, s.window = "periodic")
plot(fit_stl)
components <- fit_stl$time.series   # matrix with seasonal, trend, remainder columns

# Classical decomposition for comparison
fit_classical <- decompose(ts_y, type = "additive")
plot(fit_classical)

# Seasonally adjusted series (original minus the seasonal component)
seasonally_adjusted <- ts_y - components[, "seasonal"]

💡 STL vs decomposição clássica: qual usar

Use a STL por padrão. Ela lida com um padrão sazonal que muda, é robusta a outliers, e não perde observações no início e no fim da série como a média móvel da decomposição clássica. A decomposição clássica ainda é útil hoje como uma primeira olhada simples e rápida, ou quando uma ferramenta posterior espera especificamente o seu formato de saída.

A STL lida com apenas um único período sazonal por vez (a frequency definida no objeto ts). Para séries com múltiplos ciclos sazonais, por exemplo tanto um padrão semanal quanto um anual em dados diários, use mstl() do pacote forecast, que estende a STL para múltiplas sazonalidades.