This is a demostration of how a long list of behavioral data (Reaction Time) is converted to a dissimilarity matrix. “behavioral1.csv” is a dataset that consists of finger responses and its reaction time, recorded throughout the trials, by row. There are five blocks with 210 trials in each. The trial condition is “note” and the response time is “rt” Variable “note” is the finger movement that subjects are asked to perform. i.e. 1000 is pressing with the little finger; 1100 for the little & ring finger; 1111 for all fingers except the thumb. I am interested in all pairwise reaction time when going from one movement to another. The rts are to be mapped on a dissimilarity matrix for further analysis.

Load in the data

library(pracma)
## Warning: package 'pracma' was built under R version 4.2.2
library(plot.matrix)
## Warning: package 'plot.matrix' was built under R version 4.2.2
participant_n = 1                               #specifying total subject number. For demostration, we are preprocessing a single subject.
setwd("C:\\Users\\tommy\\桌面\\git")
temp= list.files()             
day_1 = temp[startsWith(temp, "behavioral1")]   #This is a multi-session study. For demostration, I selected the first session.
data = lapply(day_1, read.csv)
data[[1]][1:5,]
##   trial_index time_elapsed            rt      response avg_frame_time center_x
## 1          40        80235         [666]         ["s"]       16.66638      250
## 2          41        81243     [637,638]     ["a","s"]       16.66685      250
## 3          42        82252     [461,461]     ["d","f"]       16.66707      250
## 4          43        83261 [595,617,617] ["f","a","d"]       16.66793      250
## 5          44        84268         [463]         ["s"]       16.66687      250
##   center_y note correct
## 1      150  101   FALSE
## 2      150 1100    TRUE
## 3      150   11    TRUE
## 4      150 1011    TRUE
## 5      150  100    TRUE

Preprocessing

Preprocessing is done block by block. There will be a unique matrix for each block. This provides versatility for analysis. One can simply average them all to check the overall effect of “note” on “rt”. Or, one can analyse the effect of “note”on “rt” within each block and compare between blocks. i.e. fatigue effect, practice effect. For this demostration, I will average all 5 of them. You can see the matrix output for the first block.

#1st block  


block = list()
for(k in 1:participant_n){
  block[[k]] = data[[k]][which(data[[k]]$trial_index==40):which(data[[k]]$trial_index==249),]
}

#get all correct responses. I am only interested in the rts of correct responses.
block_correct = list()
for(k in 1:participant_n){
  block_correct[[k]] = block[[k]][block[[k]]$correct == "TRUE",]
}

#fill in matrix
mat = matrix(NA, nrow =14, ncol=14)
colnames(mat)= c(100,11,1010,1000,1101,101,1110,111,1011,1100,1,1001,110,10)
rownames(mat) = colnames(mat)
matlist = list()
for(k in 1:participant_n){
  for(i in 1:14){
    for(j in 1:14){
      mat[i,j] = mean(unlist(lapply(lapply(strsplit(gsub("\\[|\\]","",block_correct[[k]]$rt[which(block_correct[[k]]$note == colnames(mat)[j])[block_correct[[k]]$note[which(block_correct[[k]]$note == colnames(mat)[j])-1]==colnames(mat)[i]]]
      ),","), as.numeric), mean)), na.rm=T)   #This LONG line finds the rows for a specific note with correct input, for each of these rows, identify the previous row with the correct input and the specific note. 
                                              #After finding the rts for each of the conditions, then find their average (If there are multiple rts found for a specific condition).
    }
  }
}
options(width = 300)
mat
##        100       11   1010 1000     1101   101     1110      111     1011     1100     1     1001   110  10
## 100  612.0 465.0000 490.75   NA 789.0000 412.0 439.0000 432.8333 485.0000 422.0000    NA 500.5000 470.0  NA
## 11      NA 433.0000 712.50  794       NA 450.5       NA       NA 609.6667       NA 436.0 490.2500 428.0  NA
## 1010 468.0 478.0000     NA   NA       NA    NA       NA 617.0000 843.3333 441.0000    NA       NA    NA 496
## 1000    NA       NA 462.00   NA       NA    NA       NA       NA 527.0000 525.0000 478.5 416.0000    NA 686
## 1101 455.0       NA     NA   NA 492.6667 682.5       NA       NA 513.0000 510.3333 440.0       NA 681.0  NA
## 101     NA 500.0000     NA  466 553.6667    NA       NA       NA 511.6667       NA    NA 493.6667    NA  NA
## 1110    NA       NA     NA   NA 725.0000    NA       NA       NA 503.5556       NA    NA 537.0000    NA  NA
## 111     NA       NA     NA  456 481.6667    NA       NA 514.6667 522.3333       NA    NA       NA    NA 446
## 1011 451.8 497.0000 589.00  441       NA 514.0 510.0000 630.3333 481.6667 844.0000 537.0 505.0000 477.5 708
## 1100 547.0 563.5000     NA  480       NA    NA       NA 473.0000 519.0000 497.7500    NA 627.2500 469.5  NA
## 1       NA       NA 527.50  480       NA    NA       NA       NA 544.3333 444.5000    NA 669.7500    NA 384
## 1001 488.0       NA 533.00  483 568.5000 514.5 608.6667       NA 517.6667 544.0000    NA       NA    NA  NA
## 110  427.0       NA     NA   NA 603.3333    NA 489.3333       NA 505.0000       NA    NA       NA    NA 555
## 10      NA 413.8333 819.00  448       NA    NA 569.3333       NA       NA 409.0000    NA       NA    NA  NA

Do the same thing for the remaining 4 blocks.

#2nd block
block = list()
for(k in 1:participant_n){
  block[[k]] = data[[k]][which(data[[k]]$trial_index==251):which(data[[k]]$trial_index==461),]
}

#get all correct responses
block_correct = list()
for(k in 1:participant_n){
  block_correct[[k]] = block[[k]][block[[k]]$correct == "TRUE",]
}

#fill in matrix
mat2 = matrix(NA, nrow =14, ncol=14)
colnames(mat2)= c(100,11,1010,1000,1101,101,1110,111,1011,1100,1,1001,110,10)
rownames(mat2) = colnames(mat2)
mat2list = list()
for(k in 1:participant_n){
  for(i in 1:14){
    for(j in 1:14){
      mat2[i,j] = mean(unlist(lapply(lapply(strsplit(gsub("\\[|\\]","",block_correct[[k]]$rt[which(block_correct[[k]]$note == colnames(mat2)[j])[block_correct[[k]]$note[which(block_correct[[k]]$note == colnames(mat2)[j])-1]==colnames(mat2)[i]]]
      ),","), as.numeric), mean)), na.rm=T)   
    }
  }
}

#3rd block
block = list()
for(k in 1:participant_n){
  block[[k]] = data[[k]][which(data[[k]]$trial_index==463):which(data[[k]]$trial_index==673),]
}

#get all correct responses
block_correct = list()
for(k in 1:participant_n){
  block_correct[[k]] = block[[k]][block[[k]]$correct == "TRUE",]
}

#fill in matrix
mat3 = matrix(NA, nrow =14, ncol=14)
colnames(mat3)= c(100,11,1010,1000,1101,101,1110,111,1011,1100,1,1001,110,10)
rownames(mat3) = colnames(mat3)
mat3list = list()
for(k in 1:participant_n){
  for(i in 1:14){
    for(j in 1:14){
      mat3[i,j] = mean(unlist(lapply(lapply(strsplit(gsub("\\[|\\]","",block_correct[[k]]$rt[which(block_correct[[k]]$note == colnames(mat3)[j])[block_correct[[k]]$note[which(block_correct[[k]]$note == colnames(mat3)[j])-1]==colnames(mat3)[i]]]
      ),","), as.numeric), mean)), na.rm=T)   
    }
  }
}


#4th block
block = list()
for(k in 1:participant_n){
  block[[k]] = data[[k]][which(data[[k]]$trial_index==675):which(data[[k]]$trial_index==885),]
}

#get all correct responses
block_correct = list()
for(k in 1:participant_n){
  block_correct[[k]] = block[[k]][block[[k]]$correct == "TRUE",]
}

#fill in matrix
mat4 = matrix(NA, nrow =14, ncol=14)
colnames(mat4)= c(100,11,1010,1000,1101,101,1110,111,1011,1100,1,1001,110,10)
rownames(mat4) = colnames(mat4)
mat4list = list()
for(k in 1:participant_n){
  for(i in 1:14){
    for(j in 1:14){
      mat4[i,j] = mean(unlist(lapply(lapply(strsplit(gsub("\\[|\\]","",block_correct[[k]]$rt[which(block_correct[[k]]$note == colnames(mat4)[j])[block_correct[[k]]$note[which(block_correct[[k]]$note == colnames(mat4)[j])-1]==colnames(mat4)[i]]]
      ),","), as.numeric), mean)), na.rm=T)   
    }
  }
}


#5th block
block = list()
for(k in 1:participant_n){
  block[[k]] = data[[k]][which(data[[k]]$trial_index==887):which(data[[k]]$trial_index==1097),]
}

#get all correct responses
block_correct = list()
for(k in 1:participant_n){
  block_correct[[k]] = block[[k]][block[[k]]$correct == "TRUE",]
}

#fill in matrix
mat5 = matrix(NA, nrow =14, ncol=14)
colnames(mat5)= c(100,11,1010,1000,1101,101,1110,111,1011,1100,1,1001,110,10)
rownames(mat5) = colnames(mat5)
mat5list = list()
for(k in 1:participant_n){
  for(i in 1:14){
    for(j in 1:14){
      mat5[i,j] = mean(unlist(lapply(lapply(strsplit(gsub("\\[|\\]","",block_correct[[k]]$rt[which(block_correct[[k]]$note == colnames(mat5)[j])[block_correct[[k]]$note[which(block_correct[[k]]$note == colnames(mat5)[j])-1]==colnames(mat5)[i]]]
      ),","), as.numeric), mean)), na.rm=T)   
    }
  }
}

Averaging all 5 matrices

Now that we have a matrix for every block, I can look for trend of rts over blocks. For now, I am interested in overall performance. Find average of all mats.

allblocks_matlist<-list(mat,mat2,mat3,mat4,mat5)
Y <- do.call(cbind, allblocks_matlist)
Y <- array(Y, dim=c(dim(allblocks_matlist[[1]]), length(allblocks_matlist)))
mean_rt = apply(Y, c(1, 2), mean, na.rm = TRUE)
colnames(mean_rt)= c(100,11,1010,1000,1101,101,1110,111,1011,1100,1,1001,110,10)
rownames(mean_rt) = colnames(mean_rt)
res = plot(mean_rt,digit = 2 ,text.cell=list(pos=3, cex=0.75), col = heat.colors(7),breaks = c(200, 1000), key=NULL, axis.col = 3, cex.axis = .7)

Averaging upper and lower triangles

Now we have an averaged matrix from all blocks, we average the upper and lower triangles to make the matrix symmetrical. This entails that all movements are equivalent to its reversed movement. (“101” - “1100” is equivalent to its reverse “1100” - “101”) The diagonal is forced to be 0 to make a dissimilarity matrix. Also, we are not interested in the rt between the same finger movement.

#averaging upper and lower triangles. Then turn into array
averagedvector =  vector("list", participant_n)
for(n in 1:participant_n){
  for(i in 1:91){
    averagedvector[[n]][i]= mean(c(mean_rt[lower.tri(mean_rt)][i],t(mean_rt)[lower.tri(mean_rt)][i]),na.rm=T)
  }
}
averagedmat = matrix(, nrow =14, ncol=14)
colnames(averagedmat)= c(100,11,1010,1000,1101,101,1110,111,1011,1100,1,1001,110,10)
rownames(averagedmat) = colnames(averagedmat)
averagedreducedlist = vector("list", participant_n)
for(n in 1:participant_n){
  averagedmat[lower.tri(averagedmat)] = averagedvector[[n]]
  averagedmat = t(averagedmat)
  averagedmat[lower.tri(averagedmat)] = averagedvector[[n]]
  diag(averagedmat) = 0
  averagedreducedlist[[n]]=averagedmat
}
res = plot(averagedreducedlist[[1]],digit = 2 ,text.cell=list(pos=3, cex=0.75), col = heat.colors(7),breaks = c(200, 1000), key=NULL, axis.col = 3, cex.axis = .7)