PennController for IBEX › Forums › Support › SepWithN + random item from template
- This topic has 1 reply, 1 voice, and was last updated 1 year, 1 month ago by bta.
-
AuthorPosts
-
October 14, 2023 at 11:02 am #10890btaParticipant
I am trying to do some fancy work with my
Sequence
in my experiment, and there are essentially two parts to what I’m trying to do, and I can’t seem to put them together to do what I actually want.First, I’m trying to take a certain number of trials (e.g., 8) and presented them in a random order, and then have those randomly presented trials be separated at a fixed interval with a “take a break” style trial. I know that this part of the work can be done by: (i) putting the 8 trials in a
Template
, (ii) randomizing the order withrandomize(NAMEOFTRIAL)
, and (iii) using a function likesepWithN
(as documented in several places on these forums, e.g., [this post](https://www.pcibex.net/forums/topic/how-to-add-page-breaks-in-between-randomized-items/#post-8514)).Second, I’m trying to have the “take a break” trial be one of a certain number of trials (e.g., 4), where each of the breaks is a random (and different) break trial. To achieve this, I put the 4 break items into a csv and created a Template of trials, and I tried using the
randomProportion
function (as defined in [this post](https://groups.google.com/g/ibexexperiments/c/ap4qV9TUJ3Q/m/OOU7pQBtAwAJ)) to show only one of the 4 items in the csv after 2 stimuli.Using these ideas together, I defined the sequence through
Sequence( sepWithN(randomProportion("theSeparator", .25), randomize("theStimuli"), 2) )
. **The problem is** that this shows the same random trial from the Template fortheSeparator
for all 4 separator trials (when I would hope to have each separator trial use a different trial fortheSeparator
).For example, if my stimuli are
[abc, def, ghi, jkl, mno, pqrs, tuv, wxyz]
and my separators are[separatorA, separatorB, separatorB, separatorD]
, I am getting outputs likeghi, mno, separatorB, abc, wxyz, separatorB, def, jkl, separatorB, tuv, pqrs, separatorB
. Instead, what I would like is an output likeghi, mno, separatorA, abc, wxyz, separatorD, def, jkl, separatorB, tuv, pqrs, separatorC
.Anyone have any thoughts on how to get the desired output? I’ve created a demo experiment at this URL: [https://farm.pcibex.net/r/gdHZAR/](https://farm.pcibex.net/r/gdHZAR/).
October 21, 2023 at 2:56 pm #10916btaParticipantI have figured out a solution, which I’m sharing below (I’ve put this code in a .js file that I added under “modules”).
I use it in my
Sequence()
as inSequence( sepChunkWithChunk("theSeparator", 1, "theStimuli", 2) )
, which does what I described in my previous post (*except that it doesn’t add a separator after the final stimulus, which I did on purpose*).// this code below lets you put something like sepChunkWithChunk("separators", 1, "stimuli", 2) into the Sequence // this works so that, if you have a Trial (in a Template) named "separators" and a Trial (in a Template) named "stimuli", … // …it will present trials from the "stimuli" in chunks of 2, with each chunk separated by 1 trial from "separators" // (NOTE: there will be a "separators" chunk between chunks of "stimuli" — meaning if there are 4 chunks of "stimuli", only 3 "separators" chunks will be alreadyUsed) function SepChunkWithChunk(sep, nSep, main, nMain) { assert(typeof(nSep)=="number" && nSep>0, "nSep should be a number greater than 0"); assert(typeof(nMain)=="number" && nMain>0, "nMain should be a number greater than 0"); this.args = [sep, main]; this.run = function(arrays) { var alreadyUsedSep = []; var alreadyUsedMain = []; let sep = arrays[0]; let main = arrays[1]; var numChunksForMain = Math.ceil(main.length / nMain); // calculates the number of chunks of 'main' are necessary to show all items from 'main' var totalNumUsedFromMain = 0; // counter for tallying number of items used from 'main' var chunkedAndSeparatedArray = []; // this array will house the order of all trials, randomized into chunks of 'main' separated by chunks of 'sep' for (let i = 1; i <= numChunksForMain; ++i) { // run this loop as many times as there are chunks of 'main' counterMain = 0; counterSep = 0; // create a chunk with 'nMain' elements by looping 'nMain' times // STOP LOOPING if it's already pulled out all trials from 'main' // (this will happen if, e.g., you have 8 trials in 'main' and 'nMain' is set to something like 5 — two chunks are needed, but the second chunk should only have 3 elements) while (counterMain < nMain && totalNumUsedFromMain < main.length) { // generate a random number between 0 and (main.length-1): r = Math.floor(Math.random()*main.length); // keep generating a new random number until the random number generated hasn't been used before // (this has the effect of blocking the repetition of items from 'main' that have already been added to 'chunkedAndSeparatedArray') while (alreadyUsedMain.includes(r)) { r = Math.floor(Math.random()*main.length); } // now that a unique random number has been generated, add it to the array of random numbers that have been used alreadyUsedMain.push(r); // add the rTH trial from 'main' to 'chunkedAndSeparatedArray' chunkedAndSeparatedArray.push(main[r]); // increase counters ++counterMain; ++totalNumUsedFromMain; } // create a chunk with 'nSep' elements by looping 'nSep' times // DON'T RUN THIS LOOP if the last chunk of 'main' has just been added to 'chunkedAndSeparatedArray' if (i != (numChunksForMain)){ while (counterSep < nSep) { // generate a random number between 0 and (sep.length-1): r = Math.floor(Math.random()*sep.length); // keep generating a new random number until the random number generated hasn't been used before // UNLESS all the trials from 'sep' have already been used // (this has the effect of allowing separator trials to be re-used just in case the number of separators needed is greater than the number of unique trials in 'sep') while (alreadyUsedSep.includes(r) && sep.length > alreadyUsedSep.length ) { r = Math.floor(Math.random()*sep.length); } // now that a unique random number has been generated, add it to the array of random numbers that have been used alreadyUsedSep.push(r); // add the rTH trial from 'sep' to 'chunkedAndSeparatedArray' chunkedAndSeparatedArray.push(sep[r]); // increase counter ++counterSep; } } } // return the finalized Array, which will have all the trials of 'main' in a raondomized order // moreover, between every chunk of 'nMain' trials, there is a chunk of random 'nSep' trials from 'sep' return chunkedAndSeparatedArray; } } function sepChunkWithChunk(sep, nSep, main, nMain) { return new SepChunkWithChunk(sep, nSep, main, nMain); }
-
AuthorPosts
- You must be logged in to reply to this topic.