| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- """需求自身先验分:来源内排名归一化后再形成可比分。"""
- from __future__ import annotations
- from collections import defaultdict
- from typing import Any
- from supply_infra.scoring.posterior import format_posterior_value
- from supply_infra.scoring.ranking import rank_with_scores
- DEMAND_PRIORITY_SCORE_METHOD = {
- "name": "source_rank_mean_v1",
- "range": "0-100",
- "steps": [
- "同一需求在同一来源内的非零 weight 先取平均",
- "每个来源内部独立按 weight 降序排名并归一化到 (0,1]",
- "同一需求已有来源的归一分等权平均后乘 100",
- ],
- "missing_source_policy": "未出现的来源不补零",
- "warning": "不同来源的原始 weight 不可直接相加;本分也不可与分类节点 total_score 直接相加",
- }
- def _average(values: list[float]) -> float | None:
- return sum(values) / len(values) if values else None
- def build_demand_priority_index(pool_rows: list[Any]) -> dict[str, dict[str, Any]]:
- """基于一天的完整需求池,构建需求自身的来源可比先验分。"""
- weights: dict[tuple[str, str], list[float]] = defaultdict(list)
- rows_by_name: dict[str, list[Any]] = defaultdict(list)
- observed_strategies: dict[str, set[str]] = defaultdict(set)
- for row in pool_rows:
- demand_name = str(getattr(row, "demand_name", "") or "").strip()
- strategy = str(getattr(row, "strategy", "") or "").strip()
- if not demand_name:
- continue
- rows_by_name[demand_name].append(row)
- if strategy:
- observed_strategies[demand_name].add(strategy)
- raw_weight = getattr(row, "weight", None)
- if strategy and raw_weight is not None and float(raw_weight) != 0:
- weights[(strategy, demand_name)].append(float(raw_weight))
- source_averages = {
- key: float(_average(values))
- for key, values in weights.items()
- if values
- }
- candidates_by_source: dict[str, list[tuple[str, float]]] = defaultdict(list)
- for (strategy, demand_name), avg in source_averages.items():
- candidates_by_source[strategy].append((demand_name, avg))
- positions_by_source = {
- strategy: rank_with_scores(candidates)
- for strategy, candidates in candidates_by_source.items()
- }
- index: dict[str, dict[str, Any]] = {}
- for demand_name, demand_rows in rows_by_name.items():
- sources: list[dict[str, Any]] = []
- for strategy in sorted(observed_strategies[demand_name]):
- avg = source_averages.get((strategy, demand_name))
- position = positions_by_source.get(strategy, {}).get(demand_name)
- source_row_count = sum(
- 1
- for row in demand_rows
- if str(getattr(row, "strategy", "") or "").strip() == strategy
- )
- sources.append({
- "strategy": strategy,
- "raw_weight_avg": avg,
- "raw_weight_row_count": source_row_count if avg is not None else 0,
- "rank_in_source": float(position["rank"]) if position is not None else None,
- "source_demand_count": int(position["total"]) if position is not None else 0,
- "source_normalized_rank_score": (
- float(position["normalized_score"]) if position is not None else None
- ),
- })
- normalized_scores = [
- float(item["source_normalized_rank_score"])
- for item in sources
- if item["source_normalized_rank_score"] is not None
- ]
- prior_score = (
- 100 * sum(normalized_scores) / len(normalized_scores)
- if normalized_scores
- else None
- )
- rov_values = [
- float(row.real_rov_7d)
- for row in demand_rows
- if getattr(row, "real_rov_7d", None) is not None
- ]
- vov_values = [
- float(row.real_vov_7d)
- for row in demand_rows
- if getattr(row, "real_vov_7d", None) is not None
- ]
- index[demand_name] = {
- "demand_name": demand_name,
- "source_rank_score": round(prior_score, 4) if prior_score is not None else None,
- "valid_source_count": len(normalized_scores),
- "observed_source_count": len(sources),
- "sources": sources,
- "posterior_from_exact_pool_rows": {
- "rov_diff": {
- "value": max(rov_values) if rov_values else None,
- "has_data": bool(rov_values),
- },
- "vov_diff": {
- "value": max(vov_values) if vov_values else None,
- "has_data": bool(vov_values),
- },
- },
- "exact_pool_ids": sorted({int(row.id) for row in demand_rows}),
- }
- demand_positions = rank_with_scores([
- (demand_name, float(item["source_rank_score"]))
- for demand_name, item in index.items()
- if item["source_rank_score"] is not None
- ])
- for demand_name, item in index.items():
- position = demand_positions.get(demand_name)
- item["global_demand_rank"] = float(position["rank"]) if position is not None else None
- item["global_scored_demand_count"] = int(position["total"]) if position is not None else 0
- item["score_method"] = DEMAND_PRIORITY_SCORE_METHOD["name"]
- return index
- def format_demand_priority(item: dict[str, Any] | None) -> list[str]:
- """将需求自身排名证据格式化为 Agent 易读文本。"""
- if item is None:
- return ["需求自身来源归一分=—(无需求池记录)"]
- score = item.get("source_rank_score")
- rank = item.get("global_demand_rank")
- total = int(item.get("global_scored_demand_count") or 0)
- score_text = "—" if score is None else f"{float(score):.2f}/100"
- rank_text = "无排名" if rank is None else f"{float(rank):g}/{total}"
- lines = [
- f"需求自身来源归一分={score_text};全日需求自身排名={rank_text};"
- f"有效来源={int(item.get('valid_source_count') or 0)}",
- ]
- for source in item.get("sources") or []:
- raw = source.get("raw_weight_avg")
- source_rank = source.get("rank_in_source")
- normalized = source.get("source_normalized_rank_score")
- raw_text = "—" if raw is None else f"{float(raw):.4f}"
- source_rank_text = (
- "—"
- if source_rank is None
- else f"{float(source_rank):g}/{source['source_demand_count']}"
- )
- normalized_text = "—" if normalized is None else f"{float(normalized):.4f}"
- lines.append(
- f" 来源={source['strategy']} raw_weight_avg={raw_text} "
- f"来源内名次={source_rank_text} 来源归一分={normalized_text}"
- )
- lines.append(" 口径:来源内先排名归一化,再对已有来源取均值;禁止直接相加原始 weight。")
- posterior = item.get("posterior_from_exact_pool_rows") or {}
- rov = posterior.get("rov_diff") or {}
- vov = posterior.get("vov_diff") or {}
- if rov.get("has_data") or vov.get("has_data"):
- lines.append(
- " 词级后验:"
- f"rov_diff={format_posterior_value(rov.get('value'))} "
- f"vov_diff={format_posterior_value(vov.get('value'))}"
- )
- else:
- lines.append(" 词级后验:无验证数据(效果未知)")
- return lines
|