Skip to content

ackwards

0.3.0Package record

Bass-Ackwards Hierarchical Structural Analysis

0packages depend
1.2Kdownloads / year
99.8%test coverage
12/13flavors OK

Overview

About

Implements Goldberg's (2006) doi:10.1016/j.jrp.2006.01.001 bass-ackwards method and modern descendants for hierarchical structural analysis. Extracts solutions from 1 to k factors using principal component analysis (PCA), exploratory factor analysis (EFA), or exploratory structural equation modeling (ESEM) engines. It then describes the hierarchy through the correlations between the factor scores of different levels. It computes them with linear algebra from the scoring weights and the correlation matrix the fit uses, so they are exact for that matrix. Waller (2007) doi:10.1016/j.jrp.2006.08.005 showed, for principal components, that these correlations follow from the correlation matrix alone. Includes the Forbes (2023) doi:10.1037/met0000546 extension for redundancy pruning and all-levels cross-correlations.

Changes in 0.3.0

Oblique rotation as an option, with partialled edge coefficients and

within-level factor correlations in tidy(). Also fixes to the ESEM engine,

and documentation rewritten in plain English.

Breaking changes

  • **ESEM with cor = "spearman" and missing = "fiml" now stops with an error.** Before this fix, the fit ran, but x$r held lavaan's Pearson FIML matrix while the object reported cor = "spearman". All edges were Pearson under a Spearman label. FIML estimates a multivariate-normal (Pearson) matrix on every engine, so it now requires cor = "pearson" for ESEM, as it already did for PCA and EFA. To keep a Spearman basis, use missing = "pairwise" or "listwise". The warning for ESEM with cor = "spearman" now says that lavaan fits Pearson covariances of the raw data. Before, it said "Pearson-ML model", which was wrong under WLSMV and ULSMV.
Show all changes in 0.3.0Hide the rest of the changes in 0.3.0

New features

  • **Oblique rotation as an option.** ackwards() gains a rotation argument. The default stays "varimax", and default numerical output does not change (PCA and EFA factor_cor gain row and column names, see below). With engine = "pca" or "efa", it also accepts "oblimin" and "promax". The GPArotation package (now in Suggests) is needed for "oblimin" with these engines, and for "promax" with engine = "efa". With engine = "esem", it also accepts "oblimin" and "geomin". Under an oblique rotation, each level stores the engine's factor correlation. The scores keep that correlation: EFA and ESEM use the oblique form of the ten Berge weights, and PCA uses the oblique components. The variance values follow psych's convention for correlated factors. Each edge r is then a total correlation. Primary parents, sign alignment, and the diagram still use r, and beta in tidy() gives the partialled coefficient. The fit prints a message that says so, and print(), summary(), and autoplot() label it. prune() warns that its thresholds were set for varimax. boot_edges() refits each replicate with the object's rotation. If that rotation needs GPArotation, it checks for the package first. A PCA level whose oblique rotation fails, or raises an error, ends the hierarchy at the level before it, because PCA rotates once. EFA rotates from several random starts, so its non-convergence warnings are shown and the level is kept. ESEM counts its failed starts, as the first entry under Bug fixes describes. An EFA level whose final rotation step failed still ends the hierarchy. If the installed psych rotates from one start, any EFA rotation warning ends it too. Set seed to reproduce an oblique EFA fit or any ESEM fit exactly. A new test checks the oblique results against Forbes's reference implementation on her three simulation studies.
  • **Within-level factor correlations are carried and shown.** Every engine now stores the factor correlation it reports for each level. That matrix is permuted and sign-flipped in step with the loadings, in place of a fixed identity matrix. The new table tidy(x, what = "factor_cor") has one row per pair of factors within a level. Its columns are level, factor_a, factor_b, and cor. The summary() output prints a "Within-level factor correlations" block only when some pair is correlated, which means a within-level correlation above 1e-8 in size. Under the default varimax rotation every within-level correlation is 0, so no number changes and the block is absent.
  • **Partialled edge columns.** The edge table from tidy() gains beta beside r, and the variance table gains r2. The beta column is the standardized regression weight of the child factor on all factors of the parent level together. It removes the part of r that the other factors at the parent level share. The r2 column is the share of a factor's score variance that all factors of the level just above account for together. It is NA at level 1. Under varimax beta equals r, and r2 equals the sum of that factor's squared r values from the level above. The two pairs of quantities come apart only when the factors within a level are correlated. If a level's within-level score correlation cannot be inverted, both are NA, with a warning.
  • tidy(what = "edges") now warns when the within-level score correlation of a level is nearly singular, meaning its smallest eigenvalue is below 1e-2. The factors at that level are then close to collinear, so the partialled beta for edges from that level is unstable. beta is still reported, and the warning names the level. The package chose this cutoff as a numerical guard. It is not a published rule. tidy(what = "variance") and summary() do not raise this warning.
  • PCA and EFA factor_cor matrices now carry the level's factor labels as row and column names, as the ESEM ones already did.

Bug fixes

  • **ESEM now counts failed rotation starts.** At each rotated level, lavaan keeps the best of 30 random starts (its default). In 0.2.0, ESEM showed no rotation warning, and it kept the level even when no start converged. Now a warning gives the count of starts that did not converge (for example, "3 of 30"), and the level is kept. If no start converged, the kept rotation did not converge either. The hierarchy then ends at the level before it with a warning. This holds under every ESEM rotation, varimax included. Fits where at least one start converges keep the same levels and values as before.
  • **ESEM with ULSMV on continuous items now fits under the default missing = "pairwise".** Before this fix, ackwards() stopped at level 1 with a lavaan error, complete data included. lavaan 0.7.2 efa() fails for ULSMV on continuous items when it uses all available cases. That setting now fits the complete rows, as ML and MLR do, and the edges still come from the pairwise correlation matrix. ULSMV on cor = "polychoric" and WLSMV are unchanged. The help for missing in ackwards() now says which estimators use all available cases. It no longer calls WLSMV and ULSMV ordinal-only, and it names the $meta fields that record the setting. The estimator table in the engines article no longer calls them ordinal-only either.

Documentation

  • **Which matrix an edge is exact for.** Each between-level edge is implied by the correlation matrix the fit uses, and it is exact for that matrix. The package description, the README, the help for ackwards(), tidy(), and prune(), and five articles now say so. The five articles are the introduction, engines, ordinal, recommended-workflow, and Forbes articles. Scores computed from the observed items do not in general reproduce the edges when that matrix is polychoric or Spearman. The same holds when the matrix comes from incomplete data, by pairwise deletion or by full-information maximum likelihood (FIML). The ackwards() help names the two ESEM settings in which lavaan fits a matrix other than the stored x$r. With cor = "spearman" and missing = "pairwise" or "listwise", lavaan fits Pearson covariances of the raw data. With cor = "pearson" and missing = "pairwise" on data with missing values, ML and MLR fit the complete rows, and WLSMV fits the pairwise covariances. The package description no longer says that edges can come from materialized scores, because no exported function computes them that way. It credits Waller (2007) with the closed-form result for principal components only. No code or result changed.
  • **Where the redundancy chase stops, in the prune() help.** Under the default redundancy_criterion = "direct", the chase stops at the first ancestor level that fails the criterion. A level further up that passes again does not restart it. The help now says so, and says that this stop follows Forbes's (2023) reference code, ChaseCorrPaths. To bridge such a gap by your own judgment, pass the factor to drop to manual. A factor that no rule flagged then gets prune_reason = "manual", and manual adds no row to x$prune$chains. A new test shows this on component m7f2 of the forbes2023 example. The behavior and results of prune() did not change.
  • **Varimax and oblimin side by side in the engines article.** The section on orthogonal and oblique rotation now fits EFA to the BFI-25 twice, with varimax and with oblimin, in place of the simulated example. It shows the oblimin factor correlations within each level. It matches each oblimin factor to a varimax factor by Tucker's congruence of loadings. After the match, the two primary-parent trees are the same. It lists the secondary edges that reach the display cutoff under oblimin, with r beside beta, and draws both diagrams. No package function or its output changed.
  • **Plain-English documentation.** The README, the package description, the help pages, and all nine articles were revised so that a reader outside the field can follow them on one read. Sentences are shorter, and each statistical term is explained in plain words where it is first used. No function, argument, default, or example changed. The code in the articles and the results they show did not change either. The settings that reproduce Forbes (2023), the Pearson default with its ordinal-data warning, and the descriptive reading of the hierarchy are stated as before.

From NEWS.md in the release

Install

Health

CRAN checks
1ERROR12OK
Check messages
ERRORtestsr-oldrel-windows-x86_64
Running 'testthat.R' [264s]
Running the tests in 'tests/testthat.R' failed.
Complete output:
  > library(testthat)
  > library(ackwards)
  > 
  > test_check("ackwards")
  Starting 2 test processes.
  > test-suggest_k.R: i Running parallel analysis (5 iterations, PC + FA)...
  > test-suggest_k.R: i PA-PC suggested 6 components -- above the evaluated ceiling (`k_max` = 3); reporting k <= 3.
  > test-suggest_k.R: i Increase `k_max` to evaluate the full suggestion.
  > test-suggest_k.R: i PA-FA suggested 6 factors -- above the evaluated ceiling (`k_max` = 3); reporting k <= 3.
  > test-suggest_k.R: i Increase `k_max` to evaluate the full suggestion.
  > test-suggest_k.R: v Running parallel analysis (5 iterations, PC + FA)... [506ms]
  > test-suggest_k.R: 
  > test-suggest_k.R: i Running MAP and VSS...
  > test-suggest_k.R: CD: 364 rows with missing values removed (2436 complete cases used).
  > test-suggest_k.R: v Running MAP and VSS... [271ms]
  > test-suggest_k.R: 
  > test-suggest_k.R: 
...[truncated]...
de -1073741819
  Backtrace:
       ▆
    1. └─testthat::test_check("ackwards")
    2.   └─testthat::test_dir(...)
    3.     └─testthat:::test_files(...)
    4.       └─testthat:::test_files_parallel(...)
    5.         ├─withr::with_dir(...)
    6.         │ └─base::force(code)
    7.         ├─testthat::with_reporter(...)
    8.         │ └─base::tryCatch(...)
    9.         │   └─base (local) tryCatchList(expr, classes, parentenv, handlers)
   10.         │     └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
   11.         │       └─base (local) doTryCatch(return(expr), name, parentenv, handler)
   12.         └─testthat:::parallel_event_loop_chunky(queue, reporters, ".")
   13.           └─queue$poll(Inf)
   14.             └─base::lapply(...)
   15.               └─testthat (local) FUN(X[[i]], ...)
   16.                 └─private$handle_error(msg, i)
   17.                   └─cli::cli_abort(...)
   18.                     └─rlang::abort(...)
  Execution halted

CRAN check page ↗

Slowest check: 15.7 min · r-patched-linux-x86_64
Status changes
  • ERROR12 OK, 1 ERROR2026-10-21
  • OK13 OK2026-10-17
  • ERROR12 OK, 1 ERROR2026-09-24
  • OK13 OK2026-09-09
  • ERROR12 OK, 1 ERROR2026-09-06
Show 5 earlier changesHide 5 earlier changes
  • OK13 OK2026-08-05
  • ERROR11 OK, 1 NOTE, 1 ERROR2026-08-04
  • NOTE12 OK, 1 NOTE2026-08-01
  • OK11 OK2026-07-28
  • ERROR5 OK, 1 ERROR2026-07-25
Advisories

none known

Documentation

In release 0.3.0
READMEYes · 1,072 wordsVignettesYes · 9 · dynamicNEWSYes · 100% structured · mentions 0.3.0Citation fileYes · software entry onlyWebsiteYes · pkgdown
Help pages
33 (2 internal)
Examples that run
74%17 run in a plain check · 6 only when donttest examples are enabled (R CMD check --as-cran)
Exported functions with examples
86%
Documented parameters
100%
Return-value docs
93%
Help pages citing references
21%

Downloads

1.2K
CRAN downloads in the past year
Rank #7,271 · ~3/day · ~100/mo
Daily download trend is not available in this view yet.
46230 days
1.2K90 days
1.2K1 year
Compare downloads with other packages →
Also on27 r2u21 autocran

Repository

8Stars
0Forks
0Open issues
0Open PRs
2Releases
476Commits
1Commit identities
476 commits · Last activity 2026-10-20 · 0% stars, 30d
Responsiveness
How these are sampled
PRs merged (all time)
100%
Median days to close, 50 most recently updated closed issues
2
Median days to close, 50 most recently updated closed PRs
0

Stars over time

2026-07-25 · 72026-09-01 · 8
PracticesBeta

10 development-tooling and community-health practices detected across 6 families in the upstream repository

Checks run against github.com/jmgirard/ackwards on 2026-10-18.

Continuous integration (2)
GitHub ActionsR CMD check workflow

The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel

Docs source (4)
README.Rmdpkgdown

GitHub Pages site last deployed 2026-10-06 · The repository has a site deploy workflow

pkgdown site last built 2026-10-06, before release 0.3.0 (2026-10-19)

Coverage (1)
Coverage reporting
Reproducibility and dev environment (1)
data-raw/
Show all practicesHide 2 practices
CRAN release process (1)
cran-comments.md
Lint, format, editor (1)
lintr
How this is detected·Detection ruleset v3 (2026-09-26)
AI coding toolsBeta

This repository shows signs of AI coding tools.

  • Claude Codefirst seen 2026-06-25
    Seen in the repository: CLAUDE.md
    Named in .Rbuildignore: .claude, CLAUDE.md
    Pull request it wrote: opened by a project member
    Commits: at least 319 crediting it in the message
Models named in its commits
  • Claude Sonnet 4.684 commits · Jun 2026
  • Claude Opus 4.816 commits · Jun 2026

Some of these counts are a minimum, because the search stopped before the oldest commits.

Counts are commits, not lines of code. Commits by a tool's accounts count once across all of its accounts. Commits crediting it are the largest count among its credit lines, a minimum.How this is detected·Detection ruleset v4 (updated 2026-09-27)

Dependencies

Declared dependencies
15 external dependencies (excludes base and recommended)

Installing ackwards adds 6 packages (3 with compiled code, 4.2 MB of source tarballs). Heaviest: GPArotation 1.6 MB, psych 1.1 MB, rlang 766 KB.

Depends (1)
R >= 4.1.0
Imports (6)
LinkingTo (0)
none
Enhances (0)
none
Reverse dependencies
0direct
0indirect

Nothing depends on this yet.

Datasets

People & History

People (2)
Author, Maintainer since 0.1.1 (2026)
Copyright holder
Author of the bundled AMH correlation matrix (data/forbes2023.rda), CC-BY 4.0
Releases (3)

Pick two releases to compare their code metrics.

Package metadata

First published
2026-07-24
Total releases
3 / 1 yrs
License
MIT + file LICENSE OSI
Minimum R
≥ 4.1.0
Bundled data
233 KB / 3 files
Download size
3.4 MB

Cite

Cite this package

Run in R for the authors' entry:

citation("ackwards")

The package provides a citation file, so citation() returns the authors' own entry.

Cite the R Observatory

For a number measured here: a download total, a coverage figure, an archival date.

APA

Balamuta, J. J. (2026). R Observatory: Metrics for ackwards version 0.3.0 [Data set]. HJJB, LLC. Data release v2026-10-22. https://doi.org/10.5281/zenodo.21843040

From data release v2026-10-22, which the citation names so these numbers can be found later. More on citing and the projects behind them.

Report a problem with this page →

Privacy choices

These apply to this browser and are stored on this device only. Nothing about your choice is sent to us.

Read the privacy policy