Reply To: Choosing subet of items to present

PennController for IBEX Forums Support Choosing subet of items to present Reply To: Choosing subet of items to present

#6225
Jeremy
Keymaster

Hi Maria,

I’m not sure what is happening with the picture not disappearing from the page: when I test your code, the picture always disappears as it should. I could take a closer look if you sent me a link to your experiment at support@pcibex.net

The Selector element doesn’t quite work the way you’re using it, because PennController trials execute their script in a linear top-down order: your script runs play on the sg_voicing Audio element first, then it later runs play on the sg_devoicing Audio element, and only after that does it reach the shuffle command of the Selector element, but by then the audios have already played. As a result, all your trials will play sg_voicing first and sg_devoicing second.

Another important point to note is that using shuffle and then keys will result in a non-deterministic association order-wise. See the command’s documentation page for more detail.

What you can do however is test the index of the Selector’s elements after shuffling, and decide which to play first based on that, and also properly associate the keys depending on which is first and which is second. Here is a proposal (see notes below):

const alt_test_trial = variable => [
    newTimer(500)
        .start()
        .wait()
    ,
    newImage("pl_picture", variable.PlPictureFile)
        .size(300,200)
        .print()
    ,
    newTimer(200)
        .start()
        .wait()
    ,
    newAudio("pl_voicing", variable.PlVoicingFile)
        .play()
    ,
    newTimer(2000)
        .start()
        .wait()
    ,
    getImage("pl_picture")
        .remove()
    ,
    newImage("sg_picture", variable.SgPictureFile)
        .size(200,200)
        .print()
    ,
    newTimer(200)
        .start()
        .wait()
    ,
    newAudio("sg_voicing", variable.SgVoicingFile),
    newAudio("sg_devoicing", variable.SgDevoicingFile)
    ,
    newSelector("target")
        .log()
        .add( getAudio("sg_voicing"),getAudio("sg_devoicing") )
        .shuffle()
        .test.index( getAudio("sg_voicing"), 0 )
        .success(
            getAudio("sg_voicing").play().wait(),
            getAudio("sg_devoicing").play().wait(),
            getSelector("target").keys("1","2").wait()
        )
        .failure( 
            getAudio("sg_devoicing").play().wait(),
            getAudio("sg_voicing").play().wait(),
            getSelector("target").keys("2","1").wait()
        )
    ,
    newTimer(500)
        .start()
        .wait()
]

As you can see, I use test.index to see which audio occupies the Selector’s first slot after shuffling, and I accordingly play them in that order, and associate the 1 and 2 keys relative to that order too. Another thing you’ll note is that I use wait on the Audio elements rather than creating a Timer element of a given duration. Of course feel free to go back to the Timer method if that’s more appropriate to your design’s needs.

One other thing: your last two log commands should attach to the closing parenthesis of newTrial, so they cannot be part of those const definitions. Instead, they should go directly inside the Template command, between its closing parenthesis and that of newTrial (see example below)

Regarding the 75-25 split in your testing trials, you cannot assign them different labels for the same reason that you couldn’t assign different labels to your training and testing trials: you generate them all from the same rows, and the distribution of your trial types takes scope over all of them. If you were to break them into different labels (ie. different Template/newTrial commands) you would break the solution we’ve come up with to deal with the dependency between training and testing trials.

The solution I’ll propose here is similar to what we did for testing vs training: creating a global Var element to switch between types of trials. In this case, we’ll switch between singular target and plural target. In order to have 25% of the trials with a singular target and 75% of them with a plural target, we’ll use pick twice on the set of all the testing trials, once with a 25% proportion and once with a 75% proportion, and we’ll precede the trials from each subset with another trial switching to singular or plural target. Here is how to do that, illustrated with “alt” items only:

alt = randomize("alt")
picked_alt = pick(alt, 29)
repeat_alt = pick(randomize(picked_alt), 15) // 15 testing items shown in training
new_alt = pick(alt, 15) // 15 new trials in testing
testing_alt = randomize(seq(repeat_alt,new_alt)) // Merge and randomize the 30 test trials

Sequence( 
  "intro",
  rshuffle(picked_alt),
  "transition",
  rshuffle(
    randomize(seq( 
        // Pick 22/30 test trials (~75%) and precede with pl
        precedeEachWith("plTarget", pick(testing_alt, 22) ),
        // Pick 8/30 test trials (~25%) and precede with sg
        precedeEachWith("sgTarget", pick(testing_alt, 8) ) 
    ))
  )
)

// Minimal example showing how to test "target"
const alt_test_trial = variable => [ 
    getVar("target").test.is("pl")
        .success( newText("Plural").print() )
        .failure( newText("Singular").print() )
    ,
    newText("Test trial").print()
    ,
    newButton("Next").print().wait() 
]
// Minimal example showing the absence of "target" in training
const alt_training_trial = variable => [ 
    newText("Training trial").print()
    ,
    newButton("Next").print().wait() 
]

// Make sure to define "target" in intro
newTrial("intro", 
    newVar("phase", "training").global() ,
    newVar("target").global() ,
    newButton("Start").print().wait()
)

// Each trial sets "target" to the appropriate value
newTrial("plTarget", getVar("target").set("pl") )
newTrial("sgTarget", getVar("target").set("sg") )

newTrial("transition", getVar("phase").set("test") )

// This is a minimal example too
Template( row => 
  newTrial( "alt" ,
    getVar("phase").test.is("training")
        .success( ...alt_training_trial(row) )
        .failure( ...alt_test_trial(row) )
  )
  .log( "Item" , row.Item )
)

I must say your design is particularly challenging, with all those distributional requirements! Let me know if you have questions

Jeremy

  • This reply was modified 3 years, 5 months ago by Jeremy.