Allocating participant and condition numbers

How can participant IDs be allocated to participants in an online experiment?

Back to GET - externally allocated IDs

Remember that last week we talked about the HTTP GET method, which allows us to pass parameters along to web pages. The example given was:

http://example.com/page?colour=red&size=3

What if it looked like this:

http://example.com/experiment.html?participant=12

We can use this method to pass a participant number to jsPsych, and it has a very convenient way of receiving it.

Make another copy of the latest version of your experiment and call it id_in_url.

Now right at the top of experiment.js, add this code:

var participant_id = jsPsych.data.getURLVariable('participant');
jsPsych.data.addProperties({participant: participant_id});

In your web browser, load up the new copy of the experiment. In your browser, at the end of the URL, add a participant number as above. So it will look something like:

http://jspsychlearning.ppls.ed.ac.uk/~UUN/allocator/experiment.html?participant=123

Remember that UUN is your University username, for a student this will look something like s1234567.

Complete the experiment. When you look at the data, you should see a new column called “participant”, equal to the number that you entered.

Where should participant numbers come from?

There are two possibilities here. One is that the participant will be sent from some external service (e.g. Amazon MTurk or Crowdflower, or a Qualtrics survey) and there will already be an ID in the URL. In this case, you need to record the ID as above.

We can also create our own small program on the server which allocates participant IDs. Make another copy of your experiment (without the id_in_url changes). Rename this directory to allocator. We’ll modify this to use the PHP ID allocator. Open a new file called participant_id_allocator.php and copy this code into it:

<?php
$username = explode("/", dirname(__FILE__))[2];
$server_data = '/home/'.$username.'/server_data';
$partid_file = $server_data.'/partid_example.txt';

$partid = file_get_contents($partid_file);
if ($partid == FALSE) {
    $partid = 0;
} else {
    $partid = (int)$partid;
}
$partid += 1;

if (substr(realpath(dirname($partid_file)), 0, strlen($server_data))!=$server_data) {
    error_log("attempt to write to bad path: ".$partid_file);
} else {
    file_put_contents($partid_file, (string)$partid);
}
printf('%d', $partid); // send ID!
?>

This code will:

  • Try to read a file in server_data called participant_id.txt

  • If the file doesn’t exist, set the variable $id to zero

  • If the file does exist, open it and interpret the contents as a whole number

  • Add one to $id

  • Write $id to participant_id.txt

  • Send $id back – so this will be sent back to our experiment code where we can record the ID.

The file participant_id.txt should always contain the last ID that was used. Every time this is accessed (and the program runs), it will increase the ID by 1, and forward to the experiment with the new ID.

You should be able to go to the URL for this code on the server, something like:

http://jspsychlearning.ppls.ed.ac.uk/~UUN/allocator/participant_id_allocator.php

This will send back a participant ID and you’ll see it on the page. Every time you load or reload the page, the ID should increase by 1.

Finally, since participant_id.txt always contains the next participant number, you can edit it to change the next participant ID, or delete it to start again at 1.

Adding this to an experiment

Add the call-function plugin to your HTML file (see https://www.jspsych.org/v8/plugins/call-function/).

At the top of the experiment add:

var participant_id;

Then add a new node to your experiment which will call participant_id_allocator.php and get a new participant ID.

var get_participant_id = {
    type: jsPsychCallFunction,
    async: true,
    func: function (done) {
        fetch("participant_id_allocator.php")
            .then(function (response) {return response.text();})
            .then(function (response_text) {return parseInt(response_text);})
            .then(function (result) {
                participant_id = result;
                // condition number is the remainder of participant_id divided by 3
                condition_number = participant_id % 3;
                // if remainder is 0 change to 3 (so the conditions will be 1,2,3)
                if (condition_number == 0) {
                    condition_number = 3;
                }
                // record this in our results
                jsPsych.data.addProperties({condition: condition_number});
            })
            .then(done())

    }
}

Better data filenames

At the moment the data are still being written to a file called “test.csv”. Let’s fix that!

In the part of the code where we save the data, we can add the participant number to the filename.

Look for code like this:

on_finish: function() {
    var experiment_data = jsPsych.data.get();
    save_data("test.csv", experiment_data.csv());
    // show results as well - take this out when doing real testing!
    jsPsych.data.displayData("csv");
}

and change it to this:

on_finish: function(){
    var experiment_data = jsPsych.data.get();
    save_data(participant_id+"_data.csv", experiment_data.csv());
    // show results as well - take this out when doing real testing!
    jsPsych.data.displayData("csv");
}

This adds the participant ID to the filename, so that they will be called 1_data.csv, 2_data.csv, and so on.

We could also add the date to the filename. It’s better to do this on the server, as the participant’s computer may have the date wrong. In save_data.php, change the line:

$path = $server_data."/".$obj["filename"];

to this:

$path = $server_data."/".date("Y-m-d")."_".$obj["filename"];

This adds the date (according to the server clock) to the start of the filename, plus an underscore character _ to separate this date from the rest of the name.

Run your experiment again (starting from the ID allocator) and you should see a new file in server_data with a filename something like this:

2018-02-11_3_data.csv

Condition number

When running an experiment, it’s common to want to counterbalance participants between several conditions. The usual way to do this is allocate them in order. For example, for three conditions:

Participant ID

Condition

1

1

2

2

3

3

4

1

5

2

6

3

7

1

etc.

etc.

We can easily allocate conditions using JavaScript modulus, %.

The code:

var x = y % z;

gives the remainder when y is divided by z.

In our example:

Participant ID

Remainder (ID % 3)

1

1

2

2

3

0

4

1

5

2

6

0

7

1

etc.

etc.

Let’s add this to the experiment. At the top of experiment.js, add this:

var condition_number;

Then in the call-function node where we get the participant number, replace this line:

.then(function (result) {participant_id = result;})

with this:

.then(function (result) {
    participant_id = result;
    // condition number is the remainder of participant_id divided by 3
    condition_number = participant_id % 3;
    // if remainder is 0 change to 3 (so the conditions will be 1,2,3)
    if (condition_number == 0) {
        condition_number = 3;
    }
})

Run your experiment and check that the condition number appears in the output.

Example

See this example of getting a participant number from the server, calculating a condition number, and changing the data filename.