During our study, the participants were asked, after the presentation of a word-pair, to judge whether the two words were related. When prompted, they pressed one of two buttons to answer with "yes" or "no". In this notebook, we explore whether the measured amplitude of the N400 component carries more information than the behavioral responses of the participants.
In [1]:
# Import Pandas data handing module
import pandas as pd
# For pretty display of tables
from IPython.display import display
# Load the data
data = pd.read_csv('data.csv', index_col=['subject', 'cue-english', 'association-english'])
data = data.sort_index()
# Transform the "raw" N400 amplitudes into distance measurements according to the equation above
data['distance'] = data['N400'] - data.groupby(['subject', 'association-english'])['N400'].transform('mean')
# Show the first 10 rows
display(data.head(10))
The behavior of the participants was very systematic. Except for the occasional error, whenever two words belonged to the same "animal" or "furniture" category, they would judge them as related, and unrelated otherwise. You can clearly see this when plotting the average button response (0=related, 1=unrelated) to each word-pair.
In [2]:
# Compute the average button responses for each cue-association pair
mean_button_response = data.groupby(['cue-english', 'association-english'])['button'].agg('mean').reset_index()
# Reshape the data into a cue-association word matrix
matrix = mean_button_response.pivot(index='cue-english', columns='association-english', values='button')
# Re-order the rows and columns so that animals are sorted before furniture
animals = ['elephant', 'giraffe', 'hippopotamus', 'lion', 'rhinoceros', 'tiger', 'zebra']
furniture = ['bed', 'chair', 'closet', 'couch', 'desk', 'door', 'table']
order = animals + furniture
matrix = matrix.loc[order, order]
# Display the matrix
display(matrix)
# Plot the matrix as a heatmap
from matplotlib import pyplot as plt
%matplotlib inline
plt.figure(figsize=(6, 4))
plt.matshow(matrix.values, fignum=1)
plt.xticks(range(14), matrix.columns, rotation=90)
plt.yticks(range(14), matrix.index)
plt.colorbar()
Out[2]:
The dendrogram in figure 3 of the manuscript suggests that there is a subcluster inside the furniture items that has a significant difference in the amplitude of the N400 component. We now test if this difference is also detecable in the behavioral responses.
In [3]:
# Limit the data to just the furniture items
furniture_only = data.loc(axis=0)[:, furniture, furniture]
# Assign within-cluster and between-cluster labels, based on the subclusters in figure 3 of the manuscript
cluster1 = ['desk', 'bed', 'closet']
cluster2 = ['door', 'table', 'chair', 'couch']
furniture_only = furniture_only.assign(label = [
'within' if (a in cluster1 and b in cluster1) or (a in cluster2 and b in cluster2) else 'between'
for a, b in zip(furniture_only.index.get_level_values('cue-english'),
furniture_only.index.get_level_values('association-english'))
])
within_related = len(furniture_only.query('label=="within" and button == 0'))
within_total = len(furniture_only.query('label=="within"'))
print('Ratio of within-cluster trials with a "related" response: %d/%d=%.3f' %
(within_related, within_total, within_related / float(within_total)))
between_related = len(furniture_only.query('label=="between" and button == 0'))
between_total = len(furniture_only.query('label=="between"'))
print('Ratio of between-cluster trials with a "related" response: %d/%d=%.3f' %
(between_related, between_total, between_related / float(between_total)))
# Display the first 10 rows
display(furniture_only.head(10))
In [4]:
# Bring in a bridge to R for statistics
import rpy2
%load_ext rpy2.ipython.rmagic
# The R code at the bottom produces some harmless warnings that clutter up the page.
# This disables printing of the warnings. When modifying this notebook, you may want to turn
# this back on.
import warnings
warnings.filterwarnings('ignore')
# To transfer this data frame to R, the index must be reset
furniture_only = furniture_only.reset_index()
In [5]:
%%R -i furniture_only
library('lme4')
library('lmerTest')
# Fit a LME model to test whether there is a difference between the within-cluster and between-cluster
# button responses.
m <- lmer(button ~ label + (label | subject), data=furniture_only)
print(summary(m))
It seems there is very little difference in the behavioral responses between the within-cluster and between-cluster trials. A difference that doesn't cross the significance threshold.
We now test if the difference in the distance metric persists when we restrict the data to only trials for which the participants pressed the "words are related" button.
In [6]:
# Limit the data to just the trials for which the participant pressed the "words are related" button
related_response_only = furniture_only.query('button == 0')
print('The total number of selected trials is: %d' % len(related_response_only))
# To transfer this data frame to R, the index must be reset
related_response_only = related_response_only.reset_index()
In [7]:
%%R -i related_response_only
library('lme4')
library('lmerTest')
# Fit a LME model to test whether there is a difference between the within-cluster and between-cluster
# distance values.
m <- lmer(distance ~ label + (label | subject), data=related_response_only)
print(summary(m))
Even when the data is limited to a set of trials for which there is no difference in behavioral response (the response was always "the words are related"), differences in the amplitude of the N400 component persist. This leads us to conclude that the distance metric used by our proposed method does capture effects beyond the measured behavioral data.