Skip to content

Instantly share code, notes, and snippets.

@mvacha
Created July 21, 2017 18:01
Show Gist options
  • Save mvacha/a1315ab6a4697905441551e6d2e4e5d1 to your computer and use it in GitHub Desktop.
Save mvacha/a1315ab6a4697905441551e6d2e4e5d1 to your computer and use it in GitHub Desktop.
C# implementation of Thresholding algorithm used for peek (and through) detection from https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data/22771985
/// <summary>
/// Algo from: https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data/22771985
/// </summary>
public static (IList<double> avg, IList<double> sd, IList<int> detected)
ThresholdingAlgo(this IList<double> values, int windowSize, double threshold, double influence){
//number in constructor represent capacity, not size
var avg = new List<double>(values.Count);
var mad = new List<double>(values.Count);
var filteredValues = new List<double>(values.Count); //used for calculating avg and sd
var signals = new List<int>(values.Count);
//init lists
avg.AddRange(Enumerable.Repeat(double.NaN, values.Count));
mad.AddRange(Enumerable.Repeat(double.NaN, values.Count));
filteredValues.AddRange(values.Take(windowSize));
signals.AddRange(Enumerable.Repeat(0, values.Count));
//eval first step of running mean/mad (insert at index windowSize-1)
avg[windowSize - 1] = filteredValues.Take(windowSize).Average();
mad[windowSize - 1] = filteredValues.Take(windowSize).MedianAbsoluteDeviation(); //used mad instead of sd from original algo
for (int i = windowSize; i < values.Count; i++)
{
//adding one to mad to combat false detections when mad was really low, not part of original algo
if (Math.Abs(values[i]) > avg[i - 1] + threshold * (1 + mad[i - 1]))
{
if (values[i] > avg[i - 1])
signals[i] = 1;
else
signals[i] = -1;
filteredValues.Add(influence * values[i] + (1 - influence) * filteredValues[i - 1]);
}
else
{
filteredValues.Add(values[i]);
}
//bug - takes windowsSize + 1 value to count
//left intentionally in code to achieve same results as referential implementation/pseudocode
//should start at (i - windowSize + 1) and take only (windowSize items)
avg[i] = filteredValues.Skip(i - windowSize).Take(windowSize + 1).Average();
mad[i] = filteredValues.Skip(i - windowSize).Take(windowSize + 1).MedianAbsoluteDeviation();
}
return (avg, mad, signals);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment