ro.hasna.ts.math.representation.PiecewiseCurveFitterApproximation.java Source code

Java tutorial

Introduction

Here is the source code for ro.hasna.ts.math.representation.PiecewiseCurveFitterApproximation.java

Source

/*
 * Copyright 2015 Octavian Hasna
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ro.hasna.ts.math.representation;

import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.apache.commons.math3.fitting.AbstractCurveFitter;
import org.apache.commons.math3.fitting.WeightedObservedPoint;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.Precision;
import ro.hasna.ts.math.exception.ArrayLengthIsTooSmallException;
import ro.hasna.ts.math.util.TimeSeriesPrecision;

import java.util.ArrayList;
import java.util.List;

/**
 * Implements Piecewise Approximation using a curve fitting function (ex: linear = PLA, quadratic = PQA etc.).
 * An optimised implementation for the mean function is the class {@code PiecewiseAggregateApproximation}.
 *
 * @since 1.0
 */
public class PiecewiseCurveFitterApproximation implements GenericTransformer<double[], double[][]> {
    private static final long serialVersionUID = -410430777798956046L;
    private final int segments;
    private final AbstractCurveFitter curveFitter;

    /**
     * Creates a new instance of this class.
     *
     * @param segments    the number of segments
     * @param curveFitter the curve fitting function
     * @throws NumberIsTooSmallException if segments lower than 1
     */
    public PiecewiseCurveFitterApproximation(int segments, AbstractCurveFitter curveFitter) {
        if (segments < 1) {
            throw new NumberIsTooSmallException(segments, 1, true);
        }

        this.segments = segments;
        this.curveFitter = curveFitter;
    }

    @Override
    public double[][] transform(double[] values) {
        int len = values.length;
        if (len < segments) {
            throw new ArrayLengthIsTooSmallException(len, segments, true);
        }

        int modulo = len % segments;
        double[][] reducedValues = new double[segments][];
        if (modulo == 0) {
            int segmentSize = len / segments;
            List<WeightedObservedPoint> segment = new ArrayList<>(segmentSize);
            for (int i = 0; i < segmentSize; i++) {
                segment.add(null);
            }

            int n = 0;
            for (int i = 0; i < len; i++) {
                int index = i % segmentSize;
                segment.set(index, new WeightedObservedPoint(1, index, values[i]));
                if (index + 1 == segmentSize) {
                    reducedValues[n++] = curveFitter.fit(segment);
                    if (n == segments)
                        break;
                }
            }
        } else {
            double segmentSize = len * 1.0 / segments;
            int k = 0;
            int segmentSizeReal = (int) FastMath.ceil(segmentSize) + 1;
            int index = 0;
            List<WeightedObservedPoint> segment = new ArrayList<>(segmentSizeReal);

            for (int i = 0; i < segments - 1; i++) {
                double x = (i + 1) * segmentSize - 1;

                for (; k < x; k++) {
                    segment.add(new WeightedObservedPoint(1, index, values[k]));
                    index++;
                }

                double delta = x - (int) x;
                if (!Precision.equals(delta, 0, TimeSeriesPrecision.EPSILON)) {
                    segment.add(new WeightedObservedPoint(delta, index, values[k]));
                }
                reducedValues[i] = curveFitter.fit(segment);
                segment = new ArrayList<>(segmentSizeReal);
                index = 0;

                if (!Precision.equals(delta, 1, TimeSeriesPrecision.EPSILON)) {
                    segment.add(new WeightedObservedPoint(1 - delta, index, values[k]));
                    index++;
                }
                k++;
            }

            for (; k < len; k++) {
                segment.add(new WeightedObservedPoint(1, index, values[k]));
                index++;
            }
            reducedValues[segments - 1] = curveFitter.fit(segment);
        }

        return reducedValues;
    }

    public int getSegments() {
        return segments;
    }
}