(Cette réponse répond à une question en double (maintenant fermée) de Détecter les événements en attente , qui présente certaines données sous forme graphique.)
La détection des valeurs aberrantes dépend de la nature des données et de ce que vous êtes prêt à assumer à leur sujet. Les méthodes générales reposent sur des statistiques robustes. L'esprit de cette approche est de caractériser la majeure partie des données de manière à ne pas être influencé par des valeurs aberrantes, puis de pointer sur des valeurs individuelles ne correspondant pas à cette caractérisation.
Comme il s’agit d’une série chronologique, le fait de devoir (re) détecter les valeurs aberrantes sur une base continue s’ajoute à la complication. Si cela doit être fait au fur et à mesure du déroulement de la série, nous ne pouvons utiliser que des données plus anciennes pour la détection, pas des données futures! De plus, comme protection contre les nombreux tests répétés, nous voudrions utiliser une méthode qui a un très faible taux de faux positifs.
Ces considérations suggèrent d'exécuter un test des valeurs aberrantes de fenêtre mobile simple et robuste sur les données . Il existe de nombreuses possibilités, mais une solution simple, facile à comprendre et à appliquer est basée sur un MAD en cours: déviation absolue médiane par rapport à la médiane. Il s'agit d'une mesure fortement robuste de la variation dans les données, semblable à un écart-type. Un pic éloigné serait supérieur de plusieurs DAM ou plus à la médiane.
Il reste encore quelques ajustements à faire : quelle déviation par rapport au volume de données devrait être considérée comme dépassée et combien de temps dans le passé faut-il regarder? Laissons cela comme paramètres d'expérimentation. Voici une R
implémentation appliquée aux données (avec pour émuler les données) avec les valeurs correspondantes :n = 1150 yx=(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)
Appliqué à un ensemble de données tel que la courbe rouge illustrée dans la question, il produit ce résultat:
Les données sont affichées en rouge, la fenêtre de 30 jours des seuils médians + 5 * MAD en gris et les valeurs aberrantes - qui sont simplement les valeurs de données situées au-dessus de la courbe grise - en noir.
(Le seuil ne peut être calculé qu'à partir de la fin de la fenêtre initiale. Le premier seuil est utilisé pour toutes les données de cette fenêtre initiale: c'est pourquoi la courbe grise est plate entre x = 0 et x = 30.)
Les effets de la modification des paramètres sont (a) l’augmentation de la valeur de window
aura tendance à lisser la courbe grise et (b) l’augmentation de threshold
la valeur de la courbe grise. Sachant cela, on peut prendre un segment initial des données et identifier rapidement les valeurs des paramètres qui séparent le mieux les pics périphériques du reste des données. Appliquez ces valeurs de paramètre à la vérification du reste des données. Si un graphique montre que la méthode se détériore avec le temps, cela signifie que la nature des données change et que les paramètres peuvent nécessiter un réajustement.
Notez que cette méthode suppose peu de choses sur les données: elles ne doivent pas nécessairement être distribuées normalement; ils ne doivent présenter aucune périodicité; ils n'ont même pas besoin d'être non négatif. Tout ce qu’elle suppose, c’est que les données se comportent de manière raisonnablement similaire dans le temps et que les pics périphériques sont visiblement plus élevés que le reste des données.
Si quelqu'un souhaite expérimenter (ou comparer une autre solution à celle proposée ici), voici le code que j'ai utilisé pour produire des données telles que celles présentées dans la question.
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