From 7f1f2ea51a195616003199731e4562f90d9af804 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Wed, 26 May 2021 12:54:21 +0400 Subject: [PATCH] add classes for exponential smoothing methods params --- .../ru/ulstu/services/TimeSeriesService.java | 32 ++++++---- .../exponential/AddTrendAddSeason.java | 60 ++++++++++--------- .../exponential/AddTrendNoSeason.java | 24 ++++---- .../exponential/ExponentialMethodParams.java | 15 ++--- .../exponential/NoTrendNoSeason.java | 12 ++-- .../tsMethods/exponential/param/Alpha.java | 7 +++ .../tsMethods/exponential/param/Beta.java | 7 +++ .../tsMethods/exponential/param/Gamma.java | 7 +++ .../tsMethods/exponential/param/Season.java | 9 +++ .../param/TimeSeriesMethodParam.java | 42 +++++++++++++ .../param/TimeSeriesMethodParamValue.java | 27 +++++++++ 11 files changed, 179 insertions(+), 63 deletions(-) create mode 100644 src/main/java/ru/ulstu/tsMethods/exponential/param/Alpha.java create mode 100644 src/main/java/ru/ulstu/tsMethods/exponential/param/Beta.java create mode 100644 src/main/java/ru/ulstu/tsMethods/exponential/param/Gamma.java create mode 100644 src/main/java/ru/ulstu/tsMethods/exponential/param/Season.java create mode 100644 src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParam.java create mode 100644 src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParamValue.java diff --git a/src/main/java/ru/ulstu/services/TimeSeriesService.java b/src/main/java/ru/ulstu/services/TimeSeriesService.java index 109d5f3..5d33515 100644 --- a/src/main/java/ru/ulstu/services/TimeSeriesService.java +++ b/src/main/java/ru/ulstu/services/TimeSeriesService.java @@ -7,19 +7,16 @@ import ru.ulstu.TimeSeriesUtils; import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeriesValue; import ru.ulstu.models.exceptions.ModelingException; +import ru.ulstu.tsMethods.TimeSeriesMethod; 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.util.Arrays; import java.util.List; 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 public class TimeSeriesService { @@ -36,17 +33,26 @@ public class TimeSeriesService { } public TimeSeries getForecast(TimeSeries timeSeries, int countPoints) throws ModelingException { - //NoTrendNoSeason nn = new NoTrendNoSeason(ExponentialMethodParams.of(ExponentialParamName.ALPHA, 0.8)); - AddTrendAddSeason an = new AddTrendAddSeason(timeSeries, ExponentialMethodParams.of(ALPHA, 0.0, - BETA, 1.0, GAMMA, 1.0, SEASON, 17.0)); - return an.getForecast(countPoints); + TimeSeriesMethod method; + method = new NoTrendNoSeason(timeSeries, new TimeSeriesMethodParamValue<>(new Alpha(), 0.8)); + 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.getForecast(countPoints); } public TimeSeries smoothTimeSeries(TimeSeries timeSeries) throws ModelingException { //NoTrendNoSeason nn = new NoTrendNoSeason(timeSeries, ExponentialMethodParams.of(ExponentialParamName.ALPHA, 0.8)); - AddTrendAddSeason an = new AddTrendAddSeason(timeSeries, ExponentialMethodParams.of(ALPHA, 0.0, - BETA, 1.0, GAMMA, 1.0, SEASON, 17.0)); - return an.getModel(); + TimeSeriesMethod method; + method = new NoTrendNoSeason(timeSeries, new TimeSeriesMethodParamValue<>(new Alpha(), 0.8)); + 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) { diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendAddSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendAddSeason.java index c493415..fe5f0fb 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendAddSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendAddSeason.java @@ -4,24 +4,30 @@ import ru.ulstu.models.TimeSeries; import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.TimeSeriesValidateException; import ru.ulstu.tsMethods.TimeSeriesMethod; +import ru.ulstu.tsMethods.exponential.param.*; import java.util.ArrayList; 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 { - private final ExponentialMethodParams exponentialMethodParams; + private final TimeSeriesMethodParamValue alpha; + private final TimeSeriesMethodParamValue beta; + private final TimeSeriesMethodParamValue gamma; + private final TimeSeriesMethodParamValue season; private final List sComponent = new ArrayList<>(); private final List tComponent = new ArrayList<>(); private final List iComponent = new ArrayList<>(); - public AddTrendAddSeason(TimeSeries timeSeries, ExponentialMethodParams exponentialMethodParams) throws ModelingException { + public AddTrendAddSeason(TimeSeries timeSeries, + TimeSeriesMethodParamValue alpha, + TimeSeriesMethodParamValue beta, + TimeSeriesMethodParamValue gamma, + TimeSeriesMethodParamValue season) throws ModelingException { super(timeSeries); - this.exponentialMethodParams = exponentialMethodParams; + this.alpha = alpha; + this.beta = beta; + this.gamma = gamma; + this.season = season; } @Override @@ -35,30 +41,30 @@ public class AddTrendAddSeason extends TimeSeriesMethod { TimeSeries model = new TimeSeries("Model of " + originalTimeSeries.getName()); model.addValue(originalTimeSeries.getFirstValue()); //выполняется проход модели по сглаживанию - for (int t = 1; t < exponentialMethodParams.getValue(SEASON).intValue(); t++) { - sComponent.add(exponentialMethodParams.getValue(ALPHA) * originalTimeSeries.getNumericValue(t) - + (1 - exponentialMethodParams.getValue(ALPHA)) + for (int t = 1; t < season.getValue().intValue(); t++) { + sComponent.add(alpha.getDoubleValue() * originalTimeSeries.getNumericValue(t) + + (1 - alpha.getDoubleValue()) * (sComponent.get(t - 1) + tComponent.get(t - 1))); - tComponent.add(exponentialMethodParams.getValue(BETA) + tComponent.add(beta.getDoubleValue() * (sComponent.get(t) - sComponent.get(t - 1)) - + (1 - exponentialMethodParams.getValue(BETA)) * tComponent.get(t - 1)); - iComponent.add(exponentialMethodParams.getValue(GAMMA) * originalTimeSeries.getNumericValue(t) / sComponent.get(sComponent.size() - 1) - + (1 - exponentialMethodParams.getValue(GAMMA)) * iComponent.get(0)); + + (1 - beta.getDoubleValue()) * tComponent.get(t - 1)); + iComponent.add(gamma.getDoubleValue() * originalTimeSeries.getNumericValue(t) / sComponent.get(sComponent.size() - 1) + + (1 - gamma.getDoubleValue()) * iComponent.get(0)); 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++) { - sComponent.add(exponentialMethodParams.getValue(ALPHA) * originalTimeSeries.getNumericValue(t) - / iComponent.get(t - exponentialMethodParams.getValue(SEASON).intValue()) - + (1 - exponentialMethodParams.getValue(ALPHA)) + sComponent.add(alpha.getDoubleValue() * originalTimeSeries.getNumericValue(t) + / iComponent.get(t - season.getIntValue()) + + (1 - alpha.getDoubleValue()) * (sComponent.get(t - 1) + tComponent.get(t - 1))); - tComponent.add(exponentialMethodParams.getValue(BETA) + tComponent.add(beta.getDoubleValue() * (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) - + (1 - exponentialMethodParams.getValue(GAMMA)) * iComponent.get(t - exponentialMethodParams.getValue(SEASON).intValue())); + iComponent.add(gamma.getDoubleValue() * originalTimeSeries.getNumericValue(t) / sComponent.get(sComponent.size() - 1) + + (1 - gamma.getDoubleValue()) * iComponent.get(t - season.getIntValue())); model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1)); } return model; @@ -66,7 +72,7 @@ public class AddTrendAddSeason extends TimeSeriesMethod { @Override protected void validateAdditionalParams() throws ModelingException { - if (originalTimeSeries.getLength() < exponentialMethodParams.getValue(SEASON)) { + if (originalTimeSeries.getLength() < season.getIntValue()) { throw new TimeSeriesValidateException("Период больше чем длина ряда"); } } @@ -74,11 +80,11 @@ public class AddTrendAddSeason extends TimeSeriesMethod { @Override protected TimeSeries makeForecast(TimeSeries forecast) throws ModelingException { for (int t = 1; t < forecast.getLength(); t++) { - iComponent.add(exponentialMethodParams.getValue(GAMMA) * forecast.getNumericValue(t - 1) / sComponent.get(sComponent.size() - 1) - + (1 - exponentialMethodParams.getValue(GAMMA)) * iComponent.get(t + getModel().getLength() - exponentialMethodParams.getValue(SEASON).intValue())); + iComponent.add(gamma.getDoubleValue() * forecast.getNumericValue(t - 1) / sComponent.get(sComponent.size() - 1) + + (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) - * iComponent.get(t + getModel().getLength() - exponentialMethodParams.getValue(SEASON).intValue())); + * iComponent.get(t + getModel().getLength() - season.getIntValue())); } return forecast; } diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendNoSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendNoSeason.java index 95d92c3..5e3da4e 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendNoSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/AddTrendNoSeason.java @@ -3,21 +3,25 @@ package ru.ulstu.tsMethods.exponential; import ru.ulstu.models.TimeSeries; import ru.ulstu.models.exceptions.ModelingException; 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.List; -import static ru.ulstu.tsMethods.exponential.ExponentialParamName.ALPHA; -import static ru.ulstu.tsMethods.exponential.ExponentialParamName.BETA; - public class AddTrendNoSeason extends TimeSeriesMethod { - private final ExponentialMethodParams exponentialMethodParams; + private final TimeSeriesMethodParamValue alpha; + private final TimeSeriesMethodParamValue beta; private final List sComponent = new ArrayList<>(); private final List tComponent = new ArrayList<>(); - public AddTrendNoSeason(TimeSeries timeSeries, ExponentialMethodParams exponentialMethodParams) throws ModelingException { + public AddTrendNoSeason(TimeSeries timeSeries, + TimeSeriesMethodParamValue alpha, + TimeSeriesMethodParamValue beta) throws ModelingException { super(timeSeries); - this.exponentialMethodParams = exponentialMethodParams; + this.alpha = alpha; + this.beta = beta; } @Override @@ -30,13 +34,13 @@ public class AddTrendNoSeason extends TimeSeriesMethod { model.addValue(originalTimeSeries.getFirstValue()); //выполняется проход модели по сглаживанию for (int t = 1; t < originalTimeSeries.getValues().size(); t++) { - sComponent.add(exponentialMethodParams.getValue(ALPHA) * originalTimeSeries.getNumericValue(t) - + (1 - exponentialMethodParams.getValue(ALPHA)) + sComponent.add(alpha.getDoubleValue() * originalTimeSeries.getNumericValue(t) + + (1 - alpha.getDoubleValue()) * (sComponent.get(t - 1) - tComponent.get(t - 1))); - tComponent.add(exponentialMethodParams.getValue(BETA) + tComponent.add(beta.getDoubleValue() * (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)); } return model; diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/ExponentialMethodParams.java b/src/main/java/ru/ulstu/tsMethods/exponential/ExponentialMethodParams.java index 3446cfd..cb01b10 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/ExponentialMethodParams.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/ExponentialMethodParams.java @@ -2,17 +2,18 @@ package ru.ulstu.tsMethods.exponential; import com.google.common.collect.ImmutableMap; import ru.ulstu.models.exceptions.ModelingException; +import ru.ulstu.tsMethods.exponential.param.TimeSeriesMethodParam; import java.util.Map; public class ExponentialMethodParams { - Map paramsValues; + Map paramsValues; - public ExponentialMethodParams(Map paramsValues) { + public ExponentialMethodParams(Map paramsValues) { this.paramsValues = paramsValues; } - public Double getValue(ExponentialParamName paramName) throws ModelingException { + public Double getValue(TimeSeriesMethodParam paramName) throws ModelingException { if (paramsValues.containsKey(paramName)) { return paramsValues.get(paramName); } 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)); } - 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)); } - 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)); } - 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)); } } diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java index 3464385..5c7a6d1 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java @@ -3,19 +3,19 @@ package ru.ulstu.tsMethods.exponential; import ru.ulstu.models.TimeSeries; import ru.ulstu.models.exceptions.ModelingException; 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.List; -import static ru.ulstu.tsMethods.exponential.ExponentialParamName.ALPHA; - public class NoTrendNoSeason extends TimeSeriesMethod { - private final ExponentialMethodParams exponentialMethodParams; + private final TimeSeriesMethodParamValue alpha; private final List sComponent = new ArrayList<>(); - public NoTrendNoSeason(TimeSeries timeSeries, ExponentialMethodParams exponentialMethodParams) throws ModelingException { + public NoTrendNoSeason(TimeSeries timeSeries, TimeSeriesMethodParamValue alpha) throws ModelingException { super(timeSeries); - this.exponentialMethodParams = exponentialMethodParams; + this.alpha = alpha; } @Override @@ -27,7 +27,7 @@ public class NoTrendNoSeason extends TimeSeriesMethod { //выполняется проход модели по сглаживанию for (int t = 1; t < originalTimeSeries.getValues().size(); t++) { sComponent.add(sComponent.get(t - 1) - + exponentialMethodParams.getValue(ALPHA) + + alpha.getDoubleValue() * (originalTimeSeries.getNumericValue(t) - sComponent.get(t - 1))); model.addValue(originalTimeSeries.getValues().get(t), sComponent.get(sComponent.size() - 1)); } diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/param/Alpha.java b/src/main/java/ru/ulstu/tsMethods/exponential/param/Alpha.java new file mode 100644 index 0000000..bb68013 --- /dev/null +++ b/src/main/java/ru/ulstu/tsMethods/exponential/param/Alpha.java @@ -0,0 +1,7 @@ +package ru.ulstu.tsMethods.exponential.param; + +public class Alpha extends TimeSeriesMethodParam { + public Alpha() { + super("ALPHA", 0, 1, DEFAULT_OPTIMIZATION_STEP); + } +} diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/param/Beta.java b/src/main/java/ru/ulstu/tsMethods/exponential/param/Beta.java new file mode 100644 index 0000000..9f46cc8 --- /dev/null +++ b/src/main/java/ru/ulstu/tsMethods/exponential/param/Beta.java @@ -0,0 +1,7 @@ +package ru.ulstu.tsMethods.exponential.param; + +public class Beta extends TimeSeriesMethodParam { + public Beta() { + super("BETA", 0, 1, DEFAULT_OPTIMIZATION_STEP); + } +} diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/param/Gamma.java b/src/main/java/ru/ulstu/tsMethods/exponential/param/Gamma.java new file mode 100644 index 0000000..2f759cc --- /dev/null +++ b/src/main/java/ru/ulstu/tsMethods/exponential/param/Gamma.java @@ -0,0 +1,7 @@ +package ru.ulstu.tsMethods.exponential.param; + +public class Gamma extends TimeSeriesMethodParam { + public Gamma() { + super("Gamma", 0, 1, DEFAULT_OPTIMIZATION_STEP); + } +} diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/param/Season.java b/src/main/java/ru/ulstu/tsMethods/exponential/param/Season.java new file mode 100644 index 0000000..6d7cb74 --- /dev/null +++ b/src/main/java/ru/ulstu/tsMethods/exponential/param/Season.java @@ -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); + } +} diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParam.java b/src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParam.java new file mode 100644 index 0000000..cdf0f78 --- /dev/null +++ b/src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParam.java @@ -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 + + '}'; + } +} diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParamValue.java b/src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParamValue.java new file mode 100644 index 0000000..490cd0f --- /dev/null +++ b/src/main/java/ru/ulstu/tsMethods/exponential/param/TimeSeriesMethodParamValue.java @@ -0,0 +1,27 @@ +package ru.ulstu.tsMethods.exponential.param; + +public class TimeSeriesMethodParamValue { + 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(); + } +}