14

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

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

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

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

时间序列

4

6 回答 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 个资产回报之间的相关性:

  • 创建 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%

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

于 2012-09-03T15:24:06.077 回答
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 是窗口大小)。

于 2012-09-03T15:50:39.490 回答
2

Eli Billauer 提出的 peakdet 算法运行良好且易于实现:

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

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

于 2012-10-12T08:17:15.513 回答
1

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

于 2012-09-03T15:59:22.237 回答
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 值作为双精度值的类。这可以很容易地用等效的东西代替。

于 2014-11-10T20:11:48.060 回答
0

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

http://haifengl.github.io/

于 2019-12-30T19:45:55.770 回答