def assignment_score_slow(cm, normalize=True, rpad=False, cpad=False): """Calls Python/Numpy implementation of the Hungarian method Testing version (uses SciPy's implementation) """ cost_matrix = -cm.to_array(rpad=rpad, cpad=cpad) ris, cis = linear_sum_assignment(cost_matrix) score = -cost_matrix[ris, cis].sum() if normalize: score = _div(score, cm.grand_total) return score
def _kappa(a, c, d, b): """An alternative implementation of Cohen's kappa (for testing) """ n = a + b + c + d p1 = a + b p2 = a + c q1 = c + d q2 = b + d if a == n or b == n or c == n or d == n: # only one cell is non-zero return np.nan elif p1 == 0 or p2 == 0 or q1 == 0 or q2 == 0: # one row or column is zero, another non-zero return 0.0 else: # no more than one cell is zero po = a + d pe = (p2 * p1 + q2 * q1) / float(n) return _div(po - pe, n - pe)
def test_2x2_invariants(): """Alternative implementations should coincide for 2x2 matrices """ for _ in xrange(100): cm = ConfusionMatrix2.from_random_counts(low=0, high=10) # object idempotency assert_equal( cm.to_ccw(), ConfusionMatrix2.from_ccw(*cm.to_ccw()).to_ccw(), msg="must be able to convert to tuple and create from tuple") # pairwise H, C, V h, c, v = cm.pairwise_hcv()[:3] check_with_nans(v, geometric_mean(h, c), ensure_nans=False) # informedness actual_info = cm.informedness() expected_info_1 = cm.TPR() + cm.TNR() - 1.0 expected_info_2 = cm.TPR() - cm.FPR() check_with_nans(actual_info, expected_info_1, 4, ensure_nans=False) check_with_nans(actual_info, expected_info_2, 4, ensure_nans=False) # markedness actual_mark = cm.markedness() expected_mark_1 = cm.PPV() + cm.NPV() - 1.0 expected_mark_2 = cm.PPV() - cm.FOR() check_with_nans(actual_mark, expected_mark_1, 4, ensure_nans=False) check_with_nans(actual_mark, expected_mark_2, 4, ensure_nans=False) # matthews corr coeff # actual_mcc = cm.matthews_corr() # expected_mcc = geometric_mean(actual_info, actual_mark) # check_with_nans(actual_mcc, expected_mcc, 4, ensure_nans=False) # kappas actual_kappa = cm.kappa() # kappa is the same as harmonic mean of kappa components expected_kappa_1 = harmonic_mean(*cm.kappas()[:2]) check_with_nans(actual_kappa, expected_kappa_1, 4, ensure_nans=False) # kappa is the same as accuracy adjusted for chance expected_kappa_2 = harmonic_mean(*cm.adjust_to_null(cm.accuracy, model='m3')) check_with_nans(actual_kappa, expected_kappa_2, 4, ensure_nans=False) # kappa is the same as Dice coeff adjusted for chance expected_kappa_3 = harmonic_mean(*cm.adjust_to_null(cm.dice_coeff, model='m3')) check_with_nans(actual_kappa, expected_kappa_3, 4, ensure_nans=False) # odds ratio and Yule's Q actual_odds_ratio = cm.DOR() actual_yule_q = cm.yule_q() expected_yule_q = _div(actual_odds_ratio - 1.0, actual_odds_ratio + 1.0) expected_odds_ratio = _div(cm.PLL(), cm.NLL()) check_with_nans(actual_odds_ratio, expected_odds_ratio, 4, ensure_nans=False) check_with_nans(actual_yule_q, expected_yule_q, 4, ensure_nans=False) # F-score and Dice expected_f = harmonic_mean(cm.precision(), cm.recall()) actual_f = cm.fscore() check_with_nans(expected_f, actual_f, 6) check_with_nans(expected_f, cm.dice_coeff(), 6, ensure_nans=False) # association coefficients (1) dice = cm.dice_coeff() expected_jaccard = _div(dice, 2.0 - dice) actual_jaccard = cm.jaccard_coeff() check_with_nans(actual_jaccard, expected_jaccard, 6, ensure_nans=False) # association coefficients (2) jaccard = cm.jaccard_coeff() expected_ss2 = _div(jaccard, 2.0 - jaccard) actual_ss2 = cm.sokal_sneath_coeff() check_with_nans(actual_ss2, expected_ss2, 6, ensure_nans=False) # adjusted ochiai actual = cm.ochiai_coeff_adj() expected = harmonic_mean(*cm.adjust_to_null(cm.ochiai_coeff, model='m3')) check_with_nans(actual, expected, 6, ensure_nans=False)