""" Copyright 2020 Johns Hopkins University (Author: Jesus Villalba) Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0) """ from pathlib import Path import numpy as np import pandas as pd import scipy.sparse as sparse from .list_utils import * from .trial_key import TrialKey from .trial_ndx import TrialNdx class SparseTrialKey(TrialKey): """Contains the trial key for speaker recognition trials. Bosaris compatible Key. Attributes: model_set: List of model names. seg_set: List of test segment names. tar: Boolean matrix with target trials to True (num_models x num_segments). non: Boolean matrix with non-target trials to True (num_models x num_segments). model_cond: Conditions related to the model. seg_cond: Conditions related to the test segment. trial_cond: Conditions related to the combination of model and test segment. model_cond_name: String list with the names of the model conditions. seg_cond_name: String list with the names of the segment conditions. trial_cond_name: String list with the names of the trial conditions. """ def __init__( self, model_set=None, seg_set=None, tar=None, non=None, model_cond=None, seg_cond=None, trial_cond=None, model_cond_name=None, seg_cond_name=None, trial_cond_name=None, ): super().__init__( model_set, seg_set, tar, non, model_cond, seg_cond, trial_cond, model_cond_name, seg_cond_name, trial_cond_name, ) def sort(self): """Sorts the object by model and test segment names.""" self.model_set, m_idx = sort(self.model_set, return_index=True) self.seg_set, s_idx = sort(self.seg_set, return_index=True) tar = self.tar.toarray() non = self.non.toarray() ix = np.ix_(m_idx, s_idx) tar = tar[ix] non = non[ix] tar = sparse.csr_matrix(tar) non = sparse.csr_matrix(non) tar.eliminate_zeros() non.eliminate_zeros() tar.sort_indices() non.sort_indices() self.tar = tar self.non = non def save_h5(self, file_path): raise NotImplementedError() def save_txt(self, file_path): """Saves object to txt file. Args: file_path: File to write the list. """ with open(file_path, "w") as f: self.tar.eliminate_zeros() self.non.eliminate_zeros() tar = self.tar.tocoo() for r, c in zip(tar.row, tar.col): f.write("%s %s target\n" % (self.model_set[r], self.seg_set[c])) non = self.non.tocoo() for r, c in zip(non.row, non.col): f.write("%s %s nontarget\n" % (self.model_set[r], self.seg_set[c])) def save_table(self, file_path, sep=None): """Saves object to txt file. Args: file_path: File to write the list. """ file_path = Path(file_path) ext = file_path.suffix if sep is None: sep = "\t" if ".tsv" in ext else "," with open(file_path, "w", encoding="utf-8") as f: f.write(f"modelid{sep}segmentid{sep}targettype\n") self.tar.eliminate_zeros() self.non.eliminate_zeros() tar = self.tar.tocoo() for r, c in zip(tar.row, tar.col): f.write(f"{self.model_set[r]}{sep}{self.seg_set[c]}{sep}target\n") non = self.non.tocoo() for r, c in zip(non.row, non.col): f.write(f"{self.model_set[r]}{sep}{self.seg_set[c]}{sep}nontarget\n") @classmethod def load_h5(cls, file_path): raise NotImplementedError() @classmethod def load_txt(cls, file_path): """Loads object from txt file Args: file_path: File to read the list. Returns: TrialKey object. """ with open(file_path, "r") as f: fields = [line.split() for line in f] models = [i[0] for i in fields] segments = [i[1] for i in fields] is_tar = [i[2] == "target" for i in fields] model_set, _, model_idx = np.unique( models, return_index=True, return_inverse=True ) seg_set, _, seg_idx = np.unique( segments, return_index=True, return_inverse=True ) tar = sparse.lil_matrix((len(model_set), len(seg_set)), dtype="bool") non = sparse.lil_matrix((len(model_set), len(seg_set)), dtype="bool") for item in zip(model_idx, seg_idx, is_tar): if item[2]: tar[item[0], item[1]] = True else: non[item[0], item[1]] = True return cls(model_set, seg_set, tar.tocsr(), non.tocsr()) @classmethod def load_table(cls, file_path, sep=None): """Loads object from txt file Args: file_path: File to read the list. Returns: SparseTrialKey object. """ file_path = Path(file_path) ext = file_path.suffix if sep is None: sep = "\t" if ".tsv" in ext else "," df = pd.read_csv(file_path, sep=sep, dtype={"modelid": str, "segmentid": str}) models = df["modelid"].values segments = df["segmentid"].values is_tar = (df["targettype"] == "target").values model_set, model_idx = np.unique(models, return_inverse=True) seg_set, seg_idx = np.unique(segments, return_inverse=True) tar = sparse.lil_matrix((len(model_set), len(seg_set)), dtype="bool") non = sparse.lil_matrix((len(model_set), len(seg_set)), dtype="bool") for i, j, target_type in zip(model_idx, seg_idx, is_tar): if target_type: tar[i, j] = True else: non[i, j] = True return cls(model_set, seg_set, tar.tocsr(), non.tocsr()) @classmethod def merge(cls, key_list): raise NotImplementedError() def to_ndx(self): """Converts TrialKey object into TrialNdx object. Returns: TrialNdx object. """ mask = np.logical_or(self.tar.toarray(), self.non.toarray()) return TrialNdx(self.model_set, self.seg_set, mask) def validate(self): """Validates the attributes of the TrialKey object.""" self.model_set = list2ndarray(self.model_set) self.seg_set = list2ndarray(self.seg_set) shape = (len(self.model_set), len(self.seg_set)) assert len(np.unique(self.model_set)) == shape[0] assert len(np.unique(self.seg_set)) == shape[1] if (self.tar is None) or (self.non is None): self.tar = sparse.csr_matrix(shape, dtype="bool") self.non = sparse.csr_matrix(shape, dtype="bool") else: assert self.tar.shape == shape assert self.non.shape == shape if self.model_cond is not None: assert self.model_cond.shape[1] == shape[0] if self.seg_cond is not None: assert self.seg_cond.shape[1] == shape[1] if self.trial_cond is not None: assert self.trial_cond.shape[1:] == shape if self.model_cond_name is not None: self.model_cond_name = list2ndarray(self.model_cond_name) if self.seg_cond_name is not None: self.seg_cond_name = list2ndarray(self.seg_cond_name) if self.trial_cond_name is not None: self.trial_cond_name = list2ndarray(self.trial_cond_name) @classmethod def from_trial_key(cls, key): tar = sparse.csr_matrix(key.tar) non = sparse.csr_matrix(key.non) tar.eliminate_zeros() non.eliminate_zeros() tar.sort_indices() non.sort_indices() return cls( key.model_set, key.seg_set, tar, non, key.model_cond, key.seg_cond, key.trial_cond, key.model_cond_name, key.seg_cond_name, key.trial_cond_name, ) def __eq__(self, other): """Equal operator""" eq = self.model_set.shape == other.model_set.shape eq = eq and np.all(self.model_set == other.model_set) eq = eq and (self.seg_set.shape == other.seg_set.shape) eq = eq and np.all(self.seg_set == other.seg_set) eq = eq and np.all(self.tar.data == other.tar.data) eq = eq and np.all(self.non.data == other.non.data) eq = eq and np.all(self.tar.indices == other.tar.indices) eq = eq and np.all(self.non.indices == other.non.indices) eq = eq and ((self.model_cond is None) == (other.model_cond is None)) eq = eq and ((self.seg_cond is None) == (other.seg_cond is None)) eq = eq and ((self.trial_cond is None) == (other.trial_cond is None)) if self.model_cond is not None: eq = eq and np.all(self.model_cond == other.model_cond) if self.seg_cond is not None: eq = eq and np.all(self.seg_cond == other.seg_cond) if self.trial_cond is not None: eq = eq and np.all(self.triall_cond == other.trial_cond) eq = eq and ((self.model_cond_name is None) == (other.model_cond_name is None)) eq = eq and ((self.seg_cond_name is None) == (other.seg_cond_name is None)) eq = eq and ((self.trial_cond_name is None) == (other.trial_cond_name is None)) if self.model_cond_name is not None: eq = eq and np.all(self.model_cond_name == other.model_cond_name) if self.seg_cond_name is not None: eq = eq and np.all(self.seg_cond_name == other.seg_cond_name) if self.trial_cond_name is not None: eq = eq and np.all(self.triall_cond_name == other.trial_cond_name) return eq