Trial templates & tables

Multi-trial experiments using templates

Most experiments consist of multiple trials, typically varying levels of a factor, e.g., whether or not the inflectional morpheme -s is present or not in our example experiment. So the last step in designing our experiment is to create an entire set of trials.

We could copy and paste the PennController(...) defining our picture-selection trial, making changes along the way to display other sentences and using different audio and image files for the different experimental items. But then we would need to edit every copy whenever we want to change small aspects of the trial structure, e.g., adding a pause of some number of milliseconds at the beginning of each trial. This is cumbersome and seems unnecessary, since the various trials will usually have the exact same structure, apart from some key ingredients.

A more sensible approach is to define a template for the general trial structure and to identify the variable bits in your PennController(...), which can then be supplied from a table.
In our case, there are four strings that will change on a trial-by-trial basis:

  • "2fishRoundTank.mp3" in newAudio("description", "2fishRoundTank.mp3"),
  • "The fish swim in a tank which is perfectly round" in newText("The fish swim in a tank which is perfectly round"),
  • "2fishRoundTank.png" in newImage("two", "2fishRoundTank.png") and
  • "1fishSquareTank.png" in newImage("one", "1fishSquareTank.png").

Everything else will be constant from one trial to the other.So we can respectively replace these bits with variable.AudioFile, variable.Description, variable.PluralImageFile and variable.SingularImageFile, and embed PennController(...) within PennController.Template( variable => ... ) to transform it into a template:

your script should also still contain the welcome trial above this

PennController.Template( 
  variable => PennController(
    newAudio("description", variable.AudioFile)
        .play()
    ,
    newText(variable.Description)
        .unfold(2600)
    ,
    newImage("two", variable.PluralImageFile)
        .settings.size(200,200)
    ,
    newImage("one", variable.SingularImageFile)
        .settings.size(200,200)
    ,
    newCanvas(450,200)
        .settings.add(   0 , 0 , getImage("two") )
        .settings.add( 250 , 0 , getImage("one") )
        .print()
    ,
    newSelector()
        .settings.add( getImage("two") , getImage("one") )
        .settings.keys(          "F"    ,          "J"   )
        .settings.log()
        .wait()
    ,
    getAudio("description")
       .wait("first")
  )
  .log( "ID" , getVar("ID") )
)

Save your script and test your experiment. You should now see a series of 4 picture-selection trials. The values for the variables for these trials are supplied by a table, whose structure we turn to next.

Table

Importing the resources at the beginning of this tutorial from github uploaded various files (such as the image and audio files we already used) to the Resources section of the main project page. This also included a file named fulldesign.csv. This file is a table created using a spreadsheet editor and saved in a comma-separated-value (CSV) format (i.e., same format as the results file). Below is a rendering of the table:

you might need to scroll to the right to see all the columns
AudioFile Description PluralImageFile SingularImageFile Item Group Ending Duration
1fishSquareTank.mp3 The fish swims in a tank which is perfectly square 2fishRoundTank.png 1fishSquareTank.png fish A -s 2600
2fishRoundTank.mp3 The fish swim in a tank which is perfectly round 2fishRoundTank.png 1fishSquareTank.png fish B No-s 2600
1deerDenseWood.mp3 The deer runs in a wood which is extremely dense 2deerSparseWood.png 1deerDenseWood.png deer B -s 2500
2deerSparseWood.mp3 The deer run in a wood which is extremely sparse 2deerSparseWood.png 1deerDenseWood.png deer A No-s 2500
1sheepRedPen.mp3 The sheep roams in a pen which is strikingly red 2sheepBluePen.png 1sheepRedPen.png sheep A -s 2200
2sheepBluePen.mp3 The sheep roam in a pen which is strikingly blue 2sheepBluePen.png 1sheepRedPen.png sheep B No-s 2200
1mooseNewPark.mp3 The moose walks in a park which is visibly new 2mooseOldPark.png 1mooseNewPark.png moose B -s 2200
2mooseOldPark.mp3 The moose walks in a park which is visibly old 2mooseOldPark.png 1mooseNewPark.png moose A No-s 2200

The template we created looks for columns with corresponding names in the CSV table based on the specified variables, such as variable.AudioFile, (e.g., AudioFile, Description, PluralImageFile and SingularImageFile) and successively uses the values in each row to generate as many trials.

There are two additional columns not corresponding to variables in our template: Group and Ending.
We commonly want a given participant to only see a subset of the various versions of our table, and the Group column controls this: PennController automatically detects it and will generate trials only using the rows with one value in Group, in our case either the A or the B rows every other time the experiment runs (for more on how the group value is chosen on a given run of the experiment, see the full documentation). As a result, even though the table contains 8 rows besides the header row, when you test the experiment you only see 4 trials, either generated from rows 2, 5, 6 and 9 (highlighted in the table above) or generated from rows 3, 4, 7 and 8.

The Ending column specifies the property of our sentences that we care about, i.e., the manipulation of our independent variable, here whether or not the verb ends in the inflectional morpheme -s.

Tracking trial details

In order to keep track of all the details for a given trial, we need the lines of our results file to indicate things like what item they correspond to, what condition (presence vs absence of -s) they correspond to and what group (A vs B) they correspond to. In other words, we want to report the values of the table’s Item, Ending and Group columns to the lines in our results file. We simply use the same .log command we used before, referring to the table’s columns using the variable. method:

PennController.Template( 
  variable => PennController(
    newAudio("description", variable.AudioFile)
        .play()
    ,
    newText(variable.Description)
        .unfold(2600)
    ,
    newImage("two", variable.PluralImageFile)
        .settings.size(200,200)
    ,
    newImage("one", variable.SingularImageFile)
        .settings.size(200,200)
    ,
    newCanvas(450,200)
        .settings.add(   0 , 0 , getImage("two") )
        .settings.add( 250 , 0 , getImage("one") )
        .print()
    ,
    newSelector()
        .settings.add( getImage("two") , getImage("one") )
        .settings.keys(          "F"    ,          "J"   )
        .settings.log()
        .wait()
    ,
    getAudio("description")
       .wait("first")
  )
  .log( "ID"     , getVar("ID")    )
  .log( "Item"   , variable.Item   )
  .log( "Ending" , variable.Ending )
  .log( "Group"  , variable.Group  )
)

Save and take the experiment, and you’ll see all the relevant information listed in the lines for each trial in the results file.


Next: Timers & Randomization