From cf46bb0bd67dbf67f607f3555c8ceca29517241b Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Sat, 12 Sep 2020 14:54:46 +0400 Subject: [PATCH 1/7] #2 -- add class for model time series --- .../controllers/TimeSeriesController.java | 8 ++++---- ...RequestParams.java => ForecastParams.java} | 2 +- .../java/ru/ulstu/models/ModelTimeSeries.java | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) rename src/main/java/ru/ulstu/models/{ForecastRequestParams.java => ForecastParams.java} (93%) create mode 100644 src/main/java/ru/ulstu/models/ModelTimeSeries.java diff --git a/src/main/java/ru/ulstu/controllers/TimeSeriesController.java b/src/main/java/ru/ulstu/controllers/TimeSeriesController.java index da0884f..48a4224 100644 --- a/src/main/java/ru/ulstu/controllers/TimeSeriesController.java +++ b/src/main/java/ru/ulstu/controllers/TimeSeriesController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import ru.ulstu.configurations.ApiConfiguration; import ru.ulstu.models.Forecast; -import ru.ulstu.models.ForecastRequestParams; +import ru.ulstu.models.ForecastParams; import ru.ulstu.models.TimeSeries; import ru.ulstu.services.TimeSeriesService; @@ -39,8 +39,8 @@ public class TimeSeriesController { @PostMapping("getForecast") @ApiOperation("Получить прогноз временного ряда") - public ResponseEntity getForecastTimeSeries(@RequestBody ForecastRequestParams forecastRequestParams) { - return new ResponseEntity<>(timeSeriesService.getForecast(forecastRequestParams.getOriginalTimeSeries(), - forecastRequestParams.getCountForecast()), HttpStatus.OK); + public ResponseEntity getForecastTimeSeries(@RequestBody ForecastParams forecastParams) { + return new ResponseEntity<>(timeSeriesService.getForecast(forecastParams.getOriginalTimeSeries(), + forecastParams.getCountForecast()), HttpStatus.OK); } } diff --git a/src/main/java/ru/ulstu/models/ForecastRequestParams.java b/src/main/java/ru/ulstu/models/ForecastParams.java similarity index 93% rename from src/main/java/ru/ulstu/models/ForecastRequestParams.java rename to src/main/java/ru/ulstu/models/ForecastParams.java index 3ec6f85..2375ac6 100644 --- a/src/main/java/ru/ulstu/models/ForecastRequestParams.java +++ b/src/main/java/ru/ulstu/models/ForecastParams.java @@ -1,6 +1,6 @@ package ru.ulstu.models; -public class ForecastRequestParams { +public class ForecastParams { private TimeSeries originalTimeSeries; private int countForecast; diff --git a/src/main/java/ru/ulstu/models/ModelTimeSeries.java b/src/main/java/ru/ulstu/models/ModelTimeSeries.java new file mode 100644 index 0000000..cd28be8 --- /dev/null +++ b/src/main/java/ru/ulstu/models/ModelTimeSeries.java @@ -0,0 +1,19 @@ +package ru.ulstu.models; + +public class ModelTimeSeries { + private TimeSeries originalTimeSeries; + private TimeSeries modelTimeSeries; + + public ModelTimeSeries(TimeSeries originalTimeSeries) { + this.originalTimeSeries = originalTimeSeries; + this.modelTimeSeries = new TimeSeries("Model time series of '" + originalTimeSeries.getName() + "'"); + } + + public TimeSeries getOriginalTimeSeries() { + return originalTimeSeries; + } + + public TimeSeries getModelTimeSeries() { + return modelTimeSeries; + } +} From cfb0782825fa6dabd22fecb7697261bf89ded83f Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Sat, 12 Sep 2020 16:13:31 +0400 Subject: [PATCH 2/7] #2 -- use class for model time series --- .../java/ru/ulstu/models/ModelTimeSeries.java | 8 ++++++++ .../ru/ulstu/services/TimeSeriesService.java | 2 +- .../ru/ulstu/tsMethods/TimeSeriesMethod.java | 17 +++++++++-------- .../tsMethods/exponential/NoTrendNoSeason.java | 11 ++++++----- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/java/ru/ulstu/models/ModelTimeSeries.java b/src/main/java/ru/ulstu/models/ModelTimeSeries.java index cd28be8..29b872c 100644 --- a/src/main/java/ru/ulstu/models/ModelTimeSeries.java +++ b/src/main/java/ru/ulstu/models/ModelTimeSeries.java @@ -16,4 +16,12 @@ public class ModelTimeSeries { public TimeSeries getModelTimeSeries() { return modelTimeSeries; } + + public void addValue(TimeSeriesValue timeSeriesValue) { + modelTimeSeries.addValue(timeSeriesValue); + } + + public void addValue(TimeSeriesValue basedOnValue, double value) { + modelTimeSeries.getValues().add(new TimeSeriesValue(basedOnValue.getDate().plusDays(1), value)); + } } diff --git a/src/main/java/ru/ulstu/services/TimeSeriesService.java b/src/main/java/ru/ulstu/services/TimeSeriesService.java index 668e547..2eb8037 100644 --- a/src/main/java/ru/ulstu/services/TimeSeriesService.java +++ b/src/main/java/ru/ulstu/services/TimeSeriesService.java @@ -25,6 +25,6 @@ public class TimeSeriesService { NoTrendNoSeason nn = new NoTrendNoSeason(timeSeries, countForecast); nn.setAlpa(0.1); nn.createModel(); - return nn.getForecastTimeSeries(); + return nn.getForecast(); } } diff --git a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java index dc86a73..58206c4 100644 --- a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java +++ b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java @@ -1,6 +1,7 @@ package ru.ulstu.tsMethods; import ru.ulstu.models.Forecast; +import ru.ulstu.models.ModelTimeSeries; import ru.ulstu.models.TimeSeries; import java.util.HashMap; @@ -8,26 +9,26 @@ import java.util.Map; public abstract class TimeSeriesMethod { protected TimeSeries originalTimeSeries; - protected TimeSeries modelTimeSeries; - protected Forecast forecastTimeSeries; + protected ModelTimeSeries model; + protected Forecast forecast; protected int countForecast; protected Map parameters = new HashMap<>(); protected void init() { - modelTimeSeries = new TimeSeries("Model time series of '" + originalTimeSeries.getName() + "'"); - forecastTimeSeries = new Forecast(originalTimeSeries); + model = new ModelTimeSeries(originalTimeSeries); + forecast = new Forecast(originalTimeSeries); } public TimeSeries getOriginalTimeSeries() { return originalTimeSeries; } - public TimeSeries getModelTimeSeries() { - return modelTimeSeries; + public ModelTimeSeries getModel() { + return model; } - public Forecast getForecastTimeSeries() { - return forecastTimeSeries; + public Forecast getForecast() { + return forecast; } public abstract void createModel(); diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java index be15776..bd78381 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java @@ -16,8 +16,8 @@ public class NoTrendNoSeason extends TimeSeriesMethod { @Override protected void init() { super.init(); - modelTimeSeries.addValue(originalTimeSeries.getValues().get(0)); - forecastTimeSeries.addValue(originalTimeSeries.getValues().get(0)); + model.addValue(originalTimeSeries.getValues().get(0)); + forecast.addValue(originalTimeSeries.getValues().get(0)); parameters.put(Param.ALPHA, 1.0); } @@ -34,12 +34,13 @@ public class NoTrendNoSeason extends TimeSeriesMethod { for (int t = 0; t < originalTimeSeries.getValues().size() - 1 + countForecast; t++) { // пока не дошли до конца ряда - сглаживаем, иначе строим прогноз if (t < originalTimeSeries.getValues().size()) { - e = originalTimeSeries.getValues().get(t).getValue() - forecastTimeSeries.getForecast().getValues().get(t).getValue(); + e = originalTimeSeries.getValues().get(t).getValue() - forecast.getForecast().getValues().get(t).getValue(); } else { e = 0; } - modelTimeSeries.addValue(modelTimeSeries.getLastValue(), modelTimeSeries.getValues().get(t).getValue() + parameters.get(Param.ALPHA) * e); // уровень - forecastTimeSeries.addValue(modelTimeSeries.getLastValue()); // прогноз + model.addValue(model.getModelTimeSeries().getLastValue(), + model.getModelTimeSeries().getValues().get(t).getValue() + parameters.get(Param.ALPHA) * e); // уровень + forecast.addValue(model.getModelTimeSeries().getLastValue()); // прогноз } } } From 3cd0d77e0c3dd978d40fbc12963be787680786ed Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Mon, 14 Sep 2020 16:40:35 +0400 Subject: [PATCH 3/7] #2 -- create validation method --- .../models/exceptions/ModelingException.java | 7 +++++++ .../TimeSeriesValidateException.java | 7 +++++++ .../ru/ulstu/services/TimeSeriesService.java | 18 ++++++++++++++---- .../ru/ulstu/tsMethods/TimeSeriesMethod.java | 17 ++++++++++++++++- .../tsMethods/exponential/NoTrendNoSeason.java | 15 +++------------ 5 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 src/main/java/ru/ulstu/models/exceptions/ModelingException.java create mode 100644 src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java diff --git a/src/main/java/ru/ulstu/models/exceptions/ModelingException.java b/src/main/java/ru/ulstu/models/exceptions/ModelingException.java new file mode 100644 index 0000000..4bebd12 --- /dev/null +++ b/src/main/java/ru/ulstu/models/exceptions/ModelingException.java @@ -0,0 +1,7 @@ +package ru.ulstu.models.exceptions; + +public class ModelingException extends Exception { + public ModelingException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java b/src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java new file mode 100644 index 0000000..7138862 --- /dev/null +++ b/src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java @@ -0,0 +1,7 @@ +package ru.ulstu.models.exceptions; + +public class TimeSeriesValidateException extends Exception { + public TimeSeriesValidateException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/ulstu/services/TimeSeriesService.java b/src/main/java/ru/ulstu/services/TimeSeriesService.java index 2eb8037..4bf9a40 100644 --- a/src/main/java/ru/ulstu/services/TimeSeriesService.java +++ b/src/main/java/ru/ulstu/services/TimeSeriesService.java @@ -1,9 +1,12 @@ package ru.ulstu.services; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import ru.ulstu.models.Forecast; import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeriesValue; +import ru.ulstu.models.exceptions.TimeSeriesValidateException; import ru.ulstu.tsMethods.exponential.NoTrendNoSeason; import java.time.LocalDate; @@ -11,6 +14,8 @@ import java.time.LocalDate; @Service public class TimeSeriesService { + private static final Logger LOGGER = LoggerFactory.getLogger(TimeSeriesService.class); + public TimeSeries getRandomTimeSeries(int length) { TimeSeries ts = new TimeSeries("Random time series"); LocalDate dateStart = LocalDate.now().minusDays(length); @@ -22,9 +27,14 @@ public class TimeSeriesService { } public Forecast getForecast(TimeSeries timeSeries, int countForecast) { - NoTrendNoSeason nn = new NoTrendNoSeason(timeSeries, countForecast); - nn.setAlpa(0.1); - nn.createModel(); - return nn.getForecast(); + try { + NoTrendNoSeason nn = new NoTrendNoSeason(timeSeries); + nn.setAlpa(0.1); + nn.createModel(); + return nn.getForecast(); + } catch (TimeSeriesValidateException ex) { + LOGGER.error("Некорректная инициализация метода моделирования", ex); + return null; + } } } diff --git a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java index 58206c4..0eb6d70 100644 --- a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java +++ b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java @@ -3,6 +3,7 @@ package ru.ulstu.tsMethods; import ru.ulstu.models.Forecast; import ru.ulstu.models.ModelTimeSeries; import ru.ulstu.models.TimeSeries; +import ru.ulstu.models.exceptions.TimeSeriesValidateException; import java.util.HashMap; import java.util.Map; @@ -14,7 +15,9 @@ public abstract class TimeSeriesMethod { protected int countForecast; protected Map parameters = new HashMap<>(); - protected void init() { + protected TimeSeriesMethod(TimeSeries originalTimeSeries) throws TimeSeriesValidateException { + validateTimeSeries(originalTimeSeries); + this.originalTimeSeries = originalTimeSeries; model = new ModelTimeSeries(originalTimeSeries); forecast = new Forecast(originalTimeSeries); } @@ -32,4 +35,16 @@ public abstract class TimeSeriesMethod { } public abstract void createModel(); + + private void validateTimeSeries(TimeSeries timeSeries) throws TimeSeriesValidateException { + if (timeSeries == null || timeSeries.isEmpty()) { + throw new TimeSeriesValidateException("Временной ряд должен быть не пустым"); + } + if (timeSeries.getValues().stream().anyMatch(val -> val == null || val.getValue() == null)) { + throw new TimeSeriesValidateException("Временной ряд содержит пустые значения"); + } + if (timeSeries.getValues().stream().anyMatch(val -> val.getDate() == null)) { + throw new TimeSeriesValidateException("Временной ряд должен иметь отметки времени"); + } + } } diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java index bd78381..cdc8d50 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java @@ -1,21 +1,13 @@ package ru.ulstu.tsMethods.exponential; import ru.ulstu.models.TimeSeries; +import ru.ulstu.models.exceptions.TimeSeriesValidateException; import ru.ulstu.tsMethods.Param; import ru.ulstu.tsMethods.TimeSeriesMethod; public class NoTrendNoSeason extends TimeSeriesMethod { - public NoTrendNoSeason(TimeSeries originalTimeSeries, int countForecast) { - this.originalTimeSeries = originalTimeSeries; - this.countForecast = countForecast; - if (originalTimeSeries.isEmpty()) { - throw new RuntimeException("Time series must not empty"); - } - } - - @Override - protected void init() { - super.init(); + public NoTrendNoSeason(TimeSeries originalTimeSeries) throws TimeSeriesValidateException { + super(originalTimeSeries); model.addValue(originalTimeSeries.getValues().get(0)); forecast.addValue(originalTimeSeries.getValues().get(0)); parameters.put(Param.ALPHA, 1.0); @@ -27,7 +19,6 @@ public class NoTrendNoSeason extends TimeSeriesMethod { @Override public void createModel() { - init(); double e; //выполняется проход модели по сглаживанию и прогнозированию countPointForecast точек From 84a228b06d99cf8e4a96c5bdb80020bbc9764bf2 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 1 Oct 2020 08:41:23 +0400 Subject: [PATCH 4/7] #2 -- first implementations of date generator, modeling and forecasting --- .../controllers/TimeSeriesController.java | 3 +- src/main/java/ru/ulstu/models/Forecast.java | 22 ++- .../{ModelTimeSeries.java => Model.java} | 6 +- src/main/java/ru/ulstu/models/TimeSeries.java | 22 +++ .../java/ru/ulstu/models/TimeSeriesValue.java | 34 ++++- .../exceptions/ForecastValidateException.java | 7 + .../TimeSeriesValidateException.java | 2 +- .../ru/ulstu/services/TimeSeriesService.java | 19 +-- .../ru/ulstu/tsMethods/TimeSeriesMethod.java | 130 ++++++++++++++---- .../exponential/NoTrendNoSeason.java | 40 +++++- 10 files changed, 229 insertions(+), 56 deletions(-) rename src/main/java/ru/ulstu/models/{ModelTimeSeries.java => Model.java} (84%) create mode 100644 src/main/java/ru/ulstu/models/exceptions/ForecastValidateException.java diff --git a/src/main/java/ru/ulstu/controllers/TimeSeriesController.java b/src/main/java/ru/ulstu/controllers/TimeSeriesController.java index 48a4224..86c1752 100644 --- a/src/main/java/ru/ulstu/controllers/TimeSeriesController.java +++ b/src/main/java/ru/ulstu/controllers/TimeSeriesController.java @@ -13,6 +13,7 @@ import ru.ulstu.configurations.ApiConfiguration; import ru.ulstu.models.Forecast; import ru.ulstu.models.ForecastParams; import ru.ulstu.models.TimeSeries; +import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.services.TimeSeriesService; @RestController @@ -39,7 +40,7 @@ public class TimeSeriesController { @PostMapping("getForecast") @ApiOperation("Получить прогноз временного ряда") - public ResponseEntity getForecastTimeSeries(@RequestBody ForecastParams forecastParams) { + public ResponseEntity getForecastTimeSeries(@RequestBody ForecastParams forecastParams) throws ModelingException { return new ResponseEntity<>(timeSeriesService.getForecast(forecastParams.getOriginalTimeSeries(), forecastParams.getCountForecast()), HttpStatus.OK); } diff --git a/src/main/java/ru/ulstu/models/Forecast.java b/src/main/java/ru/ulstu/models/Forecast.java index c3413fc..383e8fc 100644 --- a/src/main/java/ru/ulstu/models/Forecast.java +++ b/src/main/java/ru/ulstu/models/Forecast.java @@ -1,23 +1,31 @@ package ru.ulstu.models; public class Forecast { - private TimeSeries originalTimeSeries; + private Model model; private TimeSeries forecast; - public Forecast(TimeSeries originalTimeSeries) { - this.originalTimeSeries = originalTimeSeries; - this.forecast = new TimeSeries("Forecast time series of '" + originalTimeSeries.getName() + "'"); + public Forecast(Model model) { + this.model = model; + this.forecast = new TimeSeries("Forecast time series of '" + model.getOriginalTimeSeries().getName() + "'"); } - public TimeSeries getOriginalTimeSeries() { - return originalTimeSeries; + public Model getModel() { + return model; } - public TimeSeries getForecast() { + public TimeSeries getForecastTimeSeries() { return forecast; } public void addValue(TimeSeriesValue timeSeriesValue) { forecast.addValue(timeSeriesValue); } + + @Override + public String toString() { + return "Forecast{" + + "model=" + model + + ", forecast=" + forecast + + '}'; + } } diff --git a/src/main/java/ru/ulstu/models/ModelTimeSeries.java b/src/main/java/ru/ulstu/models/Model.java similarity index 84% rename from src/main/java/ru/ulstu/models/ModelTimeSeries.java rename to src/main/java/ru/ulstu/models/Model.java index 29b872c..534e112 100644 --- a/src/main/java/ru/ulstu/models/ModelTimeSeries.java +++ b/src/main/java/ru/ulstu/models/Model.java @@ -1,10 +1,10 @@ package ru.ulstu.models; -public class ModelTimeSeries { +public class Model { private TimeSeries originalTimeSeries; private TimeSeries modelTimeSeries; - public ModelTimeSeries(TimeSeries originalTimeSeries) { + public Model(TimeSeries originalTimeSeries) { this.originalTimeSeries = originalTimeSeries; this.modelTimeSeries = new TimeSeries("Model time series of '" + originalTimeSeries.getName() + "'"); } @@ -22,6 +22,6 @@ public class ModelTimeSeries { } public void addValue(TimeSeriesValue basedOnValue, double value) { - modelTimeSeries.getValues().add(new TimeSeriesValue(basedOnValue.getDate().plusDays(1), value)); + modelTimeSeries.getValues().add(new TimeSeriesValue(basedOnValue.getDate(), value)); } } diff --git a/src/main/java/ru/ulstu/models/TimeSeries.java b/src/main/java/ru/ulstu/models/TimeSeries.java index 7515cf3..619412e 100644 --- a/src/main/java/ru/ulstu/models/TimeSeries.java +++ b/src/main/java/ru/ulstu/models/TimeSeries.java @@ -59,4 +59,26 @@ public class TimeSeries { public int getLength() { return values.size(); } + + public TimeSeriesValue getFirstValue() { + if ((values.size() > 0)) { + return values.get(0); + } + throw new RuntimeException("Временной ряд пуст"); + } + + public Double getNumericValue(int t) { + if ((values.size() > t) && (t >= 0)) { + return values.get(t).getValue(); + } + throw new RuntimeException("Индекс выходит за границы временного ряда"); + } + + @Override + public String toString() { + return "TimeSeries{" + + "values=" + values + + ", name='" + name + '\'' + + '}'; + } } diff --git a/src/main/java/ru/ulstu/models/TimeSeriesValue.java b/src/main/java/ru/ulstu/models/TimeSeriesValue.java index 4fe4c0e..a96df51 100644 --- a/src/main/java/ru/ulstu/models/TimeSeriesValue.java +++ b/src/main/java/ru/ulstu/models/TimeSeriesValue.java @@ -3,23 +3,28 @@ package ru.ulstu.models; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Objects; public class TimeSeriesValue { - private LocalDate date; + private LocalDateTime date; private Double value; @JsonCreator - public TimeSeriesValue(@JsonProperty(value="date") LocalDate date, @JsonProperty(value = "value") Double value) { + public TimeSeriesValue(@JsonProperty(value = "date") LocalDateTime date, @JsonProperty(value = "value") Double value) { this.date = date; this.value = value; } - public LocalDate getDate() { + public TimeSeriesValue(LocalDateTime date) { + this.date = date; + } + + public LocalDateTime getDate() { return date; } - public void setDate(LocalDate date) { + public void setDate(LocalDateTime date) { this.date = date; } @@ -30,4 +35,23 @@ public class TimeSeriesValue { public void setValue(Double value) { this.value = value; } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeSeriesValue that = (TimeSeriesValue) o; + return Objects.equals(date, that.date) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(date, value); + } } diff --git a/src/main/java/ru/ulstu/models/exceptions/ForecastValidateException.java b/src/main/java/ru/ulstu/models/exceptions/ForecastValidateException.java new file mode 100644 index 0000000..083ef0e --- /dev/null +++ b/src/main/java/ru/ulstu/models/exceptions/ForecastValidateException.java @@ -0,0 +1,7 @@ +package ru.ulstu.models.exceptions; + +public class ForecastValidateException extends ModelingException { + public ForecastValidateException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java b/src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java index 7138862..1137ed7 100644 --- a/src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java +++ b/src/main/java/ru/ulstu/models/exceptions/TimeSeriesValidateException.java @@ -1,6 +1,6 @@ package ru.ulstu.models.exceptions; -public class TimeSeriesValidateException extends Exception { +public class TimeSeriesValidateException extends ModelingException { public TimeSeriesValidateException(String message) { super(message); } diff --git a/src/main/java/ru/ulstu/services/TimeSeriesService.java b/src/main/java/ru/ulstu/services/TimeSeriesService.java index 4bf9a40..ccee4d0 100644 --- a/src/main/java/ru/ulstu/services/TimeSeriesService.java +++ b/src/main/java/ru/ulstu/services/TimeSeriesService.java @@ -6,10 +6,10 @@ import org.springframework.stereotype.Service; import ru.ulstu.models.Forecast; import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeriesValue; -import ru.ulstu.models.exceptions.TimeSeriesValidateException; +import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.tsMethods.exponential.NoTrendNoSeason; -import java.time.LocalDate; +import java.time.LocalDateTime; @Service @@ -18,7 +18,7 @@ public class TimeSeriesService { public TimeSeries getRandomTimeSeries(int length) { TimeSeries ts = new TimeSeries("Random time series"); - LocalDate dateStart = LocalDate.now().minusDays(length); + LocalDateTime dateStart = LocalDateTime.now().minusDays(length); for (int i = 0; i < length; i++) { ts.getValues().add(new TimeSeriesValue(dateStart, Math.random())); dateStart = dateStart.plusDays(1); @@ -26,15 +26,8 @@ public class TimeSeriesService { return ts; } - public Forecast getForecast(TimeSeries timeSeries, int countForecast) { - try { - NoTrendNoSeason nn = new NoTrendNoSeason(timeSeries); - nn.setAlpa(0.1); - nn.createModel(); - return nn.getForecast(); - } catch (TimeSeriesValidateException ex) { - LOGGER.error("Некорректная инициализация метода моделирования", ex); - return null; - } + public Forecast getForecast(TimeSeries timeSeries, int countPoints) throws ModelingException { + NoTrendNoSeason nn = new NoTrendNoSeason(0.1); + return nn.getForecast(timeSeries, countPoints); } } diff --git a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java index 0eb6d70..5e74e89 100644 --- a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java +++ b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java @@ -1,45 +1,129 @@ package ru.ulstu.tsMethods; import ru.ulstu.models.Forecast; -import ru.ulstu.models.ModelTimeSeries; +import ru.ulstu.models.Model; import ru.ulstu.models.TimeSeries; +import ru.ulstu.models.TimeSeriesValue; +import ru.ulstu.models.exceptions.ForecastValidateException; +import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.TimeSeriesValidateException; -import java.util.HashMap; -import java.util.Map; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +/** + * Наиболее общая логика моделировани и прогнозирования временных рядов + */ public abstract class TimeSeriesMethod { - protected TimeSeries originalTimeSeries; - protected ModelTimeSeries model; - protected Forecast forecast; - protected int countForecast; - protected Map parameters = new HashMap<>(); - - protected TimeSeriesMethod(TimeSeries originalTimeSeries) throws TimeSeriesValidateException { - validateTimeSeries(originalTimeSeries); - this.originalTimeSeries = originalTimeSeries; - model = new ModelTimeSeries(originalTimeSeries); - forecast = new Forecast(originalTimeSeries); + /** + * Возвращает модельное представление временного ряда: для тех же точек времени что и в параметре timeSeries + * строится модель. Количество точек может быть изменено: сокращено при сжатии ряда, увеличено при интерполяции. + * Метод является шаблонным, выполняет операции валидации исходного ряда и потом его моделирование + * + * @param timeSeries исходный временной ряд подлежащий моделированию + * @return модель временного ряда + * @throws TimeSeriesValidateException + */ + public Model getModel(TimeSeries timeSeries) throws TimeSeriesValidateException { + validateTimeSeries(timeSeries); + return getModelOfValidTimeSeries(timeSeries); } - public TimeSeries getOriginalTimeSeries() { - return originalTimeSeries; - } + /** + * Возвращает модельное представление валидного временного ряда: для тех же точек времени что и в параметре timeSeries + * строится модель. Количество точек может быть изменено: сокращено при сжатии ряда, увеличено при интерполяции. + * + * @param timeSeries исходный временной ряд подлежащий моделированию + * @return + */ + protected abstract Model getModelOfValidTimeSeries(TimeSeries timeSeries); - public ModelTimeSeries getModel() { - return model; - } - - public Forecast getForecast() { + /** + * Выполняет построение прогноза временного ряда. Даты спрогнозированных точек будут сгенерированы по модельным точкам. + * + * @param model модель временного ряда + * @param countPoints количество точек для прогнозирования + * @return прогноз временного ряда + */ + public Forecast getForecast(Model model, int countPoints) throws ModelingException { + Forecast forecast = new Forecast(model); + forecast = generateEmptyForecastPoints(forecast, countPoints); + forecast = makeForecast(forecast); + if (!forecast.getForecastTimeSeries().getFirstValue() + .equals(forecast.getModel().getModelTimeSeries().getLastValue())) { + throw new ForecastValidateException("Первая точка прогноза должна совпадать с последней модельной точкой"); + } return forecast; } - public abstract void createModel(); + /** + * Выполняет построение прогноза ждя уже сгенерированных будущих точек временного ряда. + * + * @param forecast Заготовка прогноза временного ряда с пустыми значениями + * @return + */ + protected abstract Forecast makeForecast(Forecast forecast); + + protected Forecast generateEmptyForecastPoints(Forecast forecast, int countPointForecast) { + long diffMilliseconds = getTimeDifferenceInMilliseconds(forecast); + LocalDateTime lastTimeSeriesDateTime = forecast + .getModel() + .getOriginalTimeSeries() + .getLastValue() + .getDate() + .atZone(ZoneId.systemDefault()) + .toLocalDateTime(); + forecast.getForecastTimeSeries() + .addValue(new TimeSeriesValue(forecast.getModel().getModelTimeSeries().getLastValue().getDate())); + for (int i = 1; i < countPointForecast + 1; i++) { + forecast.getForecastTimeSeries() + .addValue(new TimeSeriesValue(forecast.getForecastTimeSeries().getValues().get(i - 1).getDate().plus(diffMilliseconds, ChronoUnit.MILLIS))); + } + return forecast; + } + + /** + * Вычисляет среднее значение между датами исходного временного ряда + * + * @param forecast объект, содержащий результат прогнозирования + * @return средняя разница между датами исходного временного ряда в миллисекундах + */ + protected long getTimeDifferenceInMilliseconds(Forecast forecast) { + long diffMilliseconds = 0; + for (int i = 1; i < forecast.getModel().getOriginalTimeSeries().getLength(); i++) { + diffMilliseconds += forecast.getModel().getOriginalTimeSeries().getValues().get(i - 1).getDate() + .until(forecast.getModel().getOriginalTimeSeries().getValues().get(i).getDate(), ChronoUnit.MILLIS); + } + return diffMilliseconds / (forecast.getModel().getOriginalTimeSeries().getLength() - 1); + } + + /** + * Выполняет построение модели и прогноза временного ряда. Даты спрогнозированных точек будут сгенерированы + * по модельным точкам. + * + * @param timeSeries временной ряда + * @param countPoints количество точек для прогнозирования + * @return прогноз временного ряда + */ + public Forecast getForecast(TimeSeries timeSeries, int countPoints) throws ModelingException { + validateForecastParams(countPoints); + return getForecast(getModel(timeSeries), countPoints); + } + + private void validateForecastParams(int countPoints) throws ForecastValidateException { + if (countPoints < 1) { + throw new ForecastValidateException("Количество прогнозных точек должно быть больше 0"); + } + } private void validateTimeSeries(TimeSeries timeSeries) throws TimeSeriesValidateException { if (timeSeries == null || timeSeries.isEmpty()) { throw new TimeSeriesValidateException("Временной ряд должен быть не пустым"); } + if (timeSeries.getLength() < 2) { + throw new TimeSeriesValidateException("Временной ряд должен содержать хотя бы 2 точки"); + } if (timeSeries.getValues().stream().anyMatch(val -> val == null || val.getValue() == null)) { throw new TimeSeriesValidateException("Временной ряд содержит пустые значения"); } diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java index cdc8d50..13ac7ef 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java @@ -1,12 +1,45 @@ package ru.ulstu.tsMethods.exponential; +import ru.ulstu.models.Forecast; +import ru.ulstu.models.Model; import ru.ulstu.models.TimeSeries; -import ru.ulstu.models.exceptions.TimeSeriesValidateException; -import ru.ulstu.tsMethods.Param; import ru.ulstu.tsMethods.TimeSeriesMethod; public class NoTrendNoSeason extends TimeSeriesMethod { - public NoTrendNoSeason(TimeSeries originalTimeSeries) throws TimeSeriesValidateException { + private double alpha; + + public NoTrendNoSeason(double alpha) { + this.alpha = alpha; + } + + @Override + protected Model getModelOfValidTimeSeries(TimeSeries timeSeries) { + Model model = new Model(timeSeries); + model.addValue(timeSeries.getFirstValue()); + //выполняется проход модели по сглаживанию + for (int t = 1; t < timeSeries.getValues().size(); t++) { + model.addValue(timeSeries.getValues().get(t), + (1 - alpha) * timeSeries.getNumericValue(t) + + alpha * model.getModelTimeSeries().getValues().get(t - 1).getValue()); + } + return model; + } + + @Override + protected Forecast makeForecast(Forecast forecast) { + forecast.getForecastTimeSeries() + .getFirstValue() + .setValue(forecast.getModel().getModelTimeSeries().getLastValue().getValue()); + for (int t = 1; t < forecast.getForecastTimeSeries().getLength(); t++) { + forecast.getForecastTimeSeries() + .getValues() + .get(t) + .setValue(alpha * forecast.getForecastTimeSeries().getValues().get(t - 1).getValue()); + } + return forecast; + } + +/* public NoTrendNoSeason(TimeSeries originalTimeSeries) throws TimeSeriesValidateException { super(originalTimeSeries); model.addValue(originalTimeSeries.getValues().get(0)); forecast.addValue(originalTimeSeries.getValues().get(0)); @@ -34,4 +67,5 @@ public class NoTrendNoSeason extends TimeSeriesMethod { forecast.addValue(model.getModelTimeSeries().getLastValue()); // прогноз } } + */ } From be9a761f0f14694d4640d8d45c15a700e82db5ef Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 1 Oct 2020 08:41:49 +0400 Subject: [PATCH 5/7] #2 -- first implementations of date generator, modeling and forecasting --- .../ulstu/controllers/AdviceController.java | 59 +++++++++++++++++++ .../models/response/ControllerResponse.java | 24 ++++++++ .../response/ControllerResponseError.java | 23 ++++++++ .../ulstu/models/response/ErrorConstants.java | 29 +++++++++ .../ru/ulstu/models/response/Response.java | 15 +++++ .../models/response/ResponseExtended.java | 11 ++++ 6 files changed, 161 insertions(+) create mode 100644 src/main/java/ru/ulstu/controllers/AdviceController.java create mode 100644 src/main/java/ru/ulstu/models/response/ControllerResponse.java create mode 100644 src/main/java/ru/ulstu/models/response/ControllerResponseError.java create mode 100644 src/main/java/ru/ulstu/models/response/ErrorConstants.java create mode 100644 src/main/java/ru/ulstu/models/response/Response.java create mode 100644 src/main/java/ru/ulstu/models/response/ResponseExtended.java diff --git a/src/main/java/ru/ulstu/controllers/AdviceController.java b/src/main/java/ru/ulstu/controllers/AdviceController.java new file mode 100644 index 0000000..de504d1 --- /dev/null +++ b/src/main/java/ru/ulstu/controllers/AdviceController.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020. Anton Romanov + * 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 ru.ulstu.controllers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpServerErrorException; +import ru.ulstu.models.exceptions.ForecastValidateException; +import ru.ulstu.models.exceptions.TimeSeriesValidateException; +import ru.ulstu.models.response.ErrorConstants; +import ru.ulstu.models.response.ResponseExtended; + +@RestController +@ControllerAdvice +public class AdviceController { + private final Logger log = LoggerFactory.getLogger(AdviceController.class); + + private ResponseExtended handleException(ErrorConstants error, E errorData) { + log.warn(error.toString()); + return new ResponseExtended<>(error, errorData); + } + + @ExceptionHandler(Exception.class) + public ResponseExtended handleUnknownException(Throwable e) { + e.printStackTrace(); + return handleException(ErrorConstants.UNKNOWN, e.getMessage()); + } + + @ExceptionHandler(HttpServerErrorException.class) + public ResponseExtended handleHttpClientException(Throwable e) { + return handleException(ErrorConstants.HTTP_CLIENT_ERROR, e.getMessage()); + } + + @ExceptionHandler(TimeSeriesValidateException.class) + public ResponseExtended handleTimeSeriesValidateException(Throwable e) { + return handleException(ErrorConstants.TIME_SERIES_VALIDATE_ERROR, e.getMessage()); + } + + @ExceptionHandler(ForecastValidateException.class) + public ResponseExtended handleForecastValidateException(Throwable e) { + return handleException(ErrorConstants.FORECAST_PARAMS_ERROR, e.getMessage()); + } +} diff --git a/src/main/java/ru/ulstu/models/response/ControllerResponse.java b/src/main/java/ru/ulstu/models/response/ControllerResponse.java new file mode 100644 index 0000000..fa3d13a --- /dev/null +++ b/src/main/java/ru/ulstu/models/response/ControllerResponse.java @@ -0,0 +1,24 @@ +package ru.ulstu.models.response; + +class ControllerResponse { + private final D data; + private final ControllerResponseError error; + + ControllerResponse(D data) { + this.data = data; + this.error = null; + } + + ControllerResponse(ControllerResponseError error) { + this.data = null; + this.error = error; + } + + public D getData() { + return data; + } + + public ControllerResponseError getError() { + return error; + } +} diff --git a/src/main/java/ru/ulstu/models/response/ControllerResponseError.java b/src/main/java/ru/ulstu/models/response/ControllerResponseError.java new file mode 100644 index 0000000..b4fa31c --- /dev/null +++ b/src/main/java/ru/ulstu/models/response/ControllerResponseError.java @@ -0,0 +1,23 @@ +package ru.ulstu.models.response; + +class ControllerResponseError { + private final ErrorConstants description; + private final D data; + + ControllerResponseError(ErrorConstants description, D data) { + this.description = description; + this.data = data; + } + + public int getCode() { + return description.getCode(); + } + + public String getMessage() { + return description.getMessage(); + } + + public D getData() { + return data; + } +} diff --git a/src/main/java/ru/ulstu/models/response/ErrorConstants.java b/src/main/java/ru/ulstu/models/response/ErrorConstants.java new file mode 100644 index 0000000..44c6bdf --- /dev/null +++ b/src/main/java/ru/ulstu/models/response/ErrorConstants.java @@ -0,0 +1,29 @@ +package ru.ulstu.models.response; + +public enum ErrorConstants { + UNKNOWN(0, "Unknown error"), + TIME_SERIES_VALIDATE_ERROR(10, "Некорректный временной ряд"), + FORECAST_PARAMS_ERROR(11, "Некорректные параметры для прогнозирования"), + HTTP_CLIENT_ERROR(66, "Http client error"); + + private final int code; + private final String message; + + ErrorConstants(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + @Override + public String toString() { + return String.format("%d: %s", code, message); + } +} diff --git a/src/main/java/ru/ulstu/models/response/Response.java b/src/main/java/ru/ulstu/models/response/Response.java new file mode 100644 index 0000000..fa5ba67 --- /dev/null +++ b/src/main/java/ru/ulstu/models/response/Response.java @@ -0,0 +1,15 @@ +package ru.ulstu.models.response; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class Response extends ResponseEntity { + + public Response(D data) { + super(new ControllerResponse(data), HttpStatus.OK); + } + + public Response(ErrorConstants error) { + super(new ControllerResponse(new ControllerResponseError<>(error, null)), HttpStatus.OK); + } +} diff --git a/src/main/java/ru/ulstu/models/response/ResponseExtended.java b/src/main/java/ru/ulstu/models/response/ResponseExtended.java new file mode 100644 index 0000000..c79be44 --- /dev/null +++ b/src/main/java/ru/ulstu/models/response/ResponseExtended.java @@ -0,0 +1,11 @@ +package ru.ulstu.models.response; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class ResponseExtended extends ResponseEntity { + + public ResponseExtended(ErrorConstants error, E errorData) { + super(new ControllerResponse(new ControllerResponseError(error, errorData)), HttpStatus.OK); + } +} From 85c4b3cad81e1da5b2e62232e8aa1b7e8c5a1d66 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Sat, 10 Oct 2020 13:36:35 +0400 Subject: [PATCH 6/7] #2 -- fix forecasting --- src/main/java/ru/ulstu/TimeSeriesUtils.java | 35 +++++++++++++++++++ .../controllers/TimeSeriesController.java | 12 +++++++ src/main/java/ru/ulstu/models/TimeSeries.java | 4 +++ .../java/ru/ulstu/models/TimeSeriesValue.java | 4 +++ .../ru/ulstu/services/TimeSeriesService.java | 33 ++++++++++++++++- .../ru/ulstu/tsMethods/TimeSeriesMethod.java | 29 ++------------- .../exponential/NoTrendNoSeason.java | 2 +- 7 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 src/main/java/ru/ulstu/TimeSeriesUtils.java diff --git a/src/main/java/ru/ulstu/TimeSeriesUtils.java b/src/main/java/ru/ulstu/TimeSeriesUtils.java new file mode 100644 index 0000000..d79e422 --- /dev/null +++ b/src/main/java/ru/ulstu/TimeSeriesUtils.java @@ -0,0 +1,35 @@ +package ru.ulstu; + +import ru.ulstu.models.TimeSeries; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +public class TimeSeriesUtils { + /** + * Вычисляет среднее значение между датами временного ряда + * + * @param timeSeries объект, содержащий временной ряд + * @return средняя разница между датами исходного временного ряда в миллисекундах + */ + public static long getTimeDifferenceInMilliseconds(TimeSeries timeSeries) { + long diffMilliseconds = 0; + for (int i = 1; i < timeSeries.getLength(); i++) { + diffMilliseconds += timeSeries.getValues().get(i - 1).getDate() + .until(timeSeries.getValues().get(i).getDate(), ChronoUnit.MILLIS); + } + return diffMilliseconds / (timeSeries.getLength() - 1); + } + + public static TimeSeries fillDates(TimeSeries timeSeries, long milliseconds) { + timeSeries.getLastValue().setDate(LocalDateTime.now()); + for (int i = timeSeries.getLength() - 2; i >= 0; i--) { + timeSeries.getValues().get(i).setDate(timeSeries.getValues().get(i + 1).getDate().minus(milliseconds, ChronoUnit.MILLIS)); + } + return timeSeries; + } + + public static TimeSeries fillDates(TimeSeries timeSeries) { + return fillDates(timeSeries, 1000 * 60 * 60 * 24); + } +} diff --git a/src/main/java/ru/ulstu/controllers/TimeSeriesController.java b/src/main/java/ru/ulstu/controllers/TimeSeriesController.java index 86c1752..835cbea 100644 --- a/src/main/java/ru/ulstu/controllers/TimeSeriesController.java +++ b/src/main/java/ru/ulstu/controllers/TimeSeriesController.java @@ -38,6 +38,18 @@ public class TimeSeriesController { return new ResponseEntity<>(timeSeriesService.getRandomTimeSeries(length), HttpStatus.OK); } + @GetMapping("getFromString") + @ApiOperation("Преобразовать строку с разделителями во временной ряд") + public ResponseEntity getTimeSeriesFromString(@RequestParam("tsString") String tsString) { + return new ResponseEntity<>(timeSeriesService.getTimeSeriesFromString(tsString), HttpStatus.OK); + } + + @PostMapping("timeSeriesToString") + @ApiOperation("Преобразовать временной ряд в строку с разделителями") + public ResponseEntity getTimeSeriesToString(@RequestBody TimeSeries timeSeries) { + return new ResponseEntity<>(timeSeriesService.getTimeSeriesToString(timeSeries), HttpStatus.OK); + } + @PostMapping("getForecast") @ApiOperation("Получить прогноз временного ряда") public ResponseEntity getForecastTimeSeries(@RequestBody ForecastParams forecastParams) throws ModelingException { diff --git a/src/main/java/ru/ulstu/models/TimeSeries.java b/src/main/java/ru/ulstu/models/TimeSeries.java index 619412e..68d0b1f 100644 --- a/src/main/java/ru/ulstu/models/TimeSeries.java +++ b/src/main/java/ru/ulstu/models/TimeSeries.java @@ -24,6 +24,10 @@ public class TimeSeries { } + public TimeSeries(List values) { + this.values = values; + } + public List getValues() { return values; } diff --git a/src/main/java/ru/ulstu/models/TimeSeriesValue.java b/src/main/java/ru/ulstu/models/TimeSeriesValue.java index a96df51..838a925 100644 --- a/src/main/java/ru/ulstu/models/TimeSeriesValue.java +++ b/src/main/java/ru/ulstu/models/TimeSeriesValue.java @@ -20,6 +20,10 @@ public class TimeSeriesValue { this.date = date; } + public TimeSeriesValue(Double value) { + this.value = value; + } + public LocalDateTime getDate() { return date; } diff --git a/src/main/java/ru/ulstu/services/TimeSeriesService.java b/src/main/java/ru/ulstu/services/TimeSeriesService.java index ccee4d0..78a8e03 100644 --- a/src/main/java/ru/ulstu/services/TimeSeriesService.java +++ b/src/main/java/ru/ulstu/services/TimeSeriesService.java @@ -3,6 +3,7 @@ package ru.ulstu.services; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import ru.ulstu.TimeSeriesUtils; import ru.ulstu.models.Forecast; import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeriesValue; @@ -10,6 +11,9 @@ import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.tsMethods.exponential.NoTrendNoSeason; import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; @Service @@ -27,7 +31,34 @@ public class TimeSeriesService { } public Forecast getForecast(TimeSeries timeSeries, int countPoints) throws ModelingException { - NoTrendNoSeason nn = new NoTrendNoSeason(0.1); + NoTrendNoSeason nn = new NoTrendNoSeason(0.8); return nn.getForecast(timeSeries, countPoints); } + + public TimeSeries getTimeSeriesFromString(String tsString) { + List tsValues = Arrays.stream(tsString.split("\n")) + .flatMap(v -> Arrays.stream(v.split(";"))) + .flatMap(v -> Arrays.stream(v.split(","))) + .flatMap(v -> Arrays.stream(v.split("
"))) + .filter(v -> { + try { + Double.parseDouble(v); + return true; + } catch (NumberFormatException e) { + return false; + } + }) + .map(Double::parseDouble) + .map(TimeSeriesValue::new) + .collect(Collectors.toList()); + return TimeSeriesUtils.fillDates(new TimeSeries(tsValues)); + } + + public String getTimeSeriesToString(TimeSeries timeSeries) { + return timeSeries + .getValues() + .stream() + .map(v -> v.getValue().toString().replaceAll("\\.", ",")) + .collect(Collectors.joining(";")); + } } diff --git a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java index 5e74e89..a6a055c 100644 --- a/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java +++ b/src/main/java/ru/ulstu/tsMethods/TimeSeriesMethod.java @@ -1,5 +1,6 @@ package ru.ulstu.tsMethods; +import ru.ulstu.TimeSeriesUtils; import ru.ulstu.models.Forecast; import ru.ulstu.models.Model; import ru.ulstu.models.TimeSeries; @@ -8,8 +9,6 @@ import ru.ulstu.models.exceptions.ForecastValidateException; import ru.ulstu.models.exceptions.ModelingException; import ru.ulstu.models.exceptions.TimeSeriesValidateException; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.time.temporal.ChronoUnit; /** @@ -58,7 +57,7 @@ public abstract class TimeSeriesMethod { } /** - * Выполняет построение прогноза ждя уже сгенерированных будущих точек временного ряда. + * Выполняет построение прогноза для уже сгенерированных будущих точек временного ряда. * * @param forecast Заготовка прогноза временного ряда с пустыми значениями * @return @@ -66,14 +65,7 @@ public abstract class TimeSeriesMethod { protected abstract Forecast makeForecast(Forecast forecast); protected Forecast generateEmptyForecastPoints(Forecast forecast, int countPointForecast) { - long diffMilliseconds = getTimeDifferenceInMilliseconds(forecast); - LocalDateTime lastTimeSeriesDateTime = forecast - .getModel() - .getOriginalTimeSeries() - .getLastValue() - .getDate() - .atZone(ZoneId.systemDefault()) - .toLocalDateTime(); + long diffMilliseconds = TimeSeriesUtils.getTimeDifferenceInMilliseconds(forecast.getModel().getOriginalTimeSeries()); forecast.getForecastTimeSeries() .addValue(new TimeSeriesValue(forecast.getModel().getModelTimeSeries().getLastValue().getDate())); for (int i = 1; i < countPointForecast + 1; i++) { @@ -83,21 +75,6 @@ public abstract class TimeSeriesMethod { return forecast; } - /** - * Вычисляет среднее значение между датами исходного временного ряда - * - * @param forecast объект, содержащий результат прогнозирования - * @return средняя разница между датами исходного временного ряда в миллисекундах - */ - protected long getTimeDifferenceInMilliseconds(Forecast forecast) { - long diffMilliseconds = 0; - for (int i = 1; i < forecast.getModel().getOriginalTimeSeries().getLength(); i++) { - diffMilliseconds += forecast.getModel().getOriginalTimeSeries().getValues().get(i - 1).getDate() - .until(forecast.getModel().getOriginalTimeSeries().getValues().get(i).getDate(), ChronoUnit.MILLIS); - } - return diffMilliseconds / (forecast.getModel().getOriginalTimeSeries().getLength() - 1); - } - /** * Выполняет построение модели и прогноза временного ряда. Даты спрогнозированных точек будут сгенерированы * по модельным точкам. diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java index 13ac7ef..ce8b2b5 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java @@ -34,7 +34,7 @@ public class NoTrendNoSeason extends TimeSeriesMethod { forecast.getForecastTimeSeries() .getValues() .get(t) - .setValue(alpha * forecast.getForecastTimeSeries().getValues().get(t - 1).getValue()); + .setValue(forecast.getForecastTimeSeries().getValues().get(t - 1).getValue()); } return forecast; } From dba64b8dee175af8c7e079690eec12103ed63cc5 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Mon, 2 Nov 2020 10:36:28 +0400 Subject: [PATCH 7/7] #2 -- fix code --- .../exponential/NoTrendNoSeason.java | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java index ce8b2b5..deca8ef 100644 --- a/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java +++ b/src/main/java/ru/ulstu/tsMethods/exponential/NoTrendNoSeason.java @@ -38,34 +38,4 @@ public class NoTrendNoSeason extends TimeSeriesMethod { } return forecast; } - -/* public NoTrendNoSeason(TimeSeries originalTimeSeries) throws TimeSeriesValidateException { - super(originalTimeSeries); - model.addValue(originalTimeSeries.getValues().get(0)); - forecast.addValue(originalTimeSeries.getValues().get(0)); - parameters.put(Param.ALPHA, 1.0); - } - - public void setAlpa(double value) { - parameters.put(Param.ALPHA, value); - } - - @Override - public void createModel() { - double e; - - //выполняется проход модели по сглаживанию и прогнозированию countPointForecast точек - for (int t = 0; t < originalTimeSeries.getValues().size() - 1 + countForecast; t++) { - // пока не дошли до конца ряда - сглаживаем, иначе строим прогноз - if (t < originalTimeSeries.getValues().size()) { - e = originalTimeSeries.getValues().get(t).getValue() - forecast.getForecast().getValues().get(t).getValue(); - } else { - e = 0; - } - model.addValue(model.getModelTimeSeries().getLastValue(), - model.getModelTimeSeries().getValues().get(t).getValue() + parameters.get(Param.ALPHA) * e); // уровень - forecast.addValue(model.getModelTimeSeries().getLastValue()); // прогноз - } - } - */ }