feat: DEB accuracy tracking - save predictions, compute WU hit rate and MAE
This commit is contained in:
+10
-2
@@ -78,12 +78,20 @@ def analyze_weather_trend(weather_data, temp_symbol, city_name=None):
|
||||
if city_name and current_forecasts:
|
||||
blended_high, weight_info = calculate_dynamic_weights(city_name, current_forecasts)
|
||||
if blended_high is not None:
|
||||
insights.insert(0, f"🧬 <b>DEB 融合预测</b>:<b>{blended_high}{temp_symbol}</b> ({weight_info})")
|
||||
# 展示准确率(如果有历史数据)
|
||||
from src.analysis.deb_algorithm import get_deb_accuracy
|
||||
accuracy = get_deb_accuracy(city_name)
|
||||
acc_tag = ""
|
||||
if accuracy:
|
||||
hit_rate, mae, total_days, details_str = accuracy
|
||||
acc_tag = f"\n 📈 <b>DEB 历史战绩</b>:{details_str}"
|
||||
|
||||
insights.insert(0, f"🧬 <b>DEB 融合预测</b>:<b>{blended_high}{temp_symbol}</b> ({weight_info}){acc_tag}")
|
||||
ai_features.append(f"🧬 DEB系统已通过历史偏差矫正算出期待点是: {blended_high}{temp_symbol}。")
|
||||
|
||||
# 顺便把今天的预测记录下来供之后回测用
|
||||
try:
|
||||
update_daily_record(city_name, local_date_str, current_forecasts, max_so_far)
|
||||
update_daily_record(city_name, local_date_str, current_forecasts, max_so_far, deb_prediction=blended_high)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -45,11 +45,12 @@ def save_history(filepath, data):
|
||||
except Exception as e:
|
||||
print(f"Error saving history: {e}")
|
||||
|
||||
def update_daily_record(city_name, date_str, forecasts, actual_high):
|
||||
def update_daily_record(city_name, date_str, forecasts, actual_high, deb_prediction=None):
|
||||
"""
|
||||
保存/更新某城市某天的各个模型预报与最终实测值
|
||||
forecasts: dict, 例如 {"ECMWF": 28.5, "GFS": 30.0, ...}
|
||||
actual_high: float, 最终实测最高温
|
||||
deb_prediction: float, DEB 融合预测值(用于准确率追踪)
|
||||
"""
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
history_file = os.path.join(project_root, 'data', 'daily_records.json')
|
||||
@@ -61,14 +62,15 @@ def update_daily_record(city_name, date_str, forecasts, actual_high):
|
||||
if date_str not in data[city_name]:
|
||||
data[city_name][date_str] = {}
|
||||
|
||||
# 避免无意义的频繁磁盘写入:如果数据没有变化,直接返回
|
||||
# 避免无意义的频繁磁盘写入
|
||||
old_actual = data[city_name][date_str].get('actual_high')
|
||||
if old_actual == actual_high and data[city_name][date_str].get('forecasts') == forecasts:
|
||||
return
|
||||
|
||||
data[city_name][date_str]['forecasts'] = forecasts
|
||||
# 只要仍在更新或者已经结束,都记录最新高点
|
||||
data[city_name][date_str]['actual_high'] = actual_high
|
||||
if deb_prediction is not None:
|
||||
data[city_name][date_str]['deb_prediction'] = deb_prediction
|
||||
|
||||
# 自动清理:只保留最近 14 天的记录(DEB 只用 7 天,14 天留足余量)
|
||||
cutoff = (datetime.now() - timedelta(days=14)).strftime("%Y-%m-%d")
|
||||
@@ -162,3 +164,53 @@ def calculate_dynamic_weights(city_name, current_forecasts, lookback_days=7):
|
||||
|
||||
return round(blended_high, 1), " | ".join(weight_str_parts)
|
||||
|
||||
|
||||
def get_deb_accuracy(city_name):
|
||||
"""
|
||||
计算 DEB 融合预测的历史准确率
|
||||
返回: (hit_rate, mae, total_days, details_str) 或 None
|
||||
- hit_rate: WU 结算命中率 (DEB 四舍五入 == 实测四舍五入)
|
||||
- mae: 平均绝对误差
|
||||
- total_days: 有效天数
|
||||
- details_str: 格式化的展示字符串
|
||||
"""
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
history_file = os.path.join(project_root, 'data', 'daily_records.json')
|
||||
data = load_history(history_file)
|
||||
|
||||
if city_name not in data:
|
||||
return None
|
||||
|
||||
city_data = data[city_name]
|
||||
today_str = datetime.now().strftime("%Y-%m-%d")
|
||||
|
||||
hits = 0
|
||||
total = 0
|
||||
errors = []
|
||||
|
||||
for date_str in sorted(city_data.keys()):
|
||||
if date_str == today_str:
|
||||
continue # 跳过今天,还没结算
|
||||
record = city_data[date_str]
|
||||
deb_pred = record.get('deb_prediction')
|
||||
actual = record.get('actual_high')
|
||||
|
||||
if deb_pred is None or actual is None:
|
||||
continue
|
||||
|
||||
total += 1
|
||||
deb_wu = round(deb_pred)
|
||||
actual_wu = round(actual)
|
||||
if deb_wu == actual_wu:
|
||||
hits += 1
|
||||
errors.append(abs(deb_pred - actual))
|
||||
|
||||
if total == 0:
|
||||
return None
|
||||
|
||||
hit_rate = hits / total * 100
|
||||
mae = sum(errors) / len(errors)
|
||||
|
||||
details_str = f"过去{total}天 WU命中 {hits}/{total} ({hit_rate:.0f}%) | MAE: {mae:.1f}°"
|
||||
|
||||
return hit_rate, mae, total, details_str
|
||||
|
||||
Reference in New Issue
Block a user