Merge pull request '#12 -- Divide TS presentation and forecasting' (#13) from 12-get-forecast into master

Reviewed-on: #13
This commit is contained in:
romanov73 2024-11-25 10:34:12 +04:00
commit de68ee9870
4 changed files with 71 additions and 45 deletions

View File

@ -54,53 +54,60 @@ public class IndexController {
if (chartForm.getTimeSeriesMeta() != null if (chartForm.getTimeSeriesMeta() != null
&& chartForm.getTimeSeriesMeta().getKey() != null && chartForm.getTimeSeriesMeta().getKey() != null
&& !chartForm.getTimeSeriesMeta().getKey().isEmpty()) { && !chartForm.getTimeSeriesMeta().getKey().isEmpty()) {
addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()), null, model); addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()),
null,
chartForm.isNeedForecast(),
model);
} }
return "index"; return "index";
} }
private void addChartToModel(TimeSeries timeSeries, String method, Model model) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException { private void addChartToModel(TimeSeries timeSeries, String method, boolean needForecast, Model model) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
int countForecastPoints = timeSeries.getLength() > 20 ? 10 : timeSeries.getLength() / 3; if (needForecast) {
TimeSeries timeSeriesModel; int countForecastPoints = timeSeries.getLength() > 20 ? 10 : timeSeries.getLength() / 3;
ModelingResult modelingResult; TimeSeries timeSeriesModel;
if (method == null) { ModelingResult modelingResult;
timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries).getTimeSeries(); if (method == null) {
modelingResult = timeSeriesService.getForecast(timeSeries, countForecastPoints); timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries).getTimeSeries();
} else { modelingResult = timeSeriesService.getForecast(timeSeries, countForecastPoints);
timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries, method).getTimeSeries();
modelingResult = timeSeriesService.getForecast(timeSeries, method, countForecastPoints);
}
TimeSeries forecast = modelingResult.getTimeSeries();
TimeSeries testForecast = modelingResult.getTestForecast();
model.addAttribute("dates", getDatesForChart(timeSeries, forecast));
model.addAttribute("timeSeries", timeSeries.getValues().stream().map(TimeSeriesValue::getValue).toArray());
// если временной ряд был сжат моделью, то для графика нужно вставить пустые значения
TimeSeries modelWithSkips = new TimeSeries(timeSeriesModel.getKey());
int j = 0;
for (int i = 0; i < timeSeries.getLength(); i++) {
if (timeSeries.getValue(i).getDate().equals(timeSeriesModel.getValue(j).getDate())) {
modelWithSkips.addValue(timeSeriesModel.getValue(j));
j++;
} else { } else {
modelWithSkips.addValue(new TimeSeriesValue((Double) null)); timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries, method).getTimeSeries();
modelingResult = timeSeriesService.getForecast(timeSeries, method, countForecastPoints);
} }
TimeSeries forecast = modelingResult.getTimeSeries();
TimeSeries testForecast = modelingResult.getTestForecast();
// если временной ряд был сжат моделью, то для графика нужно вставить пустые значения
TimeSeries modelWithSkips = new TimeSeries(timeSeriesModel.getKey());
int j = 0;
for (int i = 0; i < timeSeries.getLength(); i++) {
if (timeSeries.getValue(i).getDate().equals(timeSeriesModel.getValue(j).getDate())) {
modelWithSkips.addValue(timeSeriesModel.getValue(j));
j++;
} else {
modelWithSkips.addValue(new TimeSeriesValue((Double) null));
}
}
model.addAttribute("model", modelWithSkips.getValues().stream().map(TimeSeriesValue::getValue).toArray());
timeSeries.getValues().remove(timeSeries.getValues().size() - 1);
List<Double> forecastValues = timeSeries.getValues().stream().map(v -> (Double) null).collect(Collectors.toList());
forecastValues.addAll(forecast.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList()));
model.addAttribute("forecast", forecastValues.toArray());
List<Double> testForecastValues = timeSeries.getValues()
.stream()
.skip(countForecastPoints)
.map(v -> (Double) null)
.collect(Collectors.toList());
testForecastValues.addAll(testForecast.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList()));
model.addAttribute("testForecast", testForecastValues.toArray());
model.addAttribute("forecastDescription", modelingResult);
model.addAttribute("statistic", statisticService.getStatistic(timeSeries));
model.addAttribute("dates", getDatesForChart(timeSeries, forecast));
} else {
model.addAttribute("dates", getDatesForChart(timeSeries, new TimeSeries()));
} }
model.addAttribute("model", modelWithSkips.getValues().stream().map(TimeSeriesValue::getValue).toArray()); model.addAttribute("timeSeries", timeSeries.getValues().stream().map(TimeSeriesValue::getValue).toArray());
timeSeries.getValues().remove(timeSeries.getValues().size() - 1);
List<Double> forecastValues = timeSeries.getValues().stream().map(v -> (Double) null).collect(Collectors.toList());
forecastValues.addAll(forecast.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList()));
model.addAttribute("forecast", forecastValues.toArray());
List<Double> testForecastValues = timeSeries.getValues()
.stream()
.skip(countForecastPoints)
.map(v -> (Double) null)
.collect(Collectors.toList());
testForecastValues.addAll(testForecast.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList()));
model.addAttribute("testForecast", testForecastValues.toArray());
model.addAttribute("forecastDescription", modelingResult);
model.addAttribute("statistic", statisticService.getStatistic(timeSeries));
} }
private List<String> getDatesForChart(TimeSeries timeSeries, TimeSeries forecast) { private List<String> getDatesForChart(TimeSeries timeSeries, TimeSeries forecast) {
@ -132,7 +139,10 @@ public class IndexController {
&& !chartForm.getTimeSeriesMeta().getKey().isEmpty() && !chartForm.getTimeSeriesMeta().getKey().isEmpty()
&& chartForm.getMethodClassName() != null && chartForm.getMethodClassName() != null
&& !chartForm.getMethodClassName().equals("")) { && !chartForm.getMethodClassName().equals("")) {
addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()), chartForm.getMethodClassName(), model); addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()),
chartForm.getMethodClassName(),
chartForm.isNeedForecast(),
model);
} }
return "method"; return "method";
} }

View File

@ -8,6 +8,8 @@ public class ChartForm {
private TimeSeriesMeta timeSeriesMeta; private TimeSeriesMeta timeSeriesMeta;
private String methodClassName = null; private String methodClassName = null;
private boolean needForecast;
public TimeSeriesSet getSet() { public TimeSeriesSet getSet() {
return set; return set;
} }
@ -31,4 +33,12 @@ public class ChartForm {
public void setMethodClassName(String methodClassName) { public void setMethodClassName(String methodClassName) {
this.methodClassName = methodClassName; this.methodClassName = methodClassName;
} }
public boolean isNeedForecast() {
return needForecast;
}
public void setNeedForecast(boolean needForecast) {
this.needForecast = needForecast;
}
} }

View File

@ -123,7 +123,10 @@
$('#select-ts').val([[*{timeSeriesMeta.key}]]); $('#select-ts').val([[*{timeSeriesMeta.key}]]);
$('#select-ts').selectpicker('refresh'); $('#select-ts').selectpicker('refresh');
</script> </script>
<input id="need-forecast" type="hidden" th:field="*{needForecast}">
<button type="button" class="btn btn-primary" onclick="$('#need-forecast').val(true); form.submit();">
Построить прогноз
</button>
<h5 th:if="${forecastDescription != null && forecastDescription.timeSeriesMethod != null}"> <h5 th:if="${forecastDescription != null && forecastDescription.timeSeriesMethod != null}">
Результаты моделирования: Результаты моделирования:
</h5> </h5>

View File

@ -120,7 +120,7 @@
<select id="select-ts" class="selectpicker form-group" data-live-search="true" <select id="select-ts" class="selectpicker form-group" data-live-search="true"
th:field="*{timeSeriesMeta}" th:field="*{timeSeriesMeta}"
data-width="90%" onchange="form.submit();"> data-width="90%" onchange="$('#need-forecast').val(false); form.submit();">
<option value="">Временной ряд</option> <option value="">Временной ряд</option>
<option th:each="ts : ${listTimeSeries}" <option th:each="ts : ${listTimeSeries}"
th:value="${ts.key}" th:value="${ts.key}"
@ -135,7 +135,7 @@
<select id="select-method" class="selectpicker form-group" data-live-search="true" <select id="select-method" class="selectpicker form-group" data-live-search="true"
th:field="*{methodClassName}" th:field="*{methodClassName}"
data-width="90%" onchange="form.submit();"> data-width="90%" onchange="$('#need-forecast').val(false); form.submit();">
<option value="">Метод прогнозирования</option> <option value="">Метод прогнозирования</option>
<option th:each="method : ${methods}" <option th:each="method : ${methods}"
th:value="${method.key}" th:value="${method.key}"
@ -148,7 +148,10 @@
</script> </script>
<input id="need-forecast" type="hidden" th:field="*{needForecast}">
<button type="button" class="btn btn-primary" onclick="$('#need-forecast').val(true); form.submit();">
Построить прогноз
</button>
<div th:if="${forecastDescription != null && forecastDescription.timeSeriesMethod != null}"> <div th:if="${forecastDescription != null && forecastDescription.timeSeriesMethod != null}">
<p> Метод прогнозирования: <span th:text="${forecastDescription.timeSeriesMethod}"> </span> <p> Метод прогнозирования: <span th:text="${forecastDescription.timeSeriesMethod}"> </span>
<p> Оценка: <span th:text="${forecastDescription.score.value}"> </span> <p> Оценка: <span th:text="${forecastDescription.score.value}"> </span>