2021-06-01 09:34:05 +04:00

146 lines
6.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
*
* * 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.tsMethods;
import com.fasterxml.jackson.annotation.JsonIgnore;
import ru.ulstu.TimeSeriesUtils;
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.time.temporal.ChronoUnit;
import java.util.List;
/**
* Наиболее общая логика моделировании и прогнозирования временных рядов
*/
public abstract class TimeSeriesMethod {
@JsonIgnore
protected TimeSeries originalTimeSeries;
@JsonIgnore
private TimeSeries model;
public abstract TimeSeriesMethod createFor(TimeSeries originalTimeSeries);
/**
* Возвращает модельное представление временного ряда: для тех же точек времени что и в параметре timeSeries
* строится модель. Количество точек может быть изменено: сокращено при сжатии ряда, увеличено при интерполяции.
* Метод является шаблонным, выполняет операции валидации исходного ряда и потом его моделирование
*
* @throws ModelingException генерируется, если есть проблемы моделирования при задании параметров
*/
protected void makeModel() throws ModelingException {
validateTimeSeries();
model = getModelOfValidTimeSeries();
}
/**
* Возвращает модельное представление валидного временного ряда: для тех же точек времени что и в параметре timeSeries
* строится модель. Количество точек может быть изменено: сокращено при сжатии ряда, увеличено при интерполяции.
*
* @return модельное представление временного ряда
*/
protected abstract TimeSeries getModelOfValidTimeSeries() throws ModelingException;
/**
* Выполняет построение прогноза временного ряда. Даты спрогнозированных точек будут сгенерированы по модельным точкам.
*
* @param countPoints количество точек для прогнозирования
* @return прогноз временного ряда
*/
public TimeSeries getForecastWithValidParams(int countPoints) throws ModelingException {
TimeSeries forecast = generateEmptyForecastPoints(originalTimeSeries, countPoints);
getModel();
forecast.getFirstValue().setValue(originalTimeSeries.getLastValue().getValue());
forecast = makeForecast(forecast);
return forecast;
}
/**
* Выполняет построение прогноза для уже сгенерированных будущих точек временного ряда.
*
* @param forecast Заготовка прогноза временного ряда с пустыми значениями
* @return временной ряд прогноза
*/
protected abstract TimeSeries makeForecast(TimeSeries forecast) throws ModelingException;
protected TimeSeries generateEmptyForecastPoints(TimeSeries modelTimeSeries, int countPointForecast) {
long diffMilliseconds = TimeSeriesUtils.getTimeDifferenceInMilliseconds(originalTimeSeries);
TimeSeries forecast = new TimeSeries("Forecast of " + originalTimeSeries.getName());
forecast.addValue(new TimeSeriesValue(modelTimeSeries.getLastValue().getDate()));
for (int i = 1; i < countPointForecast + 1; i++) {
forecast.addValue(new TimeSeriesValue(forecast.getValues().get(i - 1).getDate().plus(diffMilliseconds, ChronoUnit.MILLIS)));
}
return forecast;
}
/**
* Выполняет построение модели и прогноза временного ряда. Даты спрогнозированных точек будут сгенерированы
* по модельным точкам.
*
* @param countPoints количество точек для прогнозирования
* @return прогноз временного ряда
*/
public TimeSeries getForecast(int countPoints) throws ModelingException {
validateForecastParams(countPoints);
return getForecastWithValidParams(countPoints);
}
private void validateForecastParams(int countPoints) throws ForecastValidateException {
if (countPoints < 1) {
throw new ForecastValidateException("Количество прогнозных точек должно быть больше 0");
}
}
protected void validateTimeSeries() throws ModelingException {
if (originalTimeSeries == null || originalTimeSeries.isEmpty()) {
throw new TimeSeriesValidateException("Временной ряд должен быть не пустым");
}
if (originalTimeSeries.getLength() < 2) {
throw new TimeSeriesValidateException("Временной ряд должен содержать хотя бы 2 точки");
}
if (originalTimeSeries.getValues().stream().anyMatch(val -> val == null || val.getValue() == null)) {
throw new TimeSeriesValidateException("Временной ряд содержит пустые значения");
}
if (originalTimeSeries.getValues().stream().anyMatch(val -> val.getDate() == null)) {
throw new TimeSeriesValidateException("Временной ряд должен иметь отметки времени");
}
validateAdditionalParams();
}
protected void validateAdditionalParams() throws ModelingException {
}
public TimeSeries getModel() throws ModelingException {
//TODO: what if always run?
//if (model == null) {
makeModel();
//}
return model;
}
public boolean canMakeForecast(int countPoints) {
try {
validateTimeSeries();
validateForecastParams(countPoints);
} catch (ModelingException ex) {
return false;
}
return true;
}
@JsonIgnore
public abstract List<MethodParameter> getAvailableParameters();
public abstract TimeSeriesMethod setAvailableParameters(List<TimeSeriesMethodParamValue> parameters);
}