Search code examples
c#time-seriesdeedle

Find the first window satisfying a condition in a Deedle Series


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?


Solution

  • 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.