/// <summary> /// Normalize a signal using standard score. /// </summary> /// <param name="source">Input stream</param> /// <param name="window">Normalization window</param> /// <returns>Normalized signal</returns> public static IStreamable <Empty, Signal> Normalize( this IStreamable <Empty, Signal> source, long window ) { return(source .AttachAggregate( s => s.Select(e => e.val), w => w.JoinedAggregate( w.Average(e => e), w.StandardDeviation(e => e), (avg, std) => new { avg, std } ), (signal, agg) => new Signal(signal.ts, (float)((signal.val - agg.avg) / agg.std)), window, window, window - 1 ) ); }
/// <summary> /// Peak detection query. /// </summary> /// <param name="source">Input stream</param> /// <param name="period">Period of input stream</param> /// <param name="window">Mean window</param> /// <param name="factor">Scaling factor</param> /// <returns>Peaks in the signal stream</returns> public static IStreamable <Empty, Signal> Peaks( this IStreamable <Empty, Signal> source, long period, float factor, long window = 400 ) { return(source .AttachAggregate( s => s.Select(e => e.val), w => w.Average(e => e * factor), (signal, avg) => new { signal, avg }, 2 * window, 1, window ) .Where(e => e.signal.val > e.avg) .Select(e => e.signal) .StitchAggregate(w => w.TopK(e => e.val, 1)) .Select(e => e.First().Payload) .AlterEventDuration(period) ); }
/// <summary> /// Fill missing values with mean of historic values. /// </summary> /// <param name="source">Input stream</param> /// <param name="window">Mean window</param> /// <param name="period">Period of input signal stream</param> /// <param name="gap_tol">Gap tolerance</param> /// <param name="offset">Offset</param> /// <returns>Signal after missing values filled with `val`</returns> public static IStreamable <Empty, Signal> FillMean( this IStreamable <Empty, Signal> source, long window, long period, long gap_tol, long offset = 0 ) { return(source .AttachAggregate(s => s, w => w.Average(e => e.val), (signal, avg) => new { signal, avg, sqd = (signal.val - avg) * (signal.val - avg) }, window, window, window - 1) .AlterEventDuration(period) .Chop(offset, period, gap_tol) .Select((vs, s) => new { s.signal, s.avg, new_ts = vs }) .Select(e => (e.signal.ts == e.new_ts) ? e.signal : new Signal(e.new_ts, e.avg)) .AlterEventDuration(period) ); }