Search code examples
rbioinformatics

Generating a 96 or 384 well plate layout in R


I am trying to write some code which will take a .csv file which contains some sample names as input and will output a data.frame containing the sample names and either a 96 well plate or 384 well plate format (A1, B1, C1...). For those who do not know, a 96 well plate has eight alphabetically labeled rows (A, B, C, D, E, F, G, H) and 12 numerically labeled columns (1:12) and a 384 well plate has 16 alphabetically labeled rows (A:P) and 24 numerically labeled columns (1:24). I am trying to write some code that will generate either of these formats (there CAN be two different functions to do this) allowing for the samples to be labeled either DOWN (A1, B1, C1, D1, E1, F1, G1, H1, A2...) or ACROSS (A1, A2, A3, A4, A5 ...).

So far, I have figured out how to get the row names fairly easily

rowLetter <- rep(LETTERS[1:8], length.out = variable)
#variable will be based on how many samples I have

I just cannot figure out how to get the numeric column names to apply correctly... I have tried:

colNumber <- rep(1:12, times = variable) 

but it isn't that simple. All 8 rows must be filled before the col number increases by 1 if you're going 'DOWN' or all 12 columns must be filled before the row letter increases by 1 if you're going 'ACROSS'.

EDIT:

Here is a clunky version. It takes the number of samples that you have, a 'plate format' which IS NOT functional yet, and a direction and will return a data.frame with the wells and plate numbers. Next, I am going to a) fix the plate format so that it will work correctly and b) give this function the ability to take a list of samples names or ID's or whatever and return the sample names, well positions, and plate numbers!

plateLayout <- function(numOfSamples, plateFormat = 96, direction = "DOWN"){
  #This assumes that each well will be filled in order. I may need to change this, but     lets get it working first.

  #Calculate the number of plates required
  platesRequired <- ceiling(numOfSamples/plateFormat)
  rowLetter <- character(0)
  colNumber <- numeric(0)
  plateNumber <- numeric(0)

  #The following will work if the samples are going DOWN
  if(direction == "DOWN"){
    for(k in 1:platesRequired){
     rowLetter <- c(rowLetter, rep(LETTERS[1:8], length.out = 96))  
      for(i in 1:12){
       colNumber <- c(colNumber, rep(i, times = 8))
      }
     plateNumber <- c(plateNumber, rep(k, times = 96))
    }  
  plateLayout <- paste0(rowLetter, colNumber)
  plateLayout <- data.frame(plateLayout, plateNumber)
  plateLayout <- plateLayout[1:numOfSamples,]
  return(plateLayout)
  }

  #The following will work if the samples are going ACROSS 
  if(direction == "ACROSS"){
    for(k in 1:platesRequired){
      colNumber <- c(colNumber, rep(1:12, times = 8))
      for(i in 1:8){
        rowLetter <- c(rowLetter, rep(LETTERS[i], times = 12))
        }
      plateNumber <- c(plateNumber, rep(k, times = 96))
      }
    plateLayout <- paste0(rowLetter, colNumber)
    plateLayout <- data.frame(plateLayout, plateNumber)
    plateLayout <- plateLayout[1:numOfSamples,]
    return(plateLayout)
  }
}

Does anybody have any thoughts on what else might make this cool? I'm going to use this function to generate .csv or .txt files to use as sample name imports for different instruments so I will be kind of constrained in terms of 'cool features', but I think it would be cool to use ggplot to make a graphic which shows the plates and sample names?


Solution

  • The following code does what I set out to do. You can use it to make as many plates as you need, with the assumptions that whatever your import list is will be in order. It can make as many plates as you need and will add a column for "plateNumber" which will indicate which batch it's on. It can only handle 96 or 384 well plates, but that is all I deal in so that is fine.

    plateLayout <- function(numOfSamples, plateFormat = 96, direction = "DOWN"){
      #This assumes that each well will be filled in order.
    
    #Calculate the number of plates required
    platesRequired <- ceiling(numOfSamples/plateFormat)
    rowLetter <- character(0)
    colNumber <- numeric(0)
    plateNumber <- numeric(0)
    
    #define the number of columns and number of rows based on plate format (96 or 384 well plate)
    switch(as.character(plateFormat),
           "96" = {numberOfColumns = 12; numberOfRows = 8},
           "384" = {numberOfColumns = 24; numberOfRows = 16})
    
    #The following will work if the samples are going DOWN
    if(direction == "DOWN"){
      for(k in 1:platesRequired){
        rowLetter <- c(rowLetter, rep(LETTERS[1:numberOfRows], length.out = plateFormat))  
      for(i in 1:numberOfColumns){
        colNumber <- c(colNumber, rep(i, times = numberOfRows))
        }
    plateNumber <- c(plateNumber, rep(k, times = plateFormat))
      }  
    plateLayout <- paste0(rowLetter, colNumber)
    plateLayout <- data.frame(plateNumber,plateLayout)
    plateLayout <- plateLayout[1:numOfSamples,]
    return(plateLayout)
    }
    
    #The following will work if the samples are going ACROSS 
    if(direction == "ACROSS"){
      for(k in 1:platesRequired){
        colNumber <- c(colNumber, rep(1:numberOfColumns, times = numberOfRows))
        for(i in 1:numberOfRows){
          rowLetter <- c(rowLetter, rep(LETTERS[i], times = numberOfColumns))
          }
        plateNumber <- c(plateNumber, rep(k, times = plateFormat))
        }
      plateLayout <- paste0(rowLetter, colNumber)
      plateLayout <- data.frame(plateNumber, plateLayout)
      plateLayout <- plateLayout[1:numOfSamples,]
      return(plateLayout)
      }
    }
    

    An example of how to use this would be as follows

    #load whatever data you're going to use to get a plate layout on (sample ID's or names or whatever)
    thisData <- read.csv("data.csv")
    
    #make a data.frame containing your sample names and the function's output
        #alternatively you can use length() if you have a list
    plateLayoutDataFrame <- data.frame(thisData$sampleNames, plateLayout(nrow(thisData), plateFormat = 96, direction = "DOWN")
    
    #It will return something similar to the following, depending on your selections
    #data plateNumber plateLayout
    #sample1           1          A1
    #sample2           1          B1
    #sample3           1          C1
    #sample4           1          D1
    #sample5           1          E1
    #sample6           1          F1
    #sample7           1          G1
    #sample8           1          H1
    #sample9           1          A2
    #sample10          1          B2
    #sample11          1          C2
    #sample12          1          D2
    #sample13          1          E2
    #sample14          1          F2
    #sample15          1          G2
    

    That sums up this function for now. Roland offered a good method of doing this which is less verbose, but I wanted to avoid the use of external packages if possible. I'm working on a shiny app now which actually uses this! I want it to be able to automatically subset based on the 'plateNumber' and write each plate as it's own file... for more on this, go to: Automatic multi-file download in R-Shiny