| Title: | Automatic Toolkit for Construction, Optimization, Scoring and Simulation of Forced-Choice Tests |
|---|---|
| Description: | Forced-choice (FC) response has gained increasing popularity and interest for its resistance to faking when well-designed (Cao & Drasgow, 2019 <doi:10.1037/apl0000414>). To established well-designed FC scales, typically each item within a block should measure different trait and have similar level of social desirability (Zhang et al., 2020 <doi:10.1177/1094428119836486>). Recent study also suggests the importance of high inter-item agreement of social desirability between items within a block (Pavlov et al., 2021 <doi:10.31234/osf.io/hmnrc>). In addition to this, FC developers may also need to maximize factor loading differences (Brown & Maydeu-Olivares, 2011 <doi:10.1177/0013164410375112>) or minimize item location differences (Cao & Drasgow, 2019 <doi:10.1037/apl0000414>) depending on scoring models. Decision of which items should be assigned to the same block, also called as item pairing, is thus critical to the quality of an FC test. Because such pairing process often requires researchers to meet multiple objectives, manual pairing becomes impractical or even not feasible once the number of latent traits and/or number of items per elevates. To address these problems, autoFC is developed as a automatic and efficient tool for facilitating the automatic construction of FC tests (Li et al., 2022 <doi:10.1177/01466216211051726>), essentially exempting users from the burden of manual item pairing. Given characteristics of each item (and item responses), FC measures can be constructed either automatically based on user-defined pairing criteria and weights, or based on exact specifications of each block (i.e., blueprint; see Li et al., 2025 <doi:10.1177/10944281241229784>). Users can also generate simulated responses based on the Thurstonian Item Response Theory model (Brown & Maydeu-Olivares, 2011 <doi:10.1177/0013164410375112>) and predict trait scores of simulated/actual respondents based on an estimated model. |
| Authors: | Mengtong Li [cre, aut] (ORCID: <https://orcid.org/0000-0002-1766-4976>), Tianjun Sun [aut] (ORCID: <https://orcid.org/0000-0002-3655-0042>), Bo Zhang [aut] (ORCID: <https://orcid.org/0000-0002-6730-7336>) |
| Maintainer: | Mengtong Li <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 1.0.0.1001 |
| Built: | 2026-06-10 07:15:53 UTC |
| Source: | https://github.com/cran/autoFC |
Constructs an initial set of forced-choice blocks that strictly adhere to a user-specified blueprint (e.g., exact trait and keying combinations for every block). It utilizes a fast random-search heuristic to simultaneously optimize a specified matching criterion (e.g., minimizing within-block difficulty variance).
This function acts as an excellent "Smart Initializer" before passing the
resulting blocks into assemble_blocks() for further global optimization.
build_blueprint_blocks( item_chars, blueprint, bp_block_col = "block", bp_trait_col = "trait", bp_sign_col = "sign", bp_crit_col = NULL, df_id_col = "item_num", df_trait_col = "trait", df_sign_col = "sign", df_crit_col = NULL, optim_func = NULL, adjust_factor = 1.1, max_comb_attempts = 100, max_adjust_attempts = 5 )build_blueprint_blocks( item_chars, blueprint, bp_block_col = "block", bp_trait_col = "trait", bp_sign_col = "sign", bp_crit_col = NULL, df_id_col = "item_num", df_trait_col = "trait", df_sign_col = "sign", df_crit_col = NULL, optim_func = NULL, adjust_factor = 1.1, max_comb_attempts = 100, max_adjust_attempts = 5 )
item_chars |
A data frame containing the item pool characteristics. Each row represents a single item. |
blueprint |
A data frame detailing the exact structural requirements for every block. Must contain columns mapping to the block ID, required trait, and required keying direction. |
bp_block_col |
Character string. The column name in |
bp_trait_col |
Character string. The column name in |
bp_sign_col |
Character string. The column name in |
bp_crit_col |
Character string. Optional. The column name in |
df_id_col |
Character string. The column name in |
df_trait_col |
Character string. The column name in |
df_sign_col |
Character string. The column name in |
df_crit_col |
Character string. Optional. The column name in |
optim_func |
Character string or function object. Optional. The function used to evaluate the
items within a block (e.g., |
adjust_factor |
Numeric value > 1. The multiplier used to relax the matching criterion target
if a valid combination cannot be found. For example, |
max_comb_attempts |
Integer. The maximum number of random combinations to generate and test
against the matching criterion before relaxing the target. Defaults to |
max_adjust_attempts |
Integer. The maximum number of times to relax the target criterion using
|
A data frame containing the successfully matched items. The data frame will include
all original columns from item_chars, plus tracking columns N_adjusts,
final_criteria, and block_id.
If the algorithm fails to construct a block due to depleted inventory or overly strict
criteria, it returns the partially built scale up to the point of failure.
Create Target Pair Distribution for Scale-Level Fit
build_target_dist( traits, total_pairs, equal_mixed_ratio = c(1, 1), allow_same_trait = FALSE )build_target_dist( traits, total_pairs, equal_mixed_ratio = c(1, 1), allow_same_trait = FALSE )
traits |
A character vector of unique trait names (e.g., c("O", "C", "E", "A", "N")). |
total_pairs |
Integer. The total number of pairs your test will generate. |
equal_mixed_ratio |
Numeric vector of length 2. The desired ratio of "equal" keyed pairs to "mixed" keyed pairs (e.g., c(1, 1) for 50/50 split). |
allow_same_trait |
Logical. Do we allow items measuring the same traits to be paired together? |
A flexible data frame containing target counts for the SA algorithm.
Calculates the total "energy" of one or multiple paired item blocks. This function has been heavily optimized for speed, as it is called thousands of times during the Simulated Annealing optimization loop.
cal_block_energy(block, char_list, weights, fun_list)cal_block_energy(block, char_list, weights, fun_list)
block |
An n by k integer matrix of item indices. |
char_list |
A list of vectors, where each vector contains the full item pool data for one specific characteristic. |
weights |
A numeric vector of weights. |
fun_list |
A list of pre-matched R function objects (e.g., |
A numeric value indicating the total energy for the given item blocks.
Calculates local block energy including IIA metrics. Heavily optimized for Simulated Annealing.
cal_block_energy_with_iia( block, char_list, weights, fun_list, rater_chars, iia_weights = c(BPlin = 1, BPquad = 1, AClin = 1, ACquad = 1), verbose = FALSE )cal_block_energy_with_iia( block, char_list, weights, fun_list, rater_chars, iia_weights = c(BPlin = 1, BPquad = 1, AClin = 1, ACquad = 1), verbose = FALSE )
block |
An n by k integer matrix of item indices. |
char_list |
A list of vectors for item characteristics. |
weights |
A numeric vector of weights for item characteristics. |
fun_list |
A list of pre-matched R function objects. |
rater_chars |
A matrix of participant responses. |
iia_weights |
A numeric vector of length 4 for IIA metrics. |
verbose |
Logical. If TRUE, prints metrics. Defaults to FALSE. |
A numeric value indicating the total energy.
Convert Most/Least (MOLE) Survey Data into Pairwise Binary Outcomes
convert_mole_to_pairwise(data, n_blocks, block_size)convert_mole_to_pairwise(data, n_blocks, block_size)
data |
A data frame with exactly (2 * n_blocks) columns. Odd columns = "Most" choice, Even columns = "Least" choice. Values should be numeric item IDs (either global or local block IDs). |
n_blocks |
Integer. Number of blocks in the questionnaire. |
block_size |
Integer. Number of items per block. |
A data frame of pairwise binary outcomes (1s, 0s, and NAs).
Converts a dataset of ranked forced-choice blocks into pairwise binary comparisons suitable for Thurstonian IRT modeling.
convert_ranks_to_pairwise( data, n_blocks, block_size, lower_rank_is_better = TRUE )convert_ranks_to_pairwise( data, n_blocks, block_size, lower_rank_is_better = TRUE )
data |
A data frame or matrix where rows are respondents and columns are items. The columns must be ordered by block (e.g., Block 1 Item 1, Block 1 Item 2, Block 1 Item 3, Block 2 Item 1, etc.). Each column represents the assigned rank of that corresponding item in the block. |
n_blocks |
Integer. Number of blocks in the questionnaire. |
block_size |
Integer. Number of items per block. |
lower_rank_is_better |
Logical. In your data, does a smaller number mean a higher preference? (e.g., 1 = Most Preferred, 2 = Second Most). Defaults to TRUE. |
A data frame of binary outcomes (1s, 0s, and NAs). The column names will match the "i1i2", "i1i3" format expected by the syntax generators.
Generates a structured data frame representing a forced-choice test blueprint. Optionally exports it directly to a CSV file so test developers can manually design their block structures (assigning specific traits and keying) in spreadsheet software.
create_blueprint_template(n_blocks, block_size, file_path = NULL)create_blueprint_template(n_blocks, block_size, file_path = NULL)
n_blocks |
Integer. Number of total FC blocks. |
block_size |
Integer. Desired block size for the FC scale. |
file_path |
Character. Optional. If provided, the template will be saved
to this path as a CSV file (e.g., |
A data frame containing the block and item_num columns,
plus empty columns for trait and sign ready to be filled.
Calculates empirical reliability for IRT-based trait scores using the formula provided by Brown & Maydeu-Olivares (2018).
empirical_reliability(dataset, score_names, se_names = NULL)empirical_reliability(dataset, score_names, se_names = NULL)
dataset |
A data frame containing the trait estimates and standard errors. |
score_names |
Character vector. The names of the columns specifying trait scores. |
se_names |
Optional character vector. The names of the columns specifying trait
standard errors. If |
For trait scores estimated using item response theory (IRT) models, a single test-level reliability coefficient (like Cronbach's Alpha) is often inappropriate because the standard error of measurement varies across the latent continuum.
Empirical reliability provides a summary estimate of how reliable the trait scores are "as a whole" by comparing the variance of the estimated scores to the average error variance.
A named numeric vector containing the empirical reliability estimates for each trait.
Mengtong Li
Brown, A., & Maydeu-Olivares, A. (2018). Ordinal factor analysis of graded-preference questionnaire data. Structural Equation Modeling, 25(4), 516-529. doi:10.1080/10705511.2017.1392247
# Create fake scores and standard errors fake_scores <- data.frame( Trait1 = rnorm(100), Trait1_SE = runif(100, 0.1, 0.3), Trait2 = rnorm(100), Trait2_SE = runif(100, 0.2, 0.4) ) # Auto-detects the "_SE" columns empirical_reliability(fake_scores, score_names = c("Trait1", "Trait2"))# Create fake scores and standard errors fake_scores <- data.frame( Trait1 = rnorm(100), Trait1_SE = runif(100, 0.1, 0.3), Trait2 = rnorm(100), Trait2_SE = runif(100, 0.2, 0.4) ) # Auto-detects the "_SE" columns empirical_reliability(fake_scores, score_names = c("Trait1", "Trait2"))
Extract Only Adjacent Rank Pairs for Stan (Heister et al., 2025)
extract_adjacent_pairs_stan(raw_ranks, n_blocks, block_size)extract_adjacent_pairs_stan(raw_ranks, n_blocks, block_size)
raw_ranks |
Dataframe of raw rankings (1 = Best). |
n_blocks |
Integer. |
block_size |
Integer. |
A long-format list ready to inject directly into the Stan data list.
Extract Trait Scores and SEs from a Fitted Stan TIRT Model
extract_tirt_stan_scores(fit, stan_data)extract_tirt_stan_scores(fit, stan_data)
fit |
A fitted cmdstanr object. |
stan_data |
The data list passed to Stan (must contain the 'trait_names' attribute). |
A data frame with respondents as rows, and Traits and Trait SEs as columns.
Returns 1 if each element in the vector is unique, and 0 otherwise.
facfun(vec)facfun(vec)
vec |
Input vector. |
1 if each element in the vector is unique, and 0 otherwise.
Mengtong Li
facfun(c("Openness", "Neuroticism", "Agreeableness")) facfun(c("Openness", "Openness", "Agreeableness"))facfun(c("Openness", "Neuroticism", "Agreeableness")) facfun(c("Openness", "Openness", "Agreeableness"))
The study conducted by Li and colleagues (2025) included four experimental groups each completing a differently designed FC scale. This dataset shows how the four scales are assembled in each group
FC_blocksFC_blocks
A data frame with 60 rows and 4 columns
Ashton, M. C., & Lee, K. (2009). The HEXACO-60: A short measure of the major dimensions of personality. Journal of Personality Assessment, 91(4), 340-345. doi:10.1080/00223890902935878
Li, M., Zhang, B., Li, L., Sun, T., & Brown, A. (2025). Mixed-keying or desirability-matching in the construction of forced-choice measures? An empirical investigation and practical recommendations. Organizational Research Methods, 28(2), 296-329. doi:10.1177/10944281241229784
Includes the keying and measured factor of the 60 HEXACO-60 items.
FC_item_infoFC_item_info
A data frame with 60 rows and 3 columns
Item number
Keying of each item
Measured factor of each item
Ashton, M. C., & Lee, K. (2009). The HEXACO-60: A short measure of the major dimensions of personality. Journal of Personality Assessment, 91(4), 340-345. doi:10.1080/00223890902935878
Li, M., Zhang, B., Li, L., Sun, T., & Brown, A. (2025). Mixed-keying or desirability-matching in the construction of forced-choice measures? An empirical investigation and practical recommendations. Organizational Research Methods, 28(2), 296-329. doi:10.1177/10944281241229784
Generates the syntax required to fit either a Second-Order Thurstonian Factor Model or a First-Order Thurstonian IRT Model in lavaan.
generate_tirt_lavaan_syntax( n_blocks, block_size, key_matrix, trait_col, key_col, cor_matrix = NULL, model_type = c("TFM", "TIRT"), force_positive_variances = TRUE, more_constraints = "" )generate_tirt_lavaan_syntax( n_blocks, block_size, key_matrix, trait_col, key_col, cor_matrix = NULL, model_type = c("TFM", "TIRT"), force_positive_variances = TRUE, more_constraints = "" )
n_blocks |
Integer. Number of blocks in the questionnaire. |
block_size |
Integer. Number of items per block. |
key_matrix |
A data.frame with two columns, one indicating item trait, another indicating item sign. |
trait_col |
Character string. The name of the column in |
key_col |
Character string. The name of the column in |
cor_matrix |
Optional matrix. Starting values for trait correlations. |
model_type |
Character. Either "TFM" (Second-Order Thurstonian Factor Model) or "IRT" (First-Order). |
force_positive_variances |
Logical. If TRUE, prevents Heywood cases. In "Factor" models, utility variances > 0.0001. In "IRT" models, utility variances and derived correlated errors > 0.0001. Defaults to TRUE. |
more_constraints |
Additional lavaan syntax strings for constraining certain parameters if needed. |
A character string containing the lavaan model syntax.
Generate Mplus Syntax for Thurstonian IRT (TIRT) or Thurstonian Factor Model (TFM)
generate_tirt_mplus_syntax( title = "TIRT Model", data_file, n_blocks, block_size, key_matrix, trait_col, key_col, cor_matrix = NULL, model_type = c("TIRT", "TFM"), tfm_free_pair_residuals = TRUE, estimator = c("WLSMV", "ULSMV", "ULS"), missing_code = "*", force_positive_variances = TRUE, quick_run = FALSE, out_file = "tirt_model.inp" )generate_tirt_mplus_syntax( title = "TIRT Model", data_file, n_blocks, block_size, key_matrix, trait_col, key_col, cor_matrix = NULL, model_type = c("TIRT", "TFM"), tfm_free_pair_residuals = TRUE, estimator = c("WLSMV", "ULSMV", "ULS"), missing_code = "*", force_positive_variances = TRUE, quick_run = FALSE, out_file = "tirt_model.inp" )
title |
Character. Title of the Mplus model. |
data_file |
Character. Name of the input data file. |
n_blocks |
Integer. Number of blocks in the questionnaire. |
block_size |
Integer. Number of items per block. |
key_matrix |
A data.frame with two columns, one indicating item trait, another indicating item sign. |
trait_col |
Character string. The name of the column in |
key_col |
Character string. The name of the column in |
cor_matrix |
Optional matrix. Starting values for trait correlations. |
model_type |
Character. Choose "TIRT" for the First-Order parameterization or "TFM" for the Second-Order parameterization. |
tfm_free_pair_residuals |
Logical. In the TFM model, should the residual variance of the observed pairs be freely estimated? Defaults to TRUE. |
estimator |
Character. The Mplus estimator to use ("WLSMV", "ULSMV", "ULS"). |
missing_code |
Numeric or Character. The value representing missing data. |
force_positive_variances |
Logical. Constrain residual variances to be strictly positive (> 0.001/0) to prevent Heywood cases. Defaults to TRUE. |
quick_run |
Logical. Specify TRUE to speed up estimation by not producing Chi-Square and standard errors. Defaults to FALSE. |
out_file |
Character. The filename to save the generated Mplus syntax. |
Automatically reduces logical dependencies for full rank data (Heister et al. 2025), handles missing/MOLE data natively, and generates Stan syntax capable of within-chain parallelization (reduce_sum).
generate_tirt_stan_syntax( pairwise_data, n_blocks, block_size, n_traits, key_matrix, trait_col, key_col, apply_heister = TRUE )generate_tirt_stan_syntax( pairwise_data, n_blocks, block_size, n_traits, key_matrix, trait_col, key_col, apply_heister = TRUE )
pairwise_data |
Dataframe of binary outcomes. |
n_blocks |
Integer. Number of blocks. |
block_size |
Integer. Number of items per block. |
n_traits |
Integer. Number of traits. |
key_matrix |
A data.frame with two columns, one indicating item trait, another indicating item sign. |
trait_col |
Character string. The name of the column in |
key_col |
Character string. The name of the column in |
apply_heister |
Logical. Apply logical dependency reduction? Defaults to TRUE. |
A list containing syntax (Stan code) and data (List for rstan).
Performs CFA (or accepts an existing fitted model) and extracts the parameter estimates required for generating simulated Thurstonian IRT data.
get_CFA_estimates(fit_model, response_data = NULL, item_names = NULL)get_CFA_estimates(fit_model, response_data = NULL, item_names = NULL)
fit_model |
Either a character string containing |
response_data |
Optional. Data frame containing Likert-type responses.
Required only if |
item_names |
Optional character vector. Names of the items. If |
A list containing loadings, intercepts, residuals,
covariances, and the full model_fit object.
This function prints IIA metrics for select items, given the individual responses for the items.
get_iia(block, data)get_iia(block, data)
block |
An n by k integer matrix, where n is the number of item blocks and k is the number of items per block. |
data |
A p by m numeric matrix with scores of each of the p participants for the m items. |
An n by k matrix indicating the four IIA metrics for each item block.
Mengtong Li
item_responses <- matrix(sample(seq(1:5), 600*60, replace = TRUE), ncol = 60, byrow = TRUE) get_iia(matrix(seq(1:60), ncol = 3, byrow = TRUE), item_responses)item_responses <- matrix(sample(seq(1:5), 600*60, replace = TRUE), ncol = 60, byrow = TRUE) get_iia(matrix(seq(1:60), ncol = 3, byrow = TRUE), item_responses)
Takes the factor analysis results extracted by get_CFA_estimates()
and generates simulated person traits, item parameters, and latent utility values
for a Thurstonian IRT model.
get_simulation_matrices(cfa_estimates, N, empirical = FALSE)get_simulation_matrices(cfa_estimates, N, empirical = FALSE)
cfa_estimates |
A list object returned by |
N |
Integer. Number of simulated responses (participants) to generate. |
empirical |
Logical. As in |
A list containing matrices for Lambda (Loadings), Mu (Intercepts),
Epsilon (Residuals), Theta (Latent Traits), and Utility.
cfa_model <- paste0("H =~ ", paste0("SS", seq(6, 18, by = 6), collapse = " + "), "\n", "E =~ ", paste0("SS", seq(5, 18, by = 6), collapse = " + "), "\n", "X =~ ", paste0("SS", seq(4, 18, by = 6), collapse = " + "), "\n") cfa_fit <- lavaan::cfa(cfa_model, data = HEXACO_example_data[1:500,], ordered = TRUE, estimator = "WLSMV", verbose = TRUE, test = "none", se = "none") cfa_estimates <- get_CFA_estimates(cfa_fit) simu_estimates <- get_simulation_matrices(cfa_estimates, N = 500) ### Because utilities orders are exactly the order each item appears in the cfa model, ### you may need to re-order the columns back in your own case. num_order <- order(as.numeric(gsub("[^0-9]", "", colnames(simu_estimates$Utility)))) new_Utility <- simu_estimates$Utility[, num_order]cfa_model <- paste0("H =~ ", paste0("SS", seq(6, 18, by = 6), collapse = " + "), "\n", "E =~ ", paste0("SS", seq(5, 18, by = 6), collapse = " + "), "\n", "X =~ ", paste0("SS", seq(4, 18, by = 6), collapse = " + "), "\n") cfa_fit <- lavaan::cfa(cfa_model, data = HEXACO_example_data[1:500,], ordered = TRUE, estimator = "WLSMV", verbose = TRUE, test = "none", se = "none") cfa_estimates <- get_CFA_estimates(cfa_fit) simu_estimates <- get_simulation_matrices(cfa_estimates, N = 500) ### Because utilities orders are exactly the order each item appears in the cfa model, ### you may need to re-order the columns back in your own case. num_order <- order(as.numeric(gsub("[^0-9]", "", colnames(simu_estimates$Utility)))) new_Utility <- simu_estimates$Utility[, num_order]
Actual Responses to the HEXACO-60 (Ashton & Lee, 2009) scale. These response data are taken from the study conducted by Li and colleagues (2025).
HEXACO_example_dataHEXACO_example_data
A data frame with 2177 rows and 61 columns:
https://osf.io/yvpz3/?view_only=08601755f471440b80973194571b60bd
Ashton, M. C., & Lee, K. (2009). The HEXACO-60: A short measure of the major dimensions of personality. Journal of Personality Assessment, 91(4), 340-345. doi:10.1080/00223890902935878
Li, M., Zhang, B., Li, L., Sun, T., & Brown, A. (2025). Mixed-keying or desirability-matching in the construction of forced-choice measures? An empirical investigation and practical recommendations. Organizational Research Methods, 28(2), 296-329. doi:10.1177/10944281241229784
Generates a matrix of randomly paired blocks, where each row represents a block of items. This serves as a fast, unstructured initial solution for forced-choice test assembly algorithms.
make_random_block(total_items, target_items = total_items, block_size)make_random_block(total_items, target_items = total_items, block_size)
total_items |
Integer. The total number of items available in the item pool. |
target_items |
Integer. The number of items to sample from |
block_size |
Integer. The number of items in each block (e.g., 2 for pairs,
3 for triplets). Must be |
Given the total number of items in the pool, this function randomly samples
target_items and formats them into a block matrix.
If target_items is not a multiple of block_size, the function will
randomly draw additional items from the sampled pool to fill the incomplete final block,
ensuring the matrix structure is strictly maintained.
A matrix of integers indicating the item IDs. The number of rows equals
ceiling(target_items / block_size), and the number of columns
equals block_size.
# Use all 60 items to build 20 blocks of 3 make_random_block(total_items = 60, block_size = 3) # Sample 45 items from a pool of 60 to build 15 blocks of 3 make_random_block(total_items = 60, target_items = 45, block_size = 3) # Handle cases where target_items is not a multiple of block_size # (Will randomly reuse 1 item to fill the last triplet) make_random_block(total_items = 60, target_items = 50, block_size = 3)# Use all 60 items to build 20 blocks of 3 make_random_block(total_items = 60, block_size = 3) # Sample 45 items from a pool of 60 to build 15 blocks of 3 make_random_block(total_items = 60, target_items = 45, block_size = 3) # Handle cases where target_items is not a multiple of block_size # (Will randomly reuse 1 item to fill the last triplet) make_random_block(total_items = 60, target_items = 50, block_size = 3)
In Li et al's (2025) study, respondents completed one of the four different FC scales. The current dataset contains their actual responses to the MOLE blocks, as well as their group information.
MOLE_dataMOLE_data
A data frame with 2173 rows (respondents, 4 groups) and 40 columns #'
Li, M., Zhang, B., Li, L., Sun, T., & Brown, A. (2025). Mixed-keying or desirability-matching in the construction of forced-choice measures? An empirical investigation and practical recommendations. Organizational Research Methods, 28(2), 296-329. doi:10.1177/10944281241229784
Automatic construction of forced-choice tests based on the Simulated Annealing algorithm. Allows items to be:
Matched in either pairs, triplets, quadruplets or blocks of any size;
Matched based on any number of item-level characteristics (e.g. Social desirability, factor) based on any customized criteria;
Matched based on person-level inter-item agreement (IIA) metrics;
Optimally distributed at the global scale-level to ensure specific trait-pair combinations (e.g., evenly/unevenly distributing equal and/or mixed keyed pairs)
optimize_blocks( blocks = NULL, total_items = NULL, temp_initial = NULL, temp_eta = 0.01, temp_cooling = 0.999, temp_stop_ratio = 1e-06, item_chars, char_weights = NULL, optim_funcs = NULL, n_exchange = 2, prob_new_item = 0.25, use_iia = FALSE, response_matrix = NULL, iia_weights = c(BPlin = 1, BPquad = 1, AClin = 1, ACquad = 1), trait_col = NULL, key_col = NULL, target_dist = NULL, scale_fit_weight = 1, prevent_overlap = FALSE )optimize_blocks( blocks = NULL, total_items = NULL, temp_initial = NULL, temp_eta = 0.01, temp_cooling = 0.999, temp_stop_ratio = 1e-06, item_chars, char_weights = NULL, optim_funcs = NULL, n_exchange = 2, prob_new_item = 0.25, use_iia = FALSE, response_matrix = NULL, iia_weights = c(BPlin = 1, BPquad = 1, AClin = 1, ACquad = 1), trait_col = NULL, key_col = NULL, target_dist = NULL, scale_fit_weight = 1, prevent_overlap = FALSE )
blocks |
An n by k integer matrix, where n is the number of item blocks and k is the number of items per block. Serves as the initial starting blocks for the automatic pairing method. |
total_items |
Integer value. How many items do we sample from
in order to build these |
temp_initial |
Initial temperature value. Can be left as |
temp_eta |
A positive numeric value. The ratio of initial temperature to
initial energy of |
temp_cooling |
A positive numeric value less than 1. Determines the reduction rate of the temperature after each iteration. Default is 0.999. |
temp_stop_ratio |
A positive numeric value less than 1.
Iteration stops when the temperature drops below |
item_chars |
An m by r data frame, where m is the total number of items to sample from, whereas r is the number of item characteristics. |
char_weights |
A vector of length r with weights for each
item characteristic in |
optim_funcs |
A vector of customized function names for optimizing each item characteristic within each block, with length r. |
n_exchange |
Integer value. Determines how many blocks are exchanged
in order to produce a new solution for each iteration.
Should be a value larger than 1 and less than |
prob_new_item |
A value between 0 and 1. Probability of choosing the strategy of picking a new item, when not all candidate items are used to build the FC scale. Default is 0.25. |
use_iia |
Logical. Are IIA metrics used when performing automatic pairing? Default is FALSE. |
response_matrix |
A p by m numeric matrix with scores of each of the
p participants for the m items. Ignored when |
iia_weights |
A vector of length 4 indicating weights given to each IIA metric: Linearly weighted AC (Gwet, 2008; 2014); Quadratic weighted AC; Linearly weighted Brennan-Prediger (BP) Index (Brennan & Prediger, 1981; Gwet, 2014); Quadratic weighted BP. |
trait_col |
Character string. The name of the column in |
key_col |
Character string. The name of the column in |
target_dist |
A data frame detailing the target distribution of trait-pair combinations
for global scale-level fit. Must contain columns |
scale_fit_weight |
Numeric value. The weight applied to the global scale-level fit penalty relative to the local block-level energy. Default is 1.0. Increase this value to prioritize global trait distribution over local block matching. |
prevent_overlap |
Logical. If target_dist is specified, should strict prevention of trait overlap (i.e., block containing items measuring the same trait) be enforced? Default is FALSE. |
A list containing:
block_initial: Initial starting block
energy_initial: Initial energy for block_initial
block_final: Final paired block after optimization by SA
energy_final: Final energy for block_final
The essence of SA is the probablistic acceptance of solutions inferior to
the current state, which avoids getting stuck in local maxima/minima.
It is also recommended to try out different values of
char_weights, iia_weights, temp_eta to find out the best
combination of initial temperature and energy value
in order to provide optimally paired blocks.
Mengtong Li
Brennan, R. L., & Prediger, D. J. (1981). Coefficient kappa: Some uses, misuses, and alternatives. Educational and Psychological Measurement, 41(3), 687-699. https://doi.org/10.1177/001316448104100307
Gwet, K. L. (2008). Computing inter rater reliability and its variance in the presence of high agreement. British Journal of Mathematical and Statistical Psychology, 61(1), 29-48. https://doi.org/10.1348/000711006X126600
Gwet, K. L. (2014). Handbook of inter-rater reliability (4th ed.): The definitive guide to measuring the extent of agreement among raters. Gaithersburg, MD: Advanced Analytics Press.
This function provides a simple, enhanced diagnostic plot examining the performance of the forced-choice scale based on simulated or known true scores.
plot_scores(x_scores, y_scores, type = c("simple", "abs.diff"), ...)plot_scores(x_scores, y_scores, type = c("simple", "abs.diff"), ...)
x_scores |
Numeric vector. Scores to be plotted on the x-axis (typically True Scores). |
y_scores |
Numeric vector. Scores to be plotted on the y-axis (typically Estimated Scores). |
type |
Character. Which type of plot to display? Options are |
... |
Additional graphical parameters passed to |
This function extends base R plot() by automatically adding
standard diagnostic reference lines (a 1:1 identity line for "simple", and a
0-error baseline for "abs.diff") to aid visual inspection of bias.
A base R scatter plot.
true <- rnorm(100) est <- true + rnorm(100, 0, 0.3) plot_scores(true, est, type = "simple", main = "Recovery") plot_scores(true, est, type = "abs.diff", main = "Error Magnitude")true <- rnorm(100) est <- true + rnorm(100, 0, 0.3) plot_scores(true, est, type = "simple", main = "Recovery") plot_scores(true, est, type = "abs.diff", main = "Error Magnitude")
Extracts posterior means of item parameters from a fitted Stan TIRT model and uses analytical BFGS optimization to instantly score new respondents.
predict_tirt_stan(fit, stan_data, new_pairwise_data)predict_tirt_stan(fit, stan_data, new_pairwise_data)
fit |
A fitted model object from either cmdstanr or rstan. |
stan_data |
The original data list passed to Stan (used to map the test design). |
new_pairwise_data |
A dataframe of pairwise binary responses for the NEW respondents. |
A data frame containing MAP trait scores and Standard Errors.
Prepare pairwise list data for Stan
prepare_tirt_stan_data( pairwise_data, n_blocks, block_size, key_matrix, trait_col, key_col, apply_heister = FALSE )prepare_tirt_stan_data( pairwise_data, n_blocks, block_size, key_matrix, trait_col, key_col, apply_heister = FALSE )
pairwise_data |
A data frame of binary pairwise outcomes (output of
|
n_blocks |
Integer. Number of blocks in the questionnaire. |
block_size |
Integer. Number of items per block. |
key_matrix |
A data.frame with two columns, one indicating item trait, another indicating item sign. |
trait_col |
Character string. The name of the column in |
key_col |
Character string. The name of the column in |
apply_heister |
Experimental. Logical. Should the optimization techinques proposed by Heister et al., (2025) be applied? Default is FALSE. |
A diagnostic function to examine measurement accuracy. It calculates the Root Mean Square Error (RMSE) either overall, or within specific intervals on the latent trait continuum.
RMSE_range(true_scores, estimated_scores, range_breaks = NULL)RMSE_range(true_scores, estimated_scores, range_breaks = NULL)
true_scores |
Numeric vector. Actual/known trait scores. |
estimated_scores |
Numeric vector. Estimated trait scores from the model. |
range_breaks |
Numeric vector. Optional. Specifies the cut points for the trait
score bins (e.g., |
If range_breaks is NULL, returns a single numeric RMSE value.
If range_breaks is specified, returns a named numeric vector showing
the RMSE within each score bin.
true <- rnorm(100) est <- true + rnorm(100, 0, 0.3) # Overall RMSE RMSE_range(true, est) # Binned RMSE RMSE_range(true, est, range_breaks = c(-3, -1, 1, 3))true <- rnorm(100) est <- true + rnorm(100, 0, 0.3) # Overall RMSE RMSE_range(true, est) # Binned RMSE RMSE_range(true, est, range_breaks = c(-3, -1, 1, 3))
Calculates traditional ipsative sum scores from pairwise binary outcomes. Natively handles both full rankings and partial rankings (MOLE format) via pro-rated scaling, and automatically reverses scores for negatively-keyed items.
score_tirt_ipsative( pairwise_data, n_blocks, block_size, key_matrix, trait_col, key_col )score_tirt_ipsative( pairwise_data, n_blocks, block_size, key_matrix, trait_col, key_col )
pairwise_data |
A data frame of binary pairwise outcomes (output of
|
n_blocks |
Integer. Number of blocks in the questionnaire. |
block_size |
Integer. Number of items per block. |
key_matrix |
A data.frame with two columns, one indicating item trait, another indicating item sign. |
trait_col |
Character string. The name of the column in |
key_col |
Character string. The name of the column in |
A data frame of classical ipsative scores for each unique trait, with rows representing respondents.
Calculates MAP trait scores and Standard Errors for a fitted lavaan model. Automatically detects whether the model uses the First-Order (TIRT) or Second-Order (TFM) parameterization.
score_tirt_lavaan(fit, data, trait_names = NULL, floor_value = 1e-04)score_tirt_lavaan(fit, data, trait_names = NULL, floor_value = 1e-04)
fit |
A fitted lavaan object. |
data |
The pairwise binary dataset used to fit the model. |
trait_names |
Character vector of the traits to score (e.g., c("Trait1", "Trait2")). Can be left NULL and detected by the function. |
floor_value |
Numeric. If negative residual variances for observed variables are provided, replace those negative residual variances with this floor value. |
A data frame containing Trait scores and their Standard Errors (SE).
Extracts the estimated parameters from an Mplus .out file and uses a fast analytical gradient in R to instantly calculate MAP trait scores and Standard Errors. Works seamlessly for both First-Order (TIRT) and Second-Order (TFM) models.
score_tirt_mplus(inp_file, data, trait_names = NULL, run_model = TRUE)score_tirt_mplus(inp_file, data, trait_names = NULL, run_model = TRUE)
inp_file |
Character. Path to the Mplus .inp (or .out) file. |
data |
The pairwise binary dataset used to fit the model. |
trait_names |
Optional character vector. The original names of the traits to use as column names in the final output. If NULL, uses the Mplus generated names. |
run_model |
Logical. If TRUE, runs Mplus before scoring. If FALSE, assumes the .out file already exists. Defaults to TRUE. |
A data frame containing Trait scores and their Standard Errors (SE).
Run Stan TIRT Model and Extract Formatted Scores
score_tirt_stan( stan_data, chains = 4, parallel_chains = 4, threads_per_chain = 2, iter_warmup = 1000, iter_sampling = 1000, init = 0 )score_tirt_stan( stan_data, chains = 4, parallel_chains = 4, threads_per_chain = 2, iter_warmup = 1000, iter_sampling = 1000, init = 0 )
stan_data |
The data list generated by prepare_tirt_stan_data(). |
chains |
Integer. Number of MCMC chains. Defaults to 4. |
parallel_chains |
Integer. How many chains to run simultaneously. Defaults to 4. |
threads_per_chain |
Integer. CPU threads allocated INSIDE each chain. Defaults to 2. |
iter_warmup |
Integer. Warmup iterations. Defaults to 1000. |
iter_sampling |
Integer. Sampling iterations. Defaults to 1000. |
init |
Same as the |
A list containing scores (Data frame of traits) and fit (The cmdstanr fit object).
Analyzes a constructed forced-choice block design and tallies the number of equally-keyed and mixed-keyed pairs for every trait combination.
summarize_trait_pairs( blocks, item_chars, trait_col, key_col, block_size = NULL, output_format = c("wide", "long") )summarize_trait_pairs( blocks, item_chars, trait_col, key_col, block_size = NULL, output_format = c("wide", "long") )
blocks |
Either an n-by-k matrix of item IDs (where rows are blocks), or a flat vector of ordered item IDs. |
item_chars |
Data frame containing the item characteristics. |
trait_col |
Character string. Name of the column in |
key_col |
Character string. Name of the column in |
block_size |
Integer. Required only if |
output_format |
Character. Either "wide" (default, cross-tabulated summary)
or "long" (matches the exact data frame format of |
A data frame of the tallied pairs.