You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

时间序列预测方向一致性准确率函数封装及KeyError问题解决

时间序列预测方向一致性准确率函数封装及KeyError修复

需求说明

需要计算时间序列预测中真实值(y_true)与预测值(y_pred)的连续月份涨跌方向一致性比率,作为评估指标:

  • 方向定义:当月值减上月值为正标记为上涨(1),负为下跌(-1),0为持平(0)
  • 计算时排除y_true方向为null或0的行

样本数据

date  y_true  y_pred1  y_pred2
0    2017-1-31    6.42    -2.35    15.57
1    2017-2-28   -2.35    15.57     6.64
2    2017-3-31   15.57     6.64     7.61
3    2017-4-30    6.64     7.61    10.28
4    2017-5-31    7.61     7.61     6.34
5    2017-6-30   10.28     6.34     4.88
6    2017-7-31    6.34     4.88     7.91
7    2017-8-31    6.34     7.91     6.26
8    2017-9-30    7.91     6.26    11.51
9   2017-10-31    6.26    11.51    10.73
10  2017-11-30   11.51    10.73    10.65
11  2017-12-31   10.73    10.65    32.05

原计算逻辑及结果

以下代码可完成样本计算,得到结果0.2和0.4:

import numpy as np
import pandas as pd

def cal_arrays_direction(value):
    if value > 0:
        return 1
    elif value < 0:
        return -1
    elif value == 0:
        return 0
    else:
        return np.NaN
    
df['y_true_diff'] = df['y_true'].diff(1).map(cal_arrays_direction)
df['y_pred1_diff'] = df['y_pred1'].diff(1).map(cal_arrays_direction)
df['y_pred2_diff'] = df['y_pred2'].diff(1).map(cal_arrays_direction)

df['y_true_y_pred1'] = np.where((df['y_true_diff'] == df['y_pred1_diff']), 1, 0)
df['y_true_y_pred2'] = np.where((df['y_true_diff'] == df['y_pred2_diff']), 1, 0)

dir_acc_y_true_pred1 = df['y_true_y_pred1'].value_counts()[1] / (df['y_true_diff'].value_counts()[-1] 
+ df['y_true_diff'].value_counts()[1])
print(dir_acc_y_true_pred1)

dir_acc_y_true_pred2 = df['y_true_y_pred2'].value_counts()[1] / (df['y_true_diff'].value_counts()[-1] 
 + df['y_true_diff'].value_counts()[1])
print(dir_acc_y_true_pred2)

输出:

0.2
0.4

封装需求

需要将上述逻辑封装为类似sklearn中MSE、RMSE的可复用函数,函数签名如下:

def direction_consistency_acc(y_true, y_pred):
     ...
     return dir_acc_ratio

遇到的问题

封装后运行触发KeyError,错误信息如下:

Update 1

Traceback (most recent call last):
  File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\indexes\base.py", line 3803, in get_loc
    return self._engine.get_loc(casted_key)
  File "pandas\_libs\index.pyx", line 138, in pandas._libs.index.IndexEngine.get_loc
  File "pandas\_libs\index.pyx", line 165, in pandas._libs.index.IndexEngine.get_loc
  File "pandas\_libs\hashtable_class_helper.pxi", line 1577, in pandas._libs.hashtable.Float64HashTable.get_item
  File "pandas\_libs\hashtable_class_helper.pxi", line 1587, in pandas._libs.hashtable.Float64HashTable.get_item
KeyError: 1.0

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "..\code\stacked model_2022-11-08.py", line 353, in <module>
    run_model(df)
  File "..\code\stacked model_2022-11-08.py", line 258, in run_model
    out1 = direction_consistency_acc(preds['y_true'], preds[['y_pred1','y_pred2',
  File "..\code\stacked model_2022-11-08.py", line 245, in direction_consistency_acc
    dir_acc_y_true_pred = preds[f'y_true_{col}'].eq(1).sum() / (s[-1] + s[1])
  File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\series.py", line 981, in __getitem__
    return self._get_value(key)
  File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\series.py", line 1089, in _get_value
    loc = self.index.get_loc(label)
  File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\indexes\base.py", line 3805, in get_loc
    raise KeyError(key) from err
KeyError: 1

Update 2

排查发现当y_true_diff仅存在-1.0时触发错误:

...
2021-05-31
-1.0    4
 1.0    2
Name: y_true_diff, dtype: int64
2021-06-30
-1.0    5
 1.0    1
Name: y_true_diff, dtype: int64
2021-07-31
-1.0    6
Name: y_true_diff, dtype: int64
Traceback (most recent call last):
  File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\indexes\base.py", line 3803, in get_loc
    return self._engine.get_loc(casted_key)
  File "pandas\_libs\index.pyx", line 138, in pandas._libs.index.IndexEngine.get_loc
  File "pandas\_libs\index.pyx", line 165, in pandas._libs.index.IndexEngine.get_loc
  File "pandas\_libs\hashtable_class_helper.pxi", line 1577, in pandas._libs.hashtable.Float64HashTable.get_item
  File "pandas\_libs\hashtable_class_helper.pxi", line 1587, in pandas._libs.hashtable.Float64HashTable.get_item
KeyError: 1.0

The above exception was the direct cause of the following exception:

    Traceback (most recent call last):
      File "..\code\stacked model_2022-11-08.py", line 353, in <module>
        run_model(df)
      File "..\code\stacked model_2022-11-08.py", line 258, in run_model
        out1 = direction_consistency_acc(preds['y_true'], preds[['y_pred1','y_pred2',
      File "..\code\stacked model_2022-11-08.py", line 245, in direction_consistency_acc
        dir_acc_y_true_pred = preds[f'y_true_{col}'].eq(1).sum() / (s[-1] + s[1])
      File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\series.py", line 981, in __getitem__
        return self._get_value(key)
      File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\series.py", line 1089, in _get_value
        loc = self.index.get_loc(label)
      File "C:\Users\LSTM\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\indexes\base.py", line 3805, in get_loc
        raise KeyError(key) from err
    KeyError: 1

解决方案

问题根源

KeyError出现的原因是直接通过value_counts()[1]或value_counts()[-1]取值时,若该方向(上涨/下跌)不存在,就会触发键不存在的异常。比如当y_true只有下跌方向时,value_counts()中没有1这个键,导致报错。

修复后的函数

以下是封装好且修复异常的函数,支持单预测列和多预测列输入,同时处理边界情况:

import numpy as np
import pandas as pd

def direction_consistency_acc(y_true, y_pred):
    # 定义方向转换函数
    def get_direction(series):
        diff = series.diff(1)
        return np.where(diff > 0, 1, np.where(diff < 0, -1, 0))
    
    # 计算真实值方向
    y_true_dir = get_direction(y_true)
    # 过滤掉真实值方向为0或NaN的索引
    valid_idx = (y_true_dir != 0) & (~pd.isna(y_true_dir))
    
    # 处理多预测列的情况
    if isinstance(y_pred, pd.DataFrame):
        results = {}
        for col in y_pred.columns:
            y_pred_dir = get_direction(y_pred[col])
            # 只保留有效索引的方向数据
            true_valid = y_true_dir[valid_idx]
            pred_valid = y_pred_dir[valid_idx]
            # 计算一致的数量
            match_count = (true_valid == pred_valid).sum()
            # 计算有效样本总数(真实值非0的数量)
            total_valid = len(true_valid)
            # 避免除以0
            results[col] = match_count / total_valid if total_valid > 0 else 0.0
        return results
    else:
        # 单预测列的情况
        y_pred_dir = get_direction(y_pred)
        true_valid = y_true_dir[valid_idx]
        pred_valid = y_pred_dir[valid_idx]
        match_count = (true_valid == pred_valid).sum()
        total_valid = len(true_valid)
        return match_count / total_valid if total_valid > 0 else 0.0

函数说明

  1. 方向转换:使用np.where替代map,更高效地生成方向标记
  2. 有效数据过滤:直接通过布尔索引过滤真实值方向为0或NaN的数据,避免后续无效计算
  3. 边界处理:
    • 当真实值没有有效涨跌数据(total_valid=0)时,返回0.0避免除以0错误
    • 处理多预测列输入时,返回各列对应的准确率字典
  4. 避免KeyError:不再依赖value_counts()的键取值,直接通过布尔求和计算匹配数和有效总数

测试验证

用样本数据测试:

# 加载样本数据
df = pd.DataFrame({
    'date': ['2017-1-31', '2017-2-28', '2017-3-31', '2017-4-30', '2017-5-31', '2017-6-30',
             '2017-7-31', '2017-8-31', '2017-9-30', '2017-10-31', '2017-11-30', '2017-12-31'],
    'y_true': [6.42, -2.35, 15.57, 6.64, 7.61, 10.28, 6.34, 6.34, 7.91, 6.26, 11.51, 10.73],
    'y_pred1': [-2.35, 15.57, 6.64, 7.61, 7.61, 6.34, 4.88, 7.91, 6.26, 11.51, 10.73, 10.65],
    'y_pred2': [15.57, 6.64, 7.61, 10.28, 6.34, 4.88, 7.91, 6.26, 11.51, 10.73, 10.65, 32.05]
})

# 单预测列测试
print(direction_consistency_acc(df['y_true'], df['y_pred1']))  # 输出0.2
print(direction_consistency_acc(df['y_true'], df['y_pred2']))  # 输出0.4

# 多预测列测试
print(direction_consistency_acc(df['y_true'], df[['y_pred1', 'y_pred2']]))
# 输出:{'y_pred1': 0.2, 'y_pred2': 0.4}

异常场景测试

测试只有下跌方向的情况:

# 构造只有下跌的真实值数据
test_true = pd.Series([10, 8, 6, 4, 2])
test_pred = pd.Series([9, 7, 5, 3, 1])

print(direction_consistency_acc(test_true, test_pred))  # 输出1.0,全部方向一致

内容的提问来源于stack exchange,提问作者ah bon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:01:17