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
You seem to simply look for slope inversion (from positive to negative and vice versa). A rough java algo could be (not tested):
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:
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).
If you want something statistically more sound, you could measure the cross correlation between the two series. You can check Wikipedia, or this site.
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.
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.
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.
http://haifengl.github.io/
The peakdet algorithm as proposed by Eli Billauer works very well and is easy to implement:
http://www.billauer.co.il/peakdet.html
The algorithm works especially well with noisy signals where methods using the first derivative fail.
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).