# Get the previous row of a column

PennController for IBEX Forums Support Get the previous row of a column

Viewing 7 posts - 1 through 7 (of 7 total)
• Author
Posts
• #10640
Larissa_Cury
Participant

Hello, everyone! I’m creating an experiment which has the following rationale: “Display a string on the screen from the “sequence” column and play the corresponding audio. Afterwards, you’ll press either S or K to indicate your answer. The strings become bigger as the experiment continues. The experiment finishes when you get the 2 strings of the same size wrong”. R is easier for me, so I made this logic there, simulating the experiment:

The items are in the “Sequence” column, and I’ve storaged their lentgh in the “lenNumeric” column. I’ve also uploaded a wide version with the items separated by “_”, if that’s necessary.

``````
library(tidyverse)
library(stringr)

### Start at 0 - no errors

errorNum <- 0

for (i in 1:6) { ## Note: this is not zero based such as Js

df <- data

print(str_glue('Item number: {i} ; Sequence: {df\$sequence[i]}')) ## simulate newText()

# ### store current length

lenCurr <- df\$lenNumeric[i]

if (answer == 's') {

print('correct')

errorNum <- 0 ## erase error count

} else if (answer == 'k') {

### store and count mistakes:

errorNum <- errorNum + 1 ## increase by one

if (errorNum == 2) {  ## errorNum == 2 equals 'two mistakes in sequence'

if(lenCurr == df\$lenNumeric[i - 1]) { ## if lenCurr equals previous Length

print('stop experiment')

break ## break loop (experiment finishes)

} else if (lenCurr != df\$lenNumeric[i - 1]) {

errorNum <- 0 ## erase error count

}

}

}

}
``````

My idea was:

1) “Create an error counter *errorNum* which starts at 0 outside of the loop so that it doesn’t get erased whenever we enter the loop”
2) “Store the current length of the *sequence* from the *lenNumeric* column so that we can compare this current lentgh with the previous lentgh afterwards”
3) “If the answer is right (=== ‘s’), then simply move on, but make sure to erase the error count in case a participant get 2 sequential errors, but with different lengths (which is allowed)
4) Ok, now if the answer is wrong, then we first increase the error count by 1 `errorNum <- errorNum + 1`
5) Great. Now, if we get to mistakes in sequence, then `errorNum == 2`
6) Good. Now we have to test if the current lentgh is the same of the previous length to break the loop (aka, finish experiment), so we do:
`lenCurr == df\$lenNumeric[i - 1]`
7) Otherwise, `lenCurr != df\$lenNumeric[i - 1]` , we just erase the counter again

This seems to be working just fine in R. I tried to convert this logic to Pcibex, but I’m struggling with that…I couldn’t write the logit to compare the curr length to the previous length

The experiment with the data is here: https://farm.pcibex.net/r/uuULrc/ — my attempt is there too

I guess my biggest problem is to try to associate a loop with a Pcibex trial.

• This topic was modified 1 year, 1 month ago by Larissa_Cury.
#10642
Larissa_Cury
Participant

We have to erase the counter if the answer is right in case a participant get 1 error followed by a correct answer (which is allowed), not in case we have two sequential errors, I wrote it wrong above.

My attempt in Pcibex was this, but I got stuck…

``````
// CREATE an error count outside "loop" so that it doesn't gets erased whenever a trial starts
let errorNum = 0;

Template("newStimuliShort.csv", row =>
newTrial("listOfNumbers",
defaultText
.cssContainer({
'text-align': 'center',
"justify-content": 'center',
"align-items": 'center' ,
'font-size': '50px',
})
.center()
.print()
,
newMediaRecorder("recorder", "audio").log().record()
//  ,
//  newTimer("recording", 60000).log().start()
,
newText("words", row.sequence) // Display sequences
,
newAudio("audio", row.audio)  // play corresponding audio (which is in Portuguese)
.play()
,
getAudio("audio").wait("first") // wait audio finishes
,
newAudio("beep", "beep.mp3")
.play()
,
getAudio("beep").wait("first") // wait audio finishes
,
newVar("currLen").set(row.lenNumeric) // get current i length of "sequence"
,
newKey("NEXT", "SK")
.wait()
.log()
,
getKey("NEXT")
.disable() // make sure we only have 1 answer - participant can only answer once
,
getKey("NEXT")
.test.pressed("S")
.success(
newText("success", 'yey'),   // if the answer is right,
getVar("errorNum").set(0))   // make sure to erase the error count in case a participant get 1 error followed by a correct answer
.failure(

newVar("errorCount").set( errorNum + 1)  // Increase error count by 1 ans store it in a variable called 'errorCount'
,

// I would need sth here to store the errors so that k + k generates errorCount = K , then I guess I could use .testIs() to see if errorCount === 2. If yes, then
we should check if the current length equals the last length and break the experiment in case it's TRUE. Otherwise just erase the error count again and the
experiment continues

// newText("lastLen", row.lenNumeric).map((w,i) => <code>\${w[i - 1]}</code>) // get the last length ---- COULDN'T DO IT!!

//    ,
newText("failure")
//    .text( getVar("errorCount") ) // I was displaying just to see if it was working
.text(getVar("errorCount")) // I was displaying just to see if it was working
.cssContainer({"font-size":"30px", "text-align":"center", "margin-top":"255px","font-family":"Comic Sans MS", "color":"red","white-space":"nowrap"})

)
,
newTimer("wait-success",100) //timer for the success or failure messenge
.start()
.wait()
,
getText("success")
.remove()
,
getText("failure")
.remove()
// ,
// getTimer("recording").wait()
,
getMediaRecorder("recorder").stop()
)
);
``````

PS: I tried to use 0 and 1 instead of letters for the keypress, but I couldn’t do it. I guess it was interpreting 0 and 1 as strings, not numbers, how would I overcome that as well ?

• This reply was modified 1 year, 1 month ago by Larissa_Cury. Reason: forgot to put block code
#10645
Jeremy
Keymaster

Hi Larissa,

• `getVar("errorNum").set(0)`: this won’t work, because you never create a Var element named “errorNum”
• `newVar("errorCount").set( errorNum + 1)`: this will always set the value to 1, because you set the javascript variable `errorNum` to 0 at the top of your script and never update it
• `newText("lastLen", row.lenNumeric).map((w,i) => `\${w[i - 1]}`)`: you are using `.map` on the closing parenthesis of `newText()` but there is no command called `map` on Text elements. It wouldn’t make sense to call `.map` on `row.lenNumeric` either, since `row.lenNumeric` is not an Array (which is the type of object on which the `map` method is defined)

I have created a project here to illustrate how to implement the logic you describe (I didn’t include any audio playback or recording). What allows one to share information from one trial to the next is the Var element’s `.global` command

Jeremy

#10653
Larissa_Cury
Participant

Thank you very much, Jeremy! as always! I’ll sleep on it and give you some feedback!

#10742
Larissa_Cury
Participant

Hi, @Jeremy! It’s working properly, thank you VERY much, as always!

See, why can I set any trial before the logic? For example, I’m trying to add a button to make it fo fullscreen, but nothing happens before the logic ?

``````
Sequence(
"fullscreen",
// Lengths go from 1 through 6 \\ 1 to 9 -> zero based = 0-8 (?)
...[...Array(9)].map((v,i)=>randomize("sequence-"+parseInt(i+1)))
,
"end"
);

newTrial('fullscreen',
newButton("wait","openFull")
.center()
.print()
,
fullscreen()
);

``````
• This reply was modified 1 year ago by Larissa_Cury. Reason: correct code
#10744
Larissa_Cury
Participant

Hi, @Jeremy ! I could fiz the previous issue, I wasn’t adding a .wait(). However, the `getTEXT()` bellow do not show to be working: https://farm.pcibex.net/r/QIOhcO/

Note: they are working in another experiment just fine, I don’t know why they aren’t here:

``````
// ðŸ‘‰ Instructions trial
newTrial("instructions",
defaultText
.center()
.print()
,
newCanvas("my-canvas", 950,625) //950, 625
.cssContainer({
"background-color": '#FFFCF1',
"border": '2px solid #73AD21'
})
.center()
.print()
,
newText("welcome-researcher-msg", "Welcome, researcher!")
.cssContainer({
// "border": "5px solid black",
//  "background-color": "yellow",
//  "color": "black",
"font-size":"50px",
"white-space":"nowrap"
})
,
newText("type-ID-msg", "<br>Please, type <b style=color:red;>your participant's ID</b> below and click on 'START EXPERIMENT'.</br>")
,
newTextInput("inputID", "")
.center()
.cssContainer({
"margin":"2em",  // Add a em margin around this element
"height":"35px",
"border":"#FFFCF1",
})

.print()
,
newButton("wait","START EXPERIMENT ðŸ‘‰")
.cssContainer({
"border": "none",
"font-size": "40px",
// "color": "white",
"background-color": "green",
"cursor": "pointer",
})
.center()
.print()

// Only validate a click on Start when inputID has been filled
.wait( getTextInput("inputID").testNot.text("")
.failure(newText("failure", "Please, type your participant's ID above ðŸ‘†")
.cssContainer({
"margin-top": "1em",
"color": "red"})
.center()
.print()
))
,
// Store the text from inputID into the Var element
getVar("ID").set( getTextInput("inputID") )
);

the texts are appearing below the canvas, I don't know why..Why is that?
``````
#10747
Jeremy
Keymaster

Hi Larissa,

There are two issues about `getText` in the code from your message (the same points apply to the “wait” button): first, you use `getText("failure")` before you even create it, which might cause a reference error for PennController. Second, you `print()` a Text element named “failure” (which you create at the same time, with `newText`) upon a click with no text in the input box: calling `print()` with empty parentheses always has the effect of appending the content of the element (if any) below the most recently `print()`-ed element; so in your case, the text will appear below the (also just `print()`-ed) button

What you want is something like this (ignoring CSS for simplicity):

```newCanvas("my-canvas", 950,625) //950, 625
.add(320,320, newButton("wait", "START EXPERIMENT ðŸ‘‰"))
// ...
getButton("wait")
.wait(
getTextInput("inputID").testNot.text("")
.failure( getText("failure").visible() )
)
```

Regarding the fullscreen issue, you are not `wait`ing for the button so the browser tries to go fullscreen as soon as the experiment starts, which most browsers won’t allow for security/user-experience concerns

Jeremy

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