add classes for exponential smoothing methods params

This commit is contained in:
Anton Romanov 2021-05-26 12:54:21 +04:00
parent 9e3ecc1d31
commit 7f1f2ea51a
11 changed files with 179 additions and 63 deletions

View File

@ -7,19 +7,16 @@ import ru.ulstu.TimeSeriesUtils;
import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeries;
import ru.ulstu.models.TimeSeriesValue; import ru.ulstu.models.TimeSeriesValue;
import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.ModelingException;
import ru.ulstu.tsMethods.TimeSeriesMethod;
import ru.ulstu.tsMethods.exponential.AddTrendAddSeason; import ru.ulstu.tsMethods.exponential.AddTrendAddSeason;
import ru.ulstu.tsMethods.exponential.ExponentialMethodParams; import ru.ulstu.tsMethods.exponential.NoTrendNoSeason;
import ru.ulstu.tsMethods.exponential.param.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.ALPHA;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.BETA;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.GAMMA;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.SEASON;
@Service @Service
public class TimeSeriesService { public class TimeSeriesService {
@ -36,17 +33,26 @@ public class TimeSeriesService {
} }
public TimeSeries getForecast(TimeSeries timeSeries, int countPoints) throws ModelingException { public TimeSeries getForecast(TimeSeries timeSeries, int countPoints) throws ModelingException {
//NoTrendNoSeason nn = new NoTrendNoSeason(ExponentialMethodParams.of(ExponentialParamName.ALPHA, 0.8)); TimeSeriesMethod method;
AddTrendAddSeason an = new AddTrendAddSeason(timeSeries, ExponentialMethodParams.of(ALPHA, 0.0, method = new NoTrendNoSeason(timeSeries, new TimeSeriesMethodParamValue<>(new Alpha(), 0.8));
BETA, 1.0, GAMMA, 1.0, SEASON, 17.0)); method = new AddTrendAddSeason(timeSeries,
return an.getForecast(countPoints); new TimeSeriesMethodParamValue<>(new Alpha(), 0.5),
new TimeSeriesMethodParamValue<>(new Beta(), 0.5),
new TimeSeriesMethodParamValue<>(new Gamma(), 0.5),
new TimeSeriesMethodParamValue<>(new Season(), 17));
return method.getForecast(countPoints);
} }
public TimeSeries smoothTimeSeries(TimeSeries timeSeries) throws ModelingException { public TimeSeries smoothTimeSeries(TimeSeries timeSeries) throws ModelingException {
//NoTrendNoSeason nn = new NoTrendNoSeason(timeSeries, ExponentialMethodParams.of(ExponentialParamName.ALPHA, 0.8)); //NoTrendNoSeason nn = new NoTrendNoSeason(timeSeries, ExponentialMethodParams.of(ExponentialParamName.ALPHA, 0.8));
AddTrendAddSeason an = new AddTrendAddSeason(timeSeries, ExponentialMethodParams.of(ALPHA, 0.0, TimeSeriesMethod method;
BETA, 1.0, GAMMA, 1.0, SEASON, 17.0)); method = new NoTrendNoSeason(timeSeries, new TimeSeriesMethodParamValue<>(new Alpha(), 0.8));
return an.getModel(); method = new AddTrendAddSeason(timeSeries,
new TimeSeriesMethodParamValue<>(new Alpha(), 0.5),
new TimeSeriesMethodParamValue<>(new Beta(), 0.5),
new TimeSeriesMethodParamValue<>(new Gamma(), 0.5),
new TimeSeriesMethodParamValue<>(new Season(), 17));
return method.getModel();
} }
public TimeSeries getTimeSeriesFromString(String tsString) { public TimeSeries getTimeSeriesFromString(String tsString) {

View File

@ -4,24 +4,30 @@ import ru.ulstu.models.TimeSeries;
import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.ModelingException;
import ru.ulstu.models.exceptions.TimeSeriesValidateException; import ru.ulstu.models.exceptions.TimeSeriesValidateException;
import ru.ulstu.tsMethods.TimeSeriesMethod; import ru.ulstu.tsMethods.TimeSeriesMethod;
import ru.ulstu.tsMethods.exponential.param.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.ALPHA;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.BETA;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.GAMMA;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.SEASON;
public class AddTrendAddSeason extends TimeSeriesMethod { public class AddTrendAddSeason extends TimeSeriesMethod {
private final ExponentialMethodParams exponentialMethodParams; private final TimeSeriesMethodParamValue<Alpha> alpha;
private final TimeSeriesMethodParamValue<Beta> beta;
private final TimeSeriesMethodParamValue<Gamma> gamma;
private final TimeSeriesMethodParamValue<Season> season;
private final List<Double> sComponent = new ArrayList<>(); private final List<Double> sComponent = new ArrayList<>();
private final List<Double> tComponent = new ArrayList<>(); private final List<Double> tComponent = new ArrayList<>();
private final List<Double> iComponent = new ArrayList<>(); private final List<Double> iComponent = new ArrayList<>();
public AddTrendAddSeason(TimeSeries timeSeries, ExponentialMethodParams exponentialMethodParams) throws ModelingException { public AddTrendAddSeason(TimeSeries timeSeries,
TimeSeriesMethodParamValue<Alpha> alpha,
TimeSeriesMethodParamValue<Beta> beta,
TimeSeriesMethodParamValue<Gamma> gamma,
TimeSeriesMethodParamValue<Season> season) throws ModelingException {
super(timeSeries); super(timeSeries);
this.exponentialMethodParams = exponentialMethodParams; this.alpha = alpha;
this.beta = beta;
this.gamma = gamma;
this.season = season;
} }
@Override @Override
@ -35,30 +41,30 @@ public class AddTrendAddSeason extends TimeSeriesMethod {
TimeSeries model = new TimeSeries("Model of " + originalTimeSeries.getName()); TimeSeries model = new TimeSeries("Model of " + originalTimeSeries.getName());
model.addValue(originalTimeSeries.getFirstValue()); model.addValue(originalTimeSeries.getFirstValue());
//выполняется проход модели по сглаживанию //выполняется проход модели по сглаживанию
for (int t = 1; t < exponentialMethodParams.getValue(SEASON).intValue(); t++) { for (int t = 1; t < season.getValue().intValue(); t++) {
sComponent.add(exponentialMethodParams.getValue(ALPHA) * originalTimeSeries.getNumericValue(t) sComponent.add(alpha.getDoubleValue() * originalTimeSeries.getNumericValue(t)
+ (1 - exponentialMethodParams.getValue(ALPHA)) + (1 - alpha.getDoubleValue())
* (sComponent.get(t - 1) + tComponent.get(t - 1))); * (sComponent.get(t - 1) + tComponent.get(t - 1)));
tComponent.add(exponentialMethodParams.getValue(BETA) tComponent.add(beta.getDoubleValue()
* (sComponent.get(t) - sComponent.get(t - 1)) * (sComponent.get(t) - sComponent.get(t - 1))
+ (1 - exponentialMethodParams.getValue(BETA)) * tComponent.get(t - 1)); + (1 - beta.getDoubleValue()) * tComponent.get(t - 1));
iComponent.add(exponentialMethodParams.getValue(GAMMA) * originalTimeSeries.getNumericValue(t) / sComponent.get(sComponent.size() - 1) iComponent.add(gamma.getDoubleValue() * originalTimeSeries.getNumericValue(t) / sComponent.get(sComponent.size() - 1)
+ (1 - exponentialMethodParams.getValue(GAMMA)) * iComponent.get(0)); + (1 - gamma.getDoubleValue()) * iComponent.get(0));
model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1)); model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1));
} }
for (int t = exponentialMethodParams.getValue(SEASON).intValue(); for (int t = season.getIntValue();
t < originalTimeSeries.getValues().size(); t++) { t < originalTimeSeries.getValues().size(); t++) {
sComponent.add(exponentialMethodParams.getValue(ALPHA) * originalTimeSeries.getNumericValue(t) sComponent.add(alpha.getDoubleValue() * originalTimeSeries.getNumericValue(t)
/ iComponent.get(t - exponentialMethodParams.getValue(SEASON).intValue()) / iComponent.get(t - season.getIntValue())
+ (1 - exponentialMethodParams.getValue(ALPHA)) + (1 - alpha.getDoubleValue())
* (sComponent.get(t - 1) + tComponent.get(t - 1))); * (sComponent.get(t - 1) + tComponent.get(t - 1)));
tComponent.add(exponentialMethodParams.getValue(BETA) tComponent.add(beta.getDoubleValue()
* (sComponent.get(t) - sComponent.get(t - 1)) * (sComponent.get(t) - sComponent.get(t - 1))
+ (1 - exponentialMethodParams.getValue(BETA)) * tComponent.get(t - 1)); + (1 - beta.getDoubleValue()) * tComponent.get(t - 1));
iComponent.add(exponentialMethodParams.getValue(GAMMA) * originalTimeSeries.getNumericValue(t) / sComponent.get(sComponent.size() - 1) iComponent.add(gamma.getDoubleValue() * originalTimeSeries.getNumericValue(t) / sComponent.get(sComponent.size() - 1)
+ (1 - exponentialMethodParams.getValue(GAMMA)) * iComponent.get(t - exponentialMethodParams.getValue(SEASON).intValue())); + (1 - gamma.getDoubleValue()) * iComponent.get(t - season.getIntValue()));
model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1)); model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1));
} }
return model; return model;
@ -66,7 +72,7 @@ public class AddTrendAddSeason extends TimeSeriesMethod {
@Override @Override
protected void validateAdditionalParams() throws ModelingException { protected void validateAdditionalParams() throws ModelingException {
if (originalTimeSeries.getLength() < exponentialMethodParams.getValue(SEASON)) { if (originalTimeSeries.getLength() < season.getIntValue()) {
throw new TimeSeriesValidateException("Период больше чем длина ряда"); throw new TimeSeriesValidateException("Период больше чем длина ряда");
} }
} }
@ -74,11 +80,11 @@ public class AddTrendAddSeason extends TimeSeriesMethod {
@Override @Override
protected TimeSeries makeForecast(TimeSeries forecast) throws ModelingException { protected TimeSeries makeForecast(TimeSeries forecast) throws ModelingException {
for (int t = 1; t < forecast.getLength(); t++) { for (int t = 1; t < forecast.getLength(); t++) {
iComponent.add(exponentialMethodParams.getValue(GAMMA) * forecast.getNumericValue(t - 1) / sComponent.get(sComponent.size() - 1) iComponent.add(gamma.getDoubleValue() * forecast.getNumericValue(t - 1) / sComponent.get(sComponent.size() - 1)
+ (1 - exponentialMethodParams.getValue(GAMMA)) * iComponent.get(t + getModel().getLength() - exponentialMethodParams.getValue(SEASON).intValue())); + (1 - gamma.getDoubleValue()) * iComponent.get(t + getModel().getLength() - season.getIntValue()));
forecast.getValues().get(t).setValue((sComponent.get(sComponent.size() - 1) + tComponent.get(tComponent.size() - 1) * t) forecast.getValues().get(t).setValue((sComponent.get(sComponent.size() - 1) + tComponent.get(tComponent.size() - 1) * t)
* iComponent.get(t + getModel().getLength() - exponentialMethodParams.getValue(SEASON).intValue())); * iComponent.get(t + getModel().getLength() - season.getIntValue()));
} }
return forecast; return forecast;
} }

View File

@ -3,21 +3,25 @@ package ru.ulstu.tsMethods.exponential;
import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeries;
import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.ModelingException;
import ru.ulstu.tsMethods.TimeSeriesMethod; import ru.ulstu.tsMethods.TimeSeriesMethod;
import ru.ulstu.tsMethods.exponential.param.Alpha;
import ru.ulstu.tsMethods.exponential.param.Beta;
import ru.ulstu.tsMethods.exponential.param.TimeSeriesMethodParamValue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.ALPHA;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.BETA;
public class AddTrendNoSeason extends TimeSeriesMethod { public class AddTrendNoSeason extends TimeSeriesMethod {
private final ExponentialMethodParams exponentialMethodParams; private final TimeSeriesMethodParamValue<Alpha> alpha;
private final TimeSeriesMethodParamValue<Beta> beta;
private final List<Double> sComponent = new ArrayList<>(); private final List<Double> sComponent = new ArrayList<>();
private final List<Double> tComponent = new ArrayList<>(); private final List<Double> tComponent = new ArrayList<>();
public AddTrendNoSeason(TimeSeries timeSeries, ExponentialMethodParams exponentialMethodParams) throws ModelingException { public AddTrendNoSeason(TimeSeries timeSeries,
TimeSeriesMethodParamValue<Alpha> alpha,
TimeSeriesMethodParamValue<Beta> beta) throws ModelingException {
super(timeSeries); super(timeSeries);
this.exponentialMethodParams = exponentialMethodParams; this.alpha = alpha;
this.beta = beta;
} }
@Override @Override
@ -30,13 +34,13 @@ public class AddTrendNoSeason extends TimeSeriesMethod {
model.addValue(originalTimeSeries.getFirstValue()); model.addValue(originalTimeSeries.getFirstValue());
//выполняется проход модели по сглаживанию //выполняется проход модели по сглаживанию
for (int t = 1; t < originalTimeSeries.getValues().size(); t++) { for (int t = 1; t < originalTimeSeries.getValues().size(); t++) {
sComponent.add(exponentialMethodParams.getValue(ALPHA) * originalTimeSeries.getNumericValue(t) sComponent.add(alpha.getDoubleValue() * originalTimeSeries.getNumericValue(t)
+ (1 - exponentialMethodParams.getValue(ALPHA)) + (1 - alpha.getDoubleValue())
* (sComponent.get(t - 1) - tComponent.get(t - 1))); * (sComponent.get(t - 1) - tComponent.get(t - 1)));
tComponent.add(exponentialMethodParams.getValue(BETA) tComponent.add(beta.getDoubleValue()
* (sComponent.get(t) - sComponent.get(t - 1)) * (sComponent.get(t) - sComponent.get(t - 1))
+ (1 - exponentialMethodParams.getValue(BETA)) * tComponent.get(t - 1)); + (1 - beta.getDoubleValue()) * tComponent.get(t - 1));
model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1)); model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1));
} }
return model; return model;

View File

@ -2,17 +2,18 @@ package ru.ulstu.tsMethods.exponential;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.ModelingException;
import ru.ulstu.tsMethods.exponential.param.TimeSeriesMethodParam;
import java.util.Map; import java.util.Map;
public class ExponentialMethodParams { public class ExponentialMethodParams {
Map<ExponentialParamName, Double> paramsValues; Map<TimeSeriesMethodParam, Double> paramsValues;
public ExponentialMethodParams(Map<ExponentialParamName, Double> paramsValues) { public ExponentialMethodParams(Map<TimeSeriesMethodParam, Double> paramsValues) {
this.paramsValues = paramsValues; this.paramsValues = paramsValues;
} }
public Double getValue(ExponentialParamName paramName) throws ModelingException { public Double getValue(TimeSeriesMethodParam paramName) throws ModelingException {
if (paramsValues.containsKey(paramName)) { if (paramsValues.containsKey(paramName)) {
return paramsValues.get(paramName); return paramsValues.get(paramName);
} else { } else {
@ -20,19 +21,19 @@ public class ExponentialMethodParams {
} }
} }
public static ExponentialMethodParams of(ExponentialParamName param1, Double value1) { public static ExponentialMethodParams of(TimeSeriesMethodParam param1, Double value1) {
return new ExponentialMethodParams(ImmutableMap.of(param1, value1)); return new ExponentialMethodParams(ImmutableMap.of(param1, value1));
} }
public static ExponentialMethodParams of(ExponentialParamName param1, Double value1, ExponentialParamName param2, Double value2) { public static ExponentialMethodParams of(TimeSeriesMethodParam param1, Double value1, TimeSeriesMethodParam param2, Double value2) {
return new ExponentialMethodParams(ImmutableMap.of(param1, value1, param2, value2)); return new ExponentialMethodParams(ImmutableMap.of(param1, value1, param2, value2));
} }
public static ExponentialMethodParams of(ExponentialParamName param1, Double value1, ExponentialParamName param2, Double value2, ExponentialParamName param3, Double value3) { public static ExponentialMethodParams of(TimeSeriesMethodParam param1, Double value1, TimeSeriesMethodParam param2, Double value2, TimeSeriesMethodParam param3, Double value3) {
return new ExponentialMethodParams(ImmutableMap.of(param1, value1, param2, value2, param3, value3)); return new ExponentialMethodParams(ImmutableMap.of(param1, value1, param2, value2, param3, value3));
} }
public static ExponentialMethodParams of(ExponentialParamName param1, Double value1, ExponentialParamName param2, Double value2, ExponentialParamName param3, Double value3, ExponentialParamName param4, Double value4) { public static ExponentialMethodParams of(TimeSeriesMethodParam param1, Double value1, TimeSeriesMethodParam param2, Double value2, TimeSeriesMethodParam param3, Double value3, TimeSeriesMethodParam param4, Double value4) {
return new ExponentialMethodParams(ImmutableMap.of(param1, value1, param2, value2, param3, value3, param4, value4)); return new ExponentialMethodParams(ImmutableMap.of(param1, value1, param2, value2, param3, value3, param4, value4));
} }
} }

View File

@ -3,19 +3,19 @@ package ru.ulstu.tsMethods.exponential;
import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeries;
import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.ModelingException;
import ru.ulstu.tsMethods.TimeSeriesMethod; import ru.ulstu.tsMethods.TimeSeriesMethod;
import ru.ulstu.tsMethods.exponential.param.Alpha;
import ru.ulstu.tsMethods.exponential.param.TimeSeriesMethodParamValue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static ru.ulstu.tsMethods.exponential.ExponentialParamName.ALPHA;
public class NoTrendNoSeason extends TimeSeriesMethod { public class NoTrendNoSeason extends TimeSeriesMethod {
private final ExponentialMethodParams exponentialMethodParams; private final TimeSeriesMethodParamValue<Alpha> alpha;
private final List<Double> sComponent = new ArrayList<>(); private final List<Double> sComponent = new ArrayList<>();
public NoTrendNoSeason(TimeSeries timeSeries, ExponentialMethodParams exponentialMethodParams) throws ModelingException { public NoTrendNoSeason(TimeSeries timeSeries, TimeSeriesMethodParamValue<Alpha> alpha) throws ModelingException {
super(timeSeries); super(timeSeries);
this.exponentialMethodParams = exponentialMethodParams; this.alpha = alpha;
} }
@Override @Override
@ -27,7 +27,7 @@ public class NoTrendNoSeason extends TimeSeriesMethod {
//выполняется проход модели по сглаживанию //выполняется проход модели по сглаживанию
for (int t = 1; t < originalTimeSeries.getValues().size(); t++) { for (int t = 1; t < originalTimeSeries.getValues().size(); t++) {
sComponent.add(sComponent.get(t - 1) sComponent.add(sComponent.get(t - 1)
+ exponentialMethodParams.getValue(ALPHA) + alpha.getDoubleValue()
* (originalTimeSeries.getNumericValue(t) - sComponent.get(t - 1))); * (originalTimeSeries.getNumericValue(t) - sComponent.get(t - 1)));
model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1)); model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1));
} }

View File

@ -0,0 +1,7 @@
package ru.ulstu.tsMethods.exponential.param;
public class Alpha extends TimeSeriesMethodParam {
public Alpha() {
super("ALPHA", 0, 1, DEFAULT_OPTIMIZATION_STEP);
}
}

View File

@ -0,0 +1,7 @@
package ru.ulstu.tsMethods.exponential.param;
public class Beta extends TimeSeriesMethodParam {
public Beta() {
super("BETA", 0, 1, DEFAULT_OPTIMIZATION_STEP);
}
}

View File

@ -0,0 +1,7 @@
package ru.ulstu.tsMethods.exponential.param;
public class Gamma extends TimeSeriesMethodParam {
public Gamma() {
super("Gamma", 0, 1, DEFAULT_OPTIMIZATION_STEP);
}
}

View File

@ -0,0 +1,9 @@
package ru.ulstu.tsMethods.exponential.param;
public class Season extends TimeSeriesMethodParam {
private final static int DEFAULT_SEASON_OPTIMIZATION_STEP = 1;
public Season() {
super("Сезонность", 0, 12, DEFAULT_SEASON_OPTIMIZATION_STEP);
}
}

View File

@ -0,0 +1,42 @@
package ru.ulstu.tsMethods.exponential.param;
public abstract class TimeSeriesMethodParam {
public static final Float DEFAULT_OPTIMIZATION_STEP = 0.01f;
private final String key;
private final Number minValue;
private final Number maxValue;
private final Number optimizationStep;
public TimeSeriesMethodParam(String key, Number minValue, Number maxValue, Number optimizationStep) {
this.key = key;
this.minValue = minValue;
this.maxValue = maxValue;
this.optimizationStep = optimizationStep;
}
public String getKey() {
return key;
}
public Number getMinValue() {
return minValue;
}
public Number getMaxValue() {
return maxValue;
}
public Number getOptimizationStep() {
return optimizationStep;
}
@Override
public String toString() {
return "TimeSeriesMethodParam{" +
"key='" + key + '\'' +
", minValue=" + minValue +
", maxValue=" + maxValue +
", delta=" + optimizationStep +
'}';
}
}

View File

@ -0,0 +1,27 @@
package ru.ulstu.tsMethods.exponential.param;
public class TimeSeriesMethodParamValue<T extends TimeSeriesMethodParam> {
private final T param;
private final Number value;
public TimeSeriesMethodParamValue(T param, Number value) {
this.param = param;
this.value = value;
}
public T getParam() {
return param;
}
public Number getValue() {
return value;
}
public double getDoubleValue() {
return value.doubleValue();
}
public int getIntValue() {
return value.intValue();
}
}