Previous page: Sample experiment: Priming Design
Next page: Data Collection & Data Analyses
On this page, we will see how to use PennController‘s functions to generate multiple template-based trials from a spreadsheet.
Preparation of the table
On the previous page, we ended up defining four items: 2 picture and 2 rating trials. Both items in each pair had exactly the same format, with only a few parameters varying from one item to the other.
Rather than manually copying the same code over and over, editing the variable parts each time (a method which is prone to fatal typos!) we will create a spreadsheet that associates each item with its variables, and use it to automatically take care of the copying for us by feeding a template.
Rating trials table
For the moment, we will only deal with the rating trials, to get a sense of how PennController handles the spreadsheet-template interface.
Using your favorite spreadsheet editor, create a table for the rating trials like this one:
Sentence |
---|
To me the color green is… |
To me the color purple is… |
Adding a table to the Ibex project
Save your spreadsheet as a CSV file (e.g. design.csv) and upload it to the folder chunk_includes of your Ibex project.
Generating trials with PennController.Template
We will now use the PennController.Template
function to generate Ibex items from the content of the spreadsheet. This is how we use it:
[js]PennController.ResetPrefix(null);
PennController.Template(
// ‘row’ will successively point to each row of the table (feel free to use another name)
row => PennController(
// row.Sentence will iteratively take the value of the column ‘Sentence’ for each row
newText( “sentence” , row.Sentence )
,
newScale(“judgment”, “cold”, “cool”, “lukewarm”, “warm”, “hot”)
.settings.labelsPosition(“top”)
.settings.before( getText(“sentence”) )
.settings.size(“auto”)
.print()
.wait()
)
);[/js]
As you can see, the template’s structure is identical to the rating items we defined before, but we replaced the content of the Text element with row.Sentence
. This is how it works: Template
goes through each row of the spreadsheet, fills row
(or whatever name you use before => PennController(
) with the values of the cells for the row it is currently inspecting, and generates an item using the template. Since our spreadsheet has two rows, it will use the template twice to generate two items, with row.Sentence
successively taking its value from the Sentence cell of the first and then the second row.
Extending the table
We could add as many rows to the spreadsheet in order to generate more rating trials, but now we would like to also generate picture trials using the spreadsheet.
Picture trials
We need to add three more columns to the spreadsheet for the picture trials, one (Type) to distinguish the rating rows from the picture rows, and two which will point to the images of each patch for a given trial. Let’s name these two last columns Color1 and Color2 (since we won’t use those columns for the rating trials, we’ll fill them with NAs for the rating rows). Now we can add new rows to the spreadsheet and use the columns to define picture-trial-specific parameters.
Type | Sentence | Color1 | Color2 |
---|---|---|---|
rating | To me the color green is… | NA | NA |
rating | To me the color purple is… | NA | NA |
picture | Which patch is greener? | green1.png | green2.png |
picture | Which patch is purpler? | purple1.png | purple2.png |
Updating the table from your Ibex project
Now that you have updated your spreadsheet, you also need to update the table you previously updated to your Ibex project. Once again, save your spreadsheet as a CSV file and go to your Ibex project. Under chunk_includes, locate the file you uploaded before and click on upload new version to its right, then select the new CSV file from your computer: your table is now up to date.
One spreadsheet, Two templates
We now need to define a new template based on the structure of the picture trials we scripted before, so we will call Template
a second time. But since we are using a single table for two different types of trial, we need to assign the rating rows to the rating template, and the picture rows to the picture template. We can do so by referring to the table in each call to Template
and by filtering the rows by looking at the text in their Type column.
Since your project only contains one table so far (your Ibex project has only one CSV file under chunk_includes) you can refer to it using the special keyword PennController.defaultTable
, and filter its rows using the method .filter
onto it, as follows:
[js]// Show the picture trials first (though we generate them second)
PennController.Sequence( randomize(“picture”) , randomize(“rating”) );
PennController.ResetPrefix(null);
// We don’t give the full URL in the spreadsheet
PennController.AddHost(“http://files.lab.florianschwarz.net/ibexfiles/PennController/SampleTrials/”);
// As before, we use Template to define a template
// But now, we also specify that we want to use a subset of the table:
// this template only uses the rows where Type is ‘rating’
PennController.Template( PennController.defaultTable.filter(“Type”,”rating”) ,
row => PennController( “rating” ,
newText( “sentence” , row.Sentence )
,
newScale(“judgment”, “cold”, “cool”, “lukewarm”, “warm”, “hot”)
.settings.labelsPosition(“top”)
.settings.before( getText(“sentence”) )
.settings.size(“auto”)
.print()
.wait()
)
);
// We use Template a second time to define a template for the ‘picture’ trials
// This template only uses the rows where Type is ‘picture’
PennController.Template( PennController.defaultTable.filter(“Type”,”picture”) ,
row => PennController( “picture” ,
defaultImage
.settings.size(200, 200)
,
newText(“test sentence”, row.Sentence)
.print()
,
newCanvas(“patches”, 500, 200)
.settings.add( 0, 0, newImage(“color1”, row.Color1) )
.settings.add( 300, 0, newImage(“color2”, row.Color2) )
.print()
,
newSelector(“patch”)
.settings.add( getImage(“color1”) , getImage(“color2”) )
.wait()
)
);
[/js]
Under the hood, this code generates exactly the same script from the page before: it generates 4 items in the native Ibex format for us. This way, we can manipulate the order of presentation of the items in the shuffleSequence
variable the same way we did before.
Within-item manipulation
Let’s say we want each of our picture trials to come in two versions: one version with the comparative as before, and one version with a less comparative instead. That is, each item now comes in two variants. We have to reflect this in our spreadsheet (we focus on the picture rows for the moment, throwing in two extra colors just for the fun of it!):
Type | Group | Sentence | Color1 | Color2 |
---|---|---|---|---|
picture | More | Which patch is greener? | green1.png | green2.png |
picture | Less | Which patch is less green? | green1.png | green2.png |
picture | More | Which patch is purpler? | purple1.png | purple2.png |
picture | Less | Which patch is less purple? | purple1.png | purple2.png |
picture | More | Which patch is beiger? | beige1.png | beige2.png |
picture | Less | Which patch is less beige? | beige1.png | beige2.png |
picture | More | Which patch is oranger? | orange1.png | orange2.png |
picture | Less | Which patch is less orange? | orange1.png | orange2.png |
We inserted one column, Group, and duplicated each picture row to reflect our group-design manipulation. The Group column defines different groups of participants: for any given participant, the template will generate items using either only the More rows, or only the Less rows.
Alternatively, we could have chosen to show one group of participants half the items in one variant, and half the items in the other variants—the other group of participants would see the complimentary half-half distribution. Let’s make one group group A and the other group group B. We simply cycle through the items by defining the Group column as shown below: participants from group A will see green and beige with the more comparative and purple and orange with the less comparative, participants from group B will see green and beige with the less comparative and purple and orange with the more comparative.
Type | Group | Sentence | Color1 | Color2 |
---|---|---|---|---|
picture | A | Which patch is greener? | green1.png | green2.png |
picture | B | Which patch is less green? | green1.png | green2.png |
picture | B | Which patch is purpler? | purple1.png | purple2.png |
picture | A | Which patch is less purple? | purple1.png | purple2.png |
picture | A | Which patch is beiger? | beige1.png | beige2.png |
picture | B | Which patch is less beige? | beige1.png | beige2.png |
picture | B | Which patch is oranger? | orange1.png | orange2.png |
picture | A | Which patch is less orange? | orange1.png | orange2.png |
Whichever manipulation you end up going with, PennController.Template automatically detects the presence of a Group column and generates group-specific trials accordingly, so you can keep the script from above exactly as it is.
Before you update your file though, you have to also duplicate the rating rows to match the Group assignations. Since we only want our manipulation to affect the picture items, we simply create duplicates of the rating rows where only the value in Group changes: this way, rating trials will appear the same for participants from either group. Below is a spreadsheet reflecting the former, non-latin-square group design:
Type | Group | Sentence | Color1 | Color2 |
---|---|---|---|---|
rating | More | To me the color green is… | NA | NA |
rating | Less | To me the color green is… | NA | NA |
rating | More | To me the color purple is… | NA | NA |
rating | Less | To me the color purple is… | NA | NA |
rating | More | To me the color beige is… | NA | NA |
rating | Less | To me the color beige is… | NA | NA |
rating | More | To me the color orange is… | NA | NA |
rating | Less | To me the color orange is… | NA | NA |
picture | More | Which patch is greener? | green1.png | green2.png |
picture | Less | Which patch is less green? | green1.png | green2.png |
picture | More | Which patch is purpler? | purple1.png | purple2.png |
picture | Less | Which patch is less purple? | purple1.png | purple2.png |
picture | More | Which patch is beiger? | beige1.png | beige2.png |
picture | Less | Which patch is less beige? | beige1.png | beige2.png |
picture | More | Which patch is oranger? | orange1.png | orange2.png |
picture | Less | Which patch is less orange? | orange1.png | orange2.png |
Github
In case you got lost on the way, you can sync the branch spreadsheet
(remember that syncing with this GitHub repository will erase whatever currently is in your Ibex project) of https://github.com/PennController/Tutorial.git
Assigning groups to participants
Ibex provides different methods to run the experiment in each group. If you simply click the link of your experiment, Ibex will use its internal counter to choose a group. By default, the counter is increased each time a participant complete the experiment. In your case, since your design defines two different groups, clicking the link will assign you to a different group depending on whether an even or an odd number of participants already completed the task. Feel free to try it yourself, completing and not completing the task at times, to see the effect.
If you want more control over group assignment, you can directly modify the URL of the experiment, by replacing the experiment.html
part at the end with server.py?withsquare=N
where N
is a number used to override the internal counter’s value. In this case, using server.py?withsquare=0
(even) or server.py?withsquare=1
(odd) will attribute different groups. This is helpful when you send the URL around for data collection but want to control which group your participants end up in.
Using two tables [OPTIONAL]
Please note that the next pages will build on the one-table script above: you will have to adapt those scripts accordingly if you want to use the two-table solution.
If you don’t like having to introduce NAs in your spreadsheet and duplicate rows for invariant items, you can split your spreadsheet in two: one spreadsheet for the rating items, and one spreadsheet for the picture items.
Rating
We can now keep only the Sentence column from this spreadsheet, falling back on the one-column format that we started with.
Sentence |
---|
To me the color green is… |
To me the color purple is… |
To me the color beige is… |
To me the color orange is… |
Picture
This spreadsheet keeps all but the Type column and only contains the picture rows.
Group | Sentence | Color1 | Color2 |
---|---|---|---|
More | Which patch is greener? | green1.png | green2.png |
Less | Which patch is less green? | green1.png | green2.png |
More | Which patch is purpler? | purple1.png | purple2.png |
Less | Which patch is less purple? | purple1.png | purple2.png |
More | Which patch is beiger? | beige1.png | beige2.png |
Less | Which patch is less beige? | beige1.png | beige2.png |
More | Which patch is oranger? | orange1.png | orange2.png |
Less | Which patch is less orange? | orange1.png | orange2.png |
In your Ibex project
Just repeat the procedure from the section Adding a table to the Ibex project (make sure you upload both CSV files as separate files—i.e. do not click upload new version after you upload the first of the two CSV files).
Summary
So now you should have more than one CSV file under chunk_includes. PennController.defaultTable
points to the table in the file whose name comes first in the alpha-numerical order. In your case, you have two templates in your script that you each want to use with a specific CSV file. So instead of using PennController.defaultTable
, you will use PennController.GetTable( "filename.csv" )
, replacing filename.csv with the filename of the appropriate CSV file for its respective template.
Your script should look like this (with possibly different names instead of rating-table.csv
and picture-table.csv
for your tables—you may also have felt free to choose to use a different name for the row
variable):
[js]PennController.Sequence( randomize(“picture”) , randomize(“rating”) );
PennController.ResetPrefix(null);
PennController.AddHost(“http://files.lab.florianschwarz.net/ibexfiles/PennController/SampleTrials/”);
// No need to filter: rating-table.csv only contains what we need for this template
PennController.Template( PennController.GetTable(“ratingfull.csv”) ,
row => PennController( “rating” ,
newText( “green” , row.Sentence )
,
newScale(“judgment”, “cold”, “cool”, “lukewarm”, “warm”, “hot”)
.settings.labelsPosition(“top”)
.settings.before( getText(“green”) )
.settings.size(“auto”)
.print()
.wait()
)
);
// No need to filter: rating-picture.csv only contains what we need for this template
PennController.Template( PennController.GetTable(“picturefull.csv”) ,
row => PennController( “picture” ,
defaultImage
.settings.size(200, 200)
,
newText(“test sentence”, row.Sentence)
.print()
,
newCanvas(“patches”, 500, 200)
.settings.add( 0, 0, newImage(“color1”, row.Color1) )
.settings.add( 300, 0, newImage(“color2”, row.Color2) )
.print()
,
newSelector(“patch”)
.settings.add( getImage(“color1”) , getImage(“color2”) )
.wait()
)
);
[/js]
As you may have noticed, your mixed-item-type table is no longer useful, so feel free to delete its file from your Ibex project if you feel so inclined.
Note on PennController.defaultTable and PennController.GetTable
When we introduced Template
, we did not refer to any table. We did not need to do so, because we only used one table and one template. Under the hood, when you do not specify which table you want to use, Template
uses the table referred to by PennController.defaultTable
. This keyword always picks the table whose name comes first in the alpha-numerical order: if you only have one CSV file, it will necessarily refer to its table, but if you have at least two CSV files, say rating.csv and picture.csv, PennController.defaultTable
will refer to picture.csv, because p comes before r in the alpha-numerical order.
Next page: Data Collection & Data Analyses