一. 精度指标的公式
按指标量纲的不同,将下列11种精度指标分为3类:第①类是零次的相对性指标,第②类是一次的绝对性指标,第③类是二次的绝对性指标。
二. 精度函数和测试代码,代码最后将12种精度指标和7种相关性指标结果整合为一个综合评价结果;每种指标的使用条件和优势劣势写在备注中。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import random
import seaborn as sns
from sklearn import metrics
from statsmodels.tools import eval_measures
from scipy import stats
sns.set_style('darkgrid')
plt.rc('font', size=10)
# ###########---------------set up and plot input data-----------------######################
base_value = 10 # 设置level、trend、season项的基数
steps_day, steps_week = 1, 1
length = [steps_day*20+steps_day, steps_week*20+steps_week, steps_week*20+steps_week] # 代表周、日序列对的长度
weights = []
for i in range(-base_value + 1, 1):
weights.append(0.5 ** i) # 设置y_level项随机序列的权重呈递减指数分布,底数越小,y_level中较小值所占比例越大。
weights = np.array(weights)
# #########################################################--构造乘法周期性时间序列,模拟真实销售;外层是list,内层的每一条序列是series
y_level_actual, y_trend_actual, y_season_actual, y_noise_actual, y_input_mul_actual = [[]] * len(length), [[]] * len(length), [[]] * len(length), [[]] * len(length), [[]] * len(length)
for i in range(0, len(length)):
y_season_actual[i] = np.sqrt(base_value) * np.sin(np.linspace(np.pi / 2, 10 * np.pi, length[i])) # 用正弦函数模拟周期性
y_level_actual[i] = np.array(random.choices(range(0, base_value), weights=weights, k=length[i])) / np.average(abs(y_season_actual[i])) + np.average(abs(y_season_actual[i])) # 用指数权重分布随机数模拟水平项
y_trend_actual[i] = (2 * max(y_season_actual[i]) + np.log(np.linspace(2, 2 ** (base_value / 8), num=length[i])) / np.log(1.1)
+ (min(np.log(np.linspace(2, 2 ** (base_value / 8), num=length[i])) / np.log(1.1)) + max(np.log(np.linspace(2, 2 ** (base_value / 8), num=length[i])) / np.log(1.1)))
/ length[i] * np.linspace(1, length[i], num=length[i])) / 10 * np.average(y_level_actual[i]) # 用对数函数与线性函数的均值模拟趋势性
y_noise_actual[i] = 3*np.random.standard_t(length[i]-1, length[i]) # 假定数据处于理想状态,并使噪音以乘法方式进入模型,则可令噪音在0附近呈学生分布。
y_noise_actual[i][abs(y_noise_actual[i]) < max(y_noise_actual[i])*0.9] = 1 # 保留随机数中的离群值,将非离群值置为1
y_input_mul_actual[i] = (y_level_actual[i] + y_trend_actual[i]) * y_season_actual[i] * y_noise_actual[i] # 假定周期项以乘法方式组成输入数据
print(f'第{
i}条真实序列中水平项的极差:{
max(y_level_actual[i]) - min(y_level_actual[i])},均值:{
np.mean(y_level_actual[i])}')
print(f'第{
i}条真实序列中趋势项的极差:{
max(y_trend_actual[i]) - min(y_trend_actual[i])},均值:{
np.mean(y_trend_actual[i])}')
print(f'第{
i}条真实序列中周期项的极差:{
max(y_season_actual[i]) - min(y_season_actual[i])},均值:{
np.mean(y_season_actual[i])}')
print(f'第{
i}条真实序列中噪音项的极差:{
max(y_noise_actual[i]) - min(y_noise_actual[i])},均值:{
np.mean(y_noise_actual[i])}')
print(f'第{
i}条真实乘法性序列最终极差:{
max(y_input_mul_actual[i]) - min(y_input_mul_actual[i])},均值:{
np.mean(y_input_mul_actual[i])}', '\n')
y_level_actual[i] = pd.Series(y_level_actual[i]).rename('y_level_actual')
y_trend_actual[i] = pd.Series(y_trend_actual[i]).rename('y_trend_actual')
y_season_actual[i] = pd.Series(y_season_actual[i]).rename('y_season_actual')
y_noise_actual[i] = pd.Series(y_noise_actual[i]).rename('y_noise_actual')
y_input_mul_actual[i] = pd.Series(y_input_mul_actual[i]).rename('y_input_mul_actual')
# y_input_mul_actual[i][y_input_mul_actual[i] < 0.011] = 0.011 # 将series中小于0.011的数置为0.011;因为后续regression_accuracy,regression_evaluation会将series中小于0的置为0.01,若此处不将小于0.011的置为0.011,则画出的图可能与后续两个综合评估函数中所使用的序列不一致。
print('第{0}条真实序列的初始生成值:'.format(i))
print(y_input_mul_actual[i], '\n')
##########################################################--构造乘法周期性时间序列,模拟预测销售;外层是list,内层的每一条序列是series
y_level_pred, y_trend_pred, y_season_pred, y_noise_pred, y_input_mul_pred = [[]] * len(length), [[]] * len(length), [[]] * len(length), [[]] * len(length), [[]] * len(length)
for i in range(0, len(length)):
y_season_pred[i] = 1/2 * np.sqrt(base_value) * np.sin(np.linspace(np.pi / 2, 10 * np.pi, length[i])) # 用正弦函数模拟周期性,使预测销售的波动振幅比真实销售小
y_level_pred[i] = np.array(random.choices(range(0, base_value), weights=weights, k=length[i])) / np.average(abs(y_season_pred[i])) + np.average(abs(y_season_pred[i])) # 用指数权重分布随机数模拟水平项,使其相对于真实销售有所偏移
y_trend_pred[i] = (2 * max(y_season_pred[i]) + np.log(np.linspace(2, 2 ** (base_value / 8), num=length[i])) / np.log(1.1)
+ (min(np.log(np.linspace(2, 2 ** (base_value / 8), num=length[i])) / np.log(1.1)) + max(np.log(np.linspace(2, 2 ** (base_value / 8), num=length[i])) / np.log(1.1)))
/ length[i] * np.linspace(1, length[i], num=length[i])) / 10 * np.average(y_level_pred[i]) # 用对数函数与线性函数的均值模拟趋势性
y_noise_pred[i] = np.random.standard_t(length[i]-1, length[i]) # 假定数据处于理想状态,并使噪音以乘法方式进入模型,则可令噪音在0附近呈学生分布;使其比真实销售的噪音小。
y_noise_pred[i][abs(y_noise_pred[i]) < max(y_noise_pred[i])*0.9] = 1 # 保留随机数中的离群值,将非离群值置为1
y_input_mul_pred[i] = (y_level_pred[i] + y_trend_pred[i]) * y_season_pred[i] * y_noise_pred[i] # 假定周期项以乘法方式组成输入数据
print(f'第{
i}条预测序列中水平项的极差:{
max(y_level_pred[i]) - min(y_level_pred[i])},均值:{
np.mean(y_level_pred[i])}')
print(f'第{
i}条预测序列中趋势项的极差:{
max(y_trend_pred[i]) - min(y_trend_pred[i])},均值:{
np.mean(y_trend_pred[i])}')
print(f'第{
i}条预测序列中周期项的极差:{
max(y_season_pred[i]) - min(y_season_pred[i])},均值:{
np.mean(y_season_pred[i])}')
print(f'第{
i}条预测序列中噪音项的极差:{
max(y_noise_pred[i]) - min(y_noise_pred[i])},均值:{
np.mean(y_noise_pred[i])}')
print(f'第{
i}条预测乘法性序列最终极差:{
max(y_input_mul_pred[i]) - min(y_input_mul_pred[i])},均值:{
np.mean(y_input_mul_pred[i])}', '\n')
y_level_pred[i] = pd.Series(y_level_pred[i]).rename('y_level_pred')
y_trend_pred[i] = pd.Series(y_trend_pred[i]).rename('y_trend_pred')
y_season_pred[i] = pd.Series(y_season_pred[i]).rename('y_season_pred')
y_noise_pred[i] = pd.Series(y_noise_pred[i]).rename('y_noise_pred')
y_input_mul_pred[i] = pd.Series(y_input_mul_pred[i]).rename('y_input_mul_pred')
# y_input_mul_pred[i][y_input_mul_pred[i] < 0.011] = 0.011 # 将series中小于0.011的数置为0.011;因为后续regression_accuracy,regression_evaluation会将series中小于0的置为0.01,若此处不将小于0.011的置为0.011,则画出的图可能与后续两个综合评估函数中所使用的序列不一致。
print('第{0}条预测序列的初始生成值:'.format(i))
print(y_input_mul_pred[i], '\n')
for i in range(len(y_input_mul_actual)):
# 绘制真实值和对应的预测值序列
plt.figure('origin {0}'.format(i), figsize=(5,10))
ax1 = plt.subplot(5,1,1)
ax2 = plt.subplot(5,1,2)
ax3 = plt.subplot(5,1,3)
ax4 = plt.subplot(5,1,4)
ax5 = plt.subplot(5,1,5)
y_input_mul_actual[i].plot(ax=ax1, legend=True)
y_level_actual[i].plot(ax=ax2, legend=True)
y_trend_actual[i].plot(ax=ax3, legend=True)
y_season_actual[i].plot(ax=ax4, legend=True)
y_noise_actual[i].plot(ax=ax5, legend=True)
y_input_mul_pred[i].plot(ax=ax1, legend=True)
y_level_pred[i].plot(ax=ax2, legend=True)
y_trend_pred[i].plot(ax=ax3, legend=True)
y_season_pred[i].plot(ax=ax4, legend=True)
y_noise_pred[i].plot(ax=ax5, legend=True)
"""
预测结果评价函数的使用方法:
1. 评价种类、品类群、课别、企业、全体层级的结果时,将企业种类的各对预测金额及理论销售金额传入函数,而不是将各汇总层级的序列对输入函数,避免低层级的偏差信息因正负号不同而消失;
评价单品、品种层级的结果时,将企业单品的各对预测销量及理论销量传入函数。不用单品评价结果去计算种类及更高层级的精度,是因为预测系统算法设计的优化目标是企业种类预测金额,而不是直接优化单品预测销量。
2. 当同一目标有三种序列时:预测2.0、预测1.0、实际销售,应把预测2.0和实际销售、预测1.0和实际销售分别传入函数regression_evaluation,
来评价预测2.0贴近实际销售的程度、预测1.0贴近实际销售的程度。当要评价预测2.0和预测1.0的偏离程度时,应把从regression_evaluation得到的两种值,再传入函数smape得到偏离程度。
因为smape是所有指标里唯一具有无偏性的百分比指标,不是以某一种序列为目标,评价另一种序列趋近该种序列的程度;所以不应把预测2.0和预测1.0传入regression_evaluation直接得到偏离程度。
也不能将预测2.0与预测1.0拼到一起,与理论销售作为一个整体的序列对传入regression_evaluation,因为两种预测结果是由两种不同模型得到的,若拼到一起,
在后续归一化时会相互影响,进而也会影响其后的加权平均,从而不能对每种模型的预测结果得到准确的相互独立的评价结果。
3. 除了最终指标precision是最重要的评价指标外,如果更关心平时的预测结果,参考MAPE更好;如果更关心节假日期间的预测结果,参考RMSE(eval_measures.rmse)更好。
4. 若要评价某个具体的企业种类的预测结果,不能将这个序列对输入regression_evaluation,可以输入分项函数中的任意一个;
若要评价某个种类的预测结果,则将各个企业种类的预测序列和真实序列组成的序列对传入regression_evaluation,对返回结果按序列求平均,得到该种类的预测评价结果;
此时序列对中的y_true根据企业的不同而不同;而不是将该种类的汇总序列对传入regression_evaluation。若要对某个具体的企业单品、某个单品的预测结果进行评价,与4中方法类似。
5. 若要评价某个企业品类群的预测结果,则将该企业品类群下所有企业种类序列对传入regression_evaluation,对返回结果按序列求平均,此时序列对中的y_true是不同的,因为是不同的企业种类的理论销售;
而不是将该品类群的汇总序列对传入regression_evaluation。若要对某个企业课别、某个企业、全体的预测结果进行评价,与5中方法类似;
若要对某个企业品种、某个品种的预测结果进行评价,也与5中方法类似,只是传入regression_evaluation的是由企业单品构成的序列对。
6. 若要评价(优选)不同算法模型或不同中间计算结果对同一企业种类的预测结果,则传入regression_evaluation的序列对中y_true是相同的,因为是同一企业种类的理论销售。
7. 综上,可完成对任一层级、每个层级下任一子集的准确评价。
"""
def dyn_seri_weighted(seri, type=None, w=None, initial=1, r=2, d=1):
"""
传入一维数组seri,可以是series,array,list,tuple;若type='geometric'或'arithmetic',且输入了w,则w不起作用;若不输入权重,则根据seri的长度动态计算基于几何级数或算数级数再作归一化的权重,再做算术平均;
也可人为输入权重做算术平均;若不输入type和w,则进行简单算数平均;因为使用np.dot,则seri索引越小,权重越大;将seri各点与权重相乘再相加,得到一个最终点。
:param seri: 需要进行加权变成一个点的一维数组
:param type: 采用几何级数或算数级数进行加权,或人为指定权重,或默认权重相等,type = 'geometric'或'arithmetic'或None;若type='geometric'或'arithmetic',且输入了w,则w不起作用。
:param w: 一维的权重系数,可以是series,array,list,tuple;若手动输入,其长度必须和一维数组seri(即序列点数)相等
:param r: 指定几何级数分母的公比
:param d: 指定算数级数分母的公差
:param initial: 指定算数级数分母的初始值
:return: seri各点与权重w相乘再相加,返回的一个加权后的最终点
"""
if type not in ['geometric', 'arithmetic', None]:
raise Exception('type must be one of geometric, arithmetic or None')
if type is not None:
w = list()
if type == 'geometric':
for i in range(len(seri)):
w.append(initial * (1 / r) ** i) # 生成首项是initial,公比是(1/r)的几何级数作权重
else:
for i in range(len(seri)):
w.append(1 / (initial + d * i)) # 生成首项是initial,公差是d的算术级数,再做倒数作为权重
w = np.array(w) / sum(w)
elif (type is None) and (w is None):
w = np.ones(len(seri)) / sum(np.ones(len(seri))) # 生成均等权重
elif (type is None) and (w is not None) and (len(w) == len(seri)):
w = np.array(w) / sum(w) # 自定义权重
else:
raise Exception('手动输入的权重长度必须和一维数组长度(即序列点数)相等')
if abs(sum(w)-1) > 0.001:
raise Exception('weights are not useable')
return np.dot(np.array(seri), w)
def rmse(y_true, y_pred):
return np.sqrt(((y_true - y_pred) ** 2).mean())
# y_true, y_pred无限制条件
def emlae(y_true, y_pred):
"""
:param y_true: 一条真实值序列,array,series,list,tuple均可,长度要与预测值长度相等
:param y_pred: 一条预测值序列,array,series,list,tuple均可,长度要与真实值长度相等
:return: EMLAE,将残差离群值压缩后的一次绝对性指标,对残差的离群值不如MAE敏感
"""
n = len(y_true)
y_true = np.array(y_true)
y_pred = np.array(y_pred)
emlae = np.exp(sum(np.log(abs(y_pred - y_true) + 1)) / n) - 1
return emlae
# y_true != 0
def mape(y_true, y_pred):
"""
:param y_true: 一条真实值序列,array,series,list,tuple均可,长度要与预测值长度相等,且不能为0
:param y_pred: 一条预测值序列,array,series,list,tuple均可,长度要与真实值长度相等
:return: MAPE,零次的相对性指标,小数,代表预测值偏离真实值的平均程度;若>1,表示预测值偏离真实值的平均程度超过真实值的1倍,若<1,表示预测值偏离真实值的平均程度小于真实值的1倍
"""
n = len(y_true)
y_true = np.array(y_true)
y_pred = np.array(y_pred)
mape = sum(abs((y_pred - y_true) / y_true)) / n
return mape
# y_true + y_pred != 0
def smape(y_true, y_pred):
"""
:param y_true: 一条真实值序列,array,series,list,tuple均可,长度要与预测值长度相等,与对应预测值不能同时为0
:param y_pred: 一条预测值序列,array,series,list,tuple均可,长度要与真实值长度相等,与对应真实值不能同时为0
:return: SMAPE,零次的相对性指标,小数,代表预测值偏离真实值的平均程度;不以某类特定的量为基准分母,而是将两类值均作为分母,第一具有对称性,
第二不会因某类量数值的大小而影响评价结果,所以受离群值影响也比MAPE小;若>1,表示预测值偏离真实值的平均程度超过真实值与预测值均值的1倍,若<1,表示预测值偏离真实值的平均程度小于真实值与预测值均值的1倍。
"""
n = len(y_true)
y_true = np.array(y_true)
y_pred = np.array(y_pred)
smape = sum(abs(2 * (y_pred - y_true) / (y_pred + y_true))) / n
return smape
# y_true, y_pred无限制条件
def male(y_true, y_pred):
"""
param:
Y:原始序列,array,series,list,tuple均可
y:拟合序列,array,series,list,tuple均可
return:
对数MAE值
"""
y_true, y_pred = np.array(y_true), np.array(y_pred)
y_true[y_true < 0] = 0
y_pred[y_pred == -1] = -0.99
male = sum(abs(np.log(abs(y_true+1)) - np.log(abs(y_pred+1)))) / len(y_true)
return male
def regression_accuracy(y_true, y_pred):
"""
:param y_true: 若干条真实序列组成的一个二维list或array或series,其中的每条真实序列必须是带索引的series,为了能对>0的数值的索引取交集;并与y_pred中的预测序列按顺序一一对应
:param y_pred: 若干条预测序列组成的一个二维list或array或series,其中的每条预测序列必须是带索引的series,为了能对>0的数值的索引取交集;并与y_true中的真实序列按顺序一一对应
:return: 精度指标,按顺序分别是:最终精度指标,MAPE, SMAPE, RMSPE, MTD_p2, EMLAE, MAE, RMSE, MedAE, MTD_p1, MSE, MSLE
测试几种常用的评价序列对的精度指标
原则:1.带平方项的指标会放大在正负1之外的残差的影响,而压缩在正负1之内的残差的影响,由于各指标越接近零越好,则会惩罚正负1之外的残差,偏离正负1越远,越受到惩罚;而奖励正负1之内的残差。
2.作对数变换的指标会降低离群值的影响,相对于不带对数项的指标,会惩罚非离群值。因为在(1,+∞)区间内,y=lnx的一阶导数比y=x的一阶导数小,则y=lnx比y=x递增慢。
3.ln(1/x)+len(x)=0,即对一个数(如x)取对数,与其倒数(1/x)取对数,互为相反数;当x>0,ln(1/x)+x-1≥0,lnx+(1/x)-1≥0,可由求导证明;在(0,4]的区间内,lnx的增长速度快于x**(1/2),在[4,+∞)区间内,lnx的增长速度慢于x**(1/2),可由求导证明。
"""
MAPE, SMAPE, RMSPE, MTD_p2 = [], [], [], [] # 零次的相对性指标
EMLAE, MALE, MAE, RMSE, MedAE, MTD_p1 = [], [], [], [], [], [] # 一次的绝对性指标
MSE, MSLE = [], [] # 二次的绝对性指标
y_true_trun, y_pred_trun = [], []
for i in range(len(y_true)):
# 为了统一下列12个精度指标的条件,在y_true和y_pred的序列对中,取大于0的对应点,即排除≤0的对应点;但不应取>0,可以取>0.01,否则若序列中存在大于0但非常接近0的数做分母,可能产生很大的值,不利于得到有效可用的精度值
judge = (y_true[i] > 0.01) & (y_pred[i] > 0.01)
if sum(judge):
y_true_trun.append(y_true[i][judge])
y_pred_trun.append(y_pred[i][judge])
else: continue
if (len(y_true_trun) != len(y_pred_trun)) or (len(y_true_trun) < 2):
raise Exception('y_true_trun与y_pred_trun中序列条数必须相等且≥2') # 若序列对的数目小于2,则数值变换后的指标均为1
plt.figure('finall input of regression_accuracy')
for i in range(len(y_true_trun)):
ax = plt.subplot(len(y_true_trun), 1, i+1)
xlim = plt.gca().set_xlim(0, length[i]-1) # xlim使图形按x轴上的点数充满横坐标
y_true_trun[i].plot(ax=ax, legend=True)
y_pred_trun[i].plot(ax=ax, legend=True)
print('第{0}组实际输入的序列对:'.format(i))
print(y_true_trun[i], '\n', y_pred_trun[i], '\n')
for i in range(len(y_true_trun)):
# 第一组,零次的相对性指标:
MAPE.append(mape(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true != 0; no bias
SMAPE.append(smape(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true + y_pred != 0; symmetric MAPE, no bias and more general, less susceptible to outliers than MAPE.
RMSPE.append(eval_measures.rmspe(np.array(y_true_trun[i]), np.array(y_pred_trun[i])) / 10) # y_true != 0; susceptible to outliers of deviation ratio, if more, RMSPE will be larger than MAPE.
MTD_p2.append(metrics.mean_tweedie_deviance(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]), power=2)) # y_pred > 0, y_true > 0; less susceptible to outliers than MAPE when y_pred[i] / y_true[i] > 1, nevertheless, more susceptible to outliers than MAPE when y_pred[i] / y_true[i] < 1
# 第二组,一次的绝对性指标:
EMLAE.append(emlae(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; less susceptible to outliers of error than MAE, so this will penalize small deviation and award large deviation relative to MAE.
MALE.append(male(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件;
MAE.append(metrics.mean_absolute_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; this metric has no penalty, no bias
RMSE.append(eval_measures.rmse(np.array(y_true_trun[i]), np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件;susceptible to outliers of error than MAE, so this will penalize large deviation and award small deviation relative to MAE.
MedAE.append(metrics.median_absolute_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; if len(y) is slightly large; won't be affected by outliers completely
MTD_p1.append(metrics.mean_tweedie_deviance(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]), power=1)) # y_pred > 0, y_true ≥ 0; The higher `p` the less weight is given to extreme deviations between true and predicted targets.
# 第三组,二次的绝对性指标:
MSE.append(metrics.mean_squared_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; this metric penalizes a large residual greater than a small residual because of square
MSLE.append(metrics.mean_squared_log_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true≥0, y_pred≥0; this metric penalizes an under-predicted estimate greater than an over-predicted estimate because of logarithm
print('判断前的真实(及预测)序列对数:', len(y_true), ' 判断后的真实(及预测)序列对数:', len(y_true_trun), '\n')
print('原始的评估指标:')
print('第一组,零次的相对性指标:', '\n', 'MAPE:', MAPE, '\n', 'SMAPE:', SMAPE, '\n', 'RMSPE:', RMSPE, '\n', 'MTD_p2:', MTD_p2)
print('第二组,一次的绝对性指标:', '\n', 'EMLAE:', EMLAE, '\n', 'MALE:', MALE, '\n', 'MAE:', MAE, '\n', 'RMSE:', RMSE, '\n', 'MedAE:', MedAE, '\n', 'MTD_p1:', MTD_p1)
print('第三组,二次的绝对性指标:', '\n', 'MSE:', MSE, '\n', 'MSLE:', MSLE, '\n')
# 将各序列对的若干精度指标整合成各序列对的最终单一评价指标;序列对的数目必须≥2,否则归一化后各指标值均为1。
# 将各精度指标在各自维度内进行数值变换:1.对各指标除以其均值,将任意数量级的指标转化为在1上下波动的数值。
# 2.再对抹平数量级后的指标取幂函数,进一步缩小指标内数值的差距,保留代表优劣的方向性即可;原始指标内数值差异越大,所开次方根数越大,反之越小,可以避免指标间离群值的出现。
# 3.再对list作归一化,将所有结果都转化为(0,1)之间的数,越趋近0越好,代表预测列越趋近真实序列;最终精度presion经过有偏向的加权后,也是(0,1)之间的数值。
MAPE_1 = (MAPE / np.mean(MAPE)) / sum(MAPE / np.mean(MAPE))
SMAPE_1 = (SMAPE / np.mean(SMAPE)) / sum(SMAPE / np.mean(SMAPE))
RMSPE_1 = (RMSPE / np.mean(RMSPE)) / sum(RMSPE / np.mean(RMSPE))
MTD_p2_1 = np.sqrt(MTD_p2 / np.mean(MTD_p2)) / sum(np.sqrt(MTD_p2 / np.mean(MTD_p2)))
EMLAE_1 = np.sqrt(EMLAE / np.mean(EMLAE)) / sum(np.sqrt(EMLAE / np.mean(EMLAE)))
MALE_1 = (MALE / np.mean(MALE)) / sum(MALE / np.mean(MALE))
MAE_1 = np.sqrt(MAE / np.mean(MAE)) / sum(np.sqrt(MAE / np.mean(MAE)))
RMSE_1 = np.sqrt(RMSE / np.mean(RMSE)) / sum(np.sqrt(RMSE / np.mean(RMSE)))
MedAE_1 = np.sqrt(MedAE / np.mean(MedAE)) / sum(np.sqrt(MedAE / np.mean(MedAE)))
MTD_p1_1 = np.sqrt(MTD_p1 / np.mean(MTD_p1)) / sum(np.sqrt(MTD_p1 / np.mean(MTD_p1)))
MSE_1 = (MSE / np.mean(MSE))**(1/4) / sum((MSE / np.mean(MSE))**(1/4))
MSLE_1 = np.sqrt(MSLE / np.mean(MSLE)) / sum(np.sqrt(MSLE / np.mean(MSLE)))
print('数值变换后的评估指标:')
print('第一组,零次的相对性指标:', '\n', 'MAPE:', MAPE_1, '\n', 'SMAPE:', SMAPE_1, '\n', 'RMSPE:', RMSPE_1, '\n', 'MTD_p2:', MTD_p2_1)
print('第二组,一次的绝对性指标:', '\n', 'EMLAE:', EMLAE_1, '\n', 'MALE:', MALE_1, '\n', 'MAE:', MAE_1, '\n', 'RMSE:', RMSE_1, '\n', 'MedAE:', MedAE_1, '\n', 'MTD_p1:', MTD_p1_1)
print('第三组,二次的绝对性指标:', '\n', 'MSE:', MSE_1, '\n', 'MSLE:', MSLE_1, '\n')
precision = []
for i in range(len(y_true_trun)):
# 不用调和平均、几何平均,避免结果向极小值趋近;不用均方根,避免结果向极大值趋近;使用算术平均加权,权重可根据实际需求手动调整。
precision.append(dyn_seri_weighted([MAPE_1[i], SMAPE_1[i], RMSPE_1[i], MTD_p2_1[i],
EMLAE_1[i], MALE_1[i], MAE_1[i], RMSE_1[i], MedAE_1[i], MTD_p1_1[i],
MSE_1[i], MSLE_1[i]], w=[3,2,2,1, 1,1,1,3,1,1, 1,1]))
print('各序列对的最终精度:', '\n', np.array(precision), '\n')
return precision, MAPE, SMAPE, RMSPE, MTD_p2, EMLAE, MALE, MAE, RMSE, MedAE, MTD_p1, MSE, MSLE # 注意返回的各分量精度指标是未归一化前的数值,而最终precision是由各分量精度指标归一化后的数值算出的
def regression_evaluation(y_true, y_pred):
"""
:param y_true: 若干条真实序列组成的一个二维list或array或series,其中的每条真实序列必须是带索引的series,为了能对>0的数值的索引取交集;
并与y_pred中的预测序列按顺序一一对应;y_true是历史上进模型之前的可能经过处理的真实值。
:param y_pred: 若干条预测序列组成的一个二维list或array或series,其中的每条预测序列必须是带索引的series,为了能对>0的数值的索引取交集;
并与y_true中的真实序列按顺序一一对应;y_pred是历史上该模型输出的预测值,或者经过补偿的预测值,总之是最终用于订货的预测值。
:return: 精度指标,按顺序分别是:最终精度指标,MAPE, SMAPE, RMSPE, MTD_p2, EMLAE, MAE, RMSE, MedAE, MTD_p1, MSE, MSLE, VAR
测试几种常用的评价序列对的精度指标
原则:1.带平方项的指标会放大在正负1之外的残差的影响,而压缩在正负1之内的残差的影响,由于各指标越接近零越好,则会惩罚正负1之外的残差,偏离正负1越远,越受到惩罚;而奖励正负1之内的残差。
2.作对数变换的指标会降低离群值的影响,相对于不带对数项的指标,会惩罚非离群值。因为在(1,+∞)区间内,y=lnx的一阶导数比y=x的一阶导数小,则y=lnx比y=x递增慢。
3.ln(1/x)+len(x)=0,即对一个数(如x)取对数,与其倒数(1/x)取对数,互为相反数;当x>0,ln(1/x)+x-1≥0,lnx+(1/x)-1≥0,可由求导证明;在(0,4]的区间内,lnx的增长速度快于x**(1/2),在[4,+∞)区间内,lnx的增长速度慢于x**(1/2),可由求导证明。
"""
MAPE, SMAPE, RMSPE, MTD_p2 = [], [], [], [] # 零次的相对性指标
EMLAE, MALE, MAE, RMSE, MedAE, MTD_p1 = [], [], [], [], [], [] # 一次的绝对性指标
MSE, MSLE = [], [] # 二次的绝对性指标
VAR, R2, PR, SR, KT, WT, MGC = [], [], [], [], [], [], [] # 相关性指标
y_true_trun, y_pred_trun = [], []
for i in range(len(y_true)):
# 为了统一下列精度指标的条件,在y_true和y_pred的序列对中,取大于0的对应点,即排除≤0的对应点;但不应取>0,可以取>0.01,否则若序列中存在大于0但非常接近0的数做分母,可能产生很大的值,不利于得到有效可用的精度值
judge = (y_true[i] > 0.01) & (y_pred[i] > 0.01)
if sum(judge):
y_true_trun.append(y_true[i][judge])
y_pred_trun.append(y_pred[i][judge])
else: continue
if (len(y_true_trun) != len(y_pred_trun)) or (len(y_true_trun) < 2):
raise Exception('y_true_trun与y_pred_trun中序列条数必须相等且≥2') # 若序列对的数目小于2,则数值变换后的指标均为1
plt.figure('finall inputs of first three groups of accuracy functions')
for i in range(len(y_true_trun)):
# 前三组精度函数使用如图所示形态的数据作为输入值
ax = plt.subplot(len(y_true_trun), 1, i+1)
xlim = plt.gca().set_xlim(0, length[i]-1) # xlim使图形按x轴上的点数充满横坐标
y_true_trun[i].plot(ax=ax, legend=True)
y_pred_trun[i].plot(ax=ax, legend=True)
print('第{0}组实际输入的序列对:'.format(i))
print(y_true_trun[i], '\n', y_pred_trun[i], '\n')
for i in range(len(y_true_trun)):
# 第四组相关性函数使用如图所示形态的数据作为输入值。此for循环不能与上一个for循环合并,否则会错误调用plt。
plt.figure('the 4th group of correlation fuctions use following scatters as {0}th inputs'.format(i))
plt.scatter(y=y_true_trun[i], x=y_pred_trun[i])
plt.xlabel('y_pred_trun[{0}]'.format(i))
plt.ylabel('y_true_trun[{0}]'.format(i))
for i in range(len(y_true_trun)):
if (len(y_true_trun[i]) < 5) or (len(y_pred_trun[i]) < 5):
raise Exception('实际使用的序列对y_true_trun[{0}]与y_pred_trun[{1}]中,点数过少不具有统计意义,每条序列至少要≥5个点'.format(i, i))
# 第一组,零次的相对性指标:
MAPE.append(mape(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true != 0; no bias
SMAPE.append(smape(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true + y_pred != 0; symmetric MAPE, no bias and more general, less susceptible to outliers than MAPE.
RMSPE.append(eval_measures.rmspe(np.array(y_true_trun[i]), np.array(y_pred_trun[i])) / 10) # y_true != 0; susceptible to outliers of deviation ratio, if more, RMSPE will be larger than MAPE.
MTD_p2.append(metrics.mean_tweedie_deviance(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]), power=2)) # y_pred > 0, y_true > 0; less susceptible to outliers than MAPE when y_pred[i] / y_true[i] > 1, nevertheless, more susceptible to outliers than MAPE when y_pred[i] / y_true[i] < 1
# 第二组,一次的绝对性指标:
EMLAE.append(emlae(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; less susceptible to outliers of error than MAE, so this will penalize small deviation and award large deviation relative to MAE.
MALE.append(male(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件;
MAE.append(metrics.mean_absolute_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; this metric has no penalty, no bias
RMSE.append(eval_measures.rmse(np.array(y_true_trun[i]), np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件;susceptible to outliers of error than MAE, so this will penalize large deviation and award small deviation relative to MAE.
MedAE.append(metrics.median_absolute_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; if len(y) is slightly large; won't be affected by outliers completely
MTD_p1.append(metrics.mean_tweedie_deviance(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]), power=1)) # y_pred > 0, y_true ≥ 0; The higher `p` the less weight is given to extreme deviations between true and predicted targets.
# 第三组,二次的绝对性指标:
MSE.append(metrics.mean_squared_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true, y_pred无限制条件; this metric penalizes a large residual greater than a small residual because of square
MSLE.append(metrics.mean_squared_log_error(y_true=np.array(y_true_trun[i]), y_pred=np.array(y_pred_trun[i]))) # y_true≥0, y_pred≥0; this metric penalizes an under-predicted estimate greater than an over-predicted estimate because of logarithm
# 第四组,相关性指标:
VAR.append(metrics.explained_variance_score(y_true=y_true_trun[i], y_pred=y_pred_trun[i])) # y_true, y_pred无限制条件;但explained_variance_score为极大化目标函数,值域为(-∞, 1],越趋近1越好;与其余的极小化目标函数相反,它们的因变量是越小越好。
R2.append(metrics.r2_score(y_true=y_true_trun[i], y_pred=y_pred_trun[i])) # y_true, y_pred的series中,至少要有≥2个点,否则会返回nan;r2_score也为极大化目标函数,值域为(-∞, 1],越趋近1越好;与其余的极小化目标函数相反,它们的因变量是越小越好。
PR.append(stats.pearsonr(x=y_true_trun[i], y=y_pred_trun[i])[0])
SR.append(stats.spearmanr(a=y_true_trun[i], b=y_pred_trun[i])[0])
KT.append(stats.kendalltau(x=y_true_trun[i], y=y_pred_trun[i])[0])
WT.append(stats.weightedtau(x=y_true_trun[i], y=y_pred_trun[i])[0])
MGC.append(stats.multiscale_graphcorr(x=np.array(y_true_trun[i]), y=np.array(y_pred_trun[i]))[0]) # hardly affected by abnormal scatters (i.e. outliers); x and y must be ndarrays; MGC requires at least 5 samples to give reasonable results
print('判断前的真实(及预测)序列对数:', len(y_true), ' 判断后的真实(及预测)序列对数:', len(y_true_trun), '\n')
print('原始的评估指标:')
print('第一组,零次的相对性指标:', '\n', 'MAPE:', MAPE, '\n', 'SMAPE:', SMAPE, '\n', 'RMSPE:', RMSPE, '\n', 'MTD_p2:', MTD_p2)
print('第二组,一次的绝对性指标:', '\n', 'EMLAE:', EMLAE, '\n', 'MALE:', MALE, '\n', 'MAE:', MAE, '\n', 'RMSE:', RMSE, '\n', 'MedAE:', MedAE, '\n', 'MTD_p1:', MTD_p1)
print('第三组,二次的绝对性指标:', '\n', 'MSE:', MSE, '\n', 'MSLE:', MSLE)
print('第四组,相关性指标:', '\n', 'VAR:', VAR, '\n', 'R2:', R2, '\n', 'PR:', PR, '\n', 'SR:', SR, '\n', 'KT:', KT, '\n', 'WT:', WT, '\n', 'MGC:', MGC, '\n')
# 将各序列对的若干精度指标整合成各序列对的最终单一评价指标;序列对的数目必须≥2,否则归一化后各指标值均为1。
# 将各精度指标在各自维度内进行数值变换:1.对各指标除以其均值,将任意数量级的指标转化为在1上下波动的数值。
# 2.再对抹平数量级后的指标取幂函数,进一步缩小指标内数值的差距,保留代表优劣的方向性即可;原始指标内数值差异越大,所开次方根数越大,反之越小,可以避免指标间离群值的出现。
# 3.再对list作归一化,将所有结果都转化为(0,1)之间的数,越趋近0越好,代表预测列越趋近真实序列;最终精度presion经过有偏向的加权后,也是(0,1)之间的数值。
MAPE_1 = (MAPE / np.mean(MAPE)) / sum(MAPE / np.mean(MAPE))
SMAPE_1 = (SMAPE / np.mean(SMAPE)) / sum(SMAPE / np.mean(SMAPE))
RMSPE_1 = (RMSPE / np.mean(RMSPE)) / sum(RMSPE / np.mean(RMSPE))
MTD_p2_1 = np.sqrt(MTD_p2 / np.mean(MTD_p2)) / sum(np.sqrt(MTD_p2 / np.mean(MTD_p2)))
EMLAE_1 = np.sqrt(EMLAE / np.mean(EMLAE)) / sum(np.sqrt(EMLAE / np.mean(EMLAE)))
MALE_1 = (MALE / np.mean(MALE)) / sum(MALE / np.mean(MALE))
MAE_1 = np.sqrt(MAE / np.mean(MAE)) / sum(np.sqrt(MAE / np.mean(MAE)))
RMSE_1 = np.sqrt(RMSE / np.mean(RMSE)) / sum(np.sqrt(RMSE / np.mean(RMSE)))
MedAE_1 = np.sqrt(MedAE / np.mean(MedAE)) / sum(np.sqrt(MedAE / np.mean(MedAE)))
MTD_p1_1 = np.sqrt(MTD_p1 / np.mean(MTD_p1)) / sum(np.sqrt(MTD_p1 / np.mean(MTD_p1)))
MSE_1 = (MSE / np.mean(MSE))**(1/4) / sum((MSE / np.mean(MSE))**(1/4))
MSLE_1 = np.sqrt(MSLE / np.mean(MSLE)) / sum(np.sqrt(MSLE / np.mean(MSLE)))
VAR_1 = (-np.array(VAR)+1.01 + np.mean(-np.array(VAR)+1.01)) / sum(-np.array(VAR)+1.01 + np.mean(-np.array(VAR)+1.01)) # 因为VAR的取值范围是(-∞, 1],越趋近1越好,可看作极大化目标函数,与其他指标相反;所以需要对其做数值变换,使其变为极小化目标函数。
VAR_1 = (VAR_1 / np.mean(VAR_1))**(1/4) / sum((VAR_1 / np.mean(VAR_1))**(1/4)) # 但不能+1,可以加比1多一点点的任何数,如1.01,否则当原始VAR均为1时,sum(-np.array(VAR)+1 + np.mean(-np.array(VAR)+1))就会为0,则VAR_1就会为nan。
R2_1 = (-np.array(R2)+1.01 + np.mean(-np.array(R2)+1.01)) / sum(-np.array(R2)+1.01 + np.mean(-np.array(R2)+1.01)) # 因为R2的取值范围是(-∞, 1],越趋近1越好,可看作极大化目标函数,与其他指标相反;所以需要对其做数值变换,使其变为极小化目标函数。
R2_1 = (R2_1 / np.mean(R2_1))**(1/4) / sum((R2_1 / np.mean(R2_1))**(1/4)) # 但不能+1,可以加比1多一点点的任何数,如1.01,否则当原始VAR均为1时,sum(-np.array(R2)+1 + np.mean(-np.array(R2)+1))就会为0,则R2_1就会为nan。
PR_1 = (-np.array(PR)+1.01 + np.mean(-np.array(PR)+1.01)) / sum(-np.array(PR)+1.01 + np.mean(-np.array(PR)+1.01)) # 因为PR的取值范围是[-1, 1],越趋近1越好,可看作极大化目标函数,与其他指标相反;所以需要对其做数值变换,使其变为极小化目标函数。
PR_1 = (PR_1 / np.mean(PR_1))**(1/4) / sum((PR_1 / np.mean(PR_1))**(1/4)) # 但不能+1,可以加比1多一点点的任何数,如1.01,否则当原始PR均为1时,sum(-np.array(PR)+1 + np.mean(-np.array(PR)+1))就会为0,则PR_1就会为nan。
SR_1 = (-np.array(SR)+1.01 + np.mean(-np.array(SR)+1.01)) / sum(-np.array(SR)+1.01 + np.mean(-np.array(SR)+1.01)) # 因为SR的取值范围是[-1, 1],越趋近1越好,可看作极大化目标函数,与其他指标相反;所以需要对其做数值变换,使其变为极小化目标函数。
SR_1 = (SR_1 / np.mean(SR_1))**(1/4) / sum((SR_1 / np.mean(SR_1))**(1/4)) # 但不能+1,可以加比1多一点点的任何数,如1.01,否则当原始SR均为1时,sum(-np.array(SR)+1 + np.mean(-np.array(SR)+1))就会为0,则SR_1就会为nan。
KT_1 = (-np.array(KT)+1.01 + np.mean(-np.array(KT)+1.01)) / sum(-np.array(KT)+1.01 + np.mean(-np.array(KT)+1.01)) # 因为KT的取值范围是[-1, 1],越趋近1越好,可看作极大化目标函数,与其他指标相反;所以需要对其做数值变换,使其变为极小化目标函数。
KT_1 = (KT_1 / np.mean(KT_1))**(1/4) / sum((KT_1 / np.mean(KT_1))**(1/4)) # 但不能+1,可以加比1多一点点的任何数,如1.01,否则当原始KT均为1时,sum(-np.array(KT)+1 + np.mean(-np.array(KT)+1))就会为0,则KT_1就会为nan。
WT_1 = (-np.array(WT)+1.01 + np.mean(-np.array(WT)+1.01)) / sum(-np.array(WT)+1.01 + np.mean(-np.array(WT)+1.01)) # 因为WT的取值范围是[-1, 1],越趋近1越好,可看作极大化目标函数,与其他指标相反;所以需要对其做数值变换,使其变为极小化目标函数。
WT_1 = (WT_1 / np.mean(WT_1))**(1/4) / sum((WT_1 / np.mean(WT_1))**(1/4)) # 但不能+1,可以加比1多一点点的任何数,如1.01,否则当原始WT均为1时,sum(-np.array(WT)+1 + np.mean(-np.array(WT)+1))就会为0,则WT_1就会为nan。
MGC_1 = (-np.array(MGC)+1.01 + np.mean(-np.array(MGC)+1.01)) / sum(-np.array(MGC)+1.01 + np.mean(-np.array(MGC)+1.01)) # 因为MGC的取值范围是[-1, 1],越趋近1越好,可看作极大化目标函数,与其他指标相反;所以需要对其做数值变换,使其变为极小化目标函数。
MGC_1 = (MGC_1 / np.mean(MGC_1))**(1/4) / sum((MGC_1 / np.mean(MGC_1))**(1/4)) # 但不能+1,可以加比1多一点点的任何数,如1.01,否则当原始MGC均为1时,sum(-np.array(MGC)+1 + np.mean(-np.array(MGC)+1))就会为0,则MGC_1就会为nan。
print('数值变换后的评估指标:')
print('第一组,零次的相对性指标:', '\n', 'MAPE:', MAPE_1, '\n', 'SMAPE:', SMAPE_1, '\n', 'RMSPE:', RMSPE_1, '\n', 'MTD_p2:', MTD_p2_1)
print('第二组,一次的绝对性指标:', '\n', 'EMLAE:', EMLAE_1, '\n', 'MALE:', MALE_1, '\n', 'MAE:', MAE_1, '\n', 'RMSE:', RMSE_1, '\n', 'MedAE:', MedAE_1, '\n', 'MTD_p1:', MTD_p1_1)
print('第三组,二次的绝对性指标:', '\n', 'MSE:', MSE_1, '\n', 'MSLE:', MSLE_1)
print('第四组,相关性指标:', '\n', 'VAR:', VAR_1, '\n', 'R2:', R2_1, '\n', 'PR:', PR_1, '\n', 'SR:', SR_1, '\n', 'KT:', KT_1, '\n', 'WT:', WT_1, '\n', 'MGC:', MGC_1, '\n')
precision = []
for i in range(len(y_true_trun)):
# 不用调和平均、几何平均,避免结果向极小值趋近;不用均方根,避免结果向极大值趋近;使用算术平均加权,权重可根据实际需求手动调整。
precision.append(dyn_seri_weighted([MAPE_1[i], SMAPE_1[i], RMSPE_1[i], MTD_p2_1[i],
EMLAE_1[i], MALE_1[i], MAE_1[i], RMSE_1[i], MedAE_1[i], MTD_p1_1[i],
MSE_1[i], MSLE_1[i],
VAR_1[i], R2_1[i], PR_1[i], SR_1[i], KT_1[i], WT_1[i], MGC_1[i]],
w=[3,2,2,1, 1,1,1,3,1,1, 1,1, 1,2,1,1,1,1,1]))
print('各序列对的最终精度:', '\n', np.array(precision), '\n')
# 注意返回的各分量指标是未数值变换前的结果,而最终precision是由各分量指标经数值变换后的结果加权算出的
return precision, MAPE, SMAPE, RMSPE, MTD_p2, EMLAE, MALE, MAE, RMSE, MedAE, MTD_p1, MSE, MSLE, VAR, R2, PR, SR, KT, WT, MGC
results_v1 = regression_accuracy(y_true=y_input_mul_actual, y_pred=y_input_mul_pred)
results_v1 = pd.DataFrame(results_v1, index=['precision',
'MAPE', 'SMAPE', 'RMSPE', 'MTD_p2',
'EMLAE', 'MALE', 'MAE', 'RMSE', 'MedAE', 'MTD_p1',
'MSE', 'MSLE'])
print('指标个数:', len(results_v1))
print(results_v1, '\n')
results_v2 = regression_evaluation(y_true=y_input_mul_actual, y_pred=y_input_mul_pred)
results_v2 = pd.DataFrame(results_v2, index=['precision',
'MAPE', 'SMAPE', 'RMSPE', 'MTD_p2',
'EMLAE', 'MALE', 'MAE', 'RMSE', 'MedAE', 'MTD_p1',
'MSE', 'MSLE',
'VAR', 'R2', 'PR', 'SR', 'KT', 'WT', 'MGC'])
print('指标个数:', len(results_v2))
print(results_v2, '\n')
################################################################################################################
# 将评估函数结果用于动态加权的使用方法:
# 使用步骤:1.将每个门店单品,两种模型的一段历史区间,预测序列和真实序列的两组序列对,输入regression_evaluation,得到两个评估指标;
# 2.对这两个指标进行如下操作,得到每个门店单品的两个模型在下一个月所要采用的权重w;
w = (1-results_v2.loc['precision']) / (1-results_v2.loc['precision']).sum()
print('w:', w, '\n', sum(w))
# 因为regression_evaluation返回的最终评估指标precision是(0,1)之间的值,越趋近0则模型预测结果越好,所以需要使用一个递减函数对precision做变换,才能得到真正能使用的权重。
# 这里采用y=1-x,而不用y=1/x,可以避免当x较小时,1/x被放大过多,且一点微小的扰动都会对1/x产生较大影响的不利效应。
# 3.预测下一个月的新数据时,将每个门店单品两个模型的预测序列和各自权重w输入dyn_df_weighted,得到一条预测序列,就是最终发布的预测值。
################################################################################################################
def dyn_df_weighted(df, type=None, w=None, initial=1, r=2, d=1):
"""
传入二维数组df;若type='geometric'或'arithmetic',且输入了w,则w不起作用;若不输入权重,则根据df的列数动态计算基于几何级数或算数级数再作归一化的权重,再做算术平均;
也可人为输入权重做算术平均;若不输入type和w,则进行简单算数平均;因为使用np.matmul,则df.columns的索引越小,权重越大;将df的各列与权重相乘再相加,得到一条最终的序列。
:param df: 需要进行加权变成一条序列的二维数组,df的每列代表一条需要进行加权的序列
:param type: 采用几何级数或算数级数进行加权,或人为指定权重,或默认权重相等,type = 'geometric'或'arithmetic'或None;若type='geometric'或'arithmetic',且输入了w,则w不起作用。
:param w: 一维的权重系数,可以是series,array,list,tuple;若手动输入,其长度必须和一维数组seri(即序列点数)相等
:param r: 指定几何级数分母的公比
:param d: 指定算数级数分母的公差
:param initial: 指定算数级数分母的初始值
:return: df各列与权重w相乘再相加,返回一条最终的序列
"""
if type not in ['geometric', 'arithmetic', None]:
raise Exception('type must be one of geometric, arithmetic or None')
if type is not None:
w = list()
if type == 'geometric':
for i in range(len(df.columns)):
w.append(initial * (1 / r) ** i) # 生成首项是initial,公比是(1/r)的几何级数作权重
else:
for i in range(len(df.columns)):
w.append(1 / (initial + d * i)) # 生成首项是initial,公差是d的算术级数,再做倒数作为权重
w = np.array(w) / sum(w)
elif (type is None) and (w is None):
w = np.ones(len(df.columns)) / sum(np.ones(len(df.columns))) # 生成均等权重
elif (type is None) and (w is not None) and (len(w) == len(df.columns)):
w = np.array(w) / sum(w) # 自定义权重
else:
raise Exception('手动输入的权重长度必须和一维数组长度(即序列点数)相等')
if abs(sum(w)-1) > 0.001:
raise Exception('weights are not useable')
return np.matmul(df.values, w)