--- title: "From Content Validity to Empirical Validation" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{From Content Validity to Empirical Validation} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(contentvalidR) ``` ## Where content validity stops A content-validity pretest answers questions you can ask before anyone responds to the scale: are these items relevant to the construct, does the set cover the domain, do experts agree, and is each item congruent with its intended objective. It cannot answer whether an item will correlate with its construct, load on the intended factor, behave the same across groups, or relate to other constructs as theory predicts. Those questions need response data. So a pretest ends with a decision: which items go forward into data collection. `content_handoff()` packages that decision, so the item set and the reasons behind it travel together into the next stage. ## A worked panel Four experts rate five items for relevance on a 1-4 scale. The last item is rated poorly, and one is borderline: ```{r panel} relevance <- matrix( c(4,4,4,3, 4,4,3,4, 3,4,4,4, 3,2,3,2, 2,2,1,2), nrow = 4, dimnames = list(NULL, paste0("Item", 1:5)) ) fit <- expert_validity(relevance, mode = "relevance", lo = 1, hi = 4, seed = 1) fit$results[, c("item", "N", "V", "I_CVI", "kappa_mod", "status")] ``` ## The handoff ```{r handoff} handoff <- content_handoff(fit) handoff ``` ## What the object holds ```{r components} handoff$items handoff$item_evidence head(handoff$item_statistics) ``` `items` is what the next stage consumes: the carried items only, so what you read here is exactly what you pass along. `item_evidence` keeps every reviewed item, including those held back, with the decision rule that was applied and the effective number of judges behind it. `item_statistics` stacks the statistics long, one row per item per statistic, with the criterion each was compared against where the method sets one. ```{r provenance} str(handoff$provenance[c("schema_version", "workflow", "mode", "keep", "method", "citation")]) ``` By default only items with a `Supported` status travel. Some protocols carry items flagged for review, on the grounds that a weak content-validity result deserves an empirical test rather than deletion. Say so explicitly: ```{r keep} content_handoff(fit, keep = c("Supported", "Review"))$items ``` Items held back are never silently dropped: ```{r held} handoff$item_evidence[!handoff$item_evidence$carried, c("item", "status", "recommendation")] ``` ## Constructs Item-sort and construct-rating designs map each item to a target construct, so the handoff carries that mapping as `scales`, ready for a workflow that models several scales at once: ```{r scales} sort_dat <- data.frame( item = rep(c("A1", "A2", "B1"), each = 12), rater = rep(1:12, 3), target_construct = c(rep("A", 24), rep("B", 12)), assigned_construct = c(rep("A", 11), "B", rep("A", 9), rep("B", 3), rep("B", 10), rep("A", 2)) ) content_handoff(sort_validity(sort_dat), keep = c("Supported", "Review"))$scales ``` An expert panel rating one item set for relevance has no construct column, so `scales` is `NULL`. That is a real difference, not a gap to paper over: nothing in that design says which construct each item belongs to, so a downstream workflow should screen those items rather than invent membership for them. ## Into an empirical workflow Once responses are collected, the item set carries into the empirical stage. [nomologR](https://github.com/JUhalt/nomologR) is the companion package for that stage: it begins where item-level data exist and follows the measure through screening, dimensionality, measurement models, reliability, invariance, and nomological networks. ```{r nomologr, eval=FALSE} # install.packages("nomologR", repos = "https://juhalt.r-universe.dev") library(nomologR) # `responses` is your collected data: one row per respondent, one column per item. scr <- nomo_screen(responses, items = handoff) scr$item_summary ``` That chunk is not evaluated here, because this vignette builds without nomologR installed and because a content-validity pretest has no responses to screen. The object shape is agreed between the two packages as schema version 1, and every field is a base type: character, logical, numeric, integer, data frame, list, and Date. Neither package depends on the other, so any downstream workflow can read the handoff, including one you write yourself: ```{r plain} class(handoff) ``` ## What the handoff does not claim Carrying an item forward is not a prediction that it will perform. An item can be clearly relevant to experts and still correlate poorly with its construct, load on an unintended factor, or behave differently across groups. The reverse also happens: an item held back on content grounds cannot be rescued by a strong loading, because a factor loading says nothing about whether the item belongs to the construct as defined. Both stages follow the same rule. Flag, explain, and document; never silently delete. The handoff therefore carries evidence rather than instructions, and the empirical stage is free to flag an item that content review supported. ## Reporting the transition A methods section can state, in one or two sentences, how many items entered the pretest, how many were carried forward and under which rule, and where the held-back items went. `item_evidence` is the table behind that sentence, and `provenance` records the analysis that produced it: ```{r reporting} ev <- handoff$item_evidence sprintf("%d of %d items were carried forward (%s); %d were held back.", length(handoff$items), length(unique(ev$item)), paste(handoff$provenance$keep, collapse = ", "), sum(!ev$carried)) ```