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];
            }
        }
Example #2
0
        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))];
        }