欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

时间序列预测12:用电量预测 02 朴素模型多步预测建模

程序员文章站 2022-07-11 15:35:47
...

上文,本文使用朴素模型来对家庭用电数据集进行单变量多步预测。主要内容如下:

  • 如何为模型准备数据集;
  • 如何开发指标、划分数据集、评估预测模型;
  • 如何开发和评估和比较朴素模型的性能;


如何开发用于多时间步家庭用电量预测的朴素模型

1. 数据处理

1.1 异常值处理

上一篇文章中得知,数据集中有大量标记为 的异常值,这就需要把这些异常值处理为np.nan的浮点类型,提高数据处理速度。代码如下:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 函数的参数在上篇文章中已经介绍,此处不再赘述。
dataset = pd.read_csv('household_power_consumption.txt', sep=';', header=0, 
                      low_memory=False, infer_datetime_format=True, engine='c',
                      parse_dates={'datetime':[0,1]}, index_col=['datetime'])
                      
dataset.replace('?', np.nan, inplace=True) # 替换异常值
values = dataset.values.astype('float32') # 统一数据类型为float类型,提高精度和速度

1.2 缺失值处理

刚才我们把异常值替换成了缺失值(nan),这一步我们填充这些缺失的值。一个非常简单的方法是复制前一天同一时刻的采样值。使用自定义的 fill_missing() 函数来实现:

def fill_missing(values):
    '''
    该函数实现缺失值填充
    思路:将前一天同一时刻的采样值用来填充缺失值
    '''
    one_day = 60 * 24
    for row in range(values.shape[0]):# 行循环
        for col in range(values.shape[1]): # 列循环
            if np.isnan(values[row, col]):
                values[row, col] = values[row - one_day, col]

fill_missing(dataset.values) # 填充缺失值

处理完成后,添加新列并将处理完成的数据保存,计算公式已经在前一篇文章中解释过,此处不赘述。代码如下:

# 添加剩余用电量的列
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6])
dataset.to_csv('household_power_consumption.csv')

至此,数据处理完成。


2. 模型评估

本节介绍如何开发和评估家庭用电量数据集的预测模型。本节主要包括以下四部分内容:

  • 问题建模
  • 评价指标
  • 训练集测试集划分
  • 前向验证

2.1 问题建模

有许多方法探索家庭用电量数据集。本文使用这些数据来探索一个具体的问题:用最近一段时间的数据来预测未来一周的用电量是多少。这需要建立一个预测模型预测未来七天每天的总有功功率。这类问题被称为多步时间序列预测问题。利用多个输入变量的模型可称为多变量(特征)多步时间序列预测模型。

要达到这样的目的,为了便于处理,先把原数据每分钟的耗电量采样数据重新采样成每日总耗电量。这不是必需的,但是有意义,因为我们关心的是每天的总功率。可以使用 Pandas中的 resample() 函数实现,设置参数 “D” 调用此函数,以允许按日期-时间索引加载数据并按天将数据重新采样分组。然后,可以通过计算每天所有采样值的总和,为这8个变量(特征)创建新的日耗电量数据集。

查看重新采样的结果:

daily_groups = dataset.resample('D')
daily_groups.sum()

时间序列预测12:用电量预测 02 朴素模型多步预测建模
新采样包含的原采样点个数统计:

daily_groups.count()

时间序列预测12:用电量预测 02 朴素模型多步预测建模
重采样:

daily_data = daily_groups.sum()

可以从以上输出信息得知重新采样之后,数据的shape为 (1442, 8)

保存为新的csv文件:

daily_data.to_csv('household_power_consumption_days.csv'

2.2 评价指标

预测输出为七个值组成的向量,每个值代表预测的未来一周中每一天的耗电量。多步预测问题通常是对每一个预测时间步分别进行评价。有如下建议:

  • 每隔一定周期(例如预测1天、预测3天的模型)对技能进行评价;
  • 根据预测日期长短的不同对比模型(例如,擅长预测1天的模型与擅长预测5天的模型);

总功率的单位是千瓦,应该使用具有统一单位的误差度量方法。**均方根误差(RMSE)绝对平均误差(MAE)**都符合这个单位统一的要求,本文采用更常用的 RMSE。与MAE不同,RMSE对预测错误的惩罚更大。此问题的性能指标是从第1天到第7天的每天的的RMSE。用分数来评估模型的表现以帮助模型的选择,是便捷有效的方法。一个可用的评估分数是所有单日的 RMSE。自定义的 evaluate forecast() 函数可以实现这个功能。


2.3 训练集和测试集划分

使用前三年的数据训练预测模型,最后一年的数据评估模型。将数据按照标准周(星期天开始到星期六结束)进行划分。这对模型的选定是一种有效的方法,可以预测未来一周的耗电量。也有助于建模,模型可用于预测特定的一天(如周三)或整个序列。

以周为单位对数据进行处理,先截取测试数据,剩下的为训练数据。数据的最后一年是2010年,2010年的第一个星期天是1月3日,数据在2010年11月26日结束,最近的最后一个星期六是11月20日。一共46周的测试数据。下面提供测试数据集的第一行和最后一行每日数据以供确认。
时间序列预测12:用电量预测 02 朴素模型多步预测建模
由之前的统计可知(上文中的图),每日耗电量数据从2006年12月16日开始。数据集中的第一个周日是12月17日,这是第二行数据。将数据组织成标准周,可提供159个完整的标准周用于训练预测模型。
时间序列预测12:用电量预测 02 朴素模型多步预测建模
自定义的 split_dataset() 函数将每天的数据分成训练集和测试集,并将它们组织成标准周。特定的行偏移量用于使用数据集的知识拆分数据。然后使用NumPy的 split() 函数将分割的数据集组织为每周数据。此处注意 split() 函数的功能是等间距划分,如果数据能整除会报错。


2.4 前向验证

模型使用前向验证进行评估。流程为:先提供给模型一周的数据,预测第二周;接着给第二周的实际数据,用前两周的数据,预测第三周;然后给第三周的实际数据,使用前三周的数据,预测第四周,以此类推…在下面用输入数据和输出/预测数据来演示:

Input,  Predict
[Week1]  Week2
[Week1 + Week2]  Week3
[Week1 + Week2 + Week3]  Week4
...

在此数据集上评估预测模型的前向验证方法通过自定义的 evaluate_model() 函数实现。model_func 参数为函数名。提供的这个函数用来定义模型,拟合模型,并预测一周的用电量。然后,evaluate_model() 函数根据测试数据集对模型所做的预测进行求值。


2.5 朴素预测模型(简单模型)

在任何新的预测问题上,测试朴素预测模型是很重要的。朴素模型的预测结果定量地显示了预测问题的难度,并提供了一个标准性能(baseline),通过它可以评估更复杂的预测方法。本节比较三种针对家庭功率预测问题的简单预测方法:

  • 每日持久性预测;
  • 每周持久性预测;
  • 一年前每周的持久性预测;

2.5.1 每日持久性预测

该模型取预测期前最后一天(如周六)的有功功率,作为预测期内(周日至周六)每天的有功功率值。daily_persistence() 函数实现了该策略。

2.5.2 每周持久性预测

该模型预测取前一周的整个时间作为下一周的预测。这是基于下周将与本周非常相似的想法。weekly_persistence() 函数实现了该策略。

2.5.3 一年前每周的持久性预测

类似于利用上周预测下周的想法,即根据下周将与一年前的同一周相似的想法,使用52周前的观察周作为预测。year_persistence() 函数实现了该策略。


3. 完整代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
import sklearn.metrics as skm

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False

def split_dataset(data):
    '''
    该函数实现以周为单位切分训练数据和测试数据
    '''
    # data为按天的耗电量统计数据,shape为(1442, 8)
    # 测试集取最后一年的46周(322天)数据,剩下的159周(1113天)数据为训练集,以下的切片实现此功能。
    train, test = data[1:-328], data[-328:-6]
    train = np.array(np.split(train, len(train)/7)) # 将数据划分为按周为单位的数据
    test = np.array(np.split(test, len(test)/7))
    return train, test

def evaluate_forecasts(actual, predicted):
    '''
    该函数实现根据预期值评估一个或多个周预测损失
    思路:统计所有单日预测的 RMSE
    '''
    scores = list()
    for i in range(actual.shape[1]):
        mse = skm.mean_squared_error(actual[:, i], predicted[:, i])
        rmse = math.sqrt(mse)
        scores.append(rmse)
    
    s = 0 # 计算总的 RMSE
    for row in range(actual.shape[0]):
        for col in range(actual.shape[1]):
            s += (actual[row, col] - predicted[row, col]) ** 2
    score = math.sqrt(s / (actual.shape[0] * actual.shape[1]))
    print('actual.shape[0]:{}, actual.shape[1]:{}'.format(actual.shape[0], actual.shape[1]))
    return score, scores

def summarize_scores(name, score, scores):
    s_scores = ', '.join(['%.1f' % s for s in scores])
    print('%s: [%.3f] %s\n' % (name, score, s_scores))

def evaluate_model(model_func, train, test):
    '''
    该函数实现评估单个模型
    '''
    history = [x for x in train] # # 以周为单位的数据列表
    predictions = [] # 每周的前项预测值
    for i in range(len(test)):
        yhat_sequence = model_func(history) # 预测每周的耗电量
        predictions.append(yhat_sequence)
        history.append(test[i, :]) # 将测试数据中的采样值添加到history列表,以便预测下周的用电量
    predictions = np.array(predictions)
    score, scores = evaluate_forecasts(test[:, :, 0], predictions) # 评估一周中每天的预测损失
    return score, scores

def daily_persistence(history):
    last_week = history[-1] # 获取之前一周七天的总有功功率
    value = last_week[-1, 0] # 获取前一周最后一天的总有功功率
    forecast = [value for _ in range(7)] # 准备7天预测
    return forecast

def weekly_persistence(history):
    last_week = history[-1] # 将之前一周的数据作为预测数据
    return last_week[:, 0]

def week_one_year_ago_persistence(history):
    last_week = history[-52] # 将去年同一周的数据预测数据
    return last_week[:, 0]


def model_predict_plot(dataset, days):
    train, test = split_dataset(dataset.values)
    #定义要评估的模型的名称和函数
    models = dict()
    models['daily'] = daily_persistence
    models['weekly'] = weekly_persistence
    models['week-oya'] = week_one_year_ago_persistence
    
    plt.figure(figsize=(8,6), dpi=150)
    for name, func in models.items():
        score, scores = evaluate_model(func, train, test)
        summarize_scores(name, score, scores)
        plt.plot(days, scores, marker='o', label=name)
    plt.grid(linestyle='--', alpha=0.5)
    plt.ylabel(r'$RMSE$', size=15)
    plt.title('三种模型预测结果比较', color='blue', size=20)
    plt.legend()
    plt.show()

if __name__ == '__main__':
    dataset = pd.read_csv('household_power_consumption_days.csv', header=0, 
                       infer_datetime_format=True, engine='c',
                       parse_dates=['datetime'], index_col=['datetime'])
    days = ['sun', 'mon', 'tue', 'wed', 'thr', 'fri', 'sat']
    model_predict_plot(dataset, days)

运行该示例首先打印每个模型的总分数和每日分数。我们可以看到,周策略的表现优于日策略,一年前的一周数据预测当前周的策略表现最好。我们可以在每个模型的总体RMSE得分和每个预测日的得分中看到这一点。一个例外是第一天(星期天)的预测错误,在这一天,每日持久性模型的性能似乎优于两周策略。我们可以使用一年前的一周数据预测当前周的策略,以465.294千瓦的总RMSE作为性能基线,来评估其它复杂模型在该数据集上的表现。

actual.shape[0]:46, actual.shape[1]:7
daily: [511.886] 452.9, 596.4, 532.1, 490.5, 534.3, 481.5, 482.0

actual.shape[0]:46, actual.shape[1]:7
weekly: [469.389] 567.6, 500.3, 411.2, 466.1, 471.9, 358.3, 482.0

actual.shape[0]:46, actual.shape[1]:7
week-oya: [465.294] 550.0, 446.7, 398.6, 487.0, 459.3, 313.5, 555.1

时间序列预测12:用电量预测 02 朴素模型多步预测建模


下一篇文章介绍ARIMA模型实现用电量多步预测。
时间序列预测12:用电量预测 02 朴素模型多步预测建模


参考:
https://machinelearningmastery.com/naive-methods-for-forecasting-household-electricity-consumption/
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.resample.html?highlight=resample#pandas.DataFrame.resample
https://www.statsmodels.org/stable/generated/statsmodels.graphics.tsaplots.plot_acf.html?highlight=plot_acf
https://matplotlib.org/index.html