ARIMA模型,一阶差分预测数据还原

请问我这的段时间序列模型的diamante,最后怎么将最后的一阶差分预测数据进行还原。因为预测出来的数据差距有点大

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
import statsmodels.api as sm
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.tsa.stattools import adfuller as ADF
from statsmodels.graphics.tsaplots import plot_pacf
from statsmodels.stats.diagnostic import acorr_ljungbox
from statsmodels.tsa.arima_model import ARIMA
from statsmodels.tsa.arima_model import ARMA
import warnings


# 导入数据
# 读取数据,指定年份列为指标,pandas自动将'日期'列识别为Datetime格式
data = pd.read_excel('E:\桌面\机器学习\时间序列数据分析\能源数据集.xlsx', sheet_name='Sheet1', index_col=u'年份', header=0)
# index_col:指定哪些列用作数据框的行索引(标签)
# header:是否需要将数据集的第一行用作表头,默认用作
# u'字符串':后面字符串以 Unicode 格式 进行编码,一般用在中文字符串前面,防止因为源码储存格
# 式问题,导致再次使用时出现乱码。
print(30 * '*' + '样本中的前五行的数据' + 30 * '*')
print(data.head())  # 输出前五行数据
print(30 * '*' + '*****************' + 30 * '*')
# 设置预测的年份
predict_year = 14  # 表示预测往后的14年的数据
# 获取最后一年
temp4 = data.to_dict()['能源产量']
list_1 = temp4.keys()
count = data.shape[0] - 1
date = list(list_1)[count]
# 设置字体大小,及格式
config = {
    "font.family": 'sans-serif',
    "font.size": 15,  # 这只字体的大小
    "mathtext.fontset": 'stix',
    "font.serif": ['SimSun8'],  # 宋体
    'axes.unicode_minus': False  # 处理负号
}
rcParams.update(config)
# 做时序图
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文的标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
data.plot()
plt.ylabel('能源产量')
plt.title(u'关于能源产量的时序图')

# 做自相关图
fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_subplot(111)
fig = plot_acf(data['能源产量'], ax=ax1)
fig.gca().set_title('原始序列的自相关图')
fig.gca().set_xlabel('延迟阶数')
fig.gca().set_ylabel('自相关系数(ACF)')
# 平稳性检验
print(30 * '*' + '原始序列的ADF(单位根检验)检验结果为' + 30 * '*')
# print(u'原始序列的ADF检验结果为:', ADF(data[u'能源产量']))
# 返回值依次为adf、pvalue、usedlag、nobs、critical values、icbest、regresults、resstore
# 若P值明显大于0.05,则判断该序列为非平稳序列(一定不是白噪声序列)
temp = ADF(data[u'能源产量'])[4]
print('                            cValue                        ', '    P值')
print('adf', '          ', '1%', '          ', '5%', '          ', '10%')
print(round(ADF(data[u'能源产量'])[0], 4), '    ', round(temp['1%'], 4), '      ', round(temp.get('5%'), 4), '      ',
      round(temp['10%'], 4), '          ', round(ADF(data[u'能源产量'])[1], 4))
# 如果adf(单位根检验)的P值大于0.05,判断为非平稳序列,则进行一阶差分,之后继续做平稳性判断
if round(ADF(data[u'能源产量'])[1], 4) <= 0.05:
    print('根据adf检验,判断该时间序列为平稳时间序列')
    print(30 * '*' + '输出原序列的白噪声检验:' + 30 * '*')
    #检验是否是白噪声序列
    temp3 = acorr_ljungbox(data, lags=1, return_df=True)  # lags 为滞后数,return_df设置为True以用于立即返回DataFrame
    print(temp3)  # lb_stat为统计量(stat),lb_pvalue为p值
    # 将数据原类型转换为浮点类型
    data[u'能源产量'] = data[u'能源产量'].astype(float)
    # 定阶
    pmax = int(len(data) / 10)  # 一般阶数不超过length/10
    qmax = int(len(data) / 10)  # 一般阶数不超过length/10
    bic_matrix = []  # bic矩阵
    for p in range(pmax + 1):
        tmp = []
        for q in range(qmax + 1):
            try:  # 存在部分错误,所以用try来跳过报错
                tmp.append(round(ARMA(data,order = (p, q)).fit().bic, 2))
                warnings.filterwarnings('ignore')  # 忽视异常提示
            except:
                tmp.append(None)
            warnings.filterwarnings('ignore')  # 忽视异常提示
        bic_matrix.append(tmp)
    print(30 * '*' + '输出序列的BIC矩阵:' + 30 * '*')
    for p in range(pmax + 1):
        print(bic_matrix[p])
    bic_matrix = pd.DataFrame(bic_matrix)  # 从中可以找出最小值
    p, q = bic_matrix.stack().astype('float64').idxmin()  # 先用stack展平,然后用idxmin找出最小值位置
    print(u'BIC最小的p值和q值为:%s、%s' % (p, q))
    print(30 * '*' + '输出ARMA模型矩阵:' + 30 * '*')
    model = ARMA(data, order=(p, q)).fit()  # 建立ARMA(p,q)模型
    print(30 * '*' + '模型报告' + 30 * '*')
    print(model.summary2())  # 给出一份模型报告
    print(30 * '*' + 'ARMA模型预测结果' + 30 * '*')
    temp3 = model.forecast(predict_year)  # 做为期5天的预测,返回预测结果、标准误差、置信区间
    predict_res = temp3[0]
    standard_error = temp3[1]
    confidence_interval = temp3[2]
    print('预测结果', '           ', '标准误差', '           ', '置信区间')
    for i in range(predict_year):
        print(round(predict_res[i], 2), ' ' * 10, round(standard_error[i], 2), ' ' * 10, '[',
              round(confidence_interval[i][0], 2), '~',
              round(confidence_interval[i][1], 2), ']')
else:
    print('根据adf检验,判断该时间序列为非平稳时间序列,需要进行一阶差分再次检验')
    diff_data = data.diff().dropna()  # dropna()函数是用来删除缺失值的
    diff_data.columns = [u'能源产量的一阶差分']
    print(30 * '*' + '输出差分后的前五行数据' + 30 * '*')
    print(diff_data.head())
    diff_data.plot()
    plt.ylabel('差分后序列')
    plt.title(u'关于一阶差分后能源产量的时序图')
    print(30 * '*' + '进行一阶差分序列的ADF(单位根检验)检验结果为' + 30 * '*')
    # 绘制一阶差分后的自相关图
    fig2 = plt.figure(figsize=(10, 5))
    ax2 = fig2.add_subplot(111)
    fig2 = plot_acf(diff_data['能源产量的一阶差分'], ax=ax2)
    fig2.gca().set_title('一阶差分后能源产量的自相关图')
    fig2.gca().set_xlabel('延迟阶数')
    fig2.gca().set_ylabel('自相关系数(ACF)')
    # 绘制一阶差分后的偏自相关图
    fig3 = plt.figure(figsize=(10, 5))
    ax3 = fig3.add_subplot(111)
    fig3 = plot_pacf(diff_data['能源产量的一阶差分'], ax=ax3)
    fig3.gca().set_title('一阶差分后能源产量的偏相关图')
    fig3.gca().set_xlabel('延迟阶数')
    fig3.gca().set_ylabel('偏自相关系数(PACF)')
    # 进行一阶差分后的ADF(单位根检验)检验结果为
    temp_ADF = ADF(diff_data[u'能源产量的一阶差分'])
    temp1 = temp_ADF[4]
    print('                            cValue                        ', '    P值')
    print('adf', '          ', '1%', '          ', '5%', '          ', '10%')
    print(round(temp_ADF[0], 4), '    ', round(temp1['1%'], 4), '    ', round(temp1.get('5%'), 4), '      ',
          round(temp1['10%'], 4), '           ', round(temp_ADF[1], 4))
    # 对差分后的序列进行白噪声检验
    print(30 * '*' + '输出一阶差分后序列的白噪声检验:' + 30 * '*')
    temp2 = acorr_ljungbox(diff_data, lags=1, return_df=True)  # lags 为滞后数,return_df设置为True以用于立即返回DataFrame
    print(temp2)  # lb_stat为统计量(stat),lb_pvalue为p值
    print(30 * '*' + '输出定阶的相关步骤:' + 30 * '*')
    # 将数据原类型转换为浮点类型
    data[u'能源产量'] = data[u'能源产量'].astype(float)
    # 定阶
    pmax = int(len(diff_data) / 10)  # 一般阶数不超过length/10
    qmax = int(len(diff_data) / 10)  # 一般阶数不超过length/10
    bic_matrix = []  # bic矩阵
    for p in range(pmax + 1):
        tmp = []
        for q in range(qmax + 1):
            try:  # 存在部分错误,所以用try来跳过报错
                tmp.append(round(ARIMA(data, order = (p, 1, q)).fit().bic, 2))
                warnings.filterwarnings('ignore')  # 忽视异常提示
            except:
                tmp.append(None)
            warnings.filterwarnings('ignore')  # 忽视异常提示
        bic_matrix.append(tmp)
    print(30 * '*' + '输出一阶差分序列的BIC矩阵:' + 30 * '*')
    for p in range(pmax + 1):
        print(bic_matrix[p])
    bic_matrix = pd.DataFrame(bic_matrix)  # 从中可以找出最小值
    p, q = bic_matrix.stack().astype('float64').idxmin()  # 先用stack展平,然后用idxmin找出最小值位置
    print(u'BIC最小的p值和q值为:%s、%s' % (p, q))
    print(30 * '*' + '输出ARIMA模型矩阵:' + 30 * '*')
    #如果原始序列不平稳,那么一阶差分后,将调用ARIMA模型,1表示一阶差分
    model = ARIMA(data, (p, 1, q)).fit()  # 建立ARIMA(0,1,1)模型
    print(30 * '*' + '模型报告' + 30 * '*')
    print(model.summary2())  # 给出一份模型报告
    print(30 * '*' + 'ARIMA模型预测结果' + 30 * '*')
    temp3 = model.forecast(predict_year)  # 做为期5天的预测,返回预测结果、标准误差、置信区间

    #print(temp3)

    df_shift = data['能源产量'].shift(1)
    #print(df_shift)

    #temp3[0] = temp3[0] + df_shift

    # #还原数据
    # predict_ts = model.predict()
    # # 一阶差分还原
    # diff_shift_ts = model.shift(1)
    # diff_recover_1 = predict_ts.add(diff_shift_ts)  # 再次一阶差分还原
    # rol_shift_ts = model.shift(1)
    # diff_recover = diff_recover_1.add(rol_shift_ts)  # 移动平均还原
    # rol_sum = model.rolling(window=11).sum()
    # rol_recover = diff_recover * 12 - rol_sum.shift(1)  # 对数还原
    # log_recover = np.exp(rol_recover)
    # log_recover.dropna(inplace=True)


    predict_res = temp3[0]
    print(predict_res)
    standard_error = temp3[1]
    confidence_interval = temp3[2]
    print('预测结果', '           ', '标准误差', '           ', '置信区间')
    for i in range(predict_year):
        print(round(predict_res[i], 2), ' ' * 10, round(standard_error[i], 2), ' ' * 10, '[',
              round(confidence_interval[i][0], 2), '~',
              round(confidence_interval[i][1], 2), ']')
plt.show()

由此可知,我们只需要将差分后预测出来的数据,加上原数据往后搓一位即可。代码如下:

# 一阶差分还原
# tmpdata2:原数据
# pred:一阶差分后的预测数据
df_shift = tmpdata2['ecpm_tomorrow'].shift(1)
predict = pred.add(df_shift)
# predict = pred + df_shift
predict

对于使用一阶差分的时间序列模型,预测得到的结果是对原始数据的一阶差分进行预测,而不是直接对原始数据进行预测。所以需要对预测结果进行反差分,得到最后的预测值。

具体方法如下:

  1. 获取原始数据的最后一个值,命名为last_value。
  2. 将预测得到的一阶差分与last_value相加,得到还原后的预测值。即:predicted_value = predicted_diff + last_value。

以下是一个示例代码:

import pandas as pd
from statsmodels.tsa.api import ARIMA

# 加载数据
data = pd.read_csv('data.csv', index_col=0, parse_dates=True)

# 将原始数据进行一阶差分
diff = data.diff().dropna()

# 构造ARIMA(0,1,1)模型并拟合数据
model = ARIMA(diff, order=(0,1,1))
model_fit = model.fit(disp=False)

# 预测当前时刻的一阶差分
predicted_diff = model_fit.forecast()[0]

# 获取原始数据的最后一个值
last_value = data.iloc[-1]['value']

# 将预测得到的一阶差分与last_value相加,得到还原后的预测值
predicted_value = predicted_diff + last_value

# 打印预测结果
print('Predicted value:', predicted_value)

在这个示例中,我们使用了pandas和ARIMA模型对数据进行了处理与拟合,并预测了当前时刻的一阶差分predicted_diff,然后通过获取原始数据的最后一个值last_value,将预测得到的一阶差分与last_value相加,得到了还原后的预测值predicted_value。

  • 以下回答由chatgpt基于相关博客总结生成:

    对于ARIMA模型进行一阶差分预测时,预测结果与实际数据差距较大,需要将差分预测数据还原到原始数据的尺度上,以获得更准确的预测结果。具体步骤如下:

    1. 获取原始数据,假设原始数据存储在一个名为"original_data"的DataFrame中。

    2. 对原始数据进行一阶差分处理,得到差分数据。可以使用pandas.DataFrame.diff()函数实现,代码如下:

    diff_data = original_data.diff().dropna()
    
    1. 对差分数据进行ARIMA建模,并进行预测。假设得到的预测结果存储在一个名为"diff_pred"的Series中。

    2. 将差分预测数据还原到原始数据的尺度上。可以使用pandas.Series.cumsum()函数将差分数据进行累加还原。代码如下:

    pred = original_data.iloc[0] + diff_pred.cumsum()
    

    其中,original_data.iloc[0]表示原始数据的第一个值,也就是差分数据的第一个值的原始值。

    1. 对比预测结果和实际数据,评估预测效果。

    总的代码如下:

    # 获取原始数据
    original_data = get_data()
    
    # 对原始数据进行一阶差分处理,得到差分数据
    diff_data = original_data.diff().dropna()
    
    # 对差分数据进行ARIMA建模,并进行预测
    diff_model = ARIMA(diff_data, order=(p, 1, q))
    diff_result = diff_model.fit()
    diff_pred = diff_result.predict(start=len(diff_data), end=len(diff_data) + n-1)
    
    # 将差分预测数据还原到原始数据的尺度上
    pred = original_data.iloc[0] + diff_pred.cumsum()
    
    # 对比预测结果和实际数据,评估预测效果
    evaluate(original_data, pred)
    

    其中,get_data()evaluate()函数分别用于获取数据和评估预测效果,具体实现根据不同的数据集有所不同。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
在ARIMA模型中,一阶差分后的预测数据需要进行还原操作才能得到真实的预测值。具体的还原方法分为两步。假设原始序列为$y_t$,一阶差分后序列为$dy_t$,预测的一阶差分序列为$\widehat{dy_t}$,预测的值序列为$\widehat{y_t}$。

第一步:将一阶差分后预测数据$\widehat{dy_t}$还原成预测值$\widehat{y_t}$。需要注意的是,还原时需要使用前一个时刻的真实值才能得到当前时刻的真实值。具体操作如下:

$$
\widehat{y_t}=y_{t-1} + \widehat{dy_t}
$$

第二步:将$\widehat{y_t}$做差分还原得到真实预测值。一阶差分序列是通过对原始序列进行差分得到的,所以还原预测值时需要对应进行逆差分操作,具体操作如下:

$$
\widehat{y_t}= \sum_{i=1}^{t}\widehat{dy_i} + y_0
$$

其中,$y_0$表示原始序列的第一个值。

在代码实现上,可以在ARIMA模型预测时将预测出的一阶差分序列进行还原。具体实现
如果我的回答解决了您的问题,请采纳!

以下答案由GPT-4.5Plus大模型(语音)与博主A努力中.......共同编写:

你好,我看了你的问题和代码,发现有几个需要注意的地方:

  1. ARIMA模型预测的结果是基于差分序列预测的,要想得到原始数据的预测结果,需要对预测结果进行还原操作。你的代码中虽然调用了model.forecast()获取了预测结果,但是没有进行还原操作,所以得出的预测数据差距较大,这是正常的。

  2. 还原ARIMA模型预测结果的主要步骤是:

  • 将预测结果按照差分次数进行累加(如果是seasonal ARIMA,还需要按照season长度循环累加)
  • 对于对数差分的,需要进行指数转换,得到真实数据
  1. 所以你的代码可以这么修改:
python
# ARIMA模型预测
temp3 = model.forecast(predict_year)  
predict_res = temp3[0]

# 还原预测结果
diff_recover = predict_res  # 初始化
for i in range(1, model.k_diff + 1):  # model.k_diff为差分次数
    diff_shift = model.shift(i)
    diff_recover = diff_recover + diff_shift

# 对数还原    
if model.k_diff > 0: 
    rol_recover = diff_recover * modle.k_diff  
    log_recover = np.exp(rol_recover)
else:
    log_recover = diff_recover

  1. 另外,注意还原后的预测数据要与原始数据的维度一致,所以还原后可能需要使用.dropna()或其他方法填补数据。

  2. 针对此问题,如果您有任何其他疑问,也请在此提出,我很乐意继续帮助您解答。