#' Check primers for complementarity
#'
#' Check a TAP-seq primer set, i.e. outer or inner primers for a target gene panel, for potential
#' complementarity issues when multiplexing. Uses Primer3's \code{check_primers} functionality.
#'
#' @param object A \code{\link[TAPseq:TsIO-class]{TsIO}} or
#' \code{\link[TAPseq:TsIOList-class]{TsIOList}} object containing designed primers.
#' @param primer_opt_tm,primer_min_tm,primer_max_tm Optimal, minumum and maximum primer melting
#' temperature. Should be the same values that were used when designing the primers.
#' @param thermo_params_path Optional path (character) to the \code{primer3_config} directory. Only
#' required when using Primer3 < 2.5.0.
#' @param primer3_core Path (character) to the \code{primer3_core} executable. Usually this is
#' inferred when loading/attaching the package.
#' @return A \code{\link[base]{data.frame}} with \code{check_primers} results.
#' @seealso \url{http://primer3.org/manual.html} for Primer3 manual.
#' @examples
#' library(ggplot2)
#'
#' # chr11 primers example data
#' data("chr11_primers")
#'
#' # pick best primers based on predicted off-targets for subset of all primers
#' best_primers <- pickPrimers(chr11_primers, n = 1, by = "off_targets")
#'
#' # check for complementarity
#' \dontrun{
#' comp <- checkPrimers(best_primers)
#'
#' # plot complementarity scores for every pair. the lines indicate complementarity scores of 47,
#' # the default value applied by Primer3 to identify high complementarity primer pairs
#' ggplot(comp, aes(x = primer_pair_compl_any_th, y = primer_pair_compl_end_th)) +
#' geom_hline(aes(yintercept = 47), colour = "darkgray", linetype = "dashed") +
#' geom_vline(aes(xintercept = 47), colour = "darkgray", linetype = "dashed") +
#' geom_point(alpha = 0.25) +
#' theme_bw()
#' }
#' @export
setGeneric("checkPrimers",
function(object, primer_opt_tm = 63, primer_min_tm = 59, primer_max_tm = 66,
thermo_params_path = NA, primer3_core = getOption("TAPseq.primer3_core"))
standardGeneric("checkPrimers")
)
#' @describeIn checkPrimers Check primers from \code{TsIO} objects.
#' @export
setMethod("checkPrimers", "TsIO", function(object, primer_opt_tm, primer_min_tm, primer_max_tm,
thermo_params_path, primer3_core) {
# extract primers
primers <- tapseq_primers(object)
# abort if no desinged primers are found
if (length(primers) < 2) stop("At least 2 TAP-seq primers needed!", call. = FALSE)
# check primers using Primer3
check_primers(primers, primer_opt_tm = primer_opt_tm, primer_min_tm = primer_min_tm,
primer_max_tm = primer_max_tm, thermo_params_path = thermo_params_path,
primer3_core = primer3_core)
})
#' @describeIn checkPrimers Check primers from \code{TsIOList} objects.
#' @export
setMethod("checkPrimers", "TsIOList", function(object, primer_opt_tm, primer_min_tm, primer_max_tm,
thermo_params_path, primer3_core) {
# extract primers
primers <- tapseq_primers(object)
# abort if no desinged primers are found
if (length(primers) < 2) stop("At least 2 TAP-seq primers needed!", call. = FALSE)
# check primers using Primer3
check_primers(primers, primer_opt_tm = primer_opt_tm, primer_min_tm = primer_min_tm,
primer_max_tm = primer_max_tm, thermo_params_path = thermo_params_path,
primer3_core = primer3_core)
})
# HELPER FUNCTIONS =================================================================================
# check primers for a set stored in an IRanges object (output of tapseq_primers())
check_primers <- function(primers, primer_opt_tm, primer_min_tm, primer_max_tm,
thermo_params_path, primer3_core) {
# get all primer sequences
primer_seqs <- structure(mcols(primers)$sequence, names = names(primers))
# create all possible primer pairs
pairs <- utils::combn(primer_seqs, m = 2, simplify = FALSE)
# create IO record for all primer pairs and catch any errors or warnings
message("Creating input for Primer3...")
io <- primer_pairs_io(pairs, primer_opt_tm = primer_opt_tm, primer_min_tm = primer_min_tm,
primer_max_tm = primer_max_tm,
primer_thermodynamic_parameters_path = thermo_params_path)
# design primers
message("Running Primer3...")
primer3_output <- system2(command = primer3_core, input = io, stdout = TRUE)
# parse output into list
message("Processing output...")
primer3_output <- parse_primer3_output(primer3_output)
# process output for every pair
output <- lapply(primer3_output, FUN = process_output_record)
output <- do.call(rbind, output)
row.names(output) <- NULL
# reurn output
message("Done!")
return(output)
}
# create boulder-io records for a list of primer pairs
primer_pairs_io <- function(primer_pairs, ...) {
# create boulder io records for every pair and catch errors and warnings
io <- lapply(primer_pairs, FUN = function(pair) {
tryCatch({
check_primers_io(pair, ...)
}, error = function(e) {
message("Error in check_primers_io() for primer pair: ")
message(e, "")
return(NULL)
}, warning = function(w) {
message("Warning in check_primers_io() for primer pair: ")
message(w, "")
return(NULL)
})
})
# convert from list to vector
unlist(io)
}
# create a boulder-io record for a primer pair
check_primers_io <- function(primer_pair, ...) {
# id for primer pair
pair_id <- paste(names(primer_pair), collapse = "-")
# create reverse complement of first primer
primer2_revcomp <- as.character(Biostrings::reverseComplement(DNAString(primer_pair[1])))
# abort if primers are reverse complements
if (primer_pair[2] == primer2_revcomp) {
stop("Primers are reverse complements in pair: ", pair_id, call. = FALSE)
}
# create basic input list
io <- list("sequence_id" = pair_id,
"sequence_primer" = primer_pair[[1]],
"sequence_primer_revcomp"= primer_pair[[2]],
"primer_task" = "check_primers",
"primer_explain_flag" = 1)
# add any additional arguments
io <- append(io, list(...))
# remove any NA values
io <- io[!is.na(io)]
# transform to vector where each element contains one input line ("tag=value" format)
io <- paste0(toupper(names(io)), "=", io)
# add "=" record separator at the end of the record
c(io, "=")
}
# process Primer3 check_pair output for one output record (1 pair)
process_output_record <- function(record) {
# get pair id and split into individual primer ids
pair_id <- record[["sequence_id"]]
primer1 <- sub("(.+primer_left_\\d+)-(.+primer_left_\\d+)", "\\1", pair_id)
primer2 <- sub("(.+primer_left_\\d+)-(.+primer_left_\\d+)", "\\2", pair_id)
# get pair id and primer sequences
primer1_seq <- record[["sequence_primer"]]
primer2_seq <- record[["sequence_primer_revcomp"]]
# extract results
extract <- c("primer_left_0_penalty", "primer_right_0_penalty", "primer_pair_0_penalty",
"primer_pair_0_compl_any_th", "primer_pair_0_compl_end_th")
results <- structure(as.numeric(record[extract]), names = extract)
# create output data.frame
data.frame(primer1, primer2, primer1_seq, primer2_seq,
primer1_penalty = results[["primer_left_0_penalty"]],
primer2_penalty = results[["primer_right_0_penalty"]],
primer_pair_penalty = results[["primer_pair_0_penalty"]],
primer_pair_compl_any_th = results[["primer_pair_0_compl_any_th"]],
primer_pair_compl_end_th = results[["primer_pair_0_compl_end_th"]],
stringsAsFactors = FALSE)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.