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.
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 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)
}
}
}
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)
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)