Changeset 10235:07e7c0cb8742 in orange


Ignore:
Timestamp:
02/15/12 12:06:36 (2 years ago)
Author:
anzeh <anze.staric@…>
Branch:
default
rebase_source:
f43a9ba81c78f7b8b77062a4da94d4d661b10bcd
Message:

Refactored AUC score and added unit tests for it.

Location:
Orange
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/evaluation/scoring.py

    r10231 r10235  
    13901390    return wrapped 
    13911391 
    1392 class AucClass(object): 
     1392class AUC(list): 
    13931393    ByWeightedPairs = 0 
    13941394    ByPairs = 1 
     
    13971397 
    13981398    @replace_use_weights 
    1399     def __call__(self, test_results, method=0, ignore_weights=False): 
     1399    def __init__(self, test_results=None, method=0, ignore_weights=False): 
    14001400        """ 
    14011401        Return the area under ROC curve given a set of experimental results. 
     
    14091409        :param method: DEPRECATED, call the appropriate method directly. 
    14101410        """ 
     1411        super(AUC, self).__init__() 
     1412 
     1413        self.ignore_weights=ignore_weights 
     1414        self.method=method 
     1415 
     1416        if test_results is not None: 
     1417            self.__call__(test_results) 
     1418 
     1419    def __call__(self, test_results): 
    14111420        if len(test_results.class_values) < 2: 
    14121421            raise ValueError("Cannot compute AUC on a single-class problem") 
    14131422        elif len(test_results.class_values) == 2: 
    1414             return self._compute_for_binary_class(test_results, ignore_weights) 
    1415         else: 
    1416             return self._compute_for_multi_value_class(test_results, ignore_weights, method) 
    1417  
    1418     def by_weighted_pairs(self, res, ignore_weights=False): 
     1423            self._compute_for_binary_class(test_results) 
     1424        else: 
     1425            self._compute_for_multi_value_class(test_results, self.method) 
     1426 
     1427    @classmethod 
     1428    def by_weighted_pairs(cls, res, ignore_weights=False): 
    14191429        """ 
    14201430        Compute AUC for each pair of classes (ignoring instances of all other 
     
    14271437        instances came). 
    14281438        """ 
    1429         return self._compute_for_multi_value_class(res, ignore_weights, 
    1430             method=self.ByWeightedPairs) 
    1431  
    1432     def by_pairs(self, res, ignore_weights=False): 
     1439        auc = AUC(ignore_weights=ignore_weights) 
     1440        auc._compute_for_multi_value_class(res, method=cls.ByWeightedPairs) 
     1441        return auc 
     1442 
     1443    @classmethod 
     1444    def by_pairs(cls, res, ignore_weights=False): 
    14331445        """ 
    14341446        Similar as above, except that the average over class pairs is not 
     
    14361448        distributions, but it is not related to concordance index any more. 
    14371449        """ 
    1438         return self._compute_for_multi_value_class(res, ignore_weights, 
    1439             method=self.ByPairs) 
    1440  
    1441     # Computes AUC; in multivalued class problem, AUC is computed as one against all 
    1442     # Results over folds are averages; if some folds examples from one class only, the folds are merged 
    1443     @replace_use_weights 
    1444     @deprecated_keywords({"classIndex": "class_index"}) 
    1445     def single_class(self, res, class_index=-1, ignore_weights=False): 
    1446         """ 
    1447         Compute AUC where the class with the given class_index is singled 
    1448         out and all other classes are treated as a single class. 
    1449         """ 
    1450         if class_index<0: 
    1451             if res.baseClass>=0: 
    1452                 class_index = res.baseClass 
    1453             else: 
    1454                 class_index = 1 
    1455  
    1456         if res.number_of_iterations > 1: 
    1457             return AUC_iterations(AUC_i, split_by_iterations(res), 
    1458                 (class_index, not ignore_weights, res, res.number_of_iterations)) 
    1459         else: 
    1460             return AUC_i( res, class_index, ignore_weights)[0] 
    1461  
    1462     # Computes AUC for a pair of classes (as if there were no other classes) 
    1463     # Results over folds are averages; if some folds have examples from one class only, the folds are merged 
    1464     def pair(self, res, class_index1, class_index2, ignore_weights=False): 
    1465         """ 
    1466         Computes AUC between a pair of classes, ignoring instances from all 
    1467         other classes. 
    1468         """ 
    1469         if res.number_of_iterations > 1: 
    1470             return AUC_iterations(AUC_ij, split_by_iterations(res), 
    1471                 (class_index1, class_index2, not ignore_weights, res, res.number_of_iterations)) 
    1472         else: 
    1473             return AUC_ij(res, class_index1, class_index2, ignore_weights) 
    1474  
    1475     def matrix(self, res, ignore_weights=False): 
    1476         """ 
    1477         Compute a (lower diagonal) matrix with AUCs for all pairs of classes. 
    1478         If there are empty classes, the corresponding elements in the matrix 
    1479         are -1. 
    1480         """ 
    1481         numberOfClasses = len(res.class_values) 
    1482         number_of_learners = res.number_of_learners 
    1483  
    1484         if res.number_of_iterations > 1: 
    1485             iterations, all_ite = split_by_iterations(res), res 
    1486         else: 
    1487             iterations, all_ite = [res], None 
    1488  
    1489         aucs = [[[] for _ in range(numberOfClasses)] for _ in range(number_of_learners)] 
    1490  
    1491         for classIndex1 in range(numberOfClasses): 
    1492             for classIndex2 in range(classIndex1): 
    1493                 pair_aucs = AUC_iterations(AUC_ij, iterations, (classIndex1, 
    1494                                                                 classIndex2, not ignore_weights, 
    1495                                                                 all_ite, res.number_of_iterations)) 
    1496                 if pair_aucs: 
    1497                     for lrn in range(number_of_learners): 
    1498                         aucs[lrn][classIndex1].append(pair_aucs[lrn]) 
    1499                 else: 
    1500                     for lrn in range(number_of_learners): 
    1501                         aucs[lrn][classIndex1].append(-1) 
    1502         return aucs 
    1503  
    1504     def weighted_one_against_all(self, res, ignore_weights=False): 
     1450        auc = AUC(ignore_weights=ignore_weights) 
     1451        auc._compute_for_multi_value_class(res, method=cls.ByPairs) 
     1452        return auc 
     1453 
     1454    @classmethod 
     1455    def weighted_one_against_all(cls, res, ignore_weights=False): 
    15051456        """ 
    15061457        For each class, it computes AUC for this class against all others (that 
     
    15121463        distributions. 
    15131464        """ 
    1514         return self._compute_for_multi_value_class(res, ignore_weights, 
    1515             method=self.WeightedOneAgainstAll) 
    1516  
    1517     def one_against_all(self, res, ignore_weights=False): 
     1465        auc = AUC(ignore_weights=ignore_weights) 
     1466        auc._compute_for_multi_value_class(res, 
     1467            method=cls.WeightedOneAgainstAll) 
     1468        return auc 
     1469 
     1470    @classmethod 
     1471    def one_against_all(cls, res, ignore_weights=False): 
    15181472        """As above, except that the average is not weighted.""" 
    1519         return self._compute_for_multi_value_class(res, ignore_weights, 
    1520             method=self.OneAgainstAll) 
    1521  
    1522     def _compute_for_binary_class(self, res, ignore_weights=False): 
     1473        auc = AUC(ignore_weights=ignore_weights) 
     1474        auc._compute_for_multi_value_class(res, method=cls.OneAgainstAll) 
     1475        return auc 
     1476 
     1477    @classmethod 
     1478    def single_class(cls, res, class_index=-1, ignore_weights=False): 
     1479        """ 
     1480        Compute AUC where the class with the given class_index is singled 
     1481        out and all other classes are treated as a single class. 
     1482        """ 
     1483        if class_index<0: 
     1484            if res.baseClass>=0: 
     1485                class_index = res.baseClass 
     1486            else: 
     1487                class_index = 1 
     1488 
     1489        auc = AUC(ignore_weights=ignore_weights) 
     1490        auc._compute_for_single_class(res, class_index) 
     1491        return auc 
     1492 
     1493    @classmethod 
     1494    def pair(cls, res, class_index1, class_index2, ignore_weights=False): 
     1495        """ 
     1496        Computes AUC between a pair of classes, ignoring instances from all 
     1497        other classes. 
     1498        """ 
     1499        auc = AUC(ignore_weights=ignore_weights) 
     1500        auc._compute_for_pair_of_classes(res, class_index1, class_index2) 
     1501        return auc 
     1502 
     1503    @classmethod 
     1504    def matrix(cls, res, ignore_weights=False): 
     1505        """ 
     1506        Compute a (lower diagonal) matrix with AUCs for all pairs of classes. 
     1507        If there are empty classes, the corresponding elements in the matrix 
     1508        are -1. 
     1509        """ 
     1510        auc = AUC(ignore_weights=ignore_weights) 
     1511        auc._compute_matrix(res) 
     1512        return auc 
     1513 
     1514    def _compute_for_binary_class(self, res): 
    15231515        """AUC for binary classification problems""" 
    15241516        if res.number_of_iterations > 1: 
    15251517            return self._compute_for_multiple_folds( 
    1526                         self._compute_one_class_against_all, 
    1527                         split_by_iterations(res), 
    1528                         (-1, not ignore_weights,res, res.number_of_iterations)) 
    1529         else: 
    1530             return self._compute_one_class_against_all(res, -1, ignore_weights)[0] 
    1531  
    1532     def _compute_for_multi_value_class(self, res, ignore_weights=False, 
    1533                                       method=0): 
     1518                self._compute_one_class_against_all, 
     1519                split_by_iterations(res), 
     1520                (-1, res, res.number_of_iterations)) 
     1521        else: 
     1522            auc, _ = self._compute_one_class_against_all(res, -1) 
     1523            self[:] = auc 
     1524            return self 
     1525 
     1526    def _compute_for_multi_value_class(self, res, method=0): 
    15341527        """AUC for multiclass classification problems""" 
    15351528        numberOfClasses = len(res.class_values) 
     
    15571550                                             iterations, 
    15581551                                             (classIndex1, classIndex2, 
    1559                                              not ignore_weights, all_ite, 
    1560                                              res.number_of_iterations)) 
     1552                                              all_ite, 
     1553                                              res.number_of_iterations)) 
    15611554                    if subsum_aucs: 
    15621555                        if method == self.ByWeightedPairs: 
     
    15711564                subsum_aucs = self._compute_for_multiple_folds( 
    15721565                    self._compute_one_class_against_all, 
    1573                     iterations, (classIndex, not ignore_weights, all_ite, 
     1566                    iterations, (classIndex, all_ite, 
    15741567                                 res.number_of_iterations)) 
    15751568                if subsum_aucs: 
     
    15851578            sum_aucs = [x/usefulClassPairs for x in sum_aucs] 
    15861579 
    1587         return sum_aucs 
     1580        self[:] = sum_aucs 
     1581        return self 
    15881582 
    15891583    # computes the average AUC over folds using a "AUCcomputer" (AUC_i or AUC_ij) 
     
    16041598                return aucs 
    16051599            subsum_aucs = map(add, subsum_aucs, aucs) 
    1606         return subsum_aucs 
     1600        self[:] = subsum_aucs 
     1601        return self 
     1602 
     1603    # Computes AUC; in multivalued class problem, AUC is computed as one against all 
     1604    # Results over folds are averages; if some folds examples from one class only, the folds are merged 
     1605    def _compute_for_single_class(self, res, class_index): 
     1606        if res.number_of_iterations > 1: 
     1607            self._compute_for_multiple_folds( 
     1608                self._compute_one_class_against_all, split_by_iterations(res), 
     1609                (class_index, res, res.number_of_iterations)) 
     1610        else: 
     1611            self._compute_one_class_against_all(res, class_index) 
     1612 
     1613    # Computes AUC for a pair of classes (as if there were no other classes) 
     1614    # Results over folds are averages; if some folds have examples from one class only, the folds are merged 
     1615    def _compute_for_pair_of_classes(self, res, class_index1, class_index2): 
     1616        if res.number_of_iterations > 1: 
     1617            self._compute_for_multiple_folds(self._compute_one_class_against_another, 
     1618                split_by_iterations(res), 
     1619                (class_index1, class_index2, res, res.number_of_iterations)) 
     1620        else: 
     1621            self._compute_one_class_against_another(res, class_index1, class_index2) 
    16071622 
    16081623    # computes AUC between class i and the other classes (treating them as the same class) 
    16091624    @deprecated_keywords({"classIndex": "class_index", 
    16101625                          "divideByIfIte": "divide_by_if_ite"}) 
    1611     def _compute_one_class_against_all(self, ite, class_index, 
    1612                                       ignore_weights=True, all_ite=None, 
     1626    def _compute_one_class_against_all(self, ite, class_index, all_ite=None, 
    16131627                                      divide_by_if_ite=1.0): 
    16141628        """Compute AUC between class i and all the other classes)""" 
    16151629        return self._compute_auc(corn.computeCDT, ite, all_ite, divide_by_if_ite, 
    1616             (class_index, not ignore_weights)) 
     1630            (class_index, not self.ignore_weights)) 
    16171631 
    16181632 
    16191633    # computes AUC between classes i and j as if there are no other classes 
    16201634    def _compute_one_class_against_another(self, ite, class_index1, 
    1621             class_index2, ignore_weights=False, all_ite=None, 
     1635            class_index2, all_ite=None, 
    16221636            divide_by_if_ite=1.0): 
    16231637        """ 
     
    16251639        """ 
    16261640        return self._compute_auc(corn.computeCDTPair, ite, all_ite, divide_by_if_ite, 
    1627             (class_index1, class_index2, not ignore_weights)) 
     1641            (class_index1, class_index2, not self.ignore_weights)) 
    16281642 
    16291643    # computes AUC using a specified 'cdtComputer' function 
     
    16511665        return False, False 
    16521666 
    1653 AUC = AucClass() 
    1654  
    1655 AUC_binary = replace_use_weights(deprecated_function_name(AUC._compute_for_binary_class)) 
    1656 AUC_multi = replace_use_weights(deprecated_function_name(AUC._compute_for_multi_value_class)) 
    1657 AUC_iterations = replace_use_weights(deprecated_function_name(AUC._compute_for_multiple_folds)) 
    1658 AUC_x = replace_use_weights(deprecated_function_name(AUC._compute_auc)) 
    1659 AUC_i = replace_use_weights(deprecated_function_name(AUC._compute_one_class_against_all)) 
    1660 AUC_ij = replace_use_weights(deprecated_function_name(AUC._compute_one_class_against_another)) 
     1667    def _compute_matrix(self, res): 
     1668        numberOfClasses = len(res.class_values) 
     1669        number_of_learners = res.number_of_learners 
     1670        if res.number_of_iterations > 1: 
     1671            iterations, all_ite = split_by_iterations(res), res 
     1672        else: 
     1673            iterations, all_ite = [res], None 
     1674        aucs = [[[] for _ in range(numberOfClasses)] for _ in 
     1675                                                     range(number_of_learners)] 
     1676        for classIndex1 in range(numberOfClasses): 
     1677            for classIndex2 in range(classIndex1): 
     1678                pair_aucs = self._compute_for_multiple_folds( 
     1679                    self._compute_one_class_against_another, 
     1680                    iterations, (classIndex1, 
     1681                                                  classIndex2, 
     1682                                                  all_ite, 
     1683                                                  res.number_of_iterations)) 
     1684                if pair_aucs: 
     1685                    for lrn in range(number_of_learners): 
     1686                        aucs[lrn][classIndex1].append(pair_aucs[lrn]) 
     1687                else: 
     1688                    for lrn in range(number_of_learners): 
     1689                        aucs[lrn][classIndex1].append(-1) 
     1690        self[:] = aucs 
     1691        return aucs 
     1692 
     1693#Backward compatibility 
     1694@replace_use_weights 
     1695def AUC_binary(res, ignore_weights=False): 
     1696    auc = deprecated_function_name(AUC)(ignore_weights=ignore_weights) 
     1697    auc._compute_for_binary_class(res) 
     1698    return auc 
     1699 
     1700@replace_use_weights 
     1701def AUC_multi(res, ignore_weights=False, method = 0): 
     1702    auc = deprecated_function_name(AUC)(ignore_weights=ignore_weights, 
     1703        method=method) 
     1704    auc._compute_for_multi_value_class(res) 
     1705    return auc 
     1706 
     1707def AUC_iterations(auc_computer, iterations, computer_args): 
     1708    auc = deprecated_function_name(AUC)() 
     1709    auc._compute_for_multiple_folds(auc_computer, iterations, computer_args) 
     1710    return auc 
     1711 
     1712def AUC_x(cdtComputer, ite, all_ite, divide_by_if_ite, computer_args): 
     1713    auc = deprecated_function_name(AUC)() 
     1714    result = auc._compute_auc(cdtComputer, ite, all_ite, divide_by_if_ite, 
     1715        computer_args) 
     1716    return result 
     1717 
     1718@replace_use_weights 
     1719def AUC_i(ite, class_index, ignore_weights=False, all_ite=None, 
     1720          divide_by_if_ite=1.0): 
     1721    auc = deprecated_function_name(AUC)() 
     1722    result = auc._compute_one_class_against_another(ite, class_index, 
     1723        all_ite=None, divide_by_if_ite=1.0) 
     1724    return result 
     1725 
     1726 
     1727@replace_use_weights 
     1728def AUC_ij(ite, class_index1, class_index2, ignore_weights=False, 
     1729           all_ite = None, divide_by_if_ite = 1.0): 
     1730    auc = deprecated_function_name(AUC)(ignore_weights=ignore_weights) 
     1731    result = auc._compute_one_class_against_another( 
     1732        ite, class_index1, class_index2, all_ite = None, divide_by_if_ite = 1.0) 
     1733    return result 
     1734 
     1735 
     1736 
     1737 
     1738#AUC_binary = replace_use_weights(deprecated_function_name(AUC()._compute_for_binary_class)) 
     1739#AUC_multi = replace_use_weights(deprecated_function_name(AUC._compute_for_multi_value_class)) 
     1740#AUC_iterations = replace_use_weights(deprecated_function_name(AUC._compute_for_multiple_folds)) 
     1741#AUC_x = replace_use_weights(deprecated_function_name(AUC._compute_auc)) 
     1742#AUC_i = replace_use_weights(deprecated_function_name(AUC._compute_one_class_against_all)) 
     1743#AUC_ij = replace_use_weights(deprecated_function_name(AUC._compute_one_class_against_another)) 
    16611744 
    16621745AUC_single = replace_use_weights( 
Note: See TracChangeset for help on using the changeset viewer.