Source code for evclust.metrics
# -*- coding: utf-8 -*-
# This file as well as the whole evclust package are licenced under the MIT licence (see the LICENCE.txt)
# Armel SOUBEIGA (armelsoubeiga.github.io), France, 2023
"""
This module contains the metrics functions using to evaluate credal results
"""
#---------------------- Packges------------------------------------------------
import numpy as np
from scipy.spatial.distance import pdist, squareform
#---------------------- credalRI--------------------------------------------------
[docs]
def build_matrices(F):
nbFoc = F.shape[0]
C = np.zeros((nbFoc, nbFoc)) # matrice pour les degrés de conflit
for i in range(nbFoc):
for j in range(nbFoc):
C[i, j] = 1 - np.max(np.minimum(F[i, :], F[j, :]))
if np.max(F[0, :]) > 0:
E = np.zeros((nbFoc, nbFoc))
else:
E = np.zeros((nbFoc-1, nbFoc-1))
E = np.hstack((np.ones((nbFoc-1, 1)), E))
E = np.vstack((np.ones((1, nbFoc)), E))
singleton = (np.sum(F, axis=1) == 1) # matrice pour calculer m_ij(S)
S = np.diag(singleton)
return {'C': C, 'E': E, 'S': S}
[docs]
def pairwise_mass(clus):
"""
Computes relational representations for credal partition such as generated by a credal method.
"""
n = clus['mass'].shape[0]
M = clus['mass']
mat = build_matrices(clus['F'])
# Calcul des distances en utilisant les matrices E, S et C
Me = pdist(M @ mat['E'] @ M.T, 'euclidean')
M1 = pdist(M @ mat['S'] @ M.T, 'euclidean')
M0 = pdist(M @ (mat['C'] - mat['E']) @ M.T, 'euclidean')
return {'Me': squareform(Me), 'M1': squareform(M1), 'M0': squareform(M0)}
[docs]
def credalRI(clus1, clus2):
"""
Computes generalizations of the Rand index to compare credal partitions.
Parameters :
------------
clus1:
Relational representation of the first credal partition such as generated by a credal method.
clus2:
Relational representation of the second credal partition such as generated by a credal method.
Returns :
---------
RI (float):
Credal Rand indices.
References:
-----------
In Denoeux et al. (2018), two generalizations of the Rand index for comparing credal partitions
are defined: one is based on distances between mass function, the other one is based on distances.
"""
P1 = pairwise_mass(clus1)
P2 = pairwise_mass(clus2)
RI = 1 - np.mean(P1['Me'] + P2['Me'] - P1['Me'] * P2['Me'] + P1['M1'] * P2['M0'] + P1['M0'] * P2['M1'])
return RI
#---------------------- nonspecificity--------------------------------------------------
[docs]
def nonspecificity(mass, F):
"""
Compute the nonspecificity of a credal partition.
Parameters
-----------
mass ( ndarray (n, F size)):
The credal partition.
F (array):
The array of length of focalsets.
Returns
-------
NS (float):
The nonspecificity of the credal partition.
"""
focalsets = [tuple(index + 1 for index in row.nonzero()[0]) for row in F]
n_samples = mass.shape[0]
len_fs = [len(fs) for fs in focalsets if fs != tuple()]
len_fs = np.array(len_fs)
NS = np.sum(len_fs * mass[:, 1:]) + np.sum(
mass[:, 0] * np.log2(max(len_fs)))
NS /= n_samples * np.log2(max(len_fs))
return NS