# SPI 基数校准改用非零样本中位数

- 日期：2026-03-02
- 模块：`apps/etl/connectors/feiqiu/tasks/dws/index/spending_power_index_task.py`
- 触发：全栈联调中 SPI 基数校准 6 个参数全部回退默认值（中位数为 0），用户要求改进

## 变更内容

### 修改方法：`SpendingPowerIndexTask._calibrate_amount_bases`

**原逻辑**：对全部会员（含零消费）计算中位数 → 零消费会员占 62%（105 人中 ~65 人），中位数必然为 0 → 全部 6 个参数回退 `DEFAULT_PARAMS`。

**新逻辑**：
1. 新增类常量 `_CALIBRATE_MIN_SAMPLE = 10`（非零样本最小数量阈值）
2. 中位数计算前过滤零值：`nonzero_values = [v for v in ... if v > 0]`
3. 非零样本 ≥ 10 → 使用非零中位数作为校准值
4. 非零样本 < 10 → 回退 `DEFAULT_PARAMS` 并输出 WARNING

### 新增代码标记
```python
# CHANGE 2026-03-02 | 基数校准改用非零样本中位数，零消费会员不参与校准
_CALIBRATE_MIN_SAMPLE = 10
```

## 验证结果

重新执行 `DWS_SPENDING_POWER_INDEX`（run_uuid: `8a9709bc084d4d15a9e2a40976583e24`），校准结果：

| 参数 | 非零样本数 | 校准值 | 状态 |
|------|-----------|--------|------|
| amount_base_spend_90 | 47/105 | 218.00 | ✅ 非零中位数 |
| amount_base_ticket_90 | 47/105 | 29.00 | ✅ 非零中位数 |
| amount_base_recharge_90 | 42/105 | 5000.00 | ✅ 非零中位数 |
| amount_base_ewma_90 | 47/105 | 48.30 | ✅ 非零中位数 |
| amount_base_spend_30 | 6/105 | 500.00 | ⚠️ 样本不足，回退默认 |
| amount_base_speed_abs | 6/105 | 100.00 | ⚠️ 样本不足，回退默认 |

改进：6/6 回退 → 4/6 有效校准 + 2/6 安全回退。

## 影响范围

- 仅影响 SPI 基数校准逻辑，不影响子分公式、归一化、持久化
- 校准优先级不变：配置表值 > 自动校准 > 默认值
- 30 天窗口参数（spend_30、speed_abs）因测试环境数据稀疏仍回退，生产环境数据充足时预期可正常校准

## 回滚

将 `_calibrate_amount_bases` 中的非零过滤逻辑还原为全量样本中位数即可：
```python
# 还原为：
values = [extractor(f) for f in features.values()]
median_val = self.calculate_median(values)
```
并删除 `_CALIBRATE_MIN_SAMPLE` 常量。
