Given a Deedle Series with time as the row index, I need to find the time at which the signal first satisfies a condition (in this case, stays below 0.005 for 50ms).
Currently I take a 50ms moving window and create a series from the start time and maximum value of each window, then get the first one whose max is < 0.005. It works well enough but can be very inefficient.
// Assume a timestep of 1ms
int numSteps = 50;
// Create a series from the first index and max of each window
var windowMaxes = mySeries.WindowInto(
numSteps,
s => new KeyValuePair<double, double>(s.FirstKey(), s.Max()));
var zeroes = windowMaxes.Where(kvp => kvp.Value <= 0.005);
// Set to -1 if the condition was never satisfied
var timeOfZero = zeroes.KeyCount > 0 ? zeroes.FirstKey() : -1D;
The problem is that it searches the entire series (which can get very large) even if the first window meets the condition.
Is there a simply way to do this but stop when the first window is found, instead of searching the entire Series?
Well I couldn't find a Deedly one-liner or any handy LINQ commands to do it, so I wrote the following extension method:
public static K FirstWindowWhere<K, V>(
this Series<K, V> series,
Func<V, bool> condition,
int windowSize)
{
int consecutiveTrues = 0;
foreach (var datum in series.Observations)
{
if (condition(datum.Value))
{
consecutiveTrues++;
}
else
{
consecutiveTrues = 0;
}
if (consecutiveTrues == windowSize)
{
return datum.Key;
}
}
return default(K);
}
To call with my above condition:
double zeroTime = mySeries.FirstWindowWhere(d => d <= 0.005, numSteps);
I tried a few different methods including a nice elegant one that used Series.Between
instead of Series.GetObservations
but it was noticeably slower. So this will do unless someone has a simpler/better solution.