原文: https://machinelearningmastery.com/naive-methods-for-forecasting-household-electricity-consumption/
鉴于智能电表的兴起以及太阳能电池板等发电技术的广泛采用,可提供大量的用电数据。
该数据代表了多变量时间序列的功率相关变量,而这些变量又可用于建模甚至预测未来的电力消耗。
在本教程中,您将了解如何为“家庭功耗”数据集开发测试工具,并评估三种朴素的预测策略,为更复杂的算法提供基线。
完成本教程后,您将了解:
- 如何加载,准备和下采样家庭功耗数据集,为开发模型做好准备。
- 如何为强大的测试工具开发度量标准,数据集拆分和前进验证元素,以评估预测模型。
- 如何开发,评估和比较一套朴素的持久性预测方法的表现。
让我们开始吧。
如何制定和评估用于预测家庭用电量的朴素预测方法 照片来自 Philippe Put ,保留一些权利。
本教程分为四个部分;他们是:
- 问题描述
- 加载并准备数据集
- 模型评估
- 朴素的预测模型
'家庭用电量'数据集是一个多变量时间序列数据集,描述了四年内单个家庭的用电量。
该数据是在 2006 年 12 月至 2010 年 11 月之间收集的,并且每分钟收集家庭内的能耗观察结果。
它是一个多变量系列,由七个变量组成(除日期和时间外);他们是:
- global_active_power :家庭消耗的总有功功率(千瓦)。
- global_reactive_power :家庭消耗的总无功功率(千瓦)。
- 电压:平均电压(伏特)。
- global_intensity :平均电流强度(安培)。
- sub_metering_1 :厨房的有功电能(瓦特小时的有功电能)。
- sub_metering_2 :用于洗衣的有功能量(瓦特小时的有功电能)。
- sub_metering_3 :气候控制系统的有功电能(瓦特小时的有功电能)。
有功和无功电能参考交流电的技术细节。
可以通过从总活动能量中减去三个定义的子计量变量的总和来创建第四个子计量变量,如下所示:
sub_metering_remainder = (global_active_power * 1000 / 60) - (sub_metering_1 + sub_metering_2 + sub_metering_3)
数据集可以从 UCI 机器学习库下载为单个 20 兆字节的.zip 文件:
下载数据集并将其解压缩到当前工作目录中。您现在将拥有大约 127 兆字节的文件“household_power_consumption.txt
”并包含所有观察结果。
我们可以使用read_csv()
函数来加载数据,并将前两列合并到一个日期时间列中,我们可以将其用作索引。
# load all data
dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime'])
接下来,我们可以用'_ 标记所有缺失值?_ '具有NaN
值的字符,这是一个浮点数。
这将允许我们将数据作为一个浮点值数组而不是混合类型(效率较低)。
# mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32')
我们还需要填写缺失值,因为它们已被标记。
一种非常简单的方法是从前一天的同一时间复制观察。我们可以在一个名为fill_missing()
的函数中实现它,该函数将从 24 小时前获取数据的 NumPy 数组并复制值。
# fill missing values with a value at the same time one day ago
def fill_missing(values):
one_day = 60 * 24
for row in range(values.shape[0]):
for col in range(values.shape[1]):
if isnan(values[row, col]):
values[row, col] = values[row - one_day, col]
我们可以将此函数直接应用于 DataFrame 中的数据。
# fill missing
fill_missing(dataset.values)
现在,我们可以使用上一节中的计算创建一个包含剩余子计量的新列。
# add a column for for the remainder of sub metering
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6])
我们现在可以将清理后的数据集版本保存到新文件中;在这种情况下,我们只需将文件扩展名更改为.csv,并将数据集保存为“household_power_consumption.csv
”。
# save updated dataset
dataset.to_csv('household_power_consumption.csv')
将所有这些结合在一起,下面列出了加载,清理和保存数据集的完整示例。
# load and clean-up data
from numpy import nan
from numpy import isnan
from pandas import read_csv
from pandas import to_numeric
# fill missing values with a value at the same time one day ago
def fill_missing(values):
one_day = 60 * 24
for row in range(values.shape[0]):
for col in range(values.shape[1]):
if isnan(values[row, col]):
values[row, col] = values[row - one_day, col]
# load all data
dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime'])
# mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32')
# fill missing
fill_missing(dataset.values)
# add a column for for the remainder of sub metering
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6])
# save updated dataset
dataset.to_csv('household_power_consumption.csv')
运行该示例将创建新文件'household_power_consumption.csv
',我们可以将其用作建模项目的起点。
在本节中,我们将考虑如何开发和评估家庭电力数据集的预测模型。
本节分为四个部分;他们是:
- 问题框架
- 评估指标
- 训练和测试集
- 前瞻性验证
有许多方法可以利用和探索家庭用电量数据集。
在本教程中,我们将使用这些数据来探索一个非常具体的问题;那是:
鉴于最近的耗电量,未来一周的预期耗电量是多少?
这要求预测模型预测未来七天每天的总有功功率。
从技术上讲,考虑到多个预测步骤,这个问题的框架被称为多步骤时间序列预测问题。利用多个输入变量的模型可以称为多变量多步时间序列预测模型。
这种类型的模型在规划支出方面可能有助于家庭。在供应方面,它也可能有助于规划特定家庭的电力需求。
数据集的这种框架还表明,将每分钟功耗的观察结果下采样到每日总数是有用的。这不是必需的,但考虑到我们对每天的总功率感兴趣,这是有道理的。
我们可以使用 pandas DataFrame 上的 resample()函数轻松实现这一点。使用参数'D
'调用此函数允许按日期时间索引的加载数据按天分组(查看所有偏移别名)。然后,我们可以计算每天所有观测值的总和,并为八个变量中的每一个创建每日耗电量数据的新数据集。
下面列出了完整的示例。
# resample minute data to total for each day
from pandas import read_csv
# load the new file
dataset = read_csv('household_power_consumption.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# resample data to daily
daily_groups = dataset.resample('D')
daily_data = daily_groups.sum()
# summarize
print(daily_data.shape)
print(daily_data.head())
# save
daily_data.to_csv('household_power_consumption_days.csv')
运行该示例将创建一个新的每日总功耗数据集,并将结果保存到名为“household_power_consumption_days.csv
”的单独文件中。
我们可以将其用作数据集,用于拟合和评估所选问题框架的预测模型。
预测将包含七个值,一个用于一周中的每一天。
多步预测问题通常分别评估每个预测时间步长。这有助于以下几个原因:
- 在特定提前期评论技能(例如+1 天 vs +3 天)。
- 在不同的交付时间基于他们的技能对比模型(例如,在+1 天的模型和在日期+5 的模型良好的模型)。
总功率的单位是千瓦,并且具有也在相同单位的误差度量将是有用的。均方根误差(RMSE)和平均绝对误差(MAE)都符合这个要求,尽管 RMSE 更常用,将在本教程中采用。与 MAE 不同,RMSE 更能预测预测误差。
此问题的表现指标是从第 1 天到第 7 天的每个提前期的 RMSE。
作为捷径,使用单个分数总结模型的表现以帮助模型选择可能是有用的。
可以使用的一个可能的分数是所有预测天数的 RMSE。
下面的函数evaluate_forecasts()
将实现此行为并基于多个七天预测返回模型的表现。
# evaluate one or more weekly forecasts against expected values
def evaluate_forecasts(actual, predicted):
scores = list()
# calculate an RMSE score for each day
for i in range(actual.shape[1]):
# calculate mse
mse = mean_squared_error(actual[:, i], predicted[:, i])
# calculate rmse
rmse = sqrt(mse)
# store
scores.append(rmse)
# calculate overall RMSE
s = 0
for row in range(actual.shape[0]):
for col in range(actual.shape[1]):
s += (actual[row, col] - predicted[row, col])**2
score = sqrt(s / (actual.shape[0] * actual.shape[1]))
return score, scores
运行该函数将首先返回整个 RMSE,无论白天,然后每天返回一系列 RMSE 分数。
我们将使用前三年的数据来训练预测模型和评估模型的最后一年。
给定数据集中的数据将分为标准周。这些是从周日开始到周六结束的周。
这是使用所选模型框架的现实且有用的方法,其中可以预测未来一周的功耗。它也有助于建模,其中模型可用于预测特定日期(例如星期三)或整个序列。
我们将数据拆分为标准周,从测试数据集向后工作。
数据的最后一年是 2010 年,2010 年的第一个星期日是 1 月 3 日。数据于 2010 年 11 月中旬结束,数据中最接近的最后一个星期六是 11 月 20 日。这给出了 46 周的测试数据。
下面提供了测试数据集的每日数据的第一行和最后一行以供确认。
2010-01-03,2083.4539999999984,191.61000000000055,350992.12000000034,8703.600000000033,3842.0,4920.0,10074.0,15888.233355799992
...
2010-11-20,2197.006000000004,153.76800000000028,346475.9999999998,9320.20000000002,4367.0,2947.0,11433.0,17869.76663959999
每日数据从 2006 年底开始。
数据集中的第一个星期日是 12 月 17 日,这是第二行数据。
将数据组织到标准周内为训练预测模型提供了 159 个完整的标准周。
2006-12-17,3390.46,226.0059999999994,345725.32000000024,14398.59999999998,2033.0,4187.0,13341.0,36946.66673200004
...
2010-01-02,1309.2679999999998,199.54600000000016,352332.8399999997,5489.7999999999865,801.0,298.0,6425.0,14297.133406600002
下面的函数split_dataset()
将每日数据拆分为训练集和测试集,并将每个数据组织成标准周。
使用特定行偏移来使用数据集的知识来分割数据。然后使用 NumPy split()函数将分割数据集组织成每周数据。
# split a univariate dataset into train/test sets
def split_dataset(data):
# split into standard weeks
train, test = data[1:-328], data[-328:-6]
# restructure into windows of weekly data
train = array(split(train, len(train)/7))
test = array(split(test, len(test)/7))
return train, test
我们可以通过加载每日数据集并打印训练和测试集的第一行和最后一行数据来测试此功能,以确认它们符合上述预期。
完整的代码示例如下所示。
# split into standard weeks
from numpy import split
from numpy import array
from pandas import read_csv
# split a univariate dataset into train/test sets
def split_dataset(data):
# split into standard weeks
train, test = data[1:-328], data[-328:-6]
# restructure into windows of weekly data
train = array(split(train, len(train)/7))
test = array(split(test, len(test)/7))
return train, test
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
train, test = split_dataset(dataset.values)
# validate train data
print(train.shape)
print(train[0, 0, 0], train[-1, -1, 0])
# validate test
print(test.shape)
print(test[0, 0, 0], test[-1, -1, 0])
运行该示例表明,训练数据集确实有 159 周的数据,而测试数据集有 46 周。
我们可以看到,第一行和最后一行的训练和测试数据集的总有效功率与我们定义为每组标准周界限的特定日期的数据相匹配。
(159, 7, 8)
3390.46 1309.2679999999998
(46, 7, 8)
2083.4539999999984 2197.006000000004
将使用称为前进验证的方案评估模型。
这是需要模型进行一周预测的地方,然后该模型的实际数据可用于模型,以便它可以用作在随后一周做出预测的基础。这对于如何在实践中使用模型以及对模型有益而使其能够利用最佳可用数据都是现实的。
我们可以通过分离输入数据和输出/预测数据来证明这一点。
Input, Predict
[Week1] Week2
[Week1 + Week2] Week3
[Week1 + Week2 + Week3] Week4
...
评估此数据集上的预测模型的前瞻性验证方法在下面实现,命名为 evaluate_model()。
为模型提供函数的名称作为参数“model_func
”。该功能负责定义模型,使模型适合训练数据,并进行一周的预测。
然后使用先前定义的evaluate_forecasts()
函数,针对测试数据集评估模型所做的预测。
# evaluate a single model
def evaluate_model(model_func, train, test):
# history is a list of weekly data
history = [x for x in train]
# walk-forward validation over each week
predictions = list()
for i in range(len(test)):
# predict the week
yhat_sequence = model_func(history)
# store the predictions
predictions.append(yhat_sequence)
# get real observation and add to history for predicting the next week
history.append(test[i, :])
predictions = array(predictions)
# evaluate predictions days for each week
score, scores = evaluate_forecasts(test[:, :, 0], predictions)
return score, scores
一旦我们对模型进行评估,我们就可以总结表现。
以下名为summarize_scores()
的函数将模型的表现显示为单行,以便与其他模型进行比较。
# summarize scores
def summarize_scores(name, score, scores):
s_scores = ', '.join(['%.1f' % s for s in scores])
print('%s: [%.3f] %s' % (name, score, s_scores))
我们现在已经开始评估数据集上的预测模型的所有元素。
在任何新的预测问题上测试朴素的预测模型是很重要的。
来自幼稚模型的结果提供了预测问题有多困难的定量概念,并提供了可以评估更复杂的预测方法的基准表现。
在本节中,我们将开发和比较三种用于家庭功率预测问题的朴素预测方法;他们是:
- 每日持续性预测。
- 每周持续预测。
- 每周一年的持续预测。
我们将开发的第一个朴素的预测是每日持久性模型。
该模型从预测期间(例如星期六)之前的最后一天获取有效功率,并将其用作预测期间(星期日至星期六)中每天的功率值。
下面的daily_persistence()
函数实现了每日持久性预测策略。
# daily persistence model
def daily_persistence(history):
# get the data for the prior week
last_week = history[-1]
# get the total active power for the last day
value = last_week[-1, 0]
# prepare 7 day forecast
forecast = [value for _ in range(7)]
return forecast
预测标准周时的另一个好的朴素预测是使用整个前一周作为未来一周的预测。
这是基于下周将与本周非常相似的想法。
下面的weekly_persistence()
函数实现了每周持久性预测策略。
# weekly persistence model
def weekly_persistence(history):
# get the data for the prior week
last_week = history[-1]
return last_week[:, 0]
类似于上周用于预测下周的想法是使用去年同一周预测下周的想法。
也就是说,使用 52 周前的观察周作为预测,基于下周将与一年前的同一周相似的想法。
下面的week_one_year_ago_persistence()
函数实现了一年前的预测策略。
# week one year ago persistence model
def week_one_year_ago_persistence(history):
# get the data for the prior week
last_week = history[-52]
return last_week[:, 0]
我们可以使用上一节中开发的测试工具来比较每个预测策略。
首先,可以加载数据集并将其拆分为训练集和测试集。
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# split into train and test
train, test = split_dataset(dataset.values)
每个策略都可以根据唯一名称存储在字典中。此名称可用于打印和创建乐谱图。
# define the names and functions for the models we wish to evaluate
models = dict()
models['daily'] = daily_persistence
models['weekly'] = weekly_persistence
models['week-oya'] = week_one_year_ago_persistence
然后,我们可以列举每个策略,使用前向验证对其进行评估,打印分数,并将分数添加到线图中以进行视觉比较。
# evaluate each model
days = ['sun', 'mon', 'tue', 'wed', 'thr', 'fri', 'sat']
for name, func in models.items():
# evaluate and get scores
score, scores = evaluate_model(func, train, test)
# summarize scores
summarize_scores('daily persistence', score, scores)
# plot scores
pyplot.plot(days, scores, marker='o', label=name)
将所有这些结合在一起,下面列出了评估三种朴素预测策略的完整示例。
# naive forecast strategies
from math import sqrt
from numpy import split
from numpy import array
from pandas import read_csv
from sklearn.metrics import mean_squared_error
from matplotlib import pyplot
# split a univariate dataset into train/test sets
def split_dataset(data):
# split into standard weeks
train, test = data[1:-328], data[-328:-6]
# restructure into windows of weekly data
train = array(split(train, len(train)/7))
test = array(split(test, len(test)/7))
return train, test
# evaluate one or more weekly forecasts against expected values
def evaluate_forecasts(actual, predicted):
scores = list()
# calculate an RMSE score for each day
for i in range(actual.shape[1]):
# calculate mse
mse = mean_squared_error(actual[:, i], predicted[:, i])
# calculate rmse
rmse = sqrt(mse)
# store
scores.append(rmse)
# calculate overall RMSE
s = 0
for row in range(actual.shape[0]):
for col in range(actual.shape[1]):
s += (actual[row, col] - predicted[row, col])**2
score = sqrt(s / (actual.shape[0] * actual.shape[1]))
return score, scores
# summarize scores
def summarize_scores(name, score, scores):
s_scores = ', '.join(['%.1f' % s for s in scores])
print('%s: [%.3f] %s' % (name, score, s_scores))
# evaluate a single model
def evaluate_model(model_func, train, test):
# history is a list of weekly data
history = [x for x in train]
# walk-forward validation over each week
predictions = list()
for i in range(len(test)):
# predict the week
yhat_sequence = model_func(history)
# store the predictions
predictions.append(yhat_sequence)
# get real observation and add to history for predicting the next week
history.append(test[i, :])
predictions = array(predictions)
# evaluate predictions days for each week
score, scores = evaluate_forecasts(test[:, :, 0], predictions)
return score, scores
# daily persistence model
def daily_persistence(history):
# get the data for the prior week
last_week = history[-1]
# get the total active power for the last day
value = last_week[-1, 0]
# prepare 7 day forecast
forecast = [value for _ in range(7)]
return forecast
# weekly persistence model
def weekly_persistence(history):
# get the data for the prior week
last_week = history[-1]
return last_week[:, 0]
# week one year ago persistence model
def week_one_year_ago_persistence(history):
# get the data for the prior week
last_week = history[-52]
return last_week[:, 0]
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# split into train and test
train, test = split_dataset(dataset.values)
# define the names and functions for the models we wish to evaluate
models = dict()
models['daily'] = daily_persistence
models['weekly'] = weekly_persistence
models['week-oya'] = week_one_year_ago_persistence
# evaluate each model
days = ['sun', 'mon', 'tue', 'wed', 'thr', 'fri', 'sat']
for name, func in models.items():
# evaluate and get scores
score, scores = evaluate_model(func, train, test)
# summarize scores
summarize_scores(name, score, scores)
# plot scores
pyplot.plot(days, scores, marker='o', label=name)
# show plot
pyplot.legend()
pyplot.show()
首先运行该示例打印每个模型的总分和每日分数。
我们可以看到每周策略的表现优于每日策略,而一年前的一周( week-oya )表现稍好一些。
我们可以在每个模型的总体 RMSE 分数和每个预测日的每日分数中看到这一点。一个例外是第一天(星期日)的预测误差,其中似乎每日持久性模型的表现优于两周策略。
我们可以使用周-oya 策略,总体 RMSE 为 465.294 千瓦作为表现的基准,以便更复杂的模型在这个特定的问题框架中被认为是熟练的。
daily: [511.886] 452.9, 596.4, 532.1, 490.5, 534.3, 481.5, 482.0
weekly: [469.389] 567.6, 500.3, 411.2, 466.1, 471.9, 358.3, 482.0
week-oya: [465.294] 550.0, 446.7, 398.6, 487.0, 459.3, 313.5, 555.1
还会创建每日预测错误的折线图。
除了第一天的情况外,我们可以看到每周策略的相同观察模式总体上比日常策略表现更好。
令人惊讶的是(对我来说)一年前的一周比前一周表现更好。我原本预计上周的耗电量会更加相关。
检查同一图表中的所有策略表明可能导致更好表现的策略的可能组合。
线路图比较家庭电力预测的朴素预测策略
本节列出了一些扩展您可能希望探索的教程的想法。
- 额外的朴素战略。提出,开发和评估一种更为朴素的策略,用于预测下周的功耗。
- 朴素合奏策略。制定集合策略,结合三种提议的朴素预测方法的预测。
- 优化的直接持久性模型。在直接持久性模型中测试并找到用于每个预测日的最佳相对前一天(例如-1 或-7)。
如果你探索任何这些扩展,我很想知道。
如果您希望深入了解,本节将提供有关该主题的更多资源。
- pandas.read_csv API
- pandas.DataFrame.resample API
- 重采样偏移别名
- sklearn.metrics.mean_squared_error API
- numpy.split API
在本教程中,您了解了如何为家庭功耗数据集开发测试工具,并评估三种朴素的预测策略,这些策略为更复杂的算法提供基线。
具体来说,你学到了:
- 如何加载,准备和下采样家庭功耗数据集,以便进行建模。
- 如何为强大的测试工具开发度量标准,数据集拆分和前进验证元素,以评估预测模型。
- 如何开发,评估和比较一套朴素的持久性预测方法的表现。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。