Reply To: Manipulating a PennController table?

PennController for IBEX Forums Support Manipulating a PennController table? Reply To: Manipulating a PennController table?

#5186
Jeremy
Keymaster

Hi Mieke,

Yes, you can definitely accomplish what you want with PennController, but I think you’d need to take a slightly different approach. Let me give you an example, starting with a table of this format (of course you can use a CSV file directly instead of using AddTable):

AddTable("myTable", `Item,Sentence,Group
0,tree lost its leaves,possessive
0,tree fell on itself,reflexive
1,pond had its water drained,possessive
1,pond replenished itself,reflexive
2,cloud kept its shape,possessive
2,cloud stood by itself,reflexive
3,rock had its top smoothed,possessive
3,rock spun on itself,reflexive
4,hill hid a cave at its top,possessive
4,hill hid on itself,reflexive`)

Here I have 5 trials, numbered from 0 to 4, which appear in one of two conditions (possessive vs reflexive) every other time the experiment is run, because of the Group column. Nothing crucial relies on this, it’s just for the sake of illustration.

Now I’ll define some variables in the script to configure the design you describe:

firstBlockLength = 2
secondBlockLength = 3
primes = "EU"
firstBlockQuantifiers = "EU"
secondBlockQuantifiers = "EUN"

I’m saying: there will be 2 trials in the first block and 3 trials in the second (you would replace those numbers with 30 and 24 instead). I’m also saying: each prime (regardless of block) will be either ‘E’ (for existential) or ‘U’ (for universal), and each in-sentence quantifier in the first block will be either ‘E’ (to be replaced with the determiner “A”) or ‘U’ (to be replaced with “Every”) and each in-sentence quantifier in the second block will be ‘E,’ ‘U’ or ‘N’ (to be replaced with “No”).

Note: the numbers in my example are not ideal, since the second block has 3 trials over which you cannot properly distribute 2 prime types—just make sure the number of trials in each block is a multiple of the number of prime types and the numbers of quantifiers in both trials; 24 and 30 both being multiples of both 2 and 3, you should be fine

Now here’s some javascript magic to automatically associate your trials with the desired conditions:

// Generates a random, even array of LENGTH characters picked from a string
randomArray = (length,list) => list.split('').map(v=>v.repeat(parseInt(length/list.length)))
                        .flat(1).sort(v=>Math.random()>=0.5)

listOfPrimesFirst = randomArray(firstBlockLength,primes)
listOfPrimesSecond = randomArray(secondBlockLength,primes)
listOfQuantifiersFirst = randomArray(firstBlockLength,firstBlockQuantifiers)
listOfQuantifiersSecond = randomArray(secondBlockLength,secondBlockQuantifiers)

translator = {E: "One", U: "Every", N: "No"}
// Randomly order the indices first
indices = [...new Array(5)].map((v,i)=>i).sort(v=>Math.random()>=0.5)
// Now assign properties to trials 
trials = []
listOfQuantifiersFirst.map( (v,i) => trials[indices[i]] = {
    block: "first",
    prime: translator[listOfPrimesFirst[i]], 
    quantifier: translator[v]
} )
listOfQuantifiersSecond.map( (v,i) => trials[indices[firstBlockLength+i]] = {
    block: "second", 
    prime: translator[listOfPrimesSecond[i]], 
    quantifier: translator[v]
} )

Once this is in place, you can use Template and GetTable().filter to generate trials:

Template(
    // Only use the rows where Item appears in the indices of the first block
    GetTable("myTable").filter( row=>indices.slice(0,firstBlockLength).indexOf(Number(row.Item))>=0 ) 
    ,
    row => newTrial( 'firstblock' ,
        newText("space","Please press Space").print(),
        newKey(" ").wait(),
        getText("space").remove()
        ,
        newText( "prime" , trials[row.Item].prime ).log().print(),
        newTimer(20).start().log().wait(),  // 20ms prime
        getText("prime").remove()
        ,
        newController("DashedSentence", {s: trials[row.Item].quantifier+' '+row.Sentence})
            .print().log().wait().remove()
    )
    .log( "Group" , row.Group )
    .log( "Block" , trials[row.Item].block )
    .log( "Sentence" , row.Sentence )
    .log( "Prime" , trials[row.Item].prime )
    .log( "Quantifier" , trials[row.Item].quantifier )
)


Template(
    // Only use the rows where Item appears in the indices of the second block
    GetTable("myTable").filter( row=>indices.slice(firstBlockLength,).indexOf(Number(row.Item))>=0 ) 
    ,
    row => newTrial( 'secondblock' ,
        newButton("Please click here").print().wait().remove()
        ,
        newText( "prime" , trials[row.Item].prime ).log().print(),
        newTimer(10).start().log().wait(),  // 10ms prime (more-or-less, hardware constraints)
        getText("prime").remove()
        ,
        newController("DashedSentence", {s: trials[row.Item].quantifier+' '+row.Sentence})
            .print().log().wait().remove()
    )
    .log( "Group" , row.Group )
    .log( "Block" , trials[row.Item].block )
    .log( "Sentence" , row.Sentence )
    .log( "Prime" , trials[row.Item].prime )
    .log( "Quantifier" , trials[row.Item].quantifier )
)

Let me know if you have questions about adapting this to your case

Jeremy