java 时间序列中的峰值检测

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12250134/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 08:10:09  来源:igfitidea点击:

Peak Detection in Time Series

javatime-series

提问by Sturmi12

I'm currently working on a little project in which I want to compare two time-series. The similarity measure is really vague, they are considered to be similar if the two time series roughly have the same shape.

我目前正在做一个小项目,我想在其中比较两个时间序列。相似性度量真的很模糊,如果两个时间序列的形状大致相同,则认为它们是相似的。

So I thought to myself "Well if they only need to have the same shape, I just compare the peaks of the two time-series, if the peaks are at the same position, then surely the time-series will be similar"

所以我心里想“好吧,如果他们只需要形状相同,我就比较两个时间序列的峰值,如果峰值在同一位置,那么时间序列肯定会相似”

My problem now is to find a good algorithm for the peak detection. I used google, but I only came up with the paper Simple Algorithms for Peak Detection in Time-Series. The problem is, the algorithms described in this paper work well with really extreme and thin peaks, but in the most cases, my time-serieshave rather flat peaks so they will not be detected.

我现在的问题是为峰值检测找到一个好的算法。我使用了谷歌,但我只想出了时间序列峰值检测的简单算法一文。问题是,本文中描述的算法适用于非常极端和细小的峰值,但在大多数情况下,我的时间序列具有相当平坦的峰值,因此不会被检测到。

Does anybody know where I could find or search for an algorithm which would detect the peaks shown in the following image?

有谁知道我在哪里可以找到或搜索可以检测下图所示峰值的算法?

time-series

时间序列

回答by assylias

You seem to simply look for slope inversion (from positive to negative and vice versa). A rough java algo could be (not tested):

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

Finally, a good way to measure similarity is correlation. In your case, I would look at % move correlation (in other words, you want your 2 series to go up or down at the same time) - that's typically what is done in finance where you calculate the correlation between 2 assets returns for example:

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

  • create 2 new series with the % move for each point of the 2 series
  • calculate the correlation between those 2 series
  • 为 2 个系列的每个点创建 2 个新系列,并使用 % 移动
  • 计算这两个系列之间的相关性

You can read more about returns correlations here for example. In summary, if your values are:

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

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

The "returns" series will be:

“回报”系列将是:

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

And you calculate the correlation of those 2 returns series (in this example: 0.96) to get a measure of how much the 2 curves look alike. You might want to adjust the result for variance (i.e. if one shape has a much wider range than the other).

然后计算这 2 个回报系列的相关性(在本例中为:0.96),以衡量这 2 条曲线的相似程度。您可能想要调整方差的结果(即,如果一个形状的范围比另一个大得多)。

回答by brimborium

You can use a very simple local extremes detector:

您可以使用一个非常简单的局部极值检测器:

// 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`

This will work nice with smooth series. If you have a certain variation in your data, you should put it through a low pass filter first. A very simple implementation of a low pass filter would be the moving average (every point is replaced by the average of the nearest k values, with k being the window size).

这将适用于平滑系列。如果您的数据有一定的变化,您应该先将其通过低通滤波器。低通滤波器的一个非常简单的实现是移动平均值(每个点都被最近的 k 值的平均值替换,k 是窗口大小)。

回答by Lars Frische

The peakdet algorithm as proposed by Eli Billauer works very well and is easy to implement:

Eli Billauer 提出的 peakdet 算法效果很好,并且易于实现:

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

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

The algorithm works especially well with noisy signals where methods using the first derivative fail.

该算法特别适用于噪声信号,其中使用一阶导数的方法失败。

回答by whytestalyon

I'm not sure about correlation between time series or specific peak detection algorithms but here's a little maximum peak detection algorithm I wrote. It doesn't detect the minimum peaks but could easily be extended to do so by reversing the operations in the for loop.

我不确定时间序列或特定峰值检测算法之间的相关性,但这是我编写的一个最大峰值检测算法。它不检测最小峰值,但可以通过反转 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;
    }
}

The result of this will center the detected peak on flat sections where the Y value of consecutive data points is the same. XYDataItem is just a class that contains an X and Y value as a double. This can easily be replaced with something equivalent.

这样做的结果将使检测到的峰值集中在连续数据点的 Y 值相同的平坦部分。XYDataItem 只是一个包含 X 和 Y 值作为双精度值的类。这可以很容易地用等效的东西代替。

回答by Flavio

If you want something statistically more sound, you could measure the cross correlationbetween the two series. You can check Wikipedia, or this site.

如果你想要一些统计上更合理的东西,你可以测量两个系列之间的互相关。你可以查看维基百科,或者这个站点

回答by C.T

Late answer for the question but Dynamic Time Warping (DTW) algorithm is the right choise for this type problems. Basicly there is a two time series one of them is template other one is sample. I recomment to check source code of Smile libraries DynamicTimeWarping class.

该问题的答案较晚,但动态时间扭曲 (DTW) 算法是此类问题的正确选择。基本上有两个时间序列,其中一个是模板,另一个是样本。我建议检查 Smile 库 DynamicTimeWarping 类的源代码。

http://haifengl.github.io/

http://haifengl.github.io/