Table-generated trials

From one trial to a template

The last step in designing our experiment is to add trials so we can (i) manipulate the presence vs the absence of an -s on the verb, and (ii) have more than one data-point per participant.

We could copy and paste the PennController(...) defining our picture-selection trial, bringing changes to the copies to use other sentences and audio and image files. But then we would need to edit every copy whenever we want to add a change, such as adding a pause of a few milliseconds at the beginning of each trial for example.

A better solution is to identify the variable bits in your PennController(...) so you can use it as a template whose variables will be set to use values 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.

Now that we have identified our trial-by-trial variable strings, we can respectively replace them 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 4 picture-selection trials.

Table

When you imported the resources at the beginning of this tutorial, you updated chunk_includes with images and audio files, and also with a file named fulldesign.csv. This file is a table created using a spreadsheet editor and saved in a comma-separated-value (CSV) format (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 3rd 2600
2fishRoundTank.mp3 The fish swim in a tank which is perfectly round 2fishRoundTank.png 1fishSquareTank.png fish B 1st 2600
1deerDenseWood.mp3 The deer runs in a wood which is extremely dense 2deerSparseWood.png 1deerDenseWood.png deer B 3rd 2500
2deerSparseWood.mp3 The deer run in a wood which is extremely sparse 2deerSparseWood.png 1deerDenseWood.png deer A 1st 2500
1sheepRedPen.mp3 The sheep roams in a pen which is strikingly red 2sheepBluePen.png 1sheepRedPen.png sheep A 3rd 2200
2sheepBluePen.mp3 The sheep roam in a pen which is strikingly blue 2sheepBluePen.png 1sheepRedPen.png sheep B 1st 2200
1mooseNewPark.mp3 The moose walks in a park which is visibly new 2mooseOldPark.png 1mooseNewPark.png moose B 3rd 2200
2mooseOldPark.mp3 The moose walks in a park which is visibly old 2mooseOldPark.png 1mooseNewPark.png moose A 1st 2200

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

In this case, the table also contains a column named Group, which is a special column name: 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. 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.

Tracking trial’s information

Finally, we need the lines of our results file to indicate 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  )
)


Next: Delays & Randomization