psychopy.org | Reference | Downloads | Github

Building a n-back test


#1

Hi. I am trying to design n-back test using builder view. I read the previous topics related to n-back and it was somehow unclear.

I created a routine and a loop (called trials) and in the condition file, below the “numbers” columns, I inserted numbers from 1 to 9. I selected the loop type as random.

I found the code below for dual n-back, but I only want one n-back (comparing a number only with the previous number).

# keep track of the variable that was current two trials ago:
if trials.thisN == 0: 
    two_back = your_variable # initial value needed for trial 2
if trials.thisN == 1:
    one_back = your_variable # need this to swap with two-back on later trials
if trials.thisN > 2: # need to update the n-back values
    two_back = one_back
    one_back = your_variable

# set whatever keypress values are appropriate for this trial:
if your_variable == two_back:
    correct_response = 'y'
else:
    correct_response = 'n'

I would appreciate any comment on how I could rewrite this code for n-back test.


#2

Hey Omidrezaa,

I’m not exactly sure how “your_variable” is defined but to me it looks like this could go into the right direction:

# keep track of the previous trial:
one_back = your_variable

# set whatever keypress values are appropriate for this trial:
if your_variable == one_back:
    correct_response = 'y'
else:
    correct_response = 'n'

As far as I understand your code, you constantly update the one- and two-back values. As you’re only interested in one-back, you don’t need all the if conditions anymore. Could you please let us know where “your_variable” is coming from?

Best - rbr


#3

@rbr is part way there, but in the code above, we are setting two variables to be equal and then immediately checking to see if they are equal, which isn’t going to get you very far. You need to separate these things across trials. e.g:

Start experiment tab:

# Set a junk value for the first trial, as by definition we can't
# have a one-back value there:
one_back = 'Not applicable'

Start routine tab:

# set whatever keypress values are appropriate for this trial:
if your_variable == one_back:
    correct_response = 'y'
else:
    correct_response = 'n'

End routine tab:

# update the one-back value for the *next* trial:
one_back = your_variable

#4

Hi,

I’ve designed a couple of different N-back tasks in the builder. You can download them from here - might be useful!

M.


#5

Hi! I’m starting a one-Back task with images using the builder. I’m having difficulties trying to do some things.

  1. Giving feedback. I have a practice section where I want to present a Green Fixation cross if the response was correct and a Red Fixation if the response was incorrect.
  2. Displaying elapsed time. I want to add the option to display an analog clock showing the time that has pass since the initiation of the image presentation.
  3. Reset clock task. I want to add a simultaneous task. That is, to reset the elapsed time clock every minute. I need to present a text feedback, something like “yo have reset the clock at 1:10 s”

My very first questions: it is possible to do this using the builder?


#6

Hi @setegonz, yes this can be done in Builder using a code component. A good place to start would be to have a look at the Navon task demo on Pavlovia - see here. I recommend you fork/download this task using PsychoPy 3. In this task, there is a practice block with feedback, and within the feedback is the response time, and this will give you an idea of how to present timing onscreen using a text component.

For clocks, you will want to use the clock object in psychopy, found in the core module (this is automatically imported when using Builder so no action required). The following is how you would use a clock in a code component.:

myClock = core.Clock()  # Create the clock
myClock.reset()  # At the relevant time, reset the clock to zero e.g., at beginning of a routine/trial
myClock.getTime()  # a clock function that returns the time in seconds since clock creation or the last reset call

#7

@dvbridges Thanks for your response. I’m trying to “mimic” the feedback of the Navon task demo. I’m still in my practice block. This is my set up:
In my practice block I have a practice trial routine and a feedback routine within a loop:

At my trial routine I have:

# Begin Experiment tab
# Set a junk value for the first trial, as by definition we can't # have a one-back value there: 
one_back = 'Not applicable'

# Begin Routine Tab
x = randint(low = 10, high = 20)
myClock = core.Clock()  # Create the clock
myClock.reset()  # At the relevant time, reset the clock to zero e.g., at beginning of a routine/trial
myClock.getTime() 
# set whatever keypress values are appropriate for this trial: 
if stimFile == one_back:
    correct_response = 'y'
else:
    correct_response = 'n'

## End Routine Tab
one_back = stimFile

Then at my feedback routine:

# Begin experiment tab 
msg='';

# Begin routine tab
if stimFile == one_back and respPractice = 'y' {
      msg="Correct! RT=" + resp.rt.toFixed(3);  }
 elif stimFile != one_back and respPractice = 'y' {
      msg="Correct! RT=" + resp.rt.toFixed(3);} 
else      msg="Oops! That was wrong"; }

I’m having this error:
if stimFile == one_back and respPractice = ‘y’ {
^
SyntaxError: invalid syntax

Questions:

  1. Why I’m having that error?
  2. Is it possible to have a feedback based in the previous trial and not based in a predetermined response sheet?

Here’s my experiment (https://github.com/setegonz/psychopyNback)

Thanks in advance!


#8

@setegonz, you are getting the error because you are using JS code in Python code. What you want is the python code in the Navon task. Have a look at the code component window in the Navon experiment and select python code from the code type drop down menu.


#9

The main difference between your code and mines is that you have a predetermined resp.corr value. My correct/incorrect answer depends on the previous trial.
That’s why it doesn’t suit me directly.

if (resp.corr > 0) {
     msg="Correct! RT=" + resp.rt.toFixed(3); 
} else {
     msg="Oops! That was wrong";
}

Btw I’m using:

if stimFile == one_back and respPractice = 'y' ...

…Based in one of the first post in this entry.


#10

Yes sure, but to address your error

Blockquote
I’m having this error:
if stimFile == one_back and respPractice = ‘y’ {
^
SyntaxError: invalid syntax

You have embedded javascript code into your python code. You will need to fix that first (also note the correction in the conditional - use of assignment operator in place of equality operator) e.g.,

Your code

if stimFile == one_back and respPractice = 'y' {
      msg="Correct! RT=" + resp.rt.toFixed(3);  }
 elif stimFile != one_back and respPractice = 'y' {
      msg="Correct! RT=" + resp.rt.toFixed(3);} 
else      msg="Oops! That was wrong"; 

Correction

# python code does not use curly braces to separate blocks of code
if stimFile == one_back and respPractice == 'y':
    msg="Correct! RT=" + resp.rt  # toFixed() is a JS function, so no need here.

So, in your feedback, you want to provide information from the actual trial, rather than a set text, e.g., “Oops, that was wrong”? If so, please provide details of what you want in the feedback and we can give guidance on that.


#11

Again thank you very much for your help.

I’m basically stuck in the practice routine. I even decided to start again. What I have basically is a set of images being presented. There are 3 things I would like to achieve before continuing with the task blocks.

  1. I would like to see certain numbers of images repeated (one-back hits). And I would also like to control the number of times this happens (15% one-back hits).
  2. I would like to give feedback similar to the one you use in your experiment. (Correct/Incorrect and RT)
  3. Give the opportunity to see the elapsed time whenever the subject press a certain key and then hiding the clock again.

8ED1C486-C4F0-4EA5-A6CB-AFBCAD3768E5
This is how my looks loke
7216280F-26F2-4551-A8B4-21A90C085834
This is what i have in my practice routine
7C065BE2-E627-4DF1-9462-3CED979ED997
This is my feedback routine
F9F6CEC9-EDE9-4FF9-9D83-580E63C8993F
C4FF1070-0748-45A3-A774-2ED14C3A77AB
DF027C7E-7FDB-457E-8E65-F683E59F87FB
And my code component content


#12

Well, if you want a oneback, that would mean consecutive or repeating targets. For this, you could create your stim list at the beginning of the experiment and work out how many targets vs foils you would need to achieve 15% hits. For example, 20 stim including 3 repeating targets (6 target presentations) creates conditions of 15% hit (i.e., 3 repetitions). To do this, you would randomise your stim file holding only foil stim, and insert repeating targets at random locations. Save this new conditions file into a csv e.g., newConditionFile.csv. Then, use new conditions file that you have just created in the trial handler, but use sequential ordering in the loop handler, since the randomisation is already achieved. You may have an extra column called “stimType” that uses -1, 0 and 1 to define trials as foil, pre-target, and target respectively.

When a correct response is achieved on a target add the following in the “End Routine” tab in a code component, which can be fed into your feedback textbox in the next routine. For elapsed time, you could refer to your clocks. Insert the following in relevant code component tabs of the practice routine. Note, this only works if you have a stimType column in your new conditions file:

# Begin Experiment
msg = ''

# End Routine
if stimType == 1 and respPractice == 'y':
    elapsedTime = myClock.getTime()
    msg="Correct! Time elapsed = " + elapsedTime 
else:
    elapsedTime = myClock.getTime()
    msg="Incorrect! RT = " + elapsedTime 

For showing elapsed time in a textStim for a duration of 1 second, you only need to set the text of the text component to your new msg variable, and this can be done in the Builder component in the text box using $msg


#13

I didn’t get the idea. I’m going to enumerate steps to help me understand better.

  1. I have to create the stim list in my PracticeRoutine. Here’s my list: (the first question: do I need to include repeating targets in this file? If I include repeating targets in that file, will I’m going to have the always the same images being the target over and over? (I don’t want participants to be able to identify images that are never going to be used as target and photos that can be used as a target because that can be used as a strategy to respond.)
    E4053E76-B486-44AA-812A-812471ABC514

  2. In my loop I have practiceBlock.csv (592 Bytes)
    That is the file that I’m using to call “practiceFile” that is where I have my images list. How can I get newConditionFile.csv and practiceBlock.csv to communicate?

Again
Thanks for being so thoughtful!


#14

Ok to help generate your conditions file, with randomly dispersed repeating targets, it will be helpful if you explain your method, as you would in a methods section of a paper, for example it will be useful to know how you are going to choose which are your repeating targets in your stimuli file.

If you create a new randomized file at the beginning of the experiment, to use it you just need to pass the new file name to the loop handler.


#15

Experiment instructions:

The Time-Based Prospective Memory task was formulated with two components

  1. Ongoing Task

The ongoing task was a visual one-back task. Participants had to decide whether or not the present picture was presented one stimulus before by pressing the ‘space’ key or by letting the stimulus pass in case of non hit. The stimuli we used was selected from from the International Affective Picture System IAPS (Lang et al., 2005). The images varied significantly in IAPS valence ratings, with lowest valence for negative pictures (M = x, SD = x), intermediate valence for neutral pictures (M = x, SD = x), and highest valence for positive pictures (M = x, SD = x). In total we use 25 negative, 25 positive and 25 neutral pictures. IAPS emotional arousal ratings does not differ between the selected pictures (Negative: M = x, SD = x; Positive: M = x, SD = x; Neutral: M = x, SD = x). Pictures were displayed one by one in random order on a PC screen for 600 ms. The inter stimulus interval was filled with a white noise screen presented from .7 to 3 seconds. (In practice trials the ISI was substituted by a correct/incorrect and RT notification message.)

  1. Prospective Memory Task

In order to complete the prospective memory task participants had to reset a clock every 60s by pressing the ‘x’ key while simultaneously performing the ongoing task. To monitor the passage of time, they had the option to check a digital clock at any moment of the task by pressing ‘y’ key. When pressing clock check key a digital timer showed up in the center of the screen showing the exact time passed from the previous clock reset. The first prospective memory trial started with the presentation of the first photo. Subsequent ones began when the participant reset the clock and lasted until the participant decided to reset the clock to zero again.


#16

How do you want to select the repeating images from each condition (i.e., pos, neg, neutral)? Do you want to select which stimuli are repeated at random? Also, how many stimuli from each condition will be repeated?


#17

Lets assume you have 3 conditions files, each holding pos, neg and neutral stim - neutral.xlsx (7.9 KB) neg.xlsx (7.9 KB) pos.xlsx (7.9 KB). Each stim file has 9 stim, and 3 of them repeat, and the repeats are selected at random for every participants.

At the beginning of the experiment, in a code component.:

import pandas as pd

files = ['pos.xlsx', 'neg.xlsx', 'neutral.xlsx']
# 9 images per condition
# 3 repeats per condition

# First, select repeating stim at random and indicate they repeat with a 1
newStimList = pd.DataFrame()
for f in files:
    temp = pd.read_excel(f)
    randomLocs = np.random.choice(range(temp.shape[0]), size=3, replace=False)
    temp['repeat'].iloc[randomLocs] = 1 
    newStimList = newStimList.append(temp)  # Append all stim to a new dataframe, so all in one place

# Shuffle new dataframe
newStimList = newStimList.sample(frac=1).reset_index(drop=True)
# Add new index to keep track of order
newStimList = newStimList.reset_index()
# Duplicate repeating rows
repeats = newStimList[newStimList['repeat']==1]
newStimList = pd.concat([repeats, newStimList], ignore_index=True).sort_values('index')
# Save new stim list to csv ready for trial handler
newStimList.to_csv('newStimList.csv', index=False)

This will output a stimfile like this newStimList.csv (348 Bytes). Get that working, then we can think about the rest of the experiment.


#18

Heres What I did:

  1. I Inserted a Code component with the code that you kindly prepared in the very first routine of the experiment. The only change I inserted was in the stim files. The have 22 images each.
  2. Then I’m using the new condition file as condition in loop.
    5457EAE5-8958-4949-A946-4E18DF6E4932
  3. And then stim as image.

After that, I’m getting the following error:
Traceback (most recent call last):
File “/Users/setegonz/MEGAsync/myPsychopyNback/TBPMnBack_lastrun.py”, line 78, in
temp = pd.read_excel(f)
File “/Applications/PsychoPy3.app/Contents/Resources/lib/python3.6/pandas/util/_decorators.py”, line 118, in wrapper
return func(*args, **kwargs)
File “/Applications/PsychoPy3.app/Contents/Resources/lib/python3.6/pandas/io/excel.py”, line 238, in read_excel
false_values=false_values, squeeze=squeeze, **kwds)
File “/Applications/PsychoPy3.app/Contents/Resources/lib/python3.6/pandas/io/excel.py”, line 469, in _parse_excel
sheet = self.book.sheet_by_index(asheetname)
File “xlrd/book.pyc”, line 462, in sheet_by_index
IndexError: list index out of range


#19

Perhaps see if it works with my files that I posted. If my excel sheets work, then you need to reformat your excel files to match. Make sure files are in the same location as the .psyexp file.


#20

It works!!

Now I’m working on the feedback. Here’s what I tried in the practice routine (before the feedback routine) but didn’t worked:

# Begin Experiment
msg = ''

# Begin Routine
if trialsPractice.thisN == 0:
    one_back = 'Not applicable'
if stim == one_back and respPrac == 'y':
    msg= "Correct! RT=" + resp.rt
elif stim != one_back and not respPrac:
     msg= "Correct!"
elif stim != one_back and respPrac == 'y':
    msg="Incorrect"
elif stim == one_back and not respPrac:
    msg="You miss that one"

#End routine
one_back = stim