-
Notifications
You must be signed in to change notification settings - Fork 0
Metrics adjustedmutualinformation score
Development build. This page describes
main, not a released package. The latest published Lodestar.Metrics is 0.3.0 — read its documentation.
Mutual information between two labellings, corrected for chance.
public static double Score(ReadOnlySpan<int> labelsTrue, ReadOnlySpan<int> labelsPred)Parameters — labelsTrue is the reference labelling and labelsPred the one being scored;
they must be the same length. The label values carry no meaning — only which samples share one.
Returns — double, 1 when each labelling determines the other and about 0 for two
independent ones. It can be negative: agreement worse than chance is a real outcome. The independent case
below lands a rounding step short of exactly -0.5, which is scikit-learn's value too.
Exceptions — ArgumentException when the two labellings disagree in length.
Example — a renaming, and two independent partitions scoring below zero.
using Lodestar.Metrics;
double renamed = AdjustedMutualInformation.Score([0, 0, 1, 1], [2, 2, 0, 0]); // => 1
double independent = AdjustedMutualInformation.Score([0, 0, 1, 1], [0, 1, 0, 1]); // => -0.4999…
double alone = AdjustedMutualInformation.Score([0, 0, 1, 1], [0, 1, 2, 3]);
bool chanceLevel = Math.Abs(alone) < 1e-12; // => TrueRemarks — the last pair of lines is the one worth the page. alone is chance level rather
than exactly zero — it lands a few rounding steps away, which is why the example asks the question
with a tolerance instead of promising a literal 0.
NormalizedMutualInformation.Score gives 0.667 for that
same pair, because putting every sample in its own cluster genuinely does determine the truth. What
it does not do is beat chance at it, and this metric says so. That is the reason to reach for
this one when the clusterings being compared have different numbers of clusters.
An empty input and a single sample both score 1, as they do for
AdjustedRand.Score — and unlike
FowlkesMallows.Score, which scores 0 on both. The degenerate cases
are a fact per metric rather than a rule for the family.
The correction subtracts the mutual information the two cluster-size profiles would share by
chance, summed over the hypergeometric distribution of every cell the marginals allow. Every
factorial that sum needs is of an integer, so it comes from a cumulative log(k!) table rather
than from a gammaln series approximation.
That table is not exact, and the error grows with the sample count. A cumulative sum of
logarithms accumulates: measured against lgamma and propagated through the nine-term combination
the sum uses, the relative error on each term is 5.9e-12 at 1 000 samples, 8.4e-10 at 20 000 and
2.0e-08 at 200 000. Two different bounds, worth keeping apart: the table's own error budget stays under 1e-9 up to
n ≈ 20 000 (measured above), while the frozen corpus enforces parity only up to n = 10,
its largest fixture. Between the two, parity is expected and untested; past 20 000 it is neither.
Treat the last digits as indicative there.
Applies to — net10.0, netstandard2.0.
See also — AdjustedMutualInformation,
NormalizedMutualInformation.Score,
the clustering index.
- 0001-target-framework
- 0002-unicode-comparison-unit
- 0003-provenance-and-licensing
- 0004-levenshtein-myers-backlog
- 0005-hamming-jellyfish-divergence
- 0006-ratcliff-autojunk
- 0007-metaphone-scope
- 0008-italian-enza-nltk-divergence
- 0009-sample-consumes-a-local-feed
- 0010-stop-word-list-provenance
- 0011-persistence-format
- 0012-per-package-versioning
- 0013-sentencepiece-parity-scope
- 0014-precompiled-normalizer
- 0015-sonar-rules-in-the-build
- 0016-metrics-package-placement
- 0017-bpe-parity-scope
- 0018-multiclass-roc-auc-parallelism-is-opt-in
- 0019-the-net-analysers-run-in-the-build-too
- 0020-normalize-is-a-projection-not-a-parameter
- 0021-multioutput-is-a-method-not-an-enum
- 0022-added-token-matching-flags
- 0023-byte-level-decode-substitutes
- 0024-weighted-median-averages-within-scikit-learns-epsilon
- 0025-quickselect-replaces-a-full-sort-for-the-median
- 0026-r2-and-explainedvariance-split-their-undefined-cases-differently
- 0027-r2-and-explainedvariance-vectorize-only-a-single-output
- 0028-log1p-is-kahans-identity-not-math-log-1-plus-x
- 0029-balanced-accuracy-adjusted-is-left-to-ieee-754-at-the-edge
- 0030-cohen-kappa-keeps-scikit-learns-expected-matrix-orientation
- 0031-nosamplecorrect-mirrors-numpys-float64-upcast
- 0032-fbeta-substitutes-tp-predicted-and-support-algebraically
- 0033-compensated-sum-is-neumaiers-variant
- 0034-dropout-is-refused-for-want-of-a-user
- 0035-a-null-pre-split-is-removed-with-invert-not-isolated
- 0036-a-member-may-ship-without-an-oracle-if-it-says-so
- 0037-the-guards-run-before-the-commit
- 0038-the-gate-confronts-an-exception-tag-with-the-page-that-documents-it
- 0039-mutual-information-returns-zero-on-an-empty-input
- 0040-a-curve-is-a-sealed-class-per-curve
- 0041-one-sample-file-per-public-class
- 0042-phonetic-encoders-refuse-a-null-word
- 0043-the-equality-table-is-sized-to-the-pattern
- 0044-compression-belongs-to-the-caller
- 0045-a-console-call-carries-its-reason-on-the-line
- 0046-check-adr-immutable-runs-in-ci-only
- 0047-one-gate-per-kernel-not-one-per-alphabet
- 0048-the-gate-depends-on-the-kernel-and-the-alphabet
- 0049-two-gates-per-kernel-tested-where-the-width-is-known
- 0050-the-sentencepiece-bpe-lineage-stays-a-bpe-model
- benchmark_latest
- decisions
- equivalence
- matplotlib
- migration
- nightly_run
- numpy
- pandas
- performance
- pytorch
- seaborn
- sklearn
- statsmodels