#treating IHI data as a ratio of the unconditioned baseline condition is a bad idea #these ratios have an extremely long right tail making their residuals asymmetric and badly non-normal #the difference method has fewer problems, but ratios are more common in the literature #probably because that's what Ferbert 1992 did and everybody followed suit #here I do the ratio method to see whether it changes anything very important #all results are the same, but test magnitude appears to reduce IHI #the gap between conditioned and unconditioned MEPs grows with test pulse magnitude #but unconditioned MEP sizes increase more (this should be obvious) #the ratio therefore shrinks. #this makes it appear that strong tests pulses reduce IHI, but this is an artefact library("tidyverse") library("lme4") library("merTools") library("lmerTest") library("boot") library("pbkrtest") setwd('~/your/working_directory/here') getwd() options(contrasts = c("contr.sum","contr.poly")) set.seed(42) dat<-read_csv("Belyk_exp1.csv") #reference data dat_TestOnly<-dat %>% filter(IHI=="TestOnly") %>% group_by(TPower, Subject) ############### ###wrangle### ############### Test_list<-list() subjects <- unique(dat$Subject) for(s in 1:length(subjects)){ #loop through subjects Test_list[[s]]<-dat_TestOnly %>% filter(Subject == subjects[s]) %>% #find data for this subject only summarise(Test = median(Test_EMG), Conditioning = median(Cond_EMG)) #get medians } #more useful data dat_ihi<-dat %>% filter(IHI != "TestOnly") dat_ihi$Inhibition_ratio <- NA dat_ihi$Inhibition <- NA for(i in 1:dim(dat_ihi)[1]) { #loop through rows sub_index<-which(subjects == dat_ihi$Subject[i]) #index for matching participant to test only medians #find relevant test only value specific to participant if(dat_ihi$TPower[i] == 1.0) TO <- Test_list[[sub_index]]$Test[1] if(dat_ihi$TPower[i] == 1.1) TO <- Test_list[[sub_index]]$Test[2] if(dat_ihi$TPower[i] == 1.2) TO <- Test_list[[sub_index]]$Test[3] if(dat_ihi$TPower[i] == 1.3) TO <- Test_list[[sub_index]]$Test[4] #calculate inhibition dat_ihi$Inhibition_ratio[i] <- dat_ihi$Test_EMG[i] / TO #by ratio dat_ihi$Inhibition[i] <- TO - dat_ihi$Test_EMG[i] #by subtraction, not clear which is more sensible } #fix classes dat_ihi$TPower <- as.factor(dat_ihi$TPower) dat_ihi$CPower <- as.factor(dat_ihi$CPower) dat_ihi$IHI <- as.factor(dat_ihi$IHI) dat_ihi$Subject <- as.factor(dat_ihi$Subject) ###normalize values within subject### #? is this smart? #lets wait a second. I'm not convinced that variation within one subject means the same as variation within another ####################################### ###Filter data from experiment notes### ####################################### ##remove S2 trial 62, script failed to trigger stimulator dat_ihi %>% filter(Subject == "S2" & Trial == 61) #already removed ##S3, big head movement at trials 152 #doesn't appear all that sudden. Take no action. dat_ihi %>% filter(Subject == "S3") %>% gather(key = Dimension, value = measurement, Cond_Dist_Target,Cond_Target_Error,Cond_Angular_Error,Cond_Twist_Error, Test_Dist_Target,Test_Target_Error,Test_Angular_Error,Test_Twist_Error) %>% ggplot(aes(x=Trial, y=measurement, colour= Dimension)) + geom_line()+ geom_smooth(se=F)+ geom_vline(xintercept=152) #S4 first trial missing. Already absent dat_ihi %>% filter(Subject == "S4") ##s7 experiment stopped at trial 170 #earlier than that apparently, onlu 163 recorded tail(dat_ihi %>% filter(Subject == "S7")) ##s10 camera lost sight of markers #several abrupt jumps remove trials 1-15, 85-95, 115-125 dat_ihi %>% filter(Subject == "S10") %>% gather(key = Dimension, value = measurement, Cond_Dist_Target,Cond_Target_Error,Cond_Angular_Error,Cond_Twist_Error, Test_Dist_Target,Test_Target_Error,Test_Angular_Error,Test_Twist_Error) %>% ggplot(aes(x=Trial, y=measurement, colour= Dimension)) + geom_line()+ geom_smooth(se=F)+ geom_vline(xintercept=1)+ geom_vline(xintercept=15)+ geom_vline(xintercept=85)+ geom_vline(xintercept=95)+ geom_vline(xintercept=115)+ geom_vline(xintercept=125) #remove ranges, some trials were already missing dat_ihi<-dat_ihi %>% filter((Subject == "S10" & Trial <=15)==F) %>% filter((Subject == "S10" & Trial >=85 & Trial <95)==F) %>% filter((Subject == "S10" & Trial >=115 & Trial <125)==F) ########################## ###Normalize covariates### ########################## #add columns dat_ihi$Cond_Target_Norm <- NA dat_ihi$Cond_Angular_Norm <- NA dat_ihi$Cond_Twist_Norm <- NA dat_ihi$Test_Target_Norm <- NA dat_ihi$Test_Angular_Norm <- NA dat_ihi$Test_Twist_Norm <- NA #center and scale targeting error metric for(s in subjects){ #loop through subjects sub_index <- which(dat_ihi == s) #get rows dat_ihi$Cond_Target_Norm[sub_index] <-scale(dat_ihi$Cond_Target_Error[sub_index]) dat_ihi$Cond_Angular_Norm[sub_index] <-scale(dat_ihi$Cond_Angular_Error[sub_index]) dat_ihi$Cond_Twist_Norm[sub_index] <-scale(dat_ihi$Cond_Twist_Error[sub_index]) dat_ihi$Test_Target_Norm[sub_index] <-scale(dat_ihi$Test_Target_Error[sub_index]) dat_ihi$Test_Angular_Norm[sub_index] <-scale(dat_ihi$Test_Angular_Error[sub_index]) dat_ihi$Test_Twist_Norm[sub_index] <-scale(dat_ihi$Test_Twist_Error[sub_index]) } ################################# ###Hunt for targeting outliers### ################################# #yes there are long positive tails ggplot(dat_ihi, aes(x=Cond_Target_Norm))+ geom_density() ggplot(dat_ihi, aes(x=Cond_Angular_Norm))+ geom_density() ggplot(dat_ihi, aes(x=Cond_Twist_Norm))+ geom_density() ggplot(dat_ihi, aes(x=Cond_Target_Norm))+ geom_density() ggplot(dat_ihi, aes(x=Cond_Angular_Norm))+ geom_density() ggplot(dat_ihi, aes(x=Cond_Twist_Norm))+ geom_density() ############################### ###Remove targeting outliers### ############################### #q=0.99 is about right for loping of the long tails q_Cond_Target_Norm <- quantile(dat_ihi$Cond_Target_Norm, probs=0.99) q_Cond_Angular_Norm <- quantile(dat_ihi$Cond_Angular_Norm, probs=0.99) q_Cond_Twist_Norm <- quantile(dat_ihi$Cond_Twist_Norm, probs=0.99) q_Test_Target_Norm <- quantile(dat_ihi$Test_Target_Norm, probs=0.99) q_Test_Angular_Norm <- quantile(dat_ihi$Test_Angular_Norm, probs=0.99) q_Test_Twist_Norm <- quantile(dat_ihi$Test_Twist_Norm, probs=0.99) #remove tail values dat_ihi<-dat_ihi %>% filter(Cond_Target_Norm < q_Cond_Target_Norm) %>% filter(Cond_Angular_Norm < q_Cond_Angular_Norm) %>% filter(Cond_Twist_Norm < q_Cond_Twist_Norm) %>% filter(Test_Target_Norm < q_Test_Target_Norm) %>% filter(Test_Angular_Norm < q_Test_Angular_Norm) %>% filter(Test_Twist_Norm < q_Test_Twist_Norm) #Re-scale and re-center and scale targeting error metric after outlier for(s in subjects){ #loop through subjects sub_index <- which(dat_ihi == s) #get rows dat_ihi$Cond_Target_Norm[sub_index] <-scale(dat_ihi$Cond_Target_Error[sub_index]) dat_ihi$Cond_Angular_Norm[sub_index] <-scale(dat_ihi$Cond_Angular_Error[sub_index]) dat_ihi$Cond_Twist_Norm[sub_index] <-scale(dat_ihi$Cond_Twist_Error[sub_index]) dat_ihi$Test_Target_Norm[sub_index] <-scale(dat_ihi$Test_Target_Error[sub_index]) dat_ihi$Test_Angular_Norm[sub_index] <-scale(dat_ihi$Test_Angular_Error[sub_index]) dat_ihi$Test_Twist_Norm[sub_index] <-scale(dat_ihi$Test_Twist_Error[sub_index]) } ############################ ###Linear model of ratios### ############################ dat_ihi$TPower_scaled<-scale(as.numeric(as.character(dat_ihi$TPower))) dat_ihi$CPower_scaled<-scale(as.numeric(as.character(dat_ihi$CPower))) mod_inhibit<-lmer(Inhibition_ratio ~ CPower_scaled+ TPower_scaled+ IHI+ Cond_EMG + #considered log(Cond_EMG) but AIC was worse CPower_scaled:TPower_scaled+ #plus some interactions #CPower:IHI+ #TPower:IHI+ Cond_EMG:CPower_scaled+ #Cond_EMG:TPower_scaled+ Cond_EMG:IHI+ (1+Cond_EMG|Subject) #random slopes for TPower & CPower were too much to model ,REML=F ,control =lmerControl(optimizer = "bobyqa", optCtrl=list(maxfun=1e5)), #fiddle with optimizers #,verbose=T ,data=dat_ihi) relgrad <- with(mod_inhibit@optinfo$derivs,solve(Hessian,gradient)) #is this less than about 0.0001? If so can ignore convergence warnings max(abs(relgrad)) AIC(mod_inhibit) summary(mod_inhibit) ###diagnostics #deviance residuals pdf("Ratio_QQ.pdf", width = 3, height = 3) #start figure output qqnorm(y=residuals(mod_inhibit)) #not particularly normal. long tails. bootstrap? qqline(y=residuals(mod_inhibit)) #not particularly normal. long tails. bootstrap? dev.off() pdf("Ratio_distribution_of_residuals.pdf", width = 3, height = 3) #start figure output plot(density(residuals(mod_inhibit)), main="Distribution of Residuals") dev.off() ####BOOTSTRAPPING CIs### ###bootsrap mean estiamtes and CI TPowers_scaled<-unique(dat_ihi$TPower_scaled) CPowers_scaled<-unique(dat_ihi$CPower_scaled) IHIs<-as.factor(c(0.01, 0.05)) newdata <- expand.grid(TPowers_scaled,CPowers_scaled, IHIs, 0) #Condition hand regressed out names(newdata)<-c("TPower_scaled", "CPower_scaled","IHI", "Cond_EMG") newdata<-cbind(newdata,"Z") #a representative subject names(newdata)[5] <- "Subject" PredFun <- function(.) predict(.,newdata=newdata, type="response", allow.new.levels=T) #added type arg later. used unlogodds to make up for it system.time(mod_inhibit_boot<-bootMer(mod_inhibit, PredFun, nsim = 1000, #about 90 minutes by a workhorse re.form=NULL, type="parametric", #.progress = "txt", PBargs=list(style=3),#progress bar. silenced by parallel :() parallel = "multicore", ncpus = 4)) #parallel computing mod_inhibit_boot.mean = apply(mod_inhibit_boot$t, 2, function(x) mean(x)) mod_inhibit_boot.lower = apply(mod_inhibit_boot$t, 2, function(x) as.numeric(quantile(x, probs=.025, na.rm=TRUE))) mod_inhibit_boot.upper = apply(mod_inhibit_boot$t, 2, function(x) as.numeric(quantile(x, probs=.975, na.rm=TRUE))) mod_inhibit_ci<-tibble(Inhibition=mod_inhibit_boot.mean, Lower=mod_inhibit_boot.lower, Upper=mod_inhibit_boot.upper) mod_inhibit_ci<-cbind(newdata,mod_inhibit_ci) #unscale for interpretable plotting unscale<-function(vector,dataframe,digits=2){ center<-attr(dataframe, "scaled:center") scale<-attr(dataframe, "scaled:scale") out<- round(vector*scale+center, digits) return(out) } mod_inhibit_ci$CPower<-unscale(mod_inhibit_ci$CPower_scaled, dat_ihi$CPower_scaled) mod_inhibit_ci$TPower<-unscale(mod_inhibit_ci$TPower_scaled, dat_ihi$TPower_scaled) mod_inhibit_ci<-mutate(mod_inhibit_ci,IHI = paste("IHI", IHI,"S", sep=" ")) ######################### ###Bootstrap inference### ######################### #make new models mod_inhibit_main<-lmer(Inhibition ~ CPower_scaled+ TPower_scaled+ IHI+ Cond_EMG + #considered log(Cond_EMG) but AIC was worse #CPower:TPower_scaled+ #plus some interactions #CPower:IHI+ #TPower:IHI+ #Cond_EMG:CPower_scaled+ #Cond_EMG:TPower_scaled+ #Cond_EMG:IHI+ (1+Cond_EMG|Subject) #random slopes for TPower & CPower were too much to model ,REML=F ,control =lmerControl(optimizer = "bobyqa", optCtrl=list(maxfun=1e5)), #fiddle with optimizers #,verbose=T ,data=dat_ihi) #C=conditioning power, T = test power, H = IHI, E = conditioning EMG mod_inhibit_CT<-update(mod_inhibit, .~. -CPower_scaled:TPower_scaled) mod_inhibit_EC<-update(mod_inhibit, .~. -Cond_EMG:CPower_scaled) mod_inhibit_EH<-update(mod_inhibit, .~. -Cond_EMG:IHI) mod_inhibit_C<-update(mod_inhibit_main, .~. -CPower_scaled) mod_inhibit_T<-update(mod_inhibit_main, .~. -TPower_scaled) mod_inhibit_H<-update(mod_inhibit_main, .~. -IHI) mod_inhibit_E<-update(mod_inhibit_main, .~. -Cond_EMG) (PB.C<-PBmodcomp(mod_inhibit_main, mod_inhibit_C, nsim=1000)) (PB.T<-PBmodcomp(mod_inhibit_main, mod_inhibit_T, nsim=1000)) (PB.H<-PBmodcomp(mod_inhibit_main, mod_inhibit_H, nsim=1000)) (PB.E<-PBmodcomp(mod_inhibit_main, mod_inhibit_E, nsim=1000)) (PB.CT<-PBmodcomp(mod_inhibit, mod_inhibit_CT, nsim=1000)) (PB.EC<-PBmodcomp(mod_inhibit, mod_inhibit_EC, nsim=1000)) (PB.EH<-PBmodcomp(mod_inhibit, mod_inhibit_EH, nsim=1000)) #CONFIDENCE INTERVALS AROUND ESTIMATE ci<-confint.merMod(mod_inhibit, method="boot", nsim=1000) #####FIGURES##### ##OPTIMIZATION ggplot(mod_inhibit_ci,aes(x=TPower, y=CPower, colour=Inhibition, fill=Inhibition))+ scale_color_gradient2(low="#2c7fb8",mid="#addd8e", high="#dd1c77")+ scale_fill_gradient2(low="#2c7fb8",mid="#addd8e", high="#dd1c77")+ # scale_size(range = c(2, 25), limits=c(-50,200), guide=guide_legend(reverse = TRUE))+ geom_tile(alpha=1)+ #alpha=0.35 #geom_point(aes(size=Inhibition))+ facet_grid(IHI~.)+ labs(x="Test pulse power (x MT)", y="Conditioning pulse power (x MT)", fill="Inhibition\nRation", colour ="Inhibition\nRation")+ ggtitle("Heat map of parameter space")+ theme_bw(base_size=14) ggsave("heatMap_ratio_Variant.pdf", height=8,width=6) ###bootstrap for plotting mod_merBoot <- bootMer(mod_inhibit, predict, nsim = 1000, re.form = NA) CI.lower = apply(mod_merBoot$t, 2, function(x) as.numeric(quantile(x, probs=.025, na.rm=TRUE))) CI.upper = apply(mod_merBoot$t, 2, function(x) as.numeric(quantile(x, probs=.975, na.rm=TRUE))) CI.mean = apply(mod_merBoot$t, 2, mean) ci.dat<-cbind(CI.mean, CI.upper, CI.lower, dat_ihi) ci.dat<-mutate(ci.dat, IHI = paste("IHI",IHI, "S", sep=" "), TPower = paste("TPower", TPower, sep= " ")) #bootstrapped trendlines with 95% confidence intervals ggplot(ci.dat,aes(x=Cond_EMG, y=CI.mean, colour=CPower))+ geom_ribbon(aes(ymin=CI.lower,ymax=CI.upper, fill=CPower), alpha=0.25)+ geom_line()+ labs(x="Conditioning pulse MEP (uV)", y="Inhibition (uV)")+ scale_x_continuous(breaks=c(0, 500, 1000))+ ggtitle("Paradoxcial Conditioning Pulse EMG")+ facet_grid(IHI~TPower)+ theme_bw(base_size=12) #individual differences intercepts<-ranef(mod_inhibit)$Subject[,1] + fixef(mod_inhibit)[1] #intercepts slopes<-ranef(mod_inhibit)$Subject[,2] + fixef(mod_inhibit)["Cond_EMG"] #intercepts plot(NA,ylim=c(-150,150),xlim=c(0,900), ylab="Inhibition (uV)", xlab= "Conditioning EMG",main="Individual differences in cMEP-Inhibition slope") abline(h=0, col="lightgrey", lty=1) for(i in 1: length(slopes)){ abline(a=intercepts[i], b=slopes[i],lty=i) }