Questionnaire with questions on the same page

PennController for IBEX Forums Support Questionnaire with questions on the same page

Viewing 15 posts - 1 through 15 (of 23 total)
  • Author
    Posts
  • #5434
    ReaToth
    Participant

    Hello,

    I am trying to design a research which would include a questionnaire, however, I have difficulties displaying all the questions on the same page. I am using scale as a measurement for responses while also using a table to import the resources.
    Here is my current code:

    Template("wbsi.csv", variable =>
    newTrial("WBSI",
        newText(variable.Number1, variable.Question1)
            .print()
        ,
        newScale("Rating", "Strongly Disagree",  "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree")
            .log()
            .labelsPosition("top")  
            .before( getText(variable.Number1) )
            .size("auto")
            .print()
            .wait()
        ,
        newButton("next", "Next")
            .print()
            .wait()
    ))

    However, this code displayed the questions one by one on a separate page. I would like to have all the questions displayed simultaneously on the same page with the ‘Next’ button on the bottom of the page.
    I have managed to make it work when I am not using a table, but it would make the code extremely long if I were to type in all the questions separately. Here is the code for this:

    newTrial("WBSI",
        newText("1", "how do you feel about cheating?")
        ,
        newScale("Rating", "Strongly Disagree",  "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree")
            .log()
            .labelsPosition("top")  
            .before( getText("1") )
            .size("auto")
            .print()
        ,
        newText("2", "how do you feel about skipping class?")
        ,
        newScale("Rating", "Strongly Disagree",  "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree")
            .log()
            .labelsPosition("top")  
            .before( getText("2") )
            .size("auto")
            .print()
        ,
        newButton("next", "Next")
            .print()
            .wait()
            
    )

    Thank you for your help in advance,
    Andrea

    #5435
    Jeremy
    Keymaster

    Hello Andrea,

    Since you basically want to repeat the same series of commands multiple times, you can create a function that takes your number/question as an input and outputs the commands, like this:

    question = (number,text) => [
        newText("Question-"+number, text)
        ,
        newScale("Rating-"+number, "Strongly Disagree",  "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree")
            .log()
            .labelsPosition("top")  
            .before( getText("Question-"+number) )
            .size("auto")
            .print()
    ]

    Once this is in place, you can use it like this:

    Template("wbsi.csv", variable =>
      newTrial("WBSI",
        ...question(variable.Number1, variable.Question1)
        ,
        ...question(variable.Number2, variable.Question2)
        ,
        ...question(variable.Number3, variable.Question3)
        ,
        newButton("next", "Next")
            .print()
            .wait()
      )
    )

    Jeremy

    #5465
    ReaToth
    Participant

    Hello Jeremy,

    Thank you for your quick response. Unfortunately, the code doesn’t seem to work for me. I have try to change it but couldn’t manage to figure it out. I think it has to do with my table. I have 2 columns: ‘Number’ and ‘Question’. The rows of ‘Number’ column goes from 1 to 15 while the rows of ‘Question’ column has the actual questions. In the Debug, the error message comes up: ‘No column named ‘Question3’ found in table wbsi.csv’. And the same for Question2, and Number1 and Number2.
    Apologies if the answer is obvious, but I have been trying with different codes since yesterday with no luck.

    Thank you for your help in advance,
    Andrea

    #5468
    Jeremy
    Keymaster

    Hello Andrea,

    I wrote my message under the assumption that you had multiple Number* and Question* columns in your table, and that for each row of your table, you wanted to print a new form containing as many questions as you have columns in your table.

    I suspect that it was a misunderstanding though, and that what you want to do is create a single form using multiple rows from your table, which contains a single Number and a single Question columns. There is no straightforward way of doing exactly this using PennController’s table+Template method, as it was designed to create multiple trials for your experiment, all based on the same template but filling the variable values by looking up a table.

    If all you have in your wbsi.csv table is a list of 15 rows with a Number column going from 1 to 15, and a Question column listing your different questions, you could do this instead:

    1. Create a file named wbsiQuestions.js in your Controllers folder and set a variable there to contain an array of your questions, like this (only two questions in this example—add the other ones, and don’t forget the line-ending commas):

    var wbsiQuestions = [
      "How do you feel about cheating?",
      "How do you feel about skipping class?"
    ];

    2. Keep the definition of the question function above in your main script file, and do this:

    newTrial("WBSI",
        ...wbsiQuestions.map( (v,i) => question(i,v) )
        ,
        newButton("next", "Next")
            .print()
            .wait()
    )

    Jeremy

    #5539
    ReaToth
    Participant

    Hello,

    Thank you for your help, it’s working perfectly now.

    Best,
    Andrea

    #5559
    daniela
    Participant

    Hi Jeremy,

    I’ve got a similar issue at the moment, in that we want to present a post-experiment memory task in which 50 names are presented as a list. We want to log which names are selected (presumably along a scale with radio buttons), but we also want to log other variables related to each name (from other columns in the .csv. E.g., indicating whether the name was presented in the experiment or not). We’d also like to be able to randomise the presentation of these names.

    Our question is basically: is it at all possible to present multiple values of a single column as a list, and to also be able to log whether each value was selected, while also logging variables related to each value (e.g., experimental or decoy item)?

    Best,
    Daniela

    #5561
    Jeremy
    Keymaster

    Hi Daniela,

    I would need more detail about the whole setup of your experiment, but here is an example that picks names from a table and shows them all in a single trial in a random order:

    AddTable("myTable", `name,presented
    John,yes
    Lucrecia,no
    Maggie,yes
    Vincent,yes
    Robert,no
    Shay,yes
    George,no
    __,void`)
    
    var names = [];
    var test;
    function handleNames(row){
        if (row.name=="__"){
            names = names.sort(v=>Math.random()>=0.5);
            return newTrial(
                ...names.map( r => {
                    test = test || newFunction(v=>true).test.is(true);
                    test = test.and( getScale(r.name+'-scale-'+r.presented).test.selected() );
                    return newText(r.name+": ")
                        .after(
                            newScale(r.name+'-scale-'+r.presented, 5)
                                .before( newText(" I'm sure I NEVER saw this name ") )
                                .after( newText(" I'm sure I DID see this name") )
                                .log()
                                .print()
                        )
                        .print();
                })
                ,
                newButton("Send")
                    .print()
                    .wait( test )
            )
        }
        names.push(row);
        return [];
    }
    
    
    Template( "myTable", row => handleNames(row) )

    I inserted the value of presented in the name of the Scale so you can retrieve the value from the results file, but I think it would be cleaner to just log the Scale score in the results file and add presented back to it from your input table when you analyze your results (in R, for example). If you have the option of defining an array in a js file rather than looking up a CSV table, as in Andrea’s case, I would do that instead, you wouldn’t need all these tricks like adding a row with __ in name.

    I also had to use a trick to make sure “Send” would only proceed if all Scale elements are selected.

    Jeremy

    #5595
    Philipp
    Participant

    Hi Jeremy,

    thanks for your reply. Adding on to what you wrote, I have three questions:

    1) How would you assign a label to this function such that you can assign it a place in the sequence, i.e. have it appear after the main experiment?

    2) Is it possible to include a text element on the same screen, so we can remind participants of what their task is?

    3) Could the individual items be arranged into columns instead of one long list?

    Thank you in advance!
    Philipp

    #5597
    Jeremy
    Keymaster

    Hi Philipp,

    1) You can always assign a label as the first argument of newTrial, so you could replace the line return newTrial( with return newTrial( "label" ,

    2) Yes, you can use newText("my text").print() inside the same newTrial command, either just before ...names.map( r => { or just before newButton("Send")

    3) Do you mean rearrange the table? In that case it wouldn’t make sense to use the Template command, as you would only have one row—use the method from this post instead, which is a cleaner way of creating a single trial

    Jeremy

    #5600
    daniela
    Participant

    Hi Jeremy,

    thank you for your replies, the handleNames function worked great!

    To clarify Philipp’s third question (we’re working on the same project together): we have 50 names we want presented during this post-experiment task. We would ideally present the names simultaneously in separate columns, rather than in one long list, so that participants don’t need to scroll. I imagine we could do this somehow using newCanvas and creating separate functions (e.g., handleNames1, handleNames2, etc.) in which if (row.name=="__") could be changed to only access the first 17 names (for handleNames1), then the next 17 (handleNames2), and so on. Would this be the best course of action?

    We also noticed that the randomisation doesn’t seem to work in Chrome (but does in Chrome mobile, and Firefox). Is there a way to get the randomisation to also work with Chrome?

    Thanks again for your help so far!

    Best,
    Daniela

    #5601
    Jeremy
    Keymaster

    Hi Daniela,

    Not sure what’s happening with Chrome, but use this to sort names instead:

    names = names.sort(v=>1-2*(Math.random()>=0.5));

    I don’t think using multiple functions is the way to go: the idea is to handle the multiple lines within a single function, defining multiple of them sort of defeats the purpose. One option would be to revise the content of newTrial like this:

    newCanvas("screen", "100vw", "100vh")
        .add( 0 , 0 , newCanvas("names-1", "30%", "100%") )
        .add( "center at 50%" , 0 , newCanvas("names-2", "30%", "100%") )
        .add( "right at 100%" , 0 , newCanvas("names-3", "30%", "100%") )
        .print( "center at 50vw" , "middle at 50vh" )
    ,
    ...names.map( (r,i) => {
        test = test || newFunction(v=>true).test.is(true);
        test = test.and( getScale(r.name+'-scale-'+r.presented).test.selected() );
        return newText(r.name+": ")
            .after(
                newScale(r.name+'-scale-'+r.presented, 5)
                    .log()
                    .print()
            )
            .print( "2em" , parseInt(3+(i%3)*3)+"em" , getCanvas("names-"+parseInt(1+i/3)) );
    })
    ,
    newButton("Send")
        .print( "center at 50%" , "bottom at 95%" , getCanvas("screen") )
        .wait( test )

    Notice the i variable in the map function, that lets us keep track of the index of the name we are parsing. I use it to determine in which of 3 Canvas elements, laid out as columns on a full-page Canvas, I will add the name. I also use it to calculate an appropriate Y coordinate. In this example, I use i%3 because I want columns of 3 names, but of course this is something to adjust to your needs (eg. i%17).

    Hope this helps, let me know if you have questions

    Jeremy

    #5603
    daniela
    Participant

    Hi Jeremy,

    wow, thank you so much, that’s exactly what we were looking for. You’ve worked your magic yet again!!! Very much appreciated 🙂

    Best,
    Daniela (and Philipp)

    #5606
    ReaToth
    Participant

    Hello Jeremy,

    Another question came up during my experiment design. I’m using the code you wrote down and it works perfectly. However, how would you make sure the participants selected an option for every question before moving on?
    I have used this code, but it moves on as long as the first question is answered.

    newTrial(“WBSI”,
    …wbsiQuestions.map( (v,i) => question(i,v) )
    ,
    newButton(“next”, “Next”)
    .print()
    .wait(getScale(“Rating”).test.selected())

    Thank you for your help in advance.

    #5607
    Jeremy
    Keymaster

    Hello Andrea,

    You would need to use the same method that I used in this message above:

    var test;
    newTrial("WBSI",
        ...wbsiQuestions.map( (v,i) => {
            if (i==0) test = getScale("Rating-"+i).test.selected();
            else test = test.and( getScale("Rating-"+i).test.selected() );
            return question(i,v);
        })
        ,
        newButton("next", "Next")
            .print()
            .wait( test )
    )
    #5610
    ReaToth
    Participant

    Hello Jeremy,

    Thank you for the quick solution. It is working perfectly 🙂 Last question: if I would like to add a message for the participants in case they haven’t answered all the questions, such as: ‘Please make sure you answered every question’, where would I have to insert it?

    Thank you,
    Andrea

Viewing 15 posts - 1 through 15 (of 23 total)
  • You must be logged in to reply to this topic.