The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Getting Started with ShortForm

Overview

ShortForm is an R package for constructing short-form assessments from larger item banks using reproducible, optimization-based workflows.

It provides implementations of three metaheuristic search algorithms to automate item selection while preserving prespecified psychometric properties (such as model fit):

All three search over candidate short forms of a lavaan model, evaluating each candidate’s fit and keeping the best one found. This vignette gives a quick tour of all three; see the dedicated vignettes (vignette("antColony"), vignette("simulatedAnnealing"), vignette("tabuSearch")) for a deeper look at each one.

When to use ShortForm

A shared workflow

All three algorithms follow the same general shape:

  1. Specify the full model – lavaan syntax for the item bank you’re reducing, with every candidate item already listed under its factor.
  2. Choose how many items to keep per factor, and (for simulatedAnnealing()/tabuSearch()) which fit measure or custom function to optimize.
  3. Run the algorithm – it searches candidate short forms and keeps track of the best one found.
  4. Inspect the result – every algorithm returns an S4 object with show()/summary()/plot() methods.
library(ShortForm)
#> Package 'ShortForm' version 1.0.0

Quick example: Ant Colony Optimization

set.seed(58310)

result_ACO <- antColony(
  data = lavaan::HolzingerSwineford1939,
  ants = 2, evaporation = 0.7,
  initialModel = " visual  =~ x1 + x2 + x3
                   textual =~ x4 + x5 + x6
                   speed   =~ x7 + x8 + x9 ",
  itemsPerFactor = c(3, 3, 3),
  steps = 2, fit.indices = c("cfi"), fit.statistics.test = "(cfi > 0.6)",
  maxIterations = 2, parallel = FALSE, verbose = FALSE
)

result_ACO
#> Algorithm: Ant Colony Optimization
#> Total Run Time: 0.181 secs
#> 
#> Function call:
#> antColony(data = lavaan::HolzingerSwineford1939, ants = 2, evaporation = 0.7,
#>   initialModel = " visual =~ x1 + x2 + x3\n textual =~ x4 + x5 + x6\n speed
#>   =~ x7 + x8 + x9 ", itemsPerFactor = c(3, 3, 3), steps = 2, fit.indices =
#>   c("cfi"), fit.statistics.test = "(cfi > 0.6)", maxIterations = 2, parallel =
#>   FALSE, verbose = FALSE, sample.cov = NULL, sample.nobs = NULL, items = NULL,
#>   bifactor = NULL, lavaan.model.specs = list(model.type = "cfa", estimator
#>   = "default", ordered = NULL, int.ov.free = TRUE, int.lv.free = FALSE,
#>   auto.fix.first = TRUE, auto.fix.single = TRUE, auto.var = TRUE, auto.cov.lv.x
#>   = TRUE, auto.th = TRUE, auto.delta = TRUE, auto.cov.y = TRUE, std.lv = FALSE,
#>   group = NULL, group.label = NULL, group.equal = "loadings", group.partial =
#>   NULL, group.w.free = FALSE), pheromone.calculation = "gamma")
#> 
#> Final Model Syntax:
#> visual =~ x1 + x2 + x3
#> textual =~ x4 + x5 + x6
#> speed =~ x7 + x8 + x9
#> 
#> Fit Indices: cfi
#> Fit Test: (cfi > 0.6)
#> Final Model Values: cfi = 0.931

Quick example: Simulated Annealing

set.seed(58310)

result_SA <- suppressWarnings(simulatedAnnealing(
  initialModel = " visual  =~ x1 + x2 + x3
                   textual =~ x4 + x5 + x6
                   speed   =~ x7 + x8 + x9 ",
  originalData = lavaan::HolzingerSwineford1939,
  maxIterations = 3,
  criterion = "cfi", negateCriterion = TRUE,
  itemsPerFactor = c(2, 2, 2),
  items = paste0("x", 1:9)
))
#> Initializing short form creation.
#> The initial short form is:
#> visual =~ x2 + x1
#> textual =~ x6 + x4
#> speed =~ x8 + x7
#> 
#> Using the short form randomNeighbor function.
#> Finished initializing short form options.
#>  Current Progress: 
#> Old Fit: 0.97 New Fit: 0.985                                                                     Current Step = 2 of a maximum 3.   Current Step = 3 of a maximum 3.

result_SA
#> Algorithm: Simulated Annealing
#> Total Run Time: 0.061 secs using 1 chains. 
#> 
#> Function call:
#> simulatedAnnealing(initialModel = " visual =~ x1 + x2 + x3\n textual =~ x4 + x5
#>   + x6\n speed =~ x7 + x8 + x9 ", originalData = lavaan::HolzingerSwineford1939,
#>   maxIterations = 3, criterion = "cfi", negateCriterion = TRUE, itemsPerFactor
#>   = c(2, 2, 2), items = paste0("x", 1:9), temperature = "linear", Kirkpatrick
#>   = TRUE, randomNeighbor = TRUE, lavaan.model.specs = list(model.type = "cfa",
#>   auto.var = TRUE, estimator = "default", ordered = NULL, int.ov.free = TRUE,
#>   int.lv.free = FALSE, std.lv = TRUE, auto.fix.first = FALSE, auto.fix.single
#>   = TRUE, auto.cov.lv.x = TRUE, auto.th = TRUE, auto.delta = TRUE, auto.cov.y =
#>   TRUE), maxChanges = 5, restartCriteria = "consecutive", maximumConsecutive =
#>   25, bifactor = NULL, setChains = 1, shortForm = T)
#> 
#> Final Model Syntax:
#> visual =~ x3 + x1
#> textual =~ x5 + x4
#> speed =~ x8 + x9
#> 
#> 
#> 
#> Criterion: "cfi" (maximized)
#> Final Model Value: 0.985

Which algorithm should I use?

There’s no universally “best” choice – all three are heuristic searches, so it’s reasonable to try more than one and compare results. A few practical differences:

Outputs

Every algorithm returns an S4 object with:

plot(result_TS)

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.