Pular para o conteúdo principal

Detecte anomalias com o Anomalize em R

Aprenda a detectar anomalias em grandes conjuntos de dados de séries temporais e a apresentar insights de forma muito mais simples.
Atualizado 17 de set. de 2026  · 9 min lido

Explorar com IA

ChatGPTClaudePerplexity

Anomalize: descrição breve

Quando falamos de anomalias, pensamos nos pontos de dados que são outliers ou eventos excepcionais. Identificar esses eventos é fácil em conjuntos pequenos e pode ser feito com gráficos simples, como boxplots. Mas as coisas ficam bem mais complicadas quando passamos para conjuntos grandes, especialmente no caso de séries temporais. Séries temporais são dados capturados em intervalos fixos ao longo de um período e, quando analisados, mostram tendência ou sazonalidade. Identificar anomalias nesses cenários é um desafio.

É aí que entra o pacote anomalize para detecção de anomalias em séries temporais: um algoritmo "tidy" de detecção de anomalias baseado em tempo e escalável de uma até muitas séries temporais.

Existem pacotes e métodos disponíveis que ajudam na sua construção — dá para dizer que é uma combinação de recursos existentes com uma abordagem escalável.

Os trabalhos open source que ajudaram foram:

  • Pacote AnomalyDetection do Twitter: disponível no GitHub (o cran AnomalyDetection é um trabalho diferente).
  • Função forecast::tsoutliers() do Rob Hyndman, disponível no pacote forecast.
  • Pacote tsoutliers do Javier Lopez-de-lacalle, no CRAN.

Todos esses pacotes e funções são usados para integrar um fluxo de trabalho escalável.

Falando do fluxo de trabalho do anomalize, ele é dividido em três partes:

  • Decomposição da série temporal com time_decompose().
  • Detecção de anomalias no remainder com anomalize().
  • Transformação dos limites inferior e superior de anomalias com time_recompose().

Fluxo de trabalho da detecção de anomalias

Decomposição de séries temporais

O primeiro passo é a decomposição da série temporal usando time_decompose(). O valor medido ou numérico sobre o qual a detecção precisa ser feita para um determinado grupo é decomposto em quatro colunas: observed, season, trend e remainder. O método padrão de decomposição é o stl, uma decomposição sazonal que utiliza um suavizador Loess.

A regressão Loess é o método mais comum para suavizar séries temporais voláteis. Ela ajusta várias regressões em vizinhanças locais — ou seja, os dados são segmentados e a regressão é aplicada a cada parte. Isso é útil em séries temporais porque conhecemos o limite de tempo, que é a variável X nesse caso. Esse método funciona bem quando a tendência domina a sazonalidade da série.

Aqui, tendência é o crescimento de longo prazo ao longo de muitas observações, e sazonalidade é o padrão cíclico que ocorre em um ciclo diário, por minuto ou por hora, ou semanal.

Há uma segunda técnica para decomposição sazonal em séries temporais baseada na mediana: o método do Twitter, também usado no pacote AnomalyDetection. Ele é idêntico ao STL na remoção do componente sazonal. A diferença está na remoção da tendência: usa-se a mediana por partes dos dados (uma ou várias medianas divididas em intervalos especificados) em vez de ajustar um suavizador. Esse método funciona bem quando a sazonalidade domina a tendência na série temporal.

Falando da saída da função time_decompose(), como dito acima, ela produz 4 colunas:

  • observed: os valores reais.
  • season: a sazonalidade ou padrão cíclico. O padrão é sazonalidade semanal.
  • trend: a tendência de longo prazo. O padrão é um intervalo de 3 meses.
  • remainder: usado para analisar os outliers. É simplesmente o observed menos season e trend.

A função time_decompose() contém o argumento merge; definindo-o como TRUE, mantemos os dados originais junto com as colunas geradas.

# Using data package provided in the anomalize package and taking single time series of package purrr

purrr_package = tidyverse_cran_downloads%>%
  filter(package == "purrr")%>%
  ungroup()

purrr_anomaly  = purrr_package %>%
  time_decompose(count)

purrr_anomaly%>% glimpse()
 ## Observations: 425
## Variables: 5
## $ date      <date> 2017-01-01, 2017-01-02, 2017-01-03, 2017-01-04, 201...
## $ observed  <dbl> 550, 1012, 1515, 1702, 1696, 1613, 860, 796, 2008, 2...
## $ season    <dbl> -2158.8893, 692.6571, 1087.5708, 1052.3294, 939.9377...
## $ trend     <dbl> 1496.712, 1511.009, 1525.307, 1539.604, 1553.901, 15...
## $ remainder <dbl> 1212.1777, -1191.6661, -1097.8773, -889.9334, -797.8...

Detecção de anomalias no remainder

Depois que a série temporal é analisada e o remainder tem as características desejadas para realizar a detecção, o processo cria mais três colunas.

  • remainder_l1: o limite inferior do remainder.
  • remainder_l2: o limite superior do remainder.
  • anomaly: informa se a observação é uma anomalia ou não.

Anomalias são pontos de alta alavancagem que distorcem a distribuição. O anomalize implementa dois métodos resistentes a esses pontos:

  • IQR: intervalo interquartil
  • GESD: Generalized Extreme Studentized Deviiate Test

IQR

Método semelhante ao usado na função tsoutliers() do pacote forecast. No IQR, toma-se a distribuição e os quartis de 25% e 75% para estabelecer a distribuição do remainder. Os limites são definidos, por padrão, como um fator de 3 vezes acima e abaixo do intervalo interquartil; qualquer remainder além desses limites é considerado anomalia.

GESD

No GESD, as anomalias são avaliadas progressivamente, removendo-se os piores casos e recalculando as estatísticas de teste e os valores críticos. Em outras palavras, o intervalo é recalculado de forma iterativa após identificar as anomalias.

Ambos IQR e GESD têm prós e contras. O IQR é mais rápido, pois não envolve loops, mas não é tão preciso quanto o GESD, já que as anomalias enviesam a mediana — efeito mitigado no GESD.

purrr_anomaly = purrr_anomaly%>%
  anomalize(remainder)

purrr_anomaly%>% glimpse()
## Observations: 425
## Variables: 8
## $ date         <date> 2017-01-01, 2017-01-02, 2017-01-03, 2017-01-04, ...
## $ observed     <dbl> 550, 1012, 1515, 1702, 1696, 1613, 860, 796, 2008...
## $ season       <dbl> -2158.8893, 692.6571, 1087.5708, 1052.3294, 939.9...
## $ trend        <dbl> 1496.712, 1511.009, 1525.307, 1539.604, 1553.901,...
## $ remainder    <dbl> 1212.1777, -1191.6661, -1097.8773, -889.9334, -79...
## $ remainder_l1 <dbl> -4330.511, -4330.511, -4330.511, -4330.511, -4330...
## $ remainder_l2 <dbl> 4400.459, 4400.459, 4400.459, 4400.459, 4400.459,...
## $ anomaly      <chr> "No", "No", "No", "No", "No", "No", "No", "No", "...

Transformação dos limites inferior e superior de anomalias

O último passo do fluxo é criar limites inferior e superior ao redor dos valores observados com time_recompose. Ele recompõe season, trend, remainder_l1 e remainder_l2 em novos limites, que são:

  • recomposed_l1: limite inferior de outliers ao redor dos valores observados.
  • recomposed_l2: limite superior de outliers ao redor dos valores observados.
purrr_anomaly = purrr_anomaly%>%
  time_recompose()

purrr_anomaly%>% glimpse()
## Observations: 425
## Variables: 10
## $ date          <date> 2017-01-01, 2017-01-02, 2017-01-03, 2017-01-04,...
## $ observed      <dbl> 550, 1012, 1515, 1702, 1696, 1613, 860, 796, 200...
## $ season        <dbl> -2158.8893, 692.6571, 1087.5708, 1052.3294, 939....
## $ trend         <dbl> 1496.712, 1511.009, 1525.307, 1539.604, 1553.901...
## $ remainder     <dbl> 1212.1777, -1191.6661, -1097.8773, -889.9334, -7...
## $ remainder_l1  <dbl> -4330.511, -4330.511, -4330.511, -4330.511, -433...
## $ remainder_l2  <dbl> 4400.459, 4400.459, 4400.459, 4400.459, 4400.459...
## $ anomaly       <chr> "No", "No", "No", "No", "No", "No", "No", "No", ...
## $ recomposed_l1 <dbl> -4992.689, -2126.845, -1717.634, -1738.578, -183...
## $ recomposed_l2 <dbl> 3738.281, 6604.125, 7013.336, 6992.392, 6894.298...

Plotando anomalias

purrr_anomaly%>%
  plot_anomaly_decomposition()+
  ggtitle("Plotting Anomalies")
plotando anomalias

Ajuste de parâmetros

Modelar um detector de anomalias fica incompleto sem ajustar os parâmetros — algo totalmente dependente dos dados.

Vamos ao ajuste: os parâmetros de cada etapa do fluxo são diferentes, já que cada uma executa sua própria função.

Ajustando parâmetros da decomposição

Como vimos, tendência e sazonalidade são fundamentais para decompor uma série temporal, então o ajuste também deve ser feito na frequência e na tendência da função time_decompose. Por padrão, os valores são atribuídos automaticamente: 7 dias para a frequência em ambos os métodos (STL, Twitter) e, para tendência, 91 dias no STL e 85 dias no Twitter.

Você pode ajustar um ou ambos os argumentos conforme necessário, mas observe com cuidado: mudar sem avaliar pode levar a overfitting ou underfitting no processo de decomposição.

purrr_package %>%
  time_decompose(count, frequency = "auto", trend = "2 weeks")%>%
  anomalize(remainder)%>%
  plot_anomaly_decomposition()+
  ggtitle("Trend = 2 Weeks / Frequency = auto ")
série temporal

Ajustando parâmetros para detecção de anomalias

Como dito acima, aqui a anomalia é definida a partir dos valores do remainder, calculando limites para categorizar os outliers. Os parâmetros alpha e max_anoms controlam a função anomalize(). Em resumo, o alpha controla a faixa do limite: por padrão é 0.05; diminuir seu valor aumenta a largura da faixa, dificultando que um ponto seja considerado anomalia.

purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.05)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("alpha = 0.05")
série temporal
purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.025)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("alpha = 0.025")
série temporal

O parâmetro max_anom controla a porcentagem de dados que pode ser considerada anomalia. Ele é útil quando o alpha é difícil de calibrar e você quer focar nas anomalias mais agressivas.

purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.2, max_anoms = 0.2)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("20% anomaly Allowed")
série temporal
purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.2, max_anoms = 0.05)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("5% anomaly Allowed")
série temporal

Referências

Se você quiser aprender mais sobre R, faça o curso Introduction to Time Series Analysis da DataCamp.

Confira nosso Time Series Analysis using R: Tutorial.

Tópicos
R
Ciência de dados

Cursos de R

Curso

Introdução ao R

4 h
3.1M
Domine os conceitos básicos de análise de dados em R, incluindo vetores, listas e quadros de dados, e pratique o R com conjuntos de dados reais.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado

Tutorial

Introdução a modelos não lineares e percepções usando o R

Descubra as complexidades dos modelos não lineares em comparação com os modelos lineares. Saiba mais sobre suas aplicações, limitações e como ajustá-las usando conjuntos de dados do mundo real.

Somil Asthana

11 min

Tutorial

Guia do cientista de dados para processamento de sinais

Descubra insights acionáveis ocultos em dados de sinais complexos filtrando ruídos, escolhendo visualizações apropriadas, encontrando padrões no domínio do tempo e da frequência e muito mais usando o processamento de sinais.
Amberle McKee's photo

Amberle McKee

15 min

Tutorial

Como fazer um histograma ggplot2 no R

Aprenda a criar um histograma ggplot2 no R. Crie histogramas no R com base na gramática dos gráficos.

Kevin Babitz

15 min

Tutorial

Tutorial de análise de componentes principais no R

Neste tutorial, você aprenderá a usar o R PCA (Principal Component Analysis) para extrair dados com muitas variáveis e criar visualizações para exibir esses dados.
Zoumana Keita 's photo

Zoumana Keita

15 min

Tutorial

Tutorial de regressão linear no R

Neste tutorial, você aprenderá os fundamentos de um modelo estatístico muito popular: a regressão linear.

Eladio Montero Porras

15 min

Tutorial

Testes T no tutorial do R: Saiba como realizar testes T

Determine se há uma diferença significativa entre as médias dos dois grupos usando t.test() no R.
Abid Ali Awan's photo

Abid Ali Awan

10 min

Ver MaisVer Mais