Project: s41467-024-55291-x | Function: TextClassification.get_roc_auc (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** ROC AUC computation using trapezoidal integration for model evaluation

**KEY TERMS:** {
  "Theranostic Genome": "Thereby, we describe the *Theranostic Genome* , the part of the human genome whose expression can be utilized for combined therapeutic and diagnostic applications",
  "Theranostic": "Theranostic drugs represent a new paradigm for precision oncology ^8",
  "Thereby": "Thereby, we describe the *Theranostic Genome* , the part of the human genome whose expression can be utilized for combined therapeutic and diagnostic applications"
}

**CONTEXT CODE:**         self.textcat = self.model.create_pipe("textcat", config={"exclusive_classes": True, "architecture": "ensemble"})
        self.model.add_pipe(self.textcat, last=True)
        self.textcat.add_label("POSITIVE_NUCL_MED")
        self.textcat.add_label("NEGATIVE_NUCL_MED")

    def evaluate(self, tokenizer, textcat, texts, cats, thr=0.5):
        """
        Computes the training performance metrics of the model

        :param tokenizer: tokenizer model
        :type tokenizer: spaCy's Tokenizer object
        :param textcat: text categorizer model
        :type textcat: spaCy's TextCategorizer object
        :param texts: texts used for training metrics computation
        :type texts: list(str)
        :param cats: categories corresponding to the input texts used as gold standard to compute training metrics
        :type cats: list(str)
        :param thr: prediction score threshold for category 'POSITIVE_NUCL_MED', defaults to 0.5
        :type thr: float, optional
        """
        docs = (tokenizer(text) for text in texts)
        tp = 0.0  # True positives
        fp = 1e-8  # False positives
        fn = 1e-8  # False negatives
        tn = 0.0  # True negatives
        for i, doc in enumerate(textcat.pipe(docs)):
            gold = cats[i]["cats"]
            for label, score in doc.cats.items():
                if label not in gold:
                    continue
                if label == "NEGATIVE_NUCL_MED":
                    continue
                if score >= thr and gold[label] >= 0.5:
                    tp += 1.0
                elif score >= thr and gold[label] < 0.5:
                    fp += 1.0
                elif score < thr and gold[label] < 0.5:
                    tn += 1
                elif score < thr and gold[label] >= 0.5:
                    fn += 1
        precision = tp / (tp + fp)
        recall = tp / (tp + fn)
        specificity = tn / (tn + fp)
        if (precision + recall) == 0:
            f_score = 0.0
        else:
            f_score = 2 * (precision * recall) / (precision + recall)
        return {"textcat_p": precision, "textcat_r": recall, "textcat_f": f_score, "textcat_s": specificity}


    def get_roc_auc(self, texts, cats, delta_thr=0.01, plot_roc=False):
        """
        Computes the receiver operating characteristic (ROC) area under curve (AUC) of the model using numerical
        integration (trapezoid method)

        :param texts: texts used for training metrics computation
        :type texts: list(str)
        :param cats: categories corresponding to the input texts used as gold standard to compute training metrics
        :type cats: list(str)
        :param delta_thr: prediction score threshold resolution used to compute the points of the ROC, defaults to 0.01
        :type delta_thr: float, optional
        :param plot_roc: flag indicating whether to plot (True) the ROC or not (False), defaults to False
        :type plot_roc: bool, optional

        :return: tuple containing the ROC AUC value and the :math:`x` and :math:`y` axes of the ROC
        :rtype: tuple(float, list, list)
        """
        raise NotImplementedError("Function implementation has been masked for TextClassifier.get_roc_auc")


    def get_auc_Nsq(self, texts, cats, s_frac=1.0):

        """
        Computes the receiver operating characteristic (ROC) area under curve (AUC) of the model using Algorithm 1 of
        reference “Fawcett, Tom. (2004). ROC Graphs: Notes and Practical Considerations for Researchers. Machine
        Learning. 31. 1-38.”, having a computational cost of :math:`O(N^2)` with :math:`N` being the test sample size

        :param texts: texts used for training metrics computation
        :type texts: list(str)
        :param cats: categories corresponding to the input texts used as gold standard to compute training metrics
        :type cats: list(str)
        :param s_frac: fraction of the total input test data used to compute the ROC AUC, defaults to 1.0
        :type s_frac: float, optional

        :return: ROC AUC value
        :rtype: float
        """

        pos = list()
        neg = list()
        s = int(s_frac*len(texts))
        for text, cat in random.sample(list(zip(texts, cats)), s):
            if cat["cats"]["POSITIVE_NUCL_MED"] == 1.0 and cat["cats"]["NEGATIVE_NUCL_MED"] == 0.0:
                pos.append(text)
            elif cat["cats"]["POSITIVE_NUCL_MED"] == 0.0 and cat["cats"]["NEGATIVE_NUCL_MED"] == 1.0:
                neg.append(text)
            else:
                continue

        N = len(pos) * len(neg)
        auc = 0.0
        for p in pos:
            doc_pos = self.model.tokenizer(p)
            doc_pos = self.textcat(doc_pos)
            for n in neg:
                doc_neg = self.model.tokenizer(n)
                doc_neg = self.textcat(doc_neg)
                if doc_pos.cats["POSITIVE_NUCL_MED"] >= doc_neg.cats["POSITIVE_NUCL_MED"]:
                    auc += 1.0
        auc /= N

        return auc


    def get_auc_Nlog2N(self, texts, cats):

        """
        Computes the receiver operating characteristic (ROC) area under curve (AUC) of the model using Algorithm 2 of
        reference “Fawcett, Tom. (2004). ROC Graphs: Notes and Practical Considerations for Researchers. Machine
        Learning. 31. 1-38.”, having a computational cost of :math:`O(N \\log N)` with :math:`N` being the test sample
        size

        :param texts: texts used for training metrics computation
        :type texts: list(str)
        :param cats: categories corresponding to the input texts used as gold standard to compute training metrics
        :type cats: list(str)

        :return: ROC AUC value
        :rtype: float
        """

        scores = [self.textcat(self.model.tokenizer(text)).cats["POSITIVE_NUCL_MED"] for text in texts]
        sort_idx = list(reversed([i[0] for i in sorted(enumerate(scores), key=lambda x: x[1])]))

        n_pos = 0
        n_neg = 0
        n_neg_eq = 0
        n_pos_eq = 0
        n_pos_gr = 0
        auc = 0
        for i, j in enumerate(sort_idx):

            if i != 0 and scores[j] == scores[sort_idx[i-1]]:
                if cats[sort_idx[i-1]]["cats"]["POSITIVE_NUCL_MED"] == 1.0 and cats[sort_idx[i-1]]["cats"]["NEGATIVE_NUCL_MED"] == 0.0:
                    n_pos_eq += 1
                elif cats[sort_idx[i-1]]["cats"]["POSITIVE_NUCL_MED"] == 0.0 and cats[sort_idx[i-1]]["cats"]["NEGATIVE_NUCL_MED"] == 1.0:
                    n_neg_eq += 1
            else:
                n_neg_eq = 0
                n_pos_eq = 0


Complete the function implementation replacing "raise NotImplementedError".