灰色预测小例子

参考学习b站:数学建模学习交流

在这里插入图片描述


GM(1,1)模型思路:

  1. 画出原始数据的时间序列图,并判断原始数据中是否有负数或期数是否低于4期,如果是的话则报错,否则执行下一步
  2. 对一次累加后的数据进行准指数规律检验,返回两个指标:
    指标1:光滑比小于0.5的数据占比(一般要大于60%)
    指标2:除去前两个时期外,光滑比小于0.5的数据占比(一般大于90%)
    并让用户决定数据是否满足准指数规律,满足则输入1,不满足则输入0,程序退出
  3. 让用户输入需要预测的后续期数,并判断原始数据的期数

根据 数据期数 做如下判断:

  • 数据期数为4:
    分别计算出传统的GM(1,1)模型、新信息GM(1,1)模型和新陈代谢GM(1,1)模型对于未来期数的预测结果,为了保证结果的稳健性,对三个结果求平均值作为预测值
  • 数据期数为5,6或7:
    取最后两期为试验组,前面的n‐2期为训练组;用训练组的数据分别训练三种GM模型,并将训练出来的模型分别用于预测试验组的两期数据;利用试验组两期的真实数据和预测出来的两期数据,可分别计算出三个模型的SSE;选择SSE最小的模型作为我们建模的模型
  • 数据期数大于7:
    取最后三期为试验组,其他的过程和数据期数为5,6或7的类似

最后输出并绘制图形显示预测结果,并进行残差检验和级比偏差检验即可

代码:

输入原始数据并做出时间序列图

clear;clc
year =[1995:1:2004]';  % 横坐标表示年份,写成列向量的形式
x0 = [174,179,183,189,207,234,220.5,256,270,285]';  %原始数据序列,写成列向量的形式

% 画出原始数据的时间序列图
figure(1); % 因为我们的图形不止一个,因此要设置编号
plot(year,x0,'o-'); grid on;  % 原式数据的时间序列图
set(gca,'xtick',year(1:1:end))  % 设置x轴横坐标的间隔为1
xlabel('年份');  ylabel('排污总量');  % 给坐标轴加上标签

% grid on 是matlab中的一种函数,表示在画图的时候添加网格线

在这里插入图片描述

检查数据本身正确性(可不写)

ERROR = 0; 
% 判断是否有负数元素
if sum(x0<0) > 0 
    disp('灰色预测的时间序列中不能有负数')
    ERROR = 1;
end

% 判断数据量是否太少
n = length(x0);  % 计算原始数据的长度
disp(strcat('原始数据的长度为',num2str(n)))    % strcat()是连接字符串的函数
if n<=3
    disp('数据量太小')
    ERROR = 1;
end

% 数据太多时提示可考虑使用其他方法(不报错)
if n>10
    disp('数据量较多,可使用其他的方法')
end

% 判断数据是否为列向量,如果输入的是行向量则转置为列向量
% 判断列的话就用size(x0,2)
if size(x0,1) == 1 % 只有1行
    x0 = x0';
end
if size(year,1) == 1
    year = year';
end

进行准指数规律的检验

if ERROR == 0 
    disp('------------------------------------------------------------')
    disp('准指数规律检验')
    x1 = cumsum(x0);   % 生成1-AGO序列,cumsum是累加函数
    rho = x0(2:end) ./ x1(1:end-1) ;   % 计算光滑度rho(k) = x0(k)/x1(k-1)
    
    % 画出光滑度的图形,并画上0.5的直线,表示临界值
    figure(2)
    plot(year(2:end),rho,'o-',[year(2),year(end)],[0.5,0.5],'-'); grid on;
    text(year(end-1)+0.2,0.55,'临界线')   % 在坐标(year(end-1)+0.2,0.55)上添加文本
    set(gca,'xtick',year(2:1:end))  % 设置x轴横坐标的间隔为1
    xlabel('年份');  ylabel('原始数据的光滑度');  % 给坐标轴加上标签
    
    
    disp(strcat('指标1:光滑比小于0.5的数据占比为',num2str(100*sum(rho<0.5)/(n-1)),'%'))
    disp(strcat('指标2:除去前两个时期外,光滑比小于0.5的数据占比为',num2str(100*sum(rho(3:end)<0.5)/(n-3)),'%'))
    disp('参考标准:指标1一般要大于60%, 指标2要大于90%,请判断数据是否通过检验')
    
    Judge = input('如果可以通过请输入1,不能请输入0:');
    if Judge == 0
        disp('未通过检验')
        ERROR = 1;
    end
    disp('------------------------------------------------------------')
end

输出:

准指数规律检验
指标1:光滑比小于0.5的数据占比为77.7778%
指标2:除去前两个时期外,光滑比小于0.5的数据占比为100%
参考标准:指标1一般要大于60%, 指标2要大于90%,请判断数据是否通过检验

在这里插入图片描述

数据量大于4时,我们利用试验组来选择使用传统的GM(1,1)模型、新信息GM(1,1)模型还是新陈代谢GM(1,1)模型,代码如下:

gm11.m

function [result, x0_hat, relative_residuals, eta] = gm11(x0, predict_num)
    % 函数作用:使用传统的GM(1,1)模型对数据进行预测
    %     x0:要预测的原始数据
    %     predict_num: 向后预测的期数
    %     result:预测值
    %     x0_hat:对原始数据的拟合值
    %     relative_residuals: 对模型进行评价时计算得到的相对残差
    %     eta: 对模型进行评价时计算得到的级比偏差

    n = length(x0); % 数据的长度
    x1=cumsum(x0); % 计算一次累加值
    z1 = (x1(1:end-1) + x1(2:end)) / 2;  % 计算紧邻均值生成数列(长度为n-1% 将从第二项开始的x0当成y,z1当成x,来进行一元回归  y = kx +b
    y = x0(2:end); x = z1;
    % 下面的表达式就是第四讲拟合里面的哦~ 但是要注意,此时的样本数应该是n-1,少了一项哦
    k = ((n-1)*sum(x.*y)-sum(x)*sum(y))/((n-1)*sum(x.*x)-sum(x)*sum(x));
    b = (sum(x.*x)*sum(y)-sum(x)*sum(x.*y))/((n-1)*sum(x.*x)-sum(x)*sum(x));
    a = -k;  %注意:k = -a哦
    % 注意: -a就是发展系数,  b就是灰作用量
    
    disp('现在进行GM(1,1)预测的原始数据是: ')
    disp(mat2str(x0'))  % mat2str可以将矩阵或者向量转换为字符串显示
    disp(strcat('最小二乘法拟合得到的发展系数为',num2str(-a),',灰作用量是',num2str(b)))
    disp('***************分割线***************')
    x0_hat=zeros(n,1);  x0_hat(1)=x0(1);   % x0_hat向量用来存储对x0序列的拟合值,这里先进行初始化
    for m = 1: n-1
        x0_hat(m+1) = (1-exp(a))*(x0(1)-b/a)*exp(-a*m);
    end
    result = zeros(predict_num,1);  % 初始化用来保存预测值的向量
    for i = 1: predict_num
        result(i) = (1-exp(a))*(x0(1)-b/a)*exp(-a*(n+i-1)); % 带入公式直接计算
    end

    % 计算绝对残差和相对残差
    absolute_residuals = x0(2:end) - x0_hat(2:end);   % 从第二项开始计算绝对残差,因为第一项是相同的
    relative_residuals = abs(absolute_residuals) ./ x0(2:end);  % 计算相对残差,注意分子要加绝对值,而且要使用点除
    % 计算级比和级比偏差
    class_ratio = x0(2:end) ./ x0(1:end-1) ;  % 计算级比 sigma(k) = x0(k)/x0(k-1)
    eta = abs(1-(1-0.5*a)/(1+0.5*a)*(1./class_ratio));  % 计算级比偏差
end

new_gm11.m

function [result] = new_gm11(x0, predict_num)
% 函数作用:使用新信息的GM(1,1)模型对数据进行预测
% 输入变量
%     x0:要预测的原始数据
%     predict_num: 向后预测的期数
% 输出变量
%     result:预测值
    result = zeros(predict_num,1);  % 初始化用来保存预测值的向量
    for i = 1 : predict_num  
        result(i) = gm11(x0, 1);  % 将预测一期的结果保存到result中
        x0 = [x0; result(i)];  % 更新x0向量,此时x0多了新的预测信息
    end
end

metabolism_gm11.m

function [result] = metabolism_gm11(x0, predict_num)
% 函数作用:使用新陈代谢的GM(1,1)模型对数据进行预测
% 输入变量
%     x0:要预测的原始数据
%     predict_num: 向后预测的期数
% 输出变量
%     result:预测值
    result = zeros(predict_num,1);  % 初始化用来保存预测值的向量
    for i = 1 : predict_num  
        result(i) = gm11(x0, 1);  % 将预测一期的结果保存到result中
        x0 = [x0(2:end); result(i)];  % 更新x0向量,此时x0多了新的预测信息,并且删除了最开始的那个向量
    end
end

main.m

if ERROR == 0   % 如果上述错误均没有发生时,才能执行下面的操作步骤
    if  n > 4  % 数据量大于4时,将数据分为训练组和试验组(根据原数据量大小n来取,n为5-7个则取最后两年为试验组,n大于7则取最后三年为试验组)
        disp('因为原数据的期数大于4,所以我们可以将数据组分为训练组和试验组')   % 注意,如果试验组的个数只有1个,那么三种模型的结果完全相同,因此至少要取2个试验组
        if n > 7
            test_num = 3;
        else
            test_num = 2;
        end
        train_x0 = x0(1:end-test_num);  % 训练数据
        disp('训练数据是: ')
        disp(mat2str(train_x0'))  % mat2str可以将矩阵或者向量转换为字符串显示, 这里加一撇表示转置,把列向量变成行向量方便观看
        test_x0 =  x0(end-test_num+1:end); % 试验数据
        disp('试验数据是: ')
        disp(mat2str(test_x0'))  % mat2str可以将矩阵或者向量转换为字符串显示
        disp('------------------------------------------------------------')
        
        % 使用三种模型对训练数据进行训练,返回的result就是往后预测test_num期的数据
        disp(' ')
        disp('***下面是传统的GM(1,1)模型预测的详细过程***')
        result1 = gm11(train_x0, test_num); %使用传统的GM(1,1)模型对训练数据,并预测后test_num期的结果
        disp(' ')
        disp('***下面是进行新信息的GM(1,1)模型预测的详细过程***')
        result2 = new_gm11(train_x0, test_num); %使用新信息GM(1,1)模型对训练数据,并预测后test_num期的结果
        disp(' ')
        disp('***下面是进行新陈代谢的GM(1,1)模型预测的详细过程***')
        result3 = metabolism_gm11(train_x0, test_num); %使用新陈代谢GM(1,1)模型对训练数据,并预测后test_num期的结果
        
        % 现在比较三种模型对于试验数据的预测结果
        disp(' ')
        disp('------------------------------------------------------------')
        % 绘制对试验数据进行预测的图形(对于部分数据,可能三条直线预测的结果非常接近)
        test_year = year(end-test_num+1:end);  % 试验组对应的年份
        figure(3)
        plot(test_year,test_x0,'o-',test_year,result1,'*-',test_year,result2,'+-',test_year,result3,'x-'); grid on;
        set(gca,'xtick',year(end-test_num+1): 1 :year(end))  % 设置x轴横坐标的间隔为1
        legend('试验组的真实数据','传统GM(1,1)预测结果','新信息GM(1,1)预测结果','新陈代谢GM(1,1)预测结果')  % 注意:如果lengend挡着了图形中的直线,那么lengend的位置可以自己手动拖动
        xlabel('年份');  ylabel('排污总量');  % 给坐标轴加上标签
        % 计算误差平方和SSE
        SSE1 = sum((test_x0-result1).^2);
        SSE2 = sum((test_x0-result2).^2);
        SSE3 = sum((test_x0-result3).^2);
        disp(strcat('传统GM(1,1)对于试验组预测的误差平方和为',num2str(SSE1)))
        disp(strcat('新信息GM(1,1)对于试验组预测的误差平方和为',num2str(SSE2)))
        disp(strcat('新陈代谢GM(1,1)对于试验组预测的误差平方和为',num2str(SSE3)))
        if SSE1<SSE2
            if SSE1<SSE3
                choose = 1;  % SSE1最小,选择传统GM(1,1)模型
            else
                choose = 3;  % SSE3最小,选择新陈代谢GM(1,1)模型
            end
        elseif SSE2<SSE3
            choose = 2;  % SSE2最小,选择新信息GM(1,1)模型
        else
            choose = 3;  % SSE3最小,选择新陈代谢GM(1,1)模型
        end
        Model = {'传统GM(1,1)模型','新信息GM(1,1)模型','新陈代谢GM(1,1)模型'};
        disp(strcat('因为',Model(choose),'的误差平方和最小,所以我们应该选择其进行预测'))
        disp('------------------------------------------------------------')
        
        %% 选用误差最小的那个模型进行预测
        predict_num = input('请输入你要往后面预测的期数: ');
        % 计算使用传统GM模型的结果,用来得到另外的返回变量:x0_hat, 相对残差relative_residuals和级比偏差eta
        [result, x0_hat, relative_residuals, eta] = gm11(x0, predict_num);  % 先利用gm11函数得到对原数据拟合的详细结果
        
        % % 判断我们选择的是哪个模型,如果是23,则更新刚刚由模型1计算出来的预测结果
        if choose == 2
            result = new_gm11(x0, predict_num);
        end
        if choose == 3
            result = metabolism_gm11(x0, predict_num);
        end
        
        %% 输出使用最佳的模型预测出来的结果
        disp('------------------------------------------------------------')
        disp('对原始数据的拟合结果:')
        for i = 1:n
            disp(strcat(num2str(year(i)), ' : ',num2str(x0_hat(i))))
        end
        disp(strcat('往后预测',num2str(predict_num),'期的得到的结果:'))
        for i = 1:predict_num
            disp(strcat(num2str(year(end)+i), ' : ',num2str(result(i))))
        end
    end

输出:
在这里插入图片描述

因为原数据的期数大于4,所以我们可以将数据组分为训练组和试验组
训练数据是: 
[174 179 183 189 207 234 220.5]
试验数据是: 
[256 270 285]
------------------------------------------------------------
***下面是传统的GM(1,1)模型预测的详细过程***
现在进行GM(1,1)预测的原始数据是: 
[174 179 183 189 207 234 220.5]
最小二乘法拟合得到的发展系数为0.053355,灰作用量是162.1358

 
***下面是进行新信息的GM(1,1)模型预测的详细过程***
现在进行GM(1,1)预测的原始数据是: 
[174 179 183 189 207 234 220.5]
最小二乘法拟合得到的发展系数为0.053355,灰作用量是162.1358

现在进行GM(1,1)预测的原始数据是: 
[174 179 183 189 207 234 220.5 242.509856680773]
最小二乘法拟合得到的发展系数为0.053316,灰作用量是162.1589

现在进行GM(1,1)预测的原始数据是: 
[174 179 183 189 207 234 220.5 242.509856680773 255.749834975262]
最小二乘法拟合得到的发展系数为0.053284,灰作用量是162.1806

 
***下面是进行新陈代谢的GM(1,1)模型预测的详细过程***
现在进行GM(1,1)预测的原始数据是: 
[174 179 183 189 207 234 220.5]
最小二乘法拟合得到的发展系数为0.053355,灰作用量是162.1358

现在进行GM(1,1)预测的原始数据是: 
[179 183 189 207 234 220.5 242.509856680773]
最小二乘法拟合得到的发展系数为0.055735,灰作用量是169.0787

现在进行GM(1,1)预测的原始数据是: 
[183 189 207 234 220.5 242.509856680773 257.26402744888]
最小二乘法拟合得到的发展系数为0.054739,灰作用量是180.0538

------------------------------------------------------------
传统GM(1,1)对于试验组预测的误差平方和为614.0612
新信息GM(1,1)对于试验组预测的误差平方和为618.9622
新陈代谢GM(1,1)对于试验组预测的误差平方和为531.1548

{'因为新陈代谢GM(1,1)模型的误差平方和最小,所以我们应该选择其进行预测'}

------------------------------------------------------------
请输入你要往后面预测的期数: 3

现在进行GM(1,1)预测的原始数据是: 
[174 179 183 189 207 234 220.5 256 270 285]
最小二乘法拟合得到的发展系数为0.062398,灰作用量是156.6162

现在进行GM(1,1)预测的原始数据是: 
[174 179 183 189 207 234 220.5 256 270 285]
最小二乘法拟合得到的发展系数为0.062398,灰作用量是156.6162

现在进行GM(1,1)预测的原始数据是: 
[179 183 189 207 234 220.5 256 270 285 303.012231932034]
最小二乘法拟合得到的发展系数为0.064197,灰作用量是164.7232

现在进行GM(1,1)预测的原始数据是: 
[183 189 207 234 220.5 256 270 285 303.012231932034 324.325434937853]
最小二乘法拟合得到的发展系数为0.064416,灰作用量是175.8281

------------------------------------------------------------
对原始数据的拟合结果:
1995174
1996172.809
1997183.9355
1998195.7785
1999208.3839
2000221.801
2001236.082
2002251.2825
2003267.4616
2004284.6825

往后预测3期的得到的结果:
2005303.0122
2006324.3254
2007346.0304

如果数据量等于4,那么我们直接对三种方法求一个平均来进行预测

disp('因为数据只有4期,因此我们直接将三种方法的结果求平均即可~')
predict_num = input('请输入你要往后面预测的期数: ');
disp(' ')
disp('***下面是传统的GM(1,1)模型预测的详细过程***')
[result1, x0_hat, relative_residuals, eta] = gm11(x0, predict_num);
disp(' ')
disp('***下面是进行新信息的GM(1,1)模型预测的详细过程***')
result2 = new_gm11(x0, predict_num);
disp(' ')
disp('***下面是进行新陈代谢的GM(1,1)模型预测的详细过程***')
result3 = metabolism_gm11(x0, predict_num);
result = (result1+result2+result3)/3;

disp('对原始数据的拟合结果:')
for i = 1:n
        disp(strcat(num2str(year(i)), ' : ',num2str(x0_hat(i))))
end
        disp(strcat('传统GM(1,1)往后预测',num2str(predict_num),'期的得到的结果:'))
for i = 1:predict_num
        disp(strcat(num2str(year(end)+i), ' : ',num2str(result1(i))))
end
        disp(strcat('新信息GM(1,1)往后预测',num2str(predict_num),'期的得到的结果:'))
for i = 1:predict_num
        disp(strcat(num2str(year(end)+i), ' : ',num2str(result2(i))))
end
        disp(strcat('新陈代谢GM(1,1)往后预测',num2str(predict_num),'期的得到的结果:'))
for i = 1:predict_num
        disp(strcat(num2str(year(end)+i), ' : ',num2str(result3(i))))
end
        disp(strcat('三种方法求平均得到的往后预测',num2str(predict_num),'期的得到的结果:'))
for i = 1:predict_num
        disp(strcat(num2str(year(end)+i), ' : ',num2str(result(i))))
end

绘制相对残差和级比偏差的图形(注意:因为是对原始数据的拟合效果评估,所以三个模型都是一样的)

figure(4)
subplot(2,1,1)  % 绘制子图(将图分块)
plot(year(2:end), relative_residuals,'*-'); grid on;   % 原数据中的各时期和相对残差
legend('相对残差'); xlabel('年份');
set(gca,'xtick',year(2:1:end))  % 设置x轴横坐标的间隔为1
subplot(2,1,2)
plot(year(2:end), eta,'o-'); grid on;   % 原数据中的各时期和级比偏差
legend('级比偏差'); xlabel('年份');
set(gca,'xtick',year(2:1:end))  % 设置x轴横坐标的间隔为1

在这里插入图片描述

残差检验+级比偏差检验

disp('****下面将输出对原数据拟合的评价结果***')

%% 残差检验
average_relative_residuals = mean(relative_residuals);  % 计算平均相对残差 mean函数用来均值
disp(strcat('平均相对残差为',num2str(average_relative_residuals)))
if average_relative_residuals<0.1
	disp('残差检验的结果表明:该模型对原数据的拟合程度非常不错')
elseif average_relative_residuals<0.2
	disp('残差检验的结果表明:该模型对原数据的拟合程度达到一般要求')
else
	disp('残差检验的结果表明:该模型对原数据的拟合程度不太好,建议使用其他模型预测')
end

%% 级比偏差检验
average_eta = mean(eta);   % 计算平均级比偏差
disp(strcat('平均级比偏差为',num2str(average_eta)))
if average_eta<0.1
	disp('级比偏差检验的结果表明:该模型对原数据的拟合程度非常不错')
elseif average_eta<0.2
	disp('级比偏差检验的结果表明:该模型对原数据的拟合程度达到一般要求')
else
	disp('级比偏差检验的结果表明:该模型对原数据的拟合程度不太好,建议使用其他模型预测')
end
disp(' ')
disp('------------------------------------------------------------')

结果:

****下面将输出对原数据拟合的评价结果***
平均相对残差为0.025999
残差检验的结果表明:该模型对原数据的拟合程度非常不错
平均级比偏差为0.047041
级比偏差检验的结果表明:该模型对原数据的拟合程度非常不错

绘制最终的预测效果图

figure(5)  % 下面绘图中的符号m:洋红色 b:蓝色
plot(year,x0,'-o',  year,x0_hat,'-*m',  year(end)+1:year(end)+predict_num,result,'-*b' );   grid on;
hold on;
plot([year(end),year(end)+1],[x0(end),result(1)],'-*b')
legend('原始数据','拟合数据','预测数据')  % 注意:如果lengend挡着了图形中的直线,那么lengend的位置可以自己手动拖动
set(gca,'xtick',[year(1):1:year(end)+predict_num])  % 设置x轴横坐标的间隔为1
xlabel('年份');  ylabel('排污总量');  % 给坐标轴加上标签

结果:
在这里插入图片描述

  • 12
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
当然,下面是一个简单的示例,演示如何使用MATLAB实现灰色预测。 ```matlab % 灰色预测函数 function [predict] = greyPrediction(data) % 累加生成序列 accumulation = cumsum(data); % 累加生成序列的均值 meanAccumulation = (accumulation(1:end-1) + accumulation(2:end)) / 2; % 构建累加生成数据矩阵B和数据列向量Y B = [-meanAccumulation' ones(length(meanAccumulation), 1)]; Y = data(2:end)'; % 使用最小二乘法求解参数a和b parameters = pinv(B) * Y; % 利用模型进行预测 predict = (data(1) - parameters(2) / parameters(1)) * exp(-parameters(1) * (0:length(data)-1)) + parameters(2) / parameters(1); end % 示例数据 data = [20 30 40 50 60]; % 进行灰色预测 prediction = greyPrediction(data); % 显示原始数据和预测结果 plot(1:length(data), data, 'ro-', 'LineWidth', 1.5); hold on; plot(length(data):length(data)+length(prediction)-1, prediction, 'bs-', 'LineWidth', 1.5); legend('原始数据', '预测结果'); xlabel('时间'); ylabel('数值'); ``` 在上述代码中,`greyPrediction` 函数实现了灰色预测模型的计算。首先根据原始数据计算累加生成序列,然后构建累加生成数据矩阵B和数据列向量Y。接下来使用最小二乘法求解参数a和b,最后利用模型进行预测。在示例中,我们使用了一个简单的示例数据 `data`,然后通过灰色预测模型进行预测,并使用 `plot` 函数将原始数据和预测结果进行可视化。 请注意,这只是一个简单的示例,实际应用中可能需要更多的数据处理和模型调整。灰色预测方法有多种变体和改进,具体的应用需要根据实际情况进行调整和优化。希望这个示例能对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值