| Title: | BIvariate Response Time Modeling with 'lavaan'-Style Syntax |
|---|---|
| Description: | Fits item response theory models with one or two latent variables, for example ability and speed in joint models of response accuracy and response time (van der Linden, 2007, <doi:10.1007/s11336-006-1478-z>), from 'lavaan'-style model syntax. The main engine fits the syntax in 'RTMB' by approximate Bayesian inference as an extended latent Gaussian model (Stringer, Brown and Stafford, 2023, <doi:10.1080/10618600.2022.2099403>): the persons are integrated out by adaptive Gauss-Hermite quadrature and the remaining parameters by an outer adaptive quadrature, with marginal likelihoods for model comparison; the same engine gives maximum likelihood, with casewise scores for score-based invariance tests and DIF trees. Ordinal and continuous indicators, moderated nonlinear factor analysis statements, sinh-arcsinh residuals (Jones and Pewsey, 2009, <doi:10.1093/biomet/asp053>) whose quantile effects at every level come from one fit, conditional reliability and the information of each quantile regression of the response times. An optional 'JAGS' engine translates the model into a plain 'JAGS' model file, for spike-and-slab priors and asymmetric Laplace (quantile) distributions, with marginal-likelihood fit indices (DIC, WAIC, PSIS-LOO) and posterior predictive checks. |
| Authors: | Jie-Wen Tsai [aut, cre] |
| Maintainer: | Jie-Wen Tsai <[email protected]> |
| License: | GPL-3 |
| Version: | 0.2.0 |
| Built: | 2026-10-31 18:56:24 UTC |
| Source: | https://github.com/jiewenTsai/birt |
Integrates the latent variables out (see Details of [birt()]) to get the pointwise log-likelihood, then DIC, WAIC and PSIS-LOO. Called by [birt()] unless 'fit_indices = FALSE'.
add_fit_indices(fit, n_draws = 1000, Q = 41, progress = TRUE)add_fit_indices(fit, n_draws = 1000, Q = 41, progress = TRUE)
fit |
A 'birt' object. |
n_draws |
Number of posterior draws used (reduced automatically when ALD adds quadrature layers). |
Q |
Gauss-Hermite nodes. |
progress |
Show progress. |
'fit' with 'fit_indices', 'loglik', 'loo' and 'waic' added.
Used in the 'family' argument of [birt()] to replace the default normal distribution of a latent variable (its residual, in a latent regression) or of a continuous indicator by an asymmetric Laplace distribution (ALD), which turns the corresponding regression into a quantile regression (Yu & Moyeed, 2001).
ald(p = 0.5) normal()ald(p = 0.5) normal()
p |
Quantile level(s) in (0, 1). A vector (for one entry of 'family' only) fits one model per quantile and returns a 'birt_quantile' object. |
'family = list(speed = ald(0.25))' makes the 0.25 quantile of 'speed' given its predictors equal to the linear predictor (a latent quantile regression; Wang, Feng & Song, 2016; Zhu, Gao & Zhang, 2021). 'family = list(.continuous = ald(0.5))' gives every continuous indicator a median (least absolute deviation) measurement model, which is robust to outlying values such as rapid guesses. Names are resolved by their role in the model syntax: a latent variable name is the structural level, an indicator name the measurement level, and '.continuous' all continuous indicators. Binary indicators always use the logit link.
ALD on a latent variable whose variance is fixed (e.g. 'theta', variance 1 for identification) fixes the ALD scale so that the implied variance equals the fixed value; with a free variance the scale is estimated. The factor correlation 'f1 ~~ f2' cannot be combined with ALD; write the relation as a regression 'f2 ~ f1'.
Sampling uses the normal-exponential mixture of Kozumi and Kobayashi (2011). Posterior intervals under a working ALD likelihood can be too narrow (Yang, Wang & He, 2016).
'normal()' is the default; a heteroscedastic residual is written in the model syntax, 'V(t1) ~ theta' (log residual SD linear in moderators).
An object of class 'birt_family'.
ald(0.25) ald(c(0.1, 0.5, 0.9))ald(0.25) ald(c(0.1, 0.5, 0.9))
Writes out how a fit was computed, as numbered steps with their formulas: for JAGS fits the full conditionals and the sampler JAGS assigned to every node (from 'rjags::list.samplers()' on the compiled model) and the MCMC settings; for RTMB fits the marginal likelihood by the Laplace approximation or adaptive Gauss-Hermite quadrature (centres, nodes, optimization rounds, standard errors); for 'method = "elgm"' the inner (per-person) and outer (parameter) quadrature, the node weights, the log marginal likelihood and how the posterior summaries are formed. The numbers (nodes, rounds, hyperparameters) are those of the fit. The model itself is given by [equations()].
algorithm(object, ...) ## Default S3 method: algorithm(object, ...) ## S3 method for class 'birt' algorithm(object, format = c("unicode", "latex"), samplers = TRUE, ...) ## S3 method for class 'birt_quantile' algorithm(object, format = c("unicode", "latex"), samplers = TRUE, ...) ## S3 method for class 'birt_rtmb' algorithm(object, format = c("unicode", "latex"), ...)algorithm(object, ...) ## Default S3 method: algorithm(object, ...) ## S3 method for class 'birt' algorithm(object, format = c("unicode", "latex"), samplers = TRUE, ...) ## S3 method for class 'birt_quantile' algorithm(object, format = c("unicode", "latex"), samplers = TRUE, ...) ## S3 method for class 'birt_rtmb' algorithm(object, format = c("unicode", "latex"), ...)
object |
A fit of [birt()] (either engine) or [hgrm()]. |
... |
Unused. |
format |
'"unicode"' or '"latex"'. |
samplers |
JAGS fits: compile the model (without sampling) to list the samplers JAGS assigns (needs rjags; a second or so). |
The same generic is exported by birtRcpp (Gibbs full conditionals and EM steps of its models); either package's ‘algorithm()' dispatches to the other’s fits.
A 'birt_algorithm' object (printed like [equations()]; 'as.character()' gives the lines).
if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(N = 200, K = 3, seed = 1) m <- rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3)) fit <- birt(m, d, control = rtmb_control(method = "aghq"), progress = FALSE) algorithm(fit) algorithm(fit, "latex") }if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(N = 200, K = 3, seed = 1) m <- rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3)) fit <- birt(m, d, control = rtmb_control(method = "aghq"), progress = FALSE) algorithm(fit) algorithm(fit, "latex") }
By default the model is fitted in 'RTMB' by ELGM (extended latent Gaussian model; Stringer, Brown & Stafford, 2023): approximate Bayesian inference on the marginal likelihood, the persons integrated out by adaptive Gauss-Hermite quadrature and the remaining parameters by an outer adaptive quadrature, with the priors of [dpriors()]. 'rtmb_control(method = "aghq")' gives maximum likelihood on the same marginal likelihood (needed by the score-based tools: [score_test()], [quantile_score()], [dif_tree()]); 'engine = "jags"' samples the conditional likelihood (persons sampled with the parameters) with [R2jags::jags.parallel()], for spike-and-slab priors and [ald()] quantile models.
birt( model, data, binary = NULL, family = NULL, n_chains = 4, n_iter = 10000, n_burn = 5000, thin = NULL, seed = 2026, file = NULL, label = NULL, fit_indices = TRUE, progress = TRUE, engine = c("rtmb", "jags"), control = rtmb_control(), log_rt = FALSE, id = NULL, dp = dpriors(), ssp_p = NULL, ordered = NULL, itemtype = "grm", censor = NULL, ... )birt( model, data, binary = NULL, family = NULL, n_chains = 4, n_iter = 10000, n_burn = 5000, thin = NULL, seed = 2026, file = NULL, label = NULL, fit_indices = TRUE, progress = TRUE, engine = c("rtmb", "jags"), control = rtmb_control(), log_rt = FALSE, id = NULL, dp = dpriors(), ssp_p = NULL, ordered = NULL, itemtype = "grm", censor = NULL, ... )
model |
Model syntax (see Details). |
data |
A data frame with the indicators and covariates. |
binary |
Names of the binary (0/1) indicators. By default every indicator whose observed values are all 0 or 1; all other indicators are continuous. |
family |
Named list of residual distributions, e.g. 'list(speed = ald(0.25))'; see [ald()]. Default: normal for every latent variable and continuous indicator. |
n_chains, n_iter, n_burn, thin
|
MCMC settings of 'engine = "jags"': chains (run in parallel, on at most 'getOption("birt.cores")' processes; default the number of physical cores), iterations per chain, burn-in, thinning (default: 1000 kept draws per chain). |
seed |
Random seed of the JAGS sampler ('engine = "jags"'). |
file |
Path for the generated JAGS model file (default: a temporary file). |
label |
Optional label shown in the output. |
fit_indices |
Compute the marginal-likelihood criteria after sampling. |
progress |
Show the progress of the fit: for the RTMB engine (the default) the steps as messages (Laplace start, AGHQ rounds with -2logL, the ELGM outer nodes); for 'engine = "jags"' a progress bar (the sampler then runs in a background R session with 'callr'). 'FALSE': no progress output. |
engine |
'"rtmb"' (default): the marginal likelihood (the persons integrated out) in 'RTMB', by ELGM (default, see [rtmb_control()]: per-person AGHQ inside, adaptive quadrature over the hyperparameters outside; the same priors as JAGS, 'dp' and 'prior("...")', no spike-and-slab) or by maximum likelihood ('control = rtmb_control(method = "aghq")', which ignores the priors). '"jags"': the conditional likelihood, Bayesian (the persons sampled together with the parameters). Instead of [ald()] the RTMB engine offers [shash()] residuals and moderated scales ('V(y) ~ z' in the syntax), and [quantiles()] gives the quantile effects at every level from one fit. Returns a 'birt_rtmb' object. |
control |
RTMB settings, [rtmb_control()]: 'rtmb_control(method = "ml")' (or '"aghq"') for maximum likelihood. A plain list of its arguments, e.g. 'list(method = "ml")', is passed to [rtmb_control()]. |
log_rt |
'TRUE': the continuous indicators are response times in seconds and are log-transformed before fitting (under the same names). A vector of column names log-transforms only those (e.g. when some continuous indicators are not times). Times <= 0 are an error, and a warning flags times that look already log-transformed (not right-skewed, median below 10). |
id |
Person identifiers for [scores()]: a column name of 'data' or a vector (default '1..N'). |
dp |
Default priors, [dpriors()] (engine '"jags"', and engine '"rtmb"' with 'method = "elgm"'); the prior of every parameter is shown in 'summary()'. |
ssp_p |
Prior inclusion probability of the spike-and-slab parameters: 'NULL' (default) learns it ('p_incl ~ beta(1, 1)', shared within loadings, within regressions and within the moderation effects of one kind and moderator); 'BF10' then uses the posterior mean of the learned inclusion probability as the prior odds ('prior_incl' in 'summary(fit)$selection'). A number fixes it, e.g. '0.5' for Bayes factors with prior odds 1, or '0.2' when few nonzero effects are expected. |
ordered |
Ordinal (Likert) indicators: 'TRUE' (every indicator with integer values) or their names. They can be combined with binary and continuous indicators (e.g. response times) and two latent variables; see "Ordinal indicators" and "Moderation". |
itemtype |
Model of the ordinal indicators: '"grm"' (graded response, cumulative logits), '"gpcm"' (generalized partial credit, adjacent-category logits) or '"tppcm"' (two-parameter partial credit: one discrimination per step); one value, or a named vector per item. '"gpcm"' and '"tppcm"' are experimental: with moderation statements they are not yet checked by simulation-based calibration. |
censor |
Censored continuous indicators (e.g. time limits): a named list with the limits of each, 'list(t1 = c(upper = 60), t2 = c(lower = 1, upper = 60))'. A value at or above 'upper' is right censored (only "at least 'upper'" is known; it contributes the log survival function, log P(y >= upper | f), to the likelihood) and a value at or below 'lower' is left censored (log P(y <= lower | f)); other values contribute their density, as in the usual likelihood of censored data (Meeker & Escobar, 1998; for time limits in IRT, Lee & Ying, 2015). The limits are on the scale of the columns of 'data' as given: seconds for the columns log-transformed by ‘log_rt', whose limits are log-transformed with them; on the data’s own scale otherwise (e.g. 'log(60)' when 'data' already holds log times). Normal and [shash()] residuals; 'engine = "rtmb"' (ELGM and maximum likelihood). Values recorded beyond a limit are treated as censored at the limit. See "Censoring" in Details. |
... |
Not used: an argument that 'birt()' does not have is an error, and a setting of [rtmb_control()] (e.g. 'method = "ml"') is answered with the 'control = rtmb_control(...)' call to write. |
**Syntax.** At most two latent variables. Operators:
| `f =~ y1 + y2` | loadings: the first `=~` lines of a factor are its measurement loadings (positive), also when split over consecutive lines as in lavaan; `=~` lines of a factor written after a line of the other factor are cross-loadings (any sign), e.g. `ability =~ t1` after `speed =~ -1*t1 + ...` |
| `f =~ -1*t1` | fixed loading |
| `f =~ r*t1 + r*t2` | equality by label |
| `f =~ prior("ssp")*t1` | spike-and-slab prior |
| `f =~ prior("dnorm(0, 100)")*t1` | any JAGS prior (precision parameterization) |
| `f1 ~~ f2` | factor correlation (default 0) |
| `f ~~ 1*f` | fixed (residual) variance |
| `f ~ x1 + prior("ssp")*x2` | latent regression on covariates |
| `f2 ~ f1` | path between the latent variables |
| `E(y3) ~ x2` | shift of item y3 with x2 (uniform DIF); see "Moderation" |
| `V(t1) ~ f` | log residual SD of t1 moderated by f; see "Moderation" |
A factor with at least one free loading has (residual) variance 1; a factor whose loadings are all fixed (e.g. speed with loadings -1) has a free variance. Binary indicators use a logit link, continuous indicators a normal (or ALD) distribution; every indicator has an intercept. Covariates enter as given (standardize them first) and may not be missing; indicators may be missing (at random).
**Conditional and marginal likelihood.** The two engines treat the person latents
differently:
conditional likelihood |
marginal likelihood |
|
| Bayesian | `engine = "jags"`: sampled with the parameters |
`rtmb_control(method = "elgm")` |
| maximum likelihood | (not offered: joint maximum likelihood is inconsistent) | `engine = "rtmb"` (AGHQ or Laplace) |
Both use the same model syntax and [equations()]; [algorithm()] shows the computation.
Person scores are posterior means and SDs of the sampled (JAGS) or EAPs and
posterior SDs given the estimates (RTMB; under ELGM averaged over the outer nodes). The
fit indices of JAGS fits (DIC, WAIC, PSIS-LOO) use the marginal likelihood of each draw.
("Conditional" here is about the person latents; [cond_reliability()] is reliability given
, another use of the word.)
**Identification with a path.** With unit speed loadings, a common cross-loading of 'theta' on the response times equals the path 'speed ~ theta', so a linear path together with free cross-loadings on every response time is refused.
**Ordinal indicators** ('ordered'). Categories recoded to 1..K per item; the loading is the
discrimination (positive: an ordinal item loads on one latent variable, on its first line).
'itemtype = "grm"': cumulative logits , thresholds (Samejima, 1969);
'"gpcm"': adjacent-category logits with unordered step difficulties (Muraki, 1992); '"tppcm"': the same with one
discrimination per step, , (Yu, 1991).
**Moderation** (moderated nonlinear factor analysis; Bauer, 2017). A moderation statement regresses a conditional moment on moderators z (observed columns of 'data', used as given): 'E()' the expectation, 'V()' the variance.
| `E(y1) ~ z` | shift of y1: categorical items on the latent scale,
(uniform DIF); continuous items their intercept |
| `E(y1 + y2) ~ prior("ssp")*z` | spike-and-slab shifts (DIF screening; engine `"jags"`) |
| `E(y1) ~ z:f` | loading of y1 on f, log scale, (nonuniform DIF) |
| `E(y1 + y2 + y3) ~ c1*z:f` | one common effect (equal labels), e.g. person precision |
| `V(t1) ~ z` | log residual SD of a continuous indicator (z may be a latent variable, engine `"rtmb"`) |
| `E(f) ~ z` (or `f ~ z`) | latent mean (impact) |
| `V(f) ~ z` | latent SD (log scale) |
Indicators without a term on z (or with '0*z') are anchors. 'E(f) ~ z' together with shifts of every indicator of f on z (free or equal) is not identified and is refused; spike-and-slab shifts or at least one anchor identify it. [hgrm()] writes the usual moderated graded response model in this syntax and fits it with 'engine = "rtmb"'.
**Default priors.** Measurement loadings hierarchical lognormal per latent variable ('log lambda ~ N(mu_f, 1)', 'mu_f ~ N(0, 10)'; see [dpriors()]); cross-loadings and regression coefficients 'dnorm(0, 1)'; intercepts 'dnorm(0, 1/9)' (binary) and 'dnorm(0, 1/100)' (continuous); residual SDs, factor SDs and ALD scales half-t(3); correlation uniform(-1, 1); change them with 'dp = dpriors(...)'. Spike-and-slab: 'incl ~ dbern(p_incl)', 'p_incl ~ beta(1, 1)' (or fixed, 'ssp_p'), slab 'N(0, sigma_slab^2)', 'sigma_slab ~ N+(0, 1)', shared within loadings, within regression coefficients and within the moderation effects of one kind (shifts 'E(y) ~ z' or log loadings 'E(y) ~ z:f') and moderator.
**Censoring** ('censor'). With a time limit c the recorded time of a person who ran out of
time is not their response time; treating it as observed biases the intercepts, the residual
SDs and the speed variance downwards. 'censor' replaces the density of such a value by the
probability of being beyond the limit, ( the identity for normal residuals, the standardized sinh-arcsinh
transform for [shash()]), inside the integral over the person latents. The number of censored
values is shown in the header; [cond_reliability()] of a censored fit gives the information
under its limits, and [rt_information()] the information kept under other limits.
With the default 'engine = "rtmb"', an object of class 'birt_rtmb' (see [birt_rtmb] for its accessors and [plot.birt_rtmb()]). With 'engine = "jags"', an object of class 'birt' (a list), or 'birt_quantile' when a vector of quantiles is given in 'family'; see [birt-class].
Lee, Y.-H., & Ying, Z. (2015). A mixture cure-rate model for responses and response times in time-limit tests. *Psychometrika, 80*(3), 748-775. doi:10.1007/s11336-014-9419-8
Meeker, W. Q., & Escobar, L. A. (1998). *Statistical methods for reliability data*. Wiley.
d <- sim_rtirt(N = 300, K = 5, seed = 1) model <- rtirt_syntax(paste0("y", 1:5), paste0("t", 1:5)) cat(model) # the default: approximate Bayes (ELGM) in RTMB fit <- birt(model, d, progress = FALSE) summary(fit) scores(fit) # maximum likelihood: standard errors, p-values, AIC / BIC, score tests f_ml <- birt(model, d, control = rtmb_control(method = "ml"), progress = FALSE) summary(f_ml) # JAGS engine (needs the JAGS library): spike-and-slab cross-loadings if (requireNamespace("rjags", quietly = TRUE)) { m_ssp <- rtirt_syntax(paste0("y", 1:5), paste0("t", 1:5), cross = "ssp") f_jags <- birt(m_ssp, data = d, engine = "jags", n_chains = 2, n_iter = 2000, n_burn = 1000, progress = FALSE ) summary(f_jags) }d <- sim_rtirt(N = 300, K = 5, seed = 1) model <- rtirt_syntax(paste0("y", 1:5), paste0("t", 1:5)) cat(model) # the default: approximate Bayes (ELGM) in RTMB fit <- birt(model, d, progress = FALSE) summary(fit) scores(fit) # maximum likelihood: standard errors, p-values, AIC / BIC, score tests f_ml <- birt(model, d, control = rtmb_control(method = "ml"), progress = FALSE) summary(f_ml) # JAGS engine (needs the JAGS library): spike-and-slab cross-loadings if (requireNamespace("rjags", quietly = TRUE)) { m_ssp <- rtirt_syntax(paste0("y", 1:5), paste0("t", 1:5), cross = "ssp") f_jags <- birt(m_ssp, data = d, engine = "jags", n_chains = 2, n_iter = 2000, n_burn = 1000, progress = FALSE ) summary(f_jags) }
The tables that birt returns ('estimates()', 'scores()', 'fit_indices()', 'quantiles()', 'compare()', ...) are tibbles or data frames of class 'birt_tbl', and the named numbers ('reliability()', 'coef()') are numeric vectors or matrices of class 'birt_values'. They print numbers with 3 decimals in fixed notation (6e-17 prints 0.000; only numbers of 1e10 or more, such as a diverging estimate, print as e.g. 5.950e+99), counts (N, npar, df, nodes, ESS) without decimals, and p-values below 0.001 as '<0.001'. Only the display is rounded: the values themselves are not ('x$est', 'x[["ability"]]', arithmetic and 'as.data.frame()' use the full values; 'unclass()' removes the class).
## S3 method for class 'birt_tbl' format(x, n = NULL, digits = 3, width = getOption("width"), ...) ## S3 method for class 'birt_tbl' print(x, n = NULL, digits = 3, width = getOption("width"), ...) ## S3 method for class 'birt_values' format(x, digits = 3, ...) ## S3 method for class 'birt_values' print(x, digits = 3, ...) ## S3 method for class 'birt_values' as.data.frame(x, row.names = NULL, optional = FALSE, ...)## S3 method for class 'birt_tbl' format(x, n = NULL, digits = 3, width = getOption("width"), ...) ## S3 method for class 'birt_tbl' print(x, n = NULL, digits = 3, width = getOption("width"), ...) ## S3 method for class 'birt_values' format(x, digits = 3, ...) ## S3 method for class 'birt_values' print(x, digits = 3, ...) ## S3 method for class 'birt_values' as.data.frame(x, row.names = NULL, optional = FALSE, ...)
x |
A 'birt_tbl' or 'birt_values' object. |
n |
Number of rows to print (default: all rows of tables with up to 20 rows, otherwise the first 10); 'Inf' prints all rows. |
digits |
Decimals. |
width |
Width of the printed lines (columns that do not fit continue below). |
... |
Unused. |
row.names, optional
|
Passed to [as.data.frame()]. |
'print()' returns 'x' invisibly; 'format()' the printed lines ('birt_tbl') or the formatted numbers ('birt_values').
'birt(..., family = list(speed = ald(c(.1, .5, .9))))' fits one model per quantile and returns a 'birt_quantile' object: 'fits' (list of 'birt' objects), 'p', 'target'.
## S3 method for class 'birt_quantile' estimates(object, ...) ## S3 method for class 'birt_quantile' coef(object, ...) ## S3 method for class 'birt_quantile' reliability(object, ...) ## S3 method for class 'birt_quantile' fit_indices(object, ...) ## S3 method for class 'birt_quantile' print(x, digits = 3, ...) ## S3 method for class 'birt_quantile' summary(object, ...) ## S3 method for class 'birt_quantile' plot(x, terms = NULL, ...)## S3 method for class 'birt_quantile' estimates(object, ...) ## S3 method for class 'birt_quantile' coef(object, ...) ## S3 method for class 'birt_quantile' reliability(object, ...) ## S3 method for class 'birt_quantile' fit_indices(object, ...) ## S3 method for class 'birt_quantile' print(x, digits = 3, ...) ## S3 method for class 'birt_quantile' summary(object, ...) ## S3 method for class 'birt_quantile' plot(x, terms = NULL, ...)
object, x
|
A 'birt_quantile' object. |
... |
Unused. |
digits |
Number of decimals. |
terms |
Regression terms to plot (default: all regressions of the target). |
A model fitted with 'birt(..., engine = "rtmb")': maximum likelihood with the latent variables integrated out by adaptive Gauss-Hermite quadrature (or Laplace). Accessors as for JAGS fits: 'print', 'summary', 'coef', [estimates()] ('est', 'se', 'lower', 'upper', and for maximum likelihood 'z' and 'pvalue'; for ELGM the posterior mean, SD and 95% interval), [scores()] (EAP and posterior SD from the quadrature), [reliability()], [fit_indices()] (log-likelihood, AIC, BIC), [convergence()], 'logLik', 'vcov', 'AIC', 'BIC', [quantiles()] (quantile effects) and 'plot' (see [plot.birt_rtmb()]: scores, test information, quantile curves).
## S3 method for class 'birt_rtmb' print(x, ...) ## S3 method for class 'birt_rtmb' summary(object, digits = 3, ...) ## S3 method for class 'birt_rtmb' estimates(object, ...) ## S3 method for class 'birt_rtmb' coef(object, ...) ## S3 method for class 'birt_rtmb' vcov(object, ...) ## S3 method for class 'birt_rtmb' logLik(object, ...) ## S3 method for class 'birt_rtmb' scores(object, ...) ## S3 method for class 'birt_rtmb' reliability(object, ...) ## S3 method for class 'birt_rtmb' fit_indices(object, ...) ## S3 method for class 'birt_rtmb' convergence(object, ...)## S3 method for class 'birt_rtmb' print(x, ...) ## S3 method for class 'birt_rtmb' summary(object, digits = 3, ...) ## S3 method for class 'birt_rtmb' estimates(object, ...) ## S3 method for class 'birt_rtmb' coef(object, ...) ## S3 method for class 'birt_rtmb' vcov(object, ...) ## S3 method for class 'birt_rtmb' logLik(object, ...) ## S3 method for class 'birt_rtmb' scores(object, ...) ## S3 method for class 'birt_rtmb' reliability(object, ...) ## S3 method for class 'birt_rtmb' fit_indices(object, ...) ## S3 method for class 'birt_rtmb' convergence(object, ...)
... |
Unused, or passed to [graphics::plot()]. |
object, x
|
A 'birt_rtmb' fit. |
digits |
Number of decimals. |
'summary()' of a birt fit returns the tables of the fit as one object: 'header', 'fit' (fit indices), 'items' and 'se' (one row per item: estimates and their posterior SD or standard error, as 'coef(simplify = TRUE)' in mirt), 'parameters' (one row per parameter with its interval and, for Bayesian fits, its prior, as in blavaan) and model-specific tables (e.g. 'selection', 'moderation', 'precision', 'reliability'). Each element is a tibble; 'as.data.frame()' returns 'parameters' as a plain data frame. 'print()' shows the tables under their names, so 's$items' gets the one printed as '$items'.
## S3 method for class 'birt_summary' print(x, tables = NULL, digits = 3, ...) ## S3 method for class 'birt_summary' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'birt_table' print(x, digits = 3, title = TRUE, ...)## S3 method for class 'birt_summary' print(x, tables = NULL, digits = 3, ...) ## S3 method for class 'birt_summary' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'birt_table' print(x, digits = 3, title = TRUE, ...)
x |
A 'birt_summary', or one of its tables. |
tables |
Names of the tables to print (default all). |
digits |
Decimals. |
... |
Unused. |
row.names, optional
|
Unused. |
title |
Print the title of a table. |
'print()' returns 'x' invisibly; 'as.data.frame()' the parameter table.
A JAGS fit of [birt()] is a list of class 'c("birt", "birt_fit")' with these elements and the accessors 'scores()', 'reliability()', 'fit_indices()', 'pointwise_loglik()', 'posterior_draws()', 'convergence()', 'jags_code()', 'compare()' (RTMB fits, including [hgrm()], are of class 'birt_rtmb'). The tables returned by 'estimates()', 'scores()', 'fit_indices()', 'convergence()' and 'compare()' are tibbles, and 'reliability()' and 'coef()' named numbers; they print with 3 decimals but keep the full values (see [birt_print]):
lavaan-like parameter table: 'lhs', 'op', 'rhs', 'family', 'prior', posterior 'est' (mean), 'sd', 'q025', 'q975', 'rhat', 'ess', ALD 'scale', spike-and-slab 'p_incl' and 'BF10' (posterior over prior odds of inclusion; the prior odds from 'ssp_p', or from the posterior mean of the learned inclusion probability of the parameter's group). Rows with 'op' '~~' report variances (for ALD: the implied variance, with the scale in 'scale'). 'estimates()' returns it with the columns 'se', 'lower', 'upper' in place of 'sd', 'q025', 'q975': the same names for every fit (JAGS, ELGM, maximum likelihood), as in 'as.data.frame(summary(fit))'. The old names still work with '$', '[[' and '[' (with a warning once per session).
one row per person: EAP (posterior mean) and posterior SD of each latent variable.
empirical reliability of the EAP scores, var(EAP) / (var(EAP) + mean PSD^2).
item tables: binary items (a, b = -d/a, d) and continuous indicators (intercept, loadings, residual scale and variance, inclusion probability).
draws x persons matrix of the marginal log-likelihood (latent variables integrated out); ‘loo', 'waic' the corresponding ’loo' objects; 'fit_indices' Deviance, pD, DIC, WAIC, LOOIC.
the posterior draws ([coda::mcmc.list]), including person parameters.
JAGS code and file, internal parameter table, parsed model, JAGS data, MCMC settings, run time.
estimates(object, ...) ## S3 method for class 'birt' estimates(object, ...) scores(object, ...) ## S3 method for class 'birt_fit' scores(object, ...) reliability(object, ...) ## S3 method for class 'birt_fit' reliability(object, ...) fit_indices(object, ...) ## S3 method for class 'birt_fit' fit_indices(object, ...) pointwise_loglik(object, ...) posterior_draws(object, persons = FALSE, ...) ## S3 method for class 'birt' coef(object, ...) ## S3 method for class 'birt' print(x, ...) ## S3 method for class 'birt' summary(object, digits = 3, irt = TRUE, ...)estimates(object, ...) ## S3 method for class 'birt' estimates(object, ...) scores(object, ...) ## S3 method for class 'birt_fit' scores(object, ...) reliability(object, ...) ## S3 method for class 'birt_fit' reliability(object, ...) fit_indices(object, ...) ## S3 method for class 'birt_fit' fit_indices(object, ...) pointwise_loglik(object, ...) posterior_draws(object, persons = FALSE, ...) ## S3 method for class 'birt' coef(object, ...) ## S3 method for class 'birt' print(x, ...) ## S3 method for class 'birt' summary(object, digits = 3, irt = TRUE, ...)
object, x
|
A 'birt' (or 'birt_quantile') object. |
... |
Unused. |
persons |
Include the person parameters. |
digits |
Number of decimals. |
irt |
Also show binary items in the IRT parameterization (a, b). |
Checks that a model is fitted correctly by the JAGS engine. Each replication draws the parameters from their priors and data from the model (persons, covariates, moderators and the pattern of missing values as in 'data'), fits the model with [birt()] and records the rank of every true parameter value among the posterior draws. If the model, the priors and the sampler agree, each rank is uniform (Talts et al., 2018). The simulator is written in R from the definition of the model, independently of the JAGS code that [birt()] generates, so the check also tests the translation of the syntax into JAGS.
check_sbc( model, data, n_rep = 100, n_draws = 100, binary = NULL, family = NULL, ordered = NULL, itemtype = "grm", dp = dpriors(), ssp_p = NULL, n_chains = 2, n_iter = 2000, n_burn = 1000, bins = 10, seed = 1, progress = TRUE ) ## S3 method for class 'birt_sbc' print(x, ...) ## S3 method for class 'birt_sbc' plot(x, ...)check_sbc( model, data, n_rep = 100, n_draws = 100, binary = NULL, family = NULL, ordered = NULL, itemtype = "grm", dp = dpriors(), ssp_p = NULL, n_chains = 2, n_iter = 2000, n_burn = 1000, bins = 10, seed = 1, progress = TRUE ) ## S3 method for class 'birt_sbc' print(x, ...) ## S3 method for class 'birt_sbc' plot(x, ...)
model, data, binary, family, ordered, itemtype, dp, ssp_p
|
As in [birt()]; 'data' gives the persons, covariates, moderators and missing values. |
n_rep |
Number of replications (100 or more for a real check; each one is a JAGS fit). |
n_draws |
Posterior draws used for each rank (equally spaced, which also thins). |
n_chains, n_iter, n_burn
|
MCMC settings of each fit. |
bins |
Number of bins of the uniformity test. |
seed |
Random seed. |
progress |
Show progress. |
x |
A 'birt_sbc' object. |
... |
Unused. |
Ties (spike-and-slab parameters at 0) get randomized ranks. Replications whose data would change the model (an ordinal category or a binary value that is never observed) are drawn again. Asymmetric Laplace families are not simulated.
A 'birt_sbc' object: 'ranks' (replications x parameters), 'test' (per parameter: the chi-square uniformity statistic and p-value, and the mean rank relative to its expectation), 'redraws' and 'rhat' (the largest R-hat of each replication; ranks are only meaningful when the chains converged, Talts et al., 2018, Section 5). 'plot()' shows the rank histograms.
Talts, S., Betancourt, M., Simpson, D., Vehtari, A., & Gelman, A. (2018). Validating Bayesian inference algorithms with simulation-based calibration. arXiv:1804.06788.
if (requireNamespace("rjags", quietly = TRUE)) { d <- sim_rtirt(N = 100, K = 3, seed = 1) m <- rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3)) s <- check_sbc(m, d, n_rep = 20, n_iter = 1000, n_burn = 500, progress = FALSE) s }if (requireNamespace("rjags", quietly = TRUE)) { d <- sim_rtirt(N = 100, K = 3, seed = 1) m <- rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3)) s <- check_sbc(m, d, n_rep = 20, n_iter = 1000, n_burn = 500, progress = FALSE) s }
Compare fitted models
compare(..., digits = 3)compare(..., digits = 3)
... |
'birt' objects (named arguments give the model names), or one named list. For RTMB fits ('birt_rtmb'): -2 log L, number of parameters, AIC, BIC and their differences to the best model. |
digits |
Number of decimals. |
Invisibly, a tibble: for JAGS fits the reliability, DIC, WAIC, LOOIC and the ELPD difference to the best model (from [loo::loo_compare()]); for ELGM fits the log marginal likelihood 'logML', its difference to the best model 'dlogML' and the Bayes factor of the best model against each model 'BF_best' (numeric); for maximum likelihood fits the deviance, AIC, BIC and their differences to the best model.
For a model fitted with 'engine = "rtmb"', the reliability of the first latent variable (e.g.
ability) as a function of its value,
(Nicewander, 2018), where is the expected Fisher information of all indicators
about , with the second latent variable (speed) treated as a nuisance (Schur
complement, at its conditional median) and the variance of given
the covariates. The information is split into the accuracy part (categorical indicators:
binary and ordinal), the response-time part (continuous indicators only) and both; for
distributional models ('V(y) ~ f' in the syntax, 'shash(skew = )' moderated by the latent
variables) the scale and the shape of the response-time distribution contribute too.
cond_reliability( object, theta = seq(-3, 3, by = 0.1), at = NULL, se = TRUE, bins = 10, k = 41 ) ## S3 method for class 'birt_cond_rel' plot(x, ...)cond_reliability( object, theta = seq(-3, 3, by = 0.1), at = NULL, se = TRUE, bins = 10, k = 41 ) ## S3 method for class 'birt_cond_rel' plot(x, ...)
object |
A fit from 'birt(..., engine = "rtmb")'. |
theta |
Values of the first latent variable. |
at |
Values of the covariates and moderators (a named vector); default their means. |
se |
Delta-method standard errors of the curves (from 'vcov()'). |
bins |
Number of EAP bins of the empirical check (0: none). |
k |
Gauss-Hermite nodes over SHASH residuals. |
x |
A 'birt_cond_rel' object. |
... |
Passed to [graphics::plot()]. |
* Ordinal indicators contribute their item information (graded response: Samejima, 1969; partial credit: the variance of the cumulative step discriminations), the same as [ordinal_information()]. * Moderated loadings and shifts ('E(y) ~ z:f', 'E(y) ~ z') are evaluated at the moderator values 'at' (default: their means), so the curve is the reliability for persons with these moderator values. * For a fit with censored indicators ('birt(..., censor = )') the information is that of the censored data at the fit's limits: the density part between the limits plus the probability of a censored value times the squared score of log S(c) or log F(c) (Escobar & Meeker, 1998). [rt_information()] shows what other limits would keep.
The curve is checked against the data: 'empirical' bins the persons by their EAP and compares
from the adaptive quadrature with the curve at the bin mean.
A data frame (class 'birt_cond_rel') with 'theta', the information 'info', 'info_acc', 'info_rt' and the reliabilities 'rel', 'rel_acc', 'rel_rt' (and 'se_*' with 'se = TRUE'); attribute 'empirical' with the binned check. 'plot()' draws the curves.
Nicewander, W. A. (2018). Conditional reliability coefficients for test scores. *Psychological Methods, 23*(2), 351-362.
Escobar, L. A., & Meeker, W. Q. (1998). Fisher information matrices with censoring, truncation, and explanatory variables. *Statistica Sinica, 8*, 221-237.
Samejima, F. (1969). Estimation of latent ability using a response pattern of graded scores. *Psychometrika Monograph Supplement, 34*(4, Pt. 2).
d <- sim_rtirt(N = 500, K = 6, seed = 1) m <- paste("theta =~ y1 + y2 + y3 + y4 + y5 + y6", "speed =~ -1*t1 + -1*t2 + -1*t3 + -1*t4 + -1*t5 + -1*t6", "theta =~ t1 + t2 + t3 + t4 + t5 + t6", sep = "\n" ) fit <- birt(paste(m, "V(t1 + t2 + t3 + t4 + t5 + t6) ~ theta", sep = "\n"), d, control = rtmb_control(method = "aghq"), progress = FALSE ) cr <- cond_reliability(fit) plot(cr)d <- sim_rtirt(N = 500, K = 6, seed = 1) m <- paste("theta =~ y1 + y2 + y3 + y4 + y5 + y6", "speed =~ -1*t1 + -1*t2 + -1*t3 + -1*t4 + -1*t5 + -1*t6", "theta =~ t1 + t2 + t3 + t4 + t5 + t6", sep = "\n" ) fit <- birt(paste(m, "V(t1 + t2 + t3 + t4 + t5 + t6) ~ theta", sep = "\n"), d, control = rtmb_control(method = "aghq"), progress = FALSE ) cr <- cond_reliability(fit) plot(cr)
JAGS fits: R-hat and effective sample sizes, and the parameters with R-hat above 'threshold'. RTMB fits: optimizer code, maximum gradient and the AGHQ rounds.
convergence(object, ...) ## S3 method for class 'birt_fit' convergence(object, threshold = 1.05, ...)convergence(object, ...) ## S3 method for class 'birt_fit' convergence(object, threshold = 1.05, ...)
object |
A 'birt' or 'birt_rtmb' fit. |
... |
Unused. |
threshold |
Rhat threshold for listing parameters. |
A tibble of the parameters with Rhat above 'threshold' (invisibly).
**Deprecated.** 'dif_tree()' still works, with a warning once per session, but gets no new features and will be removed in a later version. To find which parameters vary with person covariates, use [ipcr()] (individual parameter contribution regression) on a maximum likelihood fit.
dif_tree( model, data, partition, impact = TRUE, nbins = 10, alpha = 0.05, minsize = NULL, maxdepth = Inf, ... )dif_tree( model, data, partition, impact = TRUE, nbins = 10, alpha = 0.05, minsize = NULL, maxdepth = Inf, ... )
model |
Model syntax, as in [birt()]. |
data |
A data frame with the indicators. |
partition |
A data frame of partitioning variables (one row per person; numeric, ordered or factor). |
impact |
Model latent mean differences along the partitioning variables. |
nbins |
Numeric partitioning variables with more than 'nbins' distinct values are cut into 'nbins' groups at their quantiles (an ordered factor; the tree prints the groups as intervals). A split is then searched among the 'nbins - 1' group boundaries (two maximum likelihood fits each) instead of every observed value (about two fits per person in the node), which makes the tree minutes faster; the cost is that a cut point is located only up to its quantile group. 'NULL' keeps them numeric (the supLM test and the exhaustive search of [partykit::mob()]). |
alpha, minsize, maxdepth
|
Significance level (Bonferroni over variables), minimum node size and maximum depth (see [partykit::mob_control()]). |
... |
Passed to [birt()] (e.g. 'ordered', 'family'; 'control' defaults to the maximum likelihood fit, 'rtmb_control(method = "aghq")', whose scores the tests use). |
Recursively splits the persons by the partitioning variables where the measurement parameters of the binary or ordinal items (loadings, intercepts, thresholds) are not invariant: model-based recursive partitioning (Zeileis, Hothorn & Hornik, 2008; for IRT, Strobl, Kopf & Zeileis, 2015). At each node the model is fitted by maximum likelihood ('engine = "rtmb"') and the casewise scores of the measurement parameters ([estfun.birt_rtmb()]) are tested along every partitioning variable with fluctuation tests, as in [score_test()]; the most significant variable (Bonferroni-adjusted) is split if it is below 'alpha'.
**Impact.** With 'impact = TRUE' (default) the partitioning variables are covariates of every latent variable in each node ('f ~ z', centred and scaled within the node; factors as dummies), so differences in the latent means between groups are modelled and the tree looks for DIF beyond impact. With 'impact = FALSE' any variable related to the latent variables splits the tree.
**Tests.** Numeric partitioning variables are tested by the supLM (maxLM) statistic, ordered factors (including the quantile groups of 'nbins') by its version for ordinal variables (maxLM_o, 'mob_control(ordinal = "L2")'; Merkle, Fan & Zeileis, 2014) and unordered factors by the chi-square statistic.
**ELGM.** The tree is a maximum likelihood method: each node is fitted with 'rtmb_control(method = "aghq")' (the default 'control') and an ELGM 'control' is refused, since the fluctuation tests need casewise scores at the ML estimate (see "ELGM fits" in [score_test()]). There is no ELGM counterpart of the recursive search; a single candidate split can be checked by comparing ELGM fits with and without 'E(y) ~ z' ([compare()]).
Only the scores of the measurement block are tested (decorrelated within the block), so partitioning variables built from the response times (e.g. the total log time) are allowed. Parameters are on the scale of each node, so compare nodes through the tests rather than raw parameter differences.
A 'modelparty' object (partykit): 'print()', 'plot()', 'coef()' (measurement parameters per terminal node, internal scale), 'strucchange::sctest(tree, node = )' for the tests at a node.
Debelak, R., Pawel, S., Strobl, C., & Merkle, E. C. (2022). Score-based measurement invariance checks for Bayesian maximum-a-posteriori estimates in item response theory. *British Journal of Mathematical and Statistical Psychology, 75*(3), 728-752. doi:10.1111/bmsp.12275
Zeileis, A., Hothorn, T., & Hornik, K. (2008). Model-based recursive partitioning. *Journal of Computational and Graphical Statistics, 17*, 492-514. doi:10.1198/106186008X319331
Merkle, E. C., Fan, J., & Zeileis, A. (2014). Testing for measurement invariance with respect to an ordinal variable. *Psychometrika, 79*, 569-584. doi:10.1007/s11336-013-9376-7
Strobl, C., Kopf, J., & Zeileis, A. (2015). Rasch trees: A new method for detecting differential item functioning in the Rasch model. *Psychometrika, 80*, 289-316. doi:10.1007/s11336-013-9388-3
if (requireNamespace("partykit", quietly = TRUE) && requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(N = 600, K = 6, seed = 1) z <- data.frame(group = factor(sample(c("A", "B"), 600, TRUE)), x = rnorm(600)) dif_tree(rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)), d, z) # no DIF: a single node }if (requireNamespace("partykit", quietly = TRUE) && requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(N = 600, K = 6, seed = 1) z <- data.frame(group = factor(sample(c("A", "B"), 600, TRUE)), x = rnorm(600)) dif_tree(rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)), d, z) # no DIF: a single node }
As 'dpriors()' in blavaan: the default prior of each parameter class, as JAGS distributions (normal priors in the **precision** parameterization, 'dnorm(mean, 1 / sd^2)'). A 'prior("...")' modifier in the model syntax overrides the default for that parameter (loadings and regression coefficients); 'prior("ssp")' gives it a spike-and-slab prior. The prior of every parameter is listed in the 'prior' column of 'summary()' and [estimates()].
dpriors( loading = "hier", cross = "dnorm(0, 1)", beta = "dnorm(0, 1)", intercept_binary = "dnorm(0, 1/9)", intercept = "dnorm(0, 1/100)", resid_sd = "dt(0, 1, 3) T(0,)", factor_sd = "dt(0, 1, 3) T(0,)", cor = "dunif(-1, 1)", slab_sd = "dnorm(0, 1) T(0,)", moderation = "dnorm(0, 1)", threshold = "dnorm(0, 1/4)", threshold_step = "dlnorm(-0.5, 1)", step_ratio = "dlnorm(0, 1)", skew = "dnorm(0, 1)", tail = "dlnorm(0, 4)" )dpriors( loading = "hier", cross = "dnorm(0, 1)", beta = "dnorm(0, 1)", intercept_binary = "dnorm(0, 1/9)", intercept = "dnorm(0, 1/100)", resid_sd = "dt(0, 1, 3) T(0,)", factor_sd = "dt(0, 1, 3) T(0,)", cor = "dunif(-1, 1)", slab_sd = "dnorm(0, 1) T(0,)", moderation = "dnorm(0, 1)", threshold = "dnorm(0, 1/4)", threshold_step = "dlnorm(-0.5, 1)", step_ratio = "dlnorm(0, 1)", skew = "dnorm(0, 1)", tail = "dlnorm(0, 4)" )
loading |
Measurement loadings (the first '=~' lines of a factor, before any line of the
other factor; positive). The default
'"hier"' is a hierarchical lognormal prior per latent variable, |
cross |
Cross-loadings (later '=~' lines). |
beta |
Regression coefficients ('~'). |
intercept_binary, intercept
|
Intercepts of binary (logit) and continuous indicators. |
resid_sd |
Residual SD of continuous indicators (for [ald()]: of the implied SD). |
factor_sd |
SD of a latent variable with a free variance. |
cor |
Correlation of the latent variables ('f1 ~~ f2'). |
slab_sd |
SD of the spike-and-slab slab (shared within loadings / within regressions). |
moderation |
Log-scale moderation effects: 'E(y) ~ z:f' (log loading), 'V() ~ z' (log SD) and the 'skew =' moderators of [shash()]. |
threshold, threshold_step
|
Ordinal indicators: the first threshold (graded response; every step difficulty of a partial credit item), and the positive steps between the next thresholds of a graded response item (lognormal; Bayesian engines). |
step_ratio |
Ratio of a later step discrimination to the first one ('itemtype = "tppcm"'). |
skew, tail
|
Engine '"rtmb"' with 'method = "elgm"' only: the SHASH skewness and the SHASH tail weight ('dlnorm(0, 4)': log tail weight with SD 0.5, centred at the normal). |
The same priors are used by the approximate Bayesian (ELGM) fit of the RTMB engine, 'birt(..., engine = "rtmb", control = rtmb_control(method = "elgm"))', which supports 'dnorm', 'dlnorm', 'dt', 'dunif', 'dgamma', 'dexp', 'dbeta', 'ddexp' and 'dlogis', with truncation 'T(a, b)'.
A named list of prior strings (class 'birt_dpriors').
dpriors() dpriors(cross = "dnorm(0, 100)") # BSEM-style small-variance cross-loadings (SD 0.1) dpriors(loading = "dlnorm(0, 1)") # the earlier fixed loading priordpriors() dpriors(cross = "dnorm(0, 100)") # BSEM-style small-variance cross-loadings (SD 0.1) dpriors(loading = "dlnorm(0, 1)") # the earlier fixed loading prior
Writes the fitted model as equations, with the parameters as they appear in [summary()], the sets of items they apply to and, for Bayesian fits, the priors. The default is Unicode text for the console; 'format = "latex"' gives an 'aligned' block for a paper or an appendix (e.g. 'writeLines(as.character(equations(fit, "latex")), "model.tex")').
equations(object, ...) ## S3 method for class 'birt_equations' as.character(x, ...) ## S3 method for class 'birt_equations' print(x, ...) ## S3 method for class 'birt' equations(object, format = c("unicode", "latex"), priors = TRUE, ...) ## S3 method for class 'birt_rtmb' equations(object, format = c("unicode", "latex"), priors = TRUE, ...) ## S3 method for class 'birt_quantile' equations(object, format = c("unicode", "latex"), priors = TRUE, ...)equations(object, ...) ## S3 method for class 'birt_equations' as.character(x, ...) ## S3 method for class 'birt_equations' print(x, ...) ## S3 method for class 'birt' equations(object, format = c("unicode", "latex"), priors = TRUE, ...) ## S3 method for class 'birt_rtmb' equations(object, format = c("unicode", "latex"), priors = TRUE, ...) ## S3 method for class 'birt_quantile' equations(object, format = c("unicode", "latex"), priors = TRUE, ...)
object |
A fitted model ('birt()' with either engine, or 'hgrm()'). |
... |
Unused. |
x |
A 'birt_equations' object. |
format |
'"unicode"' or '"latex"'. |
priors |
Include the priors (Bayesian fits). |
A 'birt_equations' object (printed); 'as.character()' gives the lines.
cat(rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3))) if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(N = 200, K = 3, seed = 1) fit <- birt(rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3)), d, control = rtmb_control(method = "aghq"), progress = FALSE ) equations(fit) equations(fit, "latex") }cat(rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3))) if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(N = 200, K = 3, seed = 1) fit <- birt(rtirt_syntax(paste0("y", 1:3), paste0("t", 1:3)), d, control = rtmb_control(method = "aghq"), progress = FALSE ) equations(fit) equations(fit, "latex") }
'estfun()' returns, for a maximum likelihood fit of [birt()] with 'engine = "rtmb"' (adaptive Gauss-Hermite quadrature, also for 'ordered = TRUE'), the matrix of casewise scores: one row per person, one column per free parameter, the derivative of that person's marginal log-likelihood (latent variables integrated out, with the quadrature nodes of the final objective) at the estimates. The columns sum to the negative gradient of the objective, about zero. Parameters are on the internal scale of the fit (log of loadings constrained positive, log SDs, atanh of correlations; for ordinal items log a, the first threshold and log increments); the attribute 'parameters' describes each column ('label', indicator 'item', 'block': '"measurement"' for binary and ordinal items, '"rt"' for continuous indicators, '"structural"'). These are the estimating functions used by [score_test()] and by 'strucchange::gefp()'. An ELGM fit is refused (see "ELGM fits" in [score_test()]).
## S3 method for class 'birt_rtmb' estfun(x, ...) ## S3 method for class 'birt_rtmb' bread(x, ...)## S3 method for class 'birt_rtmb' estfun(x, ...) ## S3 method for class 'birt_rtmb' bread(x, ...)
x |
A fit of 'birt(..., engine = "rtmb")' (maximum likelihood, 'rtmb_control(method = "aghq")'). |
... |
Unused. |
A persons x parameters matrix.
'bread()': the inverse of the average observed information (internal scale), as in the 'sandwich' package.
A graded response model whose item discriminations and thresholds, and the mean and variance of the latent trait, depend on person-level moderators (moderated nonlinear factor analysis for ordinal items; Bauer, 2017). The typical use is one response time per person (e.g. the total completion time of a questionnaire) moderating a Likert scale: slower or faster respondents may answer with different precision (discrimination, Ferrando, 2009) or shifted thresholds, a DIF-like effect of time. 'hgrm()' is a shortcut: it writes the model in the syntax of [birt()] with 'ordered = TRUE' (stored in 'fit$model'), where every effect can also be set item by item, and fits it with 'engine = "rtmb"': maximum likelihood with per-person adaptive quadrature (marginal likelihood), or ELGM with 'control = rtmb_control(method = "elgm")'.
hgrm( model, data, moderators, a = "common", b = "none", impact = "mean", anchor = NULL, standardize = TRUE, control = rtmb_control(), dp = dpriors(), ... )hgrm( model, data, moderators, a = "common", b = "none", impact = "mean", anchor = NULL, standardize = TRUE, control = rtmb_control(), dp = dpriors(), ... )
model |
One-factor syntax, '"f =~ y1 + y2 + ..."'. Items are ordinal with integer categories; each item's observed categories are recoded to 1..K. |
data |
A data frame. |
moderators |
Names of numeric person-level moderators (e.g. '"logT"'); several are allowed (e.g. a log time and its square, or piecewise terms). Standardized before fitting unless 'standardize = FALSE', so effects are per SD. |
a, b
|
Moderation of discriminations ('a': alpha) and thresholds ('b': beta): '"free"' one effect per item (DIF), '"common"' one effect shared by all items (for 'a': a person-level precision effect as in Ferrando, 2009), or '"none"'. A vector of length 'length(moderators)' sets one option per moderator. |
impact |
Which latent moments depend on the moderators: any of '"mean"', '"var"', or '"none"'. |
anchor |
DIF-free anchor items: no threshold shifts and no item-specific ('"free"') discrimination effects. A common discrimination effect ('a = "common"') is a person-level precision effect and applies to every item. |
standardize |
Standardize the moderators. |
control |
RTMB settings, [rtmb_control()]; 'method = "elgm"' for the posterior. |
dp |
Priors of the ELGM fit, [dpriors()], as in [birt()]. |
... |
Passed to [birt()] ('progress', 'label', 'itemtype', ...). |
**Identification.** At z = 0 the trait is N(0, 1). A common threshold shift of all items is the same as a shift of the trait mean, so 'impact = "mean"' cannot be combined with 'b = "common"' or 'b = "free"' unless anchors are given. Likewise a common change of all discriminations and the trait variance differ only through the scaling of the thresholds, so 'a = "common"' or '"free"' together with 'impact = "var"' is only weakly identified.
**DIF.** Fit the model without threshold effects and test them with [score_test()] (one fit, every item), then free the flagged items' shifts with the others as anchors: 'b = "free", anchor = ...'. Spike-and-slab screening ('prior("ssp")') is written in the syntax of [birt()] with 'engine = "jags"'.
**Priors** (ELGM only; maximum likelihood has none): those of [birt()] ([dpriors()]): discriminations hierarchical lognormal, first threshold 'N(0, 2^2)', increments lognormal '(-0.5, 1)', 'gamma ~ N(0, 1)', 'psi ~ N(0, 1)'.
A [birt()] fit ('fit$model' holds the syntax). Fits with moderation also have 'dif' (one row per moderated item parameter), 'impact' and 'precision' (by moderator tercile); see also [ordinal_information()] and 'plot(fit, type = "dif")'.
if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_hgrm(N = 400, J = 6, seed = 1) fit <- hgrm("theta =~ y1 + y2 + y3 + y4 + y5 + y6", data = d, moderators = "logT", a = "common", impact = "mean", control = rtmb_control(method = "aghq"), progress = FALSE ) summary(fit) fit$model # the same model in birt() syntax score_test(fit, d$logT, by_item = TRUE) # DIF along the time, item by item }if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_hgrm(N = 400, J = 6, seed = 1) fit <- hgrm("theta =~ y1 + y2 + y3 + y4 + y5 + y6", data = d, moderators = "logT", a = "common", impact = "mean", control = rtmb_control(method = "aghq"), progress = FALSE ) summary(fit) fit$model # the same model in birt() syntax score_test(fit, d$logT, by_item = TRUE) # DIF along the time, item by item }
Which parameters change with which person covariates? ‘ipcr()' computes each person’s
individual parameter contributions (IPCs), , where
are the person's casewise scores ([estfun()][estfun.birt_rtmb]) and the bread
(the inverse of the average information, [bread()][estfun.birt_rtmb]); the IPCs average to the
estimates. Each parameter's IPCs are then regressed on the covariates: a slope that differs from
zero says the parameter varies with that covariate (DIF of an item parameter, a covariate effect
on a structural parameter). All covariates enter jointly, so each slope is adjusted for the
others (Arnold, Oberski, Brandmaier & Voelkle, 2020; Arnold, Brandmaier & Voelkle, 2021).
ipcr( object, covariates, parm = NULL, se = c("HC3", "model"), impact_check = TRUE ) ## S3 method for class 'birt_ipcr' print(x, digits = 3, all = FALSE, alpha = 0.05, ...)ipcr( object, covariates, parm = NULL, se = c("HC3", "model"), impact_check = TRUE ) ## S3 method for class 'birt_ipcr' print(x, digits = 3, all = FALSE, alpha = 0.05, ...)
object |
A maximum likelihood fit of 'birt(..., engine = "rtmb")' ('control = rtmb_control(method = "aghq")'); ELGM fits are refused, as in [score_test()]. |
covariates |
Person covariates: a data frame (numeric columns, factors, logical or character columns, which become factors) or a single vector, one row per person, without missing values. Only the covariates, e.g. 'd[c("x1", "x2")]': columns named like indicators of the model are refused. |
parm |
Parameters to regress: 'NULL' (all), '"measurement"', '"rt"', '"structural"', a regular expression on the labels, or column indices, as in [score_test()]. |
se |
'"HC3"' (default): heteroskedasticity-consistent standard errors of the slopes (the residual variance of the IPCs differs between persons by construction); '"model"': the usual OLS standard errors, as 'ipcr::ipcr()' reports them. |
impact_check |
Message when a covariate correlates with the factor scores but is not a covariate of the model (impact), as in [score_test()]: a covariate related to a latent variable shifts every intercept of its indicators, so its slopes look like DIF on all of them (e.g. on every response time when it is related to speed). Add it to the latent regression ('speed ~ x1') and run 'ipcr()' on that fit to see DIF beyond impact. |
x |
A 'birt_ipcr' object. |
digits |
Number of decimals. |
all |
Print every slope; by default only those with 'p_holm < alpha' (all slopes are in 'x$coefficients'). |
alpha |
Level of the Holm-adjusted p-values for the slopes printed by default. |
... |
Unused. |
IPCs are a one-step linearization around the pooled estimates, so the slopes are attenuated towards zero: use 'ipcr()' to screen, then fit the moderated model (e.g. 'E(y1) ~ z' or ‘V(t1) ~ z' in the syntax) for the size of an effect. The iterated version of the ’ipcr' package is not implemented. The parameters are on the internal scale of the fit, as in [estfun()]: log of the positive loadings (labelled 'log(f =~ y)'), log SDs, atanh of the correlation, the first threshold and log increments of ordinal items. Parameters at a bound (no standard error) are left out.
An object of class 'birt_ipcr': 'coefficients', a tibble with one row per parameter and regression term ('parameter', 'term', 'est', 'se', 'statistic' (t), 'p', and 'p_holm', the Holm-adjusted p-value over all slopes); 'ipcs', the persons x parameters matrix of IPCs; 'se', 'n'. 'print()' shows the slopes with 'p_holm < 0.05', sorted by p-value, and how to read a slope on the internal scale ('print(x, all = TRUE)' shows all of them).
Arnold, M., Oberski, D. L., Brandmaier, A. M., & Voelkle, M. C. (2020). Identifying heterogeneity in dynamic panel models with individual parameter contribution regression. *Structural Equation Modeling, 27*, 613-628. doi:10.1080/10705511.2019.1667240
Arnold, M., Brandmaier, A. M., & Voelkle, M. C. (2021). Predicting differences in model parameters with individual parameter contribution regression using the R package ipcr. *Psych, 3*, 360-385. doi:10.3390/psych3030027
[score_test()] for a test of one moderator along all its values.
d <- sim_rtirt(N = 400, K = 6, seed = 1) model <- rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)) fit <- birt(model, d, control = rtmb_control(method = "aghq")) ipcr(fit, d[c("x1", "x2")])d <- sim_rtirt(N = 400, K = 6, seed = 1) model <- rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)) fit <- birt(model, d, control = rtmb_control(method = "aghq")) ipcr(fit, d[c("x1", "x2")])
JAGS code of a model
jags_code( x, data = NULL, binary = NULL, family = NULL, dp = dpriors(), ssp_p = NULL, ordered = NULL, itemtype = "grm" )jags_code( x, data = NULL, binary = NULL, family = NULL, dp = dpriors(), ssp_p = NULL, ordered = NULL, itemtype = "grm" )
x |
A fitted 'birt' object, or model syntax for [birt()]. |
data, binary, family, dp, ssp_p, ordered, itemtype
|
As in [birt()] (when 'x' is syntax). |
The JAGS model code (character), printed with 'cat()'.
For fits with ordinal indicators (graded response or partial credit), the test information of those indicators as a function of the latent variable, at chosen values of the moderators.
ordinal_information(object, at = NULL, theta = seq(-3, 3, by = 0.1))ordinal_information(object, at = NULL, theta = seq(-3, 3, by = 0.1))
object |
A [birt()] fit with ordinal indicators (either engine). |
at |
Named list of moderator values (default: the 10th, 50th and 90th percentile of the first moderator, the others at their mean). |
theta |
Grid of values of the latent variable. |
Data frame with 'theta', the moderator values and the test information (point estimates of the item parameters), printed with 3 decimals (class 'birt_tbl', see [birt_print]).
For fits with the default engine ('engine = "rtmb"') see [plot.birt_rtmb()].
## S3 method for class 'birt' plot( x, type = c("loadings", "trace", "dif", "information"), pars = NULL, items = NULL, ... )## S3 method for class 'birt' plot( x, type = c("loadings", "trace", "dif", "information"), pars = NULL, items = NULL, ... )
x |
A 'birt' object. |
type |
'"loadings"', '"trace"', or for fits with ordinal indicators '"dif"' (expected item scores at low and high values of the first moderator, for the flagged items or 'items') and '"information"' (test information, see [ordinal_information()]). |
pars |
Regular expression of JAGS node names ('"trace"'). |
items |
Items for 'type = "dif"'. |
... |
Passed to [graphics::plot()]. |
'type = "loadings"': posterior means and 95 all free loadings), with spike-and-slab inclusion probabilities when present. '"trace"': trace plots of the parameters with the largest R-hat (or those matching 'pars').
Plots of a fit from [birt()] with the default engine ('engine = "rtmb"', ELGM or maximum likelihood). 'plot(fit)' draws the quantile effects when the model has effects that change with the quantile level p ('V()' statements or 'shash()' residuals); otherwise it draws the person scores ('type = "scores"'). For JAGS fits see [plot.birt()].
## S3 method for class 'birt_rtmb' plot( x, type = c("quantiles", "scores", "dif", "information"), p = seq(0.05, 0.95, by = 0.05), targets = NULL, predictors = NULL, ald = NULL, max_panels = 16, items = NULL, ... )## S3 method for class 'birt_rtmb' plot( x, type = c("quantiles", "scores", "dif", "information"), p = seq(0.05, 0.95, by = 0.05), targets = NULL, predictors = NULL, ald = NULL, max_panels = 16, items = NULL, ... )
x |
A 'birt_rtmb' fit. |
type |
'"quantiles"': quantile effects against the level p (one panel per target and predictor whose effect can vary with p; the default for models with such effects); '"scores"': EAP scores of the latent variables (a histogram for one, a scatter plot for two; the default for other models); '"information"': test information about the first latent variable, from all indicators, the accuracy indicators (binary and ordinal) and the response times (see [cond_reliability()]; for fits with ordinal indicators only: the test information of those, by the first moderator, see [ordinal_information()]); '"dif"': ordinal indicators, the expected item scores at low and high values of the first moderator. |
p |
Quantile levels. |
targets, predictors
|
Restrict the panels (names of latent variables or indicators / of predictors). |
ald |
Optional 'birt_quantile' fit ('engine = "jags"', [ald()] at several p) whose coefficients are overlaid (points with 95% intervals). |
max_panels |
Maximum number of panels. |
items |
Items for 'type = "dif"'. |
... |
Passed to [graphics::plot()]. |
Invisibly, the plotted values.
d <- sim_rtirt(N = 300, K = 6, seed = 1) fit <- birt(rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)), d, progress = FALSE) plot(fit) # no quantile effects that change with p: the scores of ability and speed plot(fit, type = "information")d <- sim_rtirt(N = 300, K = 6, seed = 1) fit <- birt(rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)), d, progress = FALSE) plot(fit) # no quantile effects that change with p: the scores of ability and speed plot(fit, type = "information")
Replicates the data from posterior draws (person parameters included) and compares: item proportions correct, the sum-score distribution, means and SDs of the continuous indicators, and the conditional dependence between each binary item and its paired continuous indicator (correlation of accuracy with the standardized residual; Bolsinova & Tijmstra, 2016). A continuous indicator is paired with a binary item when its name ends with the item name (e.g. 'lrt_ME1' and 'ME1'), or by position when the numbers of binary and continuous indicators are equal.
ppc(object, ...) ## S3 method for class 'birt' ppc(object, n_draws = 500, seed = 1, ...)ppc(object, ...) ## S3 method for class 'birt' ppc(object, n_draws = 500, seed = 1, ...)
object |
A 'birt' object. |
... |
Unused. |
n_draws |
Number of posterior draws. |
seed |
Random seed. |
An object of class 'birt_ppc' (list of tables); PPP values outside (.025, .975) are flagged.
For a model fitted with 'engine = "rtmb"': how much the -th quantile regression of the
response times on the latent variables (e.g. the cross relation 't ~ speed + ability', with
cross-loadings 'ability =~ t1' and scale or skew moderation 'V(t1) ~ ability') says about the
first latent variable, and which says the most. Speed is a nuisance and is projected
out, as in [cond_reliability()].
quantile_information( object, p = seq(0.05, 0.95, by = 0.05), theta = c(-2, 0, 2), at = NULL ) ## S3 method for class 'birt_qinfo' print(x, ...) ## S3 method for class 'birt_qinfo' plot(x, ...)quantile_information( object, p = seq(0.05, 0.95, by = 0.05), theta = c(-2, 0, 2), at = NULL ) ## S3 method for class 'birt_qinfo' print(x, ...) ## S3 method for class 'birt_qinfo' plot(x, ...)
object |
A fit of 'birt(..., engine = "rtmb")' with continuous indicators. |
p |
Quantile levels |
theta |
Values of the first latent variable. |
at |
Covariate and moderator values (default: their means), as in [cond_reliability()]. |
x |
A 'birt_qinfo' object. |
... |
Unused. |
The -th regression of item i is estimated by the check loss, whose estimating function
is . Its information about the latent variable
(Godambe, 1960) is
a signal (how far the quantile moves with ) times the precision of the quantile, the
inverse of its asymptotic variance (Koenker, 2005). It equals the
Fisher information of the indicator "faster than ". The items are summed (with
the speed direction, item by item rank one) and speed is projected out.
The values for different are not additive: each is the information of one regression
on its own, and at most the information of the whole response-time distribution ('info_rt', as in
[cond_reliability()]); 'efficiency' is the ratio. For one item with a normal residual and a
location effect (a cross-loading) the median regression is best and keeps = .64 of
the item's information; for a scale effect ('V(t) ~ ability') the median carries nothing and the
best single quantiles are = .058 and .942, which keep .30 (maximizing
; on the choice of sample quantiles, Ogawa, 1951).
With several items and the projection of speed, the efficiencies of the curve differ somewhat
from these item values.
The quantities are evaluated at the parameter estimates (ML or ELGM), at each 'theta' with speed at
its conditional median and the covariates at 'at'; the effects are conditional on , not
the average marginal effects of [quantiles()]. The response-time distribution is that of the
fitted model without censoring.
A list of class 'birt_qinfo':
* 'curve': 'theta', 'p', 'info' (all items, speed projected out), 'info_rt' (the whole RT
distribution), 'efficiency';
* 'items': 'theta', 'item', 'p', 'q' (the quantile, model scale), 'beta'
(), 'density' (), ‘info' (the item’s
, speed held fixed);
* 'best': for each 'theta', the of 'p' with the largest 'info'.
Godambe, V. P. (1960). An optimum property of regular maximum likelihood estimation. *The Annals of Mathematical Statistics, 31*, 1208-1211. doi:10.1214/aoms/1177705693
Koenker, R. (2005). *Quantile regression*. Cambridge University Press. doi:10.1017/CBO9780511754098
Ogawa, J. (1951). Contributions to the theory of systematic statistics, I. *Osaka Mathematical Journal, 3*, 175-213.
d <- sim_rtirt(N = 500, K = 6, seed = 1) m <- paste("theta =~ y1 + y2 + y3 + y4 + y5 + y6", "speed =~ -1*t1 + -1*t2 + -1*t3 + -1*t4 + -1*t5 + -1*t6", "V(t1 + t2 + t3 + t4 + t5 + t6) ~ theta", sep = "\n" ) fit <- birt(m, d, progress = FALSE) qi <- quantile_information(fit) qi plot(qi)d <- sim_rtirt(N = 500, K = 6, seed = 1) m <- paste("theta =~ y1 + y2 + y3 + y4 + y5 + y6", "speed =~ -1*t1 + -1*t2 + -1*t3 + -1*t4 + -1*t5 + -1*t6", "V(t1 + t2 + t3 + t4 + t5 + t6) ~ theta", sep = "\n" ) fit <- birt(m, d, progress = FALSE) qi <- quantile_information(fit) qi plot(qi)
For a maximum likelihood fit of [birt()] with 'engine = "rtmb"': the casewise influence
function of each quantile effect of [quantiles()] (e.g. the cross-relation 't ~ speed + ability'
of a log response time at p = 0.9). A quantile effect is a smooth function
of the parameters, so to first order
with the person's score ([estfun.birt_rtmb()]) and the inverse observed
information: the influence function of a function of an M-estimator (Hampel, 1974), obtained by
the delta method. It gives
* robust standard errors, (the sandwich of Huber,
1967, and White, 1982, valid when the SHASH or normal distribution is misspecified), next to
the model-based ones of [quantiles()];
* the influence of each person on an effect: removing person j changes the estimate by about
(as in Cook, 1977);
* score-based tests of the invariance of an effect along a moderator, 'score_test(qs, z)'
(Zeileis & Hornik, 2007; Merkle & Zeileis, 2013).
quantile_score( object, p = object$control$p, type = c("conditional", "unconditional") ) ## S3 method for class 'birt_qscore' print(x, ...) ## S3 method for class 'birt_qscore' score_test( object, moderator, test = c("auto", "DM", "CvM", "maxLM", "LM"), ... )quantile_score( object, p = object$control$p, type = c("conditional", "unconditional") ) ## S3 method for class 'birt_qscore' print(x, ...) ## S3 method for class 'birt_qscore' score_test( object, moderator, test = c("auto", "DM", "CvM", "maxLM", "LM"), ... )
object |
A maximum likelihood fit of 'birt(..., engine = "rtmb")' ('rtmb_control(method = "aghq")'). |
p |
Quantile levels. |
type |
'"conditional"' (covariates fixed) or '"unconditional"'. |
x |
A 'birt_qscore' object. |
... |
Unused. |
moderator |
One value per person: numeric, or a factor (logical and character are converted). |
test |
'"auto"' ('"DM"' for a numeric moderator, '"LM"' for a factor), '"DM"', '"CvM"', '"maxLM"' or '"LM"', as in [score_test()]. |
**Conditional or unconditional.** The effects are averages over the persons' covariates
(average marginal effects). 'type = "conditional"' treats the covariates as fixed, as
[quantiles()] does. 'type = "unconditional"' also counts their sampling:
gains , the deviation of person j's own marginal effect from the
average (Graubard & Korn, 1999). The two differ only when the effect depends on covariates.
**ELGM fits.** 'quantile_score()' is a maximum likelihood tool and refuses an ELGM fit (the default of [birt()]): refit the same model with 'control = rtmb_control(method = "aghq")'. Influence functions and score tests need casewise scores that sum to zero at the estimate; at the ELGM posterior mode they sum to minus the gradient of the log prior, the reported ELGM estimates are posterior means rather than the mode, and MAP versions of the score tests need centred scores, with error rates that depend on the covariance estimate (Debelak, Pawel, Strobl & Merkle, 2022). The Bayesian counterparts: [quantiles()] of the ELGM fit gives posterior means, SDs and intervals of the quantile effects, [quantile_test()] works on ELGM fits, and the invariance of an effect along a numeric 'z' is assessed by fitting the ELGM model with and without the moderation (e.g. 'V(t1) ~ z') and comparing the marginal likelihoods with [compare()] (a Bayes factor, which depends on the prior of the effect).
‘score_test(qs, moderator)' orders each effect’s influence function by the moderator and compares its cumulative sum, standardized by its variance, with a Brownian bridge. Under parameter invariance the partial sums of any fixed linear combination of the scores converge to a Brownian bridge (Zeileis & Hornik, 2007), so the test is valid; it detects changes of the parameters in the direction that moves the effect (to first order), one effect at a time, with Holm-adjusted p-values. It needs 'type = "conditional"': the covariate term of the unconditional influence function varies with the covariates by construction.
A persons x effects matrix of influence functions (columns 'target|predictor|p') of class 'birt_qscore', with attribute 'effects': 'est', 'se' (model-based), 'se.robust' (sandwich).
Debelak, R., Pawel, S., Strobl, C., & Merkle, E. C. (2022). Score-based measurement invariance checks for Bayesian maximum-a-posteriori estimates in item response theory. *British Journal of Mathematical and Statistical Psychology, 75*(3), 728-752. doi:10.1111/bmsp.12275
Hampel, F. R. (1974). The influence curve and its role in robust estimation. *Journal of the American Statistical Association, 69*, 383-393.
Huber, P. J. (1967). The behavior of maximum likelihood estimates under nonstandard conditions. *Proceedings of the Fifth Berkeley Symposium on Mathematical Statistics and Probability, 1*, 221-233.
White, H. (1982). Maximum likelihood estimation of misspecified models. *Econometrica, 50*, 1-25.
Cook, R. D. (1977). Detection of influential observation in linear regression. *Technometrics, 19*, 15-18.
Graubard, B. I., & Korn, E. L. (1999). Predictive margins with survey data. *Biometrics, 55*, 652-659. doi:10.1111/j.0006-341X.1999.00652.x
Zeileis, A., & Hornik, K. (2007). Generalized M-fluctuation tests for parameter instability. *Statistica Neerlandica, 61*, 488-508. doi:10.1111/j.1467-9574.2007.00371.x
For a 'birt_rtmb' fit: for each target and predictor of [quantiles()] (e.g. the cross-relation 't ~ speed + ability' of a log response time, or 'speed ~ ability + x'), a test of whether the effect changes with the quantile level p, and the difference of each level from the median.
quantile_test(object, p = object$control$p, ...) ## S3 method for class 'birt_qtest' print(x, ...)quantile_test(object, p = object$control$p, ...) ## S3 method for class 'birt_qtest' print(x, ...)
object |
A 'birt_rtmb' fit. |
p |
Quantile levels (at least two). |
... |
Unused. |
x |
A 'birt_qtest' object. |
In the model the location of the target is linear in the predictor and its scale and skewness may depend on it, so the p-quantile effect changes with p exactly when the predictor moderates the scale ('V(t) ~ k') or the skewness of the target; equal effects at every level is the null hypothesis of no heteroscedasticity, as in the test of equal slopes across regression quantiles (Koenker & Bassett, 1982). The test is therefore the Wald test of these moderation parameters (df = their number). A test of the differences of the effects across p would be singular at this null (the effects of the shape parameters vanish when the moderation is 0). Curves that change with p without such a parameter (e.g. a SHASH latent target correlated with the predictor) are tested by their differences from the median (noted); curves constant by construction are reported as such.
A list of class 'birt_qtest': 'test' (per curve: the tested 'parameters', 'chisq', 'df', 'p.value') and 'differences' (per curve and level: 'diff' = beta(p) - beta(median), 'se', 'z', 'p.value'). With ELGM the statistic uses the posterior mean and covariance (a normal approximation).
Koenker, R., & Bassett, G. (1982). Robust tests for heteroscedasticity based on regression quantiles. *Econometrica, 50*(1), 43-61. doi:10.2307/1912528
For a model fitted with 'engine = "rtmb"': the effect of each predictor on the conditional p-quantile of the second latent variable (e.g. 'speed ~ theta + x') and of each continuous indicator (e.g. the cross-relation of 'theta' with a log response time), averaged over the model-implied distribution of the moderators (average marginal effects), with delta-method standard errors (with 'method = "elgm"': posterior means, SDs and 95% credible intervals). With normal residuals and no moderators the effects are the same at every level (the mean-model coefficients); 'shash()' and 'scale' / 'skew' moderators let them change with p. These are the counterparts of the coefficients of 'ald(p)' fits (engine '"jags"'), from one fit for all p.
quantiles(object, ...) ## S3 method for class 'birt_rtmb' quantiles(object, p = object$control$p, ...)quantiles(object, ...) ## S3 method for class 'birt_rtmb' quantiles(object, p = object$control$p, ...)
object |
A 'birt_rtmb' fit. |
... |
Unused. |
p |
Quantile levels. |
'band.lower' / 'band.upper' form a simultaneous 95% band over the levels 'p' of each target-predictor curve (sup-t band; Montiel Olea & Plagborg-Moller, 2019): every effect of the curve lies in its band with probability 0.95, so the levels where the band excludes 0 can be read off together. The critical value is the 0.95 quantile of max |Z| for Z normal with the correlation of the curve's estimates (the joint delta-method covariance; with ELGM, the posterior covariance). [quantile_test()] tests whether a curve changes with p.
A data frame with 'target', 'predictor', 'p', 'est', 'se', 'ci.lower', 'ci.upper' (pointwise), 'band.lower', 'band.upper' (simultaneous over 'p'); attribute 'vcov': the joint covariance of 'est'. It prints with 3 decimals (class 'birt_tbl', see [birt_print]).
Montiel Olea, J. L., & Plagborg-Moller, M. (2019). Simultaneous confidence bands: Theory, implementation, and an application to SVARs. *Journal of Applied Econometrics, 34*(1), 1-17. doi:10.1002/jae.2656
For a model fitted with 'engine = "rtmb"': how the information that the response times carry about the first latent variable (e.g. ability, through cross-loadings 'ability =~ t1' and scale or skew moderation 'V(t1) ~ ability') is spread over the quantile levels of the response-time distribution, and how much of it, and of the reliability, a time limit keeps. Speed is a nuisance and is projected out, as in [cond_reliability()].
rt_information( object, theta = c(-2, 0, 2), bands = c(0, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 1), limits = list(slow = c(0.75, 0.9, 0.95, 0.99), fast = c(0.01, 0.05, 0.1)), at = NULL, k = 60 ) ## S3 method for class 'birt_rt_info' print(x, ...) ## S3 method for class 'birt_rt_info' plot(x, ...)rt_information( object, theta = c(-2, 0, 2), bands = c(0, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 1), limits = list(slow = c(0.75, 0.9, 0.95, 0.99), fast = c(0.01, 0.05, 0.1)), at = NULL, k = 60 ) ## S3 method for class 'birt_rt_info' print(x, ...) ## S3 method for class 'birt_rt_info' plot(x, ...)
object |
A fit of 'birt(..., engine = "rtmb")' with continuous indicators. |
theta |
Values of the first latent variable. |
bands |
Breaks of the quantile bands. |
limits |
Quantile levels of the limits; 'slow' for time limits, 'fast' for fast responses. |
at |
Covariate and moderator values (default: their means), as in [cond_reliability()]. |
k |
Gauss-Legendre nodes per interval. |
x |
A 'birt_rt_info' object. |
... |
Unused. |
* 'bands': the information of the response times in each band of quantile levels (all items at their own quantiles). The bands partition the RT information of [cond_reliability()] ('info_rt'); they show where it lies, not what a design would keep. * 'limits': the information and reliability when every response time beyond a limit is censored (only known to exceed it): ‘side = "slow"', a time limit at the item’s quantile u (as with a time limit); 'side = "fast"', responses faster than the quantile u known only as fast (as when rapid responses are not modelled). Censoring keeps the information that a response was beyond the limit, so a slow tail can hold much of the partition yet lose little under a time limit. Discarding the responses instead (truncation) loses more and biases a model that ignores it.
The decomposition of Fisher information over an ordered sample follows Zheng & Gastwirth (2000); censored information Efron & Johnstone (1990) and Escobar & Meeker (1998). The curves describe the fitted model: fast responses that are rapid guesses (Wise & Kong, 2005) violate it, and their apparent information is then an artefact. The response-time distribution is that of the fitted model without censoring; for a fit with ‘censor =', [cond_reliability()] gives the information under the fit’s own limits. Binary and ordinal indicators enter 'rel' as in [cond_reliability()], and moderated loadings or shifts are evaluated at the moderator values 'at'.
A list of class 'birt_rt_info': 'density' ('theta', 'u', 'info': the information density over u), 'bands' ('theta', 'band', 'info', 'share'), 'limits' ('theta', 'side', 'u', 'info_rt', 'kept' (share of the RT information), 'rel' (reliability with the accuracy items), 'rel_full'), and 'check' (the RT information of the partition and of [cond_reliability()]).
Zheng, G., & Gastwirth, J. L. (2000). Where is the Fisher information in an ordered sample? *Statistica Sinica, 10*, 1267-1280.
Efron, B., & Johnstone, I. M. (1990). Fisher's information in terms of the hazard rate. *The Annals of Statistics, 18*, 38-62. doi:10.1214/aos/1176347492
Escobar, L. A., & Meeker, W. Q. (1998). Fisher information matrices with censoring, truncation, and explanatory variables. *Statistica Sinica, 8*, 221-237.
Wise, S. L., & Kong, X. (2005). Response time effort: A new measure of examinee motivation in computer-based tests. *Applied Measurement in Education, 18*, 163-183. doi:10.1207/s15324818ame1802_2
Writes the lavaan-style syntax of the usual RT-IRT models from the item and time column names, to pass to [birt()] or to edit.
rtirt_syntax( items, times, cross = c("none", "free", "ssp"), structure = NULL, cov = NULL, cov_on = c("speed", "ability", "both"), speed_var = c("free", "fixed"), ability = "ability", speed = "speed" )rtirt_syntax( items, times, cross = c("none", "free", "ssp"), structure = NULL, cov = NULL, cov_on = c("speed", "ability", "both"), speed_var = c("free", "fixed"), ability = "ability", speed = "speed" )
items |
Names of the binary item columns. |
times |
Names of the (log) response time columns, in the order of 'items'. |
cross |
Cross-loadings of ability on the response times: '"none"', '"free"', or '"ssp"' (spike-and-slab priors; engine '"jags"'). |
structure |
Relation of ability and speed: '"cor"' (correlated, van der Linden, 2007), '"path"' ('speed ~ ability'), or '"none"' (independent). The default is '"cor"' without cross-loadings and '"none"' with them (a common cross-loading is the same as the correlation or the path, so they are not identified together). |
cov |
Covariates of the latent regression(s). |
cov_on |
Which latent variables are regressed on 'cov': '"speed"', '"ability"' or '"both"'. |
speed_var |
'"free"' (default; the speed loadings are fixed at -1, so the speed variance is estimated) or ‘"fixed"' ('speed ~~ 1*speed', as in the dissertation’s Chapter 5; usually too restrictive for log seconds). |
ability, speed
|
Names of the latent variables. |
The syntax (a character string of class 'birt_syntax', printed with 'cat').
items <- paste0("y", 1:5) times <- paste0("t", 1:5) rtirt_syntax(items, times) # van der Linden (2007) rtirt_syntax(items, times, cross = "ssp") # cross-loadings, ssp priors rtirt_syntax(items, times, structure = "path", cov = c("x1", "x2"))items <- paste0("y", 1:5) times <- paste0("t", 1:5) rtirt_syntax(items, times) # van der Linden (2007) rtirt_syntax(items, times, cross = "ssp") # cross-loadings, ssp priors rtirt_syntax(items, times, structure = "path", cov = c("x1", "x2"))
The counterpart of [jags_code()] for 'engine = "rtmb"': writes a standalone R script with the model written out item by item (the joint log density of one person given the standard-normal latent values), the Laplace fit and the AGHQ refinement. It expects the data frame as 'data'; parameter names and starting values are those of [birt()], so 'fit$rtmb$par' can be plugged in; from a fit, the script starts at its estimates. As in birt, the SHASH shapes are bounded and the full model starts from the normal one (birt also halves the step when an AGHQ round does not improve).
rtmb_code( x, data = NULL, binary = NULL, family = NULL, log_rt = FALSE, ordered = NULL, itemtype = "grm", censor = NULL, estimate = TRUE, k = NULL, file = NULL )rtmb_code( x, data = NULL, binary = NULL, family = NULL, log_rt = FALSE, ordered = NULL, itemtype = "grm", censor = NULL, estimate = TRUE, k = NULL, file = NULL )
x |
A 'birt_rtmb' fit, or model syntax. |
data, binary, family, log_rt, ordered, itemtype, censor
|
As in [birt()] (when 'x' is syntax). |
estimate |
Include the fitting part (Laplace and AGHQ); 'FALSE' gives the model only. |
k |
AGHQ nodes per latent dimension (default as in [rtmb_control()]). |
file |
Optional file to write the script to. |
The script (character), printed with 'cat()'.
if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(K = 5, seed = 1) f <- birt(rtirt_syntax(paste0("y", 1:5), paste0("t", 1:5), cross = "free"), d, control = rtmb_control(method = "aghq"), progress = FALSE ) path <- file.path(tempdir(), "model_rtmb.R") rtmb_code(f, file = path) # then: data <- d; source(path) }if (requireNamespace("RTMB", quietly = TRUE)) { d <- sim_rtirt(K = 5, seed = 1) f <- birt(rtirt_syntax(paste0("y", 1:5), paste0("t", 1:5), cross = "free"), d, control = rtmb_control(method = "aghq"), progress = FALSE ) path <- file.path(tempdir(), "model_rtmb.R") rtmb_code(f, file = path) # then: data <- d; source(path) }
Control settings of the RTMB engine
rtmb_control( method = c("elgm", "aghq", "laplace"), k = NULL, max_rounds = 8, tol = 0.001, p = c(0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95), trace = FALSE, inner = c("aghq", "laplace"), hyper = "auto", k_hyper = NULL, cond_modes = TRUE )rtmb_control( method = c("elgm", "aghq", "laplace"), k = NULL, max_rounds = 8, tol = 0.001, p = c(0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95), trace = FALSE, inner = c("aghq", "laplace"), hyper = "auto", k_hyper = NULL, cond_modes = TRUE )
method |
'"elgm"' (default): approximate Bayesian inference for the model as an extended latent Gaussian model (Stringer, Brown & Stafford, 2023) with the priors of [dpriors()] and 'prior()'. The latent Gaussian field W holds the person latents only; they are integrated out per person ('inner'), and the posterior of the remaining parameters is integrated by an outer adaptive quadrature: 'k_hyper' nodes per dimension on the hyperparameters 'hyper', a Gaussian (Laplace) treatment of the other directions. See *Details*. '"aghq"': maximum likelihood with adaptive Gauss-Hermite quadrature over the person latents (as GLMMadaptive or mirt; needed by [score_test()], [quantile_score()], [dif_tree()]), also written '"ml"' (or '"ML"', '"mle"'); '"laplace"': maximum likelihood, Laplace. |
k |
Quadrature nodes per latent dimension (default 21 for one latent variable, 9 for two). |
max_rounds, tol
|
AGHQ: recompute the quadrature centres at most 'max_rounds' times, until -2 log L changes by less than 'tol'. |
p |
Default quantile levels of [quantiles()]. |
trace |
Print the progress of the optimization. |
inner |
'method = "elgm"': the integral over the person latents, '"aghq"' (default; the field is separable by person, so per-person AGHQ is cheap and more accurate) or '"laplace"' (the Laplace approximation of the original ELGM). |
hyper |
'method = "elgm"': parameters with quadrature in the outer layer. '"auto"' (default): the SHASH shape (and scale / skew moderators) of the second latent variable when it is 'shash()', otherwise 'c("cor", "path", "sd")'. Keywords '"cor"' (latent correlation), '"path"' (latent regression path), '"sd"' (free latent SDs), '"shape"', '"cross"' (cross-loadings), '"resid"' (residual log SDs), or names of internal parameters ('names(fit$rtmb$x)'). 'character()' treats every direction as Gaussian (posterior mode + Laplace). '"auto"' uses at most 3 directions (the SHASH skewness and tail weight first, then skew and scale moderators, then correlation, path and SDs) and says which ones it treats as Gaussian: with 4 directions the 5^4 = 625 nodes took 5 times the outer time of 3 directions for posterior means within 0.034 SD and SDs within 2.1% (a SHASH speed with skew and scale moderators, N = 1000, 20 items); name them in 'hyper' for quadrature on all. |
k_hyper |
Outer nodes per hyperparameter (default 11, 9, 5, 5, 3, 3 for 1 to 6 hyperparameters). Each quadrature direction is scaled separately on both sides from the drop of the log posterior at +-2 SD (as in INLA's grid), so skewed posteriors are covered. |
cond_modes |
'method = "elgm"': at each outer node, move the Gaussian directions to their conditional posterior mode (default 'TRUE'; Laplace over them). 'FALSE' places them on the Gaussian conditional-mean line of the posterior mode, which is faster but thins the tails when the conditional modes move nonlinearly (e.g. SHASH skewness). |
**ELGM.** With 'method = "elgm"' the fit is the posterior mode of log p(y | theta) + log p(theta), with log p(y | theta) from per-person AGHQ (or Laplace), and the outer posterior p(theta | y) is integrated on a mixed grid: the 'hyper' directions come first in the Cholesky factor of the inverse Hessian, the other parameters sit at their conditional posterior modes ('cond_modes') and contribute their conditional (Laplace) variance. Posterior means, SDs and 95% intervals (from the quadrature moments, on the log scale for SDs and the Fisher-z scale for correlations) are reported for every parameter and for [quantiles()]; EAP scores are mixtures over the outer nodes; 'fit_indices()' gives the log marginal likelihood (for Bayes factors in [compare()]). Item parameters are deliberately kept out of W: with items in W the inner mode is a penalized joint maximum likelihood (incidental parameter bias).
A list used by [birt()] ('engine = "rtmb"').
Stringer, A., Brown, P., & Stafford, J. (2023). Fast, scalable approximations to posterior distributions in extended latent Gaussian models. *Journal of Computational and Graphical Statistics, 32*, 84-98.
Tests whether item or structural parameters change along a person-level moderator (DIF, measurement invariance, item position effects) from a single maximum likelihood fit ('birt(..., engine = "rtmb")', also with 'ordered = TRUE'): the casewise scores ([estfun.birt_rtmb()]) are ordered by the moderator, cumulated after decorrelation and compared with a Brownian bridge (Merkle & Zeileis, 2013).
score_test(object, ...) ## S3 method for class 'birt_rtmb' score_test( object, moderator, parm = "measurement", by_item = FALSE, test = c("auto", "DM", "CvM", "maxLM", "LM"), decorrelate = c("block", "full"), cluster = NULL, impact_check = TRUE, ... )score_test(object, ...) ## S3 method for class 'birt_rtmb' score_test( object, moderator, parm = "measurement", by_item = FALSE, test = c("auto", "DM", "CvM", "maxLM", "LM"), decorrelate = c("block", "full"), cluster = NULL, impact_check = TRUE, ... )
object |
A maximum likelihood fit of 'birt(..., engine = "rtmb")' ('rtmb_control(method = "aghq")'). |
... |
Passed to the method. |
moderator |
One value per person: numeric (continuous or ordered) or a factor (logical and character are turned into factors). |
parm |
Parameters to test: '"measurement"' (loadings and intercepts or thresholds of the binary and ordinal items, plus their moderation effects; default), '"rt"' (parameters of the continuous indicators, including cross-loadings on them), '"structural"', a regular expression on the parameter labels, or column indices. |
by_item |
'TRUE': also test the parameters of each indicator separately, with Holm-adjusted p-values. |
test |
'"auto"' ('"DM"' for numeric moderators, '"LM"' for factors), '"DM"' (double maximum), '"CvM"' (Cramér-von Mises), '"maxLM"' (supremum LM, 10% trimming) or '"LM"'. |
decorrelate |
'"block"' (within the tested parameters) or '"full"' (all parameters, then the tested components; not valid for moderators built from the response times). |
cluster |
Optional cluster identifiers (see Details). |
impact_check |
Message when the moderator correlates with the factor scores but is not a covariate of the model. |
**Moderators derived from the response times.** The marginal distribution of the continuous indicators (response times) does not involve the parameters of the binary or ordinal items, so in a joint model their scores have mean zero given any function of the times (for example the total log time): the measurement parameters can be tested along such a moderator, provided the scores are decorrelated within the tested block ('decorrelate = "block"', the default). The parameters of the continuous indicators cannot be tested along a moderator built from them.
**DIF and impact.** A moderator related to the latent variable (impact) shifts all intercepts and is flagged as instability. To test DIF beyond impact, add the moderator to the latent regression ('f ~ z' in the syntax, or 'E(f) ~ z' for ordinal items) and test that fit; 'score_test()' gives a message when the moderator correlates with the factor scores and is not a covariate of the model.
**Clustered data.** With 'cluster', the scores are summed within clusters, which is exact for a moderator that is constant within clusters; a person-level moderator in clustered data is refused.
**ELGM fits.** 'score_test()' is a maximum likelihood tool and refuses an ELGM fit (the default of [birt()]): refit the same model with 'control = rtmb_control(method = "aghq")'. The tests need casewise scores that sum to zero at the estimate; at the ELGM posterior mode they sum to minus the gradient of the log prior, the reported ELGM estimates are posterior means rather than the mode, and MAP versions of the score tests need centred scores, with error rates that depend on the covariance estimate (Debelak, Pawel, Strobl & Merkle, 2022). The Bayesian route for one effect is to fit the ELGM model with and without it (e.g. 'E(y1) ~ z' for uniform DIF of 'y1' along a numeric 'z') and compare the marginal likelihoods with [compare()] (a Bayes factor, which depends on the prior of the effect, 'dpriors(beta = )').
birtRcpp has a function of the same name for its models; with both packages attached, the one attached last is found first, and 'birt::score_test()' dispatches to birtRcpp for its models.
A data frame of class 'birt_score_test': 'parameters', 'n', 'statistic', 'p.value' and, with 'by_item', 'p.holm'.
Merkle, E. C., & Zeileis, A. (2013). Tests of measurement invariance without subgroups: A generalization of classical methods. *Psychometrika, 78*(1), 59-82. doi:10.1007/s11336-012-9302-4
Debelak, R., Pawel, S., Strobl, C., & Merkle, E. C. (2022). Score-based measurement invariance checks for Bayesian maximum-a-posteriori estimates in item response theory. *British Journal of Mathematical and Statistical Psychology, 75*(3), 728-752. doi:10.1111/bmsp.12275
d <- sim_rtirt(N = 400, K = 6, seed = 1) f <- birt(rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)), d, control = rtmb_control(method = "aghq"), progress = FALSE ) score_test(f, d$x1 > 0, by_item = TRUE) # a moderator unrelated to the items score_test(f, rowSums(d[paste0("t", 1:6)])) # along the total log timed <- sim_rtirt(N = 400, K = 6, seed = 1) f <- birt(rtirt_syntax(paste0("y", 1:6), paste0("t", 1:6)), d, control = rtmb_control(method = "aghq"), progress = FALSE ) score_test(f, d$x1 > 0, by_item = TRUE) # a moderator unrelated to the items score_test(f, rowSums(d[paste0("t", 1:6)])) # along the total log time
For 'birt(..., engine = "rtmb")': replaces the normal residual of the second latent variable (e.g. 'speed' in 'speed ~ theta + x') or of continuous indicators (e.g. log response times, 'family = list(.continuous = shash(skew = "theta"))') by the sinh-arcsinh distribution of Jones and Pewsey (2009): 'e = sinh((asinh(z) + eps) / delta)' with 'z ~ N(0, 1)', where 'eps' sets the skewness ('eps > 0': right-skewed) and 'delta' the tail weight ('delta < 1': heavier tails than the normal). Moderators make the skewness ('skew') linear in latent variables or covariates, and 'V(y) ~ z' in the model syntax the log scale, so that the predictor effects on the conditional quantiles can change with the level p; see [quantiles()]. This is the likelihood-based counterpart of fitting [ald()] at several quantiles with 'engine = "jags"': one fit gives every quantile, and the quantile curves cannot cross.
shash(skew = NULL)shash(skew = NULL)
skew |
Moderators of the skewness 'eps'. |
An object of class 'birt_family'.
shash(skew = "theta")shash(skew = "theta")
GRM with 'J' items and 4 categories; the log total time 'logT' lowers the discrimination of every item (person precision, 'alpha'), shifts the thresholds of items 1 and 2 ('beta'), and is related to the trait mean ('gamma').
sim_hgrm(N = 800, J = 10, alpha = 0.25, beta = 0.4, gamma = 0.2, seed = NULL)sim_hgrm(N = 800, J = 10, alpha = 0.25, beta = 0.4, gamma = 0.2, seed = NULL)
N, J
|
Persons, items. |
alpha |
Common effect of standardized 'logT' on log discrimination. |
beta |
Threshold shift of items 1 and 2 per SD of 'logT'. |
gamma |
Effect of 'logT' on the trait mean. |
seed |
Random seed for these data; 'NULL' (default) draws from the current random number stream, as in [sim_rtirt()]. |
A data frame with 'y1..yJ' (1..4), 'Time' and 'logT'.
2PL accuracy and lognormal response times with item-specific cross-loadings of ability on log response time, optionally an effect of ability on the log residual SD of the log response time (scale), and speed depending on two covariates.
sim_rtirt( N = 500, K = 10, rho = rep(c(0, 0.3), length.out = K), scale = 0, sd_speed = 0.3, beta = c(0.1, 0), seed = NULL )sim_rtirt( N = 500, K = 10, rho = rep(c(0, 0.3), length.out = K), scale = 0, sd_speed = 0.3, beta = c(0.1, 0), seed = NULL )
N |
Persons. |
K |
Items. |
rho |
Cross-loadings of ability on log RT (length 1 or K). |
scale |
Effects of ability on the log residual SD of log RT (length 1 or K; 0: none, as in 'V(t) ~ ability'). |
sd_speed |
SD of the speed residual. |
beta |
Effects of covariates 'x1', 'x2' on speed. |
seed |
Random seed for these data (the random number stream of the session is left as it was); 'NULL' (default) draws from the current stream, as 'rnorm()' does, so that 'set.seed()' before the call, or repeated calls in a simulation, give new data each time. |
A data frame with 'y1..yK' (0/1), 't1..tK' (log RT), 'x1', 'x2'.
Recomputes the parameter table, scores, reliability and item tables from the kept chains, e.g. after a chain was found in a separate posterior mode (with free cross-loadings, ability can rotate into a second speed factor in one chain). The marginal-likelihood criteria are dropped; recompute them with [add_fit_indices()].
subset_chains(object, chains)subset_chains(object, chains)
object |
A 'birt' fit. |
chains |
Indices of the chains to keep. |
The fit with the reduced draws.
if (requireNamespace("rjags", quietly = TRUE)) { fit <- birt("ability =~ y1 + y2 + y3 + y4 + y5", sim_rtirt(N = 200, K = 5, seed = 1), engine = "jags", n_chains = 3, n_iter = 1500, n_burn = 500, fit_indices = FALSE, progress = FALSE ) # e.g. chain 3 in another mode: compare per-chain means, then drop it sapply(1:3, function(ch) mean(fit$draws[[ch]][, 1])) fit <- subset_chains(fit, c(1, 2)) }if (requireNamespace("rjags", quietly = TRUE)) { fit <- birt("ability =~ y1 + y2 + y3 + y4 + y5", sim_rtirt(N = 200, K = 5, seed = 1), engine = "jags", n_chains = 3, n_iter = 1500, n_burn = 500, fit_indices = FALSE, progress = FALSE ) # e.g. chain 3 in another mode: compare per-chain means, then drop it sapply(1:3, function(ch) mean(fit$draws[[ch]][, 1])) fit <- subset_chains(fit, c(1, 2)) }