Подтвердить что ты не робот

Обнаружение пиков во временных рядах

В настоящее время я работаю над небольшим проектом, в котором я хочу сравнить два временных ряда. Мера сходства действительно расплывчата, они считаются похожими, если два временных ряда грубо имеют одинаковую форму.

Поэтому я подумал про себя: "Ну, если им нужна только одна и та же форма, я просто сравниваю пики двух временных рядов, если пики находятся в одном и том же положении, то, конечно же, временные ряды будут аналогичными"

Теперь моя проблема - найти хороший алгоритм обнаружения пика. Я использовал Google, но я только придумал бумагу Простые алгоритмы для обнаружения пиков в Time-Series. Проблема в том, что алгоритмы, описанные в этой статье, хорошо работают с действительно экстремальными и тонкими пиками, но в большинстве случаев мои временные ряды довольно плоские пики, чтобы они не были обнаружены.

Кто-нибудь знает, где я могу найти или найти алгоритм, который будет определять пики, показанные на следующем изображении?

time-series

4b9b3361

Ответ 1

Кажется, вы просто ищете инверсию наклона (от положительного к отрицательному и наоборот). Грубый java-алгоритм может быть (не проверен):

List<Point> points = ... //all the points in your curve
List<Point> extremes = new ArrayList<Point> ();
double previous = null;
double previousSlope = 0;

for (Point p : points) {
    if (previous == null) { previous = p; continue; }
    double slope = p.getValue() - previous.getValue();
    if (slope * previousSlope < 0) { //look for sign changes
        extremes.add(previous);
    }
    previousSlope = slope;
    previous = p;
}

Наконец, хороший способ измерения подобия - это корреляция. В вашем случае я бы посмотрел на корреляцию% move (другими словами, вы хотите, чтобы ваши 2 серии поднимались или опускались одновременно) - это обычно то, что делается в финансах, где вы вычисляете корреляцию между двумя доходами активов, например

  • создайте 2 новые серии с перемещением% для каждой точки 2-й серии
  • вычислить корреляцию между этими двумя сериями

Вы можете больше узнать о например, вернете корреляции. Итак, если ваши значения:

Series 1  Series 2
 100        50
 98         49
 100        52
 102        54

Серия "возвращает" будет:

Series 1  Series 2
 -2.00%     -2.00%
 +2.04%     +6.12%
 +2.00%     +3.85%

И вы вычисляете соотношение этих двух рядов возвращений (в этом примере: 0,96), чтобы получить оценку того, насколько 2 кривые выглядят одинаково. Вы можете настроить результат для дисперсии (т.е. Если одна форма имеет гораздо более широкий диапазон, чем другой).

Ответ 2

Вы можете использовать очень простой локальный детектор экстремумов:

// those are your points:
double[] f = {1, 2, 3, 4, 5, 6, 5, 4, 7, 8, 9, 3, 1, 4, 6, 8, 9, 7, 4, 1};
List<Integer> ext = new ArrayList<Integer> ();
for (int i = 0; i<f.length-2; i++) {
  if ((f[i+1]-f[i])*(f[i+2]-f[i+1]) <= 0) { // changed sign?
    ext.add(i+1);
  }
}
// now you have the indices of the extremes in your list `ext`

Это будет хорошо работать с гладкими рядами. Если у вас есть определенный вариант в ваших данных, вы должны сначала перенести его через фильтр нижних частот. Очень простая реализация фильтра нижних частот будет скользящим средним (каждая точка заменяется средним значением ближайших значений k, а k - размером окна).

Ответ 3

Алгоритм максимума, предложенный Эли Биллауэром, работает очень хорошо и легко реализуется:

http://www.billauer.co.il/peakdet.html

Алгоритм работает особенно хорошо с шумными сигналами, когда методы с использованием первой производной не работают.

Ответ 4

Если вам нужно что-то статистически более здоровое, вы можете измерить кросс-корреляцию между двумя сериями. Вы можете проверить Wikipedia или этот сайт.

Ответ 5

Я не уверен в корреляции между временными рядами или конкретными алгоритмами обнаружения пиков, но здесь есть небольшой алгоритм максимального пикового обнаружения, который я написал. Он не обнаруживает минимальные пики, но может быть легко расширен, чтобы сделать это, изменив операции в цикле for.

List<XYDataItem> maxPoints = ... //list to store the maximums
XYDataItem leftPeakPoint = new XYDataItem(0, 0);
int leftPeakPointIndex = 0;
XYDataItem rightPeakPoint = new XYDataItem(0, 0);
boolean first = true;
int index = -1;
List<XYDataItem> pointList = (List<XYDataItem>) lrpSeries.getItems();
for (XYDataItem point : pointList) {
    index++;
    if (first) {
        //initialize the first point
        leftPeakPoint = point;
        leftPeakPointIndex = index;
        first = false;
        continue;
    }
    if (leftPeakPoint.getYValue() < point.getYValue()) {
        leftPeakPoint = point;
        leftPeakPointIndex = index;
        rightPeakPoint = point;
    } else if (leftPeakPoint.getYValue() == point.getYValue()) {
        rightPeakPoint = point;
    } else {
        //determine if we are coming down off of a peak by looking at the Y value of the point before the
        //left most point that was detected as a part of a peak
        if (leftPeakPointIndex > 0) {
            XYDataItem prev = pointList.get(leftPeakPointIndex - 1);
            //if two points back has a Y value that is less than or equal to the left peak point
            //then we have found the end of the peak and we can process as such
            if (prev.getYValue() <= leftPeakPoint.getYValue()) {
                double peakx = rightPeakPoint.getXValue() - ((rightPeakPoint.getXValue() - leftPeakPoint.getXValue()) / 2D);
                maxPoints.add(new XYDataItem(peakx, leftPeakPoint.getYValue()));
            }
        }
        leftPeakPoint = point;
        leftPeakPointIndex = index;
        rightPeakPoint = point;
    }
}

Результат этого будет центрировать обнаруженный пик на плоских участках, где значение Y последовательных точек данных будет одинаковым. XYDataItem - это просто класс, который содержит значение X и Y как двойное. Это можно легко заменить чем-то эквивалентным.