Forum Replies Created
-
AuthorPosts
-
October 29, 2020 at 12:09 pm in reply to: reaction time results appear in different two columns #6272
Jeremy
KeymasterI’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
Jeremy
KeymasterHi 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
October 28, 2020 at 11:25 pm in reply to: reaction time results appear in different two columns #6268Jeremy
KeymasterHi,
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
October 27, 2020 at 3:43 pm in reply to: reaction time results appear in different two columns #6265Jeremy
KeymasterYou 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
October 27, 2020 at 11:47 am in reply to: reaction time results appear in different two columns #6262Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi 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
October 26, 2020 at 12:27 pm in reply to: reaction time results appear in different two columns #6254Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi,
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
October 25, 2020 at 1:51 pm in reply to: reaction time results appear in different two columns #6248Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi 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
October 23, 2020 at 10:32 am in reply to: DashedSentence or DashedCustom for self-paced reading with cumulative window? #6237Jeremy
KeymasterHi 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
Jeremy
KeymasterJeremy
KeymasterHi 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
-
AuthorPosts