blinklinmult.compare.clusters¶
The unit of resampling: what counts as one independent observation.
This module exists because getting this wrong is the most common way a model comparison overstates its own certainty, and it fails silently – the arithmetic is identical, only the interval is wrong, and it is wrong in the flattering direction.
A recording is one observation. A window is not. Two 15-frame windows cut from the same recording share a subject, a camera, an illuminant, an eyelid shape and a blink rate. They are close to duplicates. Resampling them independently pretends the corpus holds far more information than it does: measured on RN30, 7312 windows come from 35 recordings, and treating the windows as independent shrank the confidence interval on a model difference by 5.9x – turning an inconclusive result into a significant one.
The same argument applies one level down. The left and right eye of one subject
are two samples, but they are not independent: they blink together. So both eyes
land in the same cluster, which cluster_of() enforces by construction.
Attributes¶
Separator inside a sample id, matching |
Exceptions¶
Raised when clusters cannot be determined from the given identifiers. |
Functions¶
|
Return the cluster a sample belongs to. |
|
Reduce recording keys to their sorted, unique clusters. |
|
Draw bootstrap resamples of cluster indices. |
Module Contents¶
- blinklinmult.compare.clusters.SAMPLE_SEPARATOR = '|'¶
Separator inside a sample id, matching
data.schema.build_sample_id.
- exception blinklinmult.compare.clusters.ClusterError[source]¶
Bases:
ValueErrorRaised when clusters cannot be determined from the given identifiers.
Initialize self. See help(type(self)) for accurate signature.
- blinklinmult.compare.clusters.cluster_of(sample_id: str) str[source]¶
Return the cluster a sample belongs to.
Sample ids are
video_id|frame_group|eye_side(seebuild_sample_id()), so the cluster is the leading component. Both eyes of one subject map to the same cluster: they blink together, so resampling them independently would be the window-level error one level down.- Parameters:
sample_id (str) – An id built by
build_sample_id(), or a bare recording name, which is already its own cluster.- Returns:
The cluster key.
- Return type:
str
- Raises:
ClusterError – If the id is empty, which would silently pool everything into one nameless cluster.
- blinklinmult.compare.clusters.clusters_from_signals(recordings: collections.abc.Sequence[str]) list[str][source]¶
Reduce recording keys to their sorted, unique clusters.
- Parameters:
recordings (Sequence[str]) – Keys from
load_signals(). Read the archive through that function rather thannumpy.load(): it checks which target thetrutharrays hold, and reading ablink_presencearchive aseye_stateonce fabricated 200 045 phantom false positives.- Returns:
Unique clusters, sorted so a run is reproducible.
- Return type:
list[str]
- Raises:
ClusterError – If no recordings were given, since there is nothing to resample.
- blinklinmult.compare.clusters.resample(n_clusters: int, rounds: int, seed: int) numpy.ndarray[source]¶
Draw bootstrap resamples of cluster indices.
One array for every model to share. Drawing once and evaluating every model on the same draw is what makes the comparison paired: a resample heavy on hard recordings drags all models down together, so the difference between them is stable where their absolute scores are not. At n=20-35 clusters that between-recording variance dominates, and an unpaired interval would be too wide to resolve anything.
- Parameters:
n_clusters (int) – How many clusters the corpus has.
rounds (int) – Bootstrap replicates.
seed (int) – Seed, recorded in the output so a result can be reproduced.
- Returns:
(rounds, n_clusters)indices, drawn with replacement.- Return type:
np.ndarray
- Raises:
ClusterError – If either argument is not positive.