[docs]definfer_label_types(values:list)->list:""" Convert a list of string label values to their natural numeric type when all values match. Priority: int > float > unchanged. """ifvaluesisNoneorlen(values)==0ornotall(isinstance(v,str)forvinvalues):returnvaluesifall(_INT_PATTERN.fullmatch(v)forvinvalues):return[int(v)forvinvalues]ifall(_is_float_str(v)forvinvalues):return[float(v)forvinvalues]returnvalues
[docs]classLabel:def__init__(self,name:str,values:list=None,auxiliary_label_names:list=None,positive_class=None):self.name=nameself._values=valuesself.auxiliary_label_names=auxiliary_label_namesself._positive_class=positive_classdef__str__(self):returnf"label {self.name} ({', '.join([str(val)forvalinself.values])})"def__eq__(self,other):ifnotisinstance(other,Label):returnFalsereturnself.name==other.nameandsorted(self.values)==sorted(other.values)and \
self.positive_class==other.positive_classandself.auxiliary_label_names==other.auxiliary_label_names@propertydefpositive_class(self):""" Ensures the same class is always returned as the 'positive class', even when it was not explicitly set. When a type mismatch exists between the stored positive_class and the actual label values (e.g., int 1 vs string '1' due to pandas loading data as strings), the value is coerced to match the values' type. """ifself._positive_classisnotNone:ifself._valuesisnotNoneandself._positive_classnotinself._values:coerced=next((vforvinself._valuesifstr(v)==str(self._positive_class)),None)ifcoercedisnotNone:self._positive_class=coercedreturnself._positive_classelse:assertself._valuesisnotNone,f"Label: cannot access positive class for label {self.name} " \
f"when neither 'positive_class' nor 'values' are set."positive_class=sorted(self._values)[0]logging.info(f"Label: No positive class was set for label {self.name}. "f"Assuming default positive class '{positive_class}'.")returnpositive_class@propertydefvalues(self):""" Make sure the positive class is listed last This is needed for compatibility with `~immuneML.ml_methods.util.Util.binarize_label_classes` """ifself._positive_classisnotNone:negative_classes=sorted(self._values)negative_classes.remove(self._positive_class)returnnegative_classes+[self._positive_class]else:returnsorted(self._values)
[docs]defget_binary_negative_class(self):""" Ensures the correct 'negative class' is returned when using the Label for binary classification. """assertlen(self._values)==2,f"Label: binary negative class was requested for label {self.name} but this label contains {len(self._values)} classes: {self.values}"return[valforvalinself._valuesifval!=self.positive_class][0]
[docs]defget_desc_for_storage(self):""" Method to call when storing a label to YAML format """return{key.lstrip("_"):valueforkey,valueinvars(self).items()}