--- title: "Theory-Specified Nomological Networks with nomologR" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Theory-Specified Nomological Networks with nomologR} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} library(nomologR) knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE, fig.width = 7, fig.height = 4.5 ) ``` # Why theory comes before the network `nomologR` treats nomological evidence as a test of **explicit theoretical predictions**, not as a search for statistically significant relationships. The intended sequence is: 1. establish a defensible measurement model; 2. specify the expected relations before inspecting the structural results; 3. fit the full model; 4. compare estimates and uncertainty with the prespecified theoretical region; 5. distinguish theory strain, imprecision, measurement problems, and post-hoc exploration. # The teaching study `nomo_demo_network` simulates a validation study of an **Agency** measure. Two related constructs were also measured — **Persistence** and **Social desirability** — along with an observed **Performance** outcome. The population model is known (see `?nomo_demo_network`): - Agency predicts Persistence (standardized coefficient .45); - Agency predicts Performance (.40); - Persistence has **no** direct effect on Performance once Agency is accounted for (0); - Agency and Social desirability are unrelated (0). In real research you would not know these values. They are shown here so you can see how `nomologR` evidence behaves when a prediction is right, when it is wrong, and when it is about a negligible relation. # Specify theory before looking at results ```{r hypotheses} h <- nomo_hypotheses( "Agency -> Persistence" = positive(min = .20), "Agency <-> SocialDesirability" = negligible(within = c(-.15, .15)), "Agency -> Performance" = positive(), "Persistence -> Performance" = positive(min = .20) ) h ``` `A -> B` means a directed structural path. `A <-> B` means an association without asserting causal direction. The fourth prediction is plausible-sounding — persistent people perform better — but it is **not** true in the population model. It is included on purpose. The negligible-effect region of ±.15 for Social desirability is a **researcher-specified smallest effect size of interest (SESOI)**. It must be justified by theory or study design before the analysis; `nomologR` never invents one. A bare `negligible()` records a theoretical expectation, but `nomologR` will refuse to treat `p > .05` as confirmation of negligibility. # Fit the latent network ```{r fit} model <- nomo_model(list( Agency = c("ag1", "ag2", "ag3", "ag4"), Persistence = c("pe1", "pe2", "pe3", "pe4"), SocialDesirability = c("sd1", "sd2", "sd3") )) net <- nomo_network( model, data = nomo_demo_network, hypotheses = h ) net nomo_table(net, "fit") ``` The measurement model supplies only the factor definitions. Theory-specified paths absent from that model are added transparently, and the exact fitted syntax and unchanged `lavaan` fit remain available in the returned object. # Read relation-level evidence ```{r evidence} evidence <- nomo_table(net, "hypotheses") evidence[, c( "id", "relation", "theoretical_region", "estimate", "ci_lower", "ci_upper", "equivalence_supported", "concordance" )] ``` ```{r evidence-values, include=FALSE} ev <- function(id, col) evidence[[col]][evidence$id == id] fmt <- function(x) sprintf("%.2f", x) ``` Reading each row: - **H1** (`Agency -> Persistence`): estimate `r fmt(ev("H1", "estimate"))`, interval [`r fmt(ev("H1", "ci_lower"))`, `r fmt(ev("H1", "ci_upper"))`], classified **`r ev("H1", "concordance")`** with the prediction of at least .20. - **H2** (`Agency <-> SocialDesirability`): estimate `r fmt(ev("H2", "estimate"))`. The 90% equivalence interval [`r fmt(ev("H2", "equivalence_ci_lower"))`, `r fmt(ev("H2", "equivalence_ci_upper"))`] lies inside the ±.15 region, so the negligible prediction is supported by equivalence evidence — not merely by a non-significant p-value. - **H3** (`Agency -> Performance`): estimate `r fmt(ev("H3", "estimate"))`, classified **`r ev("H3", "concordance")`**. - **H4** (`Persistence -> Performance`): estimate `r fmt(ev("H4", "estimate"))`, interval [`r fmt(ev("H4", "ci_lower"))`, `r fmt(ev("H4", "ci_upper"))`], classified **`r ev("H4", "concordance")`** with the predicted region. Every relation also retains its standard error, p-value, evidence scope, a-priori versus post-hoc provenance, and a measurement-context flag. The goal is not to produce a single "validity score." # A marginal association is not a structural prediction Why might a researcher have expected H4? Look at the simple correlation between a Persistence composite and Performance: ```{r marginal} persistence_mean <- rowMeans(nomo_demo_network[, c("pe1", "pe2", "pe3", "pe4")]) cor(persistence_mean, nomo_demo_network$Performance) ``` The marginal correlation is positive, because both Persistence and Performance depend on Agency. The theory-specified path asks a different question: does Persistence predict Performance *given* Agency? It does not. Treating a table of observed correlations as the nomological network would have produced the wrong conclusion. # Negligible predictions and equivalence evidence For: ```{r negligible-example} negligible(within = c(-.15, .15)) ``` the researcher has supplied a quantitative negligible-effect region. With the default `equivalence_alpha = .05`, `nomo_network()` evaluates a 90% equivalence confidence interval, corresponding to the two one-sided tests procedure. This is intentionally different from declaring a relation negligible because an ordinary null-hypothesis test was not significant. # Criterion and predictive evidence `Performance` is an observed variable, while Agency is latent. The evidence table labels such relations explicitly: ```{r scope} evidence[, c("id", "relation", "evidence_scope")] ``` Observed outcomes keep their observed-variable estimand; `nomologR` does not silently insert measurement-error corrections. # Measurement context ```{r measurement} nomo_table(net, "measurement") ``` A relation can look inconsistent with theory because the estimated relationship truly differs from the prediction, or because weak measurement, improper solutions, poor fit, or large uncertainty limit what the structural result can say. The measurement context stays beside the theory evidence so the two are not confused. # Calibration and validation An internal split can be created explicitly: ```{r split} s <- nomo_split(nomo_demo_network, validation_prop = .40, seed = 2026) net_rep <- nomo_network( model, data = s, hypotheses = h ) replication <- nomo_table(net_rep, "replication") replication[, c( "id", "relation", "primary_estimate", "validation_estimate", "validation_concordance", "replication_status" )] ``` ```{r replication-values, include=FALSE} rs <- function(id) replication$replication_status[replication$id == id] h4_reading <- switch( rs("H4"), sign_change_within_uncertainty = paste( "neither sample's confidence interval excludes zero, so the sign change is", "attributed to sampling uncertainty rather than to a substantive discrepancy" ), direction_not_replicated = paste( "only one sample's confidence interval excludes zero, so the direction was", "not replicated, but no reversal is established" ), sign_reversal = paste( "both confidence intervals exclude zero on opposite sides, which is what a", "reversal requires" ), "the status is read from both confidence intervals, not the point estimates alone" ) ``` The calibration and validation subsets receive the **same prespecified fitted model**; `nomologR` does not respecify the validation model to rescue a primary result. Reading the statuses: - H1 and H3 are **`r rs("H1")`** and **`r rs("H3")`**. - H2 is **`r rs("H2")`**: with only `r nrow(s$validation)` validation cases, the equivalence interval is too wide to confirm negligibility. Splitting a sample buys independence at the cost of precision. - H4 is **`r rs("H4")`**. The population path is zero, and the two point estimates happen to differ in sign. Here `r h4_reading`. A change in sign between samples needs care. Estimates scattered around a null relation differ in sign about half the time, so `nomologR` does not call a sign change a **reversal** from the point estimates. It reads the 95 percent confidence intervals: | Status | Intervals | Meaning | |---|---|---| | `sign_reversal` | Both exclude zero, on opposite sides | Each sample on its own supports a different direction | | `direction_not_replicated` | Exactly one excludes zero | The other sample does not support that direction, nor establish the opposite | | `sign_change_within_uncertainty` | Neither excludes zero | Neither sample distinguishes the relation from zero | None of these turns a non-significant path into evidence of *no* relation. That claim needs a `negligible(within = ...)` prediction with an equivalence region, the kind of prediction H2 makes (Lakens, Scheel, & Isager, 2018). External validation data can instead be supplied with `validation_data =`, which is generally stronger evidence than an internal split. # Figures ```{r effects-plot} plot(net, type = "effects") ``` ```{r replication-plot} plot(net_rep, type = "replication") ``` These are evidence summaries, not automated theory verdicts. # Interpretation rule `nomologR` keeps **measurement evidence, theory concordance, uncertainty, replication, and researcher provenance visible at the same time**. A supported prediction strengthens the validity argument for a particular interpretation of scores; an unsupported one is information about the theory, the measure, or both. # Research basis The nomological network as the core of construct validity comes from Cronbach and Meehl (1955), and convergent and discriminant correlation patterns from Campbell and Fiske (1959). Contemporary practice separates the measurement model from structural relations (Anderson & Gerbing, 1988), treats validity as an argument about score interpretation (Messick, 1995), evaluates negligible predictions with equivalence procedures (Schuirmann, 1987; Lakens, Scheel, & Isager, 2018), and distinguishes prespecified from post-hoc predictions (Nosek et al., 2018). Full references are in `?nomo_network` and the [research basis](research-basis.html) article.