| File: Deseasonality.cs | Web Access |
| Project: src\src\Microsoft.ML.TimeSeries\Microsoft.ML.TimeSeries.csproj (Microsoft.ML.TimeSeries) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; namespace Microsoft.ML.TimeSeries { internal interface IDeseasonality { /// <summary> /// Remove the seasonality component from the given time-series. /// </summary> /// <param name="values">An array representing the input time-series.</param> /// <param name="period">The period value of the time-series.</param> /// <param name="results">The de-seasonalized time-series.</param> public abstract void Deseasonality(ref double[] values, int period, ref double[] results); } internal sealed class MeanDeseasonality : IDeseasonality { private double[] _circularComponent; public void Deseasonality(ref double[] values, int period, ref double[] results) { Array.Resize(ref _circularComponent, period); var length = values.Length; // Initialize the circular component to 0. for (int i = 0; i < period; ++i) { _circularComponent[i] = 0; } // Sum up values that locate at the same position in one period. for (int i = 0; i < length; ++i) { var indexInPeriod = i % period; _circularComponent[indexInPeriod] += values[i]; } // Calculate the mean value as circular component. var cnt = (length - 1) / period; var rest = (length - 1) % period; for (int i = 0; i < period; ++i) { var lastCircle = i <= rest ? 1 : 0; _circularComponent[i] = _circularComponent[i] / (cnt + lastCircle); } // Substract the circular component from the original series. for (int i = 0; i < length; ++i) { var indexInPeriod = i % period; results[i] -= _circularComponent[indexInPeriod]; } } } internal sealed class MedianDeseasonality : IDeseasonality { private List<double>[] _subSeries; private double[] _circularComponent; public void Deseasonality(ref double[] values, int period, ref double[] results) { Array.Resize(ref _circularComponent, period); Array.Resize(ref _subSeries, period); var length = values.Length; for (int i = 0; i < period; ++i) { _subSeries[i] = new List<double>(); } // Split the original series into #period subseries. for (int i = 0; i < length; ++i) { var indexInPeriod = i % period; _subSeries[indexInPeriod].Add(values[i]); } // Calculate the median value as circular component. for (int i = 0; i < period; ++i) { _circularComponent[i] = MathUtility.QuickMedian(_subSeries[i]); } // Substract the circular component from the original series. for (int i = 0; i < length; ++i) { var indexInPeriod = i % period; results[i] -= _circularComponent[indexInPeriod]; } } } /// <summary> /// This class takes the residual component of stl decompose as the deseasonality result. /// </summary> internal sealed class StlDeseasonality : IDeseasonality { private readonly InnerStl _stl; private readonly IDeseasonality _backupFunc; public StlDeseasonality() { _stl = new InnerStl(true); _backupFunc = new MedianDeseasonality(); } public void Deseasonality(ref double[] values, int period, ref double[] results) { bool success = _stl.Decomposition(values, period); if (success) { for (int i = 0; i < _stl.Residual.Count; ++i) { results[i] = _stl.Residual[i]; } } // invoke the back up deseasonality method if stl decompose fails. else { _backupFunc.Deseasonality(ref values, period, ref results); } } } }