Files
2026-07-13 12:49:22 +08:00

70 lines
2.7 KiB
Python

import numpy.typing as npt
from scipy.sparse import csr_matrix
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, cast
from cleanlab.internal.neighbor.knn_graph import create_knn_graph_and_index
from cleanlab.typing import Metric
if TYPE_CHECKING:
from sklearn.neighbors import NearestNeighbors
def num_neighbors_in_knn_graph(knn_graph: csr_matrix) -> int:
"""Calculate the number of neighbors per row in a knn graph."""
return knn_graph.nnz // knn_graph.shape[0]
def _process_knn_graph_from_inputs(
user_find_issues_kwargs: Dict[str, Any], statistics: Dict[str, Any], k_for_recomputation: int
) -> Optional[csr_matrix]:
"""Determine if a knn_graph is provided in the kwargs or if one is already stored in the associated Datalab instance."""
provided_knn_graph: Optional[csr_matrix] = user_find_issues_kwargs.get("knn_graph", None)
existing_knn_graph = statistics.get("weighted_knn_graph", None)
knn_graph: Optional[csr_matrix] = None
if provided_knn_graph is not None:
knn_graph = provided_knn_graph
elif existing_knn_graph is not None:
knn_graph = existing_knn_graph
num_neighbors = num_neighbors_in_knn_graph(knn_graph) if knn_graph is not None else -1
needs_recompute = k_for_recomputation > num_neighbors
if needs_recompute:
# If the provided knn graph is insufficient, then we need to recompute the knn graph
# with the provided features
knn_graph = None
return knn_graph
def knn_exists(kwargs: Dict[str, Any], statistics: Dict[str, Any], k_needed: int) -> bool:
"""Check if a sufficiently large knn graph exists in the kwargs or statistics."""
return (
_process_knn_graph_from_inputs(kwargs, statistics, k_for_recomputation=k_needed) is not None
)
def set_knn_graph(
features: Optional[npt.NDArray],
find_issues_kwargs: Dict[str, Any],
metric: Optional[Metric],
k: int,
statistics: Dict[str, Any],
) -> Tuple[csr_matrix, Metric, Optional["NearestNeighbors"]]:
# This only fetches graph (optionally)
knn_graph = _process_knn_graph_from_inputs(
find_issues_kwargs, statistics, k_for_recomputation=k
)
old_knn_metric = statistics.get("knn_metric", metric)
missing_knn_graph = knn_graph is None
metric_changes = metric and metric != old_knn_metric
knn: Optional[NearestNeighbors] = None
if missing_knn_graph or metric_changes:
assert features is not None, "Features must be provided to compute the knn graph."
knn_graph, knn = create_knn_graph_and_index(features, n_neighbors=k, metric=metric)
metric = knn.metric
return cast(csr_matrix, knn_graph), cast(Metric, metric), knn