Here is some R code for dealing with ASCII files as output by BMG MARS analysis

software, specifically to make use of data spread across different plates as a result of using the BMG Microplate Stacker.
I had a few problems with the stacker jamming when we first got it, but this was solved by (1) lubricating the magazines with a silicon spray (food-grade, not sure if that matters), (2) periodic cleaning with a degreaser, and (3) making the first step of any protocol a ‘restack’ all plates command, so that the plates are positioned more accurately. Since doing these steps, I’ve had minimal problems. In general, I like BMG plate readers for the exact reason most people don’t: the scripting language. Although it isn’t the most sophisticated, the scripting language is powerful enough to design some fairly complex protocols. Combined with the stacker, which can hold either 25 or 50 microplates depending on which magazine set you have, it’s useful for running high-throughput phenotype assays.
require(lubridate)
require(tidyverse)
require(growthcurver)
# Some functions for importing BMG-style CSV files in a tidy way
BMGtime = function(otime){
# A function to convert BMG's default time format to fractional hours (can also set in MARS)
missing.s=grep("[0-9] s",otime, value=F,invert=T)
missing.m=grep("[0-9] min",otime, value=F,invert=T)
missing.h=grep("[0-9] h",otime, value=F,invert=T)
otime[missing.s]=sub("$"," 0 s", otime[missing.s])
otime[missing.h]=sub("^","0 h ", otime[missing.h])
otime[missing.m]=sub("h", "h 0 min ", otime[missing.m])
otime=period_to_seconds(hms(gsub("[a-z ]+",":", otime)))/3600
return(otime)
}
# BMG MARS CSV exports have a trailing comma, so this drops the empty last column
read_csv_drop = function(...) read_csv(...) %>% select(-ncol(.))
# Read a single BMG Mars wide-format time series table into long format
readBMG = function(file){
growthdata = read_csv_drop(file, skip=0) %>%
select(-contains("Blank corrected"))
ndescriptors = ifelse("Group"%in%names(growthdata), 4, 3)
header = tolower(gsub("Well ", "", names(growthdata)[1:ndescriptors]))
time = growthdata %>% select(-(1:ndescriptors)) %>% slice(1) %>% t()
if(grepl("[a-z]", time)) time = BMGtime(time)
names(growthdata) = c(header, time)
growthdata = growthdata %>%
slice(2:n()) %>%
type_convert() %>%
gather("time","value",-header)
return(growthdata)
}
# Read multiple files from the same experiment (resulting from a stacker run)
readStack = function(path=NULL, pattern, parse.name=F, prefix=NULL, suffix=NULL, into=NULL, sep=NULL, ...){
stackdata = data_frame(id = list.files(pattern=pattern, path=path, ...), data = map2(id, path, ~readBMG(paste0(.y,"/",.x))))
if(parse.name){
if(!is.null(prefix)){
stackdata = stackdata %>%
mutate(id = gsub(prefix, "", id))
}
if(!is.null(suffix)){
stackdata = stackdata %>%
mutate(id = gsub(suffix, "", id))
}
stackdata = stackdata %>% separate(id, sep=sep, into=into)
}
stackdata = stackdata %>% unnest(data) %>% type_convert()
return(stackdata)}