PennController for IBEX › Forums › Support › Trial judgement and present unequal fillers
- This topic has 6 replies, 2 voices, and was last updated 1 year, 9 months ago by Jeremy.
-
AuthorPosts
-
October 16, 2022 at 5:05 am #9573xkyanParticipant
Hi Jeremy,
I’m implementing an experiment and was stuck there in designing a template. There are three points unsolved and I appreciate it a lot if you can give some suggestions.
1. I have two kinds of trials as my target, one is keyresponding to the arrow’s direction and the other one is a self-paced reading of a sentence. I hope they can show as (pic, spr) pair and each pair is regarded as a target. Currently I implemented them in NewTrial. But I think this way is not flexible. I’m wondering if there’s anything like a function so that I can put the right actions in a function for each kind?
2. The other question is about unequal number of fillers. I got 20 chances to present 40 fillers. I’m curious if it is possible to generate a list of 20 numbers between 1 to 3 that sum up to 40 and present corresponding number of fillers every chance?
3. Is there a good way to control the order of presentation in target, filler, target, filler, …?
This is the basic part I have finished (https://farm.pcibex.net/r/IqekEL/). All suggestions about solving the mentioned problems are welcome. Thank you in advance.
October 16, 2022 at 5:43 pm #9575JeremyKeymasterHi,
1. I’m not sure I understand: what’s wrong with the way you’re currently coding the design? (other than that you might want to
remove
the Canvas element instead of just the Image element, so that the Controller elements do not appear so far down on the page)If you find it easier to code the two tasks that constitute a trial separately, you can always create two functions that each return an array of PennController commands, and call those functions inside the
newTrial
so the commands are executing sequentially, eg:const pic = row => [ newImage("pic", row.pic_name).size(720, 405).print() , newCanvas("canvas_pic",720,405) .add( 0, 0, getImage("pic")) .center() .print() , newTimer("displaytime", 2000).start() , newKey("resp-flanker", "FJ") .log() .callback( getTimer("displaytime").stop() ) , getTimer("displaytime").wait() , getCanvas("canvas_pic").remove() ]; const spr = row => [ newController("DashedSentence", {s: row.sent_cont}) .print() .log() .wait() .remove() , newController("Question", {q: "Does it make sense?", as: ["Yes", "不合理"]}) .print() .log() .wait() .remove() ]; Template("items.csv", row => newTrial("experimental-trial", ...pic(row) , ...spr(row) ) )
2. Do you mean that between each target trial (ie. pic+spr pair) you want to have 1-to-3 filler trials? And that you would have 20 occurrences of those filler breaks, and you want the total of filler trials to sum up to 40?
3. This will depend on the answer to question 2. If you have, say, 40 trials labeled “target” and 40 trials labeled “filler”, then
rshuffle("target","filler")
would give you just that: a series of 80 trials alternating between trials labeled “target” and trials labeled “filler”On the other hand, if you have 20 target trials and 40 filler trials, and want to insert 1-to-3 filler trials after each target trial, which is how I understand your second point, then you could define this function to generate an array of random integers that sum up to a set value:
const nRandomIntsToSum = (length,minInt,maxInt,targetSum) => { const a = []; for (let i=0; i<length; i++) { let min = minInt, max = maxInt, remainder = targetSum-(a.reduce((x,y)=>x+y,0)+min*(length-(i+1))); if (remainder < maxInt) max = remainder; a.push( min+Math.round(Math.random()*(max-min)) ); } fisherYates(a); return a; }
Then you can use the custom
pick
function in 20 iterations, like this:targets = randomize("target") fillers = randomize("filler") repetitions = nRandomIntsToSum(20,1,3,40) Sequence( "instructions", ...repetitions.map(n=>[ pick(targets,1),pick(fillers,n) ]).flat() )
Jeremy
October 16, 2022 at 7:06 pm #9578xkyanParticipantHi Jeremy,
Thank you a lot for the time and patience helping me figure out.
For 1, yes there’s no problem with the current version, and the easier one you posted is exactly the one I’m looking for!
For 2 and 3, sorry I didn’t explain it clearly but yes you understood it right. Those two functions really help a lot and did the things I want them to do.
Again, thank you so much for the help!
October 19, 2022 at 2:34 pm #9590xkyanParticipantHi Jeremy,
Sorry it’s me again. When I tried to test the presentation of fillers, I encountered a problem that “Please wait while the resources are preloading. This may take up to 1min.” But I’ve waited for more than 15 mins and it still didn’t finish. I’m wondering if there’s sth wrong with my codes, or it was caused by some other problems (e.g. network connection, etc.).
Basically I just want to test if two kinds of fillers can be detected and presented in the right format. Here’s my code:
Sequence(randomize("fillers")) // Define the template for filler trials Template("items-fillers.csv", row => newTrial("fillers", // newVar("RT").global().set("0"), // newVar("TFPress").global().set(false), newVar("kind", row.condition) , // pic(row) getVar("kind") .test.is("flanker") .success( pic(row) ) .failure( spr(row) ) ) )
Thanks a lot in advance!
October 19, 2022 at 7:08 pm #9597JeremyKeymasterHi,
You cannot use PennController’s
test
commands to conditionally include an Image element: you are effectively creating an Image element for your filler items too, which don’t have any value forflan_file
, resulting in invalid Image elementsIn the script you currently have in your project, your sentence and flanker filler trials actually have nothing in common (as things stand, they do not define pic-spr pairs: there is no pic-spr sequence of commands, unlike in my code above for example; your code currently either includes
pic
commands orspr
commands in a trial) so if that’s the kind of design you are implementing, I would just split the CSV file in two tables and correspondingly use twoTemplate
commands to generate the filler trials (and also oneTemplate
for your target trials, since they all seem to be SPR’s):Template("flanker-fillers.csv", newTrial("fillers", ...pic(row) ) Template("sentence-fillers.csv", newTrial("fillers", ...spr(row) ) Template("items-target.csv", newTrial("targets", ...spr(row) )
Jeremy
November 26, 2022 at 1:54 am #9737xkyanParticipantHi Jeremy,
Thanks for the previous answers! Here’s a follow-up:
I tried to reorganize my materials and correct my codes as following. But the Sequence controller did not work (my guess) so I can’t present trials in the order I want.
PennController.ResetPrefix(null) // DebugOff() // function ---------------------------------- const nRandomIntsToSum = (length,minInt,maxInt,targetSum) => { const a = []; for (let i=0; i<length; i++) { let min = minInt, max = maxInt, remainder = targetSum-(a.reduce((x,y)=>x+y,0)+min*(length-(i+1))); if (remainder < maxInt) max = remainder; a.push( min+Math.round(Math.random()*(max-min)) ); } fisherYates(a); return a; } const pic = row => [ // set a fixation newText("fixation1", "+") .css("font-size","50px") .print("center at 50%" , "center at 50%") .log() , newTimer("fixtime1",500).start().wait() , getText("fixation1").remove() , // set the picture stimuli newImage("pic", row.flan_file).size(720, 405).print() , newCanvas("canvas_pic",720,405) .add( 0, 0, getImage("pic")) .center().print() , // start recording RT getVar("RT1").global().set( v => Date.now() ) , // set key response newKey("flan_RespKey", "FJ") .log("flan_RespKey", getKey("flan_RespKey")) .wait() .callback( getVar("RT1").set( v => Date.now() - v ) .log( "flanRT" , getVar("RT1") ) ) , // record RT getVar("RT1").set( v => Date.now() - v ) .log( "flanRT" , getVar("RT1") ) , // clear the screen getCanvas("canvas_pic").remove() ]; const spr = row => [ // set a fixation newText("fixation2", "+") .css("font-size","50px") .print("center at 50%" , "center at 50%") .log() , newTimer("fixtime2",500).start().wait() , getText("fixation2").remove() , // set the spr stimuli newController("DashedSentence", {s: row.sent_cont}) .print() .log() .wait() .remove() , // start recording RT getVar("RT2").global().set( v => Date.now() ) , newController("Question", {q: "这句话合理吗?", as: [["F","合理"], ["J","不合理"]], hasCorrect: parseInt(row.sentCorr), randomOrder: false}) .print() .log() .wait() .remove() , // record RT getVar("RT2").set( v => Date.now() - v ) .log( "sentRT" , getVar("RT2") ) ]; // Main -------------------------- // Instructions newTrial("instructions", defaultText .cssContainer({"margin-bottom":"1em"}) .center() .print() , newText("instructions-1", "Welcome!") , newText("instructions-2", "In this experiment, you will hear and read a sentence, and see two images.") , newText("instructions-3", "<b>Select the image that better matches the sentence:</b>") , newText("instructions-4", "Press the <b>F</b> key to select the image on the left.<br>Press the <b>J</b> key to select the image on the right.<br>You can also click on an image to select it.") , newTextInput("input_ID") .cssContainer({"margin-bottom":"1em"}) .center() .print() , newButton("wait", "Click to start the experiment") .center() .print() .wait() , newVar("ID") .global() .set(getTextInput("input_ID")) ) // Define the template for target trials (完成,没问题) Template("items-targets.csv", row => newTrial("targets", newVar("RT1").global().set("0"), newVar("RT2").global().set("0"), pic(row) , spr(row) ) ) // Define the template for filler trials Template("items-fillers-flan.csv", row => newTrial("fillers", pic(row) ) ) Template("items-fillers-spr.csv", row => newTrial("fillers", spr(row) ) ) targets = randomize("targets") fillers = randomize("fillers") repetitions = nRandomIntsToSum(60,1,3,120) Sequence( "instructions", repetitions.map(n=>[ pick(targets,1),pick(fillers,n) ]).flat() ) // Sequence("instructions", randomize("targets")) // Sequence("instructions", "targets")
However, I found that when I ran it, it would go over with the Template order I coded (i.e. all targets in the sequential order as in the csv file, then the filler-flan, then the filler-spr) and the Sequence controller seems not work at all. Here are some other observations I hope to share with you while attempting to debug:
1. I tried to change the order of Template and the presented order during experiment changed correspondingly, which proved that the Sequence controller indeed did not work and it went with the order of the Template. It makes sense after checking the doc because I found that the function of Template is generating trials so the trials are presented once they are generated.
2. I tried to change the Sequence to see which part of Sequence can work. For example, I triedSequence("instructions", "fillers", randomize("targets"))
and it worked! So I guess there might be something wrong with my current Sequence design so it failed to work and therefore the experiment just went with the sequential order of Templates. But after checking.map
and.flat
, I still didn’t find where the bug is in this line.Could you please help me figure out why it fails? I’ve been stuck here for quite a long time which is frustrating 🙁 I really appreciate your help! Thanks in advance.
November 29, 2022 at 6:55 pm #9749JeremyKeymasterHi,
The project at https://farm.pcibex.net/r/IqekEL/ does not define the function
pick
. Also, you forgot...
beforerepetitions
inSequence
Once I fix both issues, the experiment runs with a mix of target and filler trials, as intended
Jeremy
-
AuthorPosts
- You must be logged in to reply to this topic.