ClusterEval: External Clustering Validation by the Homogeneity-Parsimony Trade-Off
ClusterEval is a lightweight package for external clustering validation.
ClusterEval provides reference implementations for the homogeneity-parsimony scores proposed in arXiv 2607.20799. Evaluation of clustering quality on these two objectives provides a unified framework for assessing clustering agreement with ground truth class labels.
The package also provides set-matching variants of the homogeneity and parsimony scores, called normalized purity, and normalized inverse purity. These scores fix the definition of purity and inverse purity so that the full range of [0, 1] is attainable. The pair-based equivalents of these scores are pair specificity and pair sensitivity, exactly the familiar binary classifier metrics used by the receiver operating characteristic (ROC) curve.
Validation Approach |
Criterion 1 |
Criterion 2 |
Entropy measure |
|---|---|---|---|
Information-theoretic |
Homogeneity Score |
Parsimony Score |
Shannon Entropy |
Set-matching |
Normalized Purity Score |
Normalized Inverse Purity Score |
min-Entropy |
Pair-counting |
Pair Specificity |
Pair Sensitivity |
Collision Entropy |
All scores are implemented in a way that is compatible with evaluation metrics defined in Scikit-learn’s sklearn.metrics to allow easy replacement within existing pipelines.
Installation
ClusterEval can be installed via pip:
pip install clustereval
The package depends on numpy and scikit-learn.
Documentation and examples
API documentation is provided through docstrings.
Jupyter example notebooks can be found in the examples folder:
You can create a local copy of the API documentation in the docs folder by running:
make html
All metrics require non-empty label arrays of equal length and raise ValueError otherwise.
Support and contributing
For bug reports and enhancement requests use the Github issue tool, or (even better!) open a pull request with relevant changes.
When preparing a pull request, please run the testsuite using pytest to ensure none of the existing functionality breaks.
API Reference
- clustereval.homogeneity_score(labels_true, labels_pred)
Calculate the homogeneity score for a clustering.
Homogeneity asks whether class members \(C\) are assigned to the same cluster \(K\). Homogeneity is defined as
\[h(C, K) = 1 - \frac{H(C|K)}{H(C)}\]Homogeneity is maximal (\(h = 1\)) when all members of a cluster share the same class label, and minimal (\(h = 0\)) when cluster assignments provide no information about the classes.
- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
homogeneity – The homogeneity score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
See also
parsimony_scoreParsimony of clustering relative to class partition
- clustereval.parsimony_score(labels_true, labels_pred)
Calculate the parsimony score for a clustering.
Parsimony asks whether the \(N\) objects are clustered as simply as possible given the class \(C\). Formally, the parsimony score is defined based on the conditional entropy of clusters \(K\) given classes \(C\):
\[p(C, K) = 1 - \frac{H(K|C)}{\log(N) - H(C)}.\]Parsimony is maximal (\(p = 1\)) when all class members share an identical cluster label, and minimal (\(p = 0\)) when the clustering fully fragments each class.
- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
parsimony – The parsimony score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
See also
homogeneity_scoreHomogeneity of clustering relative to class partition
completeness_scoreCompleteness is a closely related score, which depends on the entropy of the clustering under evaluation rather than its maximum over all possible clusterings.
- clustereval.q_measure_score(labels_true, labels_pred, *, beta=1.0)
Calculate the Q-measure for a clustering.
The Q-measure is defined as the weighted harmonic mean of the normalized
homogeneityandparsimonyscores:\[Q_\beta(C, K) = \frac{(1 + \beta) h p}{\beta h + p}\]As a harmonic mean, the Q-measure is sensitive to low values in either
homogeneityorparsimony. The weight parameterbetacontrols the relative importance ofhomogeneityvsparsimony. Largerbetaputs greater weight onparsimony. The Q-measure is maximal (\(Q_\beta = 1\)) for clusterings that perfectly recover the class partition, and minimal (\(Q_\beta = 0\)) when either the clustering is completely uninformative about the classes or when the clustering fully fragments each class.- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate. beta : float, default=1.0
beta (float, default=1.0) – Weight parameter controlling importance of
homogeneityvsparsimony. Largerbetaputs greater weight onparsimony. Must be >= 0.
- Returns:
q_measure – The Q-measure for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
See also
homogeneity_scoreHomogeneity of clustering relative to class partition
parsimony_scoreParsimony of clustering relative to class partition
- clustereval.purity_score(labels_true, labels_pred)
Calculate the purity score for a clustering.
- This score is the set-matching analogue of class-conditional clustering
entropy.
When \(C\) is the class partition and \(K\) is the clustering, the purity score is defined as:
\[\mathrm{purity}(C, K) = \frac{1}{N} \sum_k \max_c \left| C_c \cap K_k \right|\]- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
purity – The purity score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
See also
normalized_purity_scorePurity normalized to [0, 1]
- clustereval.inverse_purity_score(labels_true, labels_pred)
Calculate the inverse purity score for a clustering.
When \(C\) is the class partition and \(K\) is the clustering, the inverse purity score is defined as:
\[\mathrm{inv\text{-}purity}(C, K) = \frac{1}{N} \sum_c \max_k \left| C_c \cap K_k \right|\]- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
inverse_purity – The inverse purity score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
See also
normalized_inverse_purity_scoreInverse purity normalized to [0, 1].
- clustereval.normalized_purity_score(labels_true, labels_pred)
Calculate the normalized purity score for a clustering.
This is a min-max normalized version of the purity score, which is defined as:
\[\text{norm. purity} = \frac{\mathrm{purity}(C, K) - \max_c P(c)}{1-\max_c P(c)}\]- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
normalized_purity – The normalized purity score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
- clustereval.normalized_inverse_purity_score(labels_true, labels_pred)
Calculate the normalized inverse purity score for a clustering.
This is a min-max normalized version of the inverse purity score. Given the number of unique classes, $L = mathrm{supp}(C)$, the normalized score is defined as:
\[\text{norm. inv. purity} = \frac{\mathrm{inv.\text{-}purity}(C, K) - L/N}{1-L|/N}\]- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
normalized_inverse_purity – The normalized inverse purity score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
- clustereval.pair_specificity_score(labels_true, labels_pred)
Calculate the pair specificity score for a clustering.
Pair-based evaluation metrics are based on a binary classification of pairs of samples. This score is the specificity of this binary classifier, defined as the true negative rate.
This score is the pair-based analogue of the homogeneity score.
- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
pair_specificity – The pair specificity score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
- clustereval.pair_sensitivity_score(labels_true, labels_pred)
Calculate the pair sensitivity score for a clustering.
Pair-based evaluation metrics are based on a binary classification of pairs of samples. This score is the sensitivity of this binary classifier, defined as the true positive rate.
This score is the pair-based analogue of the parsimony score.
- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
pair_sensitivity – The pair sensitivity score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
- clustereval.completeness_score(labels_true, labels_pred)
Calculate the completeness score for a clustering.
Completeness like parsimony penalizes clustering that fragment classes. However, completeness uses a normalization that depends on the entropy of the specific clustering under evaluation \(H(K)\) rather than its maximum over all possible clusterings \(\log(N) - H(C)\). Formally, completeness is defined as
\[c(C, K) = 1 - \frac{H(K|C)}{H(K)}\]- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels to evaluate.
- Returns:
completeness – The completeness score for the clustering.
- Return type:
float
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
See also
parsimony_scoreRelated score that uses a normalization based on the maximum possible entropy across all possible clusterings, which behaves more intuitively under refinement.
- clustereval.conditional_entropies(labels_true, labels_pred)
Calculate conditional entropies between two partitions.
- Parameters:
labels_true (array-like of shape (n_samples,)) – Ground truth class labels C to be used as a reference.
labels_pred (array-like of shape (n_samples,)) – Cluster labels K to evaluate.
- Returns:
NamedTuple with
joint_entropy,class_given_cluster_entropy,cluster_given_class_entropy,class_entropy, andcluster_entropyfields.- Return type:
- Raises:
ValueError – If the two label arrays have different lengths or are empty.
Notes
The logarithm used is the natural logarithm (base-e), so returns are in units of nats.
- class clustereval.ConditionalEntropyResult(joint_entropy, class_given_cluster_entropy, cluster_given_class_entropy, class_entropy, cluster_entropy)
Bases:
NamedTuple- joint_entropy: float
Alias for field number 0
- class_given_cluster_entropy: float
Alias for field number 1
- cluster_given_class_entropy: float
Alias for field number 2
- class_entropy: float
Alias for field number 3
- cluster_entropy: float
Alias for field number 4