psychopy.org | Reference | Downloads | Github

No randomisation when using method='random' in code based data.TrialHandler

Any ideas why the following code isn’t shuffling?

Test_Easy = data.TrialHandler(nReps=1, method='random', extraInfo=expInfo, originPath=-1, trialList=data.importConditions('Test_Easy.xlsx'), seed=None, name='Test_Easy')

I had to add shuffle(Test_Easy.trialList) as a workaround.

Best wishes,

Wakefield

What do you mean by “it isn’t shuffling”?

The trialList seemed to have the same order as the original Excel file.

Ahh, OK. TrialHandler doesn’t simply shuffle the trial list (eg that wouldn’t be sufficient for the “full random” setting: bear in mind that nReps can be > 1). Instead, the list stays in the original order and a list of indices is created which determines what entry from that list will be returned on any given trial. I think that would also mean that the original trial list order would be stored in the .psydat file.

Here is the code:

So if you actually run the trial handler, the trials should be selected in a random order.

This is the beauty of a Python iterator object: it can implement a custom method to return the next entry in a loop, rather than just returning the next object in a static, pre-existing list. In our case, this is also how we get to automagically update various attributes like .thisN etc, as being an iterator that implements the required __next__() method, the trial handler knows when each iteration is happening:

Thanks. So do I need a particular piece of code to run the trial handler? Potentially I might want to start, goto next, and goto arbitrary. Presumably I can repeat simply by not going to next.

I’ll need to play about with this.

The TrialHandler is something designed to be iterated over (i.e. when put in a for loop, it will return trials in an order that was specified at the time of its creation). It does have methods to reach backwards or forwards to get future or past trials, but that is more about being able to compare them to the current one.

Maybe your needs are somewhat different from what this class provides. Perhaps you should describe what you are wanting to achieve.

At the moment I’m trying to figure out when and how I should be using TrialHandler instead of my usual method of two dimensional arrays.

The experiment I was working on was an adaptive staircase where I switched between three lists of trials depending on the performance in the most recent three trials.

Normally what I’m doing is interleaving prospective memory targets with a lexical decision task and/or repeating items in an NBack. It sounds like trial handler wouldn’t be ideal for interactive fiction since that relies on being able to skip to any index equally easily.

You mean this one? :wink:

I’d suggest using a TrialHandler but only to get some of its features for free (like recording and saving the data for you). For an adaptive rather than fixed design like this, the TrialHandler won’t really help with selecting conditions though. Just set it up to run 32 trials, without supplying a trial list (not sure of the code for that, but just see what Builder generates if you specify an nReps of 32 and no conditions file).

But otherwise just manage the conditions to be selected on each iteration using your own code. You can see what I suggested in that post above (completely untested), or you might have a much more effective way of doing it.

The TrialHandler can easily be used for regular n-back tasks, as it provides a .getEarlierTrial(n=n) method that allows you to compare the current trial attributes with those n trials earlier.

1 Like

Yes that’s the adaptive staircase. I’ve already finished that job by shuffling the trialList. I hadn’t seen your reply so my solution looks a bit different.

I had a loop in Builder with nReps = 32 but no conditions. Within the loop I drew the items from a one of three trialLists based on a variable called Level. For the staircase I used .pop(0) and .append to store the more recent three values of response.corr. The experiment is working fine but it was the first time I’d read conditions into trialHandler instead of an array, just so I could find out a bit more about it. If I can’t easily ask for the nth trial from it then it probably doesn’t suit my Builder focused method of writing experiments (except insofar as it is a quick way of creating a trialList dictionary which I can use in the same way I usually use arrays).

If you want to get fancy, you could always monkey-patch the __next__() method of a given Builder loop’s TrialHandler object to use your own custom function instead, to return whatever trial you think should be next.