jspsych / jsPsych

Create behavioral experiments in a browser using JavaScript
http://www.jspsych.org
MIT License
1.03k stars 668 forks source link

calculating the number of correct responses of a survey-multi-select #3321

Open fatemene opened 3 months ago

fatemene commented 3 months ago

Dear all, I want to both give feedback to respondents and save the data for the number of correct responses when the participant chooses the correct response from a multi-select survey, but I do not know how. Can anybody help? Thanks in advance. `<!DOCTYPE html>

Memory Experiment

`

vzhang03 commented 3 months ago

Hi Fateme,

While you could access the entire data, for this particular use case for simplicity it would be easier to get data from the last trial (which is the multi-select) and iterate through that data scoring the answers. Replicating the code and experiment you had above, I could correctly score the experiments by changing the debrief block as shown below. When you implement this in your experiment you should adjust the correct answers to include the correct answers in your case. I would be happy to send my entire HTML experiment file if this isn't working for you.

`

      var debrief_block = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: function() {
          var answersHTML = jsPsych.data.getLastTrialData().values()[0]['response']['Q0'];
          const correctAnswers = ["آهن"];
          var answers = [];
          const total = 32;
          var correct = total - correctAnswers.length;

          // Iterate through each element in the input array
          for (const answer of answersHTML) {
              // Use regex to extract (number) ArabicCharacter pattern
              var match = answer.match(/\((\d+)\)\s*(\S+)/)[2];                  
              match = match.replace("</p>", ""); // removes final header
              answers.push(match); // match[2] contains the answer character
          }

          // scores correct and incorrect items
          for (const answer of answers) {
            if (correctAnswers.includes(answer)) correct++; // scores correct
            else correct --; // scores incorrect
          }

          return `<p>You responded correctly on ${100 
            * (correct/total)}% of the trials.</p>`;
        }, 
        choices: "NO_KEYS",
        trial_duration: 4000
      };

`

fatemene commented 3 months ago

Thanks a lot for the great help. The math does not seem to work correctly. I show 16 words in the presentation part; in the memory task, they should choose those 16 words from the 32 words given. If respondents choose from the 16 words given in the correctAnswers, they should get a plus score; otherwise (if the correct answer is not chosen or the incorrect answer is chosen) it should be counted as an incorrect response. We have a total of responses from which the number of correct answers should be calculated. Here is how I completed the code with your kind suggestion. `<!DOCTYPE html>

Memory Experiment

`

vzhang03 commented 3 months ago

If I'm understanding you correctly the code should be working because that was also how I had in mind. Is there a specific number that is failing for you?

        const correctAnswers = ["طبل", "پرده", "زنگ","ماهی","مدرسه", "کلاه", "قایق","بینی", "مداد", "کفش", "قهوه", "میز", "پرنده", "بخاری", "کوه","ماه" ];
        const total = 32;
        var correct = total - correctAnswers.length;

      for (const answer of answers) {
        if (correctAnswers.includes(answer)) correct++; // scores correct
        else correct --; // scores incorrect
      }

When the user doesn't put in answers, they should get 50% because they correctly selected the answers not displayed. When I tested it does return as 50%. When there are no answers can skip the for loop and calculate the answers correct as "var correct = total - correctAnswers.length = 16".

Same when testing the selection of all the answers. If the user selects all answers they start with 16 points before the responses are tallied. Then after going through each answer because 16 of the selections are correct or plus points and the 16 incorrect selections are negative points, the user should end up with 16 points which is being correctly represented when testing.

Bankminer78 commented 3 months ago

Hi Fatemene,

If you would like to calculate the number of correct responses within the test-page trial itself, you can use a higher-level function inside the on_finish parameter:

 var correct = [your list of correct options in html]

var test_page = {
    type: jsPsychSurveyMultiSelect,
    questions: [
        {
            prompt: "کدامیک از این کلمات را در لیست قبلی مشاهده کردید؟ آن را انتخاب کنید",
            options: [
                '<p style="font-size:37.34px;">(1) ME</p>',
                '<p style="font-size:37.34px;">(2) ماه</p>',
                '<p style="font-size:37.34px;">(3) طبل</p>',
                '<p style="font-size:37.34px;">(4) بخاری</p>',
                '<p style="font-size:37.34px;">(5) پرنده</p>',
                '<p style="font-size:37.34px;">(6) سنجاق</p>',
                '<p style="font-size:37.34px;">(7) مداد</p>',
                '<p style="font-size:37.34px;">(8) جغد</p>',
                '<p style="font-size:37.34px;">(9) بیل</p>',
                '<p style="font-size:37.34px;">(10) ریسمان</p>',
                '<p style="font-size:37.34px;">(11) سوراخ</p>',
                '<p style="font-size:37.34px;">(12) چانه</p>',
                '<p style="font-size:37.34px;">(13) سیمان</p>',
                '<p style="font-size:37.34px;">(14) ماهی</p>',
                '<p style="font-size:37.34px;">(15) زنگ</p>',
                '<p style="font-size:37.34px;">(16) دیوار</p>',
                '<p style="font-size:37.34px;">(17) کلاه</p>',
                '<p style="font-size:37.34px;">(18) در</p>',
                '<p style="font-size:37.34px;">(19) ME TOO</p>',
                '<p style="font-size:37.34px;">(20) کفش</p>',
                '<p style="font-size:37.34px;">(21) چنگال</p>',
                '<p style="font-size:37.34px;">(22) سطل</p>',
                '<p style="font-size:37.34px;">(23) چوب</p>',
                '<p style="font-size:37.34px;">(24) پرده</p>',
                '<p style="font-size:37.34px;">(25) میز</p>',
                '<p style="font-size:37.34px;">(26) فندک</p>',
                '<p style="font-size:37.34px;">(27) مدرسه</p>',
                '<p style="font-size:37.34px;">(28) قایق</p>',
                '<p style="font-size:37.34px;">(29) بینی</p>',
                '<p style="font-size:37.34px;">(30) کوه</p>',
                '<p style="font-size:37.34px;">(31) لیوان</p>',
                '<p style="font-size:37.34px;">(32) قهوه</p>'
            ],
            required: true, 
            horizontal: true,
        }
    ],
    data: {
                task: 'response', 
                correct_response: jsPsych.timelineVariable('stimulus')
            },
  on_finish: function(data) {
                 data.success_count = data.response.Q0.reduce((acc, item) => acc + (correct.includes(item) ? 1 : 0), 0);
          }
};

(side-note: it might be a good idea to replace the 'correct' variable with a reference to a timelineVariable and adjust the implementation of the test-page trial appropriately) The reduce function here would fold the list of responses into a number representing the number of correct responses. You can then access this value in the debrief_block trial by calling an expression likejsPsych.data.getLastTrialData().values()[0]['success_count']. In the end, your data should look something like:

{
    "rt": 6170,
    "response": {
        "Q0": [
            "<p style=\"font-size:37.34px;\">(1) ME</p>",
            "<p style=\"font-size:37.34px;\">(19) ME TOO</p>"
        ]
    },
    "question_order": [
        0
    ],
    "task": "response",
    "trial_type": "survey-multi-select",
    "trial_index": 3,
    "time_elapsed": 8761,
    "internal_node_id": "0.0-3.0",
    "success_count": *number of responses selected correctly*
},
fatemene commented 3 months ago

'

(28) قایق

', '

(29) بینی

', '

(30) کوه

', '

(31) لیوان

', '

(32) قهوه

' ], required: true, horizontal: true, } ], data: { task: 'response', correct_response: jsPsych.timelineVariable('stimulus') }, on_finish: function(data) { data.success_count = data.response.Q0.reduce((acc, item) => acc + (correct.includes(item) ? 1 : 0), 0); } };

Hi Fatemene,

If you would like to calculate the number of correct responses within the test-page trial itself, you can use a higher-level function inside the on_finish parameter:

 var correct = [your list of correct options in html]

var test_page = {
    type: jsPsychSurveyMultiSelect,
    questions: [
        {
            prompt: "کدامیک از این کلمات را در لیست قبلی مشاهده کردید؟ آن را انتخاب کنید",
            options: [
                '<p style="font-size:37.34px;">(1) ME</p>',
                '<p style="font-size:37.34px;">(2) ماه</p>',
                '<p style="font-size:37.34px;">(3) طبل</p>',
                '<p style="font-size:37.34px;">(4) بخاری</p>',
                '<p style="font-size:37.34px;">(5) پرنده</p>',
                '<p style="font-size:37.34px;">(6) سنجاق</p>',
                '<p style="font-size:37.34px;">(7) مداد</p>',
                '<p style="font-size:37.34px;">(8) جغد</p>',
                '<p style="font-size:37.34px;">(9) بیل</p>',
                '<p style="font-size:37.34px;">(10) ریسمان</p>',
                '<p style="font-size:37.34px;">(11) سوراخ</p>',
                '<p style="font-size:37.34px;">(12) چانه</p>',
                '<p style="font-size:37.34px;">(13) سیمان</p>',
                '<p style="font-size:37.34px;">(14) ماهی</p>',
                '<p style="font-size:37.34px;">(15) زنگ</p>',
                '<p style="font-size:37.34px;">(16) دیوار</p>',
                '<p style="font-size:37.34px;">(17) کلاه</p>',
                '<p style="font-size:37.34px;">(18) در</p>',
                '<p style="font-size:37.34px;">(19) ME TOO</p>',
                '<p style="font-size:37.34px;">(20) کفش</p>',
                '<p style="font-size:37.34px;">(21) چنگال</p>',
                '<p style="font-size:37.34px;">(22) سطل</p>',
                '<p style="font-size:37.34px;">(23) چوب</p>',
                '<p style="font-size:37.34px;">(24) پرده</p>',
                '<p style="font-size:37.34px;">(25) میز</p>',
                '<p style="font-size:37.34px;">(26) فندک</p>',
                '<p style="font-size:37.34px;">(27) مدرسه</p>',
                '<p style="font-size:37.34px;">(28) قایق</p>',
                '<p style="font-size:37.34px;">(29) بینی</p>',
                '<p style="font-size:37.34px;">(30) کوه</p>',
                '<p style="font-size:37.34px;">(31) لیوان</p>',
                '<p style="font-size:37.34px;">(32) قهوه</p>'
            ],
            required: true, 
            horizontal: true,
        }
    ],
    data: {
                task: 'response', 
                correct_response: jsPsych.timelineVariable('stimulus')
            },
  on_finish: function(data) {
                 data.success_count = data.response.Q0.reduce((acc, item) => acc + (correct.includes(item) ? 1 : 0), 0);
          }
};

(side-note: it might be a good idea to replace the 'correct' variable with a reference to a timelineVariable and adjust the implementation of the test-page trial appropriately) The reduce function here would fold the list of responses into a number representing the number of correct responses. You can then access this value in the debrief_block trial by calling an expression likejsPsych.data.getLastTrialData().values()[0]['success_count']. In the end, your data should look something like:

{
    "rt": 6170,
    "response": {
        "Q0": [
            "<p style=\"font-size:37.34px;\">(1) ME</p>",
            "<p style=\"font-size:37.34px;\">(19) ME TOO</p>"
        ]
    },
    "question_order": [
        0
    ],
    "task": "response",
    "trial_type": "survey-multi-select",
    "trial_index": 3,
    "time_elapsed": 8761,
    "internal_node_id": "0.0-3.0",
    "success_count": *number of responses selected correctly*
},

Thanks for the illuminating explanation. That was quite clear.

fatemene commented 3 months ago

Hi Fatemene,

If you would like to calculate the number of correct responses within the test-page trial itself, you can use a higher-level function inside the on_finish parameter:

 var correct = [your list of correct options in html]

var test_page = {
    type: jsPsychSurveyMultiSelect,
    questions: [
        {
            prompt: "کدامیک از این کلمات را در لیست قبلی مشاهده کردید؟ آن را انتخاب کنید",
            options: [
                '<p style="font-size:37.34px;">(1) ME</p>',
                '<p style="font-size:37.34px;">(2) ماه</p>',
                '<p style="font-size:37.34px;">(3) طبل</p>',
                '<p style="font-size:37.34px;">(4) بخاری</p>',
                '<p style="font-size:37.34px;">(5) پرنده</p>',
                '<p style="font-size:37.34px;">(6) سنجاق</p>',
                '<p style="font-size:37.34px;">(7) مداد</p>',
                '<p style="font-size:37.34px;">(8) جغد</p>',
                '<p style="font-size:37.34px;">(9) بیل</p>',
                '<p style="font-size:37.34px;">(10) ریسمان</p>',
                '<p style="font-size:37.34px;">(11) سوراخ</p>',
                '<p style="font-size:37.34px;">(12) چانه</p>',
                '<p style="font-size:37.34px;">(13) سیمان</p>',
                '<p style="font-size:37.34px;">(14) ماهی</p>',
                '<p style="font-size:37.34px;">(15) زنگ</p>',
                '<p style="font-size:37.34px;">(16) دیوار</p>',
                '<p style="font-size:37.34px;">(17) کلاه</p>',
                '<p style="font-size:37.34px;">(18) در</p>',
                '<p style="font-size:37.34px;">(19) ME TOO</p>',
                '<p style="font-size:37.34px;">(20) کفش</p>',
                '<p style="font-size:37.34px;">(21) چنگال</p>',
                '<p style="font-size:37.34px;">(22) سطل</p>',
                '<p style="font-size:37.34px;">(23) چوب</p>',
                '<p style="font-size:37.34px;">(24) پرده</p>',
                '<p style="font-size:37.34px;">(25) میز</p>',
                '<p style="font-size:37.34px;">(26) فندک</p>',
                '<p style="font-size:37.34px;">(27) مدرسه</p>',
                '<p style="font-size:37.34px;">(28) قایق</p>',
                '<p style="font-size:37.34px;">(29) بینی</p>',
                '<p style="font-size:37.34px;">(30) کوه</p>',
                '<p style="font-size:37.34px;">(31) لیوان</p>',
                '<p style="font-size:37.34px;">(32) قهوه</p>'
            ],
            required: true, 
            horizontal: true,
        }
    ],
    data: {
                task: 'response', 
                correct_response: jsPsych.timelineVariable('stimulus')
            },
  on_finish: function(data) {
                 data.success_count = data.response.Q0.reduce((acc, item) => acc + (correct.includes(item) ? 1 : 0), 0);
          }
};

(side-note: it might be a good idea to replace the 'correct' variable with a reference to a timelineVariable and adjust the implementation of the test-page trial appropriately) The reduce function here would fold the list of responses into a number representing the number of correct responses. You can then access this value in the debrief_block trial by calling an expression likejsPsych.data.getLastTrialData().values()[0]['success_count']. In the end, your data should look something like:

{
    "rt": 6170,
    "response": {
        "Q0": [
            "<p style=\"font-size:37.34px;\">(1) ME</p>",
            "<p style=\"font-size:37.34px;\">(19) ME TOO</p>"
        ]
    },
    "question_order": [
        0
    ],
    "task": "response",
    "trial_type": "survey-multi-select",
    "trial_index": 3,
    "time_elapsed": 8761,
    "internal_node_id": "0.0-3.0",
    "success_count": *number of responses selected correctly*
},

Hi Fatemene,

If you would like to calculate the number of correct responses within the test-page trial itself, you can use a higher-level function inside the on_finish parameter:

 var correct = [your list of correct options in html]

var test_page = {
    type: jsPsychSurveyMultiSelect,
    questions: [
        {
            prompt: "کدامیک از این کلمات را در لیست قبلی مشاهده کردید؟ آن را انتخاب کنید",
            options: [
                '<p style="font-size:37.34px;">(1) ME</p>',
                '<p style="font-size:37.34px;">(2) ماه</p>',
                '<p style="font-size:37.34px;">(3) طبل</p>',
                '<p style="font-size:37.34px;">(4) بخاری</p>',
                '<p style="font-size:37.34px;">(5) پرنده</p>',
                '<p style="font-size:37.34px;">(6) سنجاق</p>',
                '<p style="font-size:37.34px;">(7) مداد</p>',
                '<p style="font-size:37.34px;">(8) جغد</p>',
                '<p style="font-size:37.34px;">(9) بیل</p>',
                '<p style="font-size:37.34px;">(10) ریسمان</p>',
                '<p style="font-size:37.34px;">(11) سوراخ</p>',
                '<p style="font-size:37.34px;">(12) چانه</p>',
                '<p style="font-size:37.34px;">(13) سیمان</p>',
                '<p style="font-size:37.34px;">(14) ماهی</p>',
                '<p style="font-size:37.34px;">(15) زنگ</p>',
                '<p style="font-size:37.34px;">(16) دیوار</p>',
                '<p style="font-size:37.34px;">(17) کلاه</p>',
                '<p style="font-size:37.34px;">(18) در</p>',
                '<p style="font-size:37.34px;">(19) ME TOO</p>',
                '<p style="font-size:37.34px;">(20) کفش</p>',
                '<p style="font-size:37.34px;">(21) چنگال</p>',
                '<p style="font-size:37.34px;">(22) سطل</p>',
                '<p style="font-size:37.34px;">(23) چوب</p>',
                '<p style="font-size:37.34px;">(24) پرده</p>',
                '<p style="font-size:37.34px;">(25) میز</p>',
                '<p style="font-size:37.34px;">(26) فندک</p>',
                '<p style="font-size:37.34px;">(27) مدرسه</p>',
                '<p style="font-size:37.34px;">(28) قایق</p>',
                '<p style="font-size:37.34px;">(29) بینی</p>',
                '<p style="font-size:37.34px;">(30) کوه</p>',
                '<p style="font-size:37.34px;">(31) لیوان</p>',
                '<p style="font-size:37.34px;">(32) قهوه</p>'
            ],
            required: true, 
            horizontal: true,
        }
    ],
    data: {
                task: 'response', 
                correct_response: jsPsych.timelineVariable('stimulus')
            },
  on_finish: function(data) {
                 data.success_count = data.response.Q0.reduce((acc, item) => acc + (correct.includes(item) ? 1 : 0), 0);
          }
};

(side-note: it might be a good idea to replace the 'correct' variable with a reference to a timelineVariable and adjust the implementation of the test-page trial appropriately) The reduce function here would fold the list of responses into a number representing the number of correct responses. You can then access this value in the debrief_block trial by calling an expression likejsPsych.data.getLastTrialData().values()[0]['success_count']. In the end, your data should look something like:

{
    "rt": 6170,
    "response": {
        "Q0": [
            "<p style=\"font-size:37.34px;\">(1) ME</p>",
            "<p style=\"font-size:37.34px;\">(19) ME TOO</p>"
        ]
    },
    "question_order": [
        0
    ],
    "task": "response",
    "trial_type": "survey-multi-select",
    "trial_index": 3,
    "time_elapsed": 8761,
    "internal_node_id": "0.0-3.0",
    "success_count": *number of responses selected correctly*
},

Thanks for the alternative solution. unfortunately the code didn't work; most probably because I didn't know to use it.