surveyframe 0.4.0 is on CRAN
surveyframe, CRAN, R package, MCDA, AHP, TOPSIS, text analysis, topic modelling, provenance, small samples
TL;DR: surveyframe 0.4.0 reached CRAN on 20 August 2026. Install it with install.packages("surveyframe"). This is the largest release so far. It adds multi-criteria decision analysis with 10 methods, a 9-method text-analysis family for open-ended responses, a track of small-sample corrections, and a disclosed-amendment and Git-linked provenance trail for .sframe files. It also carries 2 breaking changes, described at the end. Free and open source under the MIT licence.
Decision analysis, collected inside the instrument
Multi-criteria decision analysis usually sits in a gap. Computation packages assume a clean decision matrix already exists. Survey software has no concept of a decision method at all. Getting from one to the other is manual work, and it is where mistakes enter.
0.4.0 closes that gap. It ships 10 methods, each carrying a verified literature citation:
- The Analytic Hierarchy Process (AHP) and the Analytic Network Process (ANP)
- The Decision Making Trial and Evaluation Laboratory method (DEMATEL)
- VIKOR, MOORA, SMART, WASPAS, PROMETHEE, ELECTRE, and TOPSIS
Two new item types collect the judgement data inside the survey itself. pairwise_comparison uses Saaty’s 1-to-9 ratio scale for AHP and ANP, or a 0-to-4 directed influence scale for DEMATEL. criteria_weight is a constant-sum allocation across criteria. Both render identically in the static HTML survey, the Shiny module, and the builder preview.
An aggregation layer turns per-respondent answers into the matrices the methods consume. It validates that every pair was answered, and it combines respondents with the geometric mean for AHP and ANP, which preserves reciprocity, or the arithmetic mean for DEMATEL. AHP judgements are screened for consistency against Saaty’s random-index table, and the consistency-ratio distribution is reported whether or not a study declared a filtering threshold in advance.
Every ranking method resolves its inputs in the same order, and records where each number came from, so a results table can state the provenance of what it shows. sensitivity_analysis() reports how far a ranking moves under a declared perturbation of the weights, and carries a degenerate flag so a ranking that never separated its alternatives cannot report false stability.
Each method’s implementation had to agree with an independent computation of the same method on the same matrix before it was accepted. That practice caught a real defect during development, a WASPAS runner that had inherited SMART’s normalisation step by mistake.
Text analysis for open-ended responses
Open-ended answers are usually the part of a survey that never gets analysed. 0.4.0 adds a 9-method family for them, running through the same analysis-plan and reporting pipeline as every other method:
term_freqandngram_freq, top terms and top bigrams or trigrams, as a bar chart or word cloudterm_context, a keyword-in-context concordance showing what came before and after a chosen wordco_occurrenceandco_occurrence_network, pairwise co-occurrence as a heatmap, and a clustered force-directed networktidy_sentiment, positive and negative counts and the proportion positivequanteda_dfm, a document-feature matrix summarytopic_model_ldaandstm_topics, Latent Dirichlet Allocation and structural topic modelling
A shared cleaning step, clean_text_responses(), and a 174-word English stopword list are both exported, so a study can reuse or override them outside a runner. The heavier methods keep their dependencies optional.
The vignette is explicit about what the family does not attempt. Stemming, lemmatisation, tf-idf, and keyness comparison are not implemented.
Corrections for small samples
Applied research usually runs on fewer respondents than the textbook assumes, and the ordinary versions of these tests can flip significance on repeated draws from data whose true difference never changed. This release adds:
- The Hodges-Lehmann shift estimator, for the two-group Mann-Whitney comparison
- The paired Wilcoxon pseudomedian confidence interval, for the paired t-test
- The exact odds-ratio confidence interval on Fisher’s test, which avoids the continuity correction a Wald interval needs when a cell is zero
- Firth’s bias-reduced logistic regression, for regression prone to separation at small n
An advisory on assumption_report() and sample_size_plan() flags when a study’s sample size falls in the range where these are worth considering.
Disclosed amendments and a provenance trail
A SHA-256 hash proves a .sframe file has not changed since it was written. It cannot tell a legitimate revision from an undisclosed edit, because both break the hash identically.
0.4.0 adds a disclosed-revision path alongside the hash check, without weakening it. amend_sframe() compares an instrument before and after a change and appends a timestamped entry to an ordered log. It never overwrites. Each entry records a reason from a controlled vocabulary, a free-text explanation, and which top-level fields changed.
Amendments come in 2 tiers. Pipeline amendments, such as a data correction or bot-response removal, need only a reason. Design amendments, meaning anything touching the analysis plan or a model, require a deviation report describing what changed in the research question, method, or model, and why. The sign-off field is never blank. It records a reviewer’s name or the literal none, so an unreviewed design change stays visible to anyone auditing the file.
link_git_commit() records the current Git commit SHA and subject line alongside an instrument, tying the file hash to a commit that already has an explanation attached. Git is optional.
An edit made directly to a .sframe file, bypassing amend_sframe(), still fails the integrity check on read exactly as it did before.
The .sframe format is now documented as a standalone JSON Schema, so a reviewer or a second tool can validate a file without installing the package.
What breaks
Two changes need attention before you upgrade.
validate_sframe() and validate_model() now return an sframe_validation diagnostic object, visibly, instead of returning 2 different things depending on strict. Reading $valid and $problems still works, so the common pattern needs no migration. What breaks is code using the return value as an instrument, as in instrument <- validate_sframe(instrument). Wrap it in as_sframe(). Passing a validation result where an instrument is expected now raises an error that names the fix.
The Shiny collector now emits expansion columns for matrix items instead of pipe-joining a matrix item’s cells into a single column.
Both changes came out of a code review by a Journal of Statistical Software editor. The same review prompted accessor methods for every class, so user code no longer has to reach into the underlying list. The registered S3 method count went from 41 to 103.
Get it
- Install:
install.packages("surveyframe") - Documentation: mohammedalisharafuddin.github.io/surveyframe
- Source: github.com/MohammedAliSharafuddin/surveyframe
- On CRAN: CRAN.R-project.org/package=surveyframe
The follow-up release, 0.4.1, adds a 22-demo teaching library and labelled SPSS and Stata export.