How to Calculate Average Response Time

Hi,

I have an experiment where Ps will make ratings in several different loops. What I’m trying to do is calculate the response time mean and sd from the trials in the first loop (i.e. summary mean and sd, like the average response time across all trials from that loop). Then, a later loop will call that mean and sd, subtract 1 sd from that mean, and present a countdown clock set to whatever amount of seconds that turned out to be.

So a P will complete several trials in the FCpract routine, and the P-specific mean and sd response time for that set of trials will be calculated at the end of that routine. Then, when the P gets to the FCcontrol routine, they will respond under time pressure with a countdown time set to end at whatever amount of second the mean - 1sd is.

I’ve tried to figure this out, and came across this:

and this:

which got my understanding conceptually what needs to happen. But I’ve hit a roadblock for some reason and can’t seem to integrate those solutions (i.e. code) into my study successfully.

Speaking conceptually, I suspect I will have to:
A) in Begin Routine, create an object that records and holds a list of the response times from the trials in FCpract routine,
B) then in End Routine, have two objects (meanRT and sdRT)ready to do the computation at the end of the routine and store the resulting values.
C) In the later routine, the countdown clock can just be set to ‘meanRT - sdRT’, which will produce the time pressure constraint I’m after.

So my question is: how do I do these steps? I realize there’s a lot in there, so I’ll suffice for just steps A and B if need be, and will figure out the countdown clock on my own (though would definitely appreciate insight on that as well).

I feel very close but can’t seem to identify the specific coding necessary to make this happen.

Thanks for your time.

Yes, in the “Begin routine” tab, but you need a check so this only happens once (on the first (or strictly zeroth)) trial:

if your_loop_name.thisN == 0:
    RT_list = [] # an empty list to hold your values 

No, you just append the RT from each trial to the list. No need to do any calculations until all the values are gathered:

rt_list.append(your_keyboard_component_name.rt)

Then later, in the loop where you want to access these values, calculate them then, in the “begin routine” tab:

if next_loop_name.thisN == 0: # just calculate once
    # turn the list into a numpy array that supports mathematical 
    # operations better:
    rt_array = np.array(rt_list)
    mean_rt = rt_array.mean()
    sd_rt = rt_array.std()

Yes.

Michael,
Very grateful for your swift response.

First, I initially received an error after entering this part:

It said that FCpractTrialsLoop was not defined. I replaced it with the name of the outermost loop (Practice), and that seemed to have resolved it. Is that the correct loop?

After that, I tried running it and it through up a different error:

fc_rt_list.append(FCPract.rt)
AttributeError: ‘RatingScale’ object has no attribute ‘rt’

FCPract is the name of the RatingScale component used in the FCpractTrialsLoop. Is this error happening because I’m not using a keyboard component here?

Thanks for your time.

OK, so I bumped into the issue of my loop name not being defined again when I entered

if next_loop_name.thisN == 0: # just calculate once
    # turn the list into a numpy array that supports mathematical 
    # operations better:
    rt_array = np.array(rt_list)
    mean_rt = rt_array.mean()
    sd_rt = rt_array.std()

My loop name, as you can see in the picture, would be FCcontTrialsLoop. To resolve this error, I did the same thing as my above comment, where I instead put the name of the outermost loop (called ExpBlock). That worked, only now it calculates the mean and sd at the beginning of every loop within that outer loop. Which all appear to be the same number, so that doesn’t feel like an urgent problem.

However, about those numbers…

To get around the “‘RatingScale’ object has no attribute ‘rt’” error, I changed this

rt_list.append(your_keyboard_component_name.rt)

to

rt_list.append(FCPract.getRT())

FYI, “FCPract” is the name of my rating component.

It seems to be working - I checked my data and there is a mean and sd column. However, the mean doesn’t seem to be accurate. I’ve calculate the mean with excel (in bold; m = 0.5562). Compare that to what’s in the fc_mean_rt column (m = 0.6048):

I’m wondering if getRT() retrieves something different from what’s in the FCPract.rt column. The difference seems to large to be just a rounding issue. I also ran the experiment while making deliberately slow choices to see if the mean would be proportionately larger, and it was. Despite my tinkering around, I can’t seem to pin down why those means are different. Any thoughts?

Michael,

Sorry to bombard you with replies! I have managed to solve all of these problems and everything is running smoothly now!!

For those reading, here’s a detailed description of what I did to make this work. I think it’s worth pointing out because I’ve seen this dscrepency in RTs before (this post: Cannot assign fixed (independent of RT) duration to rating scale component - #9 by daniel.riggs1) but it wasn’t resolved (from what I could tell). So to anyone who might of had similar problems, here’s what I did:

  1. In my ‘FCpract’ routine (picture above), I inserted a code component. In ‘Begin Routine’ tab, I entered this following code:
if FCpractTrialsLoop.thisN == 0:
    fc_rt_list = [] # an empty list to hold your values

FYI, ‘FCpractTrialsLoop’ is the name of the inner-most loop around the FCpract routine.

  1. In this same code component, in the ‘End Routine’ tab, I entered this code:
fc_rt_list.append(FCPract.getRT()) # Add RTs to list

thisExp.addData('fc_rt_list', fc_rt_list) # to add column in output so I can verify that 
# these RTs are the same as those automatically recorded by the ratingScale component 
# (which happens when you select 'store rating time' in the ratingScale advanced options)
  1. Lastly, in my routine called ‘InstFCcontrol’, I inserted a code component. In the ‘Begin Routine’ tab, I entered this code:
if FCcontBlock.thisN == 0: # just calculate once
    # turn the list into a numpy array that supports mathematical 
    # operations better:
    rt_array = np.array(fc_rt_list)
    fc_mean_rt = rt_array.mean()
    fc_sd_rt = rt_array.std()



thisExp.addData('fc_mean_rt', fc_mean_rt) #save to output data file
thisExp.addData('fc_sd_rt', fc_sd_rt) # save to output data file

I verified that all was going well by checking the output data file:

You can see the history of RTs being appended to the list, and can also see that the mean and sd calculated with excel (in bold) matches those calculated and stored by psychopy.

Also, the error about my loop not being defined (i.e. in ‘If your_loop_name.thisN ==0’…) was solved by changing which specific code component was holding this code. Apparently, when you enter code into a component that chronologically comes before the creation some object - in my case, the loop around a routine that hadn’t occurred yet in the code - python won’t have a history of that object yet thus won’t know what you’re talking about when you refer to it. I hadn’t appreciated this subtlety of the code component/builder view relationship, but I think it’s worth spelling out here because not appreciating it caused me a lot of headache!

Lastly, the mean and sd is only being recorded and stored once (as opposed to multiple times as it was doing before; see above post). This also had to do with which particular code component held the code (i.e. ‘loppname.thisN ==0’…). Before, I was putting the code for calculating mean and sd in the RandCode2 code component, and putting ExpBlock as my loop name. But this caused it to calculate mean and sd for every block within the ExpBlock loop (i.e. four total times by the end of the experiment). Putting this code into the code component in the FCcontrol routine and entering the name of the block (FCcontBlock) made it to where pyschopy would only calculate mean and sd only once (i.e. when that block occurs). Which is obviously what you want here!

I hope this is helpful to others who find themselves googling solutions to these roadblocks!

Matthew

2 Likes

Hi, I’m travelling at the moment but perhaps it was just as well that I didn’t get a chance to reply, as I think you would have gained a lot more by working through and resolving all of these issues yourself. Well done for doing that but also for providing a chronicle of your findings, which others might benefit from.