Ignore:
Timestamp:
02/07/12 21:42:52 (2 years ago)
Author:
anze <anze.staric@…>
Branch:
default
Parents:
10002:496d655f5c82 (diff), 9999:3b8b4cc606c0 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
rebase_source:
edfbfc3af42957c32d658743b187bedb4cfe865e
Message:

Merge

Files:
2 edited

Legend:

Unmodified
Added
Removed
  • Orange/evaluation/scoring.py

    r9999 r10003  
    66from Orange import statc, corn 
    77from Orange.misc import deprecated_keywords 
     8from Orange.evaluation import testing 
    89 
    910#### Private stuff 
     
    126127MAE = ME 
    127128 
     129 
     130class ConfusionMatrix: 
     131    """ 
     132    Classification result summary 
     133 
     134    .. attribute:: TP 
     135 
     136        True Positive predictions 
     137 
     138    .. attribute:: TN 
     139 
     140        True Negative predictions 
     141 
     142    .. attribute:: FP 
     143 
     144        False Positive predictions 
     145 
     146    .. attribute:: FN 
     147 
     148        False Negative predictions 
     149    """ 
     150    def __init__(self): 
     151        self.TP = self.FN = self.FP = self.TN = 0.0 
     152 
     153    @deprecated_keywords({"predictedPositive": "predicted_positive", 
     154                          "isPositive": "is_positive"}) 
     155    def addTFPosNeg(self, predicted_positive, is_positive, weight = 1.0): 
     156        """ 
     157        Update confusion matrix with result of a single classification 
     158 
     159        :param predicted_positive: positive class value was predicted 
     160        :param is_positive: correct class value is positive 
     161        :param weight: weight of the selected instance 
     162         """ 
     163        if predicted_positive: 
     164            if is_positive: 
     165                self.TP += weight 
     166            else: 
     167                self.FP += weight 
     168        else: 
     169            if is_positive: 
     170                self.FN += weight 
     171            else: 
     172                self.TN += weight 
     173 
     174 
    128175######################################################################### 
    129176# PERFORMANCE MEASURES: 
     
    305352# Scores for evaluation of classifiers 
    306353 
    307 @deprecated_keywords({"reportSE": "report_se"}) 
    308 def CA(test_results, report_se = False, **argkw): 
    309     """Return percentage of matches between predicted and actual class. 
    310  
    311     :param test_results: :obj:`~Orange.evaluation.testing.ExperimentResults` 
    312                          or :obj:`ConfusionMatrix`. 
    313     :param report_se: include standard error in result. 
    314     :rtype: list of scores, one for each learner. 
    315  
    316     Standard errors are estimated from deviation of CAs across folds (if 
    317     test_results were produced by cross_validation) or approximated under 
    318     the assumption of normal distribution otherwise. 
    319     """ 
    320     if isinstance(test_results, list) and len(test_results) > 0 \ 
    321                              and isinstance(test_results[0], ConfusionMatrix): 
    322         results = [] 
    323         for cm in test_results: 
    324             div = cm.TP+cm.FN+cm.FP+cm.TN 
    325             check_non_zero(div) 
    326             results.append((cm.TP+cm.TN)/div) 
    327         return results 
    328     elif test_results.number_of_iterations==1: 
     354class CAClass(object): 
     355    CONFUSION_MATRIX = 0 
     356    CONFUSION_MATRIX_LIST = 1 
     357    CLASSIFICATION = 2 
     358    CROSS_VALIDATION = 3 
     359 
     360    @deprecated_keywords({"reportSE": "report_se"}) 
     361    def __call__(self, test_results, report_se = False, unweighted=False): 
     362        """Return percentage of matches between predicted and actual class. 
     363 
     364        :param test_results: :obj:`~Orange.evaluation.testing.ExperimentResults` 
     365                             or :obj:`ConfusionMatrix`. 
     366        :param report_se: include standard error in result. 
     367        :rtype: list of scores, one for each learner. 
     368 
     369        Standard errors are estimated from deviation of CAs across folds (if 
     370        test_results were produced by cross_validation) or approximated under 
     371        the assumption of normal distribution otherwise. 
     372        """ 
     373        input_type = self.get_input_type(test_results) 
     374        if input_type == self.CONFUSION_MATRIX: 
     375            return self.from_confusion_matrix(test_results, report_se) 
     376        elif input_type == self.CONFUSION_MATRIX_LIST: 
     377            return self.from_confusion_matrix_list(test_results, report_se) 
     378        elif input_type == self.CLASSIFICATION: 
     379            return self.from_classification_results( 
     380                                        test_results, report_se, unweighted) 
     381        elif input_type == self.CROSS_VALIDATION: 
     382            return self.from_crossvalidation_results( 
     383                                        test_results, report_se, unweighted) 
     384 
     385    def from_confusion_matrix(self, cm, report_se): 
     386        all_predictions = cm.TP+cm.FN+cm.FP+cm.TN 
     387        check_non_zero(all_predictions) 
     388        ca = (cm.TP+cm.TN)/all_predictions 
     389 
     390        if report_se: 
     391            return ca, ca*(1-ca)/math.sqrt(all_predictions) 
     392        else: 
     393            return ca 
     394 
     395    def from_confusion_matrix_list(self, confusion_matrices, report_se): 
     396        return map(self.from_confusion_matrix, confusion_matrices) # TODO: report_se 
     397 
     398    def from_classification_results(self, test_results, report_se, unweighted): 
    329399        CAs = [0.0]*test_results.number_of_learners 
    330         if argkw.get("unweighted", 0) or not test_results.weights: 
    331             totweight = gettotsize(test_results) 
    332             for tex in test_results.results: 
    333                 CAs = map(lambda res, cls: res+(cls==tex.actual_class), CAs, tex.classes) 
    334         else: 
    335             totweight = 0. 
    336             for tex in test_results.results: 
    337                 CAs = map(lambda res, cls: res+(cls==tex.actual_class and tex.weight), CAs, tex.classes) 
    338                 totweight += tex.weight 
     400        totweight = 0. 
     401        for tex in test_results.results: 
     402            w = 1. if unweighted else tex.weight 
     403            CAs = map(lambda res, cls: res+(cls==tex.actual_class and w), CAs, tex.classes) 
     404            totweight += w 
    339405        check_non_zero(totweight) 
    340406        ca = [x/totweight for x in CAs] 
    341              
     407 
    342408        if report_se: 
    343409            return [(x, x*(1-x)/math.sqrt(totweight)) for x in ca] 
    344410        else: 
    345411            return ca 
    346          
    347     else: 
     412 
     413    def from_crossvalidation_results(self, test_results, report_se, unweighted): 
    348414        CAsByFold = [[0.0]*test_results.number_of_iterations for i in range(test_results.number_of_learners)] 
    349415        foldN = [0.0]*test_results.number_of_iterations 
    350416 
    351         if argkw.get("unweighted", 0) or not test_results.weights: 
    352             for tex in test_results.results: 
    353                 for lrn in range(test_results.number_of_learners): 
    354                     CAsByFold[lrn][tex.iteration_number] += (tex.classes[lrn]==tex.actual_class) 
    355                 foldN[tex.iteration_number] += 1 
    356         else: 
    357             for tex in test_results.results: 
    358                 for lrn in range(test_results.number_of_learners): 
    359                     CAsByFold[lrn][tex.iteration_number] += (tex.classes[lrn]==tex.actual_class) and tex.weight 
    360                 foldN[tex.iteration_number] += tex.weight 
     417        for tex in test_results.results: 
     418            w = 1. if unweighted else tex.weight 
     419            for lrn in range(test_results.number_of_learners): 
     420                CAsByFold[lrn][tex.iteration_number] += (tex.classes[lrn]==tex.actual_class) and w 
     421            foldN[tex.iteration_number] += w 
    361422 
    362423        return statistics_by_folds(CAsByFold, foldN, report_se, False) 
    363424 
    364  
    365 # Obsolete, but kept for compatibility 
    366 def CA_se(res, **argkw): 
    367     return CA(res, True, **argkw) 
     425    def get_input_type(self, test_results): 
     426        if isinstance(test_results, ConfusionMatrix): 
     427            return self.CONFUSION_MATRIX 
     428        elif isinstance(test_results, testing.ExperimentResults): 
     429            if test_results.number_of_iterations == 1: 
     430                return self.CLASSIFICATION 
     431            else: 
     432                return self.CROSS_VALIDATION 
     433        elif isinstance(test_results, list): 
     434            return self.CONFUSION_MATRIX_LIST 
     435 
     436 
     437 
     438CA = CAClass() 
    368439 
    369440@deprecated_keywords({"reportSE": "report_se"}) 
     
    554625    else: 
    555626        return apply(Friedman, (res, statistics), argkw) 
    556      
    557 class ConfusionMatrix: 
    558     """ 
    559     Classification result summary 
    560  
    561     .. attribute:: TP 
    562  
    563         True Positive predictions 
    564  
    565     .. attribute:: TN 
    566  
    567         True Negative predictions 
    568  
    569     .. attribute:: FP 
    570  
    571         False Positive predictions 
    572  
    573     .. attribute:: FN 
    574  
    575         False Negative predictions 
    576     """ 
    577     def __init__(self): 
    578         self.TP = self.FN = self.FP = self.TN = 0.0 
    579  
    580     @deprecated_keywords({"predictedPositive": "predicted_positive", 
    581                           "isPositive": "is_positive"}) 
    582     def addTFPosNeg(self, predicted_positive, is_positive, weight = 1.0): 
    583         """ 
    584         Update confusion matrix with result of a single classification 
    585  
    586         :param predicted_positive: positive class value was predicted 
    587         :param is_positive: correct class value is positive 
    588         :param weight: weight of the selected instance 
    589          """ 
    590         if predicted_positive: 
    591             if is_positive: 
    592                 self.TP += weight 
    593             else: 
    594                 self.FP += weight 
    595         else: 
    596             if is_positive: 
    597                 self.FN += weight 
    598             else: 
    599                 self.TN += weight 
     627 
    600628 
    601629@deprecated_keywords({"res": "test_results", 
     
    9801008@deprecated_keywords({"keepConcavities": "keep_concavities"}) 
    9811009def ROC_add_point(P, R, keep_concavities=1): 
    982     if keepConcavities: 
     1010    if keep_concavities: 
    9831011        R.append(P) 
    9841012    else: 
  • Orange/evaluation/scoring.py

    r9995 r10003  
    44 
    55import Orange 
    6 from Orange import statc 
     6from Orange import statc, corn 
    77from Orange.misc import deprecated_keywords 
     8from Orange.evaluation import testing 
    89 
    910#### Private stuff 
     
    126127MAE = ME 
    127128 
     129 
     130class ConfusionMatrix: 
     131    """ 
     132    Classification result summary 
     133 
     134    .. attribute:: TP 
     135 
     136        True Positive predictions 
     137 
     138    .. attribute:: TN 
     139 
     140        True Negative predictions 
     141 
     142    .. attribute:: FP 
     143 
     144        False Positive predictions 
     145 
     146    .. attribute:: FN 
     147 
     148        False Negative predictions 
     149    """ 
     150    def __init__(self): 
     151        self.TP = self.FN = self.FP = self.TN = 0.0 
     152 
     153    @deprecated_keywords({"predictedPositive": "predicted_positive", 
     154                          "isPositive": "is_positive"}) 
     155    def addTFPosNeg(self, predicted_positive, is_positive, weight = 1.0): 
     156        """ 
     157        Update confusion matrix with result of a single classification 
     158 
     159        :param predicted_positive: positive class value was predicted 
     160        :param is_positive: correct class value is positive 
     161        :param weight: weight of the selected instance 
     162         """ 
     163        if predicted_positive: 
     164            if is_positive: 
     165                self.TP += weight 
     166            else: 
     167                self.FP += weight 
     168        else: 
     169            if is_positive: 
     170                self.FN += weight 
     171            else: 
     172                self.TN += weight 
     173 
     174 
    128175######################################################################### 
    129176# PERFORMANCE MEASURES: 
     
    305352# Scores for evaluation of classifiers 
    306353 
    307 @deprecated_keywords({"reportSE": "report_se"}) 
    308 def CA(res, report_se = False, **argkw): 
    309     """ Computes classification accuracy, i.e. percentage of matches between 
    310     predicted and actual class. The function returns a list of classification 
    311     accuracies of all classifiers tested. If reportSE is set to true, the list 
    312     will contain tuples with accuracies and standard errors. 
    313      
    314     If results are from multiple repetitions of experiments (like those 
    315     returned by Orange.evaluation.testing.crossValidation or 
    316     Orange.evaluation.testing.proportionTest) the 
    317     standard error (SE) is estimated from deviation of classification 
    318     accuracy accross folds (SD), as SE = SD/sqrt(N), where N is number 
    319     of repetitions (e.g. number of folds). 
    320      
    321     If results are from a single repetition, we assume independency of 
    322     instances and treat the classification accuracy as distributed according 
    323     to binomial distribution. This can be approximated by normal distribution, 
    324     so we report the SE of sqrt(CA*(1-CA)/N), where CA is classification 
    325     accuracy and N is number of test instances. 
    326      
    327     Instead of ExperimentResults, this function can be given a list of 
    328     confusion matrices (see below). Standard errors are in this case 
    329     estimated using the latter method. 
    330     """ 
    331     if res.number_of_iterations==1: 
    332         if type(res)==ConfusionMatrix: 
    333             div = nm.TP+nm.FN+nm.FP+nm.TN 
    334             check_non_zero(div) 
    335             ca = [(nm.TP+nm.TN)/div] 
    336         else: 
    337             CAs = [0.0]*res.number_of_learners 
    338             if argkw.get("unweighted", 0) or not res.weights: 
    339                 totweight = gettotsize(res) 
    340                 for tex in res.results: 
    341                     CAs = map(lambda res, cls: res+(cls==tex.actual_class), CAs, tex.classes) 
    342             else: 
    343                 totweight = 0. 
    344                 for tex in res.results: 
    345                     CAs = map(lambda res, cls: res+(cls==tex.actual_class and tex.weight), CAs, tex.classes) 
    346                     totweight += tex.weight 
    347             check_non_zero(totweight) 
    348             ca = [x/totweight for x in CAs] 
    349              
     354class CAClass(object): 
     355    CONFUSION_MATRIX = 0 
     356    CONFUSION_MATRIX_LIST = 1 
     357    CLASSIFICATION = 2 
     358    CROSS_VALIDATION = 3 
     359 
     360    @deprecated_keywords({"reportSE": "report_se"}) 
     361    def __call__(self, test_results, report_se = False, unweighted=False): 
     362        """Return percentage of matches between predicted and actual class. 
     363 
     364        :param test_results: :obj:`~Orange.evaluation.testing.ExperimentResults` 
     365                             or :obj:`ConfusionMatrix`. 
     366        :param report_se: include standard error in result. 
     367        :rtype: list of scores, one for each learner. 
     368 
     369        Standard errors are estimated from deviation of CAs across folds (if 
     370        test_results were produced by cross_validation) or approximated under 
     371        the assumption of normal distribution otherwise. 
     372        """ 
     373        input_type = self.get_input_type(test_results) 
     374        if input_type == self.CONFUSION_MATRIX: 
     375            return self.from_confusion_matrix(test_results, report_se) 
     376        elif input_type == self.CONFUSION_MATRIX_LIST: 
     377            return self.from_confusion_matrix_list(test_results, report_se) 
     378        elif input_type == self.CLASSIFICATION: 
     379            return self.from_classification_results( 
     380                                        test_results, report_se, unweighted) 
     381        elif input_type == self.CROSS_VALIDATION: 
     382            return self.from_crossvalidation_results( 
     383                                        test_results, report_se, unweighted) 
     384 
     385    def from_confusion_matrix(self, cm, report_se): 
     386        all_predictions = cm.TP+cm.FN+cm.FP+cm.TN 
     387        check_non_zero(all_predictions) 
     388        ca = (cm.TP+cm.TN)/all_predictions 
     389 
     390        if report_se: 
     391            return ca, ca*(1-ca)/math.sqrt(all_predictions) 
     392        else: 
     393            return ca 
     394 
     395    def from_confusion_matrix_list(self, confusion_matrices, report_se): 
     396        return map(self.from_confusion_matrix, confusion_matrices) # TODO: report_se 
     397 
     398    def from_classification_results(self, test_results, report_se, unweighted): 
     399        CAs = [0.0]*test_results.number_of_learners 
     400        totweight = 0. 
     401        for tex in test_results.results: 
     402            w = 1. if unweighted else tex.weight 
     403            CAs = map(lambda res, cls: res+(cls==tex.actual_class and w), CAs, tex.classes) 
     404            totweight += w 
     405        check_non_zero(totweight) 
     406        ca = [x/totweight for x in CAs] 
     407 
    350408        if report_se: 
    351409            return [(x, x*(1-x)/math.sqrt(totweight)) for x in ca] 
    352410        else: 
    353411            return ca 
    354          
    355     else: 
    356         CAsByFold = [[0.0]*res.number_of_iterations for i in range(res.number_of_learners)] 
    357         foldN = [0.0]*res.number_of_iterations 
    358  
    359         if argkw.get("unweighted", 0) or not res.weights: 
    360             for tex in res.results: 
    361                 for lrn in range(res.number_of_learners): 
    362                     CAsByFold[lrn][tex.iteration_number] += (tex.classes[lrn]==tex.actual_class) 
    363                 foldN[tex.iteration_number] += 1 
    364         else: 
    365             for tex in res.results: 
    366                 for lrn in range(res.number_of_learners): 
    367                     CAsByFold[lrn][tex.iteration_number] += (tex.classes[lrn]==tex.actual_class) and tex.weight 
    368                 foldN[tex.iteration_number] += tex.weight 
     412 
     413    def from_crossvalidation_results(self, test_results, report_se, unweighted): 
     414        CAsByFold = [[0.0]*test_results.number_of_iterations for i in range(test_results.number_of_learners)] 
     415        foldN = [0.0]*test_results.number_of_iterations 
     416 
     417        for tex in test_results.results: 
     418            w = 1. if unweighted else tex.weight 
     419            for lrn in range(test_results.number_of_learners): 
     420                CAsByFold[lrn][tex.iteration_number] += (tex.classes[lrn]==tex.actual_class) and w 
     421            foldN[tex.iteration_number] += w 
    369422 
    370423        return statistics_by_folds(CAsByFold, foldN, report_se, False) 
    371424 
    372  
    373 # Obsolete, but kept for compatibility 
    374 def CA_se(res, **argkw): 
    375     return CA(res, True, **argkw) 
     425    def get_input_type(self, test_results): 
     426        if isinstance(test_results, ConfusionMatrix): 
     427            return self.CONFUSION_MATRIX 
     428        elif isinstance(test_results, testing.ExperimentResults): 
     429            if test_results.number_of_iterations == 1: 
     430                return self.CLASSIFICATION 
     431            else: 
     432                return self.CROSS_VALIDATION 
     433        elif isinstance(test_results, list): 
     434            return self.CONFUSION_MATRIX_LIST 
     435 
     436 
     437 
     438CA = CAClass() 
    376439 
    377440@deprecated_keywords({"reportSE": "report_se"}) 
     
    562625    else: 
    563626        return apply(Friedman, (res, statistics), argkw) 
    564      
    565 class ConfusionMatrix: 
    566     """ Class ConfusionMatrix stores data about false and true 
    567     predictions compared to real class. It stores the number of 
    568     True Negatives, False Positive, False Negatives and True Positives. 
    569     """ 
    570     def __init__(self): 
    571         self.TP = self.FN = self.FP = self.TN = 0.0 
    572  
    573     def addTFPosNeg(self, predictedPositive, isPositive, weight = 1.0): 
    574         if predictedPositive: 
    575             if isPositive: 
    576                 self.TP += weight 
    577             else: 
    578                 self.FP += weight 
    579         else: 
    580             if isPositive: 
    581                 self.FN += weight 
    582             else: 
    583                 self.TN += weight 
    584  
    585  
    586 @deprecated_keywords({"classIndex": "class_index"}) 
    587 def confusion_matrices(res, class_index=-1, **argkw): 
    588     """ This function can compute two different forms of confusion matrix: 
    589     one in which a certain class is marked as positive and the other(s) 
    590     negative, and another in which no class is singled out. The way to 
    591     specify what we want is somewhat confusing due to backward 
    592     compatibility issues. 
    593     """ 
    594     tfpns = [ConfusionMatrix() for i in range(res.number_of_learners)] 
     627 
     628 
     629@deprecated_keywords({"res": "test_results", 
     630                      "classIndex": "class_index"}) 
     631def confusion_matrices(test_results, class_index=1, 
     632                       unweighted=False, cutoff=.5): 
     633    """ 
     634    Return confusion matrices for test_results. 
     635 
     636    :param test_results: test results 
     637    :param class_index: index of class value for which the confusion matrices 
     638                        are to be computed. 
     639    :param unweighted: ignore instance weights. 
     640    :params cutoff: cutoff for probability 
     641 
     642    :rtype: list of :obj:`ConfusionMatrix` 
     643    """ 
     644    tfpns = [ConfusionMatrix() for i in range(test_results.number_of_learners)] 
    595645     
    596646    if class_index<0: 
    597         numberOfClasses = len(res.class_values) 
     647        numberOfClasses = len(test_results.class_values) 
    598648        if class_index < -1 or numberOfClasses > 2: 
    599             cm = [[[0.0] * numberOfClasses for i in range(numberOfClasses)] for l in range(res.number_of_learners)] 
    600             if argkw.get("unweighted", 0) or not res.weights: 
    601                 for tex in res.results: 
     649            cm = [[[0.0] * numberOfClasses for i in range(numberOfClasses)] for l in range(test_results.number_of_learners)] 
     650            if unweighted or not test_results.weights: 
     651                for tex in test_results.results: 
    602652                    trueClass = int(tex.actual_class) 
    603653                    for li, pred in enumerate(tex.classes): 
     
    606656                            cm[li][trueClass][predClass] += 1 
    607657            else: 
    608                 for tex in enumerate(res.results): 
     658                for tex in enumerate(test_results.results): 
    609659                    trueClass = int(tex.actual_class) 
    610660                    for li, pred in tex.classes: 
     
    614664            return cm 
    615665             
    616         elif res.baseClass>=0: 
    617             class_index = res.baseClass 
     666        elif test_results.baseClass>=0: 
     667            class_index = test_results.baseClass 
    618668        else: 
    619669            class_index = 1 
    620              
    621     cutoff = argkw.get("cutoff") 
    622     if cutoff: 
    623         if argkw.get("unweighted", 0) or not res.weights: 
    624             for lr in res.results: 
     670 
     671    if cutoff != .5: 
     672        if unweighted or not test_results.weights: 
     673            for lr in test_results.results: 
    625674                isPositive=(lr.actual_class==class_index) 
    626                 for i in range(res.number_of_learners): 
     675                for i in range(test_results.number_of_learners): 
    627676                    tfpns[i].addTFPosNeg(lr.probabilities[i][class_index]>cutoff, isPositive) 
    628677        else: 
    629             for lr in res.results: 
     678            for lr in test_results.results: 
    630679                isPositive=(lr.actual_class==class_index) 
    631                 for i in range(res.number_of_learners): 
     680                for i in range(test_results.number_of_learners): 
    632681                    tfpns[i].addTFPosNeg(lr.probabilities[i][class_index]>cutoff, isPositive, lr.weight) 
    633682    else: 
    634         if argkw.get("unweighted", 0) or not res.weights: 
    635             for lr in res.results: 
     683        if unweighted or not test_results.weights: 
     684            for lr in test_results.results: 
    636685                isPositive=(lr.actual_class==class_index) 
    637                 for i in range(res.number_of_learners): 
     686                for i in range(test_results.number_of_learners): 
    638687                    tfpns[i].addTFPosNeg(lr.classes[i]==class_index, isPositive) 
    639688        else: 
    640             for lr in res.results: 
     689            for lr in test_results.results: 
    641690                isPositive=(lr.actual_class==class_index) 
    642                 for i in range(res.number_of_learners): 
     691                for i in range(test_results.number_of_learners): 
    643692                    tfpns[i].addTFPosNeg(lr.classes[i]==class_index, isPositive, lr.weight) 
    644693    return tfpns 
     
    651700@deprecated_keywords({"confusionMatrix": "confusion_matrix"}) 
    652701def confusion_chi_square(confusion_matrix): 
     702    """ 
     703    Return chi square statistic of the confusion matrix 
     704    (higher value indicates that prediction is not by chance). 
     705    """ 
     706    if isinstance(confusion_matrix, ConfusionMatrix) or \ 
     707       not isinstance(confusion_matrix[1], list): 
     708        return _confusion_chi_square(confusion_matrix) 
     709    else: 
     710        return map(_confusion_chi_square, confusion_matrix) 
     711 
     712def _confusion_chi_square(confusion_matrix): 
     713    if isinstance(confusion_matrix, ConfusionMatrix): 
     714        c = confusion_matrix 
     715        confusion_matrix = [[c.TP, c.FN], [c.FP, c.TN]] 
    653716    dim = len(confusion_matrix) 
    654717    rowPriors = [sum(r) for r in confusion_matrix] 
    655     colPriors = [sum([r[i] for r in confusion_matrix]) for i in range(dim)] 
     718    colPriors = [sum(r[i] for r in confusion_matrix) for i in range(dim)] 
    656719    total = sum(rowPriors) 
    657720    rowPriors = [r/total for r in rowPriors] 
     
    666729    df = (dim-1)**2 
    667730    return ss, df, statc.chisqprob(ss, df) 
    668          
    669      
    670 def sens(confm): 
    671     """Return sensitivity (recall rate) over the given confusion matrix.""" 
    672     if type(confm) == list: 
    673         return [sens(cm) for cm in confm] 
    674     else: 
    675         tot = confm.TP+confm.FN 
     731 
     732@deprecated_keywords({"confm": "confusion_matrix"}) 
     733def sens(confusion_matrix): 
     734    """ 
     735    Return `sensitivity <http://en.wikipedia.org/wiki/Sensitivity_and_specificity>`_ 
     736    (proportion of actual positives which are correctly identified as such). 
     737    """ 
     738    if type(confusion_matrix) == list: 
     739        return [sens(cm) for cm in confusion_matrix] 
     740    else: 
     741        tot = confusion_matrix.TP+confusion_matrix.FN 
    676742        if tot < 1e-6: 
    677743            import warnings 
     
    679745            return -1 
    680746 
    681         return confm.TP/tot 
    682  
    683 def recall(confm): 
    684     """Return recall rate (sensitivity) over the given confusion matrix.""" 
    685     return sens(confm) 
    686  
    687  
    688 def spec(confm): 
    689     """Return specificity over the given confusion matrix.""" 
    690     if type(confm) == list: 
    691         return [spec(cm) for cm in confm] 
    692     else: 
    693         tot = confm.FP+confm.TN 
     747        return confusion_matrix.TP/tot 
     748 
     749 
     750@deprecated_keywords({"confm": "confusion_matrix"}) 
     751def recall(confusion_matrix): 
     752    """ 
     753    Return `recall <http://en.wikipedia.org/wiki/Precision_and_recall>`_ 
     754    (fraction of relevant instances that are retrieved). 
     755    """ 
     756    return sens(confusion_matrix) 
     757 
     758 
     759@deprecated_keywords({"confm": "confusion_matrix"}) 
     760def spec(confusion_matrix): 
     761    """ 
     762    Return `specificity <http://en.wikipedia.org/wiki/Sensitivity_and_specificity>`_ 
     763    (proportion of negatives which are correctly identified). 
     764    """ 
     765    if type(confusion_matrix) == list: 
     766        return [spec(cm) for cm in confusion_matrix] 
     767    else: 
     768        tot = confusion_matrix.FP+confusion_matrix.TN 
    694769        if tot < 1e-6: 
    695770            import warnings 
    696771            warnings.warn("Can't compute specificity: one or both classes have no instances") 
    697772            return -1 
    698         return confm.TN/tot 
    699    
    700  
    701 def PPV(confm): 
    702     """Return positive predictive value (precision rate) over the given confusion matrix.""" 
    703     if type(confm) == list: 
    704         return [PPV(cm) for cm in confm] 
    705     else: 
    706         tot = confm.TP+confm.FP 
     773        return confusion_matrix.TN/tot 
     774 
     775 
     776@deprecated_keywords({"confm": "confusion_matrix"}) 
     777def PPV(confusion_matrix): 
     778    """ 
     779    Return `positive predictive value <http://en.wikipedia.org/wiki/Positive_predictive_value>`_ 
     780    (proportion of subjects with positive test results who are correctly diagnosed).""" 
     781    if type(confusion_matrix) == list: 
     782        return [PPV(cm) for cm in confusion_matrix] 
     783    else: 
     784        tot = confusion_matrix.TP+confusion_matrix.FP 
    707785        if tot < 1e-6: 
    708786            import warnings 
    709787            warnings.warn("Can't compute PPV: one or both classes have no instances") 
    710788            return -1 
    711         return confm.TP/tot 
    712  
    713  
    714 def precision(confm): 
    715     """Return precision rate (positive predictive value) over the given confusion matrix.""" 
    716     return PPV(confm) 
    717  
    718  
    719 def NPV(confm): 
    720     """Return negative predictive value over the given confusion matrix.""" 
    721     if type(confm) == list: 
    722         return [NPV(cm) for cm in confm] 
    723     else: 
    724         tot = confm.FN+confm.TN 
     789        return confusion_matrix.TP/tot 
     790 
     791 
     792@deprecated_keywords({"confm": "confusion_matrix"}) 
     793def precision(confusion_matrix): 
     794    """ 
     795    Return `precision <http://en.wikipedia.org/wiki/Precision_and_recall>`_ 
     796    (retrieved instances that are relevant). 
     797    """ 
     798    return PPV(confusion_matrix) 
     799 
     800@deprecated_keywords({"confm": "confusion_matrix"}) 
     801def NPV(confusion_matrix): 
     802    """Return `negative predictive value <http://en.wikipedia.org/wiki/Negative_predictive_value>`_ 
     803     (proportion of subjects with a negative test result who are correctly 
     804     diagnosed). 
     805     """ 
     806    if type(confusion_matrix) == list: 
     807        return [NPV(cm) for cm in confusion_matrix] 
     808    else: 
     809        tot = confusion_matrix.FN+confusion_matrix.TN 
    725810        if tot < 1e-6: 
    726811            import warnings 
    727812            warnings.warn("Can't compute NPV: one or both classes have no instances") 
    728813            return -1 
    729         return confm.TN/tot 
    730  
    731 def F1(confm): 
    732     """Return F1 score (harmonic mean of precision and recall) over the given confusion matrix.""" 
    733     if type(confm) == list: 
    734         return [F1(cm) for cm in confm] 
    735     else: 
    736         p = precision(confm) 
    737         r = recall(confm) 
     814        return confusion_matrix.TN/tot 
     815 
     816@deprecated_keywords({"confm": "confusion_matrix"}) 
     817def F1(confusion_matrix): 
     818    """Return `F1 score <http://en.wikipedia.org/wiki/F1_score>`_ 
     819    (harmonic mean of precision and recall).""" 
     820    if type(confusion_matrix) == list: 
     821        return [F1(cm) for cm in confusion_matrix] 
     822    else: 
     823        p = precision(confusion_matrix) 
     824        r = recall(confusion_matrix) 
    738825        if p + r > 0: 
    739826            return 2. * p * r / (p + r) 
     
    743830            return -1 
    744831 
    745 def Falpha(confm, alpha=1.0): 
     832 
     833@deprecated_keywords({"confm": "confusion_matrix"}) 
     834def Falpha(confusion_matrix, alpha=1.0): 
    746835    """Return the alpha-mean of precision and recall over the given confusion matrix.""" 
    747     if type(confm) == list: 
    748         return [Falpha(cm, alpha=alpha) for cm in confm] 
    749     else: 
    750         p = precision(confm) 
    751         r = recall(confm) 
     836    if type(confusion_matrix) == list: 
     837        return [Falpha(cm, alpha=alpha) for cm in confusion_matrix] 
     838    else: 
     839        p = precision(confusion_matrix) 
     840        r = recall(confusion_matrix) 
    752841        return (1. + alpha) * p * r / (alpha * p + r) 
    753      
    754 def MCC(confm): 
    755     ''' 
    756     Return Mattew correlation coefficient over the given confusion matrix. 
    757  
    758     MCC is calculated as follows: 
    759     MCC = (TP*TN - FP*FN) / sqrt( (TP+FP)*(TP+FN)*(TN+FP)*(TN+FN) ) 
    760      
    761     [1] Matthews, B.W., Comparison of the predicted and observed secondary  
    762     structure of T4 phage lysozyme. Biochim. Biophys. Acta 1975, 405, 442-451 
    763  
    764     code by Boris Gorelik 
    765     ''' 
    766     if type(confm) == list: 
    767         return [MCC(cm) for cm in confm] 
    768     else: 
    769         truePositive = confm.TP 
    770         trueNegative = confm.TN 
    771         falsePositive = confm.FP 
    772         falseNegative = confm.FN  
     842 
     843 
     844@deprecated_keywords({"confm": "confusion_matrix"}) 
     845def MCC(confusion_matrix): 
     846    """ 
     847    Return `Matthew correlation coefficient <http://en.wikipedia.org/wiki/Matthews_correlation_coefficient>`_ 
     848    (correlation coefficient between the observed and predicted binary classifications) 
     849    """ 
     850    # code by Boris Gorelik 
     851    if type(confusion_matrix) == list: 
     852        return [MCC(cm) for cm in confusion_matrix] 
     853    else: 
     854        truePositive = confusion_matrix.TP 
     855        trueNegative = confusion_matrix.TN 
     856        falsePositive = confusion_matrix.FP 
     857        falseNegative = confusion_matrix.FN 
    773858           
    774859        try:    
     
    791876 
    792877@deprecated_keywords({"bIsListOfMatrices": "b_is_list_of_matrices"}) 
    793 def scotts_pi(confm, b_is_list_of_matrices=True): 
     878def scotts_pi(confusion_matrix, b_is_list_of_matrices=True): 
    794879   """Compute Scott's Pi for measuring inter-rater agreement for nominal data 
    795880 
     
    798883   raters. 
    799884 
    800    @param confm: confusion matrix, or list of confusion matrices. To obtain 
     885   @param confusion_matrix: confusion matrix, or list of confusion matrices. To obtain 
    801886                           non-binary confusion matrix, call 
    802887                           Orange.evaluation.scoring.compute_confusion_matrices and set the 
     
    811896   if b_is_list_of_matrices: 
    812897       try: 
    813            return [scotts_pi(cm, b_is_list_of_matrices=False) for cm in confm] 
     898           return [scotts_pi(cm, b_is_list_of_matrices=False) for cm in confusion_matrix] 
    814899       except TypeError: 
    815900           # Nevermind the parameter, maybe this is a "conventional" binary 
    816901           # confusion matrix and bIsListOfMatrices was specified by mistake 
    817            return scottsPiSingle(confm, bIsListOfMatrices=False) 
     902           return scottsPiSingle(confusion_matrix, bIsListOfMatrices=False) 
    818903   else: 
    819        if isinstance(confm, ConfusionMatrix): 
    820            confm = numpy.array( [[confm.TP, confm.FN], 
    821                    [confm.FP, confm.TN]], dtype=float) 
     904       if isinstance(confusion_matrix, ConfusionMatrix): 
     905           confusion_matrix = numpy.array( [[confusion_matrix.TP, confusion_matrix.FN], 
     906                   [confusion_matrix.FP, confusion_matrix.TN]], dtype=float) 
    822907       else: 
    823            confm = numpy.array(confm, dtype=float) 
    824  
    825        marginalSumOfRows = numpy.sum(confm, axis=0) 
    826        marginalSumOfColumns = numpy.sum(confm, axis=1) 
     908           confusion_matrix = numpy.array(confusion_matrix, dtype=float) 
     909 
     910       marginalSumOfRows = numpy.sum(confusion_matrix, axis=0) 
     911       marginalSumOfColumns = numpy.sum(confusion_matrix, axis=1) 
    827912       jointProportion = (marginalSumOfColumns + marginalSumOfRows)/ \ 
    828                            (2.0 * numpy.sum(confm, axis=None)) 
     913                           (2.0 * numpy.sum(confusion_matrix, axis=None)) 
    829914       # In the eq. above, 2.0 is what the Wikipedia page calls 
    830915       # the number of annotators. Here we have two annotators: 
     
    833918 
    834919       prExpected = numpy.sum(jointProportion ** 2, axis=None) 
    835        prActual = numpy.sum(numpy.diag(confm), axis=None)/numpy.sum(confm, axis=None) 
     920       prActual = numpy.sum(numpy.diag(confusion_matrix), axis=None)/numpy.sum(confusion_matrix, axis=None) 
    836921 
    837922       ret = (prActual - prExpected) / (1.0 - prExpected) 
     
    846931    tuples (aROC, standard error). 
    847932    """ 
    848     import corn 
    849933    useweights = res.weights and not argkw.get("unweighted", 0) 
    850934    problists, tots = corn.computeROCCumulative(res, class_index, useweights) 
     
    879963@deprecated_keywords({"classIndex": "class_index"}) 
    880964def compare_2_AUCs(res, lrn1, lrn2, class_index=-1, **argkw): 
    881     import corn 
    882965    return corn.compare2ROCs(res, lrn1, lrn2, class_index, res.weights and not argkw.get("unweighted")) 
    883966 
     
    890973    1-specificity and y is sensitivity. 
    891974    """ 
    892     import corn 
    893975    problists, tots = corn.computeROCCumulative(res, class_index) 
    894976 
     
    9461028                      "keepConcavities": "keep_concavities"}) 
    9471029def TC_compute_ROC(res, class_index=-1, keep_concavities=1): 
    948     import corn 
    9491030    problists, tots = corn.computeROCCumulative(res, class_index) 
    9501031 
     
    11711252@deprecated_keywords({"classIndex": "class_index"}) 
    11721253def compute_calibration_curve(res, class_index=-1): 
    1173     import corn 
    11741254    ## merge multiple iterations into one 
    11751255    mres = Orange.evaluation.testing.ExperimentResults(1, res.classifier_names, res.class_values, res.weights, classifiers=res.classifiers, loaded=res.loaded, test_type=res.test_type, labels=res.labels) 
     
    12341314@deprecated_keywords({"classIndex": "class_index"}) 
    12351315def compute_lift_curve(res, class_index=-1): 
    1236     import corn 
    12371316    ## merge multiple iterations into one 
    12381317    mres = Orange.evaluation.testing.ExperimentResults(1, res.classifier_names, res.class_values, res.weights, classifiers=res.classifiers, loaded=res.loaded, test_type=res.test_type, labels=res.labels) 
     
    12711350def compute_CDT(res, class_index=-1, **argkw): 
    12721351    """Obsolete, don't use""" 
    1273     import corn 
    12741352    if class_index<0: 
    12751353        if res.baseClass>=0: 
     
    13611439                      "divideByIfIte": "divide_by_if_ite"}) 
    13621440def AUC_ij(ite, class_index1, class_index2, use_weights = True, all_ite = None, divide_by_if_ite = 1.0): 
    1363     import corn 
    13641441    return AUC_x(corn.computeCDTPair, ite, all_ite, divide_by_if_ite, (class_index1, class_index2, use_weights)) 
    13651442 
     
    13691446                      "useWeights": "use_weights", 
    13701447                      "divideByIfIte": "divide_by_if_ite"}) 
    1371 def AUC_i(ite, class_index, use_weights = True, all_ite = None, divide_by_if_ite = 1.0): 
    1372     import corn 
     1448def AUC_i(ite, class_index, use_weights = True, all_ite = None, 
     1449          divide_by_if_ite = 1.0): 
    13731450    return AUC_x(corn.computeCDT, ite, all_ite, divide_by_if_ite, (class_index, use_weights)) 
    13741451 
Note: See TracChangeset for help on using the changeset viewer.