我目前正在开展一个小项目,我想比较两个时间序列。相似性度量实际上是模糊的,如果两个时间序列大致具有相同的形状,则它们被认为是相似的。
所以我对自己说“好吧,如果它们只需要具有相同的形状,我只是比较两个时间序列的峰值,如果峰值位于相同的位置,那么时间序列肯定会相似“
我现在的问题是为峰值检测找到一个好的算法。我用谷歌,但我只想出了论文Simple Algorithms for Peak Detection in Time-Series。问题是,本文中描述的算法适用于非常极端和薄的峰值,但在大多数情况下,我的time-series具有相当平坦的峰值,因此不会被检测到。
有人知道我在哪里可以找到或搜索能够检测下图所示峰值的算法吗?
答案 0 :(得分: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个资产回报之间的相关性,例如:
您可以阅读有关returns correlations here for example的更多信息。总之,如果您的值是:
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条曲线看起来相似的程度。您可能希望调整方差的结果(即,如果一个形状的范围比另一个更宽)。
答案 1 :(得分:5)
您可以使用非常简单的局部极值检测器:
// 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 :(得分:3)
Eli Billauer提出的peakdet算法运行良好,易于实现:
http://www.billauer.co.il/peakdet.html
该算法特别适用于噪声信号,其中使用一阶导数的方法失败。
答案 3 :(得分:1)
答案 4 :(得分: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值作为double的类。这很容易被等同的东西取代。
答案 5 :(得分:0)
该问题的最新答案,但动态时间规整(DTW)算法是解决此类问题的正确选择。基本上有两个时间序列,其中一个是模板,另一个是样本。我建议检查Smile库DynamicTimeWarping类的源代码。