import numpy as np
[docs]class ContingencyTable(object):
"""Initializes a contingency table of the following form:
Event
Yes No
Forecast Yes a b
No c d
Attributes:
tp (int): Number of true positives or hits.
fp (int): Number of false positives.
fn (int): Number of false negatives or misses.
tn (int): Number of true negatives.
"""
def __init__(self, tp, fp, fn, tn):
self.table = np.array([[tp, fp], [fn, tn]], dtype=float)
self.N = self.table.sum()
[docs] def update(self, tp, fp, fn, tn):
"""
Update contingency table with new values without creating a new object.
"""
self.table.ravel()[:] = [tp, fp, fn, tn]
self.N = self.table.sum()
[docs] def __add__(self, other):
"""
Add two contingency tables together and return a combined one.
:param other:
:return:
"""
sum_ct = ContingencyTable(*(self.table + other.table).ravel())
return sum_ct
[docs] def pod(self):
"""Returns Probability of Detection (POD) or Hit Rate.
Formula: a/(a+c)"""
return self.table[0, 0] / (self.table[0, 0] + self.table[1, 0])
[docs] def foh(self):
"""Returns Frequency of Hits (FOH) or Success Ratio.
Formula: a/(a+b)"""
return self.table[0, 0] / (self.table[0, 0] + self.table[0, 1])
[docs] def far(self):
"""Returns False Alarm Ratio (FAR).
Formula: b/(a+b)"""
return self.table[0, 1] / (self.table[0, 0] + self.table[0, 1])
[docs] def pofd(self):
"""Returns Probability of False Detection (POFD).
b/(b+d)"""
return self.table[0, 1] / (self.table[0, 1] + self.table[1, 1])
[docs] def fom(self):
"""Returns Frequency of Misses (FOM).
Formula: c/(a+c)."""
return self.table[1, 0] / (self.table[0, 0] + self.table[1, 0])
[docs] def dfr(self):
"""Returns Detection Failure Ratio (DFR).
Formula: c/(c+d)"""
return self.table[1, 0] / (self.table[1, 0] + self.table[1, 1])
[docs] def pon(self):
"""Returns Probability of Null (PON).
Formula: d/(b+d)"""
return self.table[1, 1] / (self.table[0, 1] + self.table[1, 1])
[docs] def focn(self):
"""Returns Frequency of Correct Null (FOCN).
Formula: d/(c+d)"""
return self.table[1, 1] / (self.table[1, 0] + self.table[1, 1])
[docs] def bias(self):
"""Returns Frequency Bias. Formula: (a+b)/(a+c)"""
return (self.table[0, 0] + self.table[0, 1]) / (self.table[0, 0] + self.table[1, 0])
[docs] def accuracy(self):
"""Finley's measure, fraction correct, accuracy (a+d)/N"""
return (self.table[0, 0] + self.table[1, 1]) / self.N
[docs] def csi(self):
"""Gilbert's Score or Threat Score or Critical Success Index a/(a+b+c)"""
return self.table[0, 0] / (self.table[0, 0] + self.table[0, 1] + self.table[1, 0])
[docs] def ets(self):
"""Equitable Threat Score, Gilbert Skill Score, v, (a - R)/(a + b + c - R), R=(a+b)(a+c)/N"""
r = (self.table[0, 0] + self.table[0, 1]) * (self.table[0, 0] + self.table[1, 0]) / self.N
return (self.table[0, 0] - r) / (self.table[0, 0] + self.table[0, 1] + self.table[1, 0] - r)
[docs] def hss(self):
"""Doolittle (Heidke) Skill Score. 2(ad-bc)/((a+b)(b+d) + (a+c)(c+d))"""
return 2 * (self.table[0, 0] * self.table[1, 1] - self.table[0, 1] * self.table[1, 0]) / (
(self.table[0, 0] + self.table[0, 1]) * (self.table[0, 1] + self.table[1, 1]) +
(self.table[0, 0] + self.table[1, 0]) * (self.table[1, 0] + self.table[1, 1]))
[docs] def pss(self):
"""Peirce (Hansen-Kuipers, True) Skill Score (ad - bc)/((a+c)(b+d))"""
return (self.table[0, 0] * self.table[1, 1] - self.table[0, 1] * self.table[1, 0]) / \
((self.table[0, 0] + self.table[1, 0]) * (self.table[0, 1] + self.table[1, 1]))
[docs] def css(self):
"""Clayton Skill Score (ad - bc)/((a+b)(c+d))"""
return (self.table[0, 0] * self.table[1, 1] - self.table[0, 1] * self.table[1, 0]) / \
((self.table[0, 0] + self.table[0, 1]) * (self.table[1, 0] + self.table[1, 1]))
def __str__(self):
table_string = '\tEvent\n\tYes\tNo\nYes\t%d\t%d\nNo\t%d\t%d\n' % (
self.table[0, 0], self.table[0, 1], self.table[1, 0], self.table[1, 1])
return table_string
if __name__ == "__main__":
table = ContingencyTable(1, 2, 3, 4)
print table
print getattr(table, "pss")()