## Some utility functions for Filters.
## Vector to map AnnotationFilter fields to actual database columns.
## Format: field name = database column name
.ENSDB_FIELDS <- c(
## gene
entrez = "entrezid",
gene_biotype = "gene_biotype",
gene_id = "gene_id",
genename = "gene_name",
gene_name = "gene_name",
symbol = "gene_name",
seq_name = "seq_name",
seq_strand = "seq_strand",
gene_start = "gene_seq_start",
gene_end = "gene_seq_end",
description = "description",
## tx
tx_id = "tx_id",
tx_biotype = "tx_biotype",
tx_name = "tx_id",
tx_start = "tx_seq_start",
tx_end = "tx_seq_end",
tx_support_level = "tx_support_level",
tx_external_name = "tx_external_name",
tx_is_canonical = "tx_is_canonical",
## exon
exon_id = "exon_id",
exon_rank = "exon_idx",
exon_start = "exon_seq_start",
exon_end = "exon_seq_end",
## protein
protein_id = "protein_id",
uniprot = "uniprot_id",
uniprot_db = "uniprot_db",
uniprot_mapping_type = "uniprot_mapping_type",
prot_dom_id = "protein_domain_id",
protein_domain_id = "protein_domain_id",
protein_domain_source = "protein_domain_source"
)
.supportedFilters <- function(x) {
flds <- .filterFields(x)
flts <- c(.fieldToClass(flds), "GRangesFilter")
flds <- c(flds, NA)
idx <- order(flts)
data.frame(filter = flts[idx], field = flds[idx], stringsAsFactors = FALSE)
}
.filterFields <- function(x) {
flds <- c("entrez", "gene_biotype", "gene_id", "gene_name", "genename",
"symbol", "seq_name", "seq_strand", "gene_start", "gene_end",
"tx_id", "tx_biotype", "tx_name", "tx_start", "tx_end",
"exon_id", "exon_rank", "exon_start", "exon_end")
if (.has_tx_external_name(x))
flds <- c(flds, "tx_external_name")
if (hasProteinData(x))
flds <- c(flds, "protein_id", "uniprot", "uniprot_db",
"uniprot_mapping_type", "prot_dom_id",
"protein_domain_id",
"protein_domain_source")
if (any(listColumns(x) == "tx_support_level"))
flds <- c(flds, "tx_support_level")
if (any(listColumns(x) == "tx_is_canonical"))
flds <- c(flds, "tx_is_canonical")
sort(flds)
}
.fieldToClass <- function(field) {
class <- gsub("_([[:alpha:]])", "\\U\\1", field, perl=TRUE)
class <- sub("^([[:alpha:]])", "\\U\\1", class, perl=TRUE)
paste0(class, if (length(class)) "Filter" else character(0))
}
#' Utility function to map from the default AnnotationFilters fields to the
#' database columns used in ensembldb.
#'
#' @param x The field name to be \emph{translated}.
#' @return The column name in the EnsDb database.
#' @noRd
.fieldInEnsDb <- function(x) {
if (length(x) == 0 || missing(x))
stop("Error in .fieldInEnsDb: got empty input argument!")
if (is.na(.ENSDB_FIELDS[x]))
stop("Unable to map field '", x, "'!")
else
.ENSDB_FIELDS[x]
}
#' Utility function to map the condition of an AnnotationFilter to the SQL
#' condition to be used in the EnsDb database.
#'
#' @param x An \code{AnnotationFilter}.
#'
#' @return A character representing the condition for the SQL call.
#' @noRd
.conditionForEnsDb <- function(x) {
cond <- condition(x)
if (length(unique(value(x))) > 1) {
if (cond == "==")
cond <- "in"
if (cond == "!=")
cond <- "not in"
}
if (cond == "==")
cond <- "="
if (cond %in% c("startsWith", "endsWith", "contains"))
cond <- "like"
cond
}
#' Single quote character values, paste multiple values and enclose in quotes.
#'
#' @param x An \code{AnnotationFilter} object.
#' @noRd
.valueForEnsDb <- function(x) {
vals <- unique(value(x))
if (is(x, "CharacterFilter")) {
vals <- sQuote(gsub(unique(vals), pattern = "'", replacement = "''"))
}
if (length(vals) > 1)
vals <- paste0("(", paste0(vals, collapse = ","), ")")
## Process the like/startsWith/endsWith
if (condition(x) == "startsWith")
vals <- paste0("'", unique(x@value), "%'")
if (condition(x) == "endsWith")
vals <- paste0("'%", unique(x@value), "'")
if (condition(x) == "contains")
vals <- paste0("'%", unique(x@value), "%'")
vals
}
#' That's to build the standard query from an AnnotationFilter for EnsDb.
#'
#' @param x An \code{AnnotationFilter}.
#' @noRd
.queryForEnsDb <- function(x) {
paste(.fieldInEnsDb(field(x)), .conditionForEnsDb(x), .valueForEnsDb(x))
}
#' This is a slightly more sophisticated function that does also prefix the
#' columns.
#' @noRd
.queryForEnsDbWithTables <- function(x, db, tables = character()) {
clmn <- .fieldInEnsDb(field(x))
if (!missing(db)) {
if (length(tables) == 0)
tables <- names(listTables(db))
clmn <- unlist(prefixColumns(db, clmn, with.tables = tables))
}
res <- paste(clmn, .conditionForEnsDb(x), .valueForEnsDb(x))
res
}
#' Simple helper function to convert expressions to AnnotationFilter or
#' AnnotationFilterList.
#'
#' @param x Can be an \code{AnnotationFilter}, an \code{AnnotationFilterList},
#' a \code{list} or a filter \code{expression}. This should NOT be empty!
#'
#' @return Returns an \code{AnnotationFilterList} with all filters.
#'
#' @noRd
.processFilterParam <- function(x, db) {
if (missing(db))
stop("Argument 'db' missing.")
## Check if x is a formula and eventually translate it.
if (is(x, "formula"))
res <- AnnotationFilter(x)
else res <- x
if (is(res, "AnnotationFilter"))
res <- AnnotationFilterList(res)
if (!is(res, "AnnotationFilterList")) {
## Did not get a filter expression, thus checking what we've got.
if (is(res, "list")) {
if (length(res)) {
## Check that all elements are AnnotationFilter objects!
if (!all(unlist(lapply(res, function(z) {
inherits(z, "AnnotationFilter")
}), use.names = FALSE)))
stop("One of more elements in 'filter' are not ",
"'AnnotationFilter' objects!", call. = FALSE)
res <- as(res, "AnnotationFilterList")
res@logOp <- rep("&", (length(res) - 1))
} else {
res <- AnnotationFilterList()
}
} else {
stop("'filter' has to be an 'AnnotationFilter', a list of ",
"'AnnotationFilter' object, an 'AnnotationFilterList' ",
"or a valid filter expression!", call. = FALSE)
}
}
supp_filters <- supportedFilters(db)$filter
have_filters <- unique(.AnnotationFilterClassNames(res))
if (!all(have_filters %in% supp_filters))
stop("AnnotationFilter classes: ",
paste(have_filters[!(have_filters %in% supp_filters)]),
" are not supported by this EnsDb database.", call. = FALSE)
res
}
############################################################
## setFeatureInGRangesFilter
##
## Simple helper function to set the @feature in GRangesFilter
## depending on the calling method.
setFeatureInGRangesFilter <- function(x, feature){
for (i in seq(along.with = x)){
if (is(x[[i]], "GRangesFilter"))
x[[i]]@feature <- feature
if (is(x[[i]], "AnnotationFilterList"))
x[[i]] <- setFeatureInGRangesFilter(x[[i]], feature = feature)
}
x
}
############################################################
## isProteinFilter
##' evaluates whether the filter is a protein annotation related filter.
##' @param x The object that should be evaluated. Can be an AnnotationFilter or
##' an AnnotationFilterList.
##' @return Returns TRUE if 'x' is a filter for protein annotation tables and
##' FALSE otherwise.
##' @noRd
isProteinFilter <- function(x) {
if (is(x, "AnnotationFilterList"))
return(unlist(lapply(x, isProteinFilter)))
else
return(is(x, "ProteinIdFilter") | is(x, "UniprotFilter") |
is(x, "ProtDomIdFilter") | is(x, "UniprotDbFilter") |
is(x, "UniprotMappingTypeFilter"))
}
#' build the \emph{where} query for a \code{GRangedFilter}. Supported conditions
#' are: \code{"start"}, \code{"end"}, \code{"equal"}, \code{"within"},
#' \code{"any"}.
#'
#' @param grf \code{GRangesFilter}.
#'
#' @param columns named character vectors with the column names for start, end,
#' strand and seq_name.
#'
#' @param db An optional \code{EnsDb} instance. Used to \emph{translate}
#' seqnames depending on the specified seqlevels style.
#'
#' @return A character with the corresponding \emph{where} query.
#' @noRd
buildWhereForGRanges <- function(grf, columns, db = NULL){
condition <- condition(grf)
if (!(condition %in% c("start", "end", "within", "equal", "any")))
stop("'condition' ", condition, " not supported. Condition (type) can ",
"be one of 'any', 'start', 'end', 'equal', 'within'.")
if( is.null(names(columns)))
stop("The vector with the required column names for the",
" GRangesFilter query has to have names!")
if (!all(c("start", "end", "seqname", "strand") %in% names(columns)))
stop("'columns' has to be a named vector with names being ",
"'start', 'end', 'seqname', 'strand'!")
## Build the query to fetch all features that are located within the range
quers <- sapply(as(value(grf), "GRangesList"), function(z) {
if (!is.null(db)) {
seqn <- formatSeqnamesForQuery(db, as.character(seqnames(z)))
} else {
seqn <- as.character(seqnames(z))
}
## start: start, seqname and strand have to match.
if (condition == "start") {
query <- paste0(columns["start"], "=", start(z), " and ",
columns["seqname"], "='", seqn, "'")
}
## end: end, seqname and strand have to match.
if (condition == "end") {
query <- paste0(columns["end"], "=", end(z), " and ",
columns["seqname"], "='", seqn, "'")
}
## equal: start, end, seqname and strand have to match.
if (condition == "equal") {
query <- paste0(columns["start"], "=", start(z), " and ",
columns["end"], "=", end(z), " and ",
columns["seqname"], "='", seqn, "'")
}
## within: start has to be >= start, end <= end, seqname and strand
## have to match.
if (condition == "within") {
query <- paste0(columns["start"], ">=", start(z), " and ",
columns["end"], "<=", end(z), " and ",
columns["seqname"], "='", seqn, "'")
}
## any: essentially the overlapping.
if (condition == "any") {
query <- paste0(columns["start"], "<=", end(z), " and ",
columns["end"], ">=", start(z), " and ",
columns["seqname"], "='", seqn, "'")
}
## Include the strand, if it's not "*"
if(as.character(strand(z)) != "*"){
query <- paste0(query, " and ", columns["strand"], " = ",
strand2num(as.character(strand(z))))
}
return(query)
})
if(length(quers) > 1)
quers <- paste0("(", quers, ")")
## Collapse now the queries.
query <- paste0(quers, collapse=" or ")
paste0("(", query, ")")
}
#' @description Helper to extract all AnnotationFilter class names from an
#' AnnotationFilterList (recursively!)
#'
#' @param x The \code{AnnotationFilterList}.
#'
#' @return A \code{character} with the names of the classes.
#' @noRd
.AnnotationFilterClassNames <- function(x) {
classes <- lapply(x, function(z) {
if (is(z, "AnnotationFilterList"))
return(.AnnotationFilterClassNames(z))
class(z)
})
unlist(classes, use.names = FALSE)
}
#' @description Test if any of the filter(s) is an SymbolFilter.
#'
#' @noRd
.anyIs <- function(x, what = "SymbolFilter") {
if (is(x, "AnnotationFilter")) {
is(x, what)
} else {
unlist(lapply(x, .anyIs, what = what))
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.