#5 -- forecast db time series

This commit is contained in:
Anton Romanov 2021-08-02 11:39:41 +04:00
parent f92a2900a4
commit b0b68199ee
14 changed files with 151 additions and 229 deletions

View File

@ -16,7 +16,7 @@ public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/{articlename:\\w+}");
registry.addRedirectViewController("/", "/index.html");
//registry.addRedirectViewController("/", "/index.html");
}
@Override

View File

@ -11,12 +11,17 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import ru.ulstu.datamodel.ChartForm;
import ru.ulstu.datamodel.ModelingResult;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.datamodel.ts.TimeSeriesValue;
import ru.ulstu.db.DbService;
import ru.ulstu.service.TimeSeriesService;
import ru.ulstu.service.UtilService;
import springfox.documentation.annotations.ApiIgnore;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Set;
@ -27,23 +32,50 @@ import java.util.stream.Collectors;
public class IndexController {
private final UtilService utilService;
private final TimeSeriesService timeSeriesService;
private final DbService dbService;
private final static Logger LOG = LoggerFactory.getLogger(IndexController.class);
public IndexController(UtilService utilService,
TimeSeriesService timeSeriesService) {
TimeSeriesService timeSeriesService,
DbService dbService) {
this.utilService = utilService;
this.timeSeriesService = timeSeriesService;
this.dbService = dbService;
}
@GetMapping("/index.html")
public String index(Model model) {
TimeSeries timeSeries = utilService.getRandomTimeSeries(100);
@GetMapping("/")
public String index(Model model) throws IOException {
model.addAttribute("sets", dbService.getSets());
model.addAttribute("chartForm", new ChartForm());
return "index.html";
}
@GetMapping("chart")
public String chart(@ModelAttribute ChartForm chartForm, Model model) throws IOException {
model.addAttribute("sets", dbService.getSets());
if (chartForm.getSet() != null) {
model.addAttribute("listTimeSeries", dbService.getTimeSeriesMeta(chartForm.getSet()));
}
if (chartForm.getTimeSeriesMeta() != null
&& chartForm.getTimeSeriesMeta().getKey() != null
&& !chartForm.getTimeSeriesMeta().getKey().isEmpty()) {
addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()), model);
}
return "index.html";
}
private void addChartToModel(TimeSeries timeSeries, Model model) {
int countForecastPoints = 10;
TimeSeries timeSeriesModel = null;
ModelingResult modelingResult = null;
TimeSeries forecast = null;
TimeSeries testForecast = null;
try {
timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries);
forecast = timeSeriesService.getForecast(timeSeries, 5).getTimeSeries();
modelingResult = timeSeriesService.getForecast(timeSeries, countForecastPoints);
forecast = modelingResult.getTimeSeries();
testForecast = modelingResult.getTestForecast();
} catch (Exception e) {
LOG.warn(e.getMessage());
}
@ -58,9 +90,18 @@ public class IndexController {
model.addAttribute("timeSeries", timeSeries.getValues().stream().map(TimeSeriesValue::getValue).toArray());
model.addAttribute("model", timeSeriesModel.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());
return "index.html";
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);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel;
import ru.ulstu.db.model.TimeSeriesMeta;
import ru.ulstu.db.model.TimeSeriesSet;
public class ChartForm {
private TimeSeriesSet set;
private TimeSeriesMeta timeSeriesMeta;
public TimeSeriesSet getSet() {
return set;
}
public void setSet(TimeSeriesSet set) {
this.set = set;
}
public TimeSeriesMeta getTimeSeriesMeta() {
return timeSeriesMeta;
}
public void setTimeSeriesMeta(TimeSeriesMeta timeSeriesMeta) {
this.timeSeriesMeta = timeSeriesMeta;
}
}

View File

@ -12,7 +12,7 @@ public abstract class Model {
protected final TimeSeries timeSeriesModel;
protected Model(TimeSeries ts) {
timeSeriesModel = new TimeSeries("Model of", ts.getName());
timeSeriesModel = new TimeSeries("Model of", ts.getKey());
}
public TimeSeries getTimeSeriesModel() {

View File

@ -14,15 +14,18 @@ import java.util.List;
public class ModelingResult {
private final TimeSeries timeSeries;
private final TimeSeries testForecast;
private final List<MethodParamValue> paramValues;
private final Score score;
private final Method method;
public ModelingResult(TimeSeries timeSeries,
TimeSeries testForecast,
List<MethodParamValue> paramValues,
Score score,
Method method) {
this.timeSeries = timeSeries;
this.testForecast = testForecast;
this.paramValues = paramValues;
this.score = score;
this.method = method;
@ -43,4 +46,8 @@ public class ModelingResult {
public Method getTimeSeriesMethod() {
return method;
}
public TimeSeries getTestForecast() {
return testForecast;
}
}

View File

@ -23,7 +23,7 @@ public class Score {
}
public Number getValue() {
return value;
return ((double) Math.round(value.doubleValue() * 100)) / 100;
}
@JsonIgnore

View File

@ -15,20 +15,20 @@ import java.util.List;
public class TimeSeries {
private List<TimeSeriesValue> values = new ArrayList<>();
private String name;
private String key;
public TimeSeries(String name) {
this.name = name;
public TimeSeries(String key) {
this.key = key;
}
public TimeSeries(String prefix, String suffix) {
this.name = String.format("%s %s", prefix, suffix);
this.key = String.format("%s %s", prefix, suffix);
}
@JsonCreator
public TimeSeries(@JsonProperty(value = "values") List<TimeSeriesValue> values, @JsonProperty(value = "name") String name) {
public TimeSeries(@JsonProperty(value = "values") List<TimeSeriesValue> values, @JsonProperty(value = "name") String key) {
this.values = values;
this.name = name;
this.key = key;
}
public TimeSeries() {
@ -43,16 +43,12 @@ public class TimeSeries {
return values;
}
public void setValues(List<TimeSeriesValue> values) {
this.values = values;
public String getKey() {
return key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
public void setKey(String key) {
this.key = key;
}
public boolean isEmpty() {
@ -102,7 +98,7 @@ public class TimeSeries {
public String toString() {
return "TimeSeries{" +
"values=" + values +
", name='" + name + '\'' +
", name='" + key + '\'' +
'}';
}
}

View File

@ -86,7 +86,7 @@ public class DbFileService implements DbService {
if (!isTimeSeriesSetExists(timeSeriesSet)) {
addSet(timeSeriesSet.getKey());
}
BufferedWriter writer = new BufferedWriter(new FileWriter(Paths.get(getSetPath(timeSeriesSet).getAbsolutePath(), timeSeries.getName() + ".csv").toFile()));
BufferedWriter writer = new BufferedWriter(new FileWriter(Paths.get(getSetPath(timeSeriesSet).getAbsolutePath(), timeSeries.getKey() + ".csv").toFile()));
writer.write(new UtilService().getTimeSeriesToDateValueString(timeSeries));
writer.close();
createMetaFile(timeSeriesSet, timeSeries);
@ -95,7 +95,7 @@ public class DbFileService implements DbService {
private void createMetaFile(TimeSeriesSet timeSeriesSet, TimeSeries timeSeries) throws IOException {
TimeSeriesMeta timeSeriesMeta = new TimeSeriesMeta(timeSeries);
new ObjectMapper()
.writeValue(Paths.get(getSetPath(timeSeriesSet).getAbsolutePath(), timeSeries.getName() + ".csv.meta")
.writeValue(Paths.get(getSetPath(timeSeriesSet).getAbsolutePath(), timeSeries.getKey() + ".csv.meta")
.toFile(), timeSeriesMeta);
}

View File

@ -21,7 +21,7 @@ public class TimeSeriesMeta {
}
public TimeSeriesMeta(TimeSeries timeSeries) {
this.key = timeSeries.getName();
this.key = timeSeries.getKey();
this.hasDateTime = timeSeries.getValues().stream().anyMatch(v -> v.getDate() != null);
this.size = timeSeries.getLength();
}

View File

@ -98,7 +98,7 @@ public abstract class Method {
protected TimeSeries generateEmptyForecastPoints(TimeSeries model, int countPointForecast) {
long diffMilliseconds = TimeSeriesUtils.getTimeDifferenceInMilliseconds(model);
TimeSeries forecast = new TimeSeries("Forecast of " + model.getName());
TimeSeries forecast = new TimeSeries("Forecast of " + model.getKey());
forecast.addValue(new TimeSeriesValue(model.getLastValue().getDate()));
for (int i = 1; i < countPointForecast + 1; i++) {
forecast.addValue(new TimeSeriesValue(forecast.getValues().get(i - 1).getDate().plus(diffMilliseconds, ChronoUnit.MILLIS)));

View File

@ -1,144 +0,0 @@
///*
// * Copyright (C) 2021 Anton Romanov - All Rights Reserved
// * You may use, distribute and modify this code, please write to: romanov73@gmail.com.
// *
// */
//
//package ru.ulstu.page;
//
//import org.primefaces.model.chart.AxisType;
//import org.primefaces.model.chart.DateAxis;
//import org.primefaces.model.chart.LegendPlacement;
//import org.primefaces.model.chart.LineChartModel;
//import org.primefaces.model.chart.LineChartSeries;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import ru.ulstu.datamodel.exception.ModelingException;
//import ru.ulstu.datamodel.ts.TimeSeries;
//import ru.ulstu.datamodel.ts.TimeSeriesValue;
//import ru.ulstu.db.DbService;
//import ru.ulstu.db.model.TimeSeriesMeta;
//import ru.ulstu.db.model.TimeSeriesSet;
//import ru.ulstu.service.TimeSeriesService;
//import ru.ulstu.service.UtilService;
//
//import javax.annotation.PostConstruct;
//import javax.faces.model.SelectItem;
//import javax.faces.view.ViewScoped;
//import javax.inject.Named;
//import java.io.IOException;
//import java.io.Serializable;
//import java.lang.reflect.InvocationTargetException;
//import java.time.format.DateTimeFormatter;
//import java.util.List;
//import java.util.concurrent.ExecutionException;
//import java.util.stream.Collectors;
//
//@Named
//@ViewScoped
//public class IndexView implements Serializable {
// private final static Logger LOG = LoggerFactory.getLogger(IndexView.class);
//
// @Autowired
// private transient TimeSeriesService timeSeriesService;
//
// @Autowired
// private transient DbService dbService;
//
// @Autowired
// private transient UtilService utilService;
//
// private LineChartModel model;
// private List<TimeSeriesMeta> timeSeriesMetas;
// private TimeSeriesMeta timeSeriesMeta;
//
// @PostConstruct
// public void init() throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException, IOException {
// timeSeriesMetas = dbService.getTimeSeriesMeta(new TimeSeriesSet("NN3"));
// timeSeriesMeta = timeSeriesMetas.get(0);
// createChart();
// }
//
// private LineChartModel initLinearModel() throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException, IOException {
// LineChartModel model = new LineChartModel();
//
// LineChartSeries series1 = new LineChartSeries();
// series1.setLabel("Временной ряд");
//
// TimeSeries timeSeries = dbService.getTimeSeries(new TimeSeriesSet("NN3"), timeSeriesMeta.getKey());
// for (TimeSeriesValue value : timeSeries.getValues()) {
// series1.set(DateTimeFormatter.ISO_LOCAL_DATE.format(value.getDate()), value.getValue());
// }
//
// LineChartSeries series2 = new LineChartSeries();
// series2.setLabel("Сглаженный ряд");
// try {
// TimeSeries smoothedTimeSeries = timeSeriesService.smoothTimeSeries(timeSeries);
// for (TimeSeriesValue value : smoothedTimeSeries.getValues()) {
// series2.set(DateTimeFormatter.ISO_LOCAL_DATE.format(value.getDate()), value.getValue());
// }
// } catch (Exception ex) {
// LOG.warn(ex.getMessage());
// }
// LineChartSeries series3 = new LineChartSeries();
// series3.setLabel("Прогноз");
// TimeSeries forecast = timeSeriesService.getForecast(timeSeries, 20).getTimeSeries();
// for (TimeSeriesValue value : forecast.getValues()) {
// series3.set(DateTimeFormatter.ISO_LOCAL_DATE.format(value.getDate()), value.getValue());
// }
// model.addSeries(series1);
// model.addSeries(series2);
// model.addSeries(series3);
// return model;
// }
//
// public void createChart() throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException, IOException {
// model = initLinearModel();
// model.setTitle("Сглаживание временного ряда");
// model.setLegendPosition("d");
// model.setLegendPlacement(LegendPlacement.OUTSIDEGRID);
// DateAxis xAxis = new DateAxis("Time");
// xAxis.setTickFormat("%#d %b %Y");
// model.getAxes().put(AxisType.X, xAxis);
// }
//
// public LineChartModel getModel() {
// return model;
// }
//
// public List<TimeSeriesMeta> getTimeSeriesMetas() {
// return timeSeriesMetas;
// }
//
// public TimeSeriesMeta getTimeSeriesMeta() {
// return timeSeriesMeta;
// }
//
// public void setTimeSeriesMeta(String timeSeriesMeta) {
// System.out.println(timeSeriesMeta);
// try {
// createChart();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public void setTimeSeriesMeta(Object timeSeriesMeta) {
// System.out.println(timeSeriesMeta);
// try {
// createChart();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public void setTimeSeriesMeta(TimeSeriesMeta timeSeriesMeta) {
// System.out.println(timeSeriesMeta);
// try {
// createChart();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//}

View File

@ -48,7 +48,7 @@ public class MethodParamBruteForce {
List<ModelingResult> results2 = new CopyOnWriteArrayList<>();
final int countPoints = (countPointsForecast > timeSeries.getLength()) ? timeSeries.getLength() / 3 : countPointsForecast;
TimeSeries reducedTimeSeries = new TimeSeries(timeSeries.getValues().stream().limit(timeSeries.getLength() - countPoints).collect(Collectors.toList()),
"test part of " + timeSeries.getName());
"test part of " + timeSeries.getKey());
Map<LocalDateTime, Double> tsValues = timeSeries.getValues().stream()
.collect(Collectors.toMap(TimeSeriesValue::getDate, TimeSeriesValue::getValue));
@ -60,7 +60,7 @@ public class MethodParamBruteForce {
if (methodInstance.canMakeForecast(reducedTimeSeries, parametersValues, countPoints)) {
results.add(executors.submit(() -> {
TimeSeries forecast = methodInstance.getForecast(reducedTimeSeries, parametersValues, countPoints);
return new ModelingResult(forecast,
return new ModelingResult(forecast, null,
parametersValues,
scoreMethod.getScore(tsValues, forecast),
methodInstance);
@ -81,6 +81,7 @@ public class MethodParamBruteForce {
forecast.getValue(0).setValue(timeSeries.getNumericValue(timeSeries.getLength() - 1));
return new ModelingResult(forecast,
bestResult.getTimeSeries(),
bestResult.getParamValues(),
bestResult.getScore(),
bestResult.getTimeSeriesMethod());
@ -105,6 +106,7 @@ public class MethodParamBruteForce {
results.add(executors.submit(() -> {
Model model = methodInstance.getModel(timeSeries, parametersValues);
return new ModelingResult(model.getTimeSeriesModel(),
null,
parametersValues,
scoreMethod.getScore(tsValues, model.getTimeSeriesModel()),
methodInstance);

View File

@ -1,49 +0,0 @@
<!--
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
~
-->
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<div class="container" layout:fragment="content">
<h1>Ошибка</h1>
<p th:if="${url}">
<b>Страница:</b> <span th:text="${url}">Page URL</span>
</p>
<p th:if="${timestamp}" id='created'>
<b>Время:</b> <span th:text="${timestamp}">Timestamp</span>
</p>
<p th:if="${status}">
<b>Response Status:</b> <span th:text="${status}">status-code</span> <span
th:if="${error}" th:text="'('+${error}+')'">error ...</span>
</p>
<p>
<a class="btn btn-primary" data-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false"
aria-controls="collapseExample">
Показать stack trace
</a>
</p>
<!-- <div class="collapse" id="collapseExample">-->
<!-- <p class="card card-body">-->
<!-- <div th:utext="'Failed URL: ' + ${url}" th:remove="tag">${url}</div>-->
<!-- </p>-->
<!-- <div th:if="${exception != null}" th:utext="'Exception: ' + ${exception.message}" th:remove="tag">-->
<!-- ${exception.message}-->
<!-- </div>-->
<!-- <ul th:remove="tag">-->
<!-- <li th:if="${exception != null && exception.stackTrace != null}" th:each="ste : ${exception.stackTrace}"-->
<!-- th:remove="tag"><span-->
<!-- th:utext="${ste}" th:remove="tag">${ste}</span></li>-->
<!-- </ul>-->
<!-- </div>-->
</div>
</div>
</html>

View File

@ -14,7 +14,7 @@
</head>
<div class="container" layout:fragment="content">
<script src="/webjars/highcharts/7.0.0/highcharts.js"></script>
<script th:inline="javascript">
<script th:inline="javascript" th:if="${timeSeries != null}">
$(document).ready(function () {
var title = {
text: 'Прогноз временного ряда'
@ -48,7 +48,7 @@
states: {
hover: {
marker: {
enabled: false
enabled: true
}
}
},
@ -69,6 +69,11 @@
color: 'rgba(255,200,0,0.5)',
data: [[${model}]]
},
{
name: 'Тестовый прогноз',
color: 'rgba(255,0,0,0.5)',
data: [[${testForecast}]]
},
{
name: 'Прогноз',
color: 'rgba(255,0,0,0.5)',
@ -82,15 +87,48 @@
json.yAxis = yAxis;
json.series = series;
json.plotOptions = plotOptions;
$('#container').highcharts(json);
$('#chart').highcharts(json);
});
</script>
<form>
<!-- <form action="#" th:action="/index" th:object="${filterForm}" method="get">-->
<form action="#" th:action="chart" th:object="${chartForm}">
<div class="row">
<div id="container" style="width: 550px; height: 400px; margin: 0 auto"></div>
<div class="col-3">
<select id="select-set" class="selectpicker" data-live-search="true" th:field="*{set}"
data-width="90%" onchange="$('#select-ts').val(''); form.submit();">
<option value="">Набор временных рядов</option>
<option th:each="set : ${sets}"
th:value="${set.key}"
th:utext="${set.key}">
</option>
</select>
<script th:inline="javascript" th:if="*{set != null}">
$('#select-set').val([[*{set.key}]]);
$('#select-set').selectpicker('refresh');
</script>
<select id="select-ts" class="selectpicker" data-live-search="true" th:field="*{timeSeriesMeta}"
data-width="90%" onchange="form.submit();">
<option value="">Временной ряд</option>
<option th:each="ts : ${listTimeSeries}"
th:value="${ts.key}"
th:utext="${ts.key}">
</option>
</select>
<script th:inline="javascript" th:if="*{timeSeriesMeta != null}">
$('#select-ts').val([[*{timeSeriesMeta.key}]]);
$('#select-ts').selectpicker('refresh');
</script>
<p> Метод прогнозирования: <span th:text="${forecastDescription.timeSeriesMethod}"> </span>
<p> Оценка: <span th:text="${forecastDescription.score.value}"> </span>
</div>
<div class="col-9">
<div id="chart" style="width: 550px; height: 400px; margin: 0 auto"></div>
</div>
</div>
</form>
</div>