Provides tools to define factorial simulation conditions and generate binary or ordinal item responses under common-factor and probit graded response model parameterizations. Supports multivariate normal and correlated gamma latent traits, reproducible replications, parameter conversion, and structured storage of generated datasets. The graded response model follows Samejima (1969).
Simulation of Ordinal Responses for Psychometric Studies
simPsyStudy defines factorial simulation conditions and generates binary or ordinal item responses under common-factor and probit graded response model parameterizations. It records the parameters, seeds, datasets, and response summaries needed to reproduce each condition.
The multiple-condition interface lets you define named sets of theta values, loadings, and thresholds, then generate every combination with separate output folders and recorded seeds.
Development status: simPsyStudy continues development of the original AUTTT package. This is a development release; CRAN preparation is in progress.
Install the development version from GitHub:
# Install remotes once, if needed.
install.packages("remotes")
remotes::install_github("Boklauth/simPsyStudy", upgrade = "never")
library(simPsyStudy)
If you update an already loaded package, restart R before loading the newly installed version.
| Component | Supported features |
|---|---|
| Latent traits | User-supplied values, multivariate normal generation, and two correlated gamma-generation methods |
| Response models | Common-factor ordinal-variable model and probit graded response model |
| Outcomes | Binary and ordered categorical item responses |
| Study designs | One or more factors and full factorial combinations of named theta, loading, and threshold sets |
| Reproducibility | Explicit replication seeds, condition manifests, saved inputs, and session information |
This example creates eight conditions: two sample sizes, two loading levels, and two threshold patterns. With two replications per condition, it produces 16 datasets.
There are six items and two factors. Items 1-3 measure Factor 1; items 4-6 measure Factor 2. Each theta matrix has one row per person and one column per factor.
library(simPsyStudy)
model <- list(1:3, 4:6)
set.seed(2026)
make_theta <- function(n) {
factor1 <- rnorm(n)
factor2 <- 0.4 * factor1 + sqrt(1 - 0.4^2) * rnorm(n)
cbind(Factor1 = factor1, Factor2 = factor2)
}
theta_sets <- list(
N100 = make_theta(100),
N200 = make_theta(200)
)
This generates normal theta values with a population correlation of 0.4. Sample correlations will vary. These matrices are held fixed across the item-response replications.
Supply one standardized FAOV (Factor Analysis with Ordinal Variables) loading per item. A threshold vector is shared by all items; alternatively, supply a matrix with one row per item. Two thresholds give three response categories.
loading_sets <- list(
low = rep(0.4, 6),
high = rep(0.8, 6)
)
threshold_sets <- list(
symmetric = c(-0.5, 0.5),
asymmetric = c(-1.2, 0.2)
)
Use FAOV thresholds, not already converted GRM intercepts. The function converts the loadings and thresholds internally.
# Choose a new folder for each run. This path is relative to getwd().
output_dir <- file.path(getwd(), "simPsyStudy_example_run_01")
conditions <- simulate_conditions(
model = model,
theta_sets = theta_sets,
loading_sets = loading_sets,
threshold_sets = threshold_sets,
replications = 2,
output_dir = output_dir,
seed = 1234,
file_prefix = "demo"
)
conditions[, c("condition", "theta", "loading", "threshold", "status")]
The function creates the output directory and a subfolder for each condition. If the destination already contains matching condition folders or run metadata, it stops before overwriting them. To repeat the example, change simPsyStudy_example_run_01 to a new folder name.
first_file <- file.path(
output_dir, conditions$folder[1], "demo_grm_rep1.dat"
)
responses <- read.table(first_file, header = FALSE)
dim(responses) # 100 persons, 6 items
head(responses)
The .dat files contain space-separated responses with no header or row names. This example uses categories 1, 2, and 3. Binary simulations use categories 0 and 1.
| Location | Files | Purpose |
|---|---|---|
| Run folder | conditions.csv |
Condition names, seeds, sample sizes, status, and any error messages |
| Run folder | simulation_plan.rds |
Input sets, design, simulator, and R session information |
| Run folder | folders.Rdata |
Condition-folder names for subsequent workflows |
| Each condition | demo_grm_rep1.dat, demo_grm_rep2.dat |
Simulated item responses |
| Each condition | parameters.rds |
Loadings, thresholds, converted parameters, and replication seeds |
| Each condition | study_cell.Rdata |
Simulator return object for that condition |
| Each condition | Response-probability CSV files | Observed response summaries |
| Each condition | Theta CSV and replication-list .dat file |
Theta inputs and dataset filenames |
A failed condition stops the run and records the error in conditions.csv; completed outputs are preserved. Automatic resuming is not currently supported.
"U" and "N"The single-condition function simdata_grm() provides two methods for drawing the random values used to assign item-response categories:
| Method | Random draw | How it is used |
|---|---|---|
"U" |
Uniform distribution, U(0, 1) |
Draws a value between 0 and 1 for each person and item, then compares it with the model's cumulative response probabilities to assign a category. |
"N" |
Standard normal distribution, N(0, 1) |
Draws an independent standard normal value for each person and item, converts it to a probability, and uses that probability to assign a category. |
These options describe the response-generation draws, not the distribution of the supplied theta values. Both methods draw independently for every person and item and have the same target distribution. simulate_conditions() accepts either method and defaults to "U".
simdata_faov() and simdata_grm()The two functions provide equivalent probit response models when their parameters are placed on corresponding scales. In the FAOV formulation, the continuous response underlying item j is
$$ Y_j^* = \lambda_j\theta + \sqrt{1-\lambda_j^2}\epsilon_j, \qquad \epsilon_j \sim N(0,1), $$
and thresholds $\tau_{jk}$ divide that response into ordered categories. The corresponding GRM cumulative probability is
$$ P(Y_j \geq k+1 \mid \theta) = \Phi(a_j\theta-d_{jk}), $$
with the conversion
$$ a_j = \frac{\lambda_j}{\sqrt{1-\lambda_j^2}}, \qquad d_{jk} = \frac{\tau_{jk}}{\sqrt{1-\lambda_j^2}}. $$
In R, the conversion is:
a <- loadings / sqrt(1 - loadings^2)
d <- sweep(thresholds, 1, sqrt(1 - loadings^2), "/")
Here, d is a positive boundary parameter. If the alternative intercept form $\Phi(a\theta+\delta)$ is used, its intercept is $\delta=-d$. The IRT difficulty is $b=d/a=\tau/\lambda$ when the loading is nonzero.
The implementations are equivalent when:
With identical theta values and seeds, simdata_faov() and simdata_grm(method = "N") generate identical responses because both use standard-normal response draws. With method = "U", the individual datasets generally differ, but they follow the same response distribution.
The functions are not directly equivalent when simdata_faov(theta_matrix = NULL) generates new theta values within each replication, when factor variances differ from 1, or when the intended model includes cross-loadings, correlated item residuals, or a logistic link. If factor variances are not 1 and the underlying item responses must remain standardized, residual variances need to be based on $1-\operatorname{diag}(\Lambda\Phi\Lambda')$ instead of $1-\lambda^2$.
P(Y[j] >= k + 1 | theta) = pnorm(a[j] * theta - d[j, k]). The condition interface converts standardized FAOV loadings and thresholds to this parameterization.i and replication r, the response-generation seed is seed + (i - 1) * replications + r. The caller's random-number state is restored afterward.simulate_conditions() and simulate_condition() call the same interface. The plural name is recommended because one call can generate many conditions.Other simulation helpers include TSK() for thresholds and response-distribution summaries, matrix-conversion functions, and multivariate normal and correlated gamma theta generators. gamma_from_normal() retains the original rejection-sampling procedure, while gamma_from_normal2() uses a Gaussian-copula transformation.
Both gamma functions are designed to generate positively skewed multivariate data, but they use different procedures and should not be expected to return identical samples or correlations.
| Function | Approach | Dimensions | Main inputs |
|---|---|---|---|
gamma_from_normal() |
Original rejection-sampling procedure: draws one correlated multivariate-normal vector and one uniform value per attempt, then retains the complete vector when all marginal gamma-to-normal criteria pass | Exactly three | shape, rate, mean_vec, cov_matrix, size, c, and seed_num |
gamma_from_normal2() |
Gaussian-copula procedure: transforms correlated normal values to uniform probabilities and then to gamma values | One or more | shape, rate, mean_vec, cov_matrix, size, and seed_num |
Use the original rejection approach as follows:
correlation <- to_cormatrix(c(0.49, 0.74, 0.87), n_dim = 3)
covariance <- to_covmatrix(correlation, c(0.95, 0.98, 1.1))
theta_rejection <- gamma_from_normal(
shape = 5,
rate = 5,
mean_vec = c(-0.1, 0, 0.1),
cov_matrix = covariance,
size = 300,
c = 4,
seed_num = 45679
)
Use the Gaussian-copula approach with:
theta_copula <- gamma_from_normal2(
shape = 5,
rate = 5,
mean_vec = c(-0.1, 0, 0.1),
cov_matrix = covariance,
size = 300,
seed_num = 45679
)
Both functions return X, scaled.X, marginal skewness and kurtosis, and sample correlation and covariance matrices. The covariance matrix controls the dependence of the normal values used by each procedure; the sample covariance of the final skewed variables can differ from the input covariance.
TSK(): thresholds and distribution summariesTSK() converts expected category proportions into normal-theory thresholds and summarizes the shape of the corresponding ordinal response distribution. Supply the planned sample size with n and a vector of category probabilities with res_prop. The probabilities must be positive and sum to 1.
response_probabilities <- c(0.04, 0.06, 0.11, 0.37, 0.42)
item_distribution <- TSK(
n = 300,
res_prop = response_probabilities
)
item_distribution$thresholds
item_distribution$skew
item_distribution$Kurt1
The returned list contains the original category probabilities, cumulative probabilities, thresholds, skewness, adjusted skewness, excess kurtosis (Kurt1), ordinary kurtosis (Kurt2), and a sample-size-adjusted excess-kurtosis estimate (Kurt3). The thresholds can be repeated across items or assembled into an item-by-threshold matrix for simdata_faov() or simulate_conditions().
create_theta_mvn(): multivariate normal theta valuescreate_theta_mvn() generates correlated multivariate-normal latent-trait values. Supply the sample size, one mean and standard deviation per factor, the pairwise factor correlations, and a seed. Pairwise correlations are ordered (1, 2), (1, 3), ..., (m - 1, m).
theta <- create_theta_mvn(
size = 300,
mean_vec = c(-0.1, 0, 0.1),
sd_vec = c(0.95, 0.98, 1.10),
ifcor_vec = c(0.30, 0.50, 0.60),
seed_num = 45679
)
head(theta$X)
head(theta$scaled.X)
theta$cor_mat
The returned X matrix has the requested means, standard deviations, and population correlation structure subject to sampling variation. scaled.X standardizes every generated factor and is the recommended input when loadings are interpreted as standardized FAOV loadings. The function also returns marginal skewness and kurtosis and the sample correlation and covariance matrices.
For complete function documentation, run:
?simulate_conditions
?TSK
?create_theta_mvn
See NEWS.md for development changes. Report problems through the GitHub issue tracker, including a small reproducible example and the output of sessionInfo().
To obtain the package citation, run:
cite <- format(citation("simPsyStudy"), style = "text")
cat(gsub("_", "", cite, fixed = TRUE), "\n")
Klauth B (2026). simPsyStudy: Simulation of Ordinal Responses for Psychometric Studies. R package version 1.1.8, https://github.com/Boklauth/simPsyStudy.
Developed by Bo Klauth. simPsyStudy is distributed under the MIT license.