PennController for IBEX › Forums › Support › Switching between multiple tasks
Tagged: flanker, multiple tasks, visual world
- This topic has 7 replies, 2 voices, and was last updated 2 years, 7 months ago by Jeremy.
-
AuthorPosts
-
May 2, 2022 at 5:54 pm #8140vlangloisParticipant
Hello,
I’m coding up an experiment that requires participants to switch between a flanker task and a sentence task (see demo for details). I have successfully programmed both in, but I want the trials for each task intermixed within the experiment.
For example, the following combinations could be:
Trial 1&2: Flanker task followed by sentence task
Trial 3&4: Sentence task followed by sentence task
Trial 5&6: Flanker task followed by flanker task
Trial 7&8: Sentence task followed by flanker taskI thought about incorporating one design template for each of these four combinations, but I probably would run into issues in regards to order (whether that is randomizing or pseudo-randomizing by hand the different combinations). What would be the best way to intermix these trials, if there is a way?
Here is the code that I have for the two tasks. Currently, the flanker task always follows the sentence task. https://farm.pcibex.net/r/RgVUnM/
Thank you!
May 3, 2022 at 9:53 am #8144JeremyKeymasterHello,
In the demo that you shared, there is a single
newTrial
which includes both task types. It is my understanding from what you said (that two tasks of the same type can follow each other) that you want to decouple the two, ie. not have them inside a singlenewTrial
command. That would also mean no longer associating specific sentences with specific sets of flanker images the way your table is currently set up. The most convenient way to start on that would be to use two different CSV tables for each task typeThen you can create different
newTrial
s inside differentTemplate
commands, giving different labels to trials of each task type. Once you have such labeled trials, you can define a custom predicate function to shuffle your trials as you’d like, for example:const popAelseB = (a,b) => a.pop()||b.pop() function LatinMix(a, b) { this.args = [a,b]; this.run = function(arrays) { assert(arrays.length == 2, "Wrong number of arguments (or bad argument) to LatinMix"); let as = arrays[0]; let bs = arrays[1]; let newArray = []; let i = 0; while (as.length || bs.length){ newArray.push( [ i%2 ? popAelseB(as,bs) : popAelseB(bs,as), (i+1)%4<2 ? popAelseB(as,bs) : popAelseB(bs,as) ] ); i++; } fisherYates(newArray); return newArray.flat(); } } function latinMix(a, b) { return new LatinMix(a, b); }
You can use the function in
Sequence
like this:Sequence( latinMix(randomize("sentence"),randomize("flanker")) ) Template("flanker.csv", row => newTrial("flanker", /* ... */ ) ) Template("sentence.csv", row => newTrial("sentence", /* ... */ ) )
Jeremy
May 3, 2022 at 1:20 pm #8146vlangloisParticipantThanks Jeremy! If I wanted to control which flanker task image precedes the sentence task, would this be hard to incorporate? In this case, would I have to keep the template with both tasks in it?
E.g.
Sequence( latinMix(randomize("flanker-sentence"), randomize("sentence"),randomize("flanker")) ) Template("flanker.csv", row => newTrial("flanker", /* ... */ ) ) Template("sentence.csv", row => newTrial("sentence", /* ... */ ) ) Template("flanker-sentence.csv", row => newTrial("flanker-sentence", /* ... */ ) )
May 3, 2022 at 3:37 pm #8147JeremyKeymasterHi,
I thought you wanted the sentence vs flanker tasks to be totally independent, because you stated that half of your items need to form
sentence-sentence
orflanker-flanker
subsequences. I’m not sure how you would achieve that with a table where each line has references for both a sentence and a flanker task: it would seem to me that such a table would systematically pair a sentence task with a flanker task, regardless of whether the former precedes or follows the latter, but that it wouldn’t really allow forsentence-sentence
orflanker-flanker
pairs. Or am I missing something?The
latinMix
function I proposed accepts only two arguments (seeassert(arrays.length == 2, "Wrong number of arguments (or bad argument) to LatinMix");
) so you will get an error if you trySequence( latinMix(randomize("flanker-sentence"), randomize("sentence"),randomize("flanker")) )
. I also don’t know what output exactly you would expect to get from that, since in your first message you listed four types of pairs resulting from crossing two types of tasks, and this is exactly whatlatinMix
doesTo reiterate, I don’t know how one could modify the code I suggested, or come up with another implementation, because I don’t know how to determine the
sentence-sentence
andflanker-flanker
pairs if you need to systematically associate onesentence
with oneflanker
task (ie. how would you decide which two sentences or which flanker images to associate to form pairs?)Note that if all you want really are
sentence-flanker
andflanker-sentence
pairs, then once you put all those pairs one after the other in a sequence of trials, you will for example get a general sequence likesentence-flanker-flanker-sentence-flanker-sentence-sentence-flanker-
etc., which does technically contains bothsentence-sentence
andflanker-flanker
subsequences, because sometimes you’ll have a pair that starts with one task type following another pair that ends with that same task type. If that’s what you’re after, then all you need to do is alternate which bit of code comes first in your existingnewTrial
, eg:// Flanker task flanker = row => [ newTimer("RT",1000).start() , newCanvas("FlankerCanvas", 1800, 900) .add(675,400, newImage(row.FlankerImage)) .scaling("page") .print("center at 50vw","middle at 50vh") , newKey("flankerFJ", "FJ") .log("first") .callback(getTimer("RT").stop()) , getTimer("RT").wait() , getKey("flankerFJ").disable() , getCanvas("FlankerCanvas").remove() ] // Sentence task sentence = row => [ newAudio("audio", row.AudioFile).play() , defaultImage.size(225, 225) , newImage("TL", row.TL) , newImage("TR", row.TR) , newImage("BL", row.BL) , newImage("BR", row.BR) , newCanvas("visualWorld", 1800, 900) .add( "left at 50px" , "top at 50px" , getImage("TL")) .add( "left at 50px" , "bottom at 850px" , getImage("BL")) .add( "right at 1700px" , "top at 50px" , getImage("TR")) .add( "right at 1700px" , "bottom at 850px" , getImage("BR")) .scaling("page") .print("center at 50vw","middle at 50vh") , getAudio("audio").wait("first") , newSelector().add(getImage("TL"), getImage("BL"), getImage("TR"), getImage("BR")) .wait() .log() , getCanvas("visualWorld").remove() , newText("comp-question", row.Question) .cssContainer({"font-size": "160%", "color": "black"}) .center() .print() , newText("yesno", "<p>YES NO</p>") .cssContainer({"font-size": "160%", "color": "black"}) .center() .print() , newKey("SentenceFJ", "FJ").log("first").wait().disable() , getText("comp-question").remove() , getText("yesno").remove() ] ABorBAs = [] // Browse the table once to fill ABorBAs with alternating 0s and 1s Template("flanker-sentence.csv", row => newTrial("__dummy__", ABorBAs.push(ABorBAs.length%2)) ) Template("flanker-sentence.csv", row => newTrial( "test" , fisherYates(ABorBAs) // Shuffle ABorBAs , ABorBA = ABorBAs.pop() // Pick the last entry from ABorBAs , newCanvas("FixationCanvas", 1800, 900) .add(675,400, newImage("Fixation.png")) .scaling("page") .print("center at 50vw","middle at 50vh") , newTimer("wait", 1000).start().wait() , clear() , ...(ABorBA?flanker(row):sentence(row)) // Flanker if 1, sentence otherwise , getTimer("wait").start().wait() , getCanvas("FixationCanvas").print("center at 50vw","middle at 50vh") , getTimer("wait").start().wait() , clear() , ...(ABorBA?sentence(row):flanker(row)) // Sentence if 1, flanker otherwise ) .log("flankerFirst", ABorBA) .setOption("hideProgressBar",true) ) Sequence( randomize("test") )
Jeremy
May 3, 2022 at 4:30 pm #8148vlangloisParticipantSorry for the confusion! I was hoping that I could control which flanker image (i.e. the direction of the middle arrow) would precede the sentence task, but I didn’t mention this in the original post. I believe that I can do that with a template that combines the two tasks, and I also think your second solution in your recent post should work, which will probably be the way to go.
For the latinMix function, does this mean there is no way to modify it to intermix between three different tasks?
Thanks for helping me out with everything!
May 3, 2022 at 4:44 pm #8149JeremyKeymasterThere is something that still confuses me when you say “which flanker image (i.e. the direction of the middle arrow) would precede the sentence task,” because the flanker image does not (immediately) precede any sentence in a
flanker-flanker
pair, and the sentence is not (immediately) preceded by any flanker image in asentence-sentence
pair. You say that my second solution should work, so I presume that you do not wantsentence-sentence
orflanker-flanker
pairs in the end?The
latinMix
function takes two sets of labeled trials, say “A” and “B,” and outputs a sequence of pairs of trials evenly distributed overA-B
,B-A
,A-A
andB-B
. For example, if you have four A trials and four B trials, you could getA(1)-B(4)-A(3)-A(2)-B(1)-A(4)-B(3)-B(2)
. I hard-coded the function to output such a crossing from strictly two labels, but it could in theory be modified to accommodate any number of labels. For example, with three labelsA
,B
andC
, a generalized function would output an even distribution of triples of trials overA-B-C
,A-C-B
,B-A-C
,B-C-A
,C-A-B
,C-B-A
,A-A-A
,A-A-B
,A-A-C
,A-B-B
,A-C-C
,B-A-A
,B-B-A
,B-B-B
,B-B-C
,B-B-B
,C-A-A
,C-B-B
,C-C-A
,C-C-B
andC-C-C
. Is that what you want?Jeremy
May 3, 2022 at 5:04 pm #8150vlangloisParticipantRight, only for the
flanker-sentence
combinations would the preceding flanker image matter. This would be the only combination that matters since it is made up of only experimental items. Then, the rest are fillers. Therefore, I think to make things simpler, it would be easier to have the latinMix function take three labels,flanker-sentence
,flanker
, andsentence
. It would also cover the remaining combinations, though sometimes there might be 3 of the same tasks in a row, which shouldn’t be a problem.Thank you again! Sorry I didn’t explain this experiment very well from the beginning.
Val
May 3, 2022 at 6:59 pm #8151JeremyKeymasterSo let me summarize and try to clarify the idea to make sure we’re on the same page
If you were to use a generalized version of
latinMix
with three labels (say,flanker-sentence
,flanker
andsentence
) you would need to create three types of trials: one type containing only the sentence task (labeledsentence
), one containing only the flanker task (labeledflanker
) and one containing both the flanker and then the sentence tasks (labeledflanker-sentence
). You say that only the latter would correspond to experimental items with a preset flanker-sentence association. This suggests to me that for those (flanker-sentence
, mixed-type trials) you would need a table where each line has references for both the sentence and the flanker tasks, whereas for the former two (sentence
andflanker
, homogeneous-type trials) you would need two tables, one that only contains references for the reference task and one that only contains references for the flanker taskNow, say you have 21 rows in the flanker-sentence table, 21 rows in the sentence table and 21 rows in the flanker table. You would get a total of 42 iterations of the flanker task in your final experiment (half from the mixed trials labeled
flanker-sentence
and half from the homogeneous trials labeledflanker
) and a total of 42 iterations of the sentence task (same logic). I use those numbers because the number of combinations that a generalized version oflatinMix
would output for three labels is 21. Some of the triplets would contain only three task iterations (mixed or not) but some would contain four iterations, some five and one would even contain six (the one that draws exclusively fromflanker-sentence
, namelyflanker-sentence-flanker-sentence-flanker-sentence
). Obviously you would end up with an over-representation of theflanker-sentence
subsequence, because we started with 21 rows in each table but one of them is used to generateflanker-sentence
while the other two generate onlyflanker
or onlysentence
. As you can see this all becomes very complicated very quicklyNow that I have a better idea of what you want, I do not think that using any version of
latinMix
will make your life simpler. I think what would help is determine how many experimental items you will have, and how many of those will use each type of flanker image (as I understand it, you have four possible flanker images). Then, you would need to determine how many iterations of each task you want in the final experiment, and whether/how you want to balance your design. For example, if you have 8 experimental trials (2 where the sentence is preceded by LL.png, 2 by LR.png, 2 by RL.png and 2 by RR.png) do you also want to have 8 corresponding filler trials? That would give you 16 trials where the sentence is preceded by a flanker task. Would you want to then have another 16 filler trials where the sentence is *followed* by a flanker image (4 with LL.png, 4 with LR.png, 4 with RL.png and 4 with RR.png)? Finally, how many more independent iterations of each task type do you want to mix in? And do you want to license sequences that would end the experiment on a flanker task?The situation would be a little simpler (although not much) if, instead of randomly generating the fillers during runtime, you defined a pseudo-randomized set of filler items in your tables to compensate your experimental items, as you would simply reference all the sentences and images by hand and the
newTrial
s would be pretty genericRe-using the
flanker
andsentence
functions I defined in my previous message (and assuming a ‘yes’ answer where it applies to all the questions I raise above) you could write a single function to generate each type of trials without having to repeat whole pieces of code, eg:trialOfType = (type,row) => newTrial(type, newCanvas("FixationCanvas", 1800, 900) .add(675,400, newImage("Fixation.png")) .scaling("page") .print("center at 50vw","middle at 50vh") , newTimer("wait", 1000).start().wait() , clear() , ...( type=="flanker-sentence"||type=="flanker" ? flanker(row) : sentence(row) ) , ...( type.match('-') ? [ getTimer("wait").start().wait() , getCanvas("FixationCanvas").print("center at 50vw","middle at 50vh") , getTimer("wait").start().wait() , clear() , ...( type=="flanker-sentence" ? sentence(row) : flanker(row) ) ] : [ null ] ) ) .setOption("hideProgressBar",true) Template("flanker-sentence_experimental.csv", row => trialOfType("flanker-sentence", row)) Template("flanker-sentence_filler.csv", row => trialOfType("flanker-sentence", row)) Template("sentence-flanker.csv", row => trialOfType("sentence-flanker", row)) Template("flanker.csv", row => trialOfType("flanker", row)) Template("sentence.csv", row => trialOfType("sentence", row)) Sequence( rshuffle("flanker-sentence", "sentence-flanker", "flanker", "sentence") )
Jeremy
-
AuthorPosts
- You must be logged in to reply to this topic.