[PYTHON] Get negative reaction time with psychopy.event.getKeys ()

Introduction

In this article, as the title suggests, when using psychopy.event.getKeys (), we will introduce that you can get a negative reaction time if ** conditions are met. Basically, the negative reaction time is not what the experiment creator wants, so I hope that it will be helpful when you get it and say "Is it a bug? I'm in trouble." Sometimes). The point is, don't forget ʻevent.clearEvents ()` if you don't want a negative reaction time.

** (Updated 20191119) ** [win.flip () seems to be important](#winflip also seems to be important 20191119 added) was added. Apparently getting a negative reaction time is not straightforward.

Method

After pressing the key, execute .reset () on the core.Clock object for reaction time measurement (for example, the variable name stopwatch), andpsychopy.event.getKeys (timeStamped = stopwatch). You get a negative reaction time. Let's look at the code that actually gives a negative reaction time.

Code example 1

get-neg-rt_1.py


from psychopy import visual, core, event

win = visual.Window()
stopwatch = core.Clock()
stim = visual.TextStim(win)
l_letter = ['a','b','c']

for letter in l_letter:
    stim.setText(letter)
    stim.draw()
    win.flip()
    core.wait(2) #Enter the key while presenting the stimulus
    stopwatch.reset() #Reset clock after keystroke
    resp = event.getKeys(timeStamped=stopwatch) #Key input processing
    print(resp)

win.close()

#Output example
# [['space', -0.17376430198783055], ['space', -0.16929624899057671]]
# [['space', -0.19804733199998736]]
# [['space', -0.19725568499416113]]

If you reverse core.wait () and stopwatch.reset (), you will not get a negative reaction time (even if it is a positive value, you still cannot get the reaction correctly. How to get it correctly Is introduced in this article). However, I don't think anyone writes such code. It should reset the clock before (that is, before core.wait ()) before or immediately after presenting the stimulus (at the beginning of the trial). However, the following example is a shameful mistake that I actually made.

Code example 2

get-neg-rt_2.py


from psychopy import visual, core, event

win = visual.Window()
stopwatch = core.Clock()
stim = visual.TextStim(win)
l_letter = ['a','b','c']

for letter in l_letter:
    stim.setText(letter)
    resp = []
    # event.clearEvents()
    stopwatch.reset()
    for n_frame in range(120): # 120f(2 seconds if the refresh rate is 60hz)
        stim.draw()
        win.flip()
        if not resp:
            resp = event.getKeys(timeStamped=stopwatch) #Key input processing

    print(resp)

win.close()

#Output example
# [['space', 0.8996025759843178]]
# [['space', -0.34911786299198866]]
# [['space', -1.4165731929824688], ['space', -0.6160959279804956]]

This code, like the code above, captures the response while presenting the stimulus for 2 seconds. The difference is that we intend to capture only the first reaction of each trial. If resp = [] is set before the stimulus is presented and resp remains an empty list, not resp becomes True, so ʻevent.getKeys ()is executed. If there is a key input,[[key, rt]] is assigned to resp, and in the subsequent loops, not resp becomes Falseand the key input is not processed. Furthermore, unlike Example 1, I'm runningstopwatch.reset () before the block presented, so at first glance it looks like there's nothing wrong with it (I thought I wasn't there). However, if you press the key more than once while presenting a certain stimulus, you will get a negative reaction time as shown in the output example. The solution is to enable ʻevent.clearEvents (), which is commented out in the code.

Why this happens

I've only recently noticed that (at least) psychopy has a ʻevent bufferthat corresponds to each key (or mouse, joystick) input, and it seems that the input is held there. This is also done when runningcore.wait (). ʻEvent.getKeys () retrieves all input held in the ** keyboard ** bufffer at that time (Official Reference # psychopy.event.getKeys)). And if timeStamped is specified, the difference between the time recorded in buffer (unix time) and the time indicated by the specified core.Clock object (stopwatch in the code example) isIt returns the value recalculated with the timing of .reset ()as 0. This gives reaction time. For these reasons, if .reset () is sandwiched between the key input andgetKeys ()as in Example 1, a negative reaction time will be obtained, and as in Example 2, one will be obtained. Even if it seems that only the reaction of the eye is taken out, the second and subsequent reactions of the same trial are held in the buffer, so they will be processed by getKeys () of the next loop. Become. The keystroke itself is before .reset (), so the time returned will be negative. The clearEvents () presented as the solution in Example 2 removes the input held in the buffer. By doing this just before the start of each trial, the second and subsequent reactions that occurred in the previous trial will not affect the next trial. Even if getKeys () is executed, the key input up to that point will disappear from the buffer.

In Example 1, you could just sort core.wait () and .reset (), but that doesn't mean it's always safe to use core.wait (). As shown in the code below, if you set a blank between trials and press a key with that blank, you will get a negative reaction time. Again, you can prevent negative reaction times by running ʻevent.clearEvents ()beforecore.wait ()(orwin.flip ()). In any case, it's important to remember ʻevent.clearEvents ().

get-neg-rt_3.py


from psychopy import visual, core, event

win = visual.Window()
stopwatch = core.Clock()
stim = visual.TextStim(win)
l_letter = ['a','b','c']

for letter in l_letter:
    # event.clearEvents()
    stim.setText(letter)
    stim.draw()
    win.flip()
    stopwatch.reset() #Reset clock after keystroke
    core.wait(2) #Enter the key while presenting the stimulus
    resp = event.getKeys(timeStamped=stopwatch) #Key input processing
    print(resp)
    # ITI
    win.flip()
    core.wait(1) #If you press the key here, you will get a negative value

win.close()

By the way, ʻevent.waitKeys () is set to execute ʻevent.clearEvents () internally by default at runtime, so you don't need to specify it ([Official Reference](https: /). /www.psychopy.org/api/event.html#psychopy.event.waitKeys)).

win.flip () also seems to be important (20191119 postscript)

After trying various things, I found that the story was not so simple, and that win.flip () also affected the reaction time calculation with getKeys (). If you execute the following code, the reaction time of the key press carried over from the previous loop will be less than 10ms.

get-neg-rt_4.py


from psychopy import visual, core, event

win = visual.Window()
stopwatch = core.Clock()
stim = visual.TextStim(win)
l_letter = ['a','b','c']

for letter in l_letter:
    stim.setText(letter)
    resp = []
    # event.clearEvents()
    stopwatch.reset()
    stim.draw()
    win.flip()
    while stopwatch.getTime() < 2:
        if not resp:
            resp = event.getKeys(timeStamped=stopwatch) #Key input processing

    print(resp)

win.close()

#Output example
# [['down', 0.5086547629907727]]
# [['down', 0.0033315849723294377], ['down', 0.0070590279647149146]]
# [['down', 0.004310511983931065], ['down', 0.009222752996720374]]

By entering the loop of while stopwatch.getTime () <2 after presenting the stimulus, the process of acquiring the reaction time for 2 seconds will continue to be repeated. The stimulus remains visible because the screen is not refreshed until the while loop ends and the nextwin.flip ()is executed. It should be the same as the second example except that stim.draw () and win.flip () are not repeated, but I can no longer get a negative reaction time. However, it does not seem to be appropriate as a reaction time. By putting draw () and flip () in the while loop, you can get a negative reaction time as in the second example. Apparently, it seems that the timing of key input is reset by win.flip () [^ 1], and in this example, getKeys () is executed immediately after that, so a value of about 10 ms is obtained. It seems that you can do it. The code below is the rationale for assuming that the input timing is "reset", that is, it retains the input timing before win.flip ().

[^ 1]: However, it is probably fixed to the value updated by the first win.flip (). In the second example, win.flip () is repeated many times with the key input remaining in the event buffer, but a negative reaction time like that is obtained. I'm not sure if this guess is correct because I haven't verified it.

get-multi-rt_4.py


from psychopy import visual, core, event

win = visual.Window()
stopwatch = core.Clock()
stim = visual.TextStim(win)
l_letter = ['a','b','c']

for letter in l_letter:
    stim.setText(letter)
    resp = []
    stopwatch.reset()
    stim.draw()
    win.flip()
    while stopwatch.getTime() < 2:
        resp += event.getKeys(timeStamped=stopwatch) #Key input processing

    print(resp)

win.close()

In this code, as in the previous example, the key input is detected for 2 seconds without updating the screen, but the difference is that multiple reactions are accepted in one trial. In this case, win.flip () has not been executed for 2 seconds, but the timing of each key input seems to be measured correctly. I'm not sure why using win.flip () updates the information held in the ʻevent buffer, and that the update probably happens only once. If anyone knows it, I would appreciate it if you could teach me. In any case, unless you really need a negative reaction time, we recommend using ʻevent.clearEvents ().

in conclusion

In this article, we have introduced the case where psychopy.event.getKeys () returns a negative reaction time. It seems that the negative reaction time was a bug born from my pride and pride that it would be okay because I tried to get only the first reaction with the conditional expression of ʻif not resp:. I feel that the event buffer was explained everywhere (Professor Togawa's book?), But I think there was no article that explained it from the perspective of such a bug. Negative reaction times may also be useful in some experiments. In that case, please consult with your own experimental procedure and handle win.flip () well. ʻEvent.clearEvents () is the basic "ki" in getting a reaction. Don't forget when you get used to it.

Recommended Posts

Get negative reaction time with psychopy.event.getKeys ()
Get standard output in real time with Python subprocess
Get started with MicroPython
Get Tweets with Tweepy
Get date with python
Get started with Mezzanine
python get current time
Get YouTube Live chat field in real time with API
Get country code with python
Get table dynamically with sqlalchemy
Get started with Django! ~ Tutorial ⑤ ~
Get Twitter timeline with python
Standard input with time limit
Get started with influxDB + Grafana
Get Youtube data with python
Get information with zabbix api
Get started with Django! ~ Tutorial ④ ~
Get started with Django! ~ Tutorial ⑥ ~
Execution time measurement with Python With
Get thread ID with python
Get started with Python! ~ ② Grammar ~
Get image features with OpenCV
Get stock price with Python
Get home directory with python
Get keyboard events with python
Time synchronization (Windows) with Python
Get Alembic information with Python
Get another tab with pyppeteer
Get ranking with Rakuten API