Jeremy

Forum Replies Created

Viewing 15 posts - 1,066 through 1,080 (of 1,522 total)
  • Author
    Posts
  • in reply to: reaction time results appear in different two columns #6272
    Jeremy
    Keymaster

    I’m surprised to see that the separator character in the lines you report is a tab rather than a comma. Did you save the results file on your device and then open it with a spreadsheet editor, by any chance?

    I edited your message to properly format the lines you report, and now you can see that they contain no line-break—you probably saw “second lines” just because of how the lines were rendered onto your screen, but they are not real second lines

    If you saved your results file with tab separators, you need to pass "\t" instead of "," as the sep argument of read.csv:

    all_results <- read.csv("results.csv", header=F, sep="\t", quote='"', comment.char='#')

    Jeremy

    in reply to: uploadRecordingsError not working? #6270
    Jeremy
    Keymaster

    Hi Masato,

    Recent versions of PennController automatically insert a page inviting to download an archive containing the MediaRecorder samples in case the upload failed. It could explain why the message you see is different from what you expect; it also makes the use of DownloadRecordingButton redundant in this case

    As with other texts in PennController, I have not yet implemented a standard method to edit the messages printed on that page, so you’ll need to use this hack:

    const replaceUploadingErrorMessage = ()=>{
        const uploadingErrorMessage = $(".PennController-PennController p:nth-child(2)");
        if (uploadingErrorMessage.length > 0 && uploadingErrorMessage[0].innerHTML.match(/^There was an error uploading the recordings:/))
            uploadingErrorMessage.html("Une erreur s'est produite lors du transfert :")
                .siblings(".Message-continue-link").html("Cliquez ici pour télécharger l'archive de vos enregistrements et les envoyer manuellement.");
        else
            window.requestAnimationFrame( replaceUploadingErrorMessage );
    };
    replaceUploadingErrorMessage();

    Let me know if you have questions

    Jeremy

    in reply to: reaction time results appear in different two columns #6268
    Jeremy
    Keymaster

    Hi,

    Can you give me an example of a full target line and a full filler line (replace the MD5 hash, ie. the second column, to maximize anonymity) taken directly from your results file?

    Jeremy

    in reply to: reaction time results appear in different two columns #6265
    Jeremy
    Keymaster

    You could check for the value of the last column (Comments) which will be empty for the short lines: replace short_lines <- dashed_results$Label=="poor_columns" with short_lines <- dashed_results$Comments==""

    Jeremy

    in reply to: reaction time results appear in different two columns #6262
    Jeremy
    Keymaster

    Hi,

    The argument of names should be a data frame that contains the content of your results file, which you load with read.csv. In the code from my message above, I use a data frame named dashed_results, a copy of all_results in which I stored the output of read.csv; the argument of read.csv should be the name of your results file, so for example if your file is named results222 (double-check whether your file has an extension) then you need to replace "results.csv" with "results222" in read.csv. Then you want to adapt the vector of names to the columns from your own results file—you can use length(all_results) to see how many columns have the longest lines from your results file

    Jeremy

    in reply to: randomizing the color of the text? #6261
    Jeremy
    Keymaster

    Hi,

    If you are fine with total randomization, you can do something like this:

    newTrial(
      newButton(['blue','red','green','purple'][Math.floor(Math.random()*4)])
        .css("color", ['blue','red','green','purple'][Math.floor(Math.random()*4)])
        .print()
        .wait()
    )

    Another option is to use pseudo-randomization by listing pre-generated color associations in a table and simply refer to the columns from inside Template

    Jeremy

    in reply to: MouseTracker recording wrong coordinates #6256
    Jeremy
    Keymaster

    Hi Irene,

    Do you use a MouseTracker element both for the clicks on the Canvas at the beginning of the experiment, and for the selections on the test trials? You can share your experiment with me by sending its URL at support@pcibex.net if you want

    Did you make sure that the content of your experiment fits entirely on a 360×720 screen, and that your participants don’t need to scroll down to make a selection at the bottom of the page?

    Jeremy

    in reply to: reaction time results appear in different two columns #6254
    Jeremy
    Keymaster

    Hi,

    For this illustration, I created a dummy experiment with just two trials:

    newTrial( "rich_columns" ,
        newController("DashedSentence", {s: "This is a first test sentence"})
            .log()
            .print()
            .wait()
    )
    .log("extra_column1", "value of extra colum 1")
    .log("extra_column2", "value of extra colum 2")
    
    
    newTrial( "poor_columns" ,
        newController("DashedSentence", {s: "This is a second test sentence"})
            .log()
            .print()
            .wait()
    )

    I took the experiment once, and ended up with this results file (comment lines removed, MD5 hash replaced):

    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,PennController,0,_Trial_,Start,1603727373087,value of extra colum 1,value of extra colum 2,NULL
    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,Controller-DashedSentence,DashedSentence,1,This,1603727376797,value of extra colum 1,value of extra colum 2,289,false,This is a first test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,Controller-DashedSentence,DashedSentence,2,is,1603727376797,value of extra colum 1,value of extra colum 2,251,false,This is a first test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,Controller-DashedSentence,DashedSentence,3,a,1603727376797,value of extra colum 1,value of extra colum 2,215,false,This is a first test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,Controller-DashedSentence,DashedSentence,4,first,1603727376797,value of extra colum 1,value of extra colum 2,236,false,This is a first test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,Controller-DashedSentence,DashedSentence,5,test,1603727376797,value of extra colum 1,value of extra colum 2,229,false,This is a first test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,Controller-DashedSentence,DashedSentence,6,sentence,1603727376797,value of extra colum 1,value of extra colum 2,291,false,This is a first test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,0,0,rich_columns,NULL,PennController,0,_Trial_,End,1603727376807,value of extra colum 1,value of extra colum 2,NULL
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,PennController,1,_Trial_,Start,1603727376828,NULL
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,Controller-DashedSentence,DashedSentence,1,This,1603727378601,240,false,This is a second test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,Controller-DashedSentence,DashedSentence,2,is,1603727378601,205,false,This is a second test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,Controller-DashedSentence,DashedSentence,3,a,1603727378601,169,false,This is a second test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,Controller-DashedSentence,DashedSentence,4,second,1603727378601,247,false,This is a second test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,Controller-DashedSentence,DashedSentence,5,test,1603727378601,215,false,This is a second test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,Controller-DashedSentence,DashedSentence,6,sentence,1603727378601,246,false,This is a second test sentence,Any addtional parameters were appended as additional columns
    1603727411,MD5ID,PennController,1,0,poor_columns,NULL,PennController,1,_Trial_,End,1603727378605,NULL

    I saved the file as results.csv and then used this R script:

    # First read the results file
    all_results <- read.csv("results.csv", header=F, sep=",", quote='"', comment.char='#')
    # We will work on the DashedSentence lines only
    dashed_results <- all_results
    # Name the columns based on the longer lines (ie the ones with extra1 and extra2)
    names(dashed_results) <- c("SubmitTime","MD5","Controller","ItemN","ElementN","Label","Group",
                               "Type","Name","Param","Value","EventTime","Extra1","Extra2",
                               "RT","LineBreak","Sentence","Comments")
    # Keep only the DashedSentence lines
    dashed_results <- subset(dashed_results, Type=="Controller-DashedSentence")
    # Let us first make the columns character-based instead of factors
    dashed_results$Comments <- as.character(dashed_results$Comments)
    dashed_results$Sentence <- as.character(dashed_results$Sentence)
    dashed_results$LineBreak <- as.character(dashed_results$LineBreak)
    dashed_results$RT <- as.character(dashed_results$RT)
    dashed_results$Extra2 <- as.character(dashed_results$Extra2)
    dashed_results$Extra1 <- as.character(dashed_results$Extra1)
    # Let us identify the short lines (the ones without extra1 and extra2)
    short_lines <- dashed_results$Label=="poor_columns"
    # Now let us move the columns
    dashed_results[short_lines,'Comments'] <- dashed_results[short_lines,'LineBreak']
    dashed_results[short_lines,'Sentence'] <- dashed_results[short_lines,'RT']
    dashed_results[short_lines,'LineBreak'] <- dashed_results[short_lines,'Extra2']
    dashed_results[short_lines,'RT'] <- dashed_results[short_lines,'Extra1']
    dashed_results[short_lines,'Extra2'] <- NA
    dashed_results[short_lines,'Extra1'] <- NA
    # Done!

    You’ll have to adapt this code to your case, of course. For example, I only have two extra-columns (the ones inserted by .log on newTrial) but judging from what you reported, it looks like you have 5 extra columns in your target items. Also, I labeled my trials rich_columns and poor_columns which, respectively, correspond to your target and filler items.

    I chose to keep the extra columns and fill them with NAs for the poor_columns lines, but alternatively you could just remove them altogether, in which case you would first need to move the columns of long_lines instead.

    Let me know if you have questions

    Jeremy

    in reply to: Page loading problems #6251
    Jeremy
    Keymaster

    Hi,

    The problem has more to do with how your browser handles cache than with PennController itself. Take a look to this page for how to perform a hard refresh—if the directions there don’t work, maybe consider clearing the cache for expt.pcibex.net entirely

    Jeremy

    in reply to: reaction time results appear in different two columns #6248
    Jeremy
    Keymaster

    Hi,

    As explained on the Controller element documentation page, extra columns will be appended at the end of the line because different types of controllers report different numbers of parameters.

    What you probably did is use log on the newTrial inside your Template, which also appends columns at the end of the line. Those take precedence over the ones appended by the Controller element, because the order of the different columns needs to be consistent across all rows corresponding to the different logged elements from the same trial: for example, if you log an extra “ParticipantID” column, it shouldn’t 13th in the line corresponding to a Button element from one trial, but 15th in the line corresponding to a Controller element from the same trial.

    Because you didn’t log those extra columns for you filler items (I assume) the Controller extra columns come earlier in the lines corresponding to the filler items. You will need to handle that externally to PCIbex. It’s an easy fix if you use R, for example, and I’d be happy to help you doing it. Just let me know

    Jeremy

    in reply to: Page loading problems #6242
    Jeremy
    Keymaster

    Hi,

    Did you try with another browser and/or in incognito mode? I do see your own logo, no preloading time at all, so I suspect your browser is using an old version of the experiment in the cache

    Jeremy

    in reply to: Choosing subet of items to present #6240
    Jeremy
    Keymaster

    Hi Maria,

    I had completely lost sight of the fact that in addition to the test on target, each of your trials also tests phase, and what I said above applies to this test too, because it’s a .test command again: all the new* elements are created upon runtime, no matter whether the trial runs as training vs test or sg_target vs pl_target. So what you did is the right thing to do: create the elements before any test and make sure to only use get* in your const declarations (with the single exceptions of sg_devoicing and pl_devoicing, which are only present in test-sg_target and test-pl_target, respectively).

    For the same reason, you need to factor newSelector("target") because otherwise you would be creating it twice. Another thing about the Selector element, I’d advise renaming it choice for example, because you already have a Var element named target and even though multiple elements of different types sharing a single name is not a fatal configuration, it’s still better practice to use different names.

    Also correct me if I’m wrong, but taking a closer look at your experiment’s script from the link you shared with me, you are using the exact same code for all three types of trials (alt, non, fil), to the letter. The way I understand it, the only differences between those three types of trials lie in what is specified in the table, ie. the nature of the pictures and audios. But the trial structure is exactly the same across all three trial types. Is that right? If so, you don’t need to triple all your Template and const declarations, you can do it just once, and simply label your trials by looking up the Type column of your table.

    See how much shorter your code becomes:

    const training_trial = variable => [
        newTimer(500).start().wait()
        ,
        getImage("sg_picture").print()
        ,
        newTimer(200).start().wait()
        ,
        getAudio("sg_voicing").play()
        ,
        newTimer(1500).start().wait()
        ,
        getImage("sg_picture").remove()
        ,
        newTimer(1000).start().wait()
        ,
        getImage("pl_picture").print()
        ,
        newTimer(200).start().wait()
        ,
        getAudio("pl_voicing").play()
        ,
        newTimer(2000).start().wait()
        ,
        getImage("pl_picture").remove()
        ,
        newTimer(200).start().wait()
    ]
    
    const test_trial = variable => [
        newSelector("choice").log()
        ,
        getVar("target").test.is("sg")
            .success( 
                newTimer(500).start().wait()
                ,
                getImage("pl_picture").print()
                ,
                newTimer(200).start().wait()
                ,
                getAudio("pl_voicing").play()
                ,
                newTimer(2000).start().wait()
                ,
                getImage("pl_picture").remove()
                ,
                getImage("sg_picture").print()
                ,
                newTimer(200).start().wait()
                ,
                newAudio("sg_devoicing", variable.SgDevoicingFile)
                ,
                getSelector("choice")
                    .add( getAudio("sg_voicing"),getAudio("sg_devoicing") )
                    .shuffle()
                    .test.index( getAudio("sg_voicing"), 0 )
                    .success(
                        getAudio("sg_voicing").play().wait(),
                        getAudio("sg_devoicing").play().wait(),
                        getSelector("choice").keys("1","2").wait()
                    )
                    .failure( 
                        getAudio("sg_devoicing").play().wait(),
                        getAudio("sg_voicing").play().wait(),
                        getSelector("choice").keys("2","1").wait()
                    )
                ,
                newTimer(500).start().wait()
            )
            .failure( 
                newTimer(500).start().wait()
                ,
                getImage("sg_picture").print()
                ,
                newTimer(200).start().wait()
                ,
                getAudio("sg_voicing").play()
                ,
                newTimer(2000).start().wait()
                ,
                getImage("sg_picture").remove()
                ,
                getImage("pl_picture").print()
                ,
                newTimer(200).start().wait()
                ,
                newAudio("pl_devoicing", variable.PlDevoicingFile)
                ,
                getSelector("choice")
                    .add( getAudio("pl_voicing"),getAudio("pl_devoicing") )
                    .shuffle()
                    .test.index( getAudio("pl_voicing"), 0 )
                    .success(
                        getAudio("pl_voicing").play().wait(),
                        getAudio("pl_devoicing").play().wait(),
                        getSelector("choice").keys("1","2").wait()
    
                    )
                    .failure( 
                        getAudio("pl_devoicing").play().wait(),
                        getAudio("pl_voicing").play().wait(),
                        getSelector("choice").keys("2","1").wait()
    
                    )
            )
    ]
    
    Template( row =>
        newTrial( row.Type ,
            newImage("sg_picture", row.SgPictureFile),
            newImage("pl_picture", row.PlPictureFile),
            newAudio("sg_voicing", row.SgVoicingFile),
            newAudio("pl_voicing", row.PlVoicingFile)
            ,
            getVar("phase").test.is("training")
                .success( ...training_trial(row) )
                .failure( ...test_trial(row) )
        )
        .log("phase", getVar("phase") )
        .log("sg_picture", row.SgPictureFile )
        .log("Item", row.Item)
    )

    Notice how you don’t need to filter your table at all, but the trials are now labeled row.Type, which ensures that you’ll still end up with trials labeled alt, some labeled fil and some labeled non, based on your table’s Type column.

    I tested this code myself, and the whole experiment ran smoothly. I think we finally got there, but let me know if you have questions

    Jeremy

    Jeremy
    Keymaster

    Hi Muxuan,

    Save DashedSentence.js using a different name (eg. DashedCustom.js), then edit it and replace the 6th line, name: "DashedSentence", with name: "DashedCustom", for example, and comment out or simply delete lines 192-193:

    if (t.currentWord - 1 >= 0)
        t.blankWord(t.currentWord - 1);

    Upload DashedCustom.js to the Controllers folder of your project. You should also save DashedSentence.css under a corresponding name, eg. DashedCustom.css, and upload it to the Aesthetics folder of your project.

    Then you can use DashedCustom like this:

    newTrial(
      newController("DashedCustom", {s: "This is a test sentence"})
        .print()
        .wait()
    )

    Jeremy

    in reply to: Editing the link in InitiateRecorder() #6236
    Jeremy
    Keymaster

    Hello,

    You can use the trick described in this message (second point)

    Jeremy

    in reply to: Choosing subet of items to present #6233
    Jeremy
    Keymaster

    Hi Maria,

    I think the two problems are related. All the commands in the success and failure blocks of a test are still part of the trial, even though upon runtime only one of the two blocks will effectively be run. Accordingly, all the new* declarations from either block are processed, which means that the code you’ve posted creates duplicates of sg_picture, pl_picture, sg_voicing and pl_voicing. That’s why when you try to .remove a picture, it can sometimes point to the wrong one (the one from the other block) and fail to effectively take it off the page. Regarding the Audio elements, I’m not sure why you see this specific problem, but I suspect problems will go away if you avoid creating the element twice.

    Here is what your code looks like after factoring the creation of the elements:

    const alt_test_trial = variable => [
        newImage("sg_picture", variable.SgPictureFile),
        newImage("pl_picture", variable.PlPictureFile),
        newAudio("sg_voicing", variable.SgVoicingFile),
        newAudio("pl_voicing", variable.PlVoicingFile)
        ,
        getVar("target").test.is("sg")
            .success( 
                newTimer(500)
                    .start()
                    .wait()
                ,
                getImage("pl_picture").print()
                ,
                newTimer(200)
                    .start()
                    .wait()
                ,
                getAudio("pl_voicing").play()
                ,
                newTimer(2000)
                    .start()
                    .wait()
                ,
                getImage("pl_picture").remove()
                ,
                getImage("sg_picture").print()
                ,
                newTimer(200)
                    .start()
                    .wait()
                ,
                newAudio("sg_devoicing", variable.SgDevoicingFile)
                ,
                newSelector("target")
                    .log()
                    .add( getAudio("sg_voicing"),getAudio("sg_devoicing") )
                    .shuffle()
                    .test.index( getAudio("sg_voicing"), 0 )
                    .success(
                        getAudio("sg_voicing").play().wait(),
                        getAudio("sg_devoicing").play().wait(),
                        getSelector("target").keys("1","2").wait()
                    )
                    .failure( 
                        getAudio("sg_devoicing").play().wait(),
                        getAudio("sg_voicing").play().wait(),
                        getSelector("target").keys("2","1").wait()
                    )
                ,
                newTimer(500)
                    .start()
                    .wait()
            )
            .failure( 
                newTimer(500)
                    .start()
                    .wait()
                ,
                getImage("sg_picture").print()
                ,
                newTimer(200)
                    .start()
                    .wait()
                ,
                getAudio("sg_voicing").play()
                ,
                newTimer(2000)
                    .start()
                    .wait()
                ,
                getImage("sg_picture").remove()
                ,
                getImage("pl_picture").print()
                ,
                newTimer(200)
                    .start()
                    .wait()
                ,
                newAudio("pl_devoicing", variable.PlDevoicingFile)
                ,
                newSelector("target")
                    .log()
                    .add( getAudio("pl_voicing"),getAudio("pl_devoicing") )
                    .shuffle()
                    .test.index( getAudio("pl_voicing"), 0 )
                    .success(
                        getAudio("pl_voicing").play().wait(),
                        getAudio("pl_devoicing").play().wait(),
                        getSelector("target").keys("1","2").wait()
    
                    )
                    .failure( 
                        getAudio("pl_devoicing").play().wait(),
                        getAudio("pl_voicing").play().wait(),
                        getSelector("target").keys("2","1").wait()
    
                    )
            )
    ]

    Don’t forget to also update the other const declarations along the same lines, and don’t hesitate to ask questions

    Jeremy

Viewing 15 posts - 1,066 through 1,080 (of 1,522 total)