--- title: "Multimodal Process IRT: Responses, Time, Gaze, and Missingness" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Multimodal Process IRT: Responses, Time, Gaze, and Missingness} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(eyeprocess) ``` # Measurement channels, not feature dumping The 0.7 architecture treats response-process observations as explicit measurement channels. A process variable is not automatically useful merely because it predicts an outcome. It should have a declared role, latent target, family, provenance, and validation programme. ```{r} spec <- irt_model_spec( id = "accuracy_time_gaze", latent = c("ability", "speed", "engagement"), channels = list( response = irt_response_channel("2pl"), rt = irt_rt_channel("lognormal"), gaze = irt_count_channel("negative_binomial") ), status = "experimental" ) spec ``` Other channels include nominal choices, survival/event time, compositional AOI measurements, process sequences, functional trajectories, and bounded continuous process measures. ```{r} irt_continuous_channel("censored_normal", value = "evidence_dwell_proportion") irt_sequence_channel("scanpath", family = "hmm") ``` # Registry ```{r} list_irt_models() ``` Models can be registered and later promoted only after their validation evidence passes an explicit gate. ```{r, eval=FALSE} register_irt_model(spec) validate_irt_model("accuracy_time_gaze", validation_data) promote_irt_model("accuracy_time_gaze", evidence = evidence_object) ``` # Response + response time + gaze `fit_joint_gaze_rt_irt()` supports two roles: * `engine = "reference"` gives a transparent crossed-effects decomposition for development and validation; * `engine = "brms"` builds a multivariate Bayesian model with shared grouping identifiers, which is the preferred route when a full Bayesian joint model is scientifically required. ```{r, eval=FALSE} fit <- fit_joint_gaze_rt_irt( data = trials, response = "correct", rt = "rt_ms", gaze = "fixation_count", person = "person_id", item = "item_id", gaze_family = "negative_binomial", engine = "brms" ) plot(fit) ``` The function does not claim that a convenient reference engine is identical to the published three-way Bayesian model. That distinction is kept in the fit metadata. # Graded responses The same idea extends to ordinal/graded outcomes: ```{r, eval=FALSE} fit_joint_graded_rt_process_irt( data = trials, response = "rating", rt = "rt_ms", process = "fixation_count", person = "person_id", item = "item_id", engine = "brms" ) ``` This is experimental until parameter recovery and external validation are completed. # Nominal distractors + option gaze Binary correct/incorrect scoring discards which alternative was selected. A nominal process model can retain both the selected option and visual consideration of each option. ```{r, eval=FALSE} fit <- fit_nominal_gaze_irt( data = option_trials, response_option = "choice", option_gaze = c("dwell_A", "dwell_B", "dwell_C", "dwell_D"), item = "item_id", person = "person_id" ) option_process_information(fit) distractor_process_map(fit) audit_distractor_attention(fit) plot(fit) ``` Interpretation should stay process-based: an option attracted or retained more visual processing. This does not establish why. # Missingness as a process ```{r, eval=FALSE} missing <- classify_item_missingness( trials, response = "response", reached = "reached", inspected = "inspected_response_region", started = "started_response" ) audit <- fit_omission_survival_irt( data = missing, response = "correct", response_time = "rt", omission_time = "elapsed", reached = "reached", person = "person_id", item = "item_id" ) plot(audit) ``` The classification separates not reached, reached but not inspected, inspected omission, and started-but-unanswered cases instead of converting them all to `NA`. # Device and algorithm facets ```{r, eval=FALSE} facet_fit <- fit_manyfacet_process_irt( data = trials, response = "correct", process = "fixation_count", person = "person_id", item = "item_id", device = "device", session = "session", algorithm = "fixation_algorithm" ) facet_effects(facet_fit) audit_process_measurement_invariance(facet_fit) plot(facet_fit) ``` A complementary `generalizability_process_study()` decomposes variance before a full measurement model is attempted. # Bounded gaze measures AOI proportions and similar process quantities often have real mass at 0 and 1. The conditional censored-normal calibration helper respects those bounds rather than silently applying ordinary Gaussian regression. ```{r, eval=FALSE} cn <- fit_censored_normal_process_irt( response_matrix = aoi_proportion_matrix, theta = calibration_theta, lower = 0, upper = 1 ) predict(cn, theta = seq(-2, 2, length.out = 9)) ``` This is conditional calibration given supplied `theta`; it is not labelled as the full marginal EM estimator from the 2026 CNRM paper.