(Этот ответ ответил на дублирующий (теперь закрытый) вопрос в разделе «Обнаружение выдающихся событий» , в котором некоторые данные представлены в графической форме.)
Обнаружение выбросов зависит от характера данных и того, что вы готовы предположить о них. Методы общего назначения опираются на надежную статистику. Суть этого подхода состоит в том, чтобы характеризовать большую часть данных таким образом, чтобы на них не влияли какие-либо выбросы, а затем указывать на любые отдельные значения, которые не вписываются в эту характеристику.
Поскольку это временной ряд, он добавляет усложнение необходимости (повторного) обнаружения выбросов на постоянной основе. Если это будет сделано по мере развертывания серии, то нам разрешено использовать только старые данные для обнаружения, а не будущие данные! Более того, в качестве защиты от множества повторных тестов мы хотели бы использовать метод с очень низким уровнем ложных срабатываний.
Эти соображения предполагают проведение простого и надежного теста выброса скользящего окна над данными . Существует много возможностей, но одна простая, легко понимаемая и легко реализуемая основывается на запущенном MAD: медиана абсолютного отклонения от медианы. Это очень надежная мера вариации в данных, сродни стандартному отклонению. Внешний пик будет на несколько MAD или больше, чем медиана.
Предстоит еще кое-что настроить : насколько сильно отклонение от объема данных следует считать отдаленным и как далеко назад следует смотреть во времени? Давайте оставим их в качестве параметров для экспериментов. Вот R
реализация, примененная к данным (с для эмуляции данных) с соответствующими значениями :п = 1150 г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)
Применительно к набору данных, подобному красной кривой, показанной в вопросе, он дает такой результат:
Данные показаны красным, 30-дневное окно средних пороговых значений + 5 * MAD серым цветом, а выбросы - просто значения данных над серой кривой - черным.
(Порог можно рассчитать только начиная с конца начального окна. Для всех данных в этом начальном окне используется первый порог: именно поэтому серая кривая является плоской между x = 0 и x = 30.)
Эффекты изменения параметров следующие : (а) увеличение значения window
приведет к сглаживанию серой кривой и (б) увеличение threshold
приведет к повышению серой кривой. Зная это, можно взять начальный сегмент данных и быстро определить значения параметров, которые лучше всего отделяют отдаленные пики от остальной части данных. Примените эти значения параметров для проверки остальных данных. Если график показывает, что метод со временем ухудшается, это означает, что характер данных меняется, и параметры могут нуждаться в перенастройке.
Обратите внимание, как мало этот метод предполагает для данных: они не должны быть нормально распределены; им не нужно показывать какую-либо периодичность; они даже не должны быть неотрицательными. Все это предполагает, что данные ведут себя схожим образом с течением времени и что внешние пики заметно выше, чем остальные данные.
Если кто-то захочет поэкспериментировать (или сравнить какое-либо другое решение с предложенным здесь), вот код, который я использовал для получения данных, подобных тем, которые показаны в вопросе.
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