Qt历史数据使用线性回归算法预测未来数据趋势

工作中写的一个线性回归的图表demo,马上就删了,做一下记录;

使用随机生成的数据,添加趋势线与预测数据

.h代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

#include <QWidget>
#include <QtCharts>

QT_CHARTS_USE_NAMESPACE

namespace Ui {
class WindowTrend_INS;
}

class WindowTrend_INS : public QWidget
{
Q_OBJECT

public:
explicit WindowTrend_INS(QWidget *parent = nullptr);
~WindowTrend_INS();

private slots:
void on_pushButton_eva_trend_clicked();

private:
Ui::WindowTrend_INS *ui;

QSplineSeries *historicSeries,*predictSeries;
QLineSeries *trendSeries;
QChart *chart;
QDateTimeAxis * axisX;
QValueAxis * axisY;
QDateTime datetimeNow,datetimeFuture30Days;

QList<QPointF> listHistoric,listPredict;

void initData();


// 计算线性回归的趋势线
QLineSeries* calculateTrendLine(const QList<QPointF>& dataPoints,double &slope,double &intercept) {
QLineSeries *trendLineSeries = new QLineSeries();
trendLineSeries->setName("趋势");

if (dataPoints.isEmpty()) {
return trendLineSeries;
}

// 计算线性回归的斜率和截距
double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
int n = dataPoints.size();

for (const QPointF& point : dataPoints) {
sumX += point.x();
sumY += point.y();
sumXY += point.x() * point.y();
sumX2 += point.x() * point.x();
}

slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); // 斜率
intercept = (sumY - slope * sumX) / n; // 截距

// 生成趋势线的起点和终点
double x1 = dataPoints.first().x();
double y1 = slope * x1 + intercept;
double x2 = dataPoints.last().x();
double y2 = slope * x2 + intercept;

*trendLineSeries << QPointF(x1, y1) << QPointF(x2, y2);
return trendLineSeries;
}

};

.cpp代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "WindowTrend_INS.h"
#include "ui_WindowTrend_INS.h"

WindowTrend_INS::WindowTrend_INS(QWidget *parent) :
QWidget(parent),
ui(new Ui::WindowTrend_INS)
{
ui->setupUi(this);
initData();
}

WindowTrend_INS::~WindowTrend_INS()
{
delete ui;
}

void WindowTrend_INS::initData()
{
datetimeNow = QDateTime::currentDateTime();
datetimeFuture30Days = datetimeNow.addDays(30);

ui->timefrom->setDateTime(datetimeNow);
ui->timeto->setDateTime(datetimeFuture30Days);

chart = new QChart();
ui->chart_trend->setRenderHint(QPainter::Antialiasing);

// 创建历史数据曲线
historicSeries = new QSplineSeries();
historicSeries->setName("历史数据");

// historicSeries->setPointLabelsVisible(true); // 显示标签
// historicSeries->setPointLabelsFormat("(@xPoint, @yPoint)"); // 设置标签格式
// historicSeries->setPointLabelsColor(Qt::blue); // 设置标签颜色
// historicSeries->setPointLabelsFont(QFont("Arial", 10)); // 设置标签字体

// 创建图表
chart->addSeries(historicSeries);
chart->setTitle("惯性元件性能预测");
chart->setTitleFont(QFont("黑体",14));

// 设置X轴和Y轴的范围
axisX = new QDateTimeAxis(this);
axisX -> setTitleText("预测时间"); //设置轴的标题
axisX -> setRange(datetimeNow,datetimeFuture30Days); //设置时间轴的范围,参数是两个时期时间QDateTime

axisX -> setFormat("MM-dd HH:mm"); //设置刻度值的格式,和格式化时间字符串一样
axisX -> setLabelsAngle(15);
chart -> addAxis(axisX, Qt::AlignBottom); //添加轴

axisY = new QValueAxis(this);
axisY->setMin(-1);
axisY->setMax(1);
axisY->setRange(-0.1, 0.1);
axisY->setTickAnchor(0.1);
axisY->setTickInterval(0.01);
axisY->setTickType(QValueAxis::TicksDynamic);
axisY->setTitleText("误差值");
chart->addAxis(axisY, Qt::AlignLeft);

historicSeries->attachAxis(axisX);
historicSeries->attachAxis(axisY);

ui->chart_trend->setChart(chart);

//生成默认数据,将从数据库提取
listHistoric.clear();
for(int i=1;i<30;i++){
// 生成 [0, 1) 范围内的随机数
double randomValue = QRandomGenerator::global()->generateDouble()*0.05-0.01;
listHistoric.append(QPointF(QDateTime::currentDateTime().addDays(i).toMSecsSinceEpoch(), randomValue));
}

historicSeries->clear();
historicSeries->append(listHistoric);

}

void WindowTrend_INS::on_pushButton_eva_trend_clicked()
{
// 计算趋势线
if(!chart->series().contains(trendSeries)){
double slope,intercept;
trendSeries = calculateTrendLine(listHistoric,slope,intercept);
trendSeries->setPen(QPen(QBrush(Qt::red),2,Qt::DotLine));
trendSeries->setName("趋势");
trendSeries->setPointLabelsVisible(true);
chart->addSeries(trendSeries);
trendSeries->attachAxis(axisX);
trendSeries->attachAxis(axisY);
}

//计算预测数据
if(!chart->series().contains(predictSeries)){
predictSeries = new QSplineSeries();
predictSeries->setPen(QPen(QBrush(Qt::green),2,Qt::DashDotDotLine));
predictSeries->setName("预测数据");
predictSeries->setPointLabelsVisible(true);
chart->addSeries(predictSeries);
predictSeries->attachAxis(axisX);
predictSeries->attachAxis(axisY);

//强调一下连接点joint
listPredict.append(listHistoric.last());
listPredict.append(listHistoric.last());
listPredict.append(listHistoric.last());
listPredict.append(listHistoric.last());
listPredict.append(listHistoric.last());

for(int i=1;i<30;i++){
// 生成 [0, 1) 范围内的随机数
double randomValue = QRandomGenerator::global()->generateDouble()*0.05-0.01;
// listPredict.append(QPointF(QDateTime::currentDateTime().addDays(i).toMSecsSinceEpoch(), randomValue));

QPointF p = QPointF(QDateTime::fromMSecsSinceEpoch(listHistoric.last().x()).addDays(i).toMSecsSinceEpoch(),
randomValue);

qDebug()<<""<< p;
listPredict.append(p);

}

predictSeries->clear();
predictSeries->append(listPredict);

//延长x轴
datetimeFuture30Days = datetimeFuture30Days.addDays(30);
axisX -> setRange(datetimeNow,datetimeFuture30Days); //设置时间轴的范围,参数是两个时期时间QDateTime

}

}

.ui文件不贴了,只有一个QChartView。注意pro文件对应增加 :

1
QT += charts