参考地址:https://github.com/signaflo/java-timeseries/wiki/ARIMA%20models
以天为维度进行预测,考虑季节性P D Q,基于java-timeseries-0.2.1实现。算法模型上p、d、q阶差在3以内,阶差超过超过3,数据失真的可能性较高,所以我们设定3以内的阶差;拟合策略为USS,同时考虑季节性P D Q阶差,具体的p、d、q比例,选取所有的组合,通过AIC原则确定最优拟合方案,3以内的超参进行运算次数为(3*3*3)*(3*3*3)次,如果扩大超参运行增大,拟合策略为 CSSML(条件平方和,然后是最大似然), 除此之外还有:CSS, ML, CSSML, USS, USSML。
pom.xml中引入
<dependency>
<groupId>com.github.jrachiele</groupId>
<artifactId>java-timeseries</artifactId>
<version>0.2.1</version>
</dependency>
创建工具类ArimaUti.java
import java.util.ArrayList;
import timeseries.TimeSeries;
import timeseries.Ts;
import timeseries.models.Forecast;
import timeseries.models.arima.Arima;
import timeseries.models.arima.Arima.FittingStrategy;
public class ArimaUtil {
/**
* github地址:https://github.com/signaflo/java-timeseries/wiki/ARIMA%20models
*
* @param args
*/
public static void main(String[] args) {
// 1、数据预处理,专家和程序相结合,以专家经验为主;
// 2、算法模型上p、d、q阶差在3以内,阶差超过超过3,数据失真的可能性较高,所以我们设定3以内的阶差;拟合策略为USS,同时考虑季节性P D
// Q阶差,具体的p、d、q比例,选取所有的组合,通过AIC原则确定最优拟合方案。
double[] data1 = new double[] { 125313.00, 137269.00, 244490.00, 287543.00, 324499.00, 311067.00, 259425.00,
329819.00, 434853.00, 429448.00, 632888.00, 772799.00, 300053.00, 396290.00, 297682.00, 427363.00,
322505.00, 319447.00, 322702.00, 325893.00, 329232.00, 309768.00, 474377.00, 528164.00, 240786.00,
164162.00, 312354.00, 293259.00, 318812.00, 300855.00, 280806.00, 329902.00, 449946.00, 475666.00,
503628.00, 751930.00, 390240.00, 258080.00, 433766.00, 373354.00, 392186.00, 398683.00, 511023.00,
509940.00, 551645.00, 627801.00, 577502.00, 722013.00, 287564.00, 393840.00, 460964.00, 394089.00,
387435.00, 401763.00, 455938.00, 433146.00, 466803.00, 541699.00, 536455.00, 611227.00, 288705.00,
212319.00, 358225.00, 269655.00, 323315.00, 245326.00, 293882.00, 319587.00, 382168.00, 392402.00,
529535.00, 684079.00, 299872.00, 370931.00, 376959.00, 353275.00, 295286.00, 536354.00, 289498.00,
258752.00, 376137.00, 398256.00, 284366.00, 548489.00, 557731.00, 139582.00, 434978.00, 329646.00,
329098.00, 476521.00, 397364.00, 394842.00, 506988.00, 605346.00, 682207.00, 834749.00, 663517.00,
703500.00, 861919.00, 875020.00, 611936.00, 932294.00, 893144.00, 883730.00, 900516.00, 900587.00,
1075590.0, 1016250.0, 783064.00, 919727.00, 917302.00, 1016711.0, 776010.00, 876441.00, 988196.00,
798981.00, 1108645.0, 1132921.0, 1255781.0, 1202435.0, 1016490.0, 935281.00, 1270376.0, 1336770.0,
1437635.0, 1555010.0, 1705866.0, 1689584.0, 1861612.0, 1819093.0, 2537813.00, 2011416.00 };
// 季节性ARIMA建模的开放源码时间序列库java-timeseries进行预测,开源MIT协议
// 拟合策略为 CSSML(条件平方和,然后是最大似然), 除此之外还有:CSS, ML, CSSML, USSML;
// 阶数在3以内,考虑季节性p、d、q阶数,通过AIC原则评估阶数拟合度。计算出最佳拟合阶数。
double[] predictResult = TimeSeriesPredictUtil.predictByDay(data1, 7, false, 2022, 10, 1);
System.out.println(predictResult.length);
System.out.println("----预测结果:" + predictResult.toString());
for (int i = 0; i < predictResult.length; i++) {
System.out.print(predictResult[i]);
System.out.print(",");
}
}
private TimeSeriesPredictUtil() {
}
/**
* 调用第三方库并对时间序列进行预测,输入对数据需要是有序甚至规律的时间序列。另外,不需要输入时间时间, 如果你需要得到时间和预测值的键值对,你需要自己组装
*
* @param data 需要预测的数据
* @param steps 你需要预测未来多少个单位的数据,比如5天,5周或者5个月。
* @param draw 是否绘图,测试时可以通过绘图直观的翻译数据预测是否准确
* @param startYear The year of the first observation.
* @param startMonth The month of the first observation - an integer between 1
* and 12 corresponding to the months January through
* December.
* @param startDay The day of the first observation - an integer between 1 and
* 31.
* @return 预测值数组
*/
public static double[] predictByDay(double[] data, int steps, boolean draw, int startYear, int startMonth,
int startDay) {
// 指定周期
TimeSeries series = Ts.newMonthlySeries(startYear, startMonth, startDay, data);// 开始时间,年、 月( 1 and 12)、 日(1 and 31)、 历史数据
ArrayList<Integer> options = new ArrayList<>();
// 差分阶数,3次以内
int[] params = new int[] { 0, 1, 2 };
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
// 模型顺序 pdq
repeatableArrangement(3, params, options, result);
// 测试预测一次
// result.add(new ArrayList<>(Arrays.asList(1, 1, 1)));
// d 差分次数,数据平稳差分次数,差分太多失真
// p 自回归参数,ar模型
// q 滑动平均参数,MA模型
// 其中(P, D, Q, s)表示季节性ARIMA模型的季节性阶数,
ArrayList<Integer> bestChoice = new ArrayList<>(3); // 三个参数,p、d、q , 表示AR、差分、MA的阶数
ArrayList<Integer> sBestChoice = new ArrayList<>(6);// 六个参数,p, d, q, P, D, Q
double minimalAic = Double.MAX_VALUE;
for (ArrayList<Integer> item : result) {
if (item.isEmpty()) {
continue;
}
// 第二步,我们可以指定任何我们认为合理的模型顺序
// Note that intercept fitting will automatically be turned off 适当拟合将自动关闭
Arima.ModelOrder order = Arima.order(item.get(0), item.get(1), item.get(2));
// 创建arima模型
// 默认拟合策略 uss ,除此之外还有:CSS, ML, CSSML, USSML;
// USS("Uncorrected Sum of Squares"), 未校正平方和
// CSS("conditional sum-of-squares"), 有条件的平方和
// ML("maximum likelihood"), 最大似然
// CSSML("conditional sum-of-squares, then maximum likelihood");
Arima model = Arima.model(series, order, FittingStrategy.CSSML);
double aic = model.aic();
// 利用三个参数 pdq ,采用AIC原则 (最小化信息量准则)
if (aic < minimalAic) {
minimalAic = aic;
bestChoice.clear();
bestChoice.addAll(item);
sBestChoice.clear();
}
// 利用三个参数 pdq ,采用AIC原则 (最小化信息量准则)
for (ArrayList<Integer> sItem : result) {
if (sItem.isEmpty()) {
continue;
}
// 除了数据p 、d q ,考虑季节性P D Q
Arima.ModelOrder sOrder = Arima.order(item.get(0), item.get(1), item.get(2), sItem.get(0), sItem.get(1),
sItem.get(2));
Arima sModel = Arima.model(series, sOrder, FittingStrategy.CSSML);
// 计算AIC
double sAic = sModel.aic();
// 最小原则
if (sAic < minimalAic) {
minimalAic = sAic;
bestChoice.clear();
sBestChoice.clear();
sBestChoice.addAll(item);
sBestChoice.addAll(sItem);
}
}
}
// 确定 p d q,还是 p, d, q, P, D, Q 后预测
// 如果考虑季节性方案最优使用季节性,如果不考虑季节最优就只使用三个参数
Arima.ModelOrder order = sBestChoice.isEmpty()
? Arima.order(bestChoice.get(0), bestChoice.get(1), bestChoice.get(2))
: Arima.order(sBestChoice.get(0), sBestChoice.get(1), sBestChoice.get(2), sBestChoice.get(3),
sBestChoice.get(4), sBestChoice.get(5));
Arima model = Arima.model(series, order, FittingStrategy.CSSML);
// 执行预测
Forecast forecast = model.forecast(steps);
if (draw) {
// 绘图
forecast.plot();
}
// 返回预测结果
return forecast.forecast().asArray();
}
// 递归获得所有不重复的阶差组合
private static void repeatableArrangement(int k, int[] arr, ArrayList<Integer> tmpArr,
ArrayList<ArrayList<Integer>> result) {
if (k == 1) {
for (int j : arr) {
tmpArr.add(j);
ArrayList<Integer> tmp = new ArrayList<>(tmpArr);
result.add(tmp);
tmpArr.remove(tmpArr.size() - 1);
}
} else if (k > 1) {
for (int j : arr) {
tmpArr.add(j);
repeatableArrangement(k - 1, arr, tmpArr, result);
tmpArr.remove(tmpArr.size() - 1);
}
}
}
}