Bayesian Q methodology: probabilistic factor analysis for the study of subjective opinions.
bayesqm is the first fully Bayesian framework for
Q-methodology factor analysis. Since Stephenson (1935), Q analysis has
relied on frequentist factor extraction with the standard error rule of
Brown (1980), which returns participant-to-factor assignments as binary
flagged / unflagged decisions and offers no measure of uncertainty about
them. bayesqm replaces that with posterior credible
intervals for every loading, probabilistic factor membership
P(dominant_i = k | Y) that makes cross-loading visible and quantifiable,
and a principled K-selection protocol built on element-wise PSIS-LOO.
The model uses a Student-t likelihood for robustness to idiosyncratic
Q-sorts and resolves rotational, sign, and label-permutation ambiguity
through the MatchAlign post-processing of Poworoznek et al. (2025).
Posterior sampling is via Stan (cmdstanr or rstan).
For each Q-sort dataset, bayesqm returns four things
classical Q analysis cannot:
P(D_j > delta | Y) and
P(D_j < delta | Y) from the posterior of an explicit
viewpoint-divergence measure rather than a classical significance
verdict.# install.packages("remotes")
remotes::install_github("rdazadda/bayesqm")bayesqm fits models with Stan and needs a working
backend: cmdstanr (recommended) or rstan.
cmdstanr is not on CRAN and is installed from the Stan
R-universe. CmdStan itself must then be installed separately:
install.packages("cmdstanr", repos = c("https://stan-dev.r-universe.dev", getOption("repos")))
cmdstanr::check_cmdstan_toolchain(fix = TRUE)
cmdstanr::install_cmdstan()
cmdstanr::cmdstan_version() # verifyOn Windows you also need Rtools; on macOS, the Xcode command-line
tools (xcode-select --install). See the Getting Started
vignette for the full setup walkthrough and the rstan
alternative.
library(bayesqm)
qdata <- read_qsort("mystudy.csv")
fit <- fit_bayesian(qdata, K = 3)
fitread_qsort() auto-detects CSV, Excel (including HTMLQ,
FlashQ, and tablet-export variants), PQMethod .DAT, Ken-Q
JSON or multi-sheet Excel, KADE ZIP, and Easy-HTMLQ Firebase JSON.
fit_bayesian() returns an object of class
bayesqm_fit. Standard R accessors work as expected:
coef(), fitted(), residuals(),
sigma(), nobs(), family(),
as.matrix(), summary(), plot(),
plus posterior_interval() and prior_summary()
from the rstantools API.
run <- run_bayes(qdata, K_max = 5)
plot_elpd(run)run_bayes() fits the model for K = 1..K_max and reports
two summaries:
The $case field labels their relationship as
agree, gap, or reversed. The ELPD
peak is always the adopted K; the case label indicates how confidently
the data discriminate between adjacent models, and a gap between the two
is itself diagnostic information.
Rather than dichotomizing each loading into flagged or unflagged,
bayesqm reports the posterior probability that factor k is
participant i’s dominant factor, P(dominant_i = k | Y).
compute_dominant_sign() adds the posterior probability that
the dominant loading is positive, so negative exemplars (a viewpoint
held by opposition) stay distinct. classify_membership()
turns the probabilities into a descriptive Strong / Moderate / Weak
tier:
compute_dominant_prob(fit$Lambda_draws)
compute_dominant_sign(fit$Lambda_draws)
classify_membership(fit$Lambda_draws)
plot_membership(fit)Distinguishing and consensus statements come from the posterior of an
explicit viewpoint-divergence measure D_j, with the
probabilities P(D_j > delta | Y) and
P(D_j < delta | Y) it implies. By default
delta is the reliability-adjusted critical difference from
classical Q analysis (Brown, 1980; Zabala & Pascual, 2016), computed
from the posterior dominant-factor counts
(critical_delta()); suggest_delta() (one
forced-distribution category) is an alternative:
d <- critical_delta(fit$Lambda_draws) # default separation (computed)
compute_divergence(fit$F_draws, delta = d)
plot_dist_cons(fit) # uses the fit's stored divergence summaryThe package provides nine base-R plots and matching
ggplot2::autoplot() methods (registered when
ggplot2 and ggdist are installed). All read
their palette through bayesqm_colors() and restore
par() on exit:
plot(fit) # cross-panel z-score dotchart
plot_loading_posterior(fit) # loadings with 50% / 95% intervals
plot_membership(fit) # dominant-factor probability heatmap
plot_dist_cons(fit) # distinguishing/consensus divergence forest
plot_ppc(fit) # posterior predictive check on by-person correlations
plot_tucker(fit) # MatchAlign alignment quality
plot_hyper(fit) # hyperparameter posteriors
plot_elpd(run) # ELPD across K with peak / Sivula annotationsFor figure export, save_bayesqm_plot() opens the
appropriate device from the file extension and
caption_bayesqm() returns a figure caption that reports K,
N, J, chains, coverage probability, and convergence diagnostics.
qmethodbayesqm_fit deliberately mirrors the slot names from the
qmethod package (Zabala, 2015): $dataset,
$loa, $zsc, $zsc_n,
$f_char, $qdc, $flagged.
$qdc is the Bayesian divergence table: per-viewpoint grid
position and z-score with 95% CrI, then D_j with 95% CrI,
pi_D, and pi_C, not the classical
significance-label vocabulary. Intentional Bayesian divergences are
documented in ?bayesqm-package:
$flagged is defined probabilistically as P(argmax_k
|λ_ik| = k) > 0.5, replacing Brown’s (1980) significance-based
rule.$f_char$characteristics omits the classical test-theory
columns; factor-score uncertainty is already in the posterior credible
intervals on $ci_lower and $ci_upper.When you have substantive factor labels,
rename_factors(fit, c("a", "b", "c")) relabels every
factor-indexed slot in one call.
posterior and bayesplotas.matrix(fit), as.array(fit), and
as.data.frame(fit) return draws in Stan-style parameter
naming (Lambda[i,k], F[j,k], nu,
sigma, tau). as_draws_df(),
as_draws_matrix(), and as_draws_array() are
registered for posterior::as_draws_* when
posterior is installed, so bayesplot and
tidybayes consume bayesqm_fit objects
natively.
Run citation("bayesqm") for the canonical citation, or
use:
Azadda, R. D., AK-ACE Team, Hueffer, K., Peter, T., & Rasmus, S. (2026). bayesqm: Bayesian Q-Methodology Factor Analysis. R package version 0.1.0. https://github.com/rdazadda/bayesqm
BibTeX is available via
toBibtex(citation("bayesqm")).
vignette("bayesqm-intro") walks the full workflow end
to end on a synthetic Q-sort.?fit_bayesian documents every prior and sampler
option.?run_bayes covers the peak-plus-Sivula thresholds and
the three case labels.?bayesqm-membership covers the probabilistic membership
and distinguishing-statement summaries.This research was, in part, funded by the National Institutes of Health (NIH) Agreement OT2HL158287 (Stacy Rasmus, Contact PI, smrasmus@alaska.edu; Karsten Hueffer and Taa’aii Peter, MPIs). The views and conclusions contained in this document are those of the authors and should not be interpreted as representing the official policies, either expressed or implied, of the NIH.
Released under the GNU General Public License version 3 (GPL-3); see https://www.gnu.org/licenses/gpl-3.0.html for the full text.