Analysing survey responses: running the plan

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.

Load the demo

demo      <- sframe_demo_data()
instr     <- demo$instrument
responses <- demo$responses

dim(responses)
#> [1] 120  18

Import responses

Response data uses instrument item IDs as column names. Metadata columns are declared explicitly. Use strict = TRUE to keep only known columns.

responses <- read_responses(
  demo$responses_path,
  instr,
  respondent_id = "respondent_id",
  submitted_at  = "submitted_at",
  meta_cols     = "started_at",
  strict        = TRUE
)

dim(responses)
#> [1] 120  18

Missing data and quality

missing_data_report(responses, instr)
#> $method
#> [1] "missing_data"
#> 
#> $item_missing
#>              variable missing_n missing_pct 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
#> 
#> $respondent_missing
#>     row missing_n missing_pct
#> 1     1         0           0
#> 2     2         0           0
#> 3     3         0           0
#> 4     4         0           0
#> 5     5         0           0
#> 6     6         0           0
#> 7     7         0           0
#> 8     8         0           0
#> 9     9         0           0
#> 10   10         0           0
#> 11   11         0           0
#> 12   12         0           0
#> 13   13         0           0
#> 14   14         0           0
#> 15   15         0           0
#> 16   16         0           0
#> 17   17         0           0
#> 18   18         0           0
#> 19   19         0           0
#> 20   20         0           0
#> 21   21         0           0
#> 22   22         0           0
#> 23   23         0           0
#> 24   24         0           0
#> 25   25         0           0
#> 26   26         0           0
#> 27   27         0           0
#> 28   28         0           0
#> 29   29         0           0
#> 30   30         0           0
#> 31   31         0           0
#> 32   32         0           0
#> 33   33         0           0
#> 34   34         0           0
#> 35   35         0           0
#> 36   36         0           0
#> 37   37         0           0
#> 38   38         0           0
#> 39   39         0           0
#> 40   40         0           0
#> 41   41         0           0
#> 42   42         0           0
#> 43   43         0           0
#> 44   44         0           0
#> 45   45         0           0
#> 46   46         0           0
#> 47   47         0           0
#> 48   48         0           0
#> 49   49         0           0
#> 50   50         0           0
#> 51   51         0           0
#> 52   52         0           0
#> 53   53         0           0
#> 54   54         0           0
#> 55   55         0           0
#> 56   56         0           0
#> 57   57         0           0
#> 58   58         0           0
#> 59   59         0           0
#> 60   60         0           0
#> 61   61         0           0
#> 62   62         0           0
#> 63   63         0           0
#> 64   64         0           0
#> 65   65         0           0
#> 66   66         0           0
#> 67   67         0           0
#> 68   68         0           0
#> 69   69         0           0
#> 70   70         0           0
#> 71   71         0           0
#> 72   72         0           0
#> 73   73         0           0
#> 74   74         0           0
#> 75   75         0           0
#> 76   76         0           0
#> 77   77         0           0
#> 78   78         0           0
#> 79   79         0           0
#> 80   80         0           0
#> 81   81         0           0
#> 82   82         0           0
#> 83   83         0           0
#> 84   84         0           0
#> 85   85         0           0
#> 86   86         0           0
#> 87   87         0           0
#> 88   88         0           0
#> 89   89         0           0
#> 90   90         0           0
#> 91   91         0           0
#> 92   92         0           0
#> 93   93         0           0
#> 94   94         0           0
#> 95   95         0           0
#> 96   96         0           0
#> 97   97         0           0
#> 98   98         0           0
#> 99   99         0           0
#> 100 100         0           0
#> 101 101         0           0
#> 102 102         0           0
#> 103 103         0           0
#> 104 104         0           0
#> 105 105         0           0
#> 106 106         0           0
#> 107 107         0           0
#> 108 108         0           0
#> 109 109         0           0
#> 110 110         0           0
#> 111 111         0           0
#> 112 112         0           0
#> 113 113         0           0
#> 114 114         0           0
#> 115 115         0           0
#> 116 116         0           0
#> 117 117         0           0
#> 118 118         0           0
#> 119 119         0           0
#> 120 120         0           0
#> 
#> $patterns
#>           pattern   n percent
#> 1 000000000000000 120       1
#> 
#> $deletion
#> $deletion$listwise_n
#> [1] 120
#> 
#> $deletion$pairwise_n
#> $deletion$pairwise_n$visit_type
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$dm_1
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$dm_2
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$dm_3
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$sq_1
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$sq_2
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$sq_3
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$sus_1
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$sus_2
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$sat_1
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$sat_2
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$bi_1
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$bi_2
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$attention
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> $deletion$pairwise_n$comments
#> visit_type       dm_1       dm_2       dm_3       sq_1       sq_2       sq_3 
#>        120        120        120        120        120        120        120 
#>      sus_1      sus_2      sat_1      sat_2       bi_1       bi_2  attention 
#>        120        120        120        120        120        120        120 
#>   comments 
#>        120 
#> 
#> 
#> 
#> $scale_missing_rules
#>                scale_id n_items min_valid
#> 1     digital_marketing       3         3
#> 2       service_quality       3         3
#> 3        sustainability       2         2
#> 4          satisfaction       2         2
#> 5 behavioural_intention       2         2
#>                               missing_rule
#> 1 Score when at least 3 item(s) are valid.
#> 2 Score when at least 3 item(s) are valid.
#> 3 Score when at least 2 item(s) are valid.
#> 4 Score when at least 2 item(s) are valid.
#> 5 Score when at least 2 item(s) are valid.
#> 
#> $mcar
#> $mcar$available
#> [1] FALSE
#> 
#> $mcar$warning
#> [1] "Little's MCAR test requires an optional package and was not run."
#> 
#> 
#> $apa
#> [1] "Missing-data diagnostics were computed for 15 variable(s)."
#> 
#> $prompt
#> [1] "Report item and respondent missingness, the missing-data pattern, and the deletion rule used for each analysis."
#> 
#> attr(,"class")
#> [1] "sframe_missing_data_report"

quality_report(
  responses, instr,
  respondent_id = "respondent_id",
  submitted_at  = "submitted_at",
  started_at    = "started_at"
)
#> Survey Data Quality Report
#>   Respondents:  120
#>   Items:        15
#>   Flagged:      109 (90.8%)
#> 
#> Attention checks:
#>   attention_agree      pass 95%  fail 6
#> 
#> Timing:
#>   Median completion time: 966.0 seconds
#> 
#> Missingness:  0.0% of respondents exceed 20% threshold

Score scales

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))

head(scored[, intersect(scale_ids, names(scored)), drop = FALSE])
#>   digital_marketing service_quality sustainability satisfaction
#> 1          2.666667        3.666667            5.0          3.5
#> 2          3.000000        2.666667            3.0          1.5
#> 3          4.666667        3.333333            3.5          4.5
#> 4          4.333333        4.000000            5.0          4.5
#> 5          3.000000        3.666667            4.0          3.0
#> 6          3.666667        3.666667            2.5          3.5
#>   behavioural_intention
#> 1                   4.5
#> 2                   2.5
#> 3                   2.5
#> 4                   5.0
#> 5                   3.5
#> 6                   3.0

Check assumptions before the plan

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"

Define the plan

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))
)

Run the plan

results <- run_analysis_plan(responses, instr)
results
#> Analysis Results: 3 research question(s)
#> 
#> RQ 1: Is digital marketing perception associated with satisfaction?
#>   Test: correlation_pearson
#>   APA:  r(118) = 0.54, p < .001
#> 
#> RQ 2: Do the three perception scales predict satisfaction?
#>   Test: regression_linear
#>   APA:  R² = 0.383, F(3, 116) = 23.95, p < .001
#> 
#> RQ 3: Do first-time and repeat visitors differ in behavioural intention?
#>   Test: mann_whitney
#>   APA:  U = 1576, z = -0.98, p = 0.327, r = 0.09

Read a single result

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: A survey instrument workflow for R* (Version 0.3.0) [Computer software]. https://github.com/MohammedAliSharafuddin/surveyframe"

Render the results report

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.

render_results(results, instr, output_file = "results.html", citation_format = "apa")

In SurveyStudio

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.

launch_studio(
  instrument     = instr,
  responses      = responses,
  screen         = "analysis",
  launch.browser = FALSE
)