feat: 预测结果百分位裁剪 — 消除极端异常值拉爆专题图色阶

- 新增 _clip_outliers(P1-P99) 方法,预测完成后自动裁剪
- 水体边界零值区域导致模型外推极端值(-86~7026),
  裁剪后克里金插值色阶不再被拉爆,正常空间细节可见
- 无异常值时不触发裁剪(零开销)
This commit is contained in:
duxin
2026-07-28 16:38:07 +08:00
parent b6fa07925a
commit 3c7e735342

View File

@ -740,12 +740,52 @@ class WaterQualityInference:
print(f"预测值范围: [{np.min(predictions):.4f}, {np.max(predictions):.4f}]")
print(f"预测值统计: 均值={np.mean(predictions):.4f}, 标准差={np.std(predictions):.4f}")
# ★ 百分位裁剪:去除极端异常值,避免专题图色阶被拉爆
predictions = self._clip_outliers(predictions)
return predictions
except Exception as e:
print(f"预测失败: {e}")
raise
@staticmethod
def _clip_outliers(predictions: np.ndarray, lower_pct: float = 1.0,
upper_pct: float = 99.0) -> np.ndarray:
"""百分位裁剪:将极端异常值裁剪到合理范围。
水体边界/零值区域的光谱异常会导致模型外推到极端值
(如 BGA 预测 -86 ~ 7026),若不处理,专题图的克里金
插值色阶会被拉爆,正常空间变化完全不可见。
Parameters
----------
predictions : np.ndarray
原始预测值
lower_pct : float
下百分位(默认 1%,低于此分位数的值被裁剪)
upper_pct : float
上百分位(默认 99%,高于此分位数的值被裁剪)
Returns
-------
np.ndarray
裁剪后的预测值(副本)
"""
lo = np.percentile(predictions, lower_pct)
hi = np.percentile(predictions, upper_pct)
# 只在实际有异常值时才裁剪
if lo >= hi:
return predictions
n_clipped_lo = int(np.sum(predictions < lo))
n_clipped_hi = int(np.sum(predictions > hi))
if n_clipped_lo == 0 and n_clipped_hi == 0:
return predictions
print(f"[异常值裁剪] P{lower_pct:.0f}={lo:.4f}, P{upper_pct:.0f}={hi:.4f}, "
f"裁剪低端 {n_clipped_lo} 个, 高端 {n_clipped_hi} 个 "
f"({(n_clipped_lo + n_clipped_hi) / len(predictions) * 100:.1f}%)")
return np.clip(predictions, lo, hi)
def save_predictions(self, coords: pd.DataFrame, predictions: np.ndarray,
output_path: str, prediction_column: str = 'prediction',
wqi_columns: Optional[pd.DataFrame] = None):