Adding a correct response counter that ends trial.

999 views
Skip to first unread message

Oliver Sokol

unread,
Mar 10, 2017, 9:28:18 AM3/10/17
to jsPsych
So I'm working on a test that will repeatedly loop through a subset of trials until a specific amount of correct responses are made in a row. Now I realize that a counter function is what has to be used but I am totally incapable of figuring out how and where to write it in.

To clarify a little more, the participant will start the test, will be given a block in which 'categorize' trials require a response to be made. If responses on the trials reach a total of 8 correct then the block ends and a new block begins. Now I've managed to group the trials in blocks and loop the block in the following fashion:

  var test_block = {
      timeline: [test_stimuli, test_stimuli2, test_stimuli3, test_stimuli4],
    randomize_order: true,
    
    loop_function: function(data){
        if(trial_count_correct < 8){
            return true;
        } else {
            return false;
        }
    }

  };

The problem is that I can only get the experiment to somewhat function by setting the trial_count_correct to = 8. No matter how i include a counter function the only values I am able to pass on into var trial_count_correct is either 0 or NaN. I've tried linking it to the standard function getSubjectData(); by including a return that counts correct responses. The result is constantly NaN. This is weird because the standard function works in the debrief_block and shows the correct accuracy %.

I have spent hours now trying to make alternate functions editing the functions, adding on_finish statements all to no avail. How would you suggest to solve this? I've tried reading questions others have had with similar problems but being a novice I'm finding it difficult to implement the solutions to my code.

I also wonder if I can use the jsPsych.finishTrial to end the block before the loop function has to loop through the whole timeline.

This is the code used:


  var test_stimuli =  { 
    type: 'categorize',  
    stimulus: '<p>A1</p>',
    key_answer: 70,
    choices: [70,74],
    data: { response:'ll'},
    correct_text: '<p>Correct!</p>',
    incorrect_text: '<p>Incorrect!</p>',
    is_html: true,
        on_finish: function (trial_data){
      var correct = (trial_data.key_press == 70);
      jsPsych.data.addDataToLastTrial({correct: correct});
    }

  }

  var test_stimuli2 = { 
    type: 'categorize',  
    stimulus: '<p>A2</p>',
    key_answer: 70,
    choices: [70,74],
    data: { response:'ll'},
    correct_text: '<p>Correct!</p>',
    incorrect_text: '<p>Incorrect!</p>',
    is_html: true,
        on_finish: function (trial_data){
      var correct = (trial_data.key_press == 70);
      jsPsych.data.addDataToLastTrial({correct: correct});
    }

  }


  var test_stimuli3 = { 
    type: 'categorize',  
    stimulus: '<p>B1</p>',
    key_answer: 70,
    choices: [70,74],
    data: { response:'ll'},
    correct_text: '<p>Correct!</p>',
    incorrect_text: '<p>Incorrect!</p>',
    is_html: true,
        on_finish: function (trial_data){
      var correct = (trial_data.key_press == 70);
      jsPsych.data.addDataToLastTrial({correct: correct});
    }

  }


  var test_stimuli4 = { 
    type: 'categorize',  
    stimulus: '<p>B2</p>',
    key_answer: 70,
    choices: [70,74],
    data: { response:'ll'},
    correct_text: '<p>Correct!</p>',
    incorrect_text: '<p>Incorrect!</p>',
    is_html: true,
    on_finish: function (trial_data){
      var correct = (trial_data.key_press == 70);
      jsPsych.data.addDataToLastTrial({correct: correct});
    }

  }


  var post_trial_gap = function() {
    return Math.floor( Math.random() * 1500 ) + 750;
  }

  var test_block = {
      timeline: [test_stimuli, test_stimuli2, test_stimuli3, test_stimuli4],
    randomize_order: true,
    
    loop_function: function(data){

        if(trial_count_correct < 8){
            return true;
        } else {
            return false;
        }
    }

  };

var testBlockArray = [test_block, test_block];

  /* define debrief block */
var trial_count_correct = 8


  function getSubjectData() {

    var trials = jsPsych.data.getTrialsOfType('categorize');

    var sum_rt = [0];
    var correct_trial_count = 0;
    var correct_rt_count = [0];
    for (var i = 0; i < trials.length; i++) {
      if (trials[i].correct == true) {
        correct_trial_count++;
        if(trials[i].rt > -1){
          sum_rt += trials[i].rt;
          correct_rt_count++;
        }
      }
    }
    return {
      rt: Math.floor(sum_rt / correct_rt_count),
      accuracy: Math.floor(correct_trial_count / trials.length * 100)
    }
  }


  var debrief_block = {
    type: "text",
    text: function() {
      var subject_data = getSubjectData();
      return "<p>You responded correctly on "+subject_data.accuracy+"% of "+
      "the trials.</p><p>Your average response time was <strong>" +
      subject_data.rt + "ms</strong>. Press any key to complete the "+
      "experiment. Thank you!</p>";

    }
  };

  /* create experiment timeline array */
var random_order = jsPsych.randomization.shuffle(testBlockArray);

  var timeline = [];
    timeline.push(welcome_block);
    timeline.push(instructions_block);
  for (var i = 0; i < random_order.length; i++){
    timeline = timeline.concat(testBlockArray[i]);
     }
    timeline.push(debrief_block);



  /* start the experiment */
  jsPsych.init({
    timeline: timeline,
    on_finish: function() {
      jsPsych.data.displayData();
    }

  });

</script>
</html>

Josh de Leeuw

unread,
Mar 10, 2017, 9:41:51 AM3/10/17
to Oliver Sokol, jsPsych
Hi, Oliver.

A couple pointers:

1) When using the categorize plugin, it will automatically record the "correct" variable for you, so you don't need the .addDataToLastTrial() bit.
2) You could declare a variable outside of a trial, var trial_count_correct = 0; Then inside the on_finish function for the categorize trials you can do:
on_finish: function(data){
  if(data.correct) { trial_count_correct++; }
}
In your loop function, trial_count_correct should be the correct value.
3) If you happen to be using the development version (GitHub master branch) and not v5.0.3, then you can also do this in your loop function:
loop_function: function(data){
  if(data.filter({correct: true}).count() > 8) { return true; } else { return false; }
}

Hope that solves it!
Josh

--
You received this message because you are subscribed to the Google Groups "jsPsych" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jspsych+u...@googlegroups.com.
To post to this group, send email to jsp...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jspsych/0881cc25-d33f-4177-9a57-5c72b79dffd3%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Oliver Sokol

unread,
Mar 13, 2017, 7:08:24 AM3/13/17
to jsPsych, oliver...@gmail.com
Hey!

This worked perfectly. Directed at point 3) that was the remainder of some code I was trying to make a solution. It is deleted now.

There is one thing you didn't answer though. Is there a way of skipping through a timeline that has been begun? So once 8 correct answers are achieved, to skip the rest of the timeline and begin a new block?

So, the loop function holds a timeline which is looped and randomized, if I achieve 8 correct responses by the time I am done halfway through the second loop, is there a method of skipping the remaining trials in the timeline?

Josh de Leeuw

unread,
Mar 15, 2017, 11:01:32 AM3/15/17
to Oliver Sokol, jsPsych
I think jsPsych.endCurrentTimeline() works, but I'm not 100% sure. It's one of those features that I added but hardly ever use and isn't part of the testing library yet.

Oliver Sokol

unread,
Mar 16, 2017, 4:40:00 AM3/16/17
to jsPsych, oliver...@gmail.com
Hello Josh, Thank you very much for continuing to help me out,
When using jsPsych.endCurrentTimeline() I receive the following error:

Uncaught TypeError: Cannot read property 'endActiveNode' of undefined
    at Object.core.endCurrentTimeline (VM351 jspsych.js:173)
    at WCST v3.html:1660

core.endCurrentTimeline @ VM351 jspsych.js:173

(anonymous) @ WCST v3.html:1660


Is there any way to fix this?

Oliver Sokol

unread,
Mar 17, 2017, 8:50:49 AM3/17/17
to jsPsych, oliver...@gmail.com
hey, so I decided to move from categorize to multi stim multi response trials. I've redone the code and everything works with 1 exception. I attempt to randomize the trials by making an array of trials then running it through the loop. The problem is that using a simple math.randomize() function only works for an initial random choice, but that choice is then repeated. So if I have 2 trials 1 of them gets selected through the randomization process, it will continue to repeat until 8 correct answers are given. I have tried using jsPsych.randomization.sample() but there is then an error for not finding type on trial node. Do you know how to solve either of the problems?

Here is the code currently being used it functions but the randomization only occurs once:

var rndmnr = Math.floor(Math.random()*2);
var W1= [
{
  type: "multi-stim-multi-response",
  stimuli: ['img/WCSTComp1.jpg','img/WCSTB1C.jpg'],
  choices: [[70, 74]],
  data:{ response:'C'},
  timing_response: 3000,
  timing_stim: [1000, -1],
  on_finish: function(data){
      var correct = false;
      if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;
        cntnr++;
      } else if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;
        cntnr++;
      }

      jsPsych.data.addDataToLastTrial({correct: correct});
    }
},
{
  type: "multi-stim-multi-response",
  stimuli: ['img/WCSTComp1.jpg','img/WCSTB2H.jpg',],
  choices: [[70, 74]],
  data:{ response:'C'},
  timing_response: 3000,
  timing_stim: [1000, -1],
  on_finish: function(data){
      var correct = false;
      if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;
        cntnr++;
      } else if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;
        cntnr++;
      }

      jsPsych.data.addDataToLastTrial({correct: correct});
    }
}

]


var container = WCSTB1C[rndmnr];

  var post_trial_gap = function() {
    return Math.floor( Math.random() * 1500 ) + 750;
  }

  var test_block = {
    timeline: [container],
    randomize_order: true,
    choices: [[70, 74]],
    loop_function: function(data){

        if(trial_count_correct < 8){
            return true;
            rndmnr = Math.floor(Math.random()*2);
        } else {
            return false;

        }
    },
  };


var testBlockArray = [test_block];

  /* define debrief block */
var trial_count_correct = 0;


  function getSubjectData() {

    var trials = jsPsych.data.getTrialsOfType('multi-stim-multi-response');

when a node including jsPsych.randomization.sample() is used to filter the W1 this is the error that pops up:
Trial level node is missing the "type" parameter. The parameters for the node are: {"0":{"type":"multi-stim-multi-response","stimuli":["img/WCSTComp1.jpg","img/WCSTB1C.jpg"],"choices":[[70,74]],"data":{"response":"C"},"timing_response":3000,"timing_stim":[1000,-1]},"choices":[[70,74]]}

now I tried to solve this by giving test_block a type multi stim multi response. This removed the above error but it now spits out the following error:
Uncaught TypeError: Cannot read property 'length' of undefined
    at Object.jsPsych.plugins.multi-stim-multi-response.plugin.trial (jspsych-multi-stim-multi-response.js:25)
    at doTrial (jspsych.js:577)
    at next_trial (jspsych.js:163)

Any help is much appreciated.

Josh de Leeuw

unread,
Mar 19, 2017, 11:21:00 AM3/19/17
to Oliver Sokol, jsPsych
I'm not sure I'm following your intent with this code. You've got:

var testBlockArray = [test_block];

Followed by:

var random_order = jsPsych.randomization.shuffle(testBlockArray);

So random_order will always be equivalent to testBlockArray, since you are shuffling an array with one element in it. Then it looks like random_order isn't referenced in the code below 

I am sure that I am just not understanding how you are trying to solve the problem or what exactly the problem is. Can you clarify?

Oliver Sokol

unread,
Mar 20, 2017, 7:46:09 AM3/20/17
to jsPsych, oliver...@gmail.com
Yes of course, apologies for the confusion.

I'll explain that I want participants to continuously do a set of trials until they perform correctly on 8 and then go on to the next block where a new set of trials. So the final idea is to have 16 trials under 1 block. and 4 blocks. Now initially I am trying to randomize the trials that are represented. Currently I am working to make the code functional so I only have 2 trials. To achieve the randomization of trials I put the trials into an array and attempted to call on the various items in this array with a random number generator through a looped node (test_block), hence this part of the code:

var rndmnr = Math.floor(Math.random()*2);

var W1 = [
{
  type: "multi-stim-multi-response",
  stimuli: ['img/WCSTComp1.jpg','img/WCSTB1C.jpg'],
  choices: [[70, 74]],
  data:{ response:'C'},
  timing_response: 3000,
  timing_stim: [1000, -1],
  on_finish: function(data){
      var correct = false;
      if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;

      } else if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;

      }

      jsPsych.data.addDataToLastTrial({correct: correct});
    }
},
{
  type: "multi-stim-multi-response",
  stimuli: ['img/WCSTComp1.jpg','img/WCSTB2H.jpg',],
  choices: [[70, 74]],
  data:{ response:'C'},
  timing_response: 3000,
  timing_stim: [1000, -1],
  on_finish: function(data){
      var correct = false;
      if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;

      } else if(data.response == 'C' && data.key_press == '[70]'){
        correct = true;
        trial_count_correct++;

      }

      jsPsych.data.addDataToLastTrial({correct: correct});
    }
}

var container = jsPsych.randomization.sample(WCSTB1C, 1);

  var post_trial_gap = function() {
    return Math.floor( Math.random() * 1500 ) + 750;
  }

  var test_block = {
    timeline: [W1[rndmnr]],
    randomize_order: true,
    choices: [[70, 74]],
    loop_function: function(data){

        if(trial_count_correct < 8){
            return true;
        } else {
            return false;

        }
    },
  };

So test_block is the block, the 2 trials are in W1 and the rndmnr variable is to generate a random number. Now this works fine, but it appears as though the random number is generated only once, meaning that all the trails within the block are the same trial (if the random number generated is 0 when the experiment is first started then all trials will just be a repeat of this first trial from the array. Until 8 correct responses are made and the block ends.) What I want instead is for a random trial from the array to be chosen each time the test_block node is looped. So you start the experiment, the trials start and are randomly selected from Array W1, then each time the test_block node is looped a different trial appears. This is not happening, as mentioned earlier, the first trial that occurs is repeated continuously. I have attempted to set rndmnr = 0; and then add a condition to each of the on_finish functions to produce another random number, but that does not solve the problem (only 1 trial is repeated). It seems as though once the test_block node is initiated it will continue to only use the first randomized number. This did not work, instead of using a random number generator I attempted to use jsPsych.randomization.sample(W1, 1). This produced the errors mentioned in my previous post.

Example of how I wanted the code to function:

Start experiment
first trial is randomized from the W1 array.
second trial is randomized from the W1 array.
randomized trials continue until 8 correct responses are made.
block ends.


The testBlockArray = [test_block]; is at this point unimportantand functions as it should, it will later contain several nodes similar to the test_block node. Once again though, this part of the code is unimportant, it is the above code that I am interested in fixing.

Josh de Leeuw

unread,
Mar 29, 2017, 10:41:30 PM3/29/17
to Oliver Sokol, jsPsych
Now I understand! I think what you need to do is repeat a single trial in the timeline, and use functions as parameters to randomly sample from the set of trial parameters. The random number will indeed only run once. The jsPsych timeline is generated at the start of the experiment, before any trials are run, which means that you can't randomly sample in this particular way. (The code on the master branch does include some new features for random sampling of trials, though.)

Here's a simplified version of what I'm suggesting:

var possible_stimuli = ['a.jpg', 'b.jpg', 'c.jpg'];

var trial = {
  type: 'single-stim',
  stimulus: function(){
    return jsPsych.randomization.sample(possible_stimuli, 1)[0];
  }
}

// add trial to a timeline, repeat as many times as needed. each time will be a new random sample from possible_stimuli.

--
You received this message because you are subscribed to the Google Groups "jsPsych" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jspsych+u...@googlegroups.com.
To post to this group, send email to jsp...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages