The analysis in surveyframe is driven by the plan stored in the
instrument. Once responses are imported, scored, and checked,
run_analysis_plan() runs every research question in one
pass and returns results formatted for reporting.
The worked-study vignette uses a published study whose data is private. This vignette uses the bundled tourism demo, a synthetic dataset shaped around the same digital marketing and tourism constructs, so the analysis runs end to end without internet access or private data.
Response data uses instrument item IDs as column names. Metadata
columns are declared explicitly. Use strict = TRUE to keep
only known columns.
mr <- missing_data_report(responses, instr)
kable(mr$item_missing, digits = 2,
col.names = c("Variable", "Missing (n)", "Missing (%)", "Valid (n)"),
caption = "Item-level missingness")| Variable | Missing (n) | Missing (%) | Valid (n) | |
|---|---|---|---|---|
| visit_type | visit_type | 0 | 0 | 120 |
| dm_1 | dm_1 | 0 | 0 | 120 |
| dm_2 | dm_2 | 0 | 0 | 120 |
| dm_3 | dm_3 | 0 | 0 | 120 |
| sq_1 | sq_1 | 0 | 0 | 120 |
| sq_2 | sq_2 | 0 | 0 | 120 |
| sq_3 | sq_3 | 0 | 0 | 120 |
| sus_1 | sus_1 | 0 | 0 | 120 |
| sus_2 | sus_2 | 0 | 0 | 120 |
| sat_1 | sat_1 | 0 | 0 | 120 |
| sat_2 | sat_2 | 0 | 0 | 120 |
| bi_1 | bi_1 | 0 | 0 | 120 |
| bi_2 | bi_2 | 0 | 0 | 120 |
| attention | attention | 0 | 0 | 120 |
| comments | comments | 0 | 0 | 120 |
qr <- quality_report(
responses, instr,
respondent_id = "respondent_id",
submitted_at = "submitted_at",
started_at = "started_at"
)
quality_summary <- data.frame(
Metric = c("Respondents", "Items", "Flagged for review", "Flag rate"),
Value = c(qr$summary$n_respondents, qr$summary$n_items, qr$summary$n_flagged,
sprintf("%.1f%%", 100 * qr$summary$flag_rate)),
stringsAsFactors = FALSE
)
kable(quality_summary, align = c("l", "r"), caption = "Quality screening summary")| Metric | Value |
|---|---|
| Respondents | 120 |
| Items | 15 |
| Flagged for review | 109 |
| Flag rate | 90.8% |
run_analysis_plan() scores the scales for you, but
scoring once up front lets you inspect the construct scores and run the
assumption checks below.
scored <- score_scales(responses, instr, keep_items = TRUE, keep_meta = TRUE)
scale_ids <- vapply(instr$scales, function(x) x$id, character(1))
score_cols <- intersect(scale_ids, names(scored))
kable(head(scored[, score_cols, drop = FALSE]), digits = 2,
caption = "Scale scores, first respondents")| digital_marketing | service_quality | sustainability | satisfaction | behavioural_intention |
|---|---|---|---|---|
| 2.67 | 3.67 | 5.0 | 3.5 | 4.5 |
| 3.00 | 2.67 | 3.0 | 1.5 | 2.5 |
| 4.67 | 3.33 | 3.5 | 4.5 | 2.5 |
| 4.33 | 4.00 | 5.0 | 4.5 | 5.0 |
| 3.00 | 3.67 | 4.0 | 3.0 | 3.5 |
| 3.67 | 3.67 | 2.5 | 3.5 | 3.0 |
The scale-score distributions show the shape of each construct before the plan runs.
op <- par(mfrow = c(1, length(score_cols)), mar = c(4, 3, 2, 1))
for (s in score_cols) {
v <- scored[[s]]; v <- v[is.finite(v)]
hist(v, col = "#16B3B1", border = "white", main = s,
xlab = "Score", ylab = "")
}assumption_report() reports the checks a technique
relies on, such as residual normality, variance inflation, and influence
for a regression.
assumption_report(
scored,
predictors = c("digital_marketing", "service_quality", "sustainability"),
outcome = "satisfaction"
)
#> $method
#> [1] "assumptions"
#>
#> $normality
#> data frame with 0 columns and 0 rows
#>
#> $homogeneity
#> data frame with 0 columns and 0 rows
#>
#> $regression
#> $regression$n
#> [1] 120
#>
#> $regression$residual_shapiro_w
#> [1] 0.9941063
#>
#> $regression$residual_shapiro_p
#> [1] 0.8980753
#>
#> $regression$vif
#> digital_marketing service_quality sustainability
#> 1.212656 1.200070 1.012163
#>
#> $regression$cooks_distance
#> 1 2 3 4 5 6
#> 1.184326e-04 9.147336e-03 1.482864e-03 3.583748e-07 2.883541e-03 1.731349e-04
#> 7 8 9 10 11 12
#> 1.323697e-02 4.460344e-03 4.542716e-05 4.287879e-02 2.143130e-02 1.509419e-03
#> 13 14 15 16 17 18
#> 5.088239e-02 5.722577e-03 2.174172e-07 1.127457e-02 8.743696e-03 2.593670e-03
#> 19 20 21 22 23 24
#> 7.553824e-03 2.109049e-02 1.932134e-05 2.370723e-03 3.851322e-04 5.461064e-03
#> 25 26 27 28 29 30
#> 1.872377e-04 4.489098e-07 1.706497e-02 2.752537e-06 1.434770e-04 3.195816e-03
#> 31 32 33 34 35 36
#> 6.905977e-02 1.536396e-02 1.260801e-02 1.534755e-03 1.380360e-02 6.848461e-03
#> 37 38 39 40 41 42
#> 1.700720e-06 3.793174e-04 2.134337e-03 2.287672e-02 1.001287e-02 4.071700e-03
#> 43 44 45 46 47 48
#> 5.471299e-03 5.270891e-02 3.433051e-02 2.975180e-03 3.167244e-03 9.174703e-04
#> 49 50 51 52 53 54
#> 1.170983e-02 8.648779e-04 1.116591e-03 8.230930e-03 1.381027e-02 3.504105e-02
#> 55 56 57 58 59 60
#> 1.571502e-04 2.905749e-03 1.402942e-03 5.808575e-03 2.019476e-03 6.948885e-06
#> 61 62 63 64 65 66
#> 1.170618e-03 3.652109e-03 2.922443e-02 9.678057e-03 3.620026e-06 3.218468e-03
#> 67 68 69 70 71 72
#> 1.378174e-03 5.716292e-03 3.730149e-03 9.087202e-03 8.357802e-03 1.187750e-02
#> 73 74 75 76 77 78
#> 2.430428e-02 3.348263e-04 9.538221e-03 1.257656e-03 1.433252e-03 1.356868e-03
#> 79 80 81 82 83 84
#> 2.563597e-05 1.801658e-03 1.835160e-03 5.784606e-04 2.366539e-02 8.884675e-04
#> 85 86 87 88 89 90
#> 3.063566e-04 9.322376e-03 1.300065e-02 5.103375e-03 6.710071e-04 3.902158e-03
#> 91 92 93 94 95 96
#> 3.851322e-04 3.045158e-03 4.482863e-04 3.504325e-03 3.537797e-03 4.644052e-04
#> 97 98 99 100 101 102
#> 1.386934e-02 4.444185e-03 5.864202e-03 9.236185e-03 2.563387e-02 3.445601e-04
#> 103 104 105 106 107 108
#> 1.467024e-03 1.170618e-03 9.051071e-04 7.373574e-03 2.295924e-05 6.910949e-04
#> 109 110 111 112 113 114
#> 3.504325e-03 2.274586e-04 9.266580e-05 4.857092e-03 4.700977e-08 2.921484e-03
#> 115 116 117 118 119 120
#> 4.090240e-03 1.105467e-02 7.365409e-04 1.938023e-02 1.012910e-03 1.535916e-02
#>
#> $regression$standardised_residuals
#> 1 2 3 4 5 6
#> -0.082923698 -1.870645918 0.395846500 0.004591293 -0.692174489 -0.185004873
#> 7 8 9 10 11 12
#> 1.084968504 0.780147050 0.110776991 -2.638742712 -1.795997774 0.319306483
#> 13 14 15 16 17 18
#> 1.954971640 -1.219815183 0.004191993 1.118225981 1.039309006 0.527129425
#> 19 20 21 22 23 24
#> -0.961835047 0.980099373 0.063225377 0.551354644 0.278271787 -0.647978136
#> 25 26 27 28 29 30
#> 0.227581315 0.004986299 -1.172216599 0.021868776 -0.095706217 -0.782623908
#> 31 32 33 34 35 36
#> -2.309384417 -1.374470793 0.932880267 -0.690640105 1.255066667 0.671314169
#> 37 38 39 40 41 42
#> 0.012570683 0.352662125 0.374079633 1.867781077 -1.228052989 0.606343018
#> 43 44 45 46 47 48
#> -0.617848955 -1.662835019 2.074446939 0.978590982 -0.793286443 0.252351509
#> 49 50 51 52 53 54
#> 1.156356561 -0.449473176 -0.363323806 1.152172372 -1.440809735 -1.866773520
#> 55 56 57 58 59 60
#> 0.189559954 -0.858506945 -0.309992596 -1.198889104 0.696726010 -0.035497078
#> 61 62 63 64 65 66
#> -0.594649744 0.646085118 1.892162918 1.346958028 0.012140769 -1.050312804
#> 67 68 69 70 71 72
#> 0.598618566 -1.508218399 -0.521321175 -0.874142231 1.497519709 1.032511438
#> 73 74 75 76 77 78
#> 2.208163710 0.178475846 1.021361391 0.616360158 -0.448239907 -0.298894184
#> 79 80 81 82 83 84
#> -0.041513884 0.484623799 -0.711839662 -0.445277725 2.271897566 0.424028147
#> 85 86 87 88 89 90
#> 0.236369610 1.923468863 -1.537113657 -0.910885755 0.287247717 0.885761213
#> 91 92 93 94 95 96
#> 0.278271787 -1.099326747 -0.299846917 -0.670336465 1.075297214 0.241274096
#> 97 98 99 100 101 102
#> 1.521915286 0.863717913 0.560137775 -0.839407809 -1.485896784 -0.173199125
#> 103 104 105 106 107 108
#> -0.230361700 -0.594649744 0.499913855 -1.172602644 -0.046456880 -0.397268813
#> 109 110 111 112 113 114
#> -0.670336465 0.181786019 -0.202323795 -0.825310891 0.001891811 -0.577856052
#> 115 116 117 118 119 120
#> -0.520419080 1.827370061 -0.348396466 -1.215115771 0.250475472 0.752951062
#>
#>
#> $expected_counts
#> NULL
#>
#> $apa
#> [1] "Assumption checks were computed."
#>
#> $prompt
#> [1] "Report assumption checks before interpreting inferential models, especially sparse cells, non-normal residuals, and high VIF values."
#>
#> attr(,"class")
#> [1] "sframe_assumption_report"Each block binds a research question to a technique and to the
variables that fill each role. A correlation expects x and
y. A regression expects predictors and a
dependent variable. A group comparison expects a
group and an outcome.
instr$analysis_plan <- list(
list(id = "RQ1",
research_question = "Is digital marketing perception associated with satisfaction?",
family = "association", method = "correlation_pearson",
roles = list(x = "digital_marketing", y = "satisfaction"),
options = list(alpha = 0.05)),
list(id = "RQ2",
research_question = "Do the three perception scales predict satisfaction?",
family = "regression", method = "regression_linear",
roles = list(predictors = c("digital_marketing", "service_quality", "sustainability"),
dependent = "satisfaction"),
options = list(alpha = 0.05)),
list(id = "RQ3",
research_question = "Do first-time and repeat visitors differ in behavioural intention?",
family = "group_comparison", method = "mann_whitney",
roles = list(group = "visit_type", outcome = "behavioural_intention"),
options = list(alpha = 0.05))
)| RQ | Research question | Method | Result (APA) | Effect |
|---|---|---|---|---|
| RQ1 | Is digital marketing perception associated with satisfaction? | pearson | r(118) = 0.54, p < .001 | large |
| RQ2 | Do the three perception scales predict satisfaction? | R² = 0.383, F(3, 116) = 23.95, p < .001 | ||
| RQ3 | Do first-time and repeat visitors differ in behavioural intention? | U = 1576, z = -0.98, p = 0.327, r = 0.09 | negligible |
Each result holds more than the printed line. It carries the APA statistic, the effect-size label where the technique reports one, a writing prompt, and the references that support the technique.
rq1 <- results[[1]]
rq1$apa
#> [1] "r(118) = 0.54, p < .001"
rq1$effect_label
#> [1] "large"
rq1$prompt
#> [1] "There was a positive, large significant correlation between digital_marketing and satisfaction, r(118) = 0.54, p < .001. Explain what this means for your research question."
unlist(rq1$citations)
#> field_2018
#> "Field, A. (2018). *Discovering statistics using IBM SPSS statistics* (5th ed.). SAGE."
#> cohen_1988
#> "Cohen, J. (1988). *Statistical power analysis for the behavioral sciences* (2nd ed.). Lawrence Erlbaum."
#> r_core
#> "R Core Team. (2026). *R: A language and environment for statistical computing*. R Foundation for Statistical Computing."
#> surveyframe
#> "Sharafuddin, M. A. (2026). *surveyframe: Survey Instrument Workflows* (Version 0.3.2) [Computer software]. https://github.com/MohammedAliSharafuddin/surveyframe"render_results() writes a self-contained HTML report
with one section per research question, each holding the APA result, the
writing prompt, a space for the interpretation, and a reference list
compiled from the techniques used.
SurveyStudio runs the same plan. Open it on the analysis screen, upload the responses, and the Analysis Plan screen runs the saved plan and shows a table of each question with its method and APA result. The full report is produced on the Export screen.