# Oddball paradigm - How to set probabilities of oddball or common stimulus

I am creating an oddball paradigm for an infant study. The experiment shows a 7 minute clip of fantasia. During the video every 6-10 seconds a pair of vibrations are sent out (triggered by an auditory beep sound) with an ISI of 700ms between them and a duration of 100ms for each vibration. For the oddball experiment we’d like to set an expectation of paired stimulation by having x amount of pairs appear first (e.g., 5) and then onwards have paired stimulation occur x% of time (70% chance) whilst 30% only a single vibration occurs.

Below is the code I have related to vibration. Keep in mind I’ve deleted the EEG trigger and tactors (vibration machine) triggers so essentially this looks like an auditory oddball paradigm. My question is how do i set a probability measure? Any help would be greatly appreciated!

``````
# we want to present the tone every
# 6 - 10 seconds for the duration of the trial
# if the sound is not currently playing
if not tactors1.playing and not tactors1.waiting:
# pick how long we will wait for
tactors1ISI = randint(6, 10)
print('tactorISI', tactors1ISI)
tactors1ISIs.append(tactors1ISI)
tactors1Onset = t +tactors1ISI
#we are waiting for the sound to play
tactors1.waiting = True
elif not tactors1.playing and tactors1.waiting:
if t >= tactors1Onset:
print('playing 1st tactor')
first_tactor_played = False
tactors1.play()
tactors1Onsets.append(t)
tactors1.playing = True
tactors1.waiting = False
tactor1_on_times +1
first_tactor_start_time = core.getTime() # Gets a timestamp of the start of the first vibration
first_tactor_played = True
elif tactors1.playing:
if t >= tactors1Onset + tactors1.secs:
tactors1.stop()
tactors1.playing = False
first_tactor_played = True
first_tactor_stop_time = core.getTime()

#2nd vibration

if not tactors2.playing and not tactors2.waiting:
if first_tactor_played == True:
# pick how long we will wait for
tactors2ISI = 0.7
print('tactor2ISI', tactors2ISI)
tactors2ISIs.append(tactors2ISI)
tactors2Onset = t +tactors2ISI
#we are waiting for the sound to play
tactors2.waiting = True
elif not tactors2.playing and tactors2.waiting:
if first_tactor_played == True:
if t >= tactors2Onset:
print('playing 2nd tactor')
tactors2.play()
tactors2Onsets.append(t)
tactors2.playing = True
tactors2.waiting = False
tactor2_on_times +1
#first_tactor_start_time = core.getTime() # Gets a timestamp of the start of the first vibration

elif tactors2.playing:
if t >= tactors2Onset + tactors2.secs:
if first_tactor_played == True:
tactors2.stop()
tactors2.playing = False
first_tactor_played = False
``````

Bumpity bump!

So below I created a function that each time stimulus is presented a random roll between 0-100 happens and for the oddball stimulus to happen the number has to be > 75. This sort of works but it is not optimal because by chance i might get less or more than 25% oddball appearances within the 7 minute trial.

See code below:

percentage_chance_pair = 0.75
dice_roll = random.randint(0,100)

# if the sound is not currently playing

if not tactors1.playing and not tactors1.waiting:
# pick how long we will wait for
tactors1ISI = randint(6, 10)
print(‘tactorISI’, tactors1ISI)
tactors1ISIs.append(tactors1ISI)
tactors1Onset = t +tactors1ISI
#we are waiting for the sound to play
tactors1.waiting = True
pulse_started_tactors1 = False
port_tactors.write([0x1b, 0x54, 0x31, 0x4c, 0x44])
port_tactors.write([0x1b, 0x54, 0x32, 0x52, 0x44])
port_trigger_eeg.write([0x00])
dice_roll = random.randint(0,100)

#elif
if not tactors1.playing and tactors1.waiting:
if t >= tactors1Onset:
print(‘playing 1st tactor’)

``````    first_tactor_played = False
tactors1.play()
tactors1_on_times +=1
tactors1Onsets.append(t)
tactors1.playing = True
tactors1.waiting = False
first_tactor_start_time = core.getTime() # Gets a timestamp of the start of the first vibration
port_tactors.write([0x1b, 0x54, 0x31,0x45])
port_tactors.write([0x1b, 0x54, 0x32,0x45])
first_tactor_played = True
print("the dice roll was", dice_roll)
``````

#elif
if tactors1.playing:
if t >= tactors1Onset + tactors1.secs:
tactors1.stop()
port_tactors.write([0x1b, 0x54, 0x31,0x44])
port_tactors.write([0x1b, 0x54, 0x32,0x44])
tactors1.playing = False
first_tactor_played = True
first_tactor_stop_time = core.getTime()

if tactors1.playing == True and not pulse_started_tactors1:
port_trigger_eeg.write([0x01])
pulse_start_time_tactors1 = globalClock.getTime()
pulse_started_tactors1 = True

``````print("tactor1 has played",tactors1_on_times,"times.")
``````

if pulse_started_tactors1 and not pulse_ended_tactors1:
if globalClock.getTime() - pulse_start_time_tactors1 >= 0.1:
port_trigger_eeg.write([0x00])
port_tactors.write([0x1b, 0x54, 0x31,0x44])
port_tactors.write([0x1b, 0x54, 0x32,0x44])
pulse_ended_tactors1 = True

#2nd vibration

if not tactors2.playing and not tactors2.waiting:
if first_tactor_played == True:
# pick how long we will wait for
tactors2ISI = 0.7
print(‘tactor2ISI’, tactors2ISI)
tactors2ISIs.append(tactors2ISI)
tactors2Onset = t +tactors2ISI
#we are waiting for the sound to play
tactors2.waiting = True
pulse_started_tactors1 = False
port_tactors.write([0x1b, 0x54, 0x31, 0x4c, 0x44])
port_tactors.write([0x1b, 0x54, 0x32, 0x52, 0x44])
#elif
if not tactors2.playing and tactors2.waiting:
if first_tactor_played == True and tactors1_on_times <=5:
if t >= tactors2Onset:
print(‘playing 2nd tactor’)
tactors2.play()
tactors2Onsets.append(t)
tactors2.playing = True
tactors2.waiting = False
port_tactors.write([0x1b, 0x54, 0x31,0x45])
port_tactors.write([0x1b, 0x54, 0x32,0x45])
print("tactor2 has played ",tactors2_on_times,“times.”)

if not tactors2.playing and tactors2.waiting:
if first_tactor_played == True and tactors1_on_times >5:
#if dice_roll <= percentage_chance_pair and t >= tactors2Onset:
if dice_roll <= 75 and t >= tactors2Onset:
print(‘playing 2nd tactor’)
tactors2.play()
tactors2_on_times +=1
tactors2Onsets.append(t)
tactors2.playing = True
tactors2.waiting = False
port_tactors.write([0x1b, 0x54, 0x31,0x45])
port_tactors.write([0x1b, 0x54, 0x32,0x45])
first_tactor_played = True

#elif
if tactors2.playing:
if t >= tactors2Onset + tactors2.secs:
if first_tactor_played == True:
tactors2.stop()
tactors2.playing = False
first_tactor_played = False
port_tactors.write([0x1b, 0x54, 0x31, 0x4c, 0x44])
port_tactors.write([0x1b, 0x54, 0x32, 0x52, 0x44])

if tactors2.playing == True and not pulse_started_tactors2:
port_trigger_eeg.write([0x02])
pulse_start_time_tactors2 = globalClock.getTime()
pulse_started_tactors2 = True
print(“tactor2 has played”,tactors2_on_times,“times.”)

if pulse_started_tactors2 and not pulse_ended_tactors2:
if globalClock.getTime() - pulse_start_time_tactors2 >= 0.1:
port_trigger_eeg.write([0x00])
port_tactors.write([0x1b, 0x54, 0x31,0x44])
port_tactors.write([0x1b, 0x54, 0x32,0x44])
pulse_ended_tactors2 = True

The way to fix this I think is to create upon start of the experiment a randomised array of tactors1ISIs. Following this, another array based on the array above should ensure that exactly 75% of stimuli presented will be pairs and that the remaining 25% are oddball stimuli… Way more complicated than I can do… Any ideas/help on how to do this would be super appreciated

Hi Achilles,

The TrialHandlerExt class can be used to create oddball experiments. it takes a parameter called weight, which can be used to set the probabilities. You can then use the random or fullRandom method for shuffling.

The setting of expectation part is not randomized, and so could be hard-coded.

Best
Suddha

1 Like

Hello Suddha,

I didn’t know about this in-built method. I’ve now created my oddball paradigm in a different way. For anyone interested in doing it my way. This code randomises single (oddball) and pair stimulation but ensures that there can never be two continuous single stimulations in a row:

``````#begin experiment
import random
import serial
from psychopy import sound
import serial
import psychtoolbox as ptb
from psychopy.core import StaticPeriod

#loads the sound file and plays sound for 0.2 seconds
tactors1 = sound.Sound('experiment_audio.wav', name='\$tactors1', stereo = True, secs = 0.2)
tactors1.setVolume(1)
tactors1.playing = False
tactors1.waiting = False

tactors2 = sound.Sound('experiment_audio.wav', name='\$tactors2', stereo = True, secs = 0.2)
tactors2.setVolume(1)
tactors2.playing = False
tactors2.waiting = False
first_tactor_start_time = core.getTime()
first_tactor_stop_time = core.getTime()

first_tactor_played = False

#this is serial port of the tactors. Change COM8 to another number if 'USB Serial Port' in Ports in Device manager has changed COM number
##port_tactors = serial.Serial('COM8',57600)
##port_tactors.write([0x1b, 0x54, 0x31, 0x4c, 0x44])
##port_tactors.write([0x1b, 0x54, 0x32, 0x52, 0x44])

##port_trigger_eeg = serial.Serial('COM5')
##port_trigger_eeg.write([0x00])

tactors1_on_times = 0
tactors2_on_times = 0

#array approach

import random

stimulation_array = []
one_indices = set()

while len(one_indices) != 15:
# set 2nd integer to however many total stimulations you'd like to deliver in total -1
idx = random.randint(0, 39)
if not {idx, idx+1, idx-1} & one_indices:
# range should be total number of stimulations but not total -1
for idx in range(40):
if idx in one_indices:
stimulation_array.append(1)
else:
stimulation_array.append(2)

print(stimulation_array)
length = len(stimulation_array)
array_position_number = 0
value = stimulation_array[array_position_number]

#begin routine

tactors1Onsets=[]
tactors1ISIs=[]

tactors2Onsets=[]
tactors2ISIs=[]

pulse_started_tactors1 = False
pulse_ended_tactors1 = False

pulse_started_tactors2 = False
pulse_ended_tactors2 = False

pulse_started_trigger = False
pulse_ended_trigger = False

tactors1.playing = False
tactors2.playing = False

trial_ended = True

#each frame

# we want to present the tone every
# 5 - 9 seconds for the duration of the trial
# if the sound is not currently playing
if not tactors1.playing and not tactors1.waiting:
if trial_ended == True:
# pick how long we will wait for
tactors1ISI = randint(5, 9)
print('tactorISI', tactors1ISI)
tactors1ISIs.append(tactors1ISI)
tactors1Onset = t +tactors1ISI
#we are waiting for the sound to play
tactors1.waiting = True
tactors2.waiting = False

# If Loop to iterate through list:
if array_position_number < length and tactors1_on_times >5:
value = stimulation_array[array_position_number]
array_position_number += 1
print('array position number is ',array_position_number)

#reset trial variables
pulse_started_tactors1 = False
pulse_ended_tactors1 = False
pulse_started_tactors2 = False
pulse_ended_tactors2 = False
trial_ended = False
elif not tactors1.playing and tactors1.waiting:
if t >= tactors1Onset:
print('playing 1st tactor')
tactors1.play()
tactors1_on_times +=1
tactors1Onsets.append(t)
tactors1.playing = True
tactors1.waiting = False
first_tactor_start_time = core.getTime() # Gets a timestamp of the start of the first vibration
##port_tactors.write([0x1b, 0x54, 0x31,0x45])
##port_tactors.write([0x1b, 0x54, 0x32,0x45])
##port_trigger_eeg.write([0x01])

if tactors1.playing:
if t >= tactors1Onset + tactors1.secs:
tactors1.stop()
##port_tactors.write([0x1b, 0x54, 0x31,0x44])
##port_tactors.write([0x1b, 0x54, 0x32,0x44])
tactors1.playing = False
first_tactor_played = True
first_tactor_stop_time = core.getTime()

if tactors1.playing == True and not pulse_started_tactors1:
pulse_start_time_tactors1 = globalClock.getTime()
pulse_started_tactors1 = True
print("tactor1 has played",tactors1_on_times,"times.")

if pulse_started_tactors1 and not pulse_ended_tactors1:
if globalClock.getTime() - pulse_start_time_tactors1 >= 0.01:
##port_trigger_eeg.write([0x00])
pulse_ended_tactors1 = True

#2nd vibration

if not tactors2.playing and not tactors2.waiting:
if first_tactor_played == True:
# pick how long we will wait for
tactors2ISI = 0.7
print('tactor2ISI', tactors2ISI)
tactors2ISIs.append(tactors2ISI)
tactors2Onset = t +tactors2ISI
#we are waiting for the sound to play
tactors2.waiting = True

if not tactors2.playing and tactors2.waiting:
if first_tactor_played == True and tactors1_on_times <=5:
if t >= tactors2Onset:
print('playing 2nd tactor')
tactors2.play()
tactors2_on_times +=1
tactors2Onsets.append(t)
print("tactor2 has played ",tactors2_on_times,"times.")
##port_tactors.write([0x1b, 0x54, 0x31,0x45])
##port_tactors.write([0x1b, 0x54, 0x32,0x45])
##port_trigger_eeg.write([0x02])
tactors2.playing = True
tactors2.waiting = False

elif first_tactor_played == True and tactors1_on_times >5:
if value>1 and t >= tactors2Onset:
print('playing 2nd tactor')
tactors2.play()
tactors2_on_times +=1
tactors2Onsets.append(t)
print("tactor2 has played ",tactors2_on_times,"times.")
##port_tactors.write([0x1b, 0x54, 0x31,0x45])
##port_tactors.write([0x1b, 0x54, 0x32,0x45])
##port_trigger_eeg.write([0x02])
tactors2.playing = True
tactors2.waiting = False

elif value <= 1:
trial_ended = True
first_tactor_played == False

if tactors2.playing:
if t >= tactors2Onset + tactors2.secs:
tactors2.stop()
tactors2.playing = False
##port_tactors.write([0x1b, 0x54, 0x31,0x44])
##port_tactors.write([0x1b, 0x54, 0x32,0x44])
first_tactor_played = False

if tactors2.playing == True and not pulse_started_tactors2:
pulse_start_time_tactors2 = globalClock.getTime()
pulse_started_tactors2 = True
pulse_ended_tactors2 = False
print("tactor2 has played",tactors2_on_times,"times.")

if pulse_started_tactors2 and not pulse_ended_tactors2:
if globalClock.getTime() - pulse_start_time_tactors2 >= 0.01:
##port_trigger_eeg.write([0x00])
pulse_ended_tactors2 = True
trial_ended = True

``````

Hopefully others trying to create oddball experiments will find this code useful