API reference¶
The public API is intentionally organised around scientific workflow layers rather than model-provider abstractions.
Top-level package¶
GazeForge: auditable AI for eye-tracking analysis.
AOI
dataclass
¶
Rectangular semantic area of interest with provenance metadata.
CallableAOIProvider
dataclass
¶
Adapter for custom/local detectors used by research teams.
detect(image: Any, labels: Sequence[str]) -> list[AOI]
¶
Run the supplied detector.
HuggingFaceZeroShotAOIProvider
dataclass
¶
Optional OWL-ViT zero-shot object detector via Transformers.
detect(image: Any, labels: Sequence[str]) -> list[AOI]
¶
Return open-vocabulary rectangular proposals.
BenchmarkDatasetCard
dataclass
¶
Provenance, evidence-strength, and split metadata for one benchmark dataset.
annotation_origin describes who or what produced the reference labels.
sampling_origin distinguishes native recordings from derived/resampled views.
reference_strength states the strongest validation interpretation supported by the
reference. These fields are intentionally explicit so algorithm-generated labels cannot be
presented as human validation merely because the underlying recording was sampled at a
desirable rate.
is_human_reference: bool
property
¶
Whether the card represents a human-derived validation reference.
is_native_human_reference: bool
property
¶
Whether human reference labels are evaluated at the native acquisition rate.
__post_init__() -> None
¶
Reject ambiguous evidence metadata before reports are generated.
to_dict() -> dict[str, Any]
¶
Serialize the dataset card.
EventModelComparison
dataclass
¶
Predictions, fold metrics, and aggregate summaries for matched validation folds.
CrossDatasetEventPrepared
dataclass
¶
Harmonised lower-rate data plus dataset-level preparation reports.
CrossDatasetEventValidation
dataclass
¶
Leave-one-dataset-out results for the two learned GazeForge baselines.
BenchmarkDashboard
dataclass
¶
Validated benchmark reports and verified report suites for public evidence.
CallableDynamicAOIProvider
dataclass
¶
Adapter for custom local dynamic-AOI detectors and trackers.
track(stimulus: Any, labels: Sequence[str]) -> list[DynamicAOIKeyframe]
¶
Run the supplied detector/tracker.
DynamicAOIKeyframe
dataclass
¶
Timestamped rectangular geometry for one semantic AOI track.
DynamicAOIEvaluation
dataclass
¶
Geometry/semantic metrics and timestamp-level matches for dynamic AOI tracks.
EventLevelEvaluation
dataclass
¶
Event intervals, one-to-one matches, and aggregate/per-class metrics.
EventModel
dataclass
¶
A fitted probabilistic event classifier plus compatibility metadata.
BenchmarkIntegrityError
¶
Bases: GazeForgeError, ValueError
Raised when a frozen benchmark report fails integrity/provenance validation.
GazeInWildLabellerAgreementRun
dataclass
¶
Aligned audited streams and pooled rate-aware human-human agreement evidence.
GazeInWildAuditedFile
dataclass
¶
One verified label/process pair with its loaded gaze stream.
GazeInWildLabelFileRecord
dataclass
¶
One audited human-labelled MATLAB stream and its identity mapping.
GazeInWildProcessFileRecord
dataclass
¶
One audited ProcessData MATLAB file.
GazeInWildSourceAuditRun
dataclass
¶
Verified source audit and all audited per-labeller streams.
GazeInWildSourceAuditSpec
dataclass
¶
Evidence contract required before Gaze-in-the-Wild results are frozen.
GazeInWildModelValidationRun
dataclass
¶
Prepared data, participant-held-out comparisons, sensitivities, and report.
GazeInWildPreparedBenchmark
dataclass
¶
One audited labeller prepared at a common analysis cadence.
Hollywood2SourceAuditRun
dataclass
¶
Verified source audit plus both human annotation streams.
Hollywood2SourceAuditSpec
dataclass
¶
Evidence contract required before Hollywood2EM is used in frozen modelling.
__post_init__() -> None
¶
Keep templates non-empirical and make empirical audits fully explicit.
to_dict() -> dict[str, Any]
¶
Return a deterministic JSON-compatible specification mapping.
from_dict(payload: Mapping[str, Any]) -> Hollywood2SourceAuditSpec
classmethod
¶
Construct a source-audit specification from decoded JSON.
Hollywood2SourceFileRecord
dataclass
¶
One audited Hollywood2EM ARFF file and its participant/trial identity.
Lund2013BenchmarkRun
dataclass
¶
Prepared data, matched-fold comparisons, stratified metrics, and report.
Lund2013PreparedBenchmark
dataclass
¶
Prepared Lund2013 rows plus explicit inclusion/exclusion provenance.
Lund2013FetchResult
dataclass
¶
Local checkout metadata returned by :func:fetch_lund2013_dataset.
Lund2013SensitivityRun
dataclass
¶
Sensitivity surface, dataset evidence card, and deterministic benchmark report.
Lund2013BenchmarkSuiteRun
dataclass
¶
Frozen child reports plus the deterministic suite-level manifest.
ModelCard
dataclass
¶
NativeEventAnnotatorAgreementRun
dataclass
¶
Verified annotation streams, aligned samples, and fingerprinted agreement report.
NativeEventBenchmarkRun
dataclass
¶
Prepared native corpus, matched model comparison, and frozen-report payload.
NativeEventBenchmarkSpec
dataclass
¶
Explicit metadata contract for a native-rate human-labelled event corpus.
__post_init__() -> None
¶
Reject incomplete or misleading native-evidence metadata.
to_dict() -> dict[str, Any]
¶
Return a deterministic JSON-compatible specification mapping.
from_dict(payload: Mapping[str, Any]) -> NativeEventBenchmarkSpec
classmethod
¶
Construct a specification from decoded JSON without silently ignoring keys.
NativeEventPreparedBenchmark
dataclass
¶
Verified native-rate analysis table plus evidence metadata.
NativeEventValidationSuiteRun
dataclass
¶
Frozen native-event child reports plus the deterministic completion manifest.
PairedModelDifferences
dataclass
¶
Per-fold paired deltas plus descriptive summaries for every model pair.
AuditTrail
dataclass
¶
Mutable collection of provenance records.
add(*, operation: str, input_data: pd.DataFrame, output_data: pd.DataFrame, parameters: dict[str, Any] | None = None, model_name: str | None = None, model_version: str | None = None, warnings: list[str] | None = None) -> ProvenanceRecord
¶
Fingerprint and append one operation.
to_frame() -> pd.DataFrame
¶
Return records as a flat table.
to_json(*, indent: int = 2) -> str
¶
Serialize records to JSON.
ProvenanceRecord
dataclass
¶
One auditable analysis operation.
to_dict() -> dict[str, Any]
¶
Serialize the record.
BenchmarkResamplingResult
dataclass
¶
Resampled labelled gaze data plus a machine-readable resampling report.
SamplingSensitivityResult
dataclass
¶
Complete settings ledger and model metrics for a resampling sensitivity surface.
ScanpathEmbeddingModel
dataclass
¶
TF-IDF n-gram encoder with optional learned SVD compression.
GazeFrame
dataclass
¶
Validated canonical gaze samples plus recording metadata.
copy() -> GazeFrame
¶
Return an independent copy.
SourceResolutionRecord
dataclass
¶
Compact common identity for one validated benchmark source-resolution checkpoint.
SourceResolutionDashboard
dataclass
¶
Integrity-checked source-resolution records prepared for public status reporting.
SourceResolutionBundleLock
dataclass
¶
Typed identity of a validated reviewed source-resolution snapshot.
StratifiedEventPerformance
dataclass
¶
Fold-wise and aggregate metrics computed from fixed out-of-fold predictions.
TemporalContextModel
dataclass
¶
A fitted context-window neural event classifier plus compatibility metadata.
ValidationResult
dataclass
¶
Grouped cross-validation predictions, fold metadata, and aggregate metrics.
VisusDynamicAOIHumanAgreementRun
dataclass
¶
Bidirectional dynamic-AOI agreement between two verified independent streams.
VisusAuditedFile
dataclass
¶
One verified local file paired with its manifest record.
VisusSourceAuditRun
dataclass
¶
Verified VISUS snapshot and deterministic source-audit report.
VisusSourceAuditSpec
dataclass
¶
Reviewed provenance and exact-file contract for a VISUS dataset copy.
to_dict() -> dict[str, Any]
¶
Return a deterministic JSON-ready representation.
VisusSourceFileRecord
dataclass
¶
One exact file identity in an audited VISUS snapshot.
VisusFrozenEvidenceBundle
dataclass
¶
Verified suite plus execution-provenance identities eligible for evidence review.
VisusCanonicalAOIIntakeRun
dataclass
¶
Canonical VISUS AOIs linked back to an exact audited source snapshot.
VisusDynamicAOIPredictionIntakeRun
dataclass
¶
Canonical model AOI predictions linked to an audited VISUS video snapshot.
VisusDynamicAOIValidationSuiteRun
dataclass
¶
Frozen VISUS provenance/validation reports plus a completion manifest.
VisusDynamicAOIModelValidationRun
dataclass
¶
Per-stimulus model-human evaluations and a deterministic benchmark report.
adapt_gazepoint_samples(data: pd.DataFrame, *, screen_size_px: tuple[int, int], participant_col: str = 'USER_FILE', trial_col: str = 'MEDIA_ID', timestamp_col: str = 'TIME', x_col: str = 'BPOGX', y_col: str = 'BPOGY', pupil_col: str | None = None, validity_col: str | None = None, time_unit: Literal['seconds', 'milliseconds'] = 'seconds', coordinates: Literal['normalized', 'pixels'] = 'normalized', sampling_rate_hz: float | None = None) -> GazeFrame
¶
Adapt Gazepoint-style sample exports using explicitly declared column semantics.
Gazepoint point-of-gaze coordinates are commonly exported as fractions of screen size.
This adapter therefore defaults to coordinates="normalized" and requires the screen
dimensions so the canonical representation is in pixels.
Column names remain configurable because Gazepoint export variants and upstream packages may expose different gaze/fixation fields.
adapt_processed_table(data: pd.DataFrame, *, participant_col: str, trial_col: str, timestamp_col: str, x_col: str, y_col: str, pupil_col: str | None = None, validity_col: str | None = None, timestamp_scale_to_ms: float = 1.0, coordinate_scale: tuple[float, float] = (1.0, 1.0), sampling_rate_hz: float | None = None, screen_size_px: tuple[int, int] | None = None, source_name: str = 'processed_table') -> GazeFrame
¶
Adapt an eyeprocesspy/gpbiometricspy/custom processed table without guessing columns.
aois_to_frame(aois: Sequence[AOI]) -> pd.DataFrame
¶
Convert AOIs to an ordinary reviewable table.
apply_aoi_review(aois: Sequence[AOI], decisions: pd.DataFrame) -> tuple[list[AOI], pd.DataFrame]
¶
Apply explicit human accept/reject/relabel/rebound decisions.
detect_semantic_aois(image: Any, *, labels: Sequence[str], provider: AOIProvider, min_confidence: float = 0.1) -> list[AOI]
¶
Generate semantic AOI proposals and retain only proposals above a threshold.
map_fixations_to_aois(fixations: pd.DataFrame, aois: Sequence[AOI], *, x_col: str = 'x_px', y_col: str = 'y_px', overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame
¶
Assign each fixation to at most one AOI while preserving unassigned rows.
build_audit_report(data: pd.DataFrame, *, trail: AuditTrail | None = None, model_cards: list[ModelCard] | None = None) -> dict[str, Any]
¶
Build a JSON-serialisable snapshot of data, provenance, and model metadata.
gaze_in_wild_manual_event_card() -> BenchmarkDatasetCard
¶
Return metadata for the independently hand-labelled Gaze-in-the-Wild corpus.
hollywood2_manual_event_card() -> BenchmarkDatasetCard
¶
Return metadata for the expert-corrected Hollywood2 eye-movement annotations.
visus_dynamic_aoi_card() -> BenchmarkDatasetCard
¶
Return the reviewed metadata card for the VISUS dynamic-AOI benchmark.
The historical data endpoint has moved/retired, so GazeForge records the benchmark but does not claim to redistribute it or provide a stable downloader until current reuse terms and a surviving authoritative distribution location are independently verified.
benchmark_fingerprint(payload: Any) -> str
¶
Return a SHA-256 fingerprint of canonical JSON content.
build_benchmark_report(*, benchmark: BenchmarkDatasetCard, metrics: dict[str, Any], model: dict[str, Any] | None = None, protocol: dict[str, Any] | None = None) -> dict[str, Any]
¶
Build a benchmark report without adding non-deterministic timestamps.
freeze_benchmark_report(report: dict[str, Any], path: str | Path, *, overwrite: bool = False) -> Path
¶
Write a deterministic benchmark JSON artifact.
Existing files are protected by default so a previously reported validation result cannot be silently replaced during a later run.
evaluate_event_calibration(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', probability_prefix: str = 'p_event_', n_bins: int = 10) -> dict[str, Any]
¶
Return Brier score, ECE, calibration bins, and confidence/coverage diagnostics.
expected_calibration_error(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', probability_prefix: str = 'p_event_', n_bins: int = 10) -> float
¶
Return weighted top-label expected calibration error (ECE).
multiclass_brier_score(y_true: Sequence[object] | pd.Series | np.ndarray, probabilities: pd.DataFrame | np.ndarray, *, labels: Sequence[str] | None = None) -> float
¶
Return the mean multiclass Brier score (lower is better).
selective_accuracy_curve(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', confidence_col: str = 'event_confidence', predicted_label_col: str = 'predicted_event', thresholds: Sequence[float] = (0.0, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95)) -> pd.DataFrame
¶
Report accuracy-versus-coverage as low-confidence samples are abstained from.
top_label_calibration_table(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', probability_prefix: str = 'p_event_', n_bins: int = 10) -> pd.DataFrame
¶
Bin top-label confidence and compare confidence with empirical accuracy.
compare_event_models_grouped(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> EventModelComparison
¶
Compare I-VT, Random Forest, and temporal MLP on identical group-held-out folds.
Each learned model is fitted from scratch within every fold. All three methods are evaluated on the exact same test rows. Calibration metrics are reported only for probabilistic learned models; deterministic I-VT receives missing calibration values rather than fabricated scores.
prepare_cross_dataset_event_benchmark(datasets: Mapping[str, GazeFrame], *, source_audit_lineages: Mapping[str, SourceAuditLineageReceipt] | None = None, target_sampling_rate_hz: float = 60.0, common_labels: Sequence[str] = ('fixation', 'saccade', 'pursuit'), min_label_purity: float = 0.75, ambiguous_label: str = 'ambiguous', require_resolved_participants: bool = True, require_verified_coordinates: bool = True, require_source_audits: bool = True, require_all_common_labels: bool = True) -> CrossDatasetEventPrepared
¶
Prepare multiple human-reference corpora for matched lower-rate validation.
Each source is independently resampled to the requested rate using the benchmark resampling guardrails. Participant and trial identifiers are namespaced by dataset after source identities have been checked, preventing accidental collisions across independently collected corpora. External sources with a reviewed source-audit contract must also supply the matching lineage receipt before their data may enter cross-dataset modelling.
run_cross_dataset_event_validation(prepared: CrossDatasetEventPrepared, *, label_col: str = 'event_label', min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> CrossDatasetEventValidation
¶
Run RF and temporal-context MLP in a leave-one-dataset-out design.
build_benchmark_dashboard(root: str | Path, *, recursive: bool = True) -> BenchmarkDashboard
¶
Build evidence tables from integrity-checked reports and complete suites under root.
Duplicate report and suite fingerprints are rejected so copied artifacts cannot inflate the apparent number of independent validation results or completed tranches on a public dashboard. Provenance-only JSON children are never promoted to performance-report rows. VISUS suites are surfaced only after the Frozen Evidence bundle gate verifies both the suite and its raw-execution provenance manifest.
discover_frozen_benchmark_reports(root: str | Path, *, recursive: bool = True) -> tuple[Path, ...]
¶
Discover benchmark-schema reports while ignoring provenance/config JSON files.
A deterministic report_fingerprint_sha256 can also belong to audited intake or provenance
reports. Those files are deliberately not treated as performance evidence unless they contain
the complete benchmark/model/protocol/metrics report body.
discover_lund2013_suite_manifests(root: str | Path, *, recursive: bool = True) -> tuple[Path, ...]
¶
Discover Lund suite completion manifests without treating them as result rows.
discover_visus_dynamic_aoi_suite_manifests(root: str | Path, *, recursive: bool = True) -> tuple[Path, ...]
¶
Discover VISUS dynamic-AOI completion manifests for strict evidence validation.
load_frozen_benchmark_report(path: str | Path) -> dict[str, Any]
¶
Load and integrity-check one frozen benchmark JSON report.
render_benchmark_dashboard_markdown(dashboard: BenchmarkDashboard) -> str
¶
Render a conservative Markdown evidence index for the documentation website.
validate_frozen_benchmark_report(report: dict[str, Any]) -> str
¶
Validate report structure and recompute its deterministic SHA-256 fingerprint.
The fingerprint is computed from the same four report-body objects used by
:func:gazeforge.benchmarks.build_benchmark_report. Any later edit to benchmark metadata,
protocol, model metadata, or metrics therefore invalidates the report.
detect_dynamic_aois(stimulus: Any, *, labels: Sequence[str], provider: DynamicAOIProvider, min_confidence: float = 0.1) -> list[DynamicAOIKeyframe]
¶
Generate dynamic semantic AOI keyframes with an explicit confidence threshold.
dynamic_aois_from_frame(frame: pd.DataFrame, *, default_source: str = 'manual') -> list[DynamicAOIKeyframe]
¶
Parse a canonical dynamic-AOI keyframe table into validated objects.
Required columns are aoi_id, label, timestamp_ms, xmin, ymin, xmax,
and ymax. Optional confidence/provenance columns are preserved when present. Duplicate
aoi_id/timestamp pairs are rejected because interpolation would otherwise be ambiguous.
dynamic_aois_to_frame(keyframes: Sequence[DynamicAOIKeyframe]) -> pd.DataFrame
¶
Convert timestamped AOI geometry to a reviewable long-format table.
interpolate_dynamic_aoi(keyframes: Sequence[DynamicAOIKeyframe], timestamp_ms: float, *, max_gap_ms: float = 100.0) -> DynamicAOIKeyframe | None
¶
Return exact/interpolated AOI geometry without temporal extrapolation.
Interpolation is only permitted when the requested timestamp lies between two keyframes and
the bracketing interval does not exceed max_gap_ms. Requests outside the observed track
range always return None.
map_fixations_to_dynamic_aois(fixations: pd.DataFrame, keyframes: Sequence[DynamicAOIKeyframe], *, timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', max_interpolation_gap_ms: float = 100.0, overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame
¶
Map timestamped fixations to dynamic AOIs without extrapolating track geometry.
build_dynamic_aoi_benchmark_report(evaluation: DynamicAOIEvaluation, *, benchmark: BenchmarkDatasetCard, model: dict[str, Any] | None = None, protocol: dict[str, Any] | None = None, fixation_agreement: dict[str, Any] | None = None, include_matches: bool = False) -> dict[str, Any]
¶
Build a deterministic benchmark report for dynamic AOI evaluation.
Timestamp-level metrics are retained so aggregate IoU/F1 values can be audited. Full matching rows are optional because long video benchmarks can generate large artifacts.
dynamic_aoi_snapshot(keyframes: Sequence[DynamicAOIKeyframe], timestamp_ms: float, *, max_interpolation_gap_ms: float = 100.0) -> list[AOI]
¶
Resolve all available dynamic AOI tracks into static AOIs at one timestamp.
Geometry outside a track's observed range, or across a bracketing gap larger than
max_interpolation_gap_ms, is omitted rather than extrapolated.
dynamic_fixation_assignment_agreement(fixations: pd.DataFrame, left: Sequence[DynamicAOIKeyframe], right: Sequence[DynamicAOIKeyframe], *, timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', max_interpolation_gap_ms: float = 100.0, overlap_rule: str = 'highest_confidence') -> dict[str, Any]
¶
Compare two dynamic-AOI references through their fixation assignments.
evaluate_dynamic_aoi_tracks(predicted: Sequence[DynamicAOIKeyframe], reference: Sequence[DynamicAOIKeyframe], *, timestamps_ms: Sequence[float], max_interpolation_gap_ms: float = 100.0, min_iou: float = 0.5, require_label_match: bool = False) -> DynamicAOIEvaluation
¶
Evaluate dynamic AOI geometry/semantics on an explicit timestamp grid.
The evaluation grid is supplied by the caller rather than inferred from prediction timestamps. This prevents a model from improving apparent coverage by choosing when it emits keyframes. Empty timestamps contribute no true/false detections but are counted in coverage diagnostics.
aoi_boundary_sensitivity(fixations: pd.DataFrame, aois: Sequence[AOI], *, perturbations_px: Sequence[float] = (-10.0, -5.0, 5.0, 10.0), x_col: str = 'x_px', y_col: str = 'y_px', label_col: str = 'aoi_label') -> pd.DataFrame
¶
Quantify fixation-assignment stability under AOI boundary perturbations.
Positive perturbations expand every boundary. Negative values contract boundaries while retaining only AOIs that still have positive width and height.
aoi_iou(left: AOI, right: AOI) -> float
¶
Return intersection-over-union for two rectangular AOIs.
evaluate_aoi_detection(predicted: Sequence[AOI], reference: Sequence[AOI], *, min_iou: float = 0.5, require_label_match: bool = False) -> dict[str, Any]
¶
Compute geometric and semantic agreement against expert/reference AOIs.
fixation_assignment_agreement(predicted: pd.DataFrame, reference: pd.DataFrame, *, key_cols: tuple[str, ...] = ('participant_id', 'trial_id', 'fixation_index'), label_col: str = 'aoi_label', unassigned_label: str = 'UNASSIGNED') -> dict[str, Any]
¶
Compare AI-derived and reference fixation-to-AOI assignments on aligned fixations.
match_aois(predicted: Sequence[AOI], reference: Sequence[AOI], *, min_iou: float = 0.5, require_label_match: bool = False) -> pd.DataFrame
¶
One-to-one match predicted AOIs to references using maximum total IoU.
Hungarian assignment is applied globally. Matches below min_iou are reported as
unmatched, which prevents weak overlaps from inflating detection performance.
pairwise_aoi_iou(predicted: Sequence[AOI], reference: Sequence[AOI]) -> pd.DataFrame
¶
Return all predicted-reference AOI IoU values in long format.
sample_label_agreement(left: pd.DataFrame, right: pd.DataFrame, *, key_cols: tuple[str, ...] = ('participant_id', 'trial_id', 'timestamp_ms'), label_col: str = 'event_label', missing_label: str = 'MISSING') -> dict[str, Any]
¶
Compare aligned sample-level event labels from two annotators or methods.
evaluate_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation
¶
Evaluate event detection/classification with one-to-one temporal matching.
evaluate_sample_event_predictions(data: pd.DataFrame, *, true_label_col: str = 'event_label', predicted_label_col: str = 'predicted_event', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5, min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation
¶
Segment sample-level truth/predictions and evaluate them at event level.
match_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> pd.DataFrame
¶
One-to-one match predicted events to references within each participant/trial group.
The maximum-total-IoU Hungarian objective is unchanged. Because each validated event stream is internally non-overlapping, only positive temporal-overlap edges can influence that objective. The positive-edge graph is decomposed into independent components before assignment, avoiding a corpus-scale dense Cartesian IoU matrix without changing accepted matches.
samples_to_event_intervals(data: pd.DataFrame, *, label_col: str = 'event_label', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5) -> pd.DataFrame
¶
Convert sample labels to contiguous half-open event intervals.
Segmentation occurs before excluded labels are removed, so an ambiguous/undefined run remains a
hard separator between two otherwise identical event labels. A timestamp gap larger than
max_gap_factor nominal sample periods also starts a new event.
temporal_event_iou(predicted_start_ms: float, predicted_end_ms: float, reference_start_ms: float, reference_end_ms: float) -> float
¶
Return temporal intersection-over-union for two half-open event intervals.
ai_classify_events(data: pd.DataFrame, model: EventModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame
¶
Classify samples with probabilities and enforce sampling-rate compatibility.
evaluate_event_predictions(y_true: pd.Series | np.ndarray, y_pred: pd.Series | np.ndarray) -> dict[str, Any]
¶
Return classification metrics and a labelled confusion matrix.
ivt_classify_events(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_px_s: float = 1000.0) -> pd.DataFrame
¶
Transparent I-VT-style baseline in pixel coordinates.
ivt_classify_events_angular(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_deg_s: float = 45.0) -> pd.DataFrame
¶
Transparent I-VT baseline using geometry-normalized angular velocity.
The default 45 deg/s threshold matches the Lund2013-calibrated maximum fixation velocity reported for the Andersson et al. benchmark. The threshold remains an explicit parameter and should not be treated as universally optimal for every device, task, or population.
train_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, random_state: int = 42, n_estimators: int = 300, rolling_window_ms: float = 80.0) -> EventModel
¶
Fit a probabilistic event model to labelled samples.
This function fits a model; it deliberately does not report validation performance. Scientific evaluation should use participant-held-out and, where applicable, stimulus/dataset-held-out test data.
load_gaze_in_wild_directory(label_root: str | Path, *, process_root: str | Path | None = None, participant_parser: Callable[[Path], str | None] | None = None, labeller: int | None = None, recursive: bool = True, confidence_threshold: float = 0.3) -> GazeFrame
¶
Load a directory of Gaze-in-the-Wild annotation files without guessing identities.
load_gaze_in_wild_mat(label_path: str | Path, *, process_path: str | Path | None = None, participant_id: str | None = None, trial_id: str | None = None, confidence_threshold: float = 0.3) -> GazeFrame
¶
Load one manually annotated Gaze-in-the-Wild recording.
Sampling rate is inferred from LabelData.T rather than hard-coded. The
published eye-tracker acquisition rate (120 Hz) and the official processed-stream
target cadence (300 Hz) are retained as distinct provenance. When ProcessData
is supplied, the official processing schema defines ETG.POR as normalized
scene-camera coordinates whose y axis has already been flipped to MATLAB image
convention; those coordinates are converted to canonical pixels using
ETG.SceneResolution.
run_gaze_in_wild_labeller_agreement(audit: GazeInWildSourceAuditRun, *, left_labeller: int, right_labeller: int, excluded_labels: tuple[str, ...] = ('unlabelled',), exclude_invalid_tracking: bool = True, event_min_iou: float = 0.5, require_complete_overlap: bool = True) -> GazeInWildLabellerAgreementRun
¶
Compare two human labellers only after the Gaze-in-the-Wild source audit passes.
Each shared participant/trial is segmented at its own timestamp-inferred sampling rate before event intervals are pooled in milliseconds. This avoids inventing one nominal cadence for a distributed snapshot whose files may differ. Invalid tracking samples can be retained as hard event separators while being excluded from analysis-label agreement.
audit_gaze_in_wild_source(label_root: str | Path, process_root: str | Path, spec: GazeInWildSourceAuditSpec) -> GazeInWildSourceAuditRun
¶
Verify an authoritative local Gaze-in-the-Wild copy before empirical reporting.
The audit binds exact label/process files to participant, trial, and labeller identities; checks current reuse declarations and coordinate evidence; infers native-file cadence from timestamps; and verifies that different labellers for the same trial reference identical underlying gaze. It produces no model-performance metrics and makes no raw-data redistribution claim.
audited_gaze_in_wild_files_by_labeller(run: GazeInWildSourceAuditRun) -> dict[int, list[GazeInWildAuditedFile]]
¶
Group an already verified audit run by human labeller without merging file cadences.
load_gaze_in_wild_source_audit_spec(path: str | Path) -> GazeInWildSourceAuditSpec
¶
Load a Gaze-in-the-Wild source-audit specification from JSON.
prepare_gaze_in_wild_benchmark(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label') -> GazeInWildPreparedBenchmark
¶
Prepare one lineage-bound audited human labeller for event-model validation.
run_gaze_in_wild_model_validation(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label', n_splits: int = 5, ivt_velocity_threshold_px_s: float = 1000.0, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5) -> GazeInWildModelValidationRun
¶
Run lineage-bound participant-disjoint validation on audited Gaze-in-the-Wild.
angular_kinematic_features(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', screen_width_px_col: str = 'screen_width_px', screen_height_px_col: str = 'screen_height_px', screen_width_physical_col: str = 'screen_width_physical', screen_height_physical_col: str = 'screen_height_physical', view_distance_physical_col: str = 'view_distance_physical') -> pd.DataFrame
¶
Compute boundary-safe sample displacement and angular velocity in degrees/second.
Physical screen dimensions and viewing distance must use the same length unit. Geometry is required to be invariant within each participant/trial group; GazeForge refuses to average conflicting geometry metadata.
pixels_to_visual_angle_deg(pixels: float | Sequence[float] | np.ndarray, *, physical_extent: float, pixel_extent: float, viewing_distance: float) -> np.ndarray
¶
Convert a pixel extent to degrees of visual angle.
physical_extent and viewing_distance may use any shared physical length unit. The
conversion follows the geometry used by Lund2013's pixels2degrees.m helper:
2 * atan((pixels * physical_extent / pixel_extent) / (2 * viewing_distance)).
load_hollywood2_arff(path: str | Path, *, annotator: str = 'final', label_col: str | None = None, participant_id: str | None = None, trial_id: str | None = None, split: str | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, confidence_threshold: float = 0.5, zero_pair_is_missing: bool = True, coordinate_unit: str = 'unverified') -> GazeFrame
¶
Load one Hollywood2EM hand-labelled ARFF recording.
The published/TUM evaluation convention stores time in microseconds and uses x, y,
and confidence for the gaze samples. handlabeller_1 contains the first/student coding
pass and handlabeller_final contains the expert-corrected labels. Participant identity is
deliberately not guessed from filenames; callers should supply it when participant-held-out
validation is intended.
load_hollywood2_directory(root: str | Path, *, annotator: str = 'final', identity_parser: IdentityParser | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, coordinate_unit: str = 'unverified') -> GazeFrame
¶
Load a Hollywood2EM ground-truth tree without guessing participant identities.
identity_parser receives each ARFF path relative to the selected ground-truth directory and
must return (participant_id, trial_id). Without it, every row receives the sentinel
__unresolved__ participant ID; this intentionally prevents accidental participant-held-out
validation until the repository-specific identity mapping has been supplied and audited.
audit_hollywood2_source(root: str | Path, spec: Hollywood2SourceAuditSpec) -> Hollywood2SourceAuditRun
¶
Verify an authoritative local Hollywood2EM copy before empirical modelling.
The audit is intentionally non-statistical: it verifies exact file identity, reuse/analysis declarations, participant/trial mapping, coordinate-unit evidence, native sampling rate, and that the student and expert labels refer to the same underlying gaze samples. It does not produce model-performance metrics and it does not imply raw-data redistribution permission.
load_audited_hollywood2_directory(root: str | Path, spec: Hollywood2SourceAuditSpec, *, annotator: str = 'final') -> GazeFrame
¶
Return one Hollywood2 annotation stream only after the full source audit passes.
load_hollywood2_source_audit_spec(path: str | Path) -> Hollywood2SourceAuditSpec
¶
Load a Hollywood2 source-audit specification from JSON.
load_lund2013_directory(root: str | Path, *, annotator: str = 'RA', recursive: bool = True) -> GazeFrame
¶
Load and concatenate Lund2013 files for one annotator from a benchmark directory.
load_lund2013_mat(path: str | Path, *, participant_id: str | None = None, trial_id: str | None = None, annotator: str | None = None, stimulus_type: str | None = None, zero_pair_is_missing: bool = True) -> GazeFrame
¶
Load one annotated Lund2013 MATLAB file into GazeForge's canonical gaze schema.
The public benchmark stores x/y coordinates in columns 4/5 of MATLAB's one-based pos
matrix and human event codes in column 6. Codes follow the original benchmark convention:
fixation=1, saccade=2, PSO=3, pursuit=4, blink=5, undefined=6; code 0 is retained as
unlabelled rather than silently discarded.
compare_lund2013_annotators(root: str | Path, *, left_annotator: str = 'MN', right_annotator: str = 'RA', target_sampling_rate_hz: float | None = None, min_label_purity: float = 0.75) -> dict[str, Any]
¶
Measure the human-human sample-label agreement ceiling for Lund2013.
prepare_lund2013_benchmark(root: str | Path, *, annotator: str = 'RA', target_sampling_rate_hz: float | None = 60.0, min_label_purity: float = 0.75, max_interpolation_gap_ms: float | None = None, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS) -> Lund2013PreparedBenchmark
¶
Load Lund2013 and prepare an explicit native- or lower-rate benchmark table.
The raw benchmark is never modified. When a lower sampling rate is requested, labels are
transferred with :func:resample_labeled_gaze; ambiguous target windows remain auditable and
are excluded only after their prevalence has been recorded in the preparation report. A
GazeForge source manifest, when present, is revalidated with its referenced files before any
benchmark rows are loaded.
run_lund2013_event_benchmark(root: str | Path, *, annotator: str = 'RA', target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200) -> Lund2013BenchmarkRun
¶
Run matched participant-held-out I-VT/RF/ContextMLP validation on Lund2013.
fetch_lund2013_dataset(destination: str | Path, *, annotators: tuple[str, ...] = LUND2013_ANNOTATORS, stimulus_families: tuple[str, ...] = LUND2013_FAMILIES, overwrite: bool = False) -> Lund2013FetchResult
¶
Fetch the pinned external Lund2013 labelled files into a local directory.
This operation is explicit and opt-in. Raw benchmark files remain external to GazeForge and
retain the upstream repository licence. Existing files are reused only when their Git blob SHA
matches the immutable upstream metadata; mismatching files are never silently replaced unless
overwrite=True is requested.
validate_lund2013_source_manifest(root: str | Path, *, verify_files: bool = True) -> dict[str, Any] | None
¶
Validate a GazeForge Lund source manifest and optionally every referenced local file.
Directories without a GazeForge source manifest remain supported and return None. Once a
manifest exists, however, it is treated as an integrity claim: its own fingerprint, pinned
upstream identity, file inventory, and (by default) every referenced MATLAB file must validate
before the checkout may be recorded as verified benchmark provenance.
run_lund2013_sampling_sensitivity(root: str | Path, *, annotator: str = 'RA', target_sampling_rates_hz: tuple[float, ...] = (120.0, 90.0, 60.0, 30.0), min_label_purities: tuple[float, ...] = (0.6, 0.75, 0.9), n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> Lund2013SensitivityRun
¶
Evaluate Lund2013 across lower sampling rates and label-purity thresholds.
The workflow uses one expert annotation stream at a time, records ambiguity before exclusions, and applies the same default label policy as the primary Lund benchmark. The angular I-VT baseline remains fixed at the supplied degrees/second threshold across the sensitivity surface. A GazeForge source manifest, when present, is revalidated with its referenced files before the sensitivity analysis begins and its verified identity is stored in the report protocol.
run_lund2013_benchmark_suite(root: str | Path, output_dir: str | Path, *, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, sensitivity_target_rates_hz: tuple[float, ...] = (120.0, 90.0, 60.0, 30.0), sensitivity_min_label_purities: tuple[float, ...] = (0.6, 0.75, 0.9), overwrite: bool = False) -> Lund2013BenchmarkSuiteRun
¶
Run and freeze the complete first-pass Lund2013 empirical validation suite.
All analyses are computed before any suite-completion manifest is written. Child reports retain independent fingerprints. The suite manifest is written last, so an interrupted or failed run cannot masquerade as a complete validation tranche merely because one child report exists.
validate_lund2013_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]
¶
Validate a frozen Lund suite manifest and, by default, every referenced child report.
run_native_event_annotator_agreement(data: pd.DataFrame, spec: NativeEventBenchmarkSpec, *, left_annotator: str, right_annotator: str, event_min_iou: float = 0.5, source_file_name: str | None = None, source_file_sha256: str | None = None) -> NativeEventAnnotatorAgreementRun
¶
Quantify native sample-label and event-boundary agreement between two human annotators.
Both annotation streams undergo the same native-rate verification used by model validation. Excluded labels are preserved during event segmentation so undefined/noise runs remain hard temporal separators rather than being deleted first and joining adjacent events.
run_native_event_file_annotator_agreement(data_path: str | Path, spec_path: str | Path, *, left_annotator: str, right_annotator: str, event_min_iou: float = 0.5) -> NativeEventAnnotatorAgreementRun
¶
Load, fingerprint, verify, and compare two native human annotation streams.
file_sha256(path: str | Path) -> str
¶
Return the SHA-256 digest of an external benchmark source file.
load_native_event_spec(path: str | Path) -> NativeEventBenchmarkSpec
¶
Load a native event benchmark specification from JSON.
load_native_event_table(path: str | Path) -> pd.DataFrame
¶
Load a portable native benchmark table from CSV or TSV without altering rows.
prepare_native_event_benchmark(data: pd.DataFrame, spec: NativeEventBenchmarkSpec, *, annotator: str | None = None, source_file_name: str | None = None, source_file_sha256: str | None = None) -> NativeEventPreparedBenchmark
¶
Verify that human-labelled samples are genuinely native-rate benchmark evidence.
No temporal resampling occurs in this function. The declared native sampling rate is checked both globally and within every participant/trial group before the dataset card can claim native human-reference evidence.
run_native_event_benchmark(data: pd.DataFrame, spec: NativeEventBenchmarkSpec, *, annotator: str | None = None, n_splits: int = 5, ivt_velocity_threshold_deg_s: float | None = None, ivt_velocity_threshold_px_s: float | None = None, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, source_file_name: str | None = None, source_file_sha256: str | None = None) -> NativeEventBenchmarkRun
¶
Run matched participant-held-out validation on verified native-rate human labels.
run_native_event_file_benchmark(data_path: str | Path, spec_path: str | Path, **kwargs: Any) -> NativeEventBenchmarkRun
¶
Load, fingerprint, verify, and benchmark one native human-labelled event table.
run_native_event_validation_suite(data_path: str | Path, spec_path: str | Path, output_dir: str | Path, *, primary_annotator: str, sensitivity_annotator: str, event_min_iou: float = 0.5, n_splits: int = 5, ivt_velocity_threshold_deg_s: float | None = None, ivt_velocity_threshold_px_s: float | None = None, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, overwrite: bool = False) -> NativeEventValidationSuiteRun
¶
Compute, freeze, and bind the three-report native event validation tranche.
All analyses are computed and cross-checked before any report is written. The completion manifest is written last. Therefore orphan child reports can never, by themselves, represent a complete validation suite.
validate_native_event_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]
¶
Validate a native-event suite manifest and, by default, all child reports.
paired_model_metric_differences(fold_metrics: pd.DataFrame, *, model_col: str = 'model', fold_col: str = 'fold', metrics: tuple[str, ...] | None = None, tie_tolerance: float = 1e-12) -> PairedModelDifferences
¶
Compare model metrics on exactly matched folds without inferential p-values.
Raw deltas are always model_a - model_b. improvement_for_a multiplies the raw delta by
the registered metric direction so positive values always mean model A performed better. The
function is deliberately descriptive: cross-validation folds share training data and are not
treated as independent replicates for hypothesis tests or confidence intervals.
fingerprint_frame(data: pd.DataFrame) -> str
¶
Create a stable SHA-256 fingerprint from values, index, columns, and dtypes.
ai_flag_anomalies(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, contamination: float | str = 'auto', random_state: int = 42, trail: AuditTrail | None = None) -> pd.DataFrame
¶
Flag unusual samples with Isolation Forest without deleting or rewriting samples.
detect_calibration_drift(data: pd.DataFrame, *, expected_x_col: str, expected_y_col: str, threshold_px: float = 100.0, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame
¶
Score drift only when known calibration/reference target coordinates are supplied.
score_trial_quality(data: pd.DataFrame, *, screen_size_px: tuple[int, int] | None = None, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame
¶
Summarise missingness, bounds, anomaly rate, and temporal gaps per trial.
resample_labeled_gaze(data: pd.DataFrame, *, target_sampling_rate_hz: float = 60.0, label_col: str = 'event_label', timestamp_col: str = 'timestamp_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), continuous_cols: Sequence[str] = ('x_px', 'y_px', 'pupil'), carry_cols: Sequence[str] = ('annotator', 'stimulus_type', 'dataset_id', 'source_file', 'screen_width_px', 'screen_height_px', 'screen_width_physical', 'screen_height_physical', 'view_distance_physical'), min_label_purity: float = 0.75, ambiguous_label: str = 'ambiguous', max_interpolation_gap_ms: float | None = None, source_sampling_rate_hz: float | None = None) -> BenchmarkResamplingResult
¶
Resample expert-labelled gaze to a lower rate with explicit boundary uncertainty.
Continuous signals are linearly interpolated only across short valid gaps. Event labels are
assigned by majority vote within one target-sample window. Windows with tied labels or purity
below min_label_purity are marked as ambiguous_label instead of silently forcing an
event identity near a source annotation boundary.
evaluate_sampling_purity_sensitivity(data: pd.DataFrame, *, target_sampling_rates_hz: Sequence[float] = (120.0, 90.0, 60.0, 30.0), min_label_purities: Sequence[float] = (0.6, 0.75, 0.9), source_sampling_rate_hz: float | None = None, label_col: str = 'event_label', group_col: str = 'participant_id', resampling_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), ambiguous_label: str = 'ambiguous', excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, n_splits: int = 5, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> SamplingSensitivityResult
¶
Evaluate model sensitivity to target sampling rate and boundary-label purity.
Every rate/purity condition is retained in settings. Ambiguous and other excluded labels
are removed only after their prevalence has been recorded, matching the primary Lund benchmark
policy. Conditions that no longer contain enough groups or labels for the requested validation
design are recorded as not_evaluable instead of being silently dropped.
cluster_scanpaths_ai(embeddings: pd.DataFrame, *, n_clusters: int, embedding_prefix: str = 'embedding_', random_state: int = 42) -> pd.DataFrame
¶
Cluster learned scanpath embeddings with K-means.
embed_scanpaths(scanpaths: pd.DataFrame, model: ScanpathEmbeddingModel, *, text_col: str = 'sequence_text', id_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame
¶
Transform scanpaths into numeric embeddings.
find_scanpath_motifs(scanpaths: pd.DataFrame, *, sequence_col: str = 'sequence', ngram_range: tuple[int, int] = (2, 3), min_count: int = 2) -> pd.DataFrame
¶
Count recurrent contiguous AOI n-grams.
fit_scanpath_embedder(scanpaths: pd.DataFrame, *, text_col: str = 'sequence_text', n_components: int = 16, ngram_range: tuple[int, int] = (1, 3), random_state: int = 42) -> ScanpathEmbeddingModel
¶
Fit a learned semantic scanpath representation.
scanpath_similarity(embeddings: pd.DataFrame, *, embedding_prefix: str = 'embedding_') -> np.ndarray
¶
Return pairwise cosine similarity for learned embeddings.
to_semantic_scanpaths(fixations: pd.DataFrame, *, label_col: str = 'aoi_label', duration_col: str | None = 'duration_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), collapse_repeats: bool = True, drop_unassigned: bool = True) -> pd.DataFrame
¶
Convert ordered fixation rows into one semantic sequence per trial.
canonicalize_gaze(data: pd.DataFrame, *, column_map: Mapping[str, str] | None = None, sampling_rate_hz: float | None = None, screen_size_px: tuple[int, int] | None = None, metadata: Mapping[str, Any] | None = None, sort: bool = True) -> GazeFrame
¶
Convert a table to GazeForge's canonical sample schema.
column_map maps canonical names to source-column names, for example
{"timestamp_ms": "TIME", "x_px": "BPOGX", "y_px": "BPOGY"}.
infer_sampling_rate_hz(data: pd.DataFrame, *, timestamp_col: str = 'timestamp_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> float
¶
Infer sampling rate from the median positive within-trial timestamp interval.
simulate_gaze(*, n_participants: int = 4, n_trials: int = 3, samples_per_trial: int = 240, sampling_rate_hz: float = 60.0, screen_size_px: tuple[int, int] = (1920, 1080), random_state: int = 42) -> pd.DataFrame
¶
Simulate smooth gaze with occasional saccade-like jumps and missing samples.
load_source_resolution_record(path: str | Path) -> SourceResolutionRecord
¶
Return a typed common view after dataset-specific source-resolution validation.
validate_gaze_in_wild_source_resolution_record(path: str | Path) -> dict[str, Any]
¶
Validate the conservative Gaze-in-the-Wild source-resolution checkpoint.
validate_hollywood2_source_resolution_record(path: str | Path) -> dict[str, Any]
¶
Validate the reviewed Hollywood2EM recovered-source checkpoint.
validate_source_resolution_record(path: str | Path) -> dict[str, Any]
¶
Validate a known v1 source-resolution checkpoint and auto-dispatch by dataset.
validate_source_resolution_records(paths: Sequence[str | Path]) -> dict[str, Any]
¶
Validate source-resolution checkpoints and fingerprint the reviewed bundle.
build_source_resolution_dashboard(root: str | Path, *, lock_path: str | Path | None = None) -> SourceResolutionDashboard
¶
Discover and validate source-resolution checkpoints and an optional reviewed lock.
Supplying lock_path upgrades only the dashboard's governance-integrity statement: the live
checkpoint bundle must exactly match the separately frozen reviewed snapshot. It does not
itself upgrade source authority, rights, source-audit readiness, or empirical status.
render_source_resolution_dashboard_markdown(dashboard: SourceResolutionDashboard) -> str
¶
Render governance status without conflating it with performance evidence.
discover_source_resolution_paths(root: str | Path) -> tuple[Path, ...]
¶
Discover the complete flat set of source-resolution checkpoints under root.
Discovery is intentionally filename-constrained and strict. Every matching file must be a JSON object with the reviewed v1 record type; malformed or mislabeled candidates fail rather than disappearing from the governance gate.
validate_source_resolution_directory(root: str | Path) -> dict[str, Any]
¶
Discover and validate every committed source-resolution checkpoint in a directory.
build_source_resolution_bundle_lock(protocol_directory: str | Path, *, reviewed_on: str, review_basis: Sequence[str]) -> dict[str, Any]
¶
Build a deterministic lock payload from the currently validated checkpoint bundle.
Building a lock does not upgrade any source-resolution state. The returned payload explicitly records that it is a non-empirical governance snapshot and cannot authorize source audit, empirical evidence, or Frozen Evidence publication.
load_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> SourceResolutionBundleLock
¶
Return a typed reviewed lock after exact current-bundle validation.
validate_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> dict[str, Any]
¶
Validate one reviewed bundle lock against the complete current checkpoint directory.
summarize_event_predictions_by_stratum(predictions: pd.DataFrame, *, stratify_col: str, label_col: str = 'event_label', model_col: str = 'comparison_model', fold_col: str = 'validation_fold', group_col: str = 'participant_id', sampling_rate_hz: float, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> StratifiedEventPerformance
¶
Summarize fixed out-of-fold predictions by a declared analysis stratum.
This function never fits or refits a model. It is intended for post-hoc descriptive validation of predictions that were already generated under a leakage-safe validation design. Fold-level metrics are computed first and then summarized, preserving fold-to-fold variability.
ai_classify_events_context(data: pd.DataFrame, model: TemporalContextModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame
¶
Classify samples using temporal context with probabilities and an abstention threshold.
train_context_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200, random_state: int = 42) -> TemporalContextModel
¶
Fit an MLP to temporal windows without crossing participant/trial boundaries.
This is the first temporal-context baseline, not a claim that an MLP is scientifically superior to I-VT, Random Forest, temporal CNN, or transformer alternatives. Performance must be established under participant- and dataset-held-out validation.
assert_no_group_leakage(train: pd.DataFrame, test: pd.DataFrame, *, group_cols: tuple[str, ...] = ('participant_id',)) -> None
¶
Raise if any protected grouping unit appears in both train and test.
dataset_holdout_context_event_validate(data: pd.DataFrame, *, dataset_col: str = 'dataset_id', participant_col: str = 'participant_id', label_col: str = 'event_label', sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200, require_disjoint_participants: bool = True) -> ValidationResult
¶
Evaluate temporal-context events by leaving each dataset out in turn.
dataset_holdout_event_validate(data: pd.DataFrame, *, dataset_col: str = 'dataset_id', participant_col: str = 'participant_id', label_col: str = 'event_label', sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, require_disjoint_participants: bool = True) -> ValidationResult
¶
Leave each dataset out in turn to measure cross-dataset generalisation.
A fresh model is trained for every held-out dataset. When
require_disjoint_participants=True, the function refuses a split in which participant IDs
occur in both train and test, preventing identity leakage across dataset labels.
grouped_context_event_cross_validate(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200) -> ValidationResult
¶
Evaluate the temporal-context event model with group-held-out folds.
grouped_event_cross_validate(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200) -> ValidationResult
¶
Evaluate event classification with participant/group-held-out folds.
This function intentionally fits a fresh model inside every fold. It never trains on samples from a group that is present in that fold's test partition.
grouped_holdout_indices(data: pd.DataFrame, *, group_col: str = 'participant_id', test_size: float = 0.2, random_state: int = 42) -> tuple[np.ndarray, np.ndarray]
¶
Return one train/test split with groups strictly isolated between partitions.
run_visus_dynamic_aoi_human_agreement(audit: VisusSourceAuditRun, *, left_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], right_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], left_stream_id: str, right_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIHumanAgreementRun
¶
Measure agreement only between independently verified VISUS AOI streams.
The ordinary single curated VISUS stream is intentionally insufficient. The source audit must first establish separately recoverable independent streams. Metrics are then computed in both reference directions so neither human stream is treated as error-free ground truth.
audit_visus_source(root: str | Path, spec: VisusSourceAuditSpec) -> VisusSourceAuditRun
¶
Verify an exact VISUS snapshot before dynamic-AOI empirical analysis.
The published benchmark describes one manual AOI annotation process involving two human contributors. Contributor count is kept separate from independently recoverable annotation streams, so human-human agreement is enabled only after independent streams are verified.
load_visus_source_audit_spec(path: str | Path) -> VisusSourceAuditSpec
¶
Load a VISUS source-audit specification from JSON.
load_visus_frozen_evidence_bundle(path: str | Path) -> VisusFrozenEvidenceBundle
¶
Return a compact typed record after full bundle validation.
validate_visus_frozen_evidence_bundle(path: str | Path) -> dict[str, Any]
¶
Require both the verified VISUS suite and its raw-execution provenance manifest.
This is a publication-eligibility integrity gate, not a scientific validity decision. It refuses a suite that cannot be tied to the execution-provenance layer introduced for the exact reviewed source-audit JSON, human AOI table, model prediction table, and external timestamp-grid JSON. It does not establish that the underlying source is authoritative or that its reuse terms are correct; those remain source-audit evidence decisions.
prepare_visus_canonical_aoi_intake(audit: VisusSourceAuditRun, table: pd.DataFrame, *, extraction_basis: str, frame_index_base: int, require_complete_manifest_coverage: bool = True) -> VisusCanonicalAOIIntakeRun
¶
Validate a reviewed VISUS AOI extraction and convert frames to canonical keyframes.
This function deliberately does not parse ViPER XML. It accepts a separately extracted,
reviewable table and requires every row to link to the exact AOI XML file already verified by
VisusSourceAuditRun. Frame-to-time conversion uses the audited published video frame rate
and an explicit 0- or 1-based frame convention supplied by the caller.
prepare_visus_dynamic_aoi_predictions(audit: VisusSourceAuditRun, table: pd.DataFrame, *, model_name: str, model_version: str, prediction_basis: str, prediction_coordinate_unit: str, frame_index_base: int, model_artifact_sha256: str | None = None, require_complete_stimulus_coverage: bool = True) -> VisusDynamicAOIPredictionIntakeRun
¶
Canonicalize externally generated VISUS model AOI tracks with audited provenance.
The function expects frame-indexed model detections/tracks. It links every stimulus to the exact
audited VISUS video file, converts frame indices with the audited video rate, and returns
DynamicAOIKeyframe mappings suitable for model-human validation. It deliberately does not
create an evaluation timestamp grid; prediction emission frames must never define that grid.
run_visus_dynamic_aoi_validation_suite(audit: VisusSourceAuditRun, reference_intake: VisusCanonicalAOIIntakeRun, prediction_intake: VisusDynamicAOIPredictionIntakeRun, timestamps_by_stimulus: Mapping[str, Sequence[float]], output_dir: str | Path, *, reference_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', human_agreement_streams: tuple[str, str] | None = None, include_matches: bool = False, overwrite: bool = False) -> VisusDynamicAOIValidationSuiteRun
¶
Compute, freeze, cross-check, and bind the audited VISUS validation tranche.
Human-human agreement becomes a required child when the source audit verifies independently recoverable streams. When independence is not verified, the suite explicitly records that the child is unavailable rather than inferring reliability from the published contributor count.
validate_visus_dynamic_aoi_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]
¶
Validate a VISUS dynamic-AOI suite manifest and its child reports.
run_visus_dynamic_aoi_model_validation(audit: VisusSourceAuditRun, *, predicted_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], reference_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], reference_stream_id: str, model_name: str, model_version: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIModelValidationRun
¶
Evaluate one explicit model against one audited VISUS human-reference stream.
Predictions and references use canonical DynamicAOIKeyframe objects. Timestamp grids are
supplied explicitly and must cover every audited stimulus, preventing prediction emission times
from becoming the evaluation grid. This function orchestrates deterministic model-human
evaluation only; it does not parse the historical VISUS XML/video formats or create empirical
evidence without a separately verified source audit.
Core modules¶
Schema¶
gazeforge.schema
¶
Canonical, vendor-neutral gaze schema.
GazeFrame
dataclass
¶
Validated canonical gaze samples plus recording metadata.
copy() -> GazeFrame
¶
Return an independent copy.
canonicalize_gaze(data: pd.DataFrame, *, column_map: Mapping[str, str] | None = None, sampling_rate_hz: float | None = None, screen_size_px: tuple[int, int] | None = None, metadata: Mapping[str, Any] | None = None, sort: bool = True) -> GazeFrame
¶
Convert a table to GazeForge's canonical sample schema.
column_map maps canonical names to source-column names, for example
{"timestamp_ms": "TIME", "x_px": "BPOGX", "y_px": "BPOGY"}.
infer_sampling_rate_hz(data: pd.DataFrame, *, timestamp_col: str = 'timestamp_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> float
¶
Infer sampling rate from the median positive within-trial timestamp interval.
Quality control¶
gazeforge.qc
¶
Auditable quality control and anomaly scoring.
ai_flag_anomalies(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, contamination: float | str = 'auto', random_state: int = 42, trail: AuditTrail | None = None) -> pd.DataFrame
¶
Flag unusual samples with Isolation Forest without deleting or rewriting samples.
detect_calibration_drift(data: pd.DataFrame, *, expected_x_col: str, expected_y_col: str, threshold_px: float = 100.0, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame
¶
Score drift only when known calibration/reference target coordinates are supplied.
score_trial_quality(data: pd.DataFrame, *, screen_size_px: tuple[int, int] | None = None, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame
¶
Summarise missingness, bounds, anomaly rate, and temporal gaps per trial.
Eye events¶
gazeforge.events
¶
Probabilistic and classical eye-event classification.
EventModel
dataclass
¶
A fitted probabilistic event classifier plus compatibility metadata.
ai_classify_events(data: pd.DataFrame, model: EventModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame
¶
Classify samples with probabilities and enforce sampling-rate compatibility.
evaluate_event_predictions(y_true: pd.Series | np.ndarray, y_pred: pd.Series | np.ndarray) -> dict[str, Any]
¶
Return classification metrics and a labelled confusion matrix.
ivt_classify_events(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_px_s: float = 1000.0) -> pd.DataFrame
¶
Transparent I-VT-style baseline in pixel coordinates.
ivt_classify_events_angular(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_deg_s: float = 45.0) -> pd.DataFrame
¶
Transparent I-VT baseline using geometry-normalized angular velocity.
The default 45 deg/s threshold matches the Lund2013-calibrated maximum fixation velocity reported for the Andersson et al. benchmark. The threshold remains an explicit parameter and should not be treated as universally optimal for every device, task, or population.
train_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, random_state: int = 42, n_estimators: int = 300, rolling_window_ms: float = 80.0) -> EventModel
¶
Fit a probabilistic event model to labelled samples.
This function fits a model; it deliberately does not report validation performance. Scientific evaluation should use participant-held-out and, where applicable, stimulus/dataset-held-out test data.
Temporal event models¶
gazeforge.temporal
¶
Sampling-rate-aware temporal-context models for eye-event classification.
TemporalContextModel
dataclass
¶
A fitted context-window neural event classifier plus compatibility metadata.
ai_classify_events_context(data: pd.DataFrame, model: TemporalContextModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame
¶
Classify samples using temporal context with probabilities and an abstention threshold.
train_context_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200, random_state: int = 42) -> TemporalContextModel
¶
Fit an MLP to temporal windows without crossing participant/trial boundaries.
This is the first temporal-context baseline, not a claim that an MLP is scientifically superior to I-VT, Random Forest, temporal CNN, or transformer alternatives. Performance must be established under participant- and dataset-held-out validation.
Semantic AOIs¶
gazeforge.aoi
¶
Semantic areas of interest (AOIs), AI providers, and human review.
AOI
dataclass
¶
Rectangular semantic area of interest with provenance metadata.
AOIProvider
¶
Bases: Protocol
Protocol implemented by semantic AOI proposal engines.
detect(image: Any, labels: Sequence[str]) -> list[AOI]
¶
Return semantic AOI proposals.
CallableAOIProvider
dataclass
¶
Adapter for custom/local detectors used by research teams.
detect(image: Any, labels: Sequence[str]) -> list[AOI]
¶
Run the supplied detector.
HuggingFaceZeroShotAOIProvider
dataclass
¶
Optional OWL-ViT zero-shot object detector via Transformers.
detect(image: Any, labels: Sequence[str]) -> list[AOI]
¶
Return open-vocabulary rectangular proposals.
aois_to_frame(aois: Sequence[AOI]) -> pd.DataFrame
¶
Convert AOIs to an ordinary reviewable table.
apply_aoi_review(aois: Sequence[AOI], decisions: pd.DataFrame) -> tuple[list[AOI], pd.DataFrame]
¶
Apply explicit human accept/reject/relabel/rebound decisions.
detect_semantic_aois(image: Any, *, labels: Sequence[str], provider: AOIProvider, min_confidence: float = 0.1) -> list[AOI]
¶
Generate semantic AOI proposals and retain only proposals above a threshold.
map_fixations_to_aois(fixations: pd.DataFrame, aois: Sequence[AOI], *, x_col: str = 'x_px', y_col: str = 'y_px', overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame
¶
Assign each fixation to at most one AOI while preserving unassigned rows.
Dynamic AOIs¶
gazeforge.dynamic_aoi
¶
Dynamic semantic AOIs for video and moving-interface eye-tracking stimuli.
CallableDynamicAOIProvider
dataclass
¶
Adapter for custom local dynamic-AOI detectors and trackers.
track(stimulus: Any, labels: Sequence[str]) -> list[DynamicAOIKeyframe]
¶
Run the supplied detector/tracker.
DynamicAOIKeyframe
dataclass
¶
Timestamped rectangular geometry for one semantic AOI track.
DynamicAOIProvider
¶
Bases: Protocol
Protocol implemented by detection/tracking engines for moving AOIs.
track(stimulus: Any, labels: Sequence[str]) -> list[DynamicAOIKeyframe]
¶
Return timestamped AOI keyframes for tracked semantic regions.
detect_dynamic_aois(stimulus: Any, *, labels: Sequence[str], provider: DynamicAOIProvider, min_confidence: float = 0.1) -> list[DynamicAOIKeyframe]
¶
Generate dynamic semantic AOI keyframes with an explicit confidence threshold.
dynamic_aois_from_frame(frame: pd.DataFrame, *, default_source: str = 'manual') -> list[DynamicAOIKeyframe]
¶
Parse a canonical dynamic-AOI keyframe table into validated objects.
Required columns are aoi_id, label, timestamp_ms, xmin, ymin, xmax,
and ymax. Optional confidence/provenance columns are preserved when present. Duplicate
aoi_id/timestamp pairs are rejected because interpolation would otherwise be ambiguous.
dynamic_aois_to_frame(keyframes: Sequence[DynamicAOIKeyframe]) -> pd.DataFrame
¶
Convert timestamped AOI geometry to a reviewable long-format table.
interpolate_dynamic_aoi(keyframes: Sequence[DynamicAOIKeyframe], timestamp_ms: float, *, max_gap_ms: float = 100.0) -> DynamicAOIKeyframe | None
¶
Return exact/interpolated AOI geometry without temporal extrapolation.
Interpolation is only permitted when the requested timestamp lies between two keyframes and
the bracketing interval does not exceed max_gap_ms. Requests outside the observed track
range always return None.
map_fixations_to_dynamic_aois(fixations: pd.DataFrame, keyframes: Sequence[DynamicAOIKeyframe], *, timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', max_interpolation_gap_ms: float = 100.0, overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame
¶
Map timestamped fixations to dynamic AOIs without extrapolating track geometry.
Scanpaths¶
gazeforge.scanpath
¶
Semantic scanpaths, motifs, learned embeddings, similarity, and clustering.
ScanpathEmbeddingModel
dataclass
¶
TF-IDF n-gram encoder with optional learned SVD compression.
cluster_scanpaths_ai(embeddings: pd.DataFrame, *, n_clusters: int, embedding_prefix: str = 'embedding_', random_state: int = 42) -> pd.DataFrame
¶
Cluster learned scanpath embeddings with K-means.
embed_scanpaths(scanpaths: pd.DataFrame, model: ScanpathEmbeddingModel, *, text_col: str = 'sequence_text', id_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame
¶
Transform scanpaths into numeric embeddings.
find_scanpath_motifs(scanpaths: pd.DataFrame, *, sequence_col: str = 'sequence', ngram_range: tuple[int, int] = (2, 3), min_count: int = 2) -> pd.DataFrame
¶
Count recurrent contiguous AOI n-grams.
fit_scanpath_embedder(scanpaths: pd.DataFrame, *, text_col: str = 'sequence_text', n_components: int = 16, ngram_range: tuple[int, int] = (1, 3), random_state: int = 42) -> ScanpathEmbeddingModel
¶
Fit a learned semantic scanpath representation.
scanpath_similarity(embeddings: pd.DataFrame, *, embedding_prefix: str = 'embedding_') -> np.ndarray
¶
Return pairwise cosine similarity for learned embeddings.
to_semantic_scanpaths(fixations: pd.DataFrame, *, label_col: str = 'aoi_label', duration_col: str | None = 'duration_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), collapse_repeats: bool = True, drop_unassigned: bool = True) -> pd.DataFrame
¶
Convert ordered fixation rows into one semantic sequence per trial.
Validation modules¶
Candidate source inventories¶
gazeforge.source_candidate
¶
Exact non-empirical inventories for candidate external benchmark copies.
CandidateSourceFile
dataclass
¶
One exact regular file in a candidate external benchmark snapshot.
to_dict() -> dict[str, Any]
¶
Return a deterministic JSON-compatible file record.
CandidateSourceInventory
dataclass
¶
build_candidate_source_inventory(root: str | Path, *, dataset_key: str) -> CandidateSourceInventory
¶
Fingerprint a candidate Hollywood2EM or Gaze-in-the-Wild copy without inferring semantics.
The inventory deliberately records only safe relative paths, byte sizes, and SHA-256 digests. File names, directory names, extensions, and apparent structure are not converted into participant, trial, annotator, coordinate, licensing, source-authority, or empirical-evidence claims.
validate_candidate_source_inventory(inventory_path: str | Path, root: str | Path) -> CandidateSourceInventory
¶
Revalidate a saved candidate inventory against the complete current local tree.
write_candidate_source_inventory(inventory: CandidateSourceInventory, path: str | Path, *, overwrite: bool = False) -> Path
¶
Write a portable inventory outside the candidate source tree.
Candidate source review scaffolds¶
gazeforge.source_candidate_review
¶
Non-empirical review scaffolds bound to exact candidate source inventories.
CandidateSourceReviewFile
dataclass
¶
One exact candidate file plus deliberately unresolved scientific review fields.
to_dict() -> dict[str, Any]
¶
Return a deterministic JSON-compatible review row.
CandidateSourceReviewScaffold
dataclass
¶
Portable manual-review worksheet tied to one exact candidate copy.
to_dict() -> dict[str, Any]
¶
Return the portable review-scaffold payload.
build_candidate_source_review_scaffold(inventory: CandidateSourceInventory) -> CandidateSourceReviewScaffold
¶
Create an unresolved review worksheet from one exact candidate inventory.
Paths, SHA-256 digests, and byte sizes are copied exactly. No file role, participant, trial, labeller, source-authority, licensing, coordinate, or empirical interpretation is inferred.
validate_candidate_source_review_scaffold(review_path: str | Path, inventory_path: str | Path, root: str | Path) -> CandidateSourceReviewScaffold
¶
Revalidate one manually editable review scaffold against the exact candidate copy.
Scientific review fields may be edited, but exact file path/hash/size identity and the non-empirical scientific boundary cannot change. Included rows must also form a coherent dataset-specific manual mapping. The scaffold never becomes an audit approval.
write_candidate_source_review_scaffold(scaffold: CandidateSourceReviewScaffold, path: str | Path, *, overwrite: bool = False) -> Path
¶
Write a review scaffold outside the candidate tree it describes.
Candidate source audit-template compiler¶
gazeforge.source_candidate_audit_template
¶
Compile reviewed candidate mappings into deliberately non-empirical audit templates.
compile_candidate_source_audit_template(scaffold: CandidateSourceReviewScaffold) -> AuditTemplateSpec
¶
Compile a reviewed candidate worksheet into an existing audit-spec template type.
The compiler never sets dataset_status='empirical' and never sets any scientific approval
boolean to true. Its output is directly loadable by the existing dataset-specific audit-spec
loaders, but the empirical audit runners continue to reject it until a separate explicit human
authorization step updates the relevant evidence fields.
write_candidate_source_audit_template(spec: AuditTemplateSpec, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path
¶
Write one non-empirical audit-spec template outside the candidate source tree.
Candidate source audit authorization¶
gazeforge.source_candidate_authorization
¶
Explicit human authorization gate between reviewed templates and empirical source audits.
CandidateSourceAuditAuthorization
dataclass
¶
Manual decision record bound to one exact non-empirical audit template.
authorize_candidate_source_audit_template(spec: AuditTemplateSpec, authorization: CandidateSourceAuditAuthorization, *, gaze_in_wild_quarantine_exit: GazeInWildQuarantineExitAuthorization | None = None) -> AuditTemplateSpec
¶
Materialize an empirical audit spec from separately reviewed authorization decisions.
This function authorizes execution of the existing source audit. It does not execute that audit, does not verify the local data copy, and does not create agreement or model evidence. Recovered Gaze-in-the-Wild candidates additionally require a separately reviewed quarantine- exit authorization bound to the exact same audit template.
build_candidate_source_audit_authorization(spec: AuditTemplateSpec) -> CandidateSourceAuditAuthorization
¶
Create a pending manual authorization record bound to one exact audit template.
load_candidate_source_audit_authorization(path: str | Path) -> CandidateSourceAuditAuthorization
¶
Load one manual source-audit authorization JSON record.
source_audit_template_fingerprint(spec: AuditTemplateSpec) -> str
¶
Fingerprint one complete source-audit template deterministically.
validate_candidate_source_audit_authorization(authorization_path: str | Path, spec: AuditTemplateSpec) -> CandidateSourceAuditAuthorization
¶
Validate one manual decision against the exact template it reviews.
write_authorized_source_audit_spec(spec: AuditTemplateSpec, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path
¶
Write an already-authorized empirical source-audit spec outside the candidate tree.
write_candidate_source_audit_authorization(authorization: CandidateSourceAuditAuthorization, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path
¶
Write an authorization record outside the candidate source tree.
Source-audit lineage receipts¶
gazeforge.source_audit_lineage
¶
Cryptographic lineage receipts for authorized source-audit reports.
SourceAuditLineageReceipt
dataclass
¶
Verified chain from reviewed template through authorization to source-audit report.
build_source_audit_lineage_receipt(template_spec: AuditTemplateSpec, authorization: CandidateSourceAuditAuthorization, audit_report: Mapping[str, Any], *, gaze_in_wild_quarantine_exit: GazeInWildQuarantineExitAuthorization | None = None) -> SourceAuditLineageReceipt
¶
Verify and bind the full reviewed-template → authorization → source-audit chain.
The function recomputes the authorized empirical specification deterministically from the original template and authorization, then requires the audit report to fingerprint that exact specification and to pass dataset-specific source-audit invariants. Recovered Gaze-in-the-Wild lineage also carries the separately reviewed quarantine-exit fingerprint. It creates no new model, agreement, AOI, native-GP3, or Frozen Evidence result.
load_source_audit_lineage_receipt(path: str | Path) -> SourceAuditLineageReceipt
¶
Load and self-validate a saved source-audit lineage receipt.
write_source_audit_lineage_receipt(receipt: SourceAuditLineageReceipt, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path
¶
Write one verified lineage receipt outside the candidate source tree.
Downstream source-audit lineage gates¶
gazeforge.downstream_lineage
¶
Fail-closed lineage gates for downstream benchmark preparation and validation.
validate_gaze_in_wild_audit_lineage(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt) -> str
¶
Bind a Gaze-in-the-Wild audit run to its exact lineage receipt.
validate_hollywood2_gaze_lineage(gaze: GazeFrame, lineage: SourceAuditLineageReceipt) -> str
¶
Bind an audited Hollywood2EM GazeFrame to its exact lineage receipt.
validate_source_audit_lineage_binding(lineage: SourceAuditLineageReceipt, *, dataset_key: str, audit_report_fingerprint_sha256: str, authorized_spec_fingerprint_sha256: str, source_manifest_fingerprints_sha256: Mapping[str, Any], source_revision: str) -> str
¶
Require one lineage receipt to match the exact audited source being consumed.
This gate does not create or upgrade empirical evidence. It only prevents a downstream benchmark from accepting a verified source audit that has been detached from the exact lineage receipt created for that audit.
Source-resolution checkpoints¶
gazeforge.source_resolution
¶
Unified validation for benchmark source-resolution checkpoints.
SourceResolutionRecord
dataclass
¶
Compact common identity for one validated benchmark source-resolution checkpoint.
load_source_resolution_record(path: str | Path) -> SourceResolutionRecord
¶
Return a typed common view after dataset-specific source-resolution validation.
validate_gaze_in_wild_source_resolution_record(path: str | Path) -> dict[str, Any]
¶
Validate the conservative Gaze-in-the-Wild source-resolution checkpoint.
validate_hollywood2_source_resolution_record(path: str | Path) -> dict[str, Any]
¶
Validate the reviewed Hollywood2EM recovered-source checkpoint.
validate_source_resolution_record(path: str | Path) -> dict[str, Any]
¶
Validate a known v1 source-resolution checkpoint and auto-dispatch by dataset.
validate_source_resolution_records(paths: Sequence[str | Path]) -> dict[str, Any]
¶
Validate source-resolution checkpoints and fingerprint the reviewed bundle.
Source-resolution discovery¶
gazeforge.source_resolution_discovery
¶
Discovery of committed source-resolution checkpoints for governance validation.
discover_source_resolution_paths(root: str | Path) -> tuple[Path, ...]
¶
Discover the complete flat set of source-resolution checkpoints under root.
Discovery is intentionally filename-constrained and strict. Every matching file must be a JSON object with the reviewed v1 record type; malformed or mislabeled candidates fail rather than disappearing from the governance gate.
validate_source_resolution_directory(root: str | Path) -> dict[str, Any]
¶
Discover and validate every committed source-resolution checkpoint in a directory.
Source-resolution status dashboard¶
gazeforge.source_resolution_dashboard
¶
Public governance dashboard for benchmark source-resolution checkpoints.
SourceResolutionDashboard
dataclass
¶
Integrity-checked source-resolution records prepared for public status reporting.
build_source_resolution_dashboard(root: str | Path, *, lock_path: str | Path | None = None) -> SourceResolutionDashboard
¶
Discover and validate source-resolution checkpoints and an optional reviewed lock.
Supplying lock_path upgrades only the dashboard's governance-integrity statement: the live
checkpoint bundle must exactly match the separately frozen reviewed snapshot. It does not
itself upgrade source authority, rights, source-audit readiness, or empirical status.
render_source_resolution_dashboard_markdown(dashboard: SourceResolutionDashboard) -> str
¶
Render governance status without conflating it with performance evidence.
Source-resolution reviewed bundle lock¶
gazeforge.source_resolution_lock
¶
Reviewed snapshot locks for non-empirical source-resolution governance bundles.
SourceResolutionBundleLock
dataclass
¶
Typed identity of a validated reviewed source-resolution snapshot.
build_source_resolution_bundle_lock(protocol_directory: str | Path, *, reviewed_on: str, review_basis: Sequence[str]) -> dict[str, Any]
¶
Build a deterministic lock payload from the currently validated checkpoint bundle.
Building a lock does not upgrade any source-resolution state. The returned payload explicitly records that it is a non-empirical governance snapshot and cannot authorize source audit, empirical evidence, or Frozen Evidence publication.
load_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> SourceResolutionBundleLock
¶
Return a typed reviewed lock after exact current-bundle validation.
validate_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> dict[str, Any]
¶
Validate one reviewed bundle lock against the complete current checkpoint directory.
Model comparison¶
gazeforge.comparison
¶
Leakage-safe comparison of classical and learned eye-event classifiers.
EventModelComparison
dataclass
¶
Predictions, fold metrics, and aggregate summaries for matched validation folds.
compare_event_models_grouped(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> EventModelComparison
¶
Compare I-VT, Random Forest, and temporal MLP on identical group-held-out folds.
Each learned model is fitted from scratch within every fold. All three methods are evaluated on the exact same test rows. Calibration metrics are reported only for probabilistic learned models; deterministic I-VT receives missing calibration values rather than fabricated scores.
Matched-fold model differences¶
gazeforge.paired
¶
Matched-fold descriptive differences for event-model validation metrics.
PairedModelDifferences
dataclass
¶
Per-fold paired deltas plus descriptive summaries for every model pair.
paired_model_metric_differences(fold_metrics: pd.DataFrame, *, model_col: str = 'model', fold_col: str = 'fold', metrics: tuple[str, ...] | None = None, tie_tolerance: float = 1e-12) -> PairedModelDifferences
¶
Compare model metrics on exactly matched folds without inferential p-values.
Raw deltas are always model_a - model_b. improvement_for_a multiplies the raw delta by
the registered metric direction so positive values always mean model A performed better. The
function is deliberately descriptive: cross-validation folds share training data and are not
treated as independent replicates for hypothesis tests or confidence intervals.
Event-level evaluation¶
gazeforge.event_evaluation
¶
Event-level eye-movement segmentation and temporal matching metrics.
EventLevelEvaluation
dataclass
¶
Event intervals, one-to-one matches, and aggregate/per-class metrics.
evaluate_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation
¶
Evaluate event detection/classification with one-to-one temporal matching.
evaluate_sample_event_predictions(data: pd.DataFrame, *, true_label_col: str = 'event_label', predicted_label_col: str = 'predicted_event', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5, min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation
¶
Segment sample-level truth/predictions and evaluate them at event level.
match_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> pd.DataFrame
¶
One-to-one match predicted events to references within each participant/trial group.
The maximum-total-IoU Hungarian objective is unchanged. Because each validated event stream is internally non-overlapping, only positive temporal-overlap edges can influence that objective. The positive-edge graph is decomposed into independent components before assignment, avoiding a corpus-scale dense Cartesian IoU matrix without changing accepted matches.
samples_to_event_intervals(data: pd.DataFrame, *, label_col: str = 'event_label', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5) -> pd.DataFrame
¶
Convert sample labels to contiguous half-open event intervals.
Segmentation occurs before excluded labels are removed, so an ambiguous/undefined run remains a
hard separator between two otherwise identical event labels. A timestamp gap larger than
max_gap_factor nominal sample periods also starts a new event.
temporal_event_iou(predicted_start_ms: float, predicted_end_ms: float, reference_start_ms: float, reference_end_ms: float) -> float
¶
Return temporal intersection-over-union for two half-open event intervals.
Stratified out-of-fold performance¶
gazeforge.stratified
¶
Post-hoc stratified metrics for leakage-safe out-of-fold event predictions.
StratifiedEventPerformance
dataclass
¶
Fold-wise and aggregate metrics computed from fixed out-of-fold predictions.
summarize_event_predictions_by_stratum(predictions: pd.DataFrame, *, stratify_col: str, label_col: str = 'event_label', model_col: str = 'comparison_model', fold_col: str = 'validation_fold', group_col: str = 'participant_id', sampling_rate_hz: float, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> StratifiedEventPerformance
¶
Summarize fixed out-of-fold predictions by a declared analysis stratum.
This function never fits or refits a model. It is intended for post-hoc descriptive validation of predictions that were already generated under a leakage-safe validation design. Fold-level metrics are computed first and then summarized, preserving fold-to-fold variability.
Sampling sensitivity¶
gazeforge.sampling_sensitivity
¶
Sampling-rate and annotation-boundary sensitivity for labelled gaze benchmarks.
SamplingSensitivityResult
dataclass
¶
Complete settings ledger and model metrics for a resampling sensitivity surface.
evaluate_sampling_purity_sensitivity(data: pd.DataFrame, *, target_sampling_rates_hz: Sequence[float] = (120.0, 90.0, 60.0, 30.0), min_label_purities: Sequence[float] = (0.6, 0.75, 0.9), source_sampling_rate_hz: float | None = None, label_col: str = 'event_label', group_col: str = 'participant_id', resampling_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), ambiguous_label: str = 'ambiguous', excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, n_splits: int = 5, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> SamplingSensitivityResult
¶
Evaluate model sensitivity to target sampling rate and boundary-label purity.
Every rate/purity condition is retained in settings. Ambiguous and other excluded labels
are removed only after their prevalence has been recorded, matching the primary Lund benchmark
policy. Conditions that no longer contain enough groups or labels for the requested validation
design are recorded as not_evaluable instead of being silently dropped.
Lund sensitivity workflow¶
gazeforge.lund_sensitivity
¶
Lund2013 sampling-rate and annotation-boundary sensitivity workflow.
Lund2013SensitivityRun
dataclass
¶
Sensitivity surface, dataset evidence card, and deterministic benchmark report.
run_lund2013_sampling_sensitivity(root: str | Path, *, annotator: str = 'RA', target_sampling_rates_hz: tuple[float, ...] = (120.0, 90.0, 60.0, 30.0), min_label_purities: tuple[float, ...] = (0.6, 0.75, 0.9), n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> Lund2013SensitivityRun
¶
Evaluate Lund2013 across lower sampling rates and label-purity thresholds.
The workflow uses one expert annotation stream at a time, records ambiguity before exclusions, and applies the same default label policy as the primary Lund benchmark. The angular I-VT baseline remains fixed at the supplied degrees/second threshold across the sensitivity surface. A GazeForge source manifest, when present, is revalidated with its referenced files before the sensitivity analysis begins and its verified identity is stored in the report protocol.
Cross-dataset validation¶
gazeforge.cross_dataset
¶
Cross-dataset eye-event benchmark preparation and validation.
CrossDatasetEventPrepared
dataclass
¶
Harmonised lower-rate data plus dataset-level preparation reports.
CrossDatasetEventValidation
dataclass
¶
Leave-one-dataset-out results for the two learned GazeForge baselines.
prepare_cross_dataset_event_benchmark(datasets: Mapping[str, GazeFrame], *, source_audit_lineages: Mapping[str, SourceAuditLineageReceipt] | None = None, target_sampling_rate_hz: float = 60.0, common_labels: Sequence[str] = ('fixation', 'saccade', 'pursuit'), min_label_purity: float = 0.75, ambiguous_label: str = 'ambiguous', require_resolved_participants: bool = True, require_verified_coordinates: bool = True, require_source_audits: bool = True, require_all_common_labels: bool = True) -> CrossDatasetEventPrepared
¶
Prepare multiple human-reference corpora for matched lower-rate validation.
Each source is independently resampled to the requested rate using the benchmark resampling guardrails. Participant and trial identifiers are namespaced by dataset after source identities have been checked, preventing accidental collisions across independently collected corpora. External sources with a reviewed source-audit contract must also supply the matching lineage receipt before their data may enter cross-dataset modelling.
run_cross_dataset_event_validation(prepared: CrossDatasetEventPrepared, *, label_col: str = 'event_label', min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> CrossDatasetEventValidation
¶
Run RF and temporal-context MLP in a leave-one-dataset-out design.
Benchmark adapters¶
Lund2013¶
gazeforge.lund2013
¶
Adapter for the manually annotated Lund2013 eye-movement benchmark files.
load_lund2013_directory(root: str | Path, *, annotator: str = 'RA', recursive: bool = True) -> GazeFrame
¶
Load and concatenate Lund2013 files for one annotator from a benchmark directory.
load_lund2013_mat(path: str | Path, *, participant_id: str | None = None, trial_id: str | None = None, annotator: str | None = None, stimulus_type: str | None = None, zero_pair_is_missing: bool = True) -> GazeFrame
¶
Load one annotated Lund2013 MATLAB file into GazeForge's canonical gaze schema.
The public benchmark stores x/y coordinates in columns 4/5 of MATLAB's one-based pos
matrix and human event codes in column 6. Codes follow the original benchmark convention:
fixation=1, saccade=2, PSO=3, pursuit=4, blink=5, undefined=6; code 0 is retained as
unlabelled rather than silently discarded.
Hollywood2EM¶
gazeforge.hollywood2
¶
Adapter for the manually annotated Hollywood2 eye-movement benchmark.
load_hollywood2_arff(path: str | Path, *, annotator: str = 'final', label_col: str | None = None, participant_id: str | None = None, trial_id: str | None = None, split: str | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, confidence_threshold: float = 0.5, zero_pair_is_missing: bool = True, coordinate_unit: str = 'unverified') -> GazeFrame
¶
Load one Hollywood2EM hand-labelled ARFF recording.
The published/TUM evaluation convention stores time in microseconds and uses x, y,
and confidence for the gaze samples. handlabeller_1 contains the first/student coding
pass and handlabeller_final contains the expert-corrected labels. Participant identity is
deliberately not guessed from filenames; callers should supply it when participant-held-out
validation is intended.
load_hollywood2_directory(root: str | Path, *, annotator: str = 'final', identity_parser: IdentityParser | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, coordinate_unit: str = 'unverified') -> GazeFrame
¶
Load a Hollywood2EM ground-truth tree without guessing participant identities.
identity_parser receives each ARFF path relative to the selected ground-truth directory and
must return (participant_id, trial_id). Without it, every row receives the sentinel
__unresolved__ participant ID; this intentionally prevents accidental participant-held-out
validation until the repository-specific identity mapping has been supplied and audited.
Hollywood2EM authoritative evidence¶
gazeforge.hollywood2_evidence
¶
Validation for the pinned authoritative Hollywood2EM ground-truth evidence.
The evidence binds the canonical GIN repository to an exact revision and to all 697 hand-labelled ground-truth ARFF blobs. It also freezes the observed student-to-expert-corrected annotation sensitivity while explicitly refusing to reinterpret the sequential correction workflow as independent human-human agreement.
Hollywood2AuthoritativeEvidence
dataclass
¶
Compact identity of the validated Hollywood2EM evidence record.
evidence_fingerprint(record: Mapping[str, Any]) -> str
¶
Return the evidence SHA-256 excluding its self-fingerprint field.
load_hollywood2_authoritative_evidence(path: str | Path) -> Hollywood2AuthoritativeEvidence
¶
Load and validate the evidence, returning a compact typed identity.
validate_hollywood2_authoritative_evidence(record_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]
¶
Validate the immutable Hollywood2 authoritative ground-truth v1 record.
validate_hollywood2_gin_probe(probe_or_path: Mapping[str, Any] | str | Path, evidence_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]
¶
Bind a freshly generated canonical GIN probe to the frozen evidence.
Hollywood2EM source audit¶
gazeforge.hollywood2_audit
¶
Auditable source-manifest verification for Hollywood2EM empirical use.
Hollywood2SourceAuditRun
dataclass
¶
Verified source audit plus both human annotation streams.
Hollywood2SourceAuditSpec
dataclass
¶
Evidence contract required before Hollywood2EM is used in frozen modelling.
__post_init__() -> None
¶
Keep templates non-empirical and make empirical audits fully explicit.
from_dict(payload: Mapping[str, Any]) -> Hollywood2SourceAuditSpec
classmethod
¶
Construct a source-audit specification from decoded JSON.
to_dict() -> dict[str, Any]
¶
Return a deterministic JSON-compatible specification mapping.
Hollywood2SourceFileRecord
dataclass
¶
One audited Hollywood2EM ARFF file and its participant/trial identity.
audit_hollywood2_source(root: str | Path, spec: Hollywood2SourceAuditSpec) -> Hollywood2SourceAuditRun
¶
Verify an authoritative local Hollywood2EM copy before empirical modelling.
The audit is intentionally non-statistical: it verifies exact file identity, reuse/analysis declarations, participant/trial mapping, coordinate-unit evidence, native sampling rate, and that the student and expert labels refer to the same underlying gaze samples. It does not produce model-performance metrics and it does not imply raw-data redistribution permission.
load_audited_hollywood2_directory(root: str | Path, spec: Hollywood2SourceAuditSpec, *, annotator: str = 'final') -> GazeFrame
¶
Return one Hollywood2 annotation stream only after the full source audit passes.
load_hollywood2_source_audit_spec(path: str | Path) -> Hollywood2SourceAuditSpec
¶
Load a Hollywood2 source-audit specification from JSON.
Gaze-in-the-Wild¶
gazeforge.gaze_in_wild
¶
Adapter for the manually annotated Gaze-in-the-Wild event benchmark.
load_gaze_in_wild_directory(label_root: str | Path, *, process_root: str | Path | None = None, participant_parser: Callable[[Path], str | None] | None = None, labeller: int | None = None, recursive: bool = True, confidence_threshold: float = 0.3) -> GazeFrame
¶
Load a directory of Gaze-in-the-Wild annotation files without guessing identities.
load_gaze_in_wild_mat(label_path: str | Path, *, process_path: str | Path | None = None, participant_id: str | None = None, trial_id: str | None = None, confidence_threshold: float = 0.3) -> GazeFrame
¶
Load one manually annotated Gaze-in-the-Wild recording.
Sampling rate is inferred from LabelData.T rather than hard-coded. The
published eye-tracker acquisition rate (120 Hz) and the official processed-stream
target cadence (300 Hz) are retained as distinct provenance. When ProcessData
is supplied, the official processing schema defines ETG.POR as normalized
scene-camera coordinates whose y axis has already been flipped to MATLAB image
convention; those coordinates are converted to canonical pixels using
ETG.SceneResolution.
Gaze-in-the-Wild source audit¶
gazeforge.gaze_in_wild_audit
¶
Auditable source verification for Gaze-in-the-Wild empirical evidence.
GazeInWildAuditedFile
dataclass
¶
One verified label/process pair with its loaded gaze stream.
GazeInWildLabelFileRecord
dataclass
¶
One audited human-labelled MATLAB stream and its identity mapping.
GazeInWildProcessFileRecord
dataclass
¶
One audited ProcessData MATLAB file.
GazeInWildSourceAuditRun
dataclass
¶
Verified source audit and all audited per-labeller streams.
GazeInWildSourceAuditSpec
dataclass
¶
Evidence contract required before Gaze-in-the-Wild results are frozen.
audit_gaze_in_wild_source(label_root: str | Path, process_root: str | Path, spec: GazeInWildSourceAuditSpec) -> GazeInWildSourceAuditRun
¶
Verify an authoritative local Gaze-in-the-Wild copy before empirical reporting.
The audit binds exact label/process files to participant, trial, and labeller identities; checks current reuse declarations and coordinate evidence; infers native-file cadence from timestamps; and verifies that different labellers for the same trial reference identical underlying gaze. It produces no model-performance metrics and makes no raw-data redistribution claim.
audited_gaze_in_wild_files_by_labeller(run: GazeInWildSourceAuditRun) -> dict[int, list[GazeInWildAuditedFile]]
¶
Group an already verified audit run by human labeller without merging file cadences.
gaze_in_wild_sampling_rate_table(run: GazeInWildSourceAuditRun) -> pd.DataFrame
¶
Return the audited per-file timestamp-inferred sampling-rate ledger.
load_gaze_in_wild_source_audit_spec(path: str | Path) -> GazeInWildSourceAuditSpec
¶
Load a Gaze-in-the-Wild source-audit specification from JSON.
Gaze-in-the-Wild labeller agreement¶
gazeforge.gaze_in_wild_agreement
¶
Rate-aware human-human agreement for audited Gaze-in-the-Wild label streams.
GazeInWildLabellerAgreementRun
dataclass
¶
Aligned audited streams and pooled rate-aware human-human agreement evidence.
run_gaze_in_wild_labeller_agreement(audit: GazeInWildSourceAuditRun, *, left_labeller: int, right_labeller: int, excluded_labels: tuple[str, ...] = ('unlabelled',), exclude_invalid_tracking: bool = True, event_min_iou: float = 0.5, require_complete_overlap: bool = True) -> GazeInWildLabellerAgreementRun
¶
Compare two human labellers only after the Gaze-in-the-Wild source audit passes.
Each shared participant/trial is segmented at its own timestamp-inferred sampling rate before event intervals are pooled in milliseconds. This avoids inventing one nominal cadence for a distributed snapshot whose files may differ. Invalid tracking samples can be retained as hard event separators while being excluded from analysis-label agreement.
Gaze-in-the-Wild model validation¶
gazeforge.gaze_in_wild_validation
¶
Audited participant-held-out model validation for Gaze-in-the-Wild.
GazeInWildModelValidationRun
dataclass
¶
Prepared data, participant-held-out comparisons, sensitivities, and report.
GazeInWildPreparedBenchmark
dataclass
¶
One audited labeller prepared at a common analysis cadence.
prepare_gaze_in_wild_benchmark(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label') -> GazeInWildPreparedBenchmark
¶
Prepare one lineage-bound audited human labeller for event-model validation.
run_gaze_in_wild_model_validation(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label', n_splits: int = 5, ivt_velocity_threshold_px_s: float = 1000.0, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5) -> GazeInWildModelValidationRun
¶
Run lineage-bound participant-disjoint validation on audited Gaze-in-the-Wild.
VISUS public partial 60 Hz evidence¶
gazeforge.visus_public_partial
¶
Validation for the pinned public VISUS-derived 60 Hz partial evidence record.
This module is deliberately separate from :mod:gazeforge.visus_evidence.
The original VISUS Frozen Evidence gate remains reserved for a fully audited
25-participant by 11-stimulus source. This record certifies only the exact
public derivative files and metrics recovered from the pinned VISUS-supervised
eye-slitscan repository.
VisusPublicPartialEvidence
dataclass
¶
Compact identity of the validated public VISUS-derived partial evidence.
evidence_fingerprint(record: Mapping[str, Any]) -> str
¶
Return the SHA-256 fingerprint of an evidence record excluding its fingerprint field.
load_visus_public_partial_evidence(path: str | Path) -> VisusPublicPartialEvidence
¶
Load the committed v1 record and return a compact validated identity.
validate_visus_public_partial_evidence(record_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]
¶
Validate the immutable v1 public VISUS-derived partial empirical record.
The validator certifies only the exact three public Tobii recordings and one dynamic-AOI stimulus recovered at the pinned upstream commit. It explicitly does not confer full VISUS source-audit status, dataset redistribution rights, human-human agreement, model validation, Frozen Evidence eligibility, or native Gazepoint GP3 status.
validate_visus_public_partial_probe(probe_or_path: Mapping[str, Any] | str | Path, evidence_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]
¶
Bind a live exact-source probe to the immutable committed v1 evidence record.
VISUS public 60 Hz event-extension evidence¶
gazeforge.visus_public_event_extension
¶
Validation for the pinned public VISUS-derived 60 Hz event-extension evidence.
This module certifies two complete public Tobii exports recovered from the
VISUS-supervised eye-slitscan repository. Participant identity and 60 Hz
event metrics are file-bound; the likely 03-dialog stimulus identity is
explicitly retained as an inference rather than promoted to source resolution.
VisusPublicEventExtensionEvidence
dataclass
¶
Compact identity of the validated VISUS public event-extension evidence.
evidence_fingerprint(record: Mapping[str, Any]) -> str
¶
Return the SHA-256 fingerprint excluding the self-fingerprint field.
load_visus_public_event_extension_evidence(record_or_path: Mapping[str, Any] | str | Path) -> VisusPublicEventExtensionEvidence
¶
Validate and return a compact typed identity for the frozen evidence.
validate_visus_public_event_extension_evidence(record_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]
¶
Validate the immutable v1 VISUS public 60 Hz event-extension record.
validate_visus_public_event_extension_probe(probe_or_path: Mapping[str, Any] | str | Path, evidence_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]
¶
Bind a freshly generated upstream probe to the immutable evidence record.
VISUS source inventory scaffold¶
gazeforge.visus_scaffold
¶
Non-empirical source-inventory scaffolding for prospective VISUS audit copies.
VisusSourceAuditScaffold
dataclass
¶
Exact local inventory paired with a deliberately non-empirical audit specification.
file_count: int
property
¶
Number of inventoried regular files.
build_visus_source_audit_scaffold(root: str | Path) -> VisusSourceAuditScaffold
¶
Inventory a candidate local VISUS tree without inferring scientific identities.
Every file is intentionally emitted with role='other' and without stimulus, participant,
or annotation-stream identities. The returned specification remains
dataset_status='template' with all empirical evidence gates unresolved. A researcher must
manually review and edit those fields before :func:gazeforge.audit_visus_source can accept
the copy as empirical evidence.
write_visus_source_audit_scaffold(scaffold: VisusSourceAuditScaffold, path: str | Path, *, overwrite: bool = False) -> Path
¶
Write one loadable template audit-spec JSON outside the inventoried source tree.
VISUS source audit¶
gazeforge.visus_audit
¶
Authoritative-source audit contract for the VISUS dynamic-AOI benchmark.
VisusAuditedFile
dataclass
¶
One verified local file paired with its manifest record.
VisusSourceAuditRun
dataclass
¶
Verified VISUS snapshot and deterministic source-audit report.
VisusSourceAuditSpec
dataclass
¶
Reviewed provenance and exact-file contract for a VISUS dataset copy.
to_dict() -> dict[str, Any]
¶
Return a deterministic JSON-ready representation.
VisusSourceFileRecord
dataclass
¶
One exact file identity in an audited VISUS snapshot.
audit_visus_source(root: str | Path, spec: VisusSourceAuditSpec) -> VisusSourceAuditRun
¶
Verify an exact VISUS snapshot before dynamic-AOI empirical analysis.
The published benchmark describes one manual AOI annotation process involving two human contributors. Contributor count is kept separate from independently recoverable annotation streams, so human-human agreement is enabled only after independent streams are verified.
load_visus_source_audit_spec(path: str | Path) -> VisusSourceAuditSpec
¶
Load a VISUS source-audit specification from JSON.
VISUS canonical AOI intake¶
gazeforge.visus_intake
¶
Audited canonical AOI intake for VISUS without guessing the raw XML schema.
VisusCanonicalAOIIntakeRun
dataclass
¶
Canonical VISUS AOIs linked back to an exact audited source snapshot.
prepare_visus_canonical_aoi_intake(audit: VisusSourceAuditRun, table: pd.DataFrame, *, extraction_basis: str, frame_index_base: int, require_complete_manifest_coverage: bool = True) -> VisusCanonicalAOIIntakeRun
¶
Validate a reviewed VISUS AOI extraction and convert frames to canonical keyframes.
This function deliberately does not parse ViPER XML. It accepts a separately extracted,
reviewable table and requires every row to link to the exact AOI XML file already verified by
VisusSourceAuditRun. Frame-to-time conversion uses the audited published video frame rate
and an explicit 0- or 1-based frame convention supplied by the caller.
VISUS model prediction intake¶
gazeforge.visus_prediction
¶
Audited model-prediction intake for VISUS dynamic AOI validation.
VisusDynamicAOIPredictionIntakeRun
dataclass
¶
Canonical model AOI predictions linked to an audited VISUS video snapshot.
prepare_visus_dynamic_aoi_predictions(audit: VisusSourceAuditRun, table: pd.DataFrame, *, model_name: str, model_version: str, prediction_basis: str, prediction_coordinate_unit: str, frame_index_base: int, model_artifact_sha256: str | None = None, require_complete_stimulus_coverage: bool = True) -> VisusDynamicAOIPredictionIntakeRun
¶
Canonicalize externally generated VISUS model AOI tracks with audited provenance.
The function expects frame-indexed model detections/tracks. It links every stimulus to the exact
audited VISUS video file, converts frame indices with the audited video rate, and returns
DynamicAOIKeyframe mappings suitable for model-human validation. It deliberately does not
create an evaluation timestamp grid; prediction emission frames must never define that grid.
VISUS model-human validation¶
gazeforge.visus_validation
¶
Source-audit-aware model-human dynamic-AOI validation for VISUS.
VisusDynamicAOIModelValidationRun
dataclass
¶
Per-stimulus model-human evaluations and a deterministic benchmark report.
run_visus_dynamic_aoi_model_validation(audit: VisusSourceAuditRun, *, predicted_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], reference_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], reference_stream_id: str, model_name: str, model_version: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIModelValidationRun
¶
Evaluate one explicit model against one audited VISUS human-reference stream.
Predictions and references use canonical DynamicAOIKeyframe objects. Timestamp grids are
supplied explicitly and must cover every audited stimulus, preventing prediction emission times
from becoming the evaluation grid. This function orchestrates deterministic model-human
evaluation only; it does not parse the historical VISUS XML/video formats or create empirical
evidence without a separately verified source audit.
VISUS human-human agreement¶
gazeforge.visus_agreement
¶
Conditional human-human dynamic-AOI agreement for audited VISUS sources.
VisusDynamicAOIHumanAgreementRun
dataclass
¶
Bidirectional dynamic-AOI agreement between two verified independent streams.
run_visus_dynamic_aoi_human_agreement(audit: VisusSourceAuditRun, *, left_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], right_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], left_stream_id: str, right_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIHumanAgreementRun
¶
Measure agreement only between independently verified VISUS AOI streams.
The ordinary single curated VISUS stream is intentionally insufficient. The source audit must first establish separately recoverable independent streams. Metrics are then computed in both reference directions so neither human stream is treated as error-free ground truth.
VISUS validation suite¶
gazeforge.visus_suite
¶
Atomic orchestration and verification for audited VISUS dynamic-AOI validation.
VisusDynamicAOIValidationSuiteRun
dataclass
¶
Frozen VISUS provenance/validation reports plus a completion manifest.
run_visus_dynamic_aoi_validation_suite(audit: VisusSourceAuditRun, reference_intake: VisusCanonicalAOIIntakeRun, prediction_intake: VisusDynamicAOIPredictionIntakeRun, timestamps_by_stimulus: Mapping[str, Sequence[float]], output_dir: str | Path, *, reference_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', human_agreement_streams: tuple[str, str] | None = None, include_matches: bool = False, overwrite: bool = False) -> VisusDynamicAOIValidationSuiteRun
¶
Compute, freeze, cross-check, and bind the audited VISUS validation tranche.
Human-human agreement becomes a required child when the source audit verifies independently recoverable streams. When independence is not verified, the suite explicitly records that the child is unavailable rather than inferring reliability from the published contributor count.
validate_visus_dynamic_aoi_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]
¶
Validate a VISUS dynamic-AOI suite manifest and its child reports.
VISUS execution provenance¶
gazeforge.visus_execution
¶
Raw-input provenance binding for guarded VISUS suite execution.
VisusExecutionInputSnapshot
dataclass
¶
Exact raw-file identity captured before VISUS execution.
VisusExecutionProvenanceRun
dataclass
¶
Frozen raw-input-to-suite provenance manifest.
build_visus_execution_provenance(audit: VisusSourceAuditRun, suite: VisusDynamicAOIValidationSuiteRun, snapshots: tuple[VisusExecutionInputSnapshot, ...]) -> dict[str, Any]
¶
Bind exact raw input files to one fully verified frozen VISUS suite.
snapshot_visus_execution_inputs(*, source_audit_spec: str | Path, human_aoi_table: str | Path, model_prediction_table: str | Path, timestamp_grid_json: str | Path) -> tuple[VisusExecutionInputSnapshot, ...]
¶
Fingerprint the four raw files consumed by the guarded VISUS CLI.
The source-audit JSON additionally receives a semantic fingerprint of the parsed
:class:~gazeforge.visus_audit.VisusSourceAuditSpec, allowing the execution manifest to prove
that the exact raw JSON corresponds to the specification used by the source audit.
validate_visus_execution_provenance(path: str | Path, *, verify_suite: bool = True) -> dict[str, Any]
¶
Validate a frozen raw-input provenance manifest and optionally its sibling suite.
verify_visus_execution_inputs_unchanged(snapshots: tuple[VisusExecutionInputSnapshot, ...], *, source_audit_spec: str | Path, human_aoi_table: str | Path, model_prediction_table: str | Path, timestamp_grid_json: str | Path) -> None
¶
Refuse provenance freezing if any raw input changed during suite execution.
visus_execution_provenance_path(output_dir: str | Path) -> Path
¶
Return the fixed provenance-manifest path for one VISUS suite directory.
write_visus_execution_provenance(manifest: dict[str, Any], output_dir: str | Path, *, overwrite: bool = False) -> VisusExecutionProvenanceRun
¶
Freeze an execution provenance manifest after its fingerprint revalidates.
VISUS Frozen Evidence bundle gate¶
gazeforge.visus_evidence
¶
Eligibility gate for publishing audited VISUS suites as Frozen Evidence.
VisusFrozenEvidenceBundle
dataclass
¶
Verified suite plus execution-provenance identities eligible for evidence review.
load_visus_frozen_evidence_bundle(path: str | Path) -> VisusFrozenEvidenceBundle
¶
Return a compact typed record after full bundle validation.
validate_visus_frozen_evidence_bundle(path: str | Path) -> dict[str, Any]
¶
Require both the verified VISUS suite and its raw-execution provenance manifest.
This is a publication-eligibility integrity gate, not a scientific validity decision. It refuses a suite that cannot be tied to the execution-provenance layer introduced for the exact reviewed source-audit JSON, human AOI table, model prediction table, and external timestamp-grid JSON. It does not establish that the underlying source is authoritative or that its reuse terms are correct; those remain source-audit evidence decisions.