public SeasonalTrendLoess buildSmoother(double[] data) { this.sanityCheck(data); if (this.fPeriodic) { this.fSeasonalWidth = (100 * data.Length); this.fSeasonalDegree = 0; } else if (this.fSeasonalDegree == 0) { this.fSeasonalDegree = 1; } var seasonalSettings = buildSettings(this.fSeasonalWidth, this.fSeasonalDegree, this.fSeasonalJump); if (this.fFlatTrend) { this.fTrendWidth = 100 * this.fPeriodLength * data.Length; this.fTrendDegree = 0; } else if (this.fLinearTrend) { this.fTrendWidth = 100 * this.fPeriodLength * data.Length; this.fTrendDegree = 1; } else if (this.fTrendDegree == 0) { this.fTrendDegree = 1; } if ((this.fTrendWidth == 0)) { this.fTrendWidth = SeasonalTrendLoessBuilder.calcDefaultTrendWidth(this.fPeriodLength, this.fSeasonalWidth); } LoessSettings trendSettings = this.buildSettings(this.fTrendWidth, this.fTrendDegree, this.fTrendJump); if ((this.fLowpassWidth == 0)) { this.fLowpassWidth = this.fPeriodLength; } LoessSettings lowpassSettings = this.buildSettings(this.fLowpassWidth, this.fLowpassDegree, this.fLowpassJump); var stl = new SeasonalTrendLoess(data, this.fPeriodLength, this.fInnerIterations, this.fRobustIterations, seasonalSettings, trendSettings, lowpassSettings); return(stl); }
/// <summary> /// Perform STL time series decomposition /// </summary> /// <param name="values"></param> /// <param name="sWindow"></param> /// <param name="sDegree"></param> /// <param name="tWindow"></param> /// <param name="tDegree"></param> /// <param name="lWindow"></param> /// <param name="lDegree"></param> /// <param name="sJump"></param> /// <param name="tJump"></param> /// <param name="lJump"></param> /// <param name="isRobust"></param> /// <param name="inner"></param> /// <param name="outer"></param> /// <returns>Tuple of the three components</returns> public static (double[] Trend, double[] Seasonal, double[] Residual) Fit(double[] values, int sWindow, int sDegree = 0, int tWindow = 0, int tDegree = 1, int lWindow = 0, int lDegree = 0, int sJump = 0, int tJump = 0, int lJump = 0, bool isRobust = false, int inner = 0, int outer = 0) { var builder = new SeasonalTrendLoessBuilder(); builder.PeriodLength = sWindow; // Data has a period of 12 builder.Periodic = sWindow > 0; // setSeasonalWidth(12). // Monthly data smoothed over 35 years builder.Robust = isRobust; // Not expecting outliers, so no robustness iterations var smoother = builder.buildSmoother(values); var stl = smoother.decompose(); return(stl.Trend, stl.Seasonal, stl.Residual); }
public static Decomposition performRobustPeriodicDecomposition(double[] data, int periodicity) { // The LOESS interpolator with degree 0 and a very long window (arbitrarily chosen to be 100 times the length of // the array) will interpolate all points as the average value of the series. This particular setting is used // for smoothing the seasonal sub-cycles, so the end result is that the seasonal component of the decomposition // is exactly periodic. // This fit is for diagnostic purposes, so we just do a single inner and outer iteration. var stlBuilder = new SeasonalTrendLoessBuilder(); stlBuilder.PeriodLength = periodicity; stlBuilder.SeasonalWidth = 100 * data.Length; stlBuilder.SeasonalDegree = 0; stlBuilder.InnerIterations = 1; stlBuilder.RobustIterations = 1; var stl = stlBuilder.buildSmoother(data); return(stl.decompose()); }