时间序列中的峰值检测

14

我目前正在一个小项目中,想要比较两个时间序列的相似度。相似性测量非常模糊,如果这两个时间序列大致相同,则认为它们是相似的。

所以我想:“如果它们只需要具有相同的形状,那么我只需要比较两个时间序列的峰值,如果峰值位于同一位置,那么这两个时间序列肯定是相似的。”

我的问题现在是找到一种好的峰值检测算法。我用了谷歌搜索,但只找到了这篇论文:Simple Algorithms for Peak Detection in Time-Series。问题在于,该论文中描述的算法适用于极端和尖锐的峰值,但在大多数情况下,我的时间序列具有相当平坦的峰值,因此不会被检测到。

有人知道我可以在哪里找到或搜索能检测以下图像中显示的峰值的算法吗?

time-series


我的高中数学有点模糊,但你不想计算滚动的一阶(或者可能是二阶,考虑到平坦性)导数,然后找到变化吗? - Matt Mitchell
我相信ZigZag指标对您非常有用 http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:zigzag - Adrian
6个回答

7
您似乎只是寻找斜率反转(从正数到负数,反之亦然)。一个简单的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;
}

最后,衡量相似性的好方法是相关性。在您的情况下,我会查看%移动相关性(换句话说,您希望您的两个系列同时上涨或下跌)- 这通常是金融领域中所做的,例如计算两种资产回报之间的相关性:

  • 创建2个新系列,其中包含2个系列每个点的%移动
  • 计算这两个系列之间的相关性

您可以在此处阅读有关回报相关性的更多信息。总之,如果您的价值为:

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

“returns”系列将包括:
Series 1  Series 2
 -2.00%     -2.00%
 +2.04%     +6.12%
 +2.00%     +3.85%

你需要计算这两个回报系列的相关性(在本例中为0.96),以衡量这两个曲线有多相似。你可能需要调整方差的结果(即,如果一个形状的范围比另一个宽得多)。


谢谢您的建议,寻找符号变化是一个非常好而简单的想法。但是我有一个问题:您的第二个想法听起来非常有趣,但我不太明白它是如何实现的。您知道在哪里可以找到更多关于它的信息吗? - IdealOutage
考虑到他提供的示例数据,相关性将是一个非常好的检测器。+1 - brimborium

6
您可以使用一个非常简单的本地极值检测器:
// 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是窗口大小)。


2

Eli Billauer提出的peakdet算法非常有效且易于实现:

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

该算法在处理噪声信号时表现尤为优秀,而使用一阶导数的方法则会失败。


2
尽管该问题被标记为Java,但OP确实要求一种允许在时间序列中找到峰值的算法。他并没有特别要求Java实现。该文章详细解释了matlab实现,但也链接到了C、Python和Fortran的实现。如果有人期望一些复制粘贴的解决方案,我很抱歉会误导他们。 - Lars Frische

1

如果你想要更加统计学上可靠的结果,你可以测量这两个序列之间的交叉相关性。你可以查看维基百科或者这个网站


谢谢提供链接,看起来非常有趣。 - IdealOutage

1
我不确定时间序列或特定峰值检测算法之间的相关性,但这里是我编写的一个小型最大峰值检测算法。它不能检测最小峰值,但可以通过反转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值的双精度类,可以轻松替换为等效内容。

0
虽然这是一个晚回答,但动态时间规整(DTW)算法是解决这类问题的正确选择。基本上有两个时间序列,其中一个是模板,另一个是样本。我建议查看Smile库中DynamicTimeWarping类的源代码。

http://haifengl.github.io/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接