Thank you for the idea Michael - I thought the code might appear via git link (I’m very new to using github/pavlovia)
Upon starting the experiment, I use this code (Py):
# Dependencies
import itertools # for flattening lists of lists into lists
import random
import math # for math.ceil() rounding up
# Import stimuli exemplars
exemplars_filename = 'stimuli.xlsx'
exemplars = data.importConditions(exemplars_filename)# Import stimuli exemplars
# Determine rows of examplars (i.e., max number of rows)
"""
This method creates a fully counterbalanced presentation of exemplars when there are 5
of them, but it will not present each one an equal number of times it the n diverges from 5.
"""
n_exemplars = len(exemplars)
list_multiplier = int(math.ceil(10/n_exemplars)) # math.ceil() rounds up. 10 is the derived from way block lengths are calculated. Admittedly, this comment doensn't adequately document why it's ten. Honestly, I have to work it out of my fingers every time and can't explain it.
# Trial generation function
def generate_trials(trial_type_column, multiplier):
"""Generate a shuffled list of stimuli exemplars from a column in an excel stimuli file"""
a = dict() # declare a dict to be populated
for i in range(len(exemplars)):
a[i] = [exemplars[i][trial_type_column]] * multiplier # populate the dict from vertical reads of the conditions
a = a.values() # extract only values (and not keys) from the list of dicts
a = list(itertools.chain(*a)) # flatten the list of dicts into a list
random.shuffle(a) # shuffle this list, so that it can be drawn from by the trials
return a
# declare trial rows (not sure if necessary, but can't be bothered to removed and test)
trial_rows = ""
# set block order based on participant code
participantNumber = int(expInfo['participant'])
if (participantNumber % 2) == 1:
block_order = 1
elif (participantNumber % 2) == 0:
block_order = 2
else:
print ("****condition file error: please enter a numeric participant code****")
___My attempt at writing it in JavaScript
csv = 'stimuli.csv'
var exemplars = .csv.toObjects(csv):
# loaded in stimuli (above)
var n_exemplars = exemplars.length;
list_multiplier = parseInt(math.ceil(10/n_exemplars))
function generate_trials(trial_type_column, multiplier) {
var a = {};
var i;
for (i = 0; i < n_exemplars; i++) {
a[i] = [exemplars[i][trial_type_column]] * multiplier
}
//#randomizing values in a
var rand = a[Math.floor(Math.random() * a.length)];
}
trial_rows = ""
participantNumber = parseInt(expInfo['participant'])
if (participantNumber % 2) == 1:
block_order = 1
elif (participantNumber % 2) == 0:
block_order = 2
else:
print ("****condition file error: please enter a numeric participant code****")
___________________________________________
Upon starting each Block, this is the Py code:
# set the block length and the rows to pull from based on the current block
# this layout follows Nosek et al. 2007, "The Implicit Association Test at age 7: A methodological and conceptual review"
if blocks.thisN == 0:
trial_rows = "0:2"
n_block_repeats = 10 #2*10 = 20 trials
modified_list_multiplier = list_multiplier
elif blocks.thisN == 1:
trial_rows = "2:4"
n_block_repeats = 10 #2*10 = 20 trials
modified_list_multiplier = list_multiplier
elif blocks.thisN == 2:
trial_rows = "0:4"
n_block_repeats = 5 #4*5 = 20 trials
modified_list_multiplier = list_multiplier
elif blocks.thisN == 3:
trial_rows = "0:4"
n_block_repeats = 10 #4*10 = 40 trials
modified_list_multiplier = list_multiplier
elif blocks.thisN == 4:
trial_rows = "0:2"
n_block_repeats = 20 #2*20 = 40 trials
modified_list_multiplier = list_multiplier * 2 # because this block has a different trials:categories ratio
elif blocks.thisN == 5:
trial_rows = "0:4"
n_block_repeats = 5 #4*5 = 20 trials
modified_list_multiplier = list_multiplier
elif blocks.thisN == 6:
trial_rows = "0:4"
n_block_repeats = 10 #4*10 = 40 trials
modified_list_multiplier = list_multiplier
# Generate list of stimuli for the block
text_trial_type_1_trials = generate_trials('text_trial_type_1_exemplars', modified_list_multiplier) # function and variable determined at begin exp.
text_trial_type_2_trials = generate_trials('text_trial_type_2_exemplars', modified_list_multiplier)
text_trial_type_3_trials = generate_trials('text_trial_type_3_exemplars', modified_list_multiplier)
text_trial_type_4_trials = generate_trials('text_trial_type_4_exemplars', modified_list_multiplier)
img_trial_type_1_trials = generate_trials('img_trial_type_1_exemplars', modified_list_multiplier)
img_trial_type_2_trials = generate_trials('img_trial_type_2_exemplars', modified_list_multiplier)
img_trial_type_3_trials = generate_trials('img_trial_type_3_exemplars', modified_list_multiplier)
img_trial_type_4_trials = generate_trials('img_trial_type_4_exemplars', modified_list_multiplier)
# set category and attribute labels based on the block order and current block
if block_order == 1 and blocks.thisN <= 3:
leftCategory = categoryA
rightCategory = categoryB
elif block_order == 1 and blocks.thisN > 3:
leftCategory = categoryB
rightCategory = categoryA
elif block_order == 2 and blocks.thisN <= 3:
leftCategory = categoryB
rightCategory = categoryA
elif block_order == 2 and blocks.thisN > 3:
leftCategory = categoryA
rightCategory = categoryB
This is my attempt for the start of each block in JavaScript:
if blocks.thisN == 0:
trial_rows = "0:2"
n_block_repeats = 10 //#2*10 = 20 trials
modified_list_multiplier = list_multiplier
else if blocks.thisN == 1:
trial_rows = "2:4"
n_block_repeats = 10 //#2*10 = 20 trials
modified_list_multiplier = list_multiplier
else if blocks.thisN == 2:
trial_rows = "0:4"
n_block_repeats = 5 //#4*5 = 20 trials
modified_list_multiplier = list_multiplier
else if blocks.thisN == 3:
trial_rows = "0:4"
n_block_repeats = 10 //#4*10 = 40 trials
modified_list_multiplier = list_multiplier
else if blocks.thisN == 4:
trial_rows = "0:2"
n_block_repeats = 20 //#2*20 = 40 trials
modified_list_multiplier = list_multiplier * 2 # because this block has a different trials:categories ratio
else if blocks.thisN == 5:
trial_rows = "0:4"
n_block_repeats = 5 //#4*5 = 20 trials
modified_list_multiplier = list_multiplier
else if blocks.thisN == 6:
trial_rows = "0:4"
n_block_repeats = 10 //#4*10 = 40 trials
modified_list_multiplier = list_multiplier
//# Generate list of stimuli for the block
text_trial_type_1_trials = generate_trials('text_trial_type_1_exemplars', modified_list_multiplier) # function and variable determined at begin exp.
text_trial_type_2_trials = generate_trials('text_trial_type_2_exemplars', modified_list_multiplier)
text_trial_type_3_trials = generate_trials('text_trial_type_3_exemplars', modified_list_multiplier)
text_trial_type_4_trials = generate_trials('text_trial_type_4_exemplars', modified_list_multiplier)
img_trial_type_1_trials = generate_trials('img_trial_type_1_exemplars', modified_list_multiplier)
img_trial_type_2_trials = generate_trials('img_trial_type_2_exemplars', modified_list_multiplier)
img_trial_type_3_trials = generate_trials('img_trial_type_3_exemplars', modified_list_multiplier)
img_trial_type_4_trials = generate_trials('img_trial_type_4_exemplars', modified_list_multiplier)
//# set category and attribute labels based on the block order and current block
if block_order == 1 and blocks.thisN <= 3:
leftCategory = categoryA
rightCategory = categoryB
else if block_order == 1 and blocks.thisN > 3:
leftCategory = categoryB
rightCategory = categoryA
else if block_order == 2 and blocks.thisN <= 3:
leftCategory = categoryB
rightCategory = categoryA
else if block_order == 2 and blocks.thisN > 3:
leftCategory = categoryA
rightCategory = categoryB
Both the Py code and JavaScript Code start the experiment with this line of code:
#declare accuracy feedback message variable
msg=""
Py code for the start of each trial:
# choose a random exemplar from the appropriate trial type list
if trial_type == 1:
text_stimulus = text_trial_type_1_trials.pop()
image_stimulus = img_trial_type_1_trials.pop()
elif trial_type == 2:
text_stimulus = text_trial_type_2_trials.pop()
image_stimulus = img_trial_type_2_trials.pop()
elif trial_type == 3:
text_stimulus = text_trial_type_3_trials.pop()
image_stimulus = img_trial_type_3_trials.pop()
elif trial_type == 4:
text_stimulus = text_trial_type_4_trials.pop()
image_stimulus = img_trial_type_4_trials.pop()
# set stimulus colors based on trial type
if trial_type == 1 or trial_type == 2:
stimulusColor = [1, 1, 1]
elif trial_type >2:
stimulusColor = [-1, 1, -1]
# set required and feedback responses
# attributes are invariate across blocks so can be determined based on trial type only
if trial_type == 3: #pos
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
elif trial_type == 4: #neg
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
# categories depend on block order, current block and trial type
if block_order == 1:
if blocks.thisN <= 3:
if trial_type == 1: #flowers
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
elif trial_type == 2: #insects
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
elif blocks.thisN >= 3:
if trial_type == 1: #flowers
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
elif trial_type == 2: #insects
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
elif block_order == 2:
if blocks.thisN <= 3:
if trial_type == 1: #flowers
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
elif trial_type == 2: #insects
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
elif blocks.thisN >= 3:
if trial_type == 1: #flowers
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
elif trial_type == 2: #insects
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
JavaScript for the start of each trial:
//# choose a random exemplar from the appropriate trial type list
if trial_type == 1:
text_stimulus = text_trial_type_1_trials.pop()
image_stimulus = img_trial_type_1_trials.pop()
else if trial_type == 2:
text_stimulus = text_trial_type_2_trials.pop()
image_stimulus = img_trial_type_2_trials.pop()
else if trial_type == 3:
text_stimulus = text_trial_type_3_trials.pop()
image_stimulus = img_trial_type_3_trials.pop()
else if trial_type == 4:
text_stimulus = text_trial_type_4_trials.pop()
image_stimulus = img_trial_type_4_trials.pop()
//# set stimulus colors based on trial type
if trial_type == 1 or trial_type == 2:
stimulusColor = [1, 1, 1]
else if trial_type >2:
stimulusColor = [-1, 1, -1]
//# set required and feedback responses
//# attributes are invariate across blocks so can be determined based on trial type only
if trial_type == 3: //#pos
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
else if trial_type == 4: //#neg
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
//# categories depend on block order, current block and trial type
if block_order == 1:
if blocks.thisN <= 3:
if trial_type == 1: //#flowers
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
else if trial_type == 2: //#insects
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
else if blocks.thisN >= 3:
if trial_type == 1: //#flowers
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
else if trial_type == 2: //#insects
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
else if block_order == 2:
if blocks.thisN <= 3:
if trial_type == 1: //#flowers
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
else if trial_type == 2: //#insects
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
else if blocks.thisN >= 3:
if trial_type == 1: //#flowers
requiredAllowed = "i"
requiredCorrect = "i"
feedbackAllowed = "e"
feedbackCorrect = "e"
else if trial_type == 2: //#insects
requiredAllowed = "e"
requiredCorrect = "e"
feedbackAllowed = "i"
feedbackCorrect = "i"
finally I use this code for each frame for every trial:
Py -
if len(feedbackResponse.keys)<1:
msg=""
else:
msg="X"
JavaScript-
if len(feedbackResponse.event.keycode)<1:
msg=""
else:
msg="X"