In this post, we’re going to go over how to program a simple memory experiment. We’ll still be working with functions, “for” loops, and lists. The only new”building block” introduced here is randomization. Basically, this is a post for reinforcing concepts earlier on.
What you’ll end up with, the Deese-Roediger-McDermott (DRM) paradigm
In this study, participants will go through a learning phase and a test phase. During the learning phase, they’ll be shown semantically similar words, e.g., “THREAD”, “HURT”, “THORN”, “POINT”. Later, during the test phase, they will be shown a series of words, some from the learning phase, some they’ve never seen before. For each word in the test phase, they have to decide whether it’s a “new” word or an “old” one. Crucially, we will insert a false memory lure, e.g., “NEEDLE”, that’s semantically similar to the words in the learning phase. If participants mark this word as “old”, they have had a false memory!
Set up
The first few lines of code should mostly look familiar. There’s only one new thing in there.
# Set up. Importing packages and setting up a window to display stimuli in
from psychopy import visual, event
import random
window = visual.Window(monitor="testMonitor", fullscr=True)
The only new thing here is that we’re importing a package called “random”, which, as you might have guessed, contains a bunch of functions related to randomizing stuff.
Next, we’ll define a function to show individual words. This will save a TON of time.
def showText(myText):
message = visual.TextStim(window, text=myText)
message.draw()
window.flip()
The first line starts with “def”, which tells python you are about to define a function. Next, the name of the function precedes the open parentheses. In those parentheses, you can put the name of the arguments you may want the function to accept. If you were writing a function that squared whatever number you input as its argument, you might define it with “def square_this(some_number):”. All the indented lines you put under the function can now reference “some_number” and the function will use whatever you want when you call the function. With “showText()” you can write “showText(‘NEEDLE’)”. Internally, “myText” will read as ‘NEEDLE’ and get treated as such by the indented lines under the function.
Next, we need to create some lists of semantically related words. I got the following list from https://www3.nd.edu/~memory/OLD/Materials/DRM.pdf
study_words=["THREAD","HURT","THORN","POINT","KNITTING","THIMBLE","CLOTH","SEWING","HAYSTACK","SHARP","INJECTION","PIN","SYRINGE","EYE","PRICK"]
We will end up using a “for” loop to go through “study_words” and show each element in the list. We’ll also eventually need a list of “new” words during the test phase, which we’ll save as “test_words”. (I just listed random words off the top of my head).
test_words=["FEAR","STEAL","RIPE","SKY","SILL","DELAY","HAND","DESK","SEAT","COLOR","REST","JAZZ","BEARD","FLOUR","SODA"]
However, we need the test phase to contain new AND old words. We’ll do this by simply adding all the elements in the “study_words” list at the end of the “test_words” list.
test_words = study_words + test_words
Easy, huh? Notice that you can assign a new value to a variable (e.g., “test_words”) by using it’s old value. In other words, I used the contents of the old “test_words” list to create the new one.
The last bit of set up we need is to add the lure word, “NEEDLE”. I could’ve just typed it at the end of “test_words” but I’m allowed to be idiosyncratic and want to see it added in separately. There are two ways you can do this. The first way is to use the + like before:
test_words = test_words + ["NEEDLE"]
Notice that I had to put “NEEDLE” as the only element in a 1-element list, hence the square brackets. There’s nothing really wrong with this method, but it’s more typical to use the ‘.append()’ function and I think it has some advantages:
test_words.append("NEEDLE")
This method doesn’t require you to re-define what “test_words” is by saying, “the new test_words is going to be equal too the old test_words plus this other stuff”. You can just say, “Add this to test_words.” I also think it’s easier to loop through lists of stimuli and whatnot using the “.append()” function.
Instructions, learning phase
For the instructions, I’ll use our handy “showText()” function to display some simple instructions, then use “time.sleep()” to keep those instructions on the screen for several seconds, and then show some new text saying, “Press the C key when you are ready to begin”. A lot of this stuff should look familiar.
# Instructions
showText('For the first phase of this study, we will show you some words to memorize for 5 seconds each.')
time.sleep(5)
showText("Press the C key when you are ready to begin")
instructions_done=event.waitKeys(keyList=['c'])
The next few lines displays each word in “study_words” for five seconds.
random.shuffle(study_words)
for i in range(len(study_words)):
showText(study_words[i])
time.sleep(8)
The function “random.shuffle()” accepts lists as inputs and randomizes the order of whatever list you put in. The next line starts a “for” loop, and says we want to do the same thing to each element “i” in “study_words”. The next two indented lines basically say, “display whatever element, i, that we’re on right now, wait five seconds…” After the “for” loop executes those two commands on the first element on the list, it’ll keep doing that over and over again until it’s gone through every element in “study_words”.
More instructions and test phase
The instructions for the test phase are a lot like the ones for the learning phase:
# test phase instructions
showText('You will now be shown several words. Some were on the list you just memorized. Some were not. For each word, press 1 if the word was on the list you studied and 2 if the word is new')
time.sleep(8)
showText("Press the C key when you are ready to begin")
instructions_done=event.waitKeys(keyList=['c'])
The code for the test phase itself might look daunting at first, but if you go line-by-line, it’s a pretty straightforward application of stuff we’ve been using all along.
random.shuffle(test_words)
for i in range(len(test_words)):
test_word=visual.TextStim(window, text=test_words[i])
test phase
random.shuffle(test_words)
for i in range(len(test_words)):
test_word=visual.TextStim(window, text=test_words[i])
response = visual.TextStim(window, text=”1 = Word was on the study list \n 2 = Word was not in the list”, pos=(0,-0.8))
test_word.draw()
response.draw()
window.flip()
response=event.waitKeys(keyList=[‘1′,’2’])
print(test_words[i],” “,response[0])
The first line “random.shuffle(test_words)” is randomizing the order of the words in the “test_words” list. The next line initiates a “for” loop that’s going to iterate over each element in “test_words”. The first two indented lines under the “for” loop are creating stimuli to show the participant. This may seem weird because we defined a “showText()” earlier so we wouldn’t have to “draw” and “flip” stimuli. Here, though, I want to show two separate text stimuli on the same screen, so I’m regressing to doing things the old fashioned way. “test_word” is the ith (i.e., whatever element, “i” the “for” loop is on at the moment) element in “test_word”. “response” is a reminder to the participant of which key means what, “1 = Word was on the study list \n 2 = Word was not in the list”. The “\n” tells psychopy to do a line break at that point. It looked better to me. Notice two that I added the “pos=(0,-0.8)” argument to make these instructions appear at the bottom of the screen.
The next two lines draw “test_word” and “response” together on the same screen, which is a weird way of phrasing it because the participant can’t see this “screen” until you it gets to the “window.flip()” command. The line starting with “response” causes python to wait for the participant to type either ‘1’ or ‘2’, then save what they typed in a list where the zeroth element is whatever they pressed.
The final “print()” command displays the word they’re on and whether they pressed ‘1’ or ‘2’ for you to copy and paste. This is still a very crude way to record data. I’ll cover a more convenient one in a subsequent post.
They’re done!
The last two lines should be pretty self-explanatory by now:
showText("You are now done with the study. Please let the experimenter know. Thank you!")
stop_program=event.waitKeys(keyList=['5'])
They display some text telling the participant to leave because they’re done with the study, then waits for the research assistant (or whoever) to press ‘5’ to close out the program.
Here’s the entire script you should’ve end up with, featuring some lovely comments explaining what each line is doing.
# Set up. Importing packages and setting up a window to show stuff in.
from psychopy import visual, event
import time
import random
window = visual.Window(monitor="testMonitor", fullscr=True)
# Custom function to show ANY text on screen
def showText(myText):
message = visual.TextStim(window, text=myText)
message.draw()
window.flip()
# Setting up the stimuli to be used in the learning and test phase
study_words=["THREAD","HURT","THORN","POINT","KNITTING","THIMBLE","CLOTH","SEWING","HAYSTACK","SHARP","INJECTION","PIN","SYRINGE","EYE","PRICK"]
test_words=["FEAR","STEAL","RIPE","SKY","SILL","DELAY","HAND","DESK","SEAT","COLOR","REST","JAZZ","BEARD","FLOUR","SODA"]
test_words=study_words+test_words
test_words.append("NEEDLE")
# Instructions for the learning phase
showText('For the first phase of this study, we will show you some words to memorize for 5 seconds each.')
time.sleep(8)
showText("Press the C key when you are ready to begin")
instructions_done=event.waitKeys(keyList=['c'])
# The learning phase
random.shuffle(study_words) # ransomize the order of the "study_words"
# go through each word in "study_words" and display it for 5 seconds
for i in range(len(study_words)):
showText(study_words[i])
time.sleep(5)
# Instructions for the test phase
showText('You will now be shown several words. Some were on the list you just memorized. Some were not. For each word, press 1 if the word was on the list you studied and 2 if the word is new')
time.sleep(8)
showText("Press the C key when you are ready to begin")
instructions_done=event.waitKeys(keyList=['c'])
# test phase
random.shuffle(test_words) # randoomize the order of the words in "test_words"
# for each word in "test_words", show that word, along with a reminder "response"
for i in range(len(test_words)):
test_word=visual.TextStim(window, text=test_words[i])
response = visual.TextStim(window, text="1 = Word was on the study list \n 2 = Word was not in the list", pos=(0,-0.8))
test_word.draw()
response.draw()
window.flip()
response=event.waitKeys(keyList=['1','2']) # record their response
print(test_words[i]," ",response[0]) # print the response
# tell the participant to get out of here, we're done with them
showText("You are now done with the study. Please let the experimenter know. Thank you!")
stop_program=event.waitKeys(keyList=['5'])