Issue with calculating respondent-level average scores from Matrix Table items. | Experience Community
Skip to main content
Solved

Issue with calculating respondent-level average scores from Matrix Table items.

  • June 3, 2026
  • 4 replies
  • 54 views

Forum|alt.badge.img+1

Hi everyone,

 

I have the following survey design: 

- There are 6 possible scenarios.
- Each respondent sees only 2 out of 6 scenarios.
- For each shown scenario, respondents answer Likert/matrix items:
- Logica: I1
- Empathische kwaliteit: I2, I3, I4
- Intentie/behoefte: I5, I6, I7
- Haalbaarheid: I8
- I want to store the selected 1–7 values in Embedded Data after each scenario block and later calculate average scores only over the shown/answered scenarios.

Embedded Data fields include:
S1_Logica_I1, S2_Logica_I1, S3_Logica_I1, S4_Logica_I1, S5_Logica_I1, S6_Logica_I1
S1_PercEmpathie_I2, S1_PercEmpathie_I3, S1_PercEmpathie_I4, etc. for S1–S6
S1_IntentieTotEngag_I5, S1_IntentieTotEngag_I6, S1_IntentieTotEngag_I7, etc. for S1–S6
S1_Haalbaarheid_I8, etc. for S1–S6. I've checked the piped text, that works fine. 

 

In order to calculate the scores I've made JS code. However, it does not function as expected and my output on the anonymous link doesn't show that placeholder HTML is replaced with the output from InnerHTML section in JS and even when it did, there's still somehow problems with calculating the averages in JS. Hereby the code:                                                                                                                                 

Qualtrics.SurveyEngine.addOnLoad(function () {
    var qid = this.questionId;

    try {
        // Schone functie om de Embedded Data op te halen
        function getED(name) {
            var v = Qualtrics.SurveyEngine.getEmbeddedData(name);
            if (v === null || v === undefined) return "";
            v = String(v).trim();
            if (v === "" || v === "NA" || v === "undefined" || v === "null") return "";
            return v.replace(",", "."); // Vervang eventuele komma's door punten voor de berekening
        }

        // Functie om het gemiddelde te berekenen over de wél ingevulde scenario's
        function avg(fields) {
            var scores = [];
            for (var i = 0; i < fields.length; i++) {
                var v = getED(fields[i]);
                if (v !== "") {
                    var n = Number(v);
                    if (!isNaN(n) && n >= 1 && n <= 7) scores.push(n);
                }
            }
            if (!scores.length) return null;
            var s = 0;
            for (var j = 0; j < scores.length; j++) s += scores[j];
            return s / scores.length;
        }

        // Formatteer naar twee decimalen met een komma (bijv. 4,50)
        function fmt(s) {
            return s === null ? "Niet beschikbaar" : s.toFixed(2).replace(".", ",");
        }

        // Jouw oorspronkelijke variabelenamen
        var logica       = ["S1_Logica_I1","S2_Logica_I1","S3_Logica_I1","S4_Logica_I1","S5_Logica_I1","S6_Logica_I1"];
        var empathie     = ["S1_PercEmpathie_I2","S1_PercEmpathie_I3","S1_PercEmpathie_I4","S2_PercEmpathie_I2","S2_PercEmpathie_I3","S2_PercEmpathie_I4","S3_PercEmpathie_I2","S3_PercEmpathie_I3","S3_PercEmpathie_I4","S4_PercEmpathie_I2","S4_PercEmpathie_I3","S4_PercEmpathie_I4","S5_PercEmpathie_I2","S5_PercEmpathie_I3","S5_PercEmpathie_I4","S6_PercEmpathie_I2","S6_PercEmpathie_I3","S6_PercEmpathie_I4"];
        var intentie     = ["S1_IntentieTotEngag_I5","S1_IntentieTotEngag_I6","S1_IntentieTotEngag_I7","S2_IntentieTotEngag_I5","S2_IntentieTotEngag_I6","S2_IntentieTotEngag_I7","S3_IntentieTotEngag_I5","S3_IntentieTotEngag_I6","S3_IntentieTotEngag_I7","S4_IntentieTotEngag_I5","S4_IntentieTotEngag_I6","S4_IntentieTotEngag_I7","S5_IntentieTotEngag_I5","S5_IntentieTotEngag_I6","S5_IntentieTotEngag_I7","S6_IntentieTotEngag_I5","S6_IntentieTotEngag_I6","S6_IntentieTotEngag_I7"];
        var haalbaarheid = ["S1_Haalbaarheid_I8","S2_Haalbaarheid_I8","S3_Haalbaarheid_I8","S4_Haalbaarheid_I8","S5_Haalbaarheid_I8","S6_Haalbaarheid_I8"];

        // We zoeken de placeholder binnen de container van deze specifieke vraag
        var container = this.getQuestionTextContainer();
        var outputDiv = container ? container.querySelector("#gemiddeldeScoresOutput") : null;

        if (outputDiv) {
            outputDiv.innerHTML =
                "<p style='margin: 8px 0;'>Logica AI-advies: <strong>" + fmt(avg(logica)) + "</strong></p>" +
                "<p style='margin: 8px 0;'>Empathische kwaliteit AI-advies: <strong>" + fmt(avg(empathie)) + "</strong></p>" +
                "<p style='margin: 8px 0;'>Behoefte AI-advies te gebruiken: <strong>" + fmt(avg(intentie)) + "</strong></p>" +
                "<p style='margin: 8px 0;'>Haalbaarheid AI-advies: <strong>" + fmt(avg(haalbaarheid)) + "</strong></p>" +
                "<p style='font-size:0.9em; margin-top:16px; color:#555;'>De scores lopen van 1 tot en met 7.</p>";
        }

    } catch (e) {
        console.error("Fout in script:", e.message);
    }
});

 

Feedback is appreciated. I'm not that experienced with JS. Thank you in advance! 

                                                                                                                    

Best answer by joel bautista

This is a very interesting setup, and I think the core challenge here is not just about calculating averages, but about correctly handling partial exposure to scenarios per respondent.

From your description, each respondent only sees 2 out of 6 scenarios, which makes the calculation more complex because you need to ensure that your averages are based only on the scenarios that were actually displayed and answered, not on all possible variables.

Based on my experience, the issue is usually not the JavaScript itself, but rather a combination of these factors:

1. Identifying which scenarios were shown

Before calculating anything, you need a reliable way to know which scenarios were presented to the respondent.

A common approach is to:

  • Set an Embedded Data flag per scenario (e.g., S1_Shown = 1, S2_Shown = 1, etc.)
  • Or derive it from whether key variables (e.g., S1_Logica_I1) have a value

2. Avoiding null or empty values in calculations

If your JavaScript is looping through all scenarios (S1–S6), it may be:

  • Including undefined or empty values
  • Which will break or distort your average

You should explicitly:

  • Validate that a value exists before including it in the calculation
  • Only aggregate values from scenarios that were actually answered

3. Timing of JavaScript execution

In Qualtrics, this is critical:

  • If the JS runs before all answers are available, the calculation will be incomplete
  • Make sure you are using the correct lifecycle hook (e.g., addOnPageSubmit or after the last block)

4. Over-reliance on JavaScript

In many cases, you can simplify the solution by combining:

  • Survey Flow (Embedded Data)
  • Branch logic
  • And even post-processing in reporting

If your final goal is reporting (e.g., average scores per respondent), it is often more robust to:

  • Store raw values cleanly
  • And calculate averages later in Reports / Dashboards

✅ Recommended approach

Instead of trying to solve everything in one JavaScript step, I would suggest:

  1. Track which scenarios are shown (Embedded Data)
  2. Store all item-level responses consistently
  3. In your calculation:
    • Only include scenarios where data exists
  4. If possible, move the final aggregation to reporting (for better stability and auditability)

💡 Key idea

The real challenge here is not matrix handling itself, but:

Conditionally aggregating data based on what each respondent actually experienced

Hope this helps — you’re definitely on the right track, and with a few adjustments, your logic should work much more reliably.

4 replies

Forum|alt.badge.img+5
  • Level 2 ●●
  • Answer
  • June 4, 2026

This is a very interesting setup, and I think the core challenge here is not just about calculating averages, but about correctly handling partial exposure to scenarios per respondent.

From your description, each respondent only sees 2 out of 6 scenarios, which makes the calculation more complex because you need to ensure that your averages are based only on the scenarios that were actually displayed and answered, not on all possible variables.

Based on my experience, the issue is usually not the JavaScript itself, but rather a combination of these factors:

1. Identifying which scenarios were shown

Before calculating anything, you need a reliable way to know which scenarios were presented to the respondent.

A common approach is to:

  • Set an Embedded Data flag per scenario (e.g., S1_Shown = 1, S2_Shown = 1, etc.)
  • Or derive it from whether key variables (e.g., S1_Logica_I1) have a value

2. Avoiding null or empty values in calculations

If your JavaScript is looping through all scenarios (S1–S6), it may be:

  • Including undefined or empty values
  • Which will break or distort your average

You should explicitly:

  • Validate that a value exists before including it in the calculation
  • Only aggregate values from scenarios that were actually answered

3. Timing of JavaScript execution

In Qualtrics, this is critical:

  • If the JS runs before all answers are available, the calculation will be incomplete
  • Make sure you are using the correct lifecycle hook (e.g., addOnPageSubmit or after the last block)

4. Over-reliance on JavaScript

In many cases, you can simplify the solution by combining:

  • Survey Flow (Embedded Data)
  • Branch logic
  • And even post-processing in reporting

If your final goal is reporting (e.g., average scores per respondent), it is often more robust to:

  • Store raw values cleanly
  • And calculate averages later in Reports / Dashboards

✅ Recommended approach

Instead of trying to solve everything in one JavaScript step, I would suggest:

  1. Track which scenarios are shown (Embedded Data)
  2. Store all item-level responses consistently
  3. In your calculation:
    • Only include scenarios where data exists
  4. If possible, move the final aggregation to reporting (for better stability and auditability)

💡 Key idea

The real challenge here is not matrix handling itself, but:

Conditionally aggregating data based on what each respondent actually experienced

Hope this helps — you’re definitely on the right track, and with a few adjustments, your logic should work much more reliably.


Forum|alt.badge.img+28

Just being curious, what made you consider using JS instead of the scoring provided within Qualtrics?


Forum|alt.badge.img+1
  • Author
  • June 5, 2026

Chee Heng_SZ thanks for your reply. I didn't have an in depth look at the scoring function and overrelied on  answers of Qualtrics AI support bot. It said this method may not be completely suited for my purpose. After careful consideration of the scoring method, I think my current survey flow may not allow it as I have 4 different scales organised in one scenario block. This doesn't align with the aspired average of two random average scenario scores from 4 different scales. 


Forum|alt.badge.img+1
  • Author
  • June 6, 2026

This is a very interesting setup, and I think the core challenge here is not just about calculating averages, but about correctly handling partial exposure to scenarios per respondent.

From your description, each respondent only sees 2 out of 6 scenarios, which makes the calculation more complex because you need to ensure that your averages are based only on the scenarios that were actually displayed and answered, not on all possible variables.

Based on my experience, the issue is usually not the JavaScript itself, but rather a combination of these factors:

1. Identifying which scenarios were shown

Before calculating anything, you need a reliable way to know which scenarios were presented to the respondent.

A common approach is to:

  • Set an Embedded Data flag per scenario (e.g., S1_Shown = 1, S2_Shown = 1, etc.)
  • Or derive it from whether key variables (e.g., S1_Logica_I1) have a value

2. Avoiding null or empty values in calculations

If your JavaScript is looping through all scenarios (S1–S6), it may be:

  • Including undefined or empty values
  • Which will break or distort your average

You should explicitly:

  • Validate that a value exists before including it in the calculation
  • Only aggregate values from scenarios that were actually answered

3. Timing of JavaScript execution

In Qualtrics, this is critical:

  • If the JS runs before all answers are available, the calculation will be incomplete
  • Make sure you are using the correct lifecycle hook (e.g., addOnPageSubmit or after the last block)

4. Over-reliance on JavaScript

In many cases, you can simplify the solution by combining:

  • Survey Flow (Embedded Data)
  • Branch logic
  • And even post-processing in reporting

If your final goal is reporting (e.g., average scores per respondent), it is often more robust to:

  • Store raw values cleanly
  • And calculate averages later in Reports / Dashboards

✅ Recommended approach

Instead of trying to solve everything in one JavaScript step, I would suggest:

  1. Track which scenarios are shown (Embedded Data)
  2. Store all item-level responses consistently
  3. In your calculation:
    • Only include scenarios where data exists
  4. If possible, move the final aggregation to reporting (for better stability and auditability)

💡 Key idea

The real challenge here is not matrix handling itself, but:

Conditionally aggregating data based on what each respondent actually experienced

Hope this helps — you’re definitely on the right track, and with a few adjustments, your logic should work much more reliably.

Thank you for your comprehensive explanation. I did something similar to your approach. I have made for all average scale scores 6 scenario bounded separate embedded data field, consisting of 4 embedded data variables. Than I've calculated scenario bounded average scale scores for each variable by the mean of the item scores. At the end (right before the block where I want to show the 4 total average scores) each group of 6 average scale scores were divided by two, quantified by piped text calculation in 4 total average embedded variables. Finally, I've shown the final piped text of total average embedded variables to the participant. This setup worked!