Choosing subet of items to present

PennController for IBEX Forums Support Choosing subet of items to present

This topic contains 9 replies, has 3 voices, and was last updated by Avatar samsanto 1 week, 4 days ago.

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #4890
    Avatar
    mg5171
    Participant

    Hi,
    I’m wondering if there’s a way to choose a random subset of items to present, and to mix in fillers and stim items.

    1. Random subset of items: I’d like to present all of the items labeled ‘stim’, but a random subset of 50 labeled ‘filler’. I currently have two blocks, one of which presents all of the ‘stim’ items, and another which presents all of the ‘filler’ items, like this:

    Template( 
        PennController.defaultTable.filter("type", "stim"), //choose only stimuli items
        
        variable => 
    
    newTrial("experiment",

    I have an analogous one for ‘filler’ items–but I want to tell it to choose a random subset of e.g. 50, instead of presenting all of them.

    2. If I can manage that, then I have 2 blocks, one with stim and one with fillers. I know about the randomize and shuffle commands that go with Sequence() at the beginning. Is there some combination of those I can use to randomize both stim and fillers, and then present them all in a random order?

    Thanks,
    Maddie

    • This topic was modified 4 months, 2 weeks ago by Avatar mg5171.
    #4892
    Jeremy
    Jeremy
    Keymaster

    Hi Maddie

    1. I can see a couple ways to do this, but you’ll need some plain javascript anyway. Here’s a suggestion, assuming you have an Item column in your table that goes from 0 to, say, 100 for your filler items:

    numberRows = 100;
    numberDraws = 50;
    randomIDs = [...new Array(numberRows)].map((v,i)=>i).sort(v=>Math.random()>0.5).splice(0,numberDraws);
    
    Template( defaultTable.filter( r => r.type=="filler" && randomIDs.indexOf(Number(r.Item))>-1 ),
      variable => newTrial(
        // ...
    

    2. I think what you are looking for is anyOf, you can use it like this: Sequence( "intro" , randomize(anyOf("experiment","filler")) )

    #4896
    Avatar
    mg5171
    Participant

    Thanks so much Jeremy!

    #5144
    Avatar
    mg5171
    Participant

    To follow up on this, I have the following. The items have a column called ‘number’ and are labeled 1-90. This seems to be giving me the first 45 only, but in a randomized order (which is also probably because I’m randomizing with the Sequence command at the beginning. Any ideas why it’s giving me the first 45 instead of a random 45 out of the total 90?

    //fillers
    
    numberRows_fillers = 90; 
    numberDraws_fillers = 45; 
    randomIDs_fillers = [...new Array(numberRows_fillers)].map((v,i)=>i).sort(v=>Math.random()>0.5).splice(1,numberDraws_fillers); 
    
    Template( defaultTable.filter( r => r.type=="filler" && randomIDs_fillers.indexOf(Number(r.number))>-1 ),
        variable =>
    
    #5146
    Jeremy
    Jeremy
    Keymaster

    Just to double-check: your table defines 90 filler items whose ‘number’ label goes from 1 to 90, right?

    When I test it I do get items whose number is over 45, but they indeed seem massively underrepresented. It seems to improve a bit when changing the > comparison when randomly sorting the indices for a >= comparison

    Here’s my full script if you want to compare with yours:

    PennController.ResetPrefix(null)
    
    let myTable = "type,number,text\n"
    
    for (let i = 0; i < 90; i++)
        myTable += "filler,"+Number(i+1)+",textfiller"+i+"\ntest,"+Number(i+1)+",texttest"+i+"\n";
    
    AddTable("myTable", myTable)
    
    Sequence( randomize(anyOf(startsWith("test"),startsWith("filler"))) )
    
    numberRows_fillers = 90; 
    numberDraws_fillers = 45; 
    randomIDs_fillers = [...new Array(numberRows_fillers)].map((v,i)=>i).sort(v=>Math.random()>=0.5).splice(1,numberDraws_fillers); 
    
    Template( GetTable("myTable").filter( r => r.type=="filler" && randomIDs_fillers.indexOf(Number(r.number))>-1 ),
        row => newTrial( "filler-"+row.number , newText("filler").print(),newButton(row.text).print().wait() )
    )
    
    Template( GetTable("myTable").filter( r => r.type=="test" && randomIDs_fillers.indexOf(Number(r.number))>-1 ),
        row => newTrial( "test-"+row.number , newText("test").print(),newButton(row.text).print().wait() )
    )

    Jeremy

    #5852
    Avatar
    samsanto
    Participant

    Hi Jeremy,

    Jumping in here because this is pretty similar to something I am trying to accomplish.

    I have an experiment with 12 blocks but the stimuli for each block are randomly generated from a master list (csv) for each participant. Essentially, one block consists of displaying 12 random “train” words and 12 random “test” words from this master list. Right now I have a file with three columns (Item, Type, Word) where item is just an index, type is Train v. Test, and Word is the actual stimulus.

    I need to generate 24 random subsets of my master list- so 12 of the “Train” words, and 12 “Test” words- without replacement (the main issue) so I guarantee that each item is only seen once, and cycle through them to display the words.

    I appreciate your help and any thoughts you have!

    Thanks,
    Sam

    #5853
    Jeremy
    Jeremy
    Keymaster

    Hi Sam,

    There probably are more optimal implementations, but how about this:

    const NBLOCKS = 24;
    const NITEMSPERBLOCK = 12;
    
    const blocks = {};
    const addItem = type => {
        if (!blocks.hasOwnProperty(type))
            blocks[type] = [...new Array(NBLOCKS)].map((v,i)=>Object({id:i, n:0}));
        let freeblocks = blocks[type].filter(v=>v.n<NITEMSPERBLOCK);
        if (!freeblocks.length) return console.error("# of items in table does not match config");
        let r = Math.floor(Math.random()*freeblocks.length);
        freeblocks[r].n++;
        return type+"-"+freeblocks[r].id;
    }
    
    Sequence(
        rshuffle("Test-0","Train-0"),"pause",
        rshuffle("Test-1","Train-1"),"pause",
        rshuffle("Test-2","Train-2"),"pause",
        rshuffle("Test-3","Train-3"),"pause",
        rshuffle("Test-4","Train-4"),"pause",
        rshuffle("Test-5","Train-5"),"pause",
        rshuffle("Test-6","Train-6"),"pause",
        rshuffle("Test-7","Train-7"),"pause",
        rshuffle("Test-8","Train-8"),"pause",
        rshuffle("Test-9","Train-9"),"pause",
        rshuffle("Test-10","Train-10"),"pause",
        rshuffle("Test-11","Train-11"),"pause",
        rshuffle("Test-12","Train-12"),"pause",
        rshuffle("Test-13","Train-13"),"pause",
        rshuffle("Test-14","Train-14"),"pause",
        rshuffle("Test-15","Train-15"),"pause",
        rshuffle("Test-16","Train-16"),"pause",
        rshuffle("Test-17","Train-17"),"pause",
        rshuffle("Test-18","Train-18"),"pause",
        rshuffle("Test-19","Train-19"),"pause",
        rshuffle("Test-20","Train-20"),"pause",
        rshuffle("Test-21","Train-21"),"pause",
        rshuffle("Test-22","Train-22"),"pause",
        rshuffle("Test-23","Train-23")
    )
    
    newTrial("pause", newButton("Take a break").print().wait() )
    
    Template( "myTable" , row =>
        newTrial( addItem(row.Type) ,
            newButton(row.Word).print().wait()
        )
    )

    This will randomly assign each of the items from your table to one of 24 (NBLOCKS) type-specific blocks of 12 (NITEMSPERBLOCK) items each. You can then write a Sequence where you randomly shuffle the items in those randomly-generated blocks, and separate each block by a pause trial.

    Jeremy

    #5862
    Avatar
    samsanto
    Participant

    Hi Jeremy,

    Thanks so much! This works pretty much how I need, I did have to switch around a couple things. Mainly I want all the “Train” to go first in a block then all the “Test” so I took out the rshuffle(…) but that was easy.

    Now I’m having a problem where I want to do slightly different key presses depending on if its a “train” or “test.” For the train words, I just display the word, for the test words, I ask want to get user input. Based on how we set it up, each block is (hopefully) guaranteed to have 12 words in it so I am keeping a global counter variable that just switches off every 12 words the action (see below). However, this isn’t working. The counter is not actually incrementing and it is stuck on the “failure” option, any idea why?

    Also if you have a better way to implement this, let me know (I feel like its too “hardcoded” if you know what I mean…)
    Thank you for the continued help, I really appreciate it!!

    
    PennController.Sequence(
        "Learn-0","Test-0","pause",
        "Learn-1","Test-1","pause",
        "Learn-2","Test-2","pause",
        "Learn-3","Test-3","pause",
        "Learn-4","Test-4","pause",
        "Learn-5","Test-5","pause",
        "Learn-6","Test-6","pause",
        "Learn-7","Test-7","pause",
        "Learn-8","Test-8","pause",
        "Learn-9","Test-9","pause",
        "Learn-10","Test-10","pause",
        "Learn-11","Test-11","pause"
    );
    PennController.ResetPrefix(null);
    
    newVar("count")
        .set(0)
        .global()
    ;
    
    PennController("pause", newButton("Take a break").print().wait() );
    
    PennController.Template( "statTableRandom.csv" , row =>
        newTrial( addItem(row.Type) ,
        
            getVar("count").test.is(12)
                .success(
                    getVar("count").set(0)
                    ,
                    newText(row.Word).print()
                    ,
                    newKey("yn").wait()
                )
                .failure(
                    getVar("count").set(v=>v+1)
                    ,
                    newText(row.Word).print()
                    ,
                    newKey(" ").wait()
                )
        )
    );
    

    -Sam

    #5863
    Jeremy
    Jeremy
    Keymaster

    Hi Sam,

    PennController elements are not meant to be used outside a newTrial command. Just check the value of the Type column to pass the appropriate parameter to your newKey command:

    Template( "statTableRandom.csv" , row =>
      newTrial( addItem(row.Type) ,
        newText(row.Word).print()
        ,
        newKey( (row.Type=="Train"?" ":"yn") ).wait()  
      )
    )

    Jeremy

    #5864
    Avatar
    samsanto
    Participant

    Oh wow, duh! Forgot I had that… for that exact point.

    Thanks so much!!

Viewing 10 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic.