MEA_analysis_Axion.R 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. # Analysis code for MEA data
  2. #library(IGM.MEA)
  3. library(meaRtools) #attach meaRtools after IGM.MEA to get right parameter names <- fixed by getting rid of IGM.MEA, just by creating an extra .R function
  4. library(plyr)
  5. library(ggplot2)
  6. library(reshape2)
  7. library(gtools)
  8. library(pracma)
  9. library(svDialogs)
  10. #CODEdirectory<- folder where the code files reside
  11. CODEdirectory<-paste0(choose.dir(default = "", caption = "SELECT THE FOLDER WITH THE CODE"),"\\")
  12. #alternative, not to select this every code launch:
  13. ##CODEdirectory<-'Your folder with the code'. Remember / or \\ at the end
  14. output_folder <- choose.dir(default = "", caption = "SELECT YOUR OUTPUT FOLDER")
  15. options(warn=1)
  16. parameter_file <- paste0(CODEdirectory, 'saved.parameters')
  17. #Select the spike files
  18. my.files <- as.list(choose.files(default = "", caption = "SELECT YOUR SPIKE FILES", multi = TRUE))#, filters = Filters, index = nrow(Filters))
  19. my.noise <- choose.files(default = "", caption = "SELECT YOUR NOISY ELECTRODE FILE", multi = FALSE)
  20. my.explog <- choose.files(default = "", caption = "SELECT YOUR TREATMENT FILE (expLog)", multi = FALSE) #MULTIPLE??????
  21. ########## ADJUST PARAMETERS ##########
  22. # load parameters
  23. my.parameters <- readRDS(parameter_file)
  24. # change parameters if needed
  25. my.parameters$elec_min_rate <- 10/60 # minimum spikes/second for electrode to be included in analysis, e.g. 10/60
  26. my.parameters$elec_max_rate <- 1000 # maximum spikes/second for electrode to be included in analysis, e.g. 1000
  27. my.parameters$well_min_rate <- 1 # minimum number of active electrodes (here >5 spikes/minute used) for a well to be included to goodwells, e.g. 1
  28. my.parameters$Sigma <- c(10, 20, 50) # window sizes for network bursts analysis in ms, e.g. c(10, 20, 50)
  29. my.parameters$min_electrodes <- 5 # minimum number of electrodes participating in a network burst, e.g. 5
  30. ########## RUN ADDITIONAL FUNCTIONS ##########
  31. MEAlayout <- as.integer(dlgInput("Enter your MEA plate layout: 12 or 48")$res)
  32. if (MEAlayout == 12) {
  33. source(paste0(CODEdirectory, "plate_layout_Axion12_LAa.R"))
  34. my.array_type <- 'Axion12'
  35. my.pos <- paste0(CODEdirectory, 'positions_Axion_12wp_200nm_spacing.csv')
  36. } else if (MEAlayout == 48) {
  37. source(paste0(CODEdirectory, "plate_layout_Axion48_LAa.R"))
  38. my.array_type <- 'Axion48'
  39. my.pos <- paste0(CODEdirectory, 'positions_Axion_48wp_350nm_spacing.csv') #positions_Axion_48wp.csv
  40. } else {
  41. stop("Please, enter the appropriate MEA layout configuration: 12 or 48 wells per plate!")
  42. }
  43. source(paste0(CODEdirectory, "logisi_algorithm_2018_LAa.R"))
  44. source(paste0(CODEdirectory, "aggregate_features_algorithm_LAa.R"))
  45. source(paste0(CODEdirectory, "burst_summary_algorithm_LAa.R"))
  46. source(paste0(CODEdirectory, "github_nb_bursts_modifications_LAa.R"))
  47. source(paste0(CODEdirectory, "cheminfo.R"))
  48. CRAPels=list()
  49. ########## IMPORT ##########
  50. my.output <- paste0(output_folder, '/Analysis')
  51. dir.create(my.output)
  52. my.out_perDIV <- paste0(my.output, '/PerDIV')
  53. dir.create(my.out_perDIV)
  54. my.out_object <- paste0(my.output, '/R objects')
  55. dir.create(my.out_object)
  56. my.out_log <- paste0(my.output, '/LogFiles')
  57. dir.create(my.out_log)
  58. analysis <- list(spikeFiles = my.files, output_dir = my.output, r_output_dir = my.out_object, output_perDIV_dir = my.out_perDIV)
  59. my.exp_ID <- strsplit(basename(my.explog), split = '_')[[1]][1] #my.treatment_file
  60. my.div <- lapply(my.files, function(x) strsplit(basename(x), split='\\.')[[1]][1]) #my.spike_files
  61. my.div <- lapply(my.div, function(x) strsplit(basename(x), split = '_')[[1]][4])
  62. # import treatment information
  63. my.chem_info <- cheminfo(my.files[[1]], my.explog)
  64. # import noisy electrode information
  65. my.noise_info <- read.csv(my.noise, stringsAsFactors = FALSE)
  66. my.noise_div <- c()
  67. for (i in 1:length(my.noise_info)) {
  68. my.noise_day <- colnames(my.noise_info)[i]
  69. my.noise_div <- c(my.noise_div, my.noise_day)
  70. }
  71. # import spike files
  72. my.set <- list()
  73. for (i in 1:length(my.files)) {
  74. my.set[[i]] <- read_spikelist_text(my.files[[i]], my.pos, array = my.array_type, div = my.div[[i]])
  75. my.set[[i]]$parameters <- my.parameters
  76. }
  77. ########## ANALYSIS ##########
  78. # filter inactive electrodes and noisy electrodes (inactive wells not filtered)
  79. for (i in 1:length(my.set)) {
  80. my.wells <- my.set[[i]]$well
  81. my.day <- my.set[[i]]$div
  82. # filter inactive electrodes
  83. low <- which(my.set[[i]]$meanfiringrate < my.parameters$elec_min_rate)
  84. high <- which(my.set[[i]]$meanfiringrate > my.parameters$elec_max_rate)
  85. extremes <- c(low, high)
  86. if (!isempty(extremes)) {
  87. bad_ids <- names(extremes)
  88. CRAPels[[i]]=bad_ids
  89. bad_ids <- c("-", bad_ids) #MINUS IN FRONT OF THEM REMOVES NOT PRESERVES
  90. my.set[[i]] <- remove_spikes(my.set[[i]], bad_ids)
  91. }
  92. # filter noisy electrodes
  93. #INSERT CONDITIONAL STATEMENT HERE !! IF NO NOISY ELECTRODES----OK, PROCEED
  94. if (!isempty(my.set[[i]]$channels)) {
  95. all_ids <- names(my.set[[i]]$meanfiringrate)
  96. bad_ids <- c()
  97. if (is.element(my.day, my.noise_div)) {
  98. my.index <- which(my.noise_div==my.day)
  99. my.current <- my.noise_info[my.index]
  100. if (nrow(my.current)>0) {
  101. for (k in 1:nrow(my.noise_info)) {
  102. if (is.element(my.current[k,1], all_ids)) {
  103. bad_ids <- c(bad_ids, my.current[k,1])
  104. }
  105. }
  106. } else {
  107. message("WARNING: Seems like your noisy electrode file is empty")
  108. }
  109. }
  110. if (length(bad_ids) > 0) {
  111. bad_ids <- c("-", bad_ids)
  112. #my.set[[i]] <- remove_spikes(my.set[[i]], bad_ids)
  113. my.set[[i]] <- suppressWarnings(remove_spikes(my.set[[i]], bad_ids))
  114. }
  115. }
  116. my.set[[i]]$parameters <- my.parameters
  117. my.set[[i]]$well <- my.wells
  118. my.set[[i]] <- get_num_ae(my.set[[i]]) #lists for each well the # of active electrodes
  119. my.set[[i]]$goodwells <- names(which(my.set[[i]]$nae >= my.parameters$well_min_rate)) #writting down goodwells' labels using "names" function
  120. my.set[[i]]$parameters$time_stamp <- format(Sys.time(), '%m-%d-%y_%H_%M%_%S')
  121. my.set[[i]]$div <- my.day
  122. my.treatment <- my.chem_info$treatment
  123. my.set[[i]]$treatment <- my.treatment
  124. names(my.set[[i]]$treatment) <- my.chem_info$well #assignation of well labels to the tratments
  125. my.size <- my.chem_info$size
  126. my.dose <- my.chem_info$dose
  127. my.units <- my.chem_info$units
  128. my.set[[i]]$size <- as.array(my.size)
  129. names(my.set[[i]]$size) <- my.wells
  130. my.set[[i]]$dose <- as.array(my.dose)
  131. names(my.set[[i]]$dose) <- my.wells
  132. my.set[[i]]$units <- as.array(my.units)
  133. names(my.set[[i]]$units) <- my.wells
  134. }
  135. # remove recordings with no active electrodes
  136. my.inactive_rec <- c()
  137. my.inactive_div <- c()
  138. for (i in 1:length(my.set)) {
  139. my.index <- c()
  140. if (isempty(my.set[[i]]$channels)) {
  141. my.index <- i
  142. my.inactive_div <- c(my.inactive_div, my.div[[i]])
  143. my.inactive_rec <- c(my.inactive_rec, my.index)
  144. }
  145. }
  146. if (!isempty(my.inactive_rec)) {
  147. my.set <- my.set[-my.inactive_rec]
  148. my.removed_recs <- cbind((my.inactive_div), (my.inactive_rec))
  149. colnames(my.removed_recs) <- c('DIV','Index')
  150. write.csv(my.removed_recs, file = paste0(my.output,'/inactive_recordings.csv'), quote = FALSE, row.names = FALSE)
  151. }
  152. # analysis begins
  153. # entropy and mutual information
  154. for (i in 1:length(my.set)) {
  155. my.emi <- calculate_entropy_and_mi(my.set[[i]], my.set[[i]]$treatment, mult_factor=1.5, bin_size=0.1)
  156. if (isempty(my.emi[["data_dists"]][["MI"]])) {
  157. my.set[[i]]$mutual_inf <- NA
  158. } else {
  159. my.set[[i]]$mutual_inf <- my.emi[["data_dists"]][["MI"]]
  160. }
  161. if (isempty(my.emi[["data_dists"]][["ENT"]])) {
  162. my.set[[i]]$entropy <- NA
  163. } else {
  164. my.set[[i]]$entropy <- my.emi[["data_dists"]][["ENT"]]
  165. }
  166. }
  167. # burst features
  168. for (i in 1:length(my.set)) {
  169. current <- my.set[[i]]
  170. if (length(current$nspikes) > 0) {
  171. current$allb <-
  172. lapply(current$spikes, function(x) logisi.pasq.method_mod(x, cutoff = 0.1))
  173. current$bs <- calc_burst_summary(current, bursty_threshold = 1)
  174. current$bs$burst_type <- "li"
  175. for (j in 1:nrow(current$bs)) {
  176. if (current$bs[j,'nbursts'] == 0) {
  177. current$bs[j,'bursts_per_sec'] <- NA
  178. current$bs[j,'bursts_per_min'] <- NA
  179. current$bs[j,'mean_dur'] <- NA
  180. current$bs[j,'mean_spikes'] <- NA
  181. }
  182. }
  183. my.set[[i]] <- current
  184. }
  185. }
  186. # isis
  187. for (i in 1:length(my.set)) {
  188. my.set[[i]] <- calculate_isis(my.set[[i]])
  189. my.set[[i]]$well_stats <- compute_mean_firingrate_by_well(my.set[[i]])
  190. }
  191. # network spikes
  192. for (i in 1:length(my.set)) {
  193. nw_spikes_old <- calculate_network_spikes(my.set[[i]], my.parameters$sur, my.parameters$ns_n, my.parameters$ns_t)
  194. nw_spikes <- summarize_network_spikes(my.set[[i]], nw_spikes_old,ns_e = 1, my.parameters$sur)
  195. my.set[[i]]$ns_all <- nw_spikes$ns_all
  196. my.set[[i]]$nw_spikes <- nw_spikes
  197. }
  198. # networks bursts
  199. nb.list <- calculate_network_bursts_mod(my.set, sigmas = my.parameters$Sigma, min_electrodes = my.parameters$min_electrodes, local_region_min_nae = my.parameters$local_region_min_nAE)
  200. nb_features <- nb_matrix_to_feature_dfs( nb.list$nb_features_merged )
  201. # attach data to s object
  202. for (i in 1:length(my.set) ){
  203. my.set[[i]]$nb_all<-nb.list$nb_all[[i]]
  204. my.set[[i]]$data.frame$nb_features<-nb.list$nb_features[[i]]
  205. }
  206. # STTC
  207. for (i in 1:length(my.set)) {
  208. my.sttc <- c()
  209. my.sttc <- compute_mean_sttc_by_well(my.set[[i]])
  210. for (k in 1:length(my.sttc)) {
  211. if (is.null(my.sttc[[k]])) {
  212. my.sttc[[k]][1] <- NA
  213. }
  214. }
  215. my.set[[i]]$mean_sttc <- my.sttc
  216. }
  217. # save the data set
  218. saveRDS(my.set, paste0(analysis$r_output_dir, '/', my.exp_ID, format(Sys.time(), '_%m-%d-%y_%H_%M%_%S')))
  219. ########## EXPORT ##########
  220. # print spikes graphs (pdf format) for each recording
  221. plot_plate_summary_for_spikes(my.set,analysis$output_perDIV_dir)
  222. # write spike feature tables for each recording
  223. suppressWarnings(write_plate_summary_for_spikes(my.set,analysis$output_perDIV_dir))
  224. # plot burst pdfs for each recording
  225. suppressWarnings(plot_plate_summary_for_bursts(my.set,analysis$output_perDIV_dir,my.parameters))
  226. # write burst feature tables for each recording
  227. write_plate_summary_for_bursts_mod(my.set,analysis$output_perDIV_dir)
  228. # ns plot and excel commands seems weird with nspikes
  229. for (i in 1:length(my.set)) {
  230. basename <- strsplit(basename(my.set[[i]]$file), "[.]")[[1]][1]
  231. #Use the next commands for plotting all the ns graphs. Try opening a pdf file so that all will be printed to the same file (which is automatically done for burst features):
  232. pdf(file=paste0(analysis$output_perDIV_dir,"/", basename, "_ns_plot.pdf"))
  233. xyplot_network_spikes(my.set[[i]]$nw_spikes)
  234. plot_active_wells_network_spikes(my.set[[1]]$nw_spikes)
  235. dev.off()
  236. }
  237. # aggregate features (goodwells, emi and sttc needed)
  238. my.aggs <- aggregate_features(my.set, 'spike', parameters = my.parameters)
  239. my.aggb <- aggregate_features_mod(my.set, 'burst', parameters = my.parameters)
  240. my.aggns <- aggregate_features(my.set, 'ns', parameters = my.parameters)
  241. # export aggregated features
  242. write_features_to_files(my.set, my.aggs, analysis$output_dir, 'spikes')
  243. write_features_to_files(my.set, my.aggb, analysis$output_dir, 'bursts')
  244. write_features_to_files(my.set, my.aggns, analysis$output_dir, 'ns')
  245. write_features_to_files(my.set, nb_features, analysis$output_dir, 'nb')
  246. # print the following alarms if needed
  247. if (length(my.div) == length(my.noise_div)) {
  248. n <- 0
  249. for (i in 1:length(my.noise_div)) {
  250. if (is.element(my.noise_div[[i]], my.div)) {
  251. n <- n + 1
  252. }
  253. }
  254. if (n != length(my.noise_div)) {
  255. print(paste0((length(my.noise_div)-n), ' noise DIVs do not correspond to rec DIVs.'))
  256. }
  257. } else {
  258. print(paste0('Length of rec DIVs does not correspond to length of noise DIVs.'))
  259. }
  260. if (!isempty(my.inactive_rec)) {
  261. print(paste0('Some recordings did not include active electrodes.'))
  262. }