# EpyNN/epynn/commons/metrics.py
# Related third party imports
import numpy as np
[docs]def metrics_functions(key=None):
"""Callback function for metrics.
:param key: Name of the metrics function, defaults to `None` which returns all functions.
:type key: str, optional
:return: Metrics functions or computed metrics.
:rtype: dict[str: function] or :class:`numpy.ndarray`
"""
metrics = {
'accuracy': accuracy,
'recall': recall,
'precision': precision,
'fscore': fscore,
'specificity': specificity,
'NPV': NPV,
}
# If key provided, returns output of function
if key:
metrics = metrics[key]
return metrics
[docs]def accuracy(Y, A):
"""Accuracy of prediction.
:param Y: True labels for a set of samples.
:type Y: :class:`numpy.ndarray`
:param A: Output of forward propagation.
:type A: :class:`numpy.ndarray`
:return: Accuracy for each sample.
:rtype: :class:`numpy.ndarray`
"""
encoded = (Y.shape[1] > 1)
P = np.argmax(A, axis=1) if encoded else np.around(A)
y = np.argmax(Y, axis=1) if encoded else Y
accuracy = (P == y)
return accuracy
[docs]def recall(Y, A):
"""Fraction of positive instances retrieved over the total.
:param Y: True labels for a set of samples.
:type Y: :class:`numpy.ndarray`
:param A: Output of forward propagation.
:type A: :class:`numpy.ndarray`
:return: Recall.
:rtype: :class:`numpy.ndarray`
"""
encoded = (Y.shape[1] > 1) # Check if one-hot encoding of labels
P = np.argmax(A, axis=1) if encoded else np.around(A)
y = np.argmax(Y, axis=1) if encoded else Y
tp = np.sum(np.where((P==0) & (y==0), 1, 0)) # True positive
fp = np.sum(np.where((P==0) & (y==1), 1, 0)) # False positive
tn = np.sum(np.where((P==1) & (y==1), 1, 0)) # True negative
fn = np.sum(np.where((P==1) & (y==0), 1, 0)) # False negative
recall = (tp / (tp+fn))
return recall
[docs]def precision(Y, A):
"""Fraction of positive samples among retrieved instances.
:param Y: True labels for a set of samples.
:type Y: :class:`numpy.ndarray`
:param A: Output of forward propagation.
:type A: :class:`numpy.ndarray`
:return: Precision.
:rtype: :class:`numpy.ndarray`
"""
encoded = (Y.shape[1] > 1) # Check if one-hot encoding of labels
P = np.argmax(A, axis=1) if encoded else np.around(A)
y = np.argmax(Y, axis=1) if encoded else Y
tp = np.sum(np.where((P==0) & (y==0), 1, 0)) # True positive
fp = np.sum(np.where((P==0) & (y==1), 1, 0)) # False positive
tn = np.sum(np.where((P==1) & (y==1), 1, 0)) # True negative
fn = np.sum(np.where((P==1) & (y==0), 1, 0)) # False negative
precision = (tp / (tp+fp))
return precision
[docs]def NPV(Y, A):
"""Fraction of negative samples among excluded instances.
:param Y: True labels for a set of samples.
:type Y: :class:`numpy.ndarray`
:param A: Output of forward propagation.
:type A: :class:`numpy.ndarray`
:return: Negative Predictive Value.
:rtype: :class:`numpy.ndarray`
"""
encoded = (Y.shape[1] > 1) # Check if one-hot encoding of labels
P = np.argmax(A, axis=1) if encoded else np.around(A)
y = np.argmax(Y, axis=1) if encoded else Y
tp = np.sum(np.where((P==0) & (y==0), 1, 0)) # True positive
fp = np.sum(np.where((P==0) & (y==1), 1, 0)) # False positive
tn = np.sum(np.where((P==1) & (y==1), 1, 0)) # True negative
fn = np.sum(np.where((P==1) & (y==0), 1, 0)) # False negative
npv = (tn / (tn+fn))
return npv
[docs]def fscore(Y, A):
"""F-Score that is the harmonic mean of recall and precision.
:param Y: True labels for a set of samples.
:type Y: :class:`numpy.ndarray`
:param A: Output of forward propagation.
:type A: :class:`numpy.ndarray`
:return: F-score.
:rtype: :class:`numpy.ndarray`
"""
encoded = (Y.shape[1] > 1) # Check if one-hot encoding of labels
P = np.argmax(A, axis=1) if encoded else np.around(A)
y = np.argmax(Y, axis=1) if encoded else Y
tp = np.sum(np.where((P==0) & (y==0), 1, 0)) # True positive
fp = np.sum(np.where((P==0) & (y==1), 1, 0)) # False positive
tn = np.sum(np.where((P==1) & (y==1), 1, 0)) # True negative
fn = np.sum(np.where((P==1) & (y==0), 1, 0)) # False negative
fscore = (tp / (tp + 0.5*(fp+fn)))
return fscore
[docs]def specificity(Y, A):
"""Fraction of negative samples among excluded instances.
:param Y: True labels for a set of samples.
:type Y: :class:`numpy.ndarray`
:param A: Output of forward propagation.
:type A: :class:`numpy.ndarray`
:return: Specificity.
:rtype: :class:`numpy.ndarray`
"""
encoded = (Y.shape[1] > 1) # Check if one-hot encoding of labels
P = np.argmax(A, axis=1) if encoded else np.around(A)
y = np.argmax(Y, axis=1) if encoded else Y
tp = np.sum(np.where((P==0) & (y==0), 1, 0)) # True positive
fp = np.sum(np.where((P==0) & (y==1), 1, 0)) # False positive
tn = np.sum(np.where((P==1) & (y==1), 1, 0)) # True negative
fn = np.sum(np.where((P==1) & (y==0), 1, 0)) # False negative
specificity = (tn / (tn+fp))
return specificity