114 lines
3.4 KiB
Python
114 lines
3.4 KiB
Python
"""
|
|
Helper functions internally used in cleanlab.regression.
|
|
"""
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
from numpy.typing import ArrayLike
|
|
from typing import Tuple, Union
|
|
|
|
|
|
def assert_valid_prediction_inputs(
|
|
labels: ArrayLike,
|
|
predictions: ArrayLike,
|
|
method: str,
|
|
) -> Tuple[np.ndarray, np.ndarray]:
|
|
"""Checks that ``labels``, ``predictions``, ``method`` are correctly formatted."""
|
|
|
|
# Load array_like input as numpy array. If not raise error.
|
|
try:
|
|
labels = np.asarray(labels)
|
|
except:
|
|
raise ValueError(f"labels must be array_like.")
|
|
|
|
try:
|
|
predictions = np.asarray(predictions)
|
|
except:
|
|
raise ValueError(f"predictions must be array_like.")
|
|
|
|
# Check if labels and predictions are 1-D and numeric
|
|
valid_labels = check_dimension_and_datatype(check_input=labels, text="labels")
|
|
valid_predictions = check_dimension_and_datatype(check_input=predictions, text="predictions")
|
|
|
|
# Check if number of examples are same.
|
|
assert (
|
|
valid_labels.shape == valid_predictions.shape
|
|
), f"Number of examples in labels {labels.shape} and predictions {predictions.shape} are not same."
|
|
|
|
# Check if inputs have missing values
|
|
check_missing_values(valid_labels, text="labels")
|
|
check_missing_values(valid_predictions, text="predictions")
|
|
|
|
# Check if method is among allowed scoring method
|
|
scoring_methods = ["residual", "outre"]
|
|
if method not in scoring_methods:
|
|
raise ValueError(f"Specified method '{method}' must be one of: {scoring_methods}.")
|
|
|
|
# return 1-D numpy array
|
|
return valid_labels, valid_predictions
|
|
|
|
|
|
def assert_valid_regression_inputs(
|
|
X: Union[np.ndarray, pd.DataFrame],
|
|
y: ArrayLike,
|
|
) -> Tuple[np.ndarray, np.ndarray]:
|
|
"""
|
|
Checks that regression inputs are properly formatted and returns the inputs in numpy array format.
|
|
"""
|
|
try:
|
|
X = np.asarray(X)
|
|
except:
|
|
raise ValueError(f"X must be array_like.")
|
|
|
|
y = check_dimension_and_datatype(y, "y")
|
|
check_missing_values(y, text="y")
|
|
|
|
if len(X) != len(y):
|
|
raise ValueError("X and y must have same length.")
|
|
|
|
return X, y
|
|
|
|
|
|
def check_dimension_and_datatype(check_input: ArrayLike, text: str) -> np.ndarray:
|
|
"""
|
|
Raises errors related to:
|
|
1. If input is empty
|
|
2. If input is not 1-D
|
|
3. If input is not numeric
|
|
|
|
If all the checks are passed, it returns the squeezed 1-D array required by the main algorithm.
|
|
"""
|
|
|
|
try:
|
|
check_input = np.asarray(check_input)
|
|
except:
|
|
raise ValueError(f"{text} could not be converted to numpy array, check input.")
|
|
|
|
# Check if input is empty
|
|
if not check_input.size:
|
|
raise ValueError(f"{text} cannot be empty array.")
|
|
|
|
# Remove axis with length one
|
|
check_input = np.squeeze(check_input)
|
|
|
|
# Check if input is 1-D
|
|
if check_input.ndim != 1:
|
|
raise ValueError(
|
|
f"Expected 1-Dimensional inputs for {text}, got {check_input.ndim} dimensions."
|
|
)
|
|
|
|
# Check if datatype is numeric
|
|
if not np.issubdtype(check_input.dtype, np.number):
|
|
raise ValueError(
|
|
f"Expected {text} to contain numeric values, got values of type {check_input.dtype}."
|
|
)
|
|
|
|
return check_input
|
|
|
|
|
|
def check_missing_values(check_input: np.ndarray, text: str):
|
|
"""Raise error if there are any missing values in Numpy array."""
|
|
|
|
if np.isnan(check_input).any():
|
|
raise ValueError(f"{text} cannot contain missing values.")
|