(Questa risposta ha risposto a una domanda duplicata (ora chiusa) in Rilevamento di eventi in sospeso , che ha presentato alcuni dati in forma grafica.)
Il rilevamento anomalo dipende dalla natura dei dati e da ciò che si è disposti ad assumere su di essi. I metodi per scopi generici si basano su statistiche affidabili. Lo spirito di questo approccio è quello di caratterizzare la maggior parte dei dati in un modo che non è influenzato da alcun outlier e quindi indicare eventuali valori individuali che non rientrano in quella caratterizzazione.
Poiché si tratta di una serie temporale, aggiunge la complicazione della necessità di (ri) rilevare valori anomali su base continuativa. Se questo deve essere fatto man mano che la serie si svolge, allora ci è permesso solo di utilizzare dati più vecchi per il rilevamento, non dati futuri! Inoltre, come protezione contro i numerosi test ripetuti, vorremmo utilizzare un metodo con un tasso di falsi positivi molto basso.
Queste considerazioni suggeriscono di eseguire un semplice e robusto test di valori anomali della finestra mobile sui dati . Ci sono molte possibilità, ma una semplice, facilmente comprensibile e facilmente implementabile si basa su una MAD in esecuzione: deviazione assoluta mediana dalla mediana. Questa è una misura fortemente robusta di variazione all'interno dei dati, simile a una deviazione standard. Un picco esterno sarebbe più MAD o maggiore della mediana.
R
x=(1,2,…,n)n=1150y
# Parameters to tune to the circumstances:
window <- 30
threshold <- 5
# An upper threshold ("ut") calculation based on the MAD:
library(zoo) # rollapply()
ut <- function(x) {m = median(x); median(x) + threshold * median(abs(x - m))}
z <- rollapply(zoo(y), window, ut, align="right")
z <- c(rep(z[1], window-1), z) # Use z[1] throughout the initial period
outliers <- y > z
# Graph the data, show the ut() cutoffs, and mark the outliers:
plot(x, y, type="l", lwd=2, col="#E00000", ylim=c(0, 20000))
lines(x, z, col="Gray")
points(x[outliers], y[outliers], pch=19)
Applicato a un set di dati come la curva rossa illustrata nella domanda, produce questo risultato:
I dati sono mostrati in rosso, la finestra di 30 giorni della soglia mediana + 5 * soglie in grigio in grigio e gli outlier - che sono semplicemente quei valori di dati sopra la curva grigia - in nero.
(La soglia può essere calcolata solo a partire dalla fine della finestra iniziale. Per tutti i dati all'interno di questa finestra iniziale, viene utilizzata la prima soglia: ecco perché la curva grigia è piatta tra x = 0 e x = 30.)
Gli effetti della modifica dei parametri sono: (a) aumentare il valore di window
tenderà a smussare la curva grigia e (b) aumentare threshold
aumenterà la curva grigia. Sapendo questo, si può prendere un segmento iniziale dei dati e identificare rapidamente i valori dei parametri che meglio separano i picchi periferici dal resto dei dati. Applicare questi valori di parametro per controllare il resto dei dati. Se un diagramma mostra che il metodo sta peggiorando nel tempo, ciò significa che la natura dei dati sta cambiando e che i parametri potrebbero richiedere una nuova ottimizzazione.
Notare quanto poco questo metodo presuppone sui dati: non devono essere normalmente distribuiti; non hanno bisogno di esibire alcuna periodicità; non devono nemmeno essere non negativi. Tutto ciò che assume è che i dati si comportino in modi ragionevolmente simili nel tempo e che i picchi periferici siano visibilmente più alti rispetto al resto dei dati.
Se qualcuno desidera sperimentare (o confrontare qualche altra soluzione con quella offerta qui), ecco il codice che ho usato per produrre dati come quelli mostrati nella domanda.
n.length <- 1150
cycle.a <- 11
cycle.b <- 365/12
amp.a <- 800
amp.b <- 8000
set.seed(17)
x <- 1:n.length
baseline <- (1/2) * amp.a * (1 + sin(x * 2*pi / cycle.a)) * rgamma(n.length, 40, scale=1/40)
peaks <- rbinom(n.length, 1, exp(2*(-1 + sin(((1 + x/2)^(1/5) / (1 + n.length/2)^(1/5))*x * 2*pi / cycle.b))*cycle.b))
y <- peaks * rgamma(n.length, 20, scale=amp.b/20) + baseline