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() # verify
On 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)
fit
read_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 summary
The 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 annotations
For 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.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.