Skip to content

Commit

Permalink
Merge pull request #33 from ankemt/improve-design-16
Browse files Browse the repository at this point in the history
Add functionality and improvements around design text file
  • Loading branch information
bvreede authored Apr 20, 2023
2 parents 6ab47de + 0d3d617 commit 59bb2ba
Show file tree
Hide file tree
Showing 13 changed files with 7,222 additions and 67 deletions.
16 changes: 8 additions & 8 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ Encoding: UTF-8
LazyData: true
RoxygenNote: 7.2.1
Imports:
brio,
dplyr,
magrittr,
robustbase (>= 0.93-6),
stats,
stringr,
tidyr,
tidyselect
assertthat,
dplyr,
magrittr,
robustbase (>= 0.93-6),
stats,
stringr,
tidyr,
tidyselect
Suggests:
testthat (>= 3.1.5)
Config/testthat/edition: 3
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Generated by roxygen2: do not edit by hand

export(categorize_wells)
export(exp_design)
export(find_outliers)
export(parse_MEA_file)
export(parse_designfile)
Expand Down
117 changes: 117 additions & 0 deletions R/exp_design.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#' Generate experimental design file
#'
#' With information provided by the user, a file is created
#' that formalizes the experimental design so it can be used to
#' analyse the MEA file.
#'
#' @param date date (string, preferably formatted as YYYYMMDD), default is prompted
#' @param expID identifier for the experiment (string), default is prompted
#' @param path path to the design file (dir/name.txt)
#'
#' @export
exp_design <- function(path="./design.txt",
date = readline(prompt="What is the date of the experiment? (YYYYMMDD) "),
expID = readline(prompt="What is the experiment ID? ")){
# TODO check if file can be created

nwells <- 48 # TODO make this a question also

write_meta(date, expID, nwells, path)

# TODO make this a while loop where wells are scored instead of a Q/A
continue <- TRUE
while(continue){
message("You can now add experimental conditions one by one:")
write_category(nwells=nwells, path=path)
check_cont <- readline(prompt = "Do you want to add another group? (y/n) ")
continue <- stringr::str_detect(check_cont, "y|Y")
}
}


write_meta <- function(date, expID, nwells, path){
directory <- dirname(path)
if(!dir.exists(directory)){
dir.create(directory, recursive=TRUE)
}
meta <- paste0("Date: ", date, "\n",
"ExperimentID: ", expID, "\n",
"Total_wells: ", nwells, "\n",
"Groups:")
write(meta, file=path)

message(paste("The design metadata has been written to file at",path))
}

write_category <- function(
group = readline(prompt="What is the experimental condition? "),
start = readline(prompt="What is the first well in this category? (e.g.: A1) "),
end = readline(prompt="What is the last well in this category? (e.g.: F8) "),
dirx = readline(
prompt="In what direction ('LR' for left-to-right or 'TB' for top-to-bottom) is the sequence of wells? "),
nwells, path){
groupname <- paste0(group, ":") # hacky way to make sure the group question is asked first
wells <- categorize_wells(start, end, nwells, direction=dirx)
write(paste(groupname, wells), file=path, append=TRUE)
}


#' Create well ranges for a design file
#'
#' @param start First well ID (e.g. "A1")
#' @param end Last well ID (e.g "B7")
#' @param nwells Total number of wells on the plate (currently only 48)
#' @param direction TB for top-to-bottom or LR for left-to-right
#'
#' @return string with a range of well IDs
#' @export
#'
#' @examples
#' # Wells from left to right, starting at A1 and ending at B4
#' categorize_wells("A1", "B4")
#' # Wells from top to bottom, starting at A1 and ending at B4
#' categorize_wells("A1", "B4", direction="TB")
categorize_wells <- function(start, end, nwells = 48, direction = "LR"){
assertthat::assert_that(direction %in% c("LR", "TB"),
msg = "The direction must be LR (left-to-right), or TB (top-to-bottom).")

assertthat::assert_that(typeof(nwells) == "double",
msg = "`nwells` should be a number.")
#TODO would be nicer if it can convert a string to a number as well

assertthat::assert_that(nwells %in% c(48), #TODO: add other options like c(6, 12, 24, 48, 96),
msg = "The total number of wells on the plate (`nwells`) is incorrect.")

rowtotal <- 6
coltotal <- 8
allcols <- 1:coltotal
allrows <- LETTERS[1:rowtotal]

startrow = stringr::str_extract(start, "[:alpha:]")
startcol = as.numeric(stringr::str_extract(start, "[:digit:]"))
endrow = stringr::str_extract(end, "[:alpha:]")
endcol = as.numeric(stringr::str_extract(end, "[:digit:]"))

rows <- LETTERS[letter_as_number(startrow):letter_as_number(endrow)]
cols <- startcol:endcol

wells <- NULL

if(direction == "LR"){
for(row in rows) wells <- c(wells, paste0(row, allcols))
removecols <- length(wells) - (max(allcols) - endcol)
wells <- wells[startcol:removecols]
} else if(direction == "TB"){
for(col in cols) wells <- c(wells, paste0(allrows, col))
maxrow <- max(letter_as_number(allrows))
removerows <- length(wells) - (maxrow - letter_as_number(endrow))
wells <- wells[letter_as_number(startrow):removerows]
}

wells <- paste(wells, collapse = " ")
return(wells)
}

letter_as_number <- function(letter) as.numeric(stats::setNames(1:26, LETTERS)[letter])


59 changes: 26 additions & 33 deletions R/parse_designfile.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,19 @@
#'
#' @export
parse_designfile <- function(path){
info <- brio::readLines(path)
data <- utils::read.csv(path, header=F, sep=":")

metadata_names <- c("Date", "ExperimentID", "Total_wells")
# remove hashed lines, and trim spaces before and after the text
data <- data[grep("^#", data$V1, invert=T),]
data$V1<- stringr::str_trim(data$V1, side = "both")
data$V2 <- stringr::str_trim(data$V2, side = "both")

# make objects to fill
designlist <- NULL
metadata <- NULL
design <- NULL
# the groups start after Groups
groups_start <- grep("Groups", data$V1)
design <- data[(groups_start+1):nrow(data),]

for(i in info){
if(stringr::str_starts(i, "#")){next} # remove any lines that start with #

i <- stringr::str_split(i, " ", simplify=TRUE)

# get date, experimentID, total_wells
if(i[1] %in% metadata_names){
metadata <- rbind(metadata,i)
} else{
if(length(i) > 2){
design <- rbind(design, i)
}
}
# get groups

}
# metadata is everything before Groups
metadata <- data[(1:groups_start-1),]

metadata <- rotate_to_df(metadata)
design <- make_design_df(design)
Expand All @@ -56,20 +44,25 @@ rotate_to_df <- function(df){


make_design_df <- function(df){
# ensure the first two columns are empty, before removing them
if(paste(df[,1],collapse="") != "" |
paste(df[,2],collapse="") != ""){
stop("Experimental design is unclear. Ensure that every group is preceded by two spaces.")
cat_wells <- stringr::str_split(df$V2, '[:blank:]*[:punct:]+[:blank:]*|[:blank:]+')
#names(cat_wells) <- df$V1

allwells <- NULL
allcats <- NULL

for(n in 1:length(cat_wells)){
wells <- cat_wells[[n]]
allwells <- c(allwells, wells)
cats <- rep(df$V1[n], length(wells))
allcats <- c(allcats, cats)
}
df <- df[,-c(1,2)]

df <- rotate_to_df(df)
df <- data.frame(Well = allwells, Group = allcats)

if(anyDuplicated(df$Well)>0){
stop("Duplicate well names are not allowed.")
}

# pivot to two columns and sort
df <- tidyr::pivot_longer(df,
cols = tidyr::everything(),
names_to = "Group",
values_to = "Well")
#TODO: make_design_df: no visible binding for global variable ‘Well’
#TODO: make_design_df: no visible binding for global variable ‘Group’
df <- dplyr::select(df, Well, Group)
Expand Down
1 change: 1 addition & 0 deletions R/treatment_ratio.R
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ return(df)


create_path <- function(path){
# can the following code not be replaced by `dirname`?
path_vector <- stringr::str_split(path, "[\\\\/]")[[1]]
lv <- length(path_vector)
if(path_vector[lv] == ""){
Expand Down
24 changes: 13 additions & 11 deletions inst/extdata/design_testfile.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
Date 20220905
ExperimentID test
Total_wells 48
Date: 20220905
ExperimentID: test
Total_wells: 48
# Specify the groups and the different wells below.
# Always use two spaces before the group name
# and separate the group and the well names with a space.
Groups
Control D1 D2 D3 D4 D5 D6 D7 D8
0.01 F1 F2 F3 F4 F5 F6 F7 F8
0.1 E1 E2 E3 E4 E5 E6 E7 E8
1 C1 C2 C3 C4 C5 C6 C7 C8
10 B1 B2 B3 B4 B5 B6 B7 B8
30 A1 A2 A3 A4 A5 A6 A7 A8
# and separate the group and the well names with a space or comma.
# This design testfile has some nice weird punctuation
# to check that everything is still parsed correctly
Groups:
Control: D1 D2 D3 D4 D5 D6 D7 D8
0.01: F1,F2,F3 F4 F5 F6 F7 F8
0.1: E1 E2,E3 E4 E5 E6 E7 E8
1: C1 C2 C3 C4 C5 C6 , C7 C8
10: B1 B2 B3 B4 B5 B6 B7 B8
30: A1 A2 A3 A4 A5 A6 A7 A8
6 changes: 6 additions & 0 deletions inst/extdata/design_testfile_duplicates.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Date: 20230407
ExperimentID: PR
Total_wells: 48
Groups:
control: A3 B3 C3 D3 E3 F3 A4
0.1: A3 B3 C3 D3 E3 F3 A4
6 changes: 6 additions & 0 deletions inst/extdata/design_testfile_unequalrows.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Date: 20230407
ExperimentID: PR
Total_wells: 48
Groups:
control: B6 B7 B8 C1 C2 C6 C4 B5
0.1: A3 B3 C3 D3 E3 F3 A4
29 changes: 29 additions & 0 deletions man/categorize_wells.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions man/exp_design.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 59bb2ba

Please sign in to comment.