参考趋势模型生成的java方法:
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.linear.*;
public class TrendAnalysisUtils {
public static void main(String[] args) {
List<String> xAxisDatas = new ArrayList<>(); // 时间序列
// 示例数据
xAxisDatas.add("2020-03-01");
xAxisDatas.add("2021-03-02");
xAxisDatas.add("2022-03-03");
xAxisDatas.add("2023-03-04");
// xAxisDatas.add("2023-01");
// xAxisDatas.add("2023-02");
// xAxisDatas.add("2023-03");
// xAxisDatas.add("2023-04");
// xAxisDatas.add("2020");
// xAxisDatas.add("2021");
// xAxisDatas.add("2022");
// xAxisDatas.add("2023");
List<BigDecimal> seriesDatas = new ArrayList<>(); // 具体数据
seriesDatas.add(new BigDecimal("26"));
seriesDatas.add(new BigDecimal("51"));
seriesDatas.add(new BigDecimal("43"));
seriesDatas.add(new BigDecimal("43"));
List<Double> numericDates = parseDates(xAxisDatas);
List<Double> seriesDataDoubles = new ArrayList<>();
for (BigDecimal value : seriesDatas) {
seriesDataDoubles.add(value.doubleValue());
}
System.out.println(analyzeLinearTrend(numericDates, seriesDataDoubles));
System.out.println(analyzeLogarithmicTrend(numericDates, seriesDataDoubles));
System.out.println(analyzeExponentialTrend(numericDates, seriesDataDoubles));
System.out.println(analyzePowerTrend(numericDates, seriesDataDoubles));
System.out.println(analyzePolynomialTrend(numericDates, seriesDataDoubles));
}
public static String trendAnalysis(List<String> xAxisDatas, List<BigDecimal> seriesDatas) {
StringBuilder buf = new StringBuilder();
List<Double> numericDates = parseDates(xAxisDatas);
List<Double> seriesDataDoubles = new ArrayList<>();
for (BigDecimal value : seriesDatas) {
seriesDataDoubles.add(value.doubleValue());
}
buf.append("<br>基于线性趋势模型分析:").append(analyzeLinearTrend(numericDates, seriesDataDoubles));
buf.append("<br>基于对数趋势模型分析:").append(analyzeLogarithmicTrend(numericDates, seriesDataDoubles));
buf.append("<br>基于指数趋势模型分析:").append(analyzeExponentialTrend(numericDates, seriesDataDoubles));
buf.append("<br>基于幂函数趋势分析:").append(analyzePowerTrend(numericDates, seriesDataDoubles));
buf.append("<br>基于多项式趋势线模型分析:").append(analyzePolynomialTrend(numericDates, seriesDataDoubles));
return buf.toString();
}
/**
* 解析日期字符串并转换为数字表示形式。
*
* @param xAxisDatas 日期字符串列表
* @return 数字表示的日期列表
*/
private static List<Double> parseDates(List<String> xAxisDatas) {
List<Double> numericDates = new ArrayList<>();
DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("yyyy");
DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
for (String dateString : xAxisDatas) {
try {
LocalDate date;
if (dateString.matches("\\d{4}-\\d{2}-\\d{2}")) { // yyyy-MM-dd
date = LocalDate.parse(dateString, dayFormatter);
} else if (dateString.matches("\\d{4}-\\d{2}")) { // yyyy-MM
date = LocalDate.parse(dateString + "-01", dayFormatter);
} else { // yyyy
date = LocalDate.parse(dateString + "-01-01", dayFormatter);
}
numericDates.add(dateToNumeric(date));
} catch (Exception e) {
System.err.println("Error parsing date string '" + dateString + "': " + e.getMessage() + ", unparsed text found at index " + dateString.indexOf('-'));
}
}
return numericDates;
}
/**
* 将日期转换为相对于某个基准日期的天数
* @param date 需要转换的日期
* @return 从基准日期开始的天数
*/
private static double dateToNumeric(LocalDate date) {
LocalDate epoch = LocalDate.ofEpochDay(0);
long daysSinceEpoch = date.toEpochDay() - epoch.toEpochDay();
return daysSinceEpoch;
}
/**
* 分析数据是否符合线性趋势
*
* 线性趋势模型:y = a + bx,其中 a 和 b 是模型的系数。
* 如果 b > 0,那么随着 x 的增加,y 也会增加,且这种增加是均匀的;
* 如果 b < 0,那么随着 x 的增加,y 会减少,且这种减少也是均匀的。
*
* @param numericDates 数字表示的时间序列
* @param seriesData 数据点列表
* @return 趋势分析结果字符串
*/
public static String analyzeLinearTrend(List<Double> numericDates, List<Double> seriesData) {
// 计算 x 的总和
double sumX = numericDates.stream().mapToDouble(Double::doubleValue).sum();
// 计算 y 的总和
double sumY = seriesData.stream().mapToDouble(Double::doubleValue).sum();
// 计算 x * y 的总和
double sumXY = 0;
// 计算 x^2 的总和
double sumXSquare = 0;
// 遍历所有数据点并计算所需的和
for (int i = 0; i < numericDates.size(); i++) {
sumXY += numericDates.get(i) * seriesData.get(i);
sumXSquare += numericDates.get(i) * numericDates.get(i);
}
// 获取数据点的数量
int n = numericDates.size();
// 计算系数 b (斜率)
double b = (n * sumXY - sumX * sumY) / (n * sumXSquare - Math.pow(sumX, 2));
// 计算系数 a (截距)
double a = (sumY - b * sumX) / n;
// 根据系数 b 的符号返回分析结果
return b > 0 ? "[指标]平稳上升,增长趋势稳定" : "[指标]持续下降";
}
/**
* 分析数据是否符合对数趋势
*
* 对数趋势模型:y = a + b * ln(x),其中 a 和 b 是模型的系数。
* 如果 b > 0,那么随着 x 的增加,y 也会增加,但增加的速度逐渐减慢;
* 如果 b < 0,那么随着 x 的增加,y 会减少,减少的速度也逐渐减慢。
*
* @param numericDates 数字表示的时间序列
* @param seriesData 数据点列表
* @return 趋势分析结果字符串
*/
public static String analyzeLogarithmicTrend(List<Double> numericDates, List<Double> seriesData) {
// 计算 ln(x) 的总和
double sumX = numericDates.stream().mapToDouble(Math::log).sum();
// 计算 y 的总和
double sumY = seriesData.stream().mapToDouble(Double::doubleValue).sum();
// 计算 ln(x) * y 的总和
double sumXY = 0;
// 计算 (ln(x))^2 的总和
double sumXSquare = 0;
// 遍历所有数据点并计算所需的和
for (int i = 0; i < numericDates.size(); i++) {
sumXY += Math.log(numericDates.get(i)) * seriesData.get(i);
sumXSquare += Math.log(numericDates.get(i)) * Math.log(numericDates.get(i));
}
// 获取数据点的数量
int n = numericDates.size();
// 计算系数 b (斜率)
double b = (n * sumXY - sumX * sumY) / (n * sumXSquare - Math.pow(sumX, 2));
// 计算系数 a (截距)
double a = (sumY - b * sumX) / n;
// 根据系数 b 的符号返回分析结果
return b > 0 ? "[指标]总体呈现上升趋势,初期增长速度快,后期增速放缓趋于平稳" : "[指标]总体呈现下降趋势,初期下降速度快,后期降速放缓";
}
/**
* 分析数据是否符合指数趋势
*
* 指数趋势模型:y = exp(a + bx),其中 a 和 b 是模型的系数。
* 如果 b > 0,那么随着 x 的增加,y 也会以指数方式增加;
* 如果 b < 0,那么随着 x 的增加,y 会以指数方式减少。
*
* @param numericDates 数字表示的时间序列
* @param seriesData 数据点列表
* @return 趋势分析结果字符串
*/
public static String analyzeExponentialTrend(List<Double> numericDates, List<Double> seriesData) {
// 计算 ln(y) 的列表
List<Double> logSeriesData = new ArrayList<>();
for (Double value : seriesData) {
logSeriesData.add(Math.log(value));
}
// 计算 x 的总和
double sumX = numericDates.stream().mapToDouble(Double::doubleValue).sum();
// 计算 ln(y) 的总和
double sumY = logSeriesData.stream().mapToDouble(Double::doubleValue).sum();
// 计算 x * ln(y) 的总和
double sumXY = 0;
// 计算 x^2 的总和
double sumXSquare = 0;
// 遍历所有数据点并计算所需的和
for (int i = 0; i < numericDates.size(); i++) {
sumXY += numericDates.get(i) * logSeriesData.get(i);
sumXSquare += numericDates.get(i) * numericDates.get(i);
}
// 获取数据点的数量
int n = numericDates.size();
// 计算系数 b (斜率)
double b = (n * sumXY - sumX * sumY) / (n * sumXSquare - Math.pow(sumX, 2));
// 计算系数 a (截距)
double a = (sumY - b * sumX) / n;
// 根据系数 b 的符号返回分析结果
return b > 0 ? "[指标]总体呈现上升趋势,增长速度越来越快" : "[指标]总体呈现下降趋势,下降速度越来越快";
}
/**
* 分析数据是否符合幂函数趋势
*
* 幂函数趋势模型:y = ax^b,其中 a 和 b 是模型的系数。
* 如果 b > 0,那么随着 x 的增加,y 也会增加;
* 如果 b < 0,那么随着 x 的增加,y 会减少。
*
* @param numericDates 数字表示的时间序列
* @param seriesData 数据点列表
* @return 趋势分析结果字符串
*/
public static String analyzePowerTrend(List<Double> numericDates, List<Double> seriesData) {
// 计算 ln(x) 的列表
List<Double> logDates = new ArrayList<>();
// 计算 ln(y) 的列表
List<Double> logSeriesData = new ArrayList<>();
for (Double value : numericDates) {
logDates.add(Math.log(value));
}
for (Double value : seriesData) {
logSeriesData.add(Math.log(value));
}
// 计算 ln(x) 的总和
double sumX = logDates.stream().mapToDouble(Double::doubleValue).sum();
// 计算 ln(y) 的总和
double sumY = logSeriesData.stream().mapToDouble(Double::doubleValue).sum();
// 计算 ln(x) * ln(y) 的总和
double sumXY = 0;
// 计算 (ln(x))^2 的总和
double sumXSquare = 0;
// 遍历所有数据点并计算所需的和
for (int i = 0; i < logDates.size(); i++) {
sumXY += logDates.get(i) * logSeriesData.get(i);
sumXSquare += logDates.get(i) * logDates.get(i);
}
// 获取数据点的数量
int n = logDates.size();
// 计算系数 b (斜率)
double b = (n * sumXY - sumX * sumY) / (n * sumXSquare - Math.pow(sumX, 2));
// 计算系数 a (截距)
double a = Math.exp((sumY - b * sumX) / n);
// 根据系数 b 的符号返回分析结果
return b > 0 ? "[指标]总体呈现上升趋势,增速很快" : "[指标]总体呈现下降趋势,下降速度很快";
}
/**
* 分析多项式趋势线模型
*
* 多项式趋势模型:y = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_1 * x + a_0,其中 a_i 是模型的系数。
* 该方法拟合多项式回归模型并计算残差的标准差来评估波动性。
*
* @param numericDates 数字表示的时间序列
* @param seriesData 数据点列表
* @return 波动分析结果字符串
*/
public static String analyzePolynomialTrend(List<Double> numericDates, List<Double> seriesData) {
double[] coefficients = fitPolynomialRegression(numericDates, seriesData, 6); // 拟合多项式回归模型
double standardDeviation = calculateStandardDeviation(coefficients, numericDates, seriesData); // 计算残差的标准差
return standardDeviation > 10 ? "[指标]波动较大,不够稳定" : "[指标]波动较小,比较稳定"; // 根据标准差判断波动性
}
/**
* 拟合多项式回归模型
*
* @param numericDates 数字表示的时间序列
* @param seriesData 数据点列表
* @param degree 多项式的阶数
* @return 多项式系数数组
*/
public static double[] fitPolynomialRegression(List<Double> numericDates, List<Double> seriesData, int degree) {
int n = numericDates.size(); // 数据点数量
RealMatrix designMatrix = new Array2DRowRealMatrix(n, degree + 1); // 设计矩阵
RealVector responseVector = new ArrayRealVector(seriesData.stream().mapToDouble(Double::doubleValue).toArray()); // 响应向量
// 构建设计矩阵
for (int i = 0; i < n; i++) {
double date = numericDates.get(i);
if (date <= 0) {
throw new IllegalArgumentException("Invalid date value encountered.");
}
for (int j = 0; j <= degree; j++) {
designMatrix.setEntry(i, j, Math.pow(date, j));
}
}
// 使用最小二乘法求解多项式系数
RealMatrix XTX = designMatrix.transpose().multiply(designMatrix);
RealMatrix XTY = new Array2DRowRealMatrix(designMatrix.getColumnDimension(), 1);
// 计算 XTY
for (int j = 0; j < designMatrix.getColumnDimension(); j++) {
XTY.setEntry(j, 0, designMatrix.getColumnVector(j).dotProduct(responseVector));
}
DecompositionSolver solver = new LUDecomposition(XTX).getSolver();
RealVector coefficients = solver.solve(XTY.getColumnVector(0));
return coefficients.toArray();
}
/**
* 创建设计矩阵(包含基函数的矩阵)
* @param numericDates 数字表示的日期列表
* @param degree 多项式的阶数
* @return 设计矩阵
*/
private static RealMatrix createDesignMatrix(List<Double> numericDates, int degree) {
double[][] matrix = new double[numericDates.size()][degree + 1]; // 初始化矩阵
for (int i = 0; i < numericDates.size(); i++) {
for (int j = 0; j <= degree; j++) {
matrix[i][j] = Math.pow(numericDates.get(i), j); // 计算基函数值
}
}
return new Array2DRowRealMatrix(matrix); // 返回设计矩阵
}
/**
* 创建响应变量矩阵
* @param seriesData 数据点列表
* @return 响应变量矩阵
*/
private static RealMatrix createYMatrix(List<Double> seriesData) {
double[] yValues = new double[seriesData.size()]; // 初始化数组
for (int i = 0; i < seriesData.size(); i++) {
yValues[i] = seriesData.get(i); // 存储数据点
}
return new Array2DRowRealMatrix(yValues); // 返回响应变量矩阵
}
/**
* 计算残差的标准差
*
* @param coefficients 多项式系数
* @param numericDates 数字表示的时间序列
* @param seriesData 数据点列表
* @return 残差的标准差
*/
public static double calculateStandardDeviation(double[] coefficients, List<Double> numericDates, List<Double> seriesData) {
int n = numericDates.size();
double sumOfSquares = 0;
for (int i = 0; i < n; i++) {
double predictedValue = 0;
double date = numericDates.get(i);
// 计算预测值
for (int j = 0; j < coefficients.length; j++) {
predictedValue += coefficients[j] * Math.pow(date, j);
}
double actualValue = seriesData.get(i);
double residual = actualValue - predictedValue;
sumOfSquares += residual * residual;
}
double meanSquareError = sumOfSquares / n;
return Math.sqrt(meanSquareError);
}
}