![JAR search and dependency download from the Maven repository](/logo.png)
timeseries.operators.LagPolynomial Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-timeseries Show documentation
Show all versions of java-timeseries Show documentation
Time Series Analysis in Java
The newest version!
/*
* Copyright (c) 2016 Jacob Rachiele
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to
* do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Contributors:
*
* Jacob Rachiele
*/
package timeseries.operators;
import data.DoubleFunctions;
import timeseries.TimeSeries;
import java.time.OffsetDateTime;
import java.util.Arrays;
/**
* Represents a polynomial in the lag operator. See Harvey's
* Forecasting, structural time series models and the Kalman filter, (1989, equation 2.1.3), or
* the wiki entry. The
* polynomial is taken in the lag operator, but is algebraically equivalent to a real or complex polynomial.
*
* @author jrachiele
*/
public class LagPolynomial {
final double[] parameters;
final double[] coefficients;
final int degree;
/**
* Construct a new lag polynomial from the given parameters. Note that the parameters given here are not the same as
* the coefficients of the polynomial since the coefficient at the zero-degreee term is always equal to 1.
*
* @param parameters the parameters of the polynomial.
*/
LagPolynomial(final double... parameters) {
this.parameters = parameters.clone();
this.coefficients = new double[parameters.length + 1];
this.coefficients[0] = 1.0;
System.arraycopy(parameters, 0, this.coefficients, 1, parameters.length);
this.degree = parameters.length;
}
/**
* Create and return a new lag polynomial representing the first difference operator.
*
* @return a new lag polynomial representing the first difference operator.
*/
public static LagPolynomial firstDifference() {
return new LagPolynomial(-1.0);
}
/**
* Create and return a new lag polynomial representing the first seasonal difference operator.
*
* @param seasonalLag the period of seasonality, which is the lag to use for the operator.
* @return a new lag polynomial representing the first seasonal difference operator.
*/
public static LagPolynomial firstSeasonalDifference(final int seasonalLag) {
double[] poly = new double[seasonalLag];
poly[seasonalLag - 1] = -1.0;
return new LagPolynomial(poly);
}
/**
* Create and return a new lag polynomial representing an arbitrary number of seasonal differences.
*
* @param seasonalLag the period of seasonality; the lag to use for the operator.
* @param D the number of seasonal differences.
* @return a new lag polynomial representing an arbitrary number of seasonal differences.
*/
public static LagPolynomial seasonalDifferences(final int seasonalLag, final int D) {
if (D < 0) {
throw new IllegalArgumentException(
"The degree of differencing must be greater than or equal to 0, but was " + D);
}
if (D > 0) {
LagPolynomial diff = LagPolynomial.firstSeasonalDifference(seasonalLag);
for (int i = 1; i < D; i++) {
diff = diff.times(diff);
}
return diff;
} else {
return new LagPolynomial();
}
}
/**
* Create and return a new lag polynomial representing an arbitrary number of differences.
*
* @param d the number of differences. An integer greater than or equal to 0.
* @return a new lag polynomial representing an arbitrary number of differences.
*/
public static LagPolynomial differences(final int d) {
if (d < 0) {
throw new IllegalArgumentException(
"The degree of differencing must be greater than or equal to 0, but was " + d);
}
if (d > 0) {
LagPolynomial diff = LagPolynomial.firstDifference();
for (int i = 1; i < d; i++) {
diff = diff.times(diff);
}
return diff;
} else {
return new LagPolynomial();
}
}
/**
* Create and return a new moving average lag polynomial.
*
* @param parameters the moving average parameters of an ARIMA model.
* @return a new moving average lag polynomial.
*/
public static LagPolynomial movingAverage(double... parameters) {
return new MovingAveragePolynomial(parameters);
}
/**
* Create and return a new autoregressive lag polynomial.
*
* @param parameters the autoregressive parameters of an ARIMA model.
* @return a new autoregressive lag polynomial.
*/
public static LagPolynomial autoRegressive(double... parameters) {
final double[] inverseParams = new double[parameters.length];
for (int i = 0; i < inverseParams.length; i++) {
inverseParams[i] = -parameters[i];
}
return new LagPolynomial(inverseParams);
}
/**
* Multiply this polynomial by another lag polynomial and return the result in a new lag polynomial.
*
* @param other the polynomial to multiply this one with.
* @return the product of this polynomial with the given polynomial.
*/
public final LagPolynomial times(final LagPolynomial other) {
final double[] newParams = new double[this.degree + other.degree + 1];
for (int i = 0; i < coefficients.length; i++) {
for (int j = 0; j < other.coefficients.length; j++) {
newParams[i + j] += coefficients[i] * other.coefficients[j];
}
}
return new LagPolynomial(DoubleFunctions.slice(newParams, 1, newParams.length));
}
/**
* Apply this lag polynomial to a time series at the given index.
*
* @param timeSeries the time series containing the index to apply this lag polynomial to.
* @param index the index of the series to apply the lag polynomial at.
* @return the result of applying this lag polynomial to the given time series at the given index.
*/
public final double apply(final TimeSeries timeSeries, final int index) {
double value = 0.0;
for (int i = 0; i < coefficients.length; i++) {
value += this.coefficients[i] * LagOperator.apply(timeSeries, index, i);
}
return value;
}
/**
* Apply this lag polynomial to a time series at the given date and time.
*
* @param timeSeries the time series containing the index to apply this lag polynomial to.
* @param dateTime the date and time of the series to apply the lag polynomial at.
* @return the result of applying this lag polynomial to the given time series at the given date and time.
*/
public final double apply(final TimeSeries timeSeries, final OffsetDateTime dateTime) {
double value = 0.0;
for (int i = 0; i < coefficients.length; i++) {
value += this.coefficients[i] * LagOperator.apply(timeSeries, dateTime, i);
}
return value;
}
/**
* Use this lag polynomial to fit the implied ARIMA model at the given index by applying the polynomial, then
* solving for the time series value at that index.
*
* @param timeSeries the time series containing the index to apply the lag polynomial to.
* @param index the index of the series to apply the lag polynomial at.
* @return the result of applying this lag polynomial to the time series at the given index, then solving
* for the value of the series at that index.
*/
public double fit(final TimeSeries timeSeries, final int index) {
double value = 0.0;
for (int i = 0; i < parameters.length; i++) {
value -= parameters[i] * LagOperator.apply(timeSeries, index, i + 1);
}
return value;
}
/**
* Use this lag polynomial to fit the implied ARIMA model at the given date and time by applying the polynomial,
* then
* solving for the value of the series at that date and time.
*
* @param timeSeries the time series containing the date and time to apply the lag polynomial to.
* @param dateTime the date and time of the series to apply the lag polynomial at.
* @return the result of applying this lag polynomial to the time series at the given date and time, then solving
* for the value of the series at that date and time.
*/
public double fit(final TimeSeries timeSeries, OffsetDateTime dateTime) {
double value = 0.0;
for (int i = 0; i < parameters.length; i++) {
value -= parameters[i] * LagOperator.apply(timeSeries, dateTime, i + 1);
}
return value;
}
/**
* Use this lag polynomial to fit the implied ARIMA model at the given index by applying the polynomial, then
* solving for the time series value at that index.
*
* @param timeSeries the time series containing the index to apply the lag polynomial to.
* @param index the index of the series to apply the lag polynomial at.
* @return the result of applying this lag polynomial to the time series at the given index, then solving
* for the value of the series at that index.
*/
public double fit(final double[] timeSeries, final int index) {
double value = 0.0;
for (int i = 0; i < parameters.length; i++) {
value -= parameters[i] * LagOperator.apply(timeSeries, index, i + 1);
}
return value;
}
/**
* Return the parameters of this lag polynomial.
*
* @return the parameters of this lag polynomial.
*/
public final double[] parameters() {
return this.parameters.clone();
}
/**
* Return the additive inverse of the parameters of this lag polynomial.
*
* @return the additive inverse of the parameters of this lag polynomial.
*/
public final double[] inverseParams() {
final double[] invParams = new double[parameters.length];
for (int i = 0; i < invParams.length; i++) {
invParams[i] = -parameters[i];
}
return invParams;
}
/**
* Return the coefficients of this lag polynomial, which includes the constant term 1.
*
* @return the coefficients of this lag polynomial.
*/
final double[] coefficients() {
return this.coefficients.clone();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("1");
for (int i = 1; i < coefficients.length - 1; i++) {
if (Math.abs(coefficients[i]) > 1E-16) {
if (coefficients[i] < 0) {
builder.append(" - ");
} else {
builder.append(" + ");
}
if (Math.abs(coefficients[i]) - 1.0 > 1E-16) {
builder.append(Math.abs(coefficients[i]));
}
builder.append("L");
if (i > 1) {
builder.append("^").append(i);
}
}
}
final int lastIndex = coefficients.length - 1;
if (coefficients[lastIndex] < 0) {
builder.append(" - ");
} else {
builder.append(" + ");
}
if (coefficients.length > 1) {
if (coefficients[lastIndex] != 1.0) {
builder.append(Math.abs(coefficients[lastIndex]));
}
builder.append("L");
}
if (coefficients.length > 2) {
builder.append("^").append(lastIndex);
}
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(coefficients);
result = prime * result + degree;
result = prime * result + Arrays.hashCode(parameters);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
LagPolynomial other = (LagPolynomial) obj;
if (!Arrays.equals(coefficients, other.coefficients)) return false;
return degree == other.degree && Arrays.equals(parameters, other.parameters);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy