private void updateSeasonalAndTrend(bool useResidualWeights) { double[] data = this.fDecomposition.fData; double[] trend = this.fDecomposition.fTrend; double[] weights = this.fDecomposition.fWeights; double[] seasonal = this.fDecomposition.fSeasonal; for (int i = 0; (i < data.Length); i++) { seasonal[i] = this.fExtendedSeasonal[this.fPeriodLength + i] - this.fDeSeasonalized[i]; trend[i] = data[i] - seasonal[i]; } // dumpDebugData("seasonal", seasonal); // dumpDebugData("trend0", trend); double[] residualWeights = useResidualWeights ? weights : null; this.fLoessSmootherFactory.Data = trend; this.fLoessSmootherFactory.ExternalWeights = residualWeights; LoessSmoother trendSmoother = this.fLoessSmootherFactory.build(); //System.arraycopy(trendSmoother.smooth(), 0, trend, 0, trend.Length); // dumpDebugData("trend", trend); var sss = trendSmoother.smooth(); for (int i = 0; i < trend.Length; i++) { trend[i] = sss[i]; } }
private void smoothOneSubSeries(double[] weights, double[] rawData, double[] smoothedData) { int cycleLength = rawData.Length; // Smooth the cyclic sub-series with LOESS and then extrapolate one place beyond each end. this.fLoessSmootherFactory.Data = rawData; this.fLoessSmootherFactory.ExternalWeights = weights; LoessSmoother smoother = this.fLoessSmootherFactory.build(); //Copy, shifting by 1 to leave room for the extrapolated point at the beginning. // System.arraycopy(smoother.smooth(), 0, smoothedData, this.fNumPeriodsToExtrapolateBackward, cycleLength); var ss = smoother.smooth(); for (int i = 0; i < cycleLength; i++) { smoothedData[this.fNumPeriodsToExtrapolateBackward + i] = ss[i]; } LoessInterpolator interpolator = smoother.Interpolator; // Extrapolate from the leftmost "width" points to the "-1" position int left = 0; int right = (left + (this.fWidth - 1)); right = Math.Min(right, (cycleLength - 1)); int leftValue = this.fNumPeriodsToExtrapolateBackward; for (int i = 1; (i <= this.fNumPeriodsToExtrapolateBackward); i++) { double ys = interpolator.smoothOnePoint((i * -1), left, right); // smoothedData[(leftValue - i)] = ys == 0 ? smoothedData[leftValue] : ys; } // Extrapolate from the rightmost "width" points to the "length" position (one past the array end). right = (cycleLength - 1); left = ((right - this.fWidth) + 1); left = Math.Max(0, left); int rightValue = (this.fNumPeriodsToExtrapolateBackward + right); for (int i = 1; (i <= this.fNumPeriodsToExtrapolateForward); i++) { Double ys = interpolator.smoothOnePoint((right + i), left, right); //smoothedData[(rightValue + i)] = (ys == null); smoothedData[rightValue + i] = ys == 0 ? smoothedData[rightValue] : ys; } }
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]; } }
private void removeSeasonality() { // double[] pass1 = this.fExtendedSeasonal.MA(this.fPeriodLength); // data.length + periodLength + 1 double[] pass2 = pass1.MA(this.fPeriodLength); // data.length + 2 double[] pass3 = pass2.MA(3); // this.fLowpassLoessFactory.Data = pass3; LoessSmoother lowPassLoess = this.fLowpassLoessFactory.build(); this.fDeSeasonalized = lowPassLoess.smooth(); // dumpDebugData("lowpass", fDeSeasonalized); }