Last active
March 23, 2023 07:23
-
-
Save marsja/458274f273bfe999fc49ad9d902a6874 to your computer and use it in GitHub Desktop.
The following scripts are the ones explained in the blog post on JEPS Bulletin: Python Programming in Psychology – From Data Collection to Analysis (http://blog.efpsa.org/2016/07/12/python-programming-in-psychology-from-data-collection-to-analysis/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
from expyriment.misc import data_preprocessing | |
# Datafolder is where our data is stored. Lets create a string with the path | |
# to the folder. Note, this script needs to | |
# be run in the main folder of our flanker script | |
datafolder = os.getcwd() + os.sep + 'data' | |
# Imported data_preprocessing from expyriment.misc above makes it possible to | |
# get our data files (they start "flanker") | |
# and save them to "flanker_data.csv". | |
data_preprocessing.write_concatenated_data(datafolder, 'flanker', | |
output_file='flanker_data.csv', | |
delimiter=',') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pandas as pd | |
import seaborn as sns | |
sns.set_style("white") | |
# creating a data frame that can be manipulated | |
dataframe = pd.read_csv('flanker_data.csv', skiprows=1) | |
print dataframe.head() | |
# Lets group the data by trial type (i.e., congruent and incongruent) | |
grouped_df = dataframe.groupby(['trialtype']) | |
# Describe gives us some descriptive statistics | |
print grouped_df.describe().unstack() | |
#Lets do a violin plot | |
viol_ax = sns.violinplot(x="trialtype", y="RT", data=dataframe) | |
sns.plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# We first start with importing the Python library we are going to use: | |
import expyriment | |
# Some settings for the experiment | |
n_trials_block = 4 | |
n_blocks = 6 | |
durations = 2000 | |
flanker_stimuli = ["<<<<<", ">>>>>", "<<><<", ">><>>"] | |
# Task instructions | |
instructions = "Press the arrow key that matches the arrow in the CENTER -- \ | |
try to ignore all other arrows. \n \ | |
Press on x if the arrow points to the left. \ | |
\n Press on m if the arrow points to the right.\n \ | |
\n press the SPACEBAR to start the test." | |
# Creating an experiment object named "Flanker task". | |
experiment = expyriment.design.Experiment(name="Flanker Task") | |
# Our experiment is initialized | |
expyriment.control.initialize(experiment) | |
# Creating our blocks using a for loop and the Block class of Expyriment. | |
for block in range(n_blocks): | |
temp_block = expyriment.design.Block(name=str(block + 1)) | |
# Creating our trials and stimuli in each block | |
for trial in range(n_trials_block): | |
# Current Stimulus | |
curr_stim = flanker_stimuli[trial] | |
# Create temporary stimulus and trial | |
temp_stim = expyriment.stimuli.TextLine(text=curr_stim, text_size=40) | |
temp_trial = expyriment.design.Trial() | |
temp_trial.add_stimulus(temp_stim) | |
# Trial type? if the number of the first arrow in what is going to be | |
# used in the current task is the same as the number (len) of the | |
# same it is congruent | |
if flanker_stimuli[trial].count(curr_stim[0]) == len(curr_stim): | |
trialtype = 'congruent' | |
else: | |
trialtype = 'incongruent' | |
# What is the correct response? 120 = 'x' | |
if curr_stim[2] == '<': | |
correctresponse = 120 | |
# 109 = 'm' | |
elif curr_stim[2] == '>': | |
correctresponse = 109 | |
# Setting trial (incongruent or congruent) and correct response | |
temp_trial.set_factor("trialtype", trialtype) | |
temp_trial.set_factor("correctresponse", correctresponse) | |
# The trial is added to our temporary block | |
temp_block.add_trial(temp_trial) | |
# Randomise the order of trials | |
temp_block.shuffle_trials() | |
experiment.add_block(temp_block)#Add the temporary block to the experiment | |
# Column names in our data files | |
experiment.data_variable_names = ["block", "correctresp", "response", "trial", | |
"RT", "accuracy", "trialtype"] | |
# Just skip a screen | |
expyriment.control.start(skip_ready_screen=True) | |
# Creating fixation cross is easy! | |
fixation_cross = expyriment.stimuli.FixCross() | |
fixation_cross.preload() | |
# Present the instructions and wait for spacebar to be pressed | |
expyriment.stimuli.TextScreen("Flanker task", instructions).present() | |
experiment.keyboard.wait(expyriment.misc.constants.K_SPACE) | |
# First block starts | |
for block in experiment.blocks: | |
# First Trial | |
for trial in block.trials: | |
# Preload the stim | |
trial.stimuli[0].preload() | |
# Present fixation cross | |
fixation_cross.present() | |
# Present it for 2000ms (stated above) | |
experiment.clock.wait(durations) | |
# Present the flanker stimuli | |
trial.stimuli[0].present() | |
# Reset the experiment clock (used later) | |
experiment.clock.reset_stopwatch() | |
# Key pressed in and RT | |
key, rt= experiment.keyboard.wait(keys=[expyriment.misc.constants.K_x, | |
expyriment.misc.constants.K_m], duration | |
= durations) | |
# We use the experiment clock here. If a subject responded after 500ms | |
# the program waits 1500ms (2000-500) | |
experiment.clock.wait(durations - experiment.clock.stopwatch_time) | |
# Check response | |
if key == trial.get_factor('correctresponse'): | |
acc = 1 | |
else: | |
acc = 0 | |
# Add data | |
experiment.data.add([block.name, trial.get_factor('correctresponse'), | |
key, trial.id, rt, acc, | |
trial.get_factor("trialtype")]) | |
# Present text between blocks as long its not the last block | |
if block.name != "6": | |
expyriment.stimuli.TextScreen("Short break", "That was block: " | |
+ block.name + | |
". \n Next block will soon start", | |
).present() | |
experiment.clock.wait(3000) | |
# Presenting thank you text on the screeen | |
expyriment.control.end(goodbye_text="Thank you for your contribution!", | |
goodbye_delay=2000) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import expyriment | |
import numpy as np | |
# Some settings for the experiment | |
n_trials_block = 4 | |
n_blocks = 6 | |
# Durations shortened, we qant it to run quick! | |
durations = 20 | |
flanker_stimuli = ["<<<<<", ">>>>>", "<<><<", ">><>>"] | |
# Added number of simulations | |
n_sims = 27 | |
# Mean and standard deviation just chosen for the sake of the blog post | |
mu1, sigma1 = 560, 35 | |
mu2, sigma2 = 642, 54 | |
# Task instructions | |
instructions = "Press the arrow key that matches the arrow in the CENTER -- \ | |
try to ignore all other arrows. \n \ | |
Press on x if the arrow points to the left. \ | |
\n Press on m if the arrow points to the right.\n \ | |
\n press the SPACEBAR to start the test." | |
# A (new) loop to run fake subjects in our simulation | |
for simp in range(n_sims): | |
experiment = expyriment.design.Experiment(name="Flanker Task") | |
expyriment.control.initialize(experiment) | |
#These two lines below make the subject ids automatically filled in and no delay.... | |
expyriment.control.defaults.auto_create_subject_id = True | |
expyriment.control.defaults.initialize_delay = 0 | |
for block in range(n_blocks): | |
temp_block = expyriment.design.Block(name=str(block + 1)) | |
for trial in range(n_trials_block): | |
temp_stim = expyriment.stimuli.TextLine(text=flanker_stimuli[trial], | |
text_size=40) | |
temp_trial = expyriment.design.Trial() | |
temp_trial.add_stimulus(temp_stim) | |
if flanker_stimuli[trial].count(flanker_stimuli[trial][0]) == len(flanker_stimuli[trial]): | |
trialtype = 'congruent' | |
else: | |
trialtype = 'incongruent' | |
if flanker_stimuli[trial][2] == '<': | |
correctresponse = 120 | |
elif flanker_stimuli[trial][2] == '>': | |
correctresponse = 109 | |
temp_trial.set_factor("trialtype", trialtype) | |
temp_trial.set_factor("correctresponse", correctresponse) | |
temp_block.add_trial(temp_trial) | |
temp_block.shuffle_trials() | |
experiment.add_block(temp_block) | |
experiment.data_variable_names = ["block", "correctresp", "response", "trial", "RT", "accuracy", "trialtype"] | |
expyriment.control.start(skip_ready_screen=True) | |
fixation_cross = expyriment.stimuli.FixCross() | |
fixation_cross.preload() | |
expyriment.stimuli.TextScreen("Flanker task", instructions).present() | |
experiment.keyboard.wait(expyriment.misc.constants.K_SPACE, duration=500) | |
for block in experiment.blocks: | |
for trial in block.trials: | |
#Using the choice method the script randomly picks 1 or 0 (accuracy) based on the probabilites (.86, .14) | |
prob_corr = np.random.choice([1, 0], p=[.86, 1 - .86]) | |
trial.stimuli[0].preload() | |
fixation_cross.present() | |
experiment.clock.wait(durations) | |
trial.stimuli[0].present() | |
experiment.clock.reset_stopwatch() | |
key, rt= experiment.keyboard.wait(keys=[expyriment.misc.constants.K_x, | |
expyriment.misc.constants.K_m], duration = durations) | |
experiment.clock.wait(durations - experiment.clock.stopwatch_time) | |
#Check response: if simulated a hit we get store the correct response in the variable 'key' | |
if prob_corr == 1: | |
key = trial.get_factor('correctresponse') | |
acc = 1 | |
else: | |
#Else we store the opposite of what should have been pressed in the trial | |
if trial.get_factor('correctresponse') == 109: | |
key = 120 | |
else: | |
key = 109 | |
acc = 0 | |
# Here we use the mean and standard deviations from earlier in the script | |
# and use the normal method to get RT | |
if trial.get_factor("trialtype") == "congruent": | |
rt = int(np.random.normal(mu1, sigma1, 1)[0]) | |
elif trial.get_factor("trialtype") == "incongruent": | |
rt = int(np.random.normal(mu2, sigma2, 1)[0]) | |
experiment.data.add([block.name, trial.get_factor('correctresponse'), key, trial.id, rt, acc, | |
trial.get_factor("trialtype")]) | |
if block.name != "6": | |
expyriment.stimuli.TextScreen("Short break", "That was block: " + block.name + ". \n Next block will soon start", | |
).present() | |
experiment.clock.wait(30) | |
expyriment.control.end(goodbye_text="Thank you very much for your contribution!", | |
goodbye_delay=12) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
import pandas as pd | |
from scipy.stats import t | |
def paired_ttest(x, y): | |
""" | |
Function that will calculate the t-ratio | |
:param x: list of scores in first condition | |
:param y: list of scores in second condition | |
:return statistics: a dictionary containing the t-value, p-value, | |
and degrees of freedom | |
""" | |
# Difference scores are going to be stored in a list | |
di = [] | |
# Sample size = the length of variable x | |
n = len(x) | |
df = n-1 | |
# For each subject append the difference score between x & y | |
# i is the index so we do the substraction for each subjects score on x and y | |
for i in range(n): | |
di.append(x[i] - y[i]) | |
# Avarage of the difference score. Note, division of integers in Python will result in an integer. Thus, we need | |
# to convert one of the numbers to float datatype (using float()) | |
dbar = float(sum(di))/n | |
# Calculation of standard deviation for the difference score | |
std_di = 0 | |
# Each subjects difference score is subtracted by the avarage and exponated (i.e., "**2) | |
for d in di: | |
std_di += (d-dbar)**2 | |
# We get the standard deviation by dividing what was calculated above (std_di) by sample size (n). This value is | |
# squared. | |
std_di = np.sqrt(std_di/n) | |
# Standard error of the mean is simple: standard deviation divided by the sample size -1 squared. | |
se_dbar = std_di/np.sqrt(n-1) | |
# T-ratio is calculated by dividing the avarage by the standard error of the mean | |
t_val = dbar/se_dbar | |
# P-value | |
pval = t.sf(np.abs(t_val), df) * 2. | |
# Last, we create the dictionary containing the needed statistics | |
statistics = {'T-value': t_val, 'Degree of Freedom': df, 'P-value': pval} | |
# Function returns a dictionary: | |
return statistics | |
# Creating a data frame that can be manipulated | |
dataframe = pd.read_csv('flanker_data.csv', skiprows=1) | |
# We need to aggregate data first we group it | |
grouped_sub = dataframe.groupby(['trialtype', 'subject_id']) | |
# Calculate the mean RT for each participant in the two trial types: | |
means = grouped_sub['RT'].mean() | |
# We only want the values (i.e., the RTs) | |
x, y = means['incongruent'].values, means['congruent'].values | |
# Obtaining the t-ratio | |
t_value = paired_ttest(x, y) | |
# Just printing the statistics | |
# Note we round the values at the third decimal using round | |
for key, value in t_value.iteritems(): | |
t_value[key] = round(value, 3) | |
print t_value |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment