File: STL\FastLoess.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.Collections.Generic;
using Microsoft.ML.Runtime;
 
namespace Microsoft.ML.TimeSeries
{
    /// <summary>
    /// This is the fast version of Loess. There are several alternatives to improve the performance. This one is an approximation approach.
    /// The smoothing is conducted on a sample set, and then the values on the left points are assigned directly.
    /// </summary>
    internal class FastLoess
    {
        /// <summary>
        /// This class is a sampling based method, so here specifies the sample size.
        /// </summary>
        private const int _sampleSize = 100;
 
        /// <summary>
        /// The minimum length of a valid time series. A time series with length equals 2 is so trivial and meaningless less than 2.
        /// </summary>
        public const int MinTimeSeriesLength = 3;
 
        private readonly IReadOnlyList<double> _x;
        private readonly IReadOnlyList<double> _y;
        private readonly int _length;
 
        private readonly Loess _smoother;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="FastLoess"/> class.
        /// The fast version of the Loess method. when the time series is too long, the sampling will be conducted first to improve the performance.
        /// </summary>
        /// <param name="xValues">The input x-axis values</param>
        /// <param name="yValues">The input y-axis values</param>
        /// <param name="isTemporal">If the regression is considered to take temporal information into account. In general, this is true if we are regressing a time series, and false if we are regressing scatter plot data</param>
        /// <param name="r">This method will provide default smoothing ratio if user did not specify</param>
        public FastLoess(IReadOnlyList<double> xValues, IReadOnlyList<double> yValues, bool isTemporal = true, int r = -1)
        {
            Contracts.CheckValue(xValues, nameof(xValues));
            Contracts.CheckValue(yValues, nameof(yValues));
            Y = new List<double>();
 
            if (yValues.Count < MinTimeSeriesLength)
                throw Contracts.Except("input data structure cannot be 0-length: lowess");
 
            _x = xValues;
            _y = yValues;
            _length = _y.Count;
 
            if (_length <= FastLoess._sampleSize)
            {
                if (r == -1)
                    _smoother = new Loess(_x, _y, isTemporal);
                else
                    _smoother = new Loess(_x, _y, isTemporal, r);
            }
            else
            {
                // Conduct sampling based strategy, to boost the performance.
                double step = _length * 1.0 / FastLoess._sampleSize;
                var sampleX = new double[FastLoess._sampleSize];
                var sampleY = new double[FastLoess._sampleSize];
                for (int i = 0; i < FastLoess._sampleSize; i++)
                {
                    int index = (int)(i * step);
                    sampleX[i] = _x[index];
                    sampleY[i] = _y[index];
                }
                if (r == -1)
                    _smoother = new Loess(sampleX, sampleY, isTemporal);
                else
                    _smoother = new Loess(sampleX, sampleY, isTemporal, r);
            }
        }
 
        /// <summary>
        /// The estimated y values.
        /// </summary>
        public List<double> Y { get; }
 
        /// <summary>
        /// Assign the smoothing values to all the data points, not only on the sample size.
        /// </summary>
        public void Estimate()
        {
            for (int i = 0; i < _length; i++)
            {
                double yValue = _smoother.EstimateY(_x[i]);
                Y.Add(yValue);
            }
        }
 
        /// <summary>
        /// Estimate a y value by giving an x value, even if the x value is not one of the input points.
        /// </summary>
        public double EstimateY(double xValue)
        {
            return _smoother.EstimateY(xValue);
        }
    }
}