#2 -- fix forecasting

This commit is contained in:
Anton Romanov 2020-10-10 13:36:35 +04:00
parent be9a761f0f
commit 85c4b3cad8
7 changed files with 91 additions and 28 deletions

View File

@ -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);
}
}

View File

@ -38,6 +38,18 @@ public class TimeSeriesController {
return new ResponseEntity<>(timeSeriesService.getRandomTimeSeries(length), HttpStatus.OK); return new ResponseEntity<>(timeSeriesService.getRandomTimeSeries(length), HttpStatus.OK);
} }
@GetMapping("getFromString")
@ApiOperation("Преобразовать строку с разделителями во временной ряд")
public ResponseEntity<TimeSeries> getTimeSeriesFromString(@RequestParam("tsString") String tsString) {
return new ResponseEntity<>(timeSeriesService.getTimeSeriesFromString(tsString), HttpStatus.OK);
}
@PostMapping("timeSeriesToString")
@ApiOperation("Преобразовать временной ряд в строку с разделителями")
public ResponseEntity<String> getTimeSeriesToString(@RequestBody TimeSeries timeSeries) {
return new ResponseEntity<>(timeSeriesService.getTimeSeriesToString(timeSeries), HttpStatus.OK);
}
@PostMapping("getForecast") @PostMapping("getForecast")
@ApiOperation("Получить прогноз временного ряда") @ApiOperation("Получить прогноз временного ряда")
public ResponseEntity<Forecast> getForecastTimeSeries(@RequestBody ForecastParams forecastParams) throws ModelingException { public ResponseEntity<Forecast> getForecastTimeSeries(@RequestBody ForecastParams forecastParams) throws ModelingException {

View File

@ -24,6 +24,10 @@ public class TimeSeries {
} }
public TimeSeries(List<TimeSeriesValue> values) {
this.values = values;
}
public List<TimeSeriesValue> getValues() { public List<TimeSeriesValue> getValues() {
return values; return values;
} }

View File

@ -20,6 +20,10 @@ public class TimeSeriesValue {
this.date = date; this.date = date;
} }
public TimeSeriesValue(Double value) {
this.value = value;
}
public LocalDateTime getDate() { public LocalDateTime getDate() {
return date; return date;
} }

View File

@ -3,6 +3,7 @@ package ru.ulstu.services;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.ulstu.TimeSeriesUtils;
import ru.ulstu.models.Forecast; import ru.ulstu.models.Forecast;
import ru.ulstu.models.TimeSeries; import ru.ulstu.models.TimeSeries;
import ru.ulstu.models.TimeSeriesValue; import ru.ulstu.models.TimeSeriesValue;
@ -10,6 +11,9 @@ import ru.ulstu.models.exceptions.ModelingException;
import ru.ulstu.tsMethods.exponential.NoTrendNoSeason; import ru.ulstu.tsMethods.exponential.NoTrendNoSeason;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Service @Service
@ -27,7 +31,34 @@ public class TimeSeriesService {
} }
public Forecast getForecast(TimeSeries timeSeries, int countPoints) throws ModelingException { 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); return nn.getForecast(timeSeries, countPoints);
} }
public TimeSeries getTimeSeriesFromString(String tsString) {
List<TimeSeriesValue> tsValues = Arrays.stream(tsString.split("\n"))
.flatMap(v -> Arrays.stream(v.split(";")))
.flatMap(v -> Arrays.stream(v.split(",")))
.flatMap(v -> Arrays.stream(v.split("<br>")))
.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(";"));
}
} }

View File

@ -1,5 +1,6 @@
package ru.ulstu.tsMethods; package ru.ulstu.tsMethods;
import ru.ulstu.TimeSeriesUtils;
import ru.ulstu.models.Forecast; import ru.ulstu.models.Forecast;
import ru.ulstu.models.Model; import ru.ulstu.models.Model;
import ru.ulstu.models.TimeSeries; 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.ModelingException;
import ru.ulstu.models.exceptions.TimeSeriesValidateException; import ru.ulstu.models.exceptions.TimeSeriesValidateException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
/** /**
@ -58,7 +57,7 @@ public abstract class TimeSeriesMethod {
} }
/** /**
* Выполняет построение прогноза ждя уже сгенерированных будущих точек временного ряда. * Выполняет построение прогноза для уже сгенерированных будущих точек временного ряда.
* *
* @param forecast Заготовка прогноза временного ряда с пустыми значениями * @param forecast Заготовка прогноза временного ряда с пустыми значениями
* @return * @return
@ -66,14 +65,7 @@ public abstract class TimeSeriesMethod {
protected abstract Forecast makeForecast(Forecast forecast); protected abstract Forecast makeForecast(Forecast forecast);
protected Forecast generateEmptyForecastPoints(Forecast forecast, int countPointForecast) { protected Forecast generateEmptyForecastPoints(Forecast forecast, int countPointForecast) {
long diffMilliseconds = getTimeDifferenceInMilliseconds(forecast); long diffMilliseconds = TimeSeriesUtils.getTimeDifferenceInMilliseconds(forecast.getModel().getOriginalTimeSeries());
LocalDateTime lastTimeSeriesDateTime = forecast
.getModel()
.getOriginalTimeSeries()
.getLastValue()
.getDate()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
forecast.getForecastTimeSeries() forecast.getForecastTimeSeries()
.addValue(new TimeSeriesValue(forecast.getModel().getModelTimeSeries().getLastValue().getDate())); .addValue(new TimeSeriesValue(forecast.getModel().getModelTimeSeries().getLastValue().getDate()));
for (int i = 1; i < countPointForecast + 1; i++) { for (int i = 1; i < countPointForecast + 1; i++) {
@ -83,21 +75,6 @@ public abstract class TimeSeriesMethod {
return forecast; 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);
}
/** /**
* Выполняет построение модели и прогноза временного ряда. Даты спрогнозированных точек будут сгенерированы * Выполняет построение модели и прогноза временного ряда. Даты спрогнозированных точек будут сгенерированы
* по модельным точкам. * по модельным точкам.

View File

@ -34,7 +34,7 @@ public class NoTrendNoSeason extends TimeSeriesMethod {
forecast.getForecastTimeSeries() forecast.getForecastTimeSeries()
.getValues() .getValues()
.get(t) .get(t)
.setValue(alpha * forecast.getForecastTimeSeries().getValues().get(t - 1).getValue()); .setValue(forecast.getForecastTimeSeries().getValues().get(t - 1).getValue());
} }
return forecast; return forecast;
} }