STIM Files
STIM files in SightLab allow you to easily manipulate an independent variable in a study. For example, you can adjust the size or position of an object per trial, and these conditions can be randomized or iterated through sequentially. Other potential conditions might be things such as a category of video to play, a specific environment that a participant will be in, etc.
SightLab has a built-in StimReader class which reads the contents of a STIM file in .csv format. To create a STIM file, you first define the variables you want to adjust and then declare what the value of that variable will be.
For example, to manipulate the size and position of an object (e.g., a basketball), you would do the following:
-
Create a STIM file with entries for 'object size' and 'object position (note that since python is sensitive to spaces, make sure to not have spaces between any values).
-
For certain situations you might want to use a config file to define the possible values for 'object size' and 'object position'. This is usually done in a config file, which is placed alongside your main script, but you can also add this directly in the code. The config file can also give the path to the STIM file. This may not be needed for all cases however.
Sample Optional Config File to define parameters for STIM file
target_object_size = {"small": [0.5, 0.5, 0.5], "medium": [2, 2, 2], "large": [4, 4, 4]}
target_object_position = {
"position1": [0, 1, 2],
"position2": [0, 1.5, 3],
"position3": [1, 0, 2],
}
STIM_FILE_LOCATION = "stim_file_directory/stim_file_example.csv"
3. In your main experiment script, use the StimReader to load the STIM file:
from sightlab_utils import stim_reader
# Initialize StimReader with the STIM file
SR = stim_reader.StimReader(STIM_FILE_LOCATION)
SR.fillStimFileEntryList()
4. Define the list of entries
entries = SR.getStimFileEntryList()
- In your experiment function, iterate or randomize through the conditions for each trial. Can also use this code to have number of trials automatically match number of lines in the STIM file:
entries = SR.getStimFileEntryList()
sightlab.setTrialCount(len(entries))
#If wanting to randomize the conditions
import random
random.shuffle(entries)
# Iterate over the entries
for i in range(len(entries)):
yield viztask.waitKeyDown(' ')
yield sightlab.startTrial()
currentEntry = entries[i]
- For each trial, adjust the object's properties based on the current entry:
if 'object size' in currentEntry:
ballSizeString = currentEntry['object size']
ballSize = object_Size[ballSizeString]
basketball.setScale(ballSize)
if 'object position' in currentEntry:
positionString = currentEntry['object position']
positionTuple = object_position[positionString]
basketball.setPosition(positionTuple)
- Make sure to yield control back to the main experiment loop after each trial:
yield viztask.waitKeyDown(' ')
print('experiment end')
By using STIM files and the StimReader, you can create more flexible and easily configurable experiments, adjusting different independent variables as needed.
Example for iterating through videos:
Stim file:
Video,Type,Config,Tag,Start,End
sightlab_resources/media_2D/SBS.mp4,video,mono,baseline,0,2
sightlab_resources/media/01_Warehouse.avi,video,mono,baseline,0,2
sightlab_resources/media_2D/SBS.mp4,video,mono,baseline,0,2
Code:
if "Video" in currentEntry:
video = viz.addVideo(currentEntry["Video"])
Add STIM file defined conditions to the experiment summary and/or data files
if 'object size' in currentEntry:
ballSizeString = currentEntry['object size']
ballSize = target_object_size[ballSizeString]
basketball.setScale(ballSize)
CONDITION = ballSizeString # Set global CONDITION
if 'object position' in currentEntry:
positionString = currentEntry['object position']
positionTuple = target_object_position[positionString]
basketball.setPosition(positionTuple)
sightlab.setExperimentSummaryData('Object Size',CONDITION)
sightlab.setExperimentSummaryData('Position', positionTuple)
STIM Files in Session Replay
Here's a quick sample of using the STIM file in the Session Replay using a few conditions
#First import a STIM file config file if using one
from STIM_File_Config import *
#Import Stim File Reader
from sightlab_utils import stim_reader
#Add these lines to locate your actual STIM file (i.e. what condition is being set for each trial)
SR = stim_reader.StimReader(STIM_FILE_PATH)
SR.fillStimFileEntryList()
#Load variables stored in experiment summary file and change the variable per trial
def find_experiment_summary():
"""Locate the experiment_summary.csv file in the replay data folder."""
files = os.listdir(replay.dataFolder)
for file in files:
if file.endswith("experiment_summary.csv"):
return os.path.join(replay.dataFolder, file)
raise FileNotFoundError("experiment_summary.csv not found in data folder.")
# Load the experiment_summary.csv file. NOTE CHANGE "OBJECT SIZE" to your variable
def load_experiment_summary():
summary_file = find_experiment_summary()
entries = []
with open(summary_file, 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
# Ignore empty rows or rows missing 'Object Size'
if row and row.get('Object Size'):
entries.append(row)
return entries
entries = load_experiment_summary()
def ontrialchanged():
#Get a handle to whatever object is being modified
basketball = replay.sceneObjects[GAZE_OBJECTS]["basketball"]
trial_number = int(replay.currTrial)
if trial_number and 1 <= trial_number <= len(entries):
currentEntry = entries[trial_number - 1]
if 'Object Size' in currentEntry:
# Set object size
ballSizeString = currentEntry['Object Size']
ballSize = target_object_size.get(ballSizeString)
if ballSize and basketball:
basketball.setScale(ballSize)
viz.callback(TRIAL_LOADED_EVENT, ontrialchanged)
ontrialchanged()
STIM File Loader
If wanting to be able to save multiple STIM files you can also add a STIM file loader to choose which one to use
folder = STIM_FILE_FOLDERlist_of_files = os.listdir(folder)current_stim_file = (f'{folder}/{list_of_files[vizinput.choose("select a stim file", list_of_files)]}') SR = stim_reader.StimReader(current_stim_file)SR.fillStimFileEntryList()