Calculating Intersubject Correlations
Intersubject correlation (ISC) is a measure used to assess the degree of synchrony among participants—that is, how similarly their responses change over time. ISC has been widely applied in fMRI and EEG research, particularly in naturalistic contexts, to quantify shared neural activity. More recently, ISC has been extended to webcam-based eye-tracking, allowing researchers to examine shared gaze patterns during dynamic stimuli.
This vignette demonstrates how to calculate ISC using two commonly used approaches:
- Pairwise ISC Approach
In this method, each participant’s data is correlated with every other participant’s data, generating a correlation matrix across all pairs.
Correlations are transformed using Fisher’s z to r transformation and averaged and then transformed back into r resulting in one ISC for each participant.
- Leave-One-Out ISC Approach - Here, each participant’s data is correlated with the mean time series of all other participants, excluding their own data. - This results in one correlation value for each participant.
To compute ISC, your data should be structured as a p Ă— t matrix, where:
- p represents the number of participants
- t represents time points
Before highlighting the function calculate_isc()
I want to break down each step that goes into calculating the ISC.
Pairwise ISC
Below, we generate a 10 Ă— 1000 matrix
library(tidyverse)
library(webgazeR)
set.seed(123)
<- matrix(rnorm(10 * 1000), nrow = 1000, ncol = 10)
data_matrix
%>%
data_matrix head()
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] -0.56047565 -0.99579872 -0.5116037 -0.15030748 0.1965498 -0.4941739
[2,] -0.23017749 -1.03995504 0.2369379 -0.32775713 0.6501132 1.1275935
[3,] 1.55870831 -0.01798024 -0.5415892 -1.44816529 0.6710042 -1.1469495
[4,] 0.07050839 -0.13217513 1.2192276 -0.69728458 -1.2841578 1.4810186
[5,] 0.12928774 -2.54934277 0.1741359 2.59849023 -2.0261096 0.9161912
[6,] 1.71506499 1.04057346 -0.6152683 -0.03741501 2.2053261 0.3351310
[,7] [,8] [,9] [,10]
[1,] -0.6992281 -1.6180367 0.5110004 1.9315759
[2,] 0.9964515 0.3791812 1.8079928 -0.6164747
[3,] -0.6927454 1.9022505 -1.7026150 -0.5625675
[4,] -0.1034830 0.6018743 0.2874488 -0.9899631
[5,] 0.6038661 1.7323497 -0.2691142 2.7312276
[6,] -0.6080450 -0.1468696 -0.3795247 -0.7216662
We use the cor() function to compute pairwise correlations between each participant’s time-binned data and that of every other participant.
We specify use = “pairwise.complete.obs” to ensure that:
If any missing values (NA) exist for a participant, those time points are excluded from the correlation for all comparisons involving that participant.
This allows us to retain all participants in the dataset while ensuring that correlations are computed using only available data.
<- cor(data_matrix, use = "pairwise.complete.obs") correlation_matrix
Since the diagonal of the correlation matrix represents self-correlations (i.e., each participant correlated with themselves), we replace those values with NA, as they are not meaningful for intersubject correlation (ISC):
diag(correlation_matrix) <- NA
correlation_matrix
[,1] [,2] [,3] [,4] [,5]
[1,] NA 0.086479441 -0.019329537 -0.0029947104 0.0322973377
[2,] 0.086479441 NA 0.026503334 -0.0070290755 0.0924646624
[3,] -0.019329537 0.026503334 NA 0.0505608499 -0.0150195347
[4,] -0.002994710 -0.007029076 0.050560850 NA -0.0004005193
[5,] 0.032297338 0.092464662 -0.015019535 -0.0004005193 NA
[6,] -0.018267991 -0.055292983 -0.033003346 0.0438457353 0.0138042312
[7,] -0.034340849 0.024316458 0.005282974 -0.0200855749 -0.0090534146
[8,] -0.030156049 0.031077180 -0.025946806 -0.0055278737 -0.0777495779
[9,] -0.009370688 0.022155213 0.058417535 -0.0064371559 0.0470798891
[10,] 0.022344953 0.022481121 0.029148322 0.0459001272 -0.0047210318
[,6] [,7] [,8] [,9] [,10]
[1,] -0.0182679912 -0.034340849 -0.030156049 -0.0093706879 0.022344953
[2,] -0.0552929834 0.024316458 0.031077180 0.0221552128 0.022481121
[3,] -0.0330033458 0.005282974 -0.025946806 0.0584175347 0.029148322
[4,] 0.0438457353 -0.020085575 -0.005527874 -0.0064371559 0.045900127
[5,] 0.0138042312 -0.009053415 -0.077749578 0.0470798891 -0.004721032
[6,] NA -0.016497452 -0.016411441 -0.0006214018 -0.010177844
[7,] -0.0164974516 NA 0.037173511 0.0791431861 0.002523005
[8,] -0.0164114415 0.037173511 NA -0.0422433679 0.010503101
[9,] -0.0006214018 0.079143186 -0.042243368 NA -0.006174781
[10,] -0.0101778437 0.002523005 0.010503101 -0.0061747810 NA
Next, we apply Fisher’s Z-transformation using atanh() to convert the correlation coefficients into Z-scores before averaging.
Averaging correlation scores directly is bad practice because:
- Correlations are bounded between -1 and 1, which skews their distribution.
- Fisher’s Z-transformation normalizes correlation values, making them more suitable for averaging.
You can also choose to use the median instead (if this is the case you do not need to apply Fisher transformation)
<- atanh(correlation_matrix) # Fisher's Z transformation z_values
Once transformed, we compute the average ISC per participant and transform back to r.
<- tanh(rowMeans(z_values, na.rm = TRUE)) # Convert back to r isc_values
the calculate_isc()
function with method=“pairwise” does all this for us.
<- calculate_isc(data_matrix, method = "pairwise", summary_statistic = "mean")
isc_pairwise
isc_pairwise
[1] 0.002985107 0.027061018 0.008523871 0.010880947 0.008761466
[6] -0.010296001 0.007625601 -0.013271741 0.015797990 0.012429909
Leave one out ISC
In the leave-one-out approach, we compute the correlation between each participant’s time series and the average time series of all other participants, excluding their own data.
First, we extract the time series for a single participant:
# Extract the full time series for the current subject
<- data_matrix[, 1] subject_time_series
and calculate the mean of the other subjects data points, excluding the current subject
<- rowMeans(data_matrix[, -1, drop = FALSE], na.rm = TRUE) leave_one_out_mean
We then compute the correlation between the participant’s time series and the leave-one-out mean:
<- cor(subject_time_series,
leave_one_out_isc
leave_one_out_mean, use = "pairwise.complete.obs")
Since this needs to be done for every participant, we use the calculate_isc() function to automate the process:
<- calculate_isc(data_matrix, method = "leave-one-out")
isc_pairwise
isc_pairwise
[1] 0.008891827 0.080509746 0.025626567 0.031021518 0.026597776
[6] -0.029482014 0.023592539 -0.038321505 0.046317774 0.036036027