| """kNN-based local scale estimation shared by local conformal methods.""" |
| import numpy as np |
| from sklearn.neighbors import NearestNeighbors |
| from ..utils.simplex import ilr |
|
|
|
|
| def knn_sigma_hat( |
| U_ref: np.ndarray, |
| R_ref: np.ndarray, |
| U_query: np.ndarray, |
| k: int = 20, |
| ) -> np.ndarray: |
| """Estimate local scale at query points via kNN in ILR space. |
| |
| Args: |
| U_ref: reference simplex predictions (n_ref, K) |
| R_ref: residuals at reference points (n_ref,) |
| U_query: query simplex predictions (n_query, K) |
| k: number of neighbors |
| |
| Returns: |
| Estimated local scale (n_query,), floored at 1e-8. |
| """ |
| if len(U_ref) == 0: |
| return np.ones(len(U_query), dtype=float) |
| Z_ref = ilr(U_ref) |
| Z_query = ilr(U_query) |
| k_actual = min(k, len(Z_ref)) |
| nn = NearestNeighbors(n_neighbors=k_actual).fit(Z_ref) |
| _, indices = nn.kneighbors(Z_query) |
| neighbor_R = R_ref[indices] |
| sigma_hat = np.median(neighbor_R, axis=1) |
| return np.maximum(sigma_hat, 1e-8) |
|
|
|
|
| def knn_sigma_leave_one_out( |
| U_ref: np.ndarray, |
| R_ref: np.ndarray, |
| k: int = 20, |
| ) -> np.ndarray: |
| """Estimate local scale at each reference point excluding itself. |
| |
| Args: |
| U_ref: reference simplex predictions (n_ref, K) |
| R_ref: residuals at reference points (n_ref,) |
| k: number of non-self neighbors |
| |
| Returns: |
| Leave-one-out local scales (n_ref,), floored at 1e-8. |
| """ |
| if len(U_ref) <= 1: |
| return np.ones(len(U_ref), dtype=float) |
|
|
| Z_ref = ilr(U_ref) |
| k_actual = min(k + 1, len(Z_ref)) |
| nn = NearestNeighbors(n_neighbors=k_actual).fit(Z_ref) |
| _, indices = nn.kneighbors(Z_ref) |
|
|
| loo_neighbors = indices[:, 1:] if k_actual > 1 else indices |
| neighbor_R = R_ref[loo_neighbors] |
| sigma_hat = np.median(neighbor_R, axis=1) |
| return np.maximum(sigma_hat, 1e-8) |
|
|