diff --git a/src/core/prediction/inference_batch.py b/src/core/prediction/inference_batch.py index 3536187..d4001cd 100644 --- a/src/core/prediction/inference_batch.py +++ b/src/core/prediction/inference_batch.py @@ -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):