Part 4.5: Reaction times, lexical decision task

Good old lexical decision task… It’s where you show participants a word like “THREAD” or a nonword like “DERHTA” and you ask them whether the word you showed them is a real word or not. If you’re not a cognitive psychologist, you may not see how this is a useful exercise.

Studies often find that, when you flash a word like “DOCTOR” for a split second and then show them a semantically related word like “NURSE” right after, people are quicker to decide that “NURSE” is a word (as opposed to a non-word) compared to if you had shown them a semantically unrelated prime like “GASOLINE” ahead of nurse. In this “study”, we’ll show people a “prime” word for 500 milliseconds, then show them a target word that is either semantically related to the prime word, semantically unrelated, or a non-word.

Set up: Import packages, create a window, and a handy showText function

We start the code by importing all the packages we need, creating a window to present everything in, and defining a “showText” function we can use throughout the script so we don’t have to constantly say “blah.draw()” and “window.flip()” all the time.

#   Set up. Importing packages and setting up a window to show stuff in.
from psychopy import visual, event, core
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()

Set up the stimuli

Next, we’ll define three lists, one containing semantically related words, another containing semantically unrelated words, and a final list containing non-words. You might recognize some of these stimuli from a previous blog post, and that the non-words as lazy scrambling of the non-related words. I’m not paid hourly. Or at all…

# Setting up the stimuli to be used in the learning and test phase
related_words=["THREAD","HURT","THORN","POINT","KNITTING","THIMBLE","CLOTH","SEWING","HAYSTACK","SHARP","INJECTION","PIN","SYRINGE","EYE","PRICK"]
not_related_words=["FEAR","STEAL","RIPE","SKY","SILL","DELAY","HAND","DESK","SEAT","COLOR","REST","JAZZ","BEARD","FLOUR","SODA"]
non_words=["EARF","TSAEL","ERIP","KSY","LISL","EALDY","NAHD","SEDK","TEAS","ROLOC","ERST","ZAJZ","BERDA","RULOF","DOSA"]

Get the clock out

Next, we’ll define a… let’s call it a function… called “clock”. We’ll use this “clock” to keep track of time. Crazy, right?

# Get the clock out
clock = core.Clock()

You can use “clock” like a stopwatch. You can create a start time with the command “start=clock.getTime()” and a stop time with the command “stop=clock.getTime()”. When you have these “start” and “stop” times recorded, you can simply record a reaction time (RT) as “RT=finish-start”. We’ll do that later though. Just wanted let you know.

Setting up the data

Next, we’ll create a list where the first element is… ANOTHER LIST!

# Setting up the data
data=[["prime","target","decision","rt"]]

Why do this? Well, the first (and right now, the only) element of the list is the header row for our data. Every new element I add will be a new row of actual data. I can just say, “data.append([whatever the prime word was, whatever the target was, which key they clicked, how long it took])” later on… except you’d have to put real inputs because that line I just put in quotes would give your computer a headache.

Instructions

Ideally, you’d have the instructions advance to “the next page”, so to speak, via user input. Like, you’d have the participant press a key to continue. In some of the other posts here, there’s code for how to do that. Here, I just had each “page” of instructions appear for 5 seconds each because I didn’t feel like going look for that code to copy and paste. I certainly wasn’t going to re-write it!

# instructions
showText("In the following study, you'll be shown one word real fast, and then a TARGET word.")
time.sleep(5)
showText("You need to decide whether the TARGET word is a real word or not.")
time.sleep(5)
showText("Press the 1 key if the TARGET word is a real word and the 2 key if it isn't.")
time.sleep(5)

Starting a “for” loop

Next, we’re going to start a “for” loop that will repeat for however many trials we want to have during the study. I picked 10 just for giggles.

for i in range(10):

I chose 10 because it works for this toy example of a study and was more convenient while writing and testing the code. You’d probably want a lot more trials in a real study. As a reminder, when you initiate a “for” loop with a line like this, everything indented under the statement will occur for range(N) times. On each iteration, you can use “i” (or whatever you want to call it, most people say “i”) as a reference for which iteration you’re on. I don’t end up using that feature in this “study”, but if you wanted to record the trial numbers, for example, you could say “trial_number=i” and put “trial_number” or just “i” itself in your new data rows and that’d work.

Draw the prime word randomly from either related_words or non_related_words

First, we’ll use “random.sample()” to basically flip a coin: heads (or “zero”) for the prime word to be drawn from “related_words” and tails (or “one”) for the prime word to be drawn from “not_related_words”. The first argument in “random.sample” specifies the range of possibilities. Since we want 2 (heads or tails), we’ll put “range(2)”. You also have to specify how many “flips” you’re making. We’re doing 1 flip. So, we end up with “random.sample(range(2),1)” to get our coin flip. Since the end result of this command is a list (even though it has only one element this time), you therefore need to specify which element of the list you want to work with. So, we’ll add a [0] at the end, because, in pythonese, zero is the first element in a list.

    # draw the prime word randomly from either related_words or not_related_words
    prime=random.sample(range(2),1)[0]
    if(prime==0):
        prime=related_words[random.sample(range(len(related_words)),1)[0]]
    if(prime==1):
        prime=not_related_words[random.sample(range(len(not_related_words)),1)[0]]

After we’ve drawn a random “0” or “1” and saved that result as “prime” we’ll use some if-then statements to re-assign “prime” as one of the words from either “related_words” or “not_related_words”. The “if” parts should be pretty straightforward by now, but the “then” parts might look crazy at first glance. They’re not THAT crazy though. The outer part is “related_words[ <index>]” or “not_related_words[ <index> ]”, depending on how our coin flip turned out. It’s just saying “prime” is equal to one of the words from one of those lists. Inside the index brackets “[]” we’re drawing a random sample just like before, but the “range()” is the length (or “len()” in pythonese) of those lists. It all might look crazy, but if you break it down, bit by bit, it’s all stuff you’ve seen already.

Draw the target word randomoly from related_words, not_related_words, or non_words

We’re still indented under the “for” loop. All this code is being executed on each iteration through the loop.

    # draw the target word randomly from related_words or not_related_words OR non_words
    target=random.sample(range(3),1)[0]
    if(target==0):
        target=related_words[random.sample(range(len(related_words)),1)[0]]
    if(target==1):
        target=not_related_words[random.sample(range(len(not_related_words)),1)[0]]
    if(target==2):
        target=non_words[random.sample(range(len(non_words)),1)[0]]

The logic here is very similar to when we assigned a prime word, it’s just been expanded to have a third possibility: being a non-word.

Fixation cross and prime word

Now that we’ve assigned a prime word and a target word for the trial we’re on, we’ll show a fixation cross and the prime word for 1 second and half a second, respectively. This is easy to do since we defined a handy function called “showText()” at the beginning of the script.

    # Show fixation cross for 1 second
    showText('+')
    time.sleep(1)
    
    # Show the prime for 0.5 seconds
    showText(prime)
    time.sleep(0.5)

Feel free to adjust these times based on your preferences.

Show target word, collect decision, RT

“showText()” makes our lives so easy. We’ll use it now to show the target word, then we’ll create a “start” time right after we show it. Nothing will happen until the participant clicks “1” or “2”, which will be saved as the first element in a list called “response”. After they’ve clicked a key, we collect a “finish” time and save “RT” as the difference between the start and finish time.

    # Show the target word
    showText(target)
    # start recording reaction time
    start=clock.getTime()
    response=event.waitKeys(keyList=['1','2'])
    # finish recording RT
    finish=clock.getTime()
    RT=finish-start
    data.append([prime,target,response[0],RT])

We cap things off by appending the prime word, target word, zeroth element of “response”, and RT as a new “row” to our “data”, which is a list of lists, the first element of which is the header for the data. This is the last block of code in our “for” loop. Everything between “for i in range(10):” and here will be repeated 10 times, or however many trials you specify.

Export the data and say bye

This last bit should be pretty straightforward, given the previous posts. We export “data” as a CSV file in the same directory the script is being stored in on your computer. After that, the screen says, “Get out of here. We’re done with you.” In so many words. You have to press the “D” key to exit the program for real.

#   EXPORT DATA
import csv
with open(time.strftime('%m-%d-%y')+time.strftime(' %H%M')+' data.csv', 'w') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerows(data)



# Tell the participant to leave
showText("You are now done with the study. Please let the experimenter on duty know.")
resp_key = event.waitKeys(keyList=['d'])

The complete code

And as usual, here’s the completed code in case you wanted to just copy and paste the whole thing into psychopy.

#   Set up. Importing packages and setting up a window to show stuff in.
from psychopy import visual, event, core
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
related_words=["THREAD","HURT","THORN","POINT","KNITTING","THIMBLE","CLOTH","SEWING","HAYSTACK","SHARP","INJECTION","PIN","SYRINGE","EYE","PRICK"]
not_related_words=["FEAR","STEAL","RIPE","SKY","SILL","DELAY","HAND","DESK","SEAT","COLOR","REST","JAZZ","BEARD","FLOUR","SODA"]
non_words=["EARF","TSAEL","ERIP","KSY","LISL","EALDY","NAHD","SEDK","TEAS","ROLOC","ERST","ZAJZ","BERDA","RULOF","DOSA"]

# Get the clock out
clock = core.Clock()

# Setting up the data
data=[["prime","target","decision","rt"]]

# instructions
showText("In the following study, you'll be shown one word real fast, and then a TARGET word.")
time.sleep(5)
showText("You need to decide whether the TARGET word is a real word or not.")
time.sleep(5)
showText("Press the 1 key if the TARGET word is a real word and the 2 key if it isn't.")
time.sleep(5)


for i in range(10):
    # draw the prime word randomly from either related_words or not_related_words
    prime=random.sample(range(2),1)[0]
    if(prime==0):
        prime=related_words[random.sample(range(len(related_words)),1)[0]]
    if(prime==1):
        prime=not_related_words[random.sample(range(len(not_related_words)),1)[0]]
    
    # draw the target word randomly from related_words or not_related_words OR non_words
    target=random.sample(range(3),1)[0]
    if(target==0):
        target=related_words[random.sample(range(len(related_words)),1)[0]]
    if(target==1):
        target=not_related_words[random.sample(range(len(not_related_words)),1)[0]]
    if(target==2):
        target=non_words[random.sample(range(len(non_words)),1)[0]]
    
    # Show fixation cross for 1 second
    showText('+')
    time.sleep(1)
    
    # Show the prime for 0.5 seconds
    showText(prime)
    time.sleep(0.5)
    
    # Show the target word
    showText(target)
    # start recording reaction time
    start=clock.getTime()
    response=event.waitKeys(keyList=['1','2'])
    # finish recording RT
    finish=clock.getTime()
    RT=finish-start
    data.append([prime,target,response[0],RT])


#   EXPORT DATA
import csv
with open(time.strftime('%m-%d-%y')+time.strftime(' %H%M')+' data.csv', 'w') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerows(data)



# Tell the participant to leave
showText("You are now done with the study. Please let the experimenter on duty know.")
resp_key = event.waitKeys(keyList=['d'])


Some suggestions

One thing you might want to change if you actually implement this is to ensure that the prime word and the target word aren’t the same. You might also take the initial trial type codes (e.g., “0” = a semantically related prime word, “2” = a non-word for the target word), save those under different names, and add them to the data that’s recorded on each trial. That would make it easier to analyze the data later. You wouldn’t have to manually code the data or write a script to code it for you. You’d probably also want to clean up the user experience in some ways. For example, you could add a prompt at the bottom when the test word is being shown, saying something like “Press 1 if it’s a word, Press 2 if it’s a non-word” and then say what their decision was on screen after they’ve pressed one of the keys.

Leave a comment

Design a site like this with WordPress.com
Get started