public void smoothSeasonal(int width, bool restoreEndPoints) { // Ensure that LOESS smoother width is odd and >= 3. width = Math.Max(3, width); if (width % 2 == 0) { width++; } // Quadratic smoothing of the seasonal component. // Do NOT perform linear interpolation between smoothed points - the quadratic spline can accommodate // sharp changes and linear interpolation would cut off peaks/valleys. LoessBuilder builder = new LoessBuilder(); builder.Width = width; builder.Degree = 2; builder.Jump = 1; builder.Data = this.fSeasonal; LoessSmoother seasonalSmoother = builder.build(); double[] smoothedSeasonal = seasonalSmoother.smooth(); // TODO: Calculate the variance reduction in smoothing the seasonal. // Update the seasonal with the smoothed values. // TODO: This is not very good - it causes discontinuities a the endpoints. // Better to transition to linear in the last half-smoother width. // Restore the end-point values as the smoother will tend to over-modify these. double s0 = this.fSeasonal[0]; double sN = this.fSeasonal[(this.fSeasonal.Length - 1)]; //System.arraycopy(smoothedSeasonal, 0, this.fSeasonal, 0, smoothedSeasonal.Length) this.fSeasonal = new double[smoothedSeasonal.Length]; for (int i = 0; i < smoothedSeasonal.Length; i++) { this.fSeasonal[i] = smoothedSeasonal[i]; } if (restoreEndPoints) { this.fSeasonal[0] = s0; this.fSeasonal[this.fSeasonal.Length - 1] = sN; } for (int i = 0; (i < smoothedSeasonal.Length); i++) { fResiduals[i] = fData[i] - fTrend[i] - fSeasonal[i]; } }
public CyclicSubSeriesSmoother(int width, int degree, int jump, int dataLength, int periodicity, int numPeriodsToExtrapolateBackward, int numPeriodsToExtrapolateForward) { this.fWidth = width; this.fLoessSmootherFactory = new LoessBuilder(); this.fLoessSmootherFactory.Width = width; this.fLoessSmootherFactory.Jump = jump; this.fLoessSmootherFactory.Degree = degree; this.fPeriodLength = periodicity; this.fNumPeriods = (dataLength / periodicity); this.fRemainder = (dataLength % periodicity); this.fNumPeriodsToExtrapolateBackward = numPeriodsToExtrapolateBackward; this.fNumPeriodsToExtrapolateForward = numPeriodsToExtrapolateForward; this.fRawCyclicSubSeries = new double[periodicity][]; this.fSmoothedCyclicSubSeries = new double[periodicity][]; this.fSubSeriesWeights = new double[periodicity][]; // Bookkeeping: Write the data length as // // n = m * periodicity + r // // where r < periodicity. The first r sub-series will have length m + 1 and the remaining will have length m. // Another way to look at this is that the cycle length is // // cycleLength = (n - p - 1) / periodicity + 1 // // where p is the index of the cycle that we're currently in. for (int period = 0; (period < periodicity); period++) { int seriesLength = (period < fRemainder) ? (fNumPeriods + 1) : fNumPeriods; this.fRawCyclicSubSeries[period] = new double[seriesLength]; this.fSmoothedCyclicSubSeries[period] = new double[(this.fNumPeriodsToExtrapolateBackward + (seriesLength + this.fNumPeriodsToExtrapolateForward))]; this.fSubSeriesWeights[period] = new double[seriesLength]; } }
public SeasonalTrendLoess(double[] data, int periodicity, int ni, int no, LoessSettings seasonalSettings, LoessSettings trendSettings, LoessSettings lowpassSettings) { this.fData = data; int size = data.Length; this.fPeriodLength = periodicity; this.fSeasonalSettings = seasonalSettings; this.fTrendSettings = trendSettings; this.fLowpassSettings = lowpassSettings; this.fInnerIterations = ni; this.fRobustIterations = no; this.fLoessSmootherFactory = new LoessBuilder(); this.fLoessSmootherFactory.Width = this.fTrendSettings.Width; this.fLoessSmootherFactory.Degree = this.fTrendSettings.Degree; this.fLoessSmootherFactory.Jump = this.fTrendSettings.Jump; this.fLowpassLoessFactory = new LoessBuilder(); this.fLowpassLoessFactory.Width = this.fLowpassSettings.Width; this.fLowpassLoessFactory.Degree = this.fLowpassSettings.Degree; this.fLowpassLoessFactory.Jump = this.fLowpassSettings.Jump; // var builder = new CyclicSubSeriesSmootherBuilder(); builder.Width = seasonalSettings.Width; builder.Degree = seasonalSettings.Degree; builder.Jump = seasonalSettings.Jump; builder.DataLength = size; builder.extrapolateForwardAndBack(1); builder.Periodicity = periodicity; /// this.fCyclicSubSeriesSmoother = builder.build(); this.fDetrend = new double[size]; this.fExtendedSeasonal = new double[(size + (2 * this.fPeriodLength))]; }