---
title: "Group-Specific Model Syntax"
author: "Mark Lai"
date: "`r Sys.Date()`"
output:
  rmarkdown::html_vignette:
    toc: true
vignette: >
  %\VignetteIndexEntry{Group-Specific Model Syntax}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

In a factorial invariance analysis, the groups sometimes do not share the same set of
observed variables. A common instance is a shorter or longer scale form: one group
answers four items and another answers five. A single shared configural model cannot
be written for such data, because it would reference an item that does not exist in
every group.

`lavaan` handles this with **group-specific model syntax**: a `group:` block defines a
separate model for each group. `pinSearch()` supports this syntax, so a partial
invariance specification search can still be run when the item sets differ across
groups.

## The syntax

Write the configural model as a string (or a character vector) with one `group: N`
block per group; each `group: N` starts on its own line:

```{r, eval = FALSE, echo = TRUE}
mod <- c(
  "group: 1", "F =~ y1 + y2 + y3 + y4",         # e.g. the short form
  "group: 2", "F =~ y1 + y2 + y3 + y4 + y5")    # e.g. the long form
```

The number of `group:` blocks, and their order, must match the groups used to split
the data. As usual, `pinSearch()` passes `config_mod` straight through to
[`lavaan::cfa()`], so you still pass `group = "..."` (via `...`) to split the data;
`group: N` then refers to the **Nth level** of that grouping factor.

## Example

We simulate a single five-item trait. Item `y5` is answered only by the "long" form,
and item `y3` loads more weakly in the "long" form, so the search has something to
find.

```{r}
library(lavaan)
library(pinsearch)
library(MASS, include.only = "mvrnorm")
set.seed(8)

lamS <- c(.90, .85, .80, .75)              # short form: y1-y4
lamL <- c(.90, .85, .50, .75, .70)         # long form:  y1-y5 (y3 weakened)
sigS <- tcrossprod(lamS) + diag(1 - lamS^2)
sigL <- tcrossprod(lamL) + diag(1 - lamL^2)

n  <- 500
gS <- mvrnorm(n, rep(0, 4), sigS)          # short form
gL <- mvrnorm(n, rep(0, 5), sigL)          # long form
dS <- as.data.frame(gS);  names(dS) <- paste0("y", 1:4)
dL <- as.data.frame(gL);  names(dL) <- paste0("y", 1:5)
df <- rbind(cbind(dS, y5 = NA, group = "short"),
            cbind(dL,          group = "long"))
df$group <- factor(df$group, levels = c("short", "long"))
```

A single model `F =~ y1 + y2 + y3 + y4 + y5` cannot describe both groups, because
`y5` is absent for the "short" form. With the group-specific syntax, both forms are
handled in one call:

```{r}
mod <- c(
  "group: 1", "F =~ y1 + y2 + y3 + y4",          # short: no y5
  "group: 2", "F =~ y1 + y2 + y3 + y4 + y5")     # long:  full form
ps <- pinSearch(mod,
    data = df, group = "group",
    type = "intercepts"        # search loadings, then intercepts
)
ps$`Non-Invariant Items`
```

The specification search flags the loading of `y3` in the long form. The final partial
invariance model is

```{r}
summary(ps$`Partial Invariance Fit`)
```

Effect sizes for the flagged item follow the usual workflow:

```{r}
pinSearch(mod, data = df, group = "group",
          type = "intercepts", effect_size = TRUE)
```

## Related

### Keeping at least two invariant items

When a group has only a few items, the search could, in principle, free so many
loadings that fewer than two invariant indicators remained. `min2 = TRUE` caps how
many items may be freed during the search:

```r
pinSearch(mod, data = df, group = "group",
          type = "loadings", min2 = TRUE)
```

### Ordered items

The same `group:` syntax works for ordered categorical indicators; just add
`ordered = ...` (and a `parameterization`, if needed) as you would for a model
without group blocks.

### Not the per-group-value idiom

Do not confuse this with lavaan's `c(.)` per-group values (for example
`F =~ c(1.0, 0.9)*y1`, as in the `pinSearch()` examples). That idiom still assumes
the *same* variables in every group; the `group:` block syntax is for *different*
observed variables or relationships.

## References

Yoon, M., & Millsap, R. E. (2007). Detecting violations of factorial invariance
using data-based specification searches: A Monte Carlo study. *Structural
Equation Modeling: A Multidisciplinary Journal, 14*(3), 435-463.
