Source code for cursus.processing.categorical.multiclass_label_processor

import numpy as np
from typing import List, Union, Optional, Dict


from ..processors import Processor


[docs] class MultiClassLabelProcessor(Processor): """ Processes multi-class labels into a format suitable for machine learning models. This processor handles encoding categorical labels into numerical arrays and optionally provides one-hot encoding using numpy. Args: label_list (Optional[List[str]]): A list of unique label strings. If provided, the processor will learn this mapping; otherwise, it will learn the mapping from the data it processes. one_hot (bool): If True, output one-hot encoded labels. strict (bool): If True, raise error for unknown labels when label_list is provided. """ def __init__( self, label_list: Optional[List[str]] = None, one_hot: bool = False, strict: bool = False, ): super().__init__() self.processor_name = "multiclass_label_processor" self.label_to_id: Dict[str, int] = {} self.id_to_label: List[str] = [] self.one_hot = one_hot self.strict = strict # `is not None` (not truthiness): an explicitly-empty label_list=[] means # "fixed vocab, currently empty" and must be distinguishable from None # ("learn the vocab from data"). if label_list is not None: self.label_to_id = { self._normalize(label): i for i, label in enumerate(label_list) } self.id_to_label = [self._normalize(label) for label in label_list] @staticmethod def _normalize(label) -> str: """Normalize a label to a stable string key. Coerce integer-valued floats to their int form first so that 1, 1.0, "1" all map to the same key "1" (str(1.0) would otherwise yield "1.0" and silently create a separate class from the int/str 1). """ if isinstance(label, float) and label.is_integer(): return str(int(label)) return str(label)
[docs] def process(self, labels: Union[str, List[str]]) -> np.ndarray: """ Encodes the input labels. Args: labels (Union[str, List[str]]): A single label or a list of labels. Returns: np.ndarray: Encoded labels as a numpy array. """ if isinstance(labels, (str, int, float)): labels = [labels] # Wrap scalar in list encoded_labels = [] for label in labels: label = self._normalize(label) # consistent with __init__ mapping if label not in self.label_to_id: if self.strict: raise ValueError(f"Label '{label}' not found in known label list.") self.label_to_id[label] = len(self.label_to_id) self.id_to_label.append(label) encoded_labels.append(self.label_to_id[label]) encoded_array = np.array(encoded_labels, dtype=np.int64) if self.one_hot: # Create one-hot encoding using numpy num_classes = len(self.id_to_label) one_hot_labels = np.eye(num_classes)[encoded_array].astype(np.float32) return one_hot_labels else: return encoded_array