#3 -- add F-Transform

This commit is contained in:
Anton Romanov 2022-04-19 18:06:40 +04:00
parent ed0300a148
commit 35fedf4fec
13 changed files with 262 additions and 14 deletions

View File

@ -82,11 +82,13 @@ public class IndexController {
private void addChartToModel(TimeSeries timeSeries, String method, Model model) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
int countForecastPoints = timeSeries.getLength() > 20 ? 10 : timeSeries.getLength() / 3;
TimeSeries timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries).getTimeSeries();
TimeSeries timeSeriesModel;
ModelingResult modelingResult;
if (method == null) {
timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries).getTimeSeries();
modelingResult = timeSeriesService.getForecast(timeSeries, countForecastPoints);
} else {
timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries, method).getTimeSeries();
modelingResult = timeSeriesService.getForecast(timeSeries, method, countForecastPoints);
}
TimeSeries forecast = modelingResult.getTimeSeries();

View File

@ -16,4 +16,12 @@ public class MethodParamValue {
public Number getValue() {
return value;
}
public Integer getIntValue() {
return value.intValue();
}
public void setValue(Number value) {
this.value = value;
}
}

View File

@ -1,8 +1,10 @@
package ru.ulstu.method;
import ru.ulstu.datamodel.ts.TimeSeries;
import java.util.List;
public abstract class MethodParameter implements Comparable {
public abstract class MethodParameter implements Comparable<MethodParameter> {
protected String name;
public MethodParameter(String name) {
@ -13,10 +15,10 @@ public abstract class MethodParameter implements Comparable {
return name;
}
public abstract List<Number> getAvailableValues();
public abstract List<Number> getAvailableValues(TimeSeries timeSeries);
@Override
public int compareTo(Object o) {
return this.name.compareTo(((MethodParameter) o).getName());
public int compareTo(MethodParameter o) {
return this.name.compareTo(o.getName());
}
}

View File

@ -1,5 +1,6 @@
package ru.ulstu.method.exponential.addtrendaddseason;
import ru.ulstu.datamodel.Model;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.MethodParamValue;
import ru.ulstu.method.MethodParameter;
@ -13,7 +14,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class AddTrendAddSeasonModel extends ru.ulstu.datamodel.Model {
public class AddTrendAddSeasonModel extends Model {
private final ExponentialMethodParamValue<Alpha> alpha = new ExponentialMethodParamValue<>(Alpha.getInstance(), 0.5);
private final ExponentialMethodParamValue<Beta> beta = new ExponentialMethodParamValue<>(Beta.getInstance(), 0.5);
private final ExponentialMethodParamValue<Gamma> gamma = new ExponentialMethodParamValue<>(Gamma.getInstance(), 0.5);

View File

@ -1,5 +1,6 @@
package ru.ulstu.method.exponential.addtrendnoseason;
import ru.ulstu.datamodel.Model;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.MethodParamValue;
import ru.ulstu.method.MethodParameter;
@ -11,7 +12,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class AddTrendNoSeasonModel extends ru.ulstu.datamodel.Model {
public class AddTrendNoSeasonModel extends Model {
private final ExponentialMethodParamValue<Alpha> alpha = new ExponentialMethodParamValue<>(Alpha.getInstance(), 0.5);
private final ExponentialMethodParamValue<Beta> beta = new ExponentialMethodParamValue<>(Beta.getInstance(), 0.5);
private final List<Double> smoothedComponent = new ArrayList<>();

View File

@ -1,5 +1,6 @@
package ru.ulstu.method.exponential.notrendnoseason;
import ru.ulstu.datamodel.Model;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.MethodParamValue;
import ru.ulstu.method.MethodParameter;
@ -10,7 +11,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class NoTrendNoSeasonModel extends ru.ulstu.datamodel.Model {
public class NoTrendNoSeasonModel extends Model {
private final ExponentialMethodParamValue<Alpha> alpha = new ExponentialMethodParamValue<>(Alpha.getInstance(), 0.5);
private final List<Double> smoothedComponent = new ArrayList<>();

View File

@ -1,6 +1,7 @@
package ru.ulstu.method.exponential.parameter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.MethodParameter;
import java.util.ArrayList;
@ -35,7 +36,7 @@ public abstract class ExponentialMethodParameter extends MethodParameter {
@Override
@JsonIgnore
public List<Number> getAvailableValues() {
public List<Number> getAvailableValues(TimeSeries timeSeries) {
List<Number> values = new ArrayList<>();
for (double i = minValue.doubleValue(); i <= maxValue.doubleValue(); i += optimizationStep.doubleValue()) {
values.add(i);

View File

@ -0,0 +1,57 @@
package ru.ulstu.method.ftransform;
/**
* Треугольная функция принадлежности
*/
public class AComponent {
private int start; // левая граница треугольника
private int end; // правая граница треугольника
private int top; // вершина треугольника
public int getStart() {
return start;
}
public AComponent() {
}
public AComponent(int start, int top, int end) {
this.start = start;
this.top = top;
this.end = end;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getTop() {
return top;
}
public void setTop(int top) {
this.top = top;
}
public double getValueAtPoint(int pointIndex) {
if (pointIndex == this.getTop()) {
return 1;
} else if ((pointIndex >= this.getEnd()) || (pointIndex <= this.getStart())) {
return 0;
} else if (pointIndex < this.getTop()) {
return (double) (pointIndex - this.getStart()) / (this.getTop() - this.getStart());
} else if (pointIndex > this.getTop()) {
return (double) -(pointIndex - this.getEnd()) / (this.getEnd() - this.getTop());
}
return 0;
}
}

View File

@ -0,0 +1,87 @@
package ru.ulstu.method.ftransform;
import org.springframework.stereotype.Component;
import ru.ulstu.datamodel.Model;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.Method;
import ru.ulstu.method.MethodParamValue;
import ru.ulstu.method.MethodParameter;
import java.util.List;
@Component
public class FTransform extends Method {
@Override
protected FTransformModel getModelOfValidTimeSeries(TimeSeries ts,
List<MethodParamValue> parameters) {
FTransformModel model = new FTransformModel(ts, parameters);
List<AComponent> aComponents = generateAComponents(ts, model.getNumberOfCoveredPoints().getIntValue(), model.getAComponents());
;
TimeSeries piecewiseLinearTrend = model.getPiecewiseLinearTrend();
TimeSeries tsModel = model.getTimeSeriesModel();
for (AComponent aComponent : aComponents) {
double sum1 = 0;
double sum2 = 0;
for (int j = 0; j < ts.getLength(); j++) {
double membership = aComponent.getValueAtPoint(j);
sum1 += membership * ts.getNumericValue(j);
sum2 += membership;
}
piecewiseLinearTrend.addValue(ts.getValue(aComponent.getTop()), sum1 / sum2);
tsModel.addValue(ts.getValue(aComponent.getTop()), sum1 / sum2);
}
return model;
}
private List<AComponent> generateAComponents(TimeSeries ts, int numberOfCoveredPoints, List<AComponent> piecewiseLinearTrend) {
long deltaForTriangle = Math.round(numberOfCoveredPoints / 2.0);
int currentPoint = 0;
while (currentPoint < ts.getLength()) {
int startPoint = (currentPoint == 0)
? 0
: piecewiseLinearTrend.get(piecewiseLinearTrend.size() - 1).getTop();
AComponent bf = new AComponent(startPoint, currentPoint, currentPoint + numberOfCoveredPoints / 2);
if (bf.getStart() < 0) {
bf.setStart(0);
}
if (bf.getEnd() > ts.getLength() - 1) {
bf.setEnd(ts.getLength() - 1);
}
if (bf.getTop() > ts.getLength() - 1) {
bf.setTop(ts.getLength() - 1);
}
piecewiseLinearTrend.add(bf);
currentPoint += deltaForTriangle;
}
if (piecewiseLinearTrend.get(piecewiseLinearTrend.size() - 1).getEnd() != piecewiseLinearTrend.get(piecewiseLinearTrend.size() - 1).getTop()) {
AComponent bf = new AComponent(piecewiseLinearTrend.get(piecewiseLinearTrend.size() - 1).getTop(),
piecewiseLinearTrend.get(piecewiseLinearTrend.size() - 1).getEnd(),
piecewiseLinearTrend.get(piecewiseLinearTrend.size() - 1).getEnd());
piecewiseLinearTrend.add(bf);
}
return piecewiseLinearTrend;
}
@Override
protected TimeSeries getForecastWithValidParams(Model model, TimeSeries forecast) {
FTransformModel fTransformModel = (FTransformModel) model;
for (int t = 1; t < forecast.getLength(); t++) {
forecast.getValues().get(t).setValue(fTransformModel.getPiecewiseLinearTrend().getLastValue().getValue());
}
return forecast;
}
@Override
public List<MethodParameter> getAvailableParameters() {
return FTransformModel.getAvailableParameters();
}
@Override
public String getName() {
return "F - преобразование";
}
}

View File

@ -0,0 +1,43 @@
package ru.ulstu.method.ftransform;
import ru.ulstu.datamodel.Model;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.MethodParamValue;
import ru.ulstu.method.MethodParameter;
import ru.ulstu.method.ftransform.parameter.NumberOfCoveredPoints;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class FTransformModel extends Model {
private final MethodParamValue numberOfCoveredPoints = new MethodParamValue(NumberOfCoveredPoints.getInstance(), 3);
private final List<AComponent> aComponents = new ArrayList<>();
private final TimeSeries piecewiseLinearTrend;
public FTransformModel(TimeSeries ts, List<MethodParamValue> parameters) {
super(ts);
piecewiseLinearTrend = new TimeSeries("Piecewise linear trend of ", ts.getKey());
for (MethodParamValue parameter : parameters) {
if (parameter.getParameter() instanceof NumberOfCoveredPoints) {
numberOfCoveredPoints.setValue(parameter.getValue());
}
}
}
public TimeSeries getPiecewiseLinearTrend() {
return piecewiseLinearTrend;
}
public List<AComponent> getAComponents() {
return aComponents;
}
public MethodParamValue getNumberOfCoveredPoints() {
return numberOfCoveredPoints;
}
public static List<MethodParameter> getAvailableParameters() {
return Collections.singletonList(NumberOfCoveredPoints.getInstance());
}
}

View File

@ -0,0 +1,29 @@
package ru.ulstu.method.ftransform.parameter;
import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.MethodParameter;
import java.util.ArrayList;
import java.util.List;
public class NumberOfCoveredPoints extends MethodParameter {
private final static int MIN_NUMBER_OF_COVERED_POINTS = 3;
private final static int MIN_INCREASING_STEP_OF_NUMBER_OF_COVERED_POINTS = 2;
public NumberOfCoveredPoints() {
super("Number of covered points");
}
public static NumberOfCoveredPoints getInstance() {
return new NumberOfCoveredPoints();
}
@Override
public List<Number> getAvailableValues(TimeSeries timeSeries) {
List<Number> values = new ArrayList<>();
for (double i = MIN_NUMBER_OF_COVERED_POINTS; i <= (timeSeries.getLength() < 10 ? 7 : timeSeries.getLength() / 3.0); i += MIN_INCREASING_STEP_OF_NUMBER_OF_COVERED_POINTS) {
values.add(i);
}
return values;
}
}

View File

@ -48,7 +48,7 @@ class MethodParamBruteForce {
.collect(Collectors.toMap(TimeSeriesValue::getDate, TimeSeriesValue::getValue));
for (Method method : methods) {
List<List<MethodParamValue>> availableParametersValues = getAvailableParametersValues(method.getAvailableParameters());
List<List<MethodParamValue>> availableParametersValues = getAvailableParametersValues(timeSeries, method.getAvailableParameters());
for (List<MethodParamValue> parametersValues : availableParametersValues) {
Method methodInstance = method.getClass().getDeclaredConstructor().newInstance();
if (methodInstance.canMakeForecast(reducedTimeSeries, parametersValues, countPoints)) {
@ -119,7 +119,7 @@ class MethodParamBruteForce {
}
*/
public ModelingResult getSmoothedTimeSeries(TimeSeries timeSeries) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
public ModelingResult getSmoothedTimeSeries(TimeSeries timeSeries, List<Method> methods) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
List<Future<ModelingResult>> results = new ArrayList<>();
List<ModelingResult> results2 = new CopyOnWriteArrayList<>();
@ -127,7 +127,7 @@ class MethodParamBruteForce {
.collect(Collectors.toMap(TimeSeriesValue::getDate, TimeSeriesValue::getValue));
for (Method method : methods) {
List<List<MethodParamValue>> availableParametersValues = getAvailableParametersValues(method.getAvailableParameters());
List<List<MethodParamValue>> availableParametersValues = getAvailableParametersValues(timeSeries, method.getAvailableParameters());
for (List<MethodParamValue> parametersValues : availableParametersValues) {
Method methodInstance = method.getClass().getDeclaredConstructor().newInstance();
if (methodInstance.canMakeModel(timeSeries, parametersValues)) {
@ -150,13 +150,25 @@ class MethodParamBruteForce {
.orElse(null);
}
private List<List<MethodParamValue>> getAvailableParametersValues(List<MethodParameter> availableParameters) {
public ModelingResult getSmoothedTimeSeries(TimeSeries timeSeries, String methodClassName) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ModelingException {
Method method = methods.stream()
.filter(m -> m.getClass().getSimpleName().equals(methodClassName))
.findAny()
.orElseThrow(() -> new ModelingException("Неизвестный метод прогнозирования"));
return getSmoothedTimeSeries(timeSeries, List.of(method));
}
public ModelingResult getSmoothedTimeSeries(TimeSeries timeSeries) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
return getSmoothedTimeSeries(timeSeries, methods);
}
private List<List<MethodParamValue>> getAvailableParametersValues(TimeSeries timeSeries, List<MethodParameter> availableParameters) {
List<List<MethodParamValue>> result = new ArrayList<>();
Map<MethodParameter, Integer> parameterOffset = new TreeMap<>();
Map<MethodParameter, List<Number>> parameterValues = new TreeMap<>();
for (MethodParameter methodParameter : availableParameters) {
parameterOffset.put(methodParameter, 0);
parameterValues.put(methodParameter, methodParameter.getAvailableValues());
parameterValues.put(methodParameter, methodParameter.getAvailableValues(timeSeries));
}
while (isNotAllParameterValuesUsed(parameterOffset, parameterValues)) {
List<MethodParamValue> resultRow = new ArrayList<>();

View File

@ -35,6 +35,10 @@ public class TimeSeriesService {
return methodParamBruteForce.getSmoothedTimeSeries(timeSeries);
}
public ModelingResult smoothTimeSeries(TimeSeries timeSeries, String methodClassName) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
return methodParamBruteForce.getSmoothedTimeSeries(timeSeries, methodClassName);
}
public List<Method> getAvailableMethods() {
return methodParamBruteForce.getAvailableMethods();
}