From 2d9b1ba7997c2b758835dcfd2332520d7a8b9ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cigunduz=E2=80=9D?= Date: Tue, 29 Sep 2026 15:50:58 +0200 Subject: [PATCH 1/2] update for revision --- DESCRIPTION | 19 +- NAMESPACE | 4 +- NEWS.md | 37 +++- R/aaa-utils.R | 52 ++++-- R/getGCfreq.R | 60 ++++--- R/getGenomeGC.R | 66 ++++--- R/getTFbindsites.R | 53 +++--- R/methylTFRAnnotationMm10-package.R | 38 ++++ R/resources.R | 122 +++++++++++++ man/availableMotifSets.Rd | 23 +++ man/genomewide_GC.Rd | 47 +++++ man/getGCfreq.Rd | 62 ++++--- man/getGenomeGC.Rd | 57 +++--- man/getTFbindsites.Rd | 54 +++--- man/methylTFRAnnotationMm10-package.Rd | 53 ++++++ man/motif_gcfreq.Rd | 69 ++++++++ man/tf_bindsites.Rd | 47 +++++ tests/testthat.R | 1 + tests/testthat/test-accessors.R | 39 ++++- tests/testthat/test-annotationhub.R | 45 +++++ tests/testthat/test-local-resources.R | 96 ++++++++++ vignettes/methylTFRAnnotationMm10.Rmd | 234 +++++++++++++++++++------ 22 files changed, 1048 insertions(+), 230 deletions(-) create mode 100644 R/methylTFRAnnotationMm10-package.R create mode 100644 R/resources.R create mode 100644 man/availableMotifSets.Rd create mode 100644 man/genomewide_GC.Rd create mode 100644 man/methylTFRAnnotationMm10-package.Rd create mode 100644 man/motif_gcfreq.Rd create mode 100644 man/tf_bindsites.Rd create mode 100644 tests/testthat/test-annotationhub.R create mode 100644 tests/testthat/test-local-resources.R diff --git a/DESCRIPTION b/DESCRIPTION index a6e3dc8..089a9db 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,20 +9,25 @@ Description: Precomputed transcription factor binding sites, motif GC for sequence composition. The data are hosted on AnnotationHub and retrieved on first use; this package provides the accessors and the documentation. -Authors@R: person( - given = "Irem B.", family = "Gunduz", - email = "irembgunduz@gmail.com", role = c("aut", "cre"), - comment = c(ORCID = "0000-0003-2641-0916")) +Authors@R: c( + person("Irem B.", "Gunduz", , "irembgunduz@gmail.com", role = c("aut", "cre"), + comment = c(ORCID = "0000-0003-2641-0916")), + person("ERA-NET Transcan-Neu III - EPILUNAR", role = "fnd", + comment = "Grant 01KT2409"), + person("Saarland University", role = "fnd", + comment = "NanoBioMed Young Investigator Grant") + ) License: Artistic-2.0 Encoding: UTF-8 -Version: 0.99.9 +Version: 0.99.10 Depends: R (>= 4.3.0) -Imports: AnnotationHub, GenomicRanges, utils +Imports: AnnotationHub, GenomicRanges, stats Suggests: knitr, rmarkdown, testthat (>= 3.0.0), BiocStyle VignetteBuilder: knitr biocViews: AnnotationData, AnnotationHub, FunctionalAnnotation, - SequenceAnnotation, Homo_sapiens + SequenceAnnotation, Mus_musculus BugReports: https://github.com/EpigenomeInformatics/methylTFRAnnotationMm10/issues URL: https://github.com/EpigenomeInformatics/methylTFRAnnotationMm10 +Roxygen: list(markdown = FALSE) RoxygenNote: 7.3.3 Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 6397fef..4fd19a0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,7 +1,7 @@ # Generated by roxygen2: do not edit by hand +export(availableMotifSets) export(getGCfreq) export(getGenomeGC) export(getTFbindsites) -import(GenomicRanges) -importFrom(utils,packageVersion) +importFrom(GenomicRanges,GRanges) diff --git a/NEWS.md b/NEWS.md index 212812f..8329e6b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,10 +1,41 @@ +# methylTFRAnnotationMm10 0.99.10 + +Changes in response to the Bioconductor review: + +* `Authors@R` lists the funders (ERA-NET Transcan-Neu III - EPILUNAR, + grant 01KT2409; Saarland University NanoBioMed Young Investigator + Grant) with the `fnd` role. + +* New package help page `?methylTFRAnnotationMm10` summarising the resources and + linking the accessors. + +* New help pages describing the structure of each AnnotationHub resource + listed in `inst/extdata/metadata.csv`: `?motif_gcfreq` + (`?altius_motif_gcfreq`, ...), `?tf_bindsites` + (`?altius_tf_bindsites`, ...) and `?genomewide_GC` + (`?genomewide_GC_mm10`). + +* New exported `availableMotifSets()` listing the accepted motif sets. + +* `getGenomeGC()` accepts `"mm10"` (or no argument) and gives an + informative error for any other assembly. + +* biocViews: `Mus_musculus` instead of `Homo_sapiens`. + +* Unit tests now cover the local-directory and AnnotationHub code paths + (the hub is mocked, so the tests run offline). + +* The vignette no longer hides code: the synthetic example data are + built in a visible chunk, every chunk is evaluated, and each accessor's + output is printed and explained. + # methylTFRAnnotationMm10 0.99.9 * Initial submission to Bioconductor. * Provides transcription factor binding sites, motif GC frequency - tables and a genome-wide GC distribution for hg38, for the - jaspar2020, cisbpv2 and altius motif sets GC frequency tables. + tables and a genome-wide GC distribution for mm10, for the + jaspar2020, cisbpv2 and altius motif sets. * Resources are hosted on AnnotationHub and downloaded on first use. @@ -15,4 +46,4 @@ * Fixes on NOTES from biocchecks -* Added 4 spaces as suggested \ No newline at end of file +* Added 4 spaces as suggested diff --git a/R/aaa-utils.R b/R/aaa-utils.R index 20e83c0..b41698d 100644 --- a/R/aaa-utils.R +++ b/R/aaa-utils.R @@ -1,14 +1,33 @@ .PKG_NAME <- "methylTFRAnnotationMm10" .MOTIF_SETS <- c("altius", "cisbpv2", "jaspar2020") -.ASSEMBLY <- "Mm10" +.ASSEMBLY <- "mm10" #' @keywords internal +#' @noRd .local_dir <- function() { d <- getOption( "methylTFRAnnotationMm10.datadir", Sys.getenv("METHYL_TFRANNOTATION_Mm10_DIR", "") ) - if (nzchar(d)) d else NULL + if (is.character(d) && length(d) == 1L && nzchar(d)) d else NULL +} + +#' @keywords internal +#' @noRd +#' @description AnnotationHub records belonging to this package, as a +#' character vector of titles named by AnnotationHub ID. Kept separate +#' from .resolve_resource() so the lookup logic can be unit-tested +#' without network access. +.hub_titles <- function() { + hub <- AnnotationHub::AnnotationHub() + hits <- AnnotationHub::query(hub, .PKG_NAME) + stats::setNames(hits$title, names(hits)) +} + +#' @keywords internal +#' @noRd +.hub_get <- function(id) { + AnnotationHub::AnnotationHub()[[id]] } #' @keywords internal @@ -32,27 +51,40 @@ } return(readRDS(path)) } - hub <- AnnotationHub::AnnotationHub() - hits <- AnnotationHub::query(hub, .PKG_NAME) - idx <- match(file, hits$title) + titles <- .hub_titles() + idx <- match(file, titles) if (is.na(idx)) { stop( "Resource not found on AnnotationHub: ", file, - "\nAvailable: ", paste(hits$title, collapse = ", ") + "\nAvailable: ", paste(titles, collapse = ", ") ) } - hits[[names(hits)[idx]]] + .hub_get(names(titles)[idx]) } #' @keywords internal #' @noRd .check_motif_set <- function(motifSet) { - motifSet <- tolower(motifSet) - if (length(motifSet) != 1 || !motifSet %in% .MOTIF_SETS) { + if (!is.character(motifSet) || length(motifSet) != 1L || + is.na(motifSet) || !tolower(motifSet) %in% .MOTIF_SETS) { stop( "Invalid motif set. Available: ", paste(.MOTIF_SETS, collapse = ", ") ) } - motifSet + tolower(motifSet) +} + +#' @title List the motif sets provided by this package +#' @description Returns the names accepted by the \code{motifSet} +#' argument of \code{\link{getTFbindsites}} and +#' \code{\link{getGCfreq}}. +#' @return A character vector of motif set names. +#' @seealso \code{\link{methylTFRAnnotationMm10}} for an overview of the +#' package. +#' @examples +#' availableMotifSets() +#' @export +availableMotifSets <- function() { + .MOTIF_SETS } diff --git a/R/getGCfreq.R b/R/getGCfreq.R index ca61531..9dc6cf8 100644 --- a/R/getGCfreq.R +++ b/R/getGCfreq.R @@ -1,32 +1,40 @@ -#' @title getGCfreq -#' @description Load the motif GC frequency table for a motif set. -#' Downloaded from AnnotationHub on first use and cached locally thereafter. -#' @param motifSet Motif set to load. One of: altius, cisbpv2, jaspar2020, -#' jaspar2020_distal. -#' @return A named \code{list} of five-row matrices, one per motif. -#' @import GenomicRanges -#' @importFrom utils packageVersion +#' @title Load motif GC frequency tables +#' @description Load the motif GC frequency tables for a motif set. +#' Downloaded from AnnotationHub on first use and cached locally +#' thereafter. +#' @details Each table describes the GC composition around one motif's +#' binding sites. Column \eqn{j} corresponds to the \eqn{j}-th 30 nt +#' window along the (extended) binding site; row \eqn{i} gives the +#' fraction of binding sites whose window at that position falls into +#' genome-wide GC quintile \eqn{i} (row 1 lowest, row 5 highest GC). +#' Columns therefore sum to one. \pkg{methylTFR} combines these +#' fractions with the observed methylation per GC bin to obtain the +#' methylation a motif would be expected to show from sequence +#' composition alone. See \code{\link{motif_gcfreq}} for the data +#' format. +#' @param motifSet Motif set to load. One of +#' \code{availableMotifSets()}: \code{altius}, \code{cisbpv2}, +#' \code{jaspar2020}. Case-insensitive. +#' @return A named \code{list} of numeric matrices with five rows (GC +#' quintiles), one matrix per motif. +#' @seealso \code{\link{motif_gcfreq}}, \code{\link{getTFbindsites}}, +#' \code{\link{getGenomeGC}} #' @examples -#' # 1. Create a dummy object and save it to a temporary directory -#' # to bypass AnnotationHub during package checks. -#' mock_dir <- tempdir() -#' mock_file <- file.path(mock_dir, "altius_motif_gcfreq.rds") +#' # Point the package at a temporary directory holding a small mock +#' # table so the example runs without downloading from AnnotationHub. +#' mock_dir <- file.path(tempdir(), "gcfreq_example") +#' dir.create(mock_dir, showWarnings = FALSE) +#' freq <- matrix(c(0.1, 0.2, 0.4, 0.2, 0.1), nrow = 5, ncol = 3) +#' saveRDS(list(MA0001.1 = freq), file.path(mock_dir, +#' "altius_motif_gcfreq.rds")) +#' old_opt <- options(methylTFRAnnotationMm10.datadir = mock_dir) #' -#' # (Mocking a 5-row matrix as expected by the function output) -#' mock_data <- list(mock_motif_1 = matrix(0.25, nrow = 5, ncol = 4)) -#' saveRDS(mock_data, mock_file) -#' -#' # 2. Temporarily point the package's local directory to tempdir() -#' old_opt <- getOption("methylTFRAnnotationMm10.datadir") -#' options(methylTFRAnnotationMm10.datadir = mock_dir) -#' -#' # 3. Run the function (it will now read the mock file) -#' x <- getGCfreq(motifSet = "altius") -#' -#' # 4. Restore options and clean up the temporary file -#' options(methylTFRAnnotationMm10.datadir = old_opt) -#' file.remove(mock_file) +#' gcfreqs <- getGCfreq("altius") +#' gcfreqs$MA0001.1 +#' colSums(gcfreqs$MA0001.1) #' +#' options(old_opt) +#' unlink(mock_dir, recursive = TRUE) #' @export getGCfreq <- function(motifSet = "altius") { motifSet <- .check_motif_set(motifSet) diff --git a/R/getGenomeGC.R b/R/getGenomeGC.R index 498e9ee..69f4d6e 100644 --- a/R/getGenomeGC.R +++ b/R/getGenomeGC.R @@ -1,36 +1,44 @@ -#' @title getGenomeGC -#' @description Load the genome-wide GC distribution. +#' @title Load the genome-wide GC distribution +#' @description Load the genome-wide GC distribution for mm10. #' Downloaded from AnnotationHub on first use and cached locally #' thereafter. -#' @param assembly Genome assembly. Defaults to the assembly this -#' package was built for. -#' @return A \code{GRanges} with GC_bias and GC_bin metadata columns. -#' @import GenomicRanges -#' @importFrom utils packageVersion -#' @examples -#' # 1. Create a dummy object and save it to a temporary directory -#' mock_dir <- tempdir() -#' mock_file <- file.path(mock_dir, "genomewide_GC_mm10.rds") -#' -#' # Mocking a GRanges object with the expected metadata columns -#' mock_gr <- GenomicRanges::GRanges("chr1:1-100") -#' mock_gr$GC_bias <- 0.5 -#' mock_gr$GC_bin <- 1L -#' saveRDS(mock_gr, mock_file) +#' @details The genome is tiled into non-overlapping 30 nt windows. Each +#' window carries its GC fraction and its genome-wide GC quintile. +#' \pkg{methylTFR} uses the table to assign every methylation call to +#' a GC bin. See \code{\link{genomewide_GC}} for the data format. #' -#' # 2. Temporarily point the package to tempdir -#' old_opt <- getOption("methylTFRAnnotationMm10.datadir") -#' options(methylTFRAnnotationMm10.datadir = mock_dir) -#' -#' # 3. Run the function -#' gc <- getGenomeGC() +#' The package serves a single assembly, so the argument can be left +#' out. It is kept so that code written as +#' \code{getGenomeGC("mm10")} works; any other assembly is an error. +#' @param assembly Genome assembly. Only \code{"mm10"} (the default) +#' is available in this package; case-insensitive. +#' @return A \code{GRanges} with metadata columns \code{GC_bias} (GC +#' fraction of the window) and \code{GC_bin} (quintile, 1 to 5). +#' @seealso \code{\link{genomewide_GC}}, \code{\link{getGCfreq}} +#' @examples +#' # Point the package at a temporary directory holding a small mock +#' # object so the example runs without downloading from AnnotationHub. +#' mock_dir <- file.path(tempdir(), "genomegc_example") +#' dir.create(mock_dir, showWarnings = FALSE) +#' gr <- GenomicRanges::GRanges(c("chr1:1-30", "chr1:31-60", "chr1:61-90")) +#' gr$GC_bias <- c(0.30, 0.43, 0.63) +#' gr$GC_bin <- c(1L, 3L, 5L) +#' saveRDS(gr, file.path(mock_dir, "genomewide_GC_mm10.rds")) +#' old_opt <- options(methylTFRAnnotationMm10.datadir = mock_dir) #' -#' # 4. Restore options and clean up -#' options(methylTFRAnnotationMm10.datadir = old_opt) -#' file.remove(mock_file) +#' getGenomeGC() #' +#' options(old_opt) +#' unlink(mock_dir, recursive = TRUE) #' @export -getGenomeGC <- function(assembly = .ASSEMBLY) { - assembly <- tolower(assembly) - .resolve_resource(paste0("genomewide_GC_", assembly, ".rds")) +getGenomeGC <- function(assembly = "mm10") { + if (!is.character(assembly) || length(assembly) != 1L || + is.na(assembly) || tolower(assembly) != .ASSEMBLY) { + stop( + .PKG_NAME, " provides the GC distribution for ", .ASSEMBLY, + " only. Call getGenomeGC() without arguments, or load the ", + "methylTFR annotation package for the assembly you need." + ) + } + .resolve_resource(paste0("genomewide_GC_", .ASSEMBLY, ".rds")) } diff --git a/R/getTFbindsites.R b/R/getTFbindsites.R index 464648c..8aea403 100644 --- a/R/getTFbindsites.R +++ b/R/getTFbindsites.R @@ -1,33 +1,36 @@ -#' @title getTFbindsites -#' @description Retrieve transcription factor binding sites for a motif set. -#' Downloaded from AnnotationHub on first use and cached locally thereafter. -#' @param motifSet Motif set to load. One of: altius, cisbpv2, jaspar2020, -#' jaspar2020_distal. -#' @return A \code{GRangesList}, one element per motif. -#' @import GenomicRanges -#' @importFrom utils packageVersion -#' @examples -#' # 1. Create a dummy object and save it to a temp directory -#' mock_dir <- tempdir() -#' mock_file <- file.path(mock_dir, "altius_tf_bindsites.rds") +#' @title Load transcription factor binding sites +#' @description Load genome-wide transcription factor binding sites for a +#' motif set. Downloaded from AnnotationHub on first use and cached +#' locally thereafter. +#' @details Each element of the returned list holds the binding sites of +#' one motif. Every range is the motif match extended by 200 bp on either +#' side, the footprint window across which \pkg{methylTFR} reads +#' methylation. See \code{\link{tf_bindsites}} for the data format. #' -#' # Mocking a GRangesList as expected by the function output +#' @param motifSet Motif set to load. One of +#' \code{availableMotifSets()}: \code{altius}, \code{cisbpv2}, +#' \code{jaspar2020}. Case-insensitive. +#' @return A \code{GRangesList} with one \code{GRanges} per motif, +#' named by motif ID. +#' @seealso \code{\link{tf_bindsites}}, \code{\link{getGCfreq}} +#' @examples +#' # Point the package at a temporary directory holding a small mock +#' # object so the example runs without downloading from AnnotationHub. +#' mock_dir <- file.path(tempdir(), "tfbs_example") +#' dir.create(mock_dir, showWarnings = FALSE) #' mock_grl <- GenomicRanges::GRangesList( -#' mock_motif_1 = GenomicRanges::GRanges("chr1:100-200") +#' MA0001.1 = GenomicRanges::GRanges( +#' c("chr1:1001-1411", "chr2:5001-5411"), +#' strand = c("+", "-") +#' ) #' ) -#' saveRDS(mock_grl, mock_file) -#' -#' # 2. Point the local directory option to the temp directory -#' old_opt <- getOption("methylTFRAnnotationMm10.datadir") -#' options(methylTFRAnnotationMm10.datadir = mock_dir) -#' -#' # 3. Run the function -#' x <- getTFbindsites() +#' saveRDS(mock_grl, file.path(mock_dir, "altius_tf_bindsites.rds")) +#' old_opt <- options(methylTFRAnnotationMm10.datadir = mock_dir) #' -#' # 4. Restore options and clean up -#' options(methylTFRAnnotationMm10.datadir = old_opt) -#' file.remove(mock_file) +#' getTFbindsites("altius") #' +#' options(old_opt) +#' unlink(mock_dir, recursive = TRUE) #' @export getTFbindsites <- function(motifSet = "altius") { motifSet <- .check_motif_set(motifSet) diff --git a/R/methylTFRAnnotationMm10-package.R b/R/methylTFRAnnotationMm10-package.R new file mode 100644 index 0000000..9aaabc3 --- /dev/null +++ b/R/methylTFRAnnotationMm10-package.R @@ -0,0 +1,38 @@ +#' methylTFRAnnotationMm10: methylTFR annotations for mm10 +#' +#' @description +#' Genome annotations that \pkg{methylTFR} needs to compute +#' GC-bias-corrected transcription factor (TF) deviation scores from DNA +#' methylation data on the Mus musculus mm10 assembly. +#' +#' @details +#' The package provides three kinds of resources, each with an accessor: +#' \describe{ +#' \item{\code{\link{getTFbindsites}}}{Genome-wide TF binding sites, +#' one \code{GRanges} per motif, extended to the footprint window +#' (see \code{\link{tf_bindsites}}).} +#' \item{\code{\link{getGCfreq}}}{Motif GC frequency tables: how +#' each motif's binding sites distribute over genome-wide GC quintiles +#' (see \code{\link{motif_gcfreq}}).} +#' \item{\code{\link{getGenomeGC}}}{The genome-wide GC distribution +#' in 30 nt windows (see \code{\link{genomewide_GC}}).} +#' } +#' \code{\link{availableMotifSets}} lists the motif sets that can be +#' requested. The three objects are passed to +#' \code{methylTFR::run_methyltfr()}. +#' +#' The data are too large to ship with the package. They are hosted on +#' AnnotationHub, downloaded on first use and served from the local +#' AnnotationHub cache afterwards. The metadata of every resource is +#' listed in \code{system.file("extdata", "metadata.csv", package = +#' "methylTFRAnnotationMm10")}, and the scripts that produced them are in +#' \code{system.file("scripts", package = "methylTFRAnnotationMm10")}. +#' +#' To read the \code{.rds} files from a local directory instead of +#' AnnotationHub (for example on a machine without internet access), set +#' \code{options(methylTFRAnnotationMm10.datadir = "/path/to/files")} or the +#' environment variable \code{METHYL_TFRANNOTATION_Mm10_DIR}. +#' +#' @seealso \code{vignette("methylTFRAnnotationMm10")} +#' @importFrom GenomicRanges GRanges +"_PACKAGE" diff --git a/R/resources.R b/R/resources.R new file mode 100644 index 0000000..aab262e --- /dev/null +++ b/R/resources.R @@ -0,0 +1,122 @@ +#' @title Motif GC frequency tables +#' @name motif_gcfreq +#' @aliases altius_motif_gcfreq cisbpv2_motif_gcfreq jaspar2020_motif_gcfreq +#' @description Per-motif tables describing the GC composition around +#' each motif's binding sites on mm10. Returned by +#' \code{\link{getGCfreq}}; the AnnotationHub record titles are +#' \code{_motif_gcfreq.rds}. +#' @format A named \code{list} with one numeric matrix per motif (names +#' are motif IDs, matching the names of the corresponding +#' \code{\link{tf_bindsites}} list). Each matrix has +#' \describe{ +#' \item{5 rows}{Genome-wide GC quintiles, from lowest (row 1) to +#' highest (row 5) GC. Quintile boundaries are the ones stored with +#' \code{\link{genomewide_GC}}, so both objects share one scale.} +#' \item{one column per window position}{A 30 nt window slides one +#' base at a time along the binding site (motif plus 200 bp on either +#' side, padded by a further 65 bp so edge windows are complete). +#' Column \eqn{j} is the \eqn{j}-th window.} +#' } +#' Entries are the fraction of the motif's binding sites whose window at +#' that position falls into each quintile; every column sums to one. +#' Windows containing \code{N} are ignored. +#' @details The tables are what make the methylTFR deviation score +#' bias-corrected. A motif whose sites sit in GC-rich sequence overlaps +#' hypomethylated CpG islands more often; combining these fractions with +#' the observed methylation per GC bin gives the methylation expected from +#' sequence composition alone, which is then subtracted. +#' +#' Available motif sets: +#' \describe{ +#' \item{\code{jaspar2020}}{JASPAR2020 CORE matrices, scanned +#' genome-wide.} +#' \item{\code{cisbpv2}}{CIS-BP v2 PWMs (\code{pwms_v2} from +#' \pkg{chromVARmotifs}), scanned genome-wide.} +#' \item{\code{altius}}{Vierstra motif archetypes v1.0.} +#' } +#' @source Built with methylTFRAnnotationBuilder +#' (\url{https://github.com/EpigenomeInformatics/methylTFRAnnotationBuilder}) +#' from BSgenome sequences; see +#' \code{system.file("scripts", "make-data.R", package = +#' "methylTFRAnnotationMm10")} +#' and \code{system.file("extdata", "metadata.csv", package = +#' "methylTFRAnnotationMm10")} for the exact sources and versions. +#' @seealso \code{\link{getGCfreq}}, \code{\link{methylTFRAnnotationMm10}} +#' @examples +#' ## On first use this downloads the resource from AnnotationHub: +#' \dontrun{ +#' gcfreqs <- getGCfreq("jaspar2020") +#' length(gcfreqs) # number of motifs +#' dim(gcfreqs[[1]]) # 5 x number of window positions +#' colSums(gcfreqs[[1]]) # all 1 +#' } +#' @keywords datasets +NULL + +#' @title Transcription factor binding sites +#' @name tf_bindsites +#' @aliases altius_tf_bindsites cisbpv2_tf_bindsites jaspar2020_tf_bindsites +#' @description Genome-wide predicted transcription factor binding sites +#' on mm10. Returned by \code{\link{getTFbindsites}}; the +#' AnnotationHub record titles are \code{_tf_bindsites.rds}. +#' @format A \code{GRangesList} with one \code{GRanges} per motif, +#' named by motif ID. Each range is a motif match on the primary +#' chromosomes (chr1-19, X, Y; chrM excluded) extended by 200 bp +#' on either side of the match, so all ranges of a motif have the same +#' width (motif width + 400 bp). The strand of the match is kept; there +#' are no metadata columns. Coordinates are 1-based. +#' @details Available motif sets: +#' \describe{ +#' \item{\code{jaspar2020}}{JASPAR2020 CORE matrices, scanned +#' genome-wide.} +#' \item{\code{cisbpv2}}{CIS-BP v2 PWMs (\code{pwms_v2} from +#' \pkg{chromVARmotifs}), scanned genome-wide.} +#' \item{\code{altius}}{Vierstra motif archetypes v1.0.} +#' } +#' @source See \code{\link{motif_gcfreq}}. +#' @seealso \code{\link{getTFbindsites}}, \code{\link{methylTFRAnnotationMm10}} +#' @examples +#' ## On first use this downloads the resource from AnnotationHub: +#' \dontrun{ +#' tfbs <- getTFbindsites("jaspar2020") +#' length(tfbs) # number of motifs +#' head(lengths(tfbs)) # binding sites per motif +#' tfbs[[1]] +#' } +#' @keywords datasets +NULL + +#' @title Genome-wide GC distribution +#' @name genomewide_GC +#' @aliases genomewide_GC_mm10 +#' @description GC content of the mm10 genome in non-overlapping 30 nt +#' windows. Returned by \code{\link{getGenomeGC}}; the AnnotationHub +#' record title is \code{genomewide_GC_mm10.rds}. +#' @format A \code{GRanges}, one range per 30 nt window on the primary +#' chromosomes, with metadata columns +#' \describe{ +#' \item{\code{GC_bias}}{Numeric, GC fraction of the window (0 to 1); +#' \code{NaN} for windows made of \code{N}.} +#' \item{\code{GC_bin}}{Integer, genome-wide GC quintile of the +#' window, 1 (lowest) to 5 (highest).} +#' } +#' and \code{S4Vectors::metadata()} entries \code{gc_breaks} (the six +#' quintile boundaries), \code{bin_scope} (\code{"genome"}: quantiles +#' taken genome-wide) and \code{step} (window step, 30). +#' @details \pkg{methylTFR} overlaps each methylation call with these +#' windows to assign it a GC bin; the motif tables in +#' \code{\link{motif_gcfreq}} use the same boundaries. +#' @source Computed from the UCSC mm10 BSgenome package; see +#' \code{system.file("scripts", "make-data.R", package = +#' "methylTFRAnnotationMm10")}. +#' @seealso \code{\link{getGenomeGC}}, \code{\link{methylTFRAnnotationMm10}} +#' @examples +#' ## On first use this downloads the resource from AnnotationHub: +#' \dontrun{ +#' gc <- getGenomeGC() +#' gc +#' table(gc$GC_bin) +#' S4Vectors::metadata(gc)$gc_breaks +#' } +#' @keywords datasets +NULL diff --git a/man/availableMotifSets.Rd b/man/availableMotifSets.Rd new file mode 100644 index 0000000..3622c92 --- /dev/null +++ b/man/availableMotifSets.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/aaa-utils.R +\name{availableMotifSets} +\alias{availableMotifSets} +\title{List the motif sets provided by this package} +\usage{ +availableMotifSets() +} +\value{ +A character vector of motif set names. +} +\description{ +Returns the names accepted by the \code{motifSet} +argument of \code{\link{getTFbindsites}} and +\code{\link{getGCfreq}}. +} +\examples{ +availableMotifSets() +} +\seealso{ +\code{\link{methylTFRAnnotationMm10}} for an overview of the +package. +} diff --git a/man/genomewide_GC.Rd b/man/genomewide_GC.Rd new file mode 100644 index 0000000..d6e1cef --- /dev/null +++ b/man/genomewide_GC.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/resources.R +\name{genomewide_GC} +\alias{genomewide_GC} +\alias{genomewide_GC_mm10} +\title{Genome-wide GC distribution} +\format{ +A \code{GRanges}, one range per 30 nt window on the primary +chromosomes, with metadata columns +\describe{ + \item{\code{GC_bias}}{Numeric, GC fraction of the window (0 to 1); + \code{NaN} for windows made of \code{N}.} + \item{\code{GC_bin}}{Integer, genome-wide GC quintile of the + window, 1 (lowest) to 5 (highest).} +} +and \code{S4Vectors::metadata()} entries \code{gc_breaks} (the six +quintile boundaries), \code{bin_scope} (\code{"genome"}: quantiles +taken genome-wide) and \code{step} (window step, 30). +} +\source{ +Computed from the UCSC mm10 BSgenome package; see +\code{system.file("scripts", "make-data.R", package = +"methylTFRAnnotationMm10")}. +} +\description{ +GC content of the mm10 genome in non-overlapping 30 nt +windows. Returned by \code{\link{getGenomeGC}}; the AnnotationHub +record title is \code{genomewide_GC_mm10.rds}. +} +\details{ +\pkg{methylTFR} overlaps each methylation call with these +windows to assign it a GC bin; the motif tables in +\code{\link{motif_gcfreq}} use the same boundaries. +} +\examples{ +## On first use this downloads the resource from AnnotationHub: +\dontrun{ +gc <- getGenomeGC() +gc +table(gc$GC_bin) +S4Vectors::metadata(gc)$gc_breaks +} +} +\seealso{ +\code{\link{getGenomeGC}}, \code{\link{methylTFRAnnotationMm10}} +} +\keyword{datasets} diff --git a/man/getGCfreq.Rd b/man/getGCfreq.Rd index d91b5ae..2e9cf24 100644 --- a/man/getGCfreq.Rd +++ b/man/getGCfreq.Rd @@ -2,40 +2,54 @@ % Please edit documentation in R/getGCfreq.R \name{getGCfreq} \alias{getGCfreq} -\title{getGCfreq} +\title{Load motif GC frequency tables} \usage{ getGCfreq(motifSet = "altius") } \arguments{ -\item{motifSet}{Motif set to load. One of: altius, cisbpv2, jaspar2020, -jaspar2020_distal.} +\item{motifSet}{Motif set to load. One of +\code{availableMotifSets()}: \code{altius}, \code{cisbpv2}, +\code{jaspar2020}. Case-insensitive.} } \value{ -A named \code{list} of five-row matrices, one per motif. +A named \code{list} of numeric matrices with five rows (GC +quintiles), one matrix per motif. } \description{ -Load the motif GC frequency table for a motif set. -Downloaded from AnnotationHub on first use and cached locally thereafter. +Load the motif GC frequency tables for a motif set. +Downloaded from AnnotationHub on first use and cached locally +thereafter. +} +\details{ +Each table describes the GC composition around one motif's +binding sites. Column \eqn{j} corresponds to the \eqn{j}-th 30 nt +window along the (extended) binding site; row \eqn{i} gives the +fraction of binding sites whose window at that position falls into +genome-wide GC quintile \eqn{i} (row 1 lowest, row 5 highest GC). +Columns therefore sum to one. \pkg{methylTFR} combines these +fractions with the observed methylation per GC bin to obtain the +methylation a motif would be expected to show from sequence +composition alone. See \code{\link{motif_gcfreq}} for the data +format. } \examples{ -# 1. Create a dummy object and save it to a temporary directory -# to bypass AnnotationHub during package checks. -mock_dir <- tempdir() -mock_file <- file.path(mock_dir, "altius_motif_gcfreq.rds") - -# (Mocking a 5-row matrix as expected by the function output) -mock_data <- list(mock_motif_1 = matrix(0.25, nrow = 5, ncol = 4)) -saveRDS(mock_data, mock_file) - -# 2. Temporarily point the package's local directory to tempdir() -old_opt <- getOption("methylTFRAnnotationMm10.datadir") -options(methylTFRAnnotationMm10.datadir = mock_dir) +# Point the package at a temporary directory holding a small mock +# table so the example runs without downloading from AnnotationHub. +mock_dir <- file.path(tempdir(), "gcfreq_example") +dir.create(mock_dir, showWarnings = FALSE) +freq <- matrix(c(0.1, 0.2, 0.4, 0.2, 0.1), nrow = 5, ncol = 3) +saveRDS(list(MA0001.1 = freq), file.path(mock_dir, +"altius_motif_gcfreq.rds")) +old_opt <- options(methylTFRAnnotationMm10.datadir = mock_dir) -# 3. Run the function (it will now read the mock file) -x <- getGCfreq(motifSet = "altius") - -# 4. Restore options and clean up the temporary file -options(methylTFRAnnotationMm10.datadir = old_opt) -file.remove(mock_file) +gcfreqs <- getGCfreq("altius") +gcfreqs$MA0001.1 +colSums(gcfreqs$MA0001.1) +options(old_opt) +unlink(mock_dir, recursive = TRUE) +} +\seealso{ +\code{\link{motif_gcfreq}}, \code{\link{getTFbindsites}}, +\code{\link{getGenomeGC}} } diff --git a/man/getGenomeGC.Rd b/man/getGenomeGC.Rd index fa12f76..31ba46b 100644 --- a/man/getGenomeGC.Rd +++ b/man/getGenomeGC.Rd @@ -2,42 +2,49 @@ % Please edit documentation in R/getGenomeGC.R \name{getGenomeGC} \alias{getGenomeGC} -\title{getGenomeGC} +\title{Load the genome-wide GC distribution} \usage{ -getGenomeGC(assembly = .ASSEMBLY) +getGenomeGC(assembly = "mm10") } \arguments{ -\item{assembly}{Genome assembly. Defaults to the assembly this -package was built for.} +\item{assembly}{Genome assembly. Only \code{"mm10"} (the default) +is available in this package; case-insensitive.} } \value{ -A \code{GRanges} with GC_bias and GC_bin metadata columns. +A \code{GRanges} with metadata columns \code{GC_bias} (GC +fraction of the window) and \code{GC_bin} (quintile, 1 to 5). } \description{ -Load the genome-wide GC distribution. +Load the genome-wide GC distribution for mm10. Downloaded from AnnotationHub on first use and cached locally thereafter. } -\examples{ -# 1. Create a dummy object and save it to a temporary directory -mock_dir <- tempdir() -mock_file <- file.path(mock_dir, "genomewide_GC_mm10.rds") - -# Mocking a GRanges object with the expected metadata columns -mock_gr <- GenomicRanges::GRanges("chr1:1-100") -mock_gr$GC_bias <- 0.5 -mock_gr$GC_bin <- 1L -saveRDS(mock_gr, mock_file) - -# 2. Temporarily point the package to tempdir -old_opt <- getOption("methylTFRAnnotationMm10.datadir") -options(methylTFRAnnotationMm10.datadir = mock_dir) +\details{ +The genome is tiled into non-overlapping 30 nt windows. Each +window carries its GC fraction and its genome-wide GC quintile. +\pkg{methylTFR} uses the table to assign every methylation call to +a GC bin. See \code{\link{genomewide_GC}} for the data format. -# 3. Run the function -gc <- getGenomeGC() +The package serves a single assembly, so the argument can be left +out. It is kept so that code written as +\code{getGenomeGC("mm10")} works; any other assembly is an error. +} +\examples{ +# Point the package at a temporary directory holding a small mock +# object so the example runs without downloading from AnnotationHub. +mock_dir <- file.path(tempdir(), "genomegc_example") +dir.create(mock_dir, showWarnings = FALSE) +gr <- GenomicRanges::GRanges(c("chr1:1-30", "chr1:31-60", "chr1:61-90")) +gr$GC_bias <- c(0.30, 0.43, 0.63) +gr$GC_bin <- c(1L, 3L, 5L) +saveRDS(gr, file.path(mock_dir, "genomewide_GC_mm10.rds")) +old_opt <- options(methylTFRAnnotationMm10.datadir = mock_dir) -# 4. Restore options and clean up -options(methylTFRAnnotationMm10.datadir = old_opt) -file.remove(mock_file) +getGenomeGC() +options(old_opt) +unlink(mock_dir, recursive = TRUE) +} +\seealso{ +\code{\link{genomewide_GC}}, \code{\link{getGCfreq}} } diff --git a/man/getTFbindsites.Rd b/man/getTFbindsites.Rd index ce92884..e815891 100644 --- a/man/getTFbindsites.Rd +++ b/man/getTFbindsites.Rd @@ -2,41 +2,49 @@ % Please edit documentation in R/getTFbindsites.R \name{getTFbindsites} \alias{getTFbindsites} -\title{getTFbindsites} +\title{Load transcription factor binding sites} \usage{ getTFbindsites(motifSet = "altius") } \arguments{ -\item{motifSet}{Motif set to load. One of: altius, cisbpv2, jaspar2020, -jaspar2020_distal.} +\item{motifSet}{Motif set to load. One of +\code{availableMotifSets()}: \code{altius}, \code{cisbpv2}, +\code{jaspar2020}. Case-insensitive.} } \value{ -A \code{GRangesList}, one element per motif. +A \code{GRangesList} with one \code{GRanges} per motif, +named by motif ID. } \description{ -Retrieve transcription factor binding sites for a motif set. -Downloaded from AnnotationHub on first use and cached locally thereafter. +Load genome-wide transcription factor binding sites for a +motif set. Downloaded from AnnotationHub on first use and cached +locally thereafter. +} +\details{ +Each element of the returned list holds the binding sites of +one motif. Every range is the motif match extended by 200 bp on either +side, the footprint window across which \pkg{methylTFR} reads +methylation. See \code{\link{tf_bindsites}} for the data format. } \examples{ -# 1. Create a dummy object and save it to a temp directory -mock_dir <- tempdir() -mock_file <- file.path(mock_dir, "altius_tf_bindsites.rds") - -# Mocking a GRangesList as expected by the function output +# Point the package at a temporary directory holding a small mock +# object so the example runs without downloading from AnnotationHub. +mock_dir <- file.path(tempdir(), "tfbs_example") +dir.create(mock_dir, showWarnings = FALSE) mock_grl <- GenomicRanges::GRangesList( - mock_motif_1 = GenomicRanges::GRanges("chr1:100-200") + MA0001.1 = GenomicRanges::GRanges( + c("chr1:1001-1411", "chr2:5001-5411"), + strand = c("+", "-") + ) ) -saveRDS(mock_grl, mock_file) - -# 2. Point the local directory option to the temp directory -old_opt <- getOption("methylTFRAnnotationMm10.datadir") -options(methylTFRAnnotationMm10.datadir = mock_dir) +saveRDS(mock_grl, file.path(mock_dir, "altius_tf_bindsites.rds")) +old_opt <- options(methylTFRAnnotationMm10.datadir = mock_dir) -# 3. Run the function -x <- getTFbindsites() - -# 4. Restore options and clean up -options(methylTFRAnnotationMm10.datadir = old_opt) -file.remove(mock_file) +getTFbindsites("altius") +options(old_opt) +unlink(mock_dir, recursive = TRUE) +} +\seealso{ +\code{\link{tf_bindsites}}, \code{\link{getGCfreq}} } diff --git a/man/methylTFRAnnotationMm10-package.Rd b/man/methylTFRAnnotationMm10-package.Rd new file mode 100644 index 0000000..63b2bfc --- /dev/null +++ b/man/methylTFRAnnotationMm10-package.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methylTFRAnnotationMm10-package.R +\docType{package} +\name{methylTFRAnnotationMm10-package} +\alias{methylTFRAnnotationMm10} +\alias{methylTFRAnnotationMm10-package} +\title{methylTFRAnnotationMm10: methylTFR annotations for mm10} +\description{ +Genome annotations that \pkg{methylTFR} needs to compute +GC-bias-corrected transcription factor (TF) deviation scores from DNA +methylation data on the Mus musculus mm10 assembly. +} +\details{ +The package provides three kinds of resources, each with an accessor: +\describe{ + \item{\code{\link{getTFbindsites}}}{Genome-wide TF binding sites, + one \code{GRanges} per motif, extended to the footprint window + (see \code{\link{tf_bindsites}}).} + \item{\code{\link{getGCfreq}}}{Motif GC frequency tables: how + each motif's binding sites distribute over genome-wide GC quintiles + (see \code{\link{motif_gcfreq}}).} + \item{\code{\link{getGenomeGC}}}{The genome-wide GC distribution + in 30 nt windows (see \code{\link{genomewide_GC}}).} +} +\code{\link{availableMotifSets}} lists the motif sets that can be +requested. The three objects are passed to +\code{methylTFR::run_methyltfr()}. + +The data are too large to ship with the package. They are hosted on +AnnotationHub, downloaded on first use and served from the local +AnnotationHub cache afterwards. The metadata of every resource is +listed in \code{system.file("extdata", "metadata.csv", package = +"methylTFRAnnotationMm10")}, and the scripts that produced them are in +\code{system.file("scripts", package = "methylTFRAnnotationMm10")}. + +To read the \code{.rds} files from a local directory instead of +AnnotationHub (for example on a machine without internet access), set +\code{options(methylTFRAnnotationMm10.datadir = "/path/to/files")} or the +environment variable \code{METHYL_TFRANNOTATION_Mm10_DIR}. +} +\seealso{ +\code{vignette("methylTFRAnnotationMm10")} +} +\author{ +\strong{Maintainer}: Irem B. Gunduz \email{irembgunduz@gmail.com} (\href{https://orcid.org/0000-0003-2641-0916}{ORCID}) + +Other contributors: +\itemize{ + \item ERA-NET Transcan-Neu III - EPILUNAR (Grant 01KT2409) [funder] + \item Saarland University (NanoBioMed Young Investigator Grant) [funder] +} + +} diff --git a/man/motif_gcfreq.Rd b/man/motif_gcfreq.Rd new file mode 100644 index 0000000..ff8ecd2 --- /dev/null +++ b/man/motif_gcfreq.Rd @@ -0,0 +1,69 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/resources.R +\name{motif_gcfreq} +\alias{motif_gcfreq} +\alias{altius_motif_gcfreq} +\alias{cisbpv2_motif_gcfreq} +\alias{jaspar2020_motif_gcfreq} +\title{Motif GC frequency tables} +\format{ +A named \code{list} with one numeric matrix per motif (names +are motif IDs, matching the names of the corresponding +\code{\link{tf_bindsites}} list). Each matrix has +\describe{ + \item{5 rows}{Genome-wide GC quintiles, from lowest (row 1) to + highest (row 5) GC. Quintile boundaries are the ones stored with + \code{\link{genomewide_GC}}, so both objects share one scale.} + \item{one column per window position}{A 30 nt window slides one + base at a time along the binding site (motif plus 200 bp on either + side, padded by a further 65 bp so edge windows are complete). + Column \eqn{j} is the \eqn{j}-th window.} +} +Entries are the fraction of the motif's binding sites whose window at +that position falls into each quintile; every column sums to one. +Windows containing \code{N} are ignored. +} +\source{ +Built with methylTFRAnnotationBuilder +(\url{https://github.com/EpigenomeInformatics/methylTFRAnnotationBuilder}) +from BSgenome sequences; see +\code{system.file("scripts", "make-data.R", package = +"methylTFRAnnotationMm10")} +and \code{system.file("extdata", "metadata.csv", package = +"methylTFRAnnotationMm10")} for the exact sources and versions. +} +\description{ +Per-motif tables describing the GC composition around +each motif's binding sites on mm10. Returned by +\code{\link{getGCfreq}}; the AnnotationHub record titles are +\code{_motif_gcfreq.rds}. +} +\details{ +The tables are what make the methylTFR deviation score +bias-corrected. A motif whose sites sit in GC-rich sequence overlaps +hypomethylated CpG islands more often; combining these fractions with +the observed methylation per GC bin gives the methylation expected from +sequence composition alone, which is then subtracted. + +Available motif sets: +\describe{ + \item{\code{jaspar2020}}{JASPAR2020 CORE matrices, scanned + genome-wide.} + \item{\code{cisbpv2}}{CIS-BP v2 PWMs (\code{pwms_v2} from + \pkg{chromVARmotifs}), scanned genome-wide.} + \item{\code{altius}}{Vierstra motif archetypes v1.0.} +} +} +\examples{ +## On first use this downloads the resource from AnnotationHub: +\dontrun{ +gcfreqs <- getGCfreq("jaspar2020") +length(gcfreqs) # number of motifs +dim(gcfreqs[[1]]) # 5 x number of window positions +colSums(gcfreqs[[1]]) # all 1 +} +} +\seealso{ +\code{\link{getGCfreq}}, \code{\link{methylTFRAnnotationMm10}} +} +\keyword{datasets} diff --git a/man/tf_bindsites.Rd b/man/tf_bindsites.Rd new file mode 100644 index 0000000..1e941b3 --- /dev/null +++ b/man/tf_bindsites.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/resources.R +\name{tf_bindsites} +\alias{tf_bindsites} +\alias{altius_tf_bindsites} +\alias{cisbpv2_tf_bindsites} +\alias{jaspar2020_tf_bindsites} +\title{Transcription factor binding sites} +\format{ +A \code{GRangesList} with one \code{GRanges} per motif, +named by motif ID. Each range is a motif match on the primary +chromosomes (chr1-19, X, Y; chrM excluded) extended by 200 bp +on either side of the match, so all ranges of a motif have the same +width (motif width + 400 bp). The strand of the match is kept; there +are no metadata columns. Coordinates are 1-based. +} +\source{ +See \code{\link{motif_gcfreq}}. +} +\description{ +Genome-wide predicted transcription factor binding sites +on mm10. Returned by \code{\link{getTFbindsites}}; the +AnnotationHub record titles are \code{_tf_bindsites.rds}. +} +\details{ +Available motif sets: +\describe{ + \item{\code{jaspar2020}}{JASPAR2020 CORE matrices, scanned + genome-wide.} + \item{\code{cisbpv2}}{CIS-BP v2 PWMs (\code{pwms_v2} from + \pkg{chromVARmotifs}), scanned genome-wide.} + \item{\code{altius}}{Vierstra motif archetypes v1.0.} +} +} +\examples{ +## On first use this downloads the resource from AnnotationHub: +\dontrun{ +tfbs <- getTFbindsites("jaspar2020") +length(tfbs) # number of motifs +head(lengths(tfbs)) # binding sites per motif +tfbs[[1]] +} +} +\seealso{ +\code{\link{getTFbindsites}}, \code{\link{methylTFRAnnotationMm10}} +} +\keyword{datasets} diff --git a/tests/testthat.R b/tests/testthat.R index 7518bd4..f4eed9e 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,3 +1,4 @@ library(testthat) library(methylTFRAnnotationMm10) + test_check("methylTFRAnnotationMm10") diff --git a/tests/testthat/test-accessors.R b/tests/testthat/test-accessors.R index b98486e..86ab5da 100644 --- a/tests/testthat/test-accessors.R +++ b/tests/testthat/test-accessors.R @@ -1,17 +1,42 @@ test_that("motif set validation rejects unknown sets", { expect_error(getGCfreq("not_a_motif_set"), "Invalid motif set") expect_error(getGCfreq(c("a", "b")), "Invalid motif set") + expect_error(getTFbindsites(NA_character_), "Invalid motif set") + expect_error(getTFbindsites(1), "Invalid motif set") + expect_error(getTFbindsites(character(0)), "Invalid motif set") }) test_that("motif set validation is case insensitive", { - expect_identical(methylTFRAnnotationMm10:::.check_motif_set("ALTIUS"), "altius") + expect_identical( + methylTFRAnnotationMm10:::.check_motif_set("ALTIUS"), "altius" + ) }) -test_that("metadata.csv covers every declared motif set", { - md <- utils::read.csv(system.file("extdata", "metadata.csv", package = "methylTFRAnnotationMm10")) - for (s in methylTFRAnnotationMm10:::.MOTIF_SETS) { - expect_true(any(grepl(paste0("^", s, "_"), md$Title)), - info = s - ) +test_that("availableMotifSets() returns the declared sets", { + sets <- availableMotifSets() + expect_type(sets, "character") + expect_identical(sets, c("altius", "cisbpv2", "jaspar2020")) + for (s in sets) { + expect_identical(methylTFRAnnotationMm10:::.check_motif_set(s), s) } }) + +test_that("metadata.csv covers every declared motif set and the genome GC", { + md <- utils::read.csv(system.file("extdata", "metadata.csv", + package = "methylTFRAnnotationMm10" + )) + for (s in availableMotifSets()) { + expect_true(paste0(s, "_motif_gcfreq.rds") %in% md$Title, info = s) + base <- sub("_distal$", "", s) + expect_true(paste0(base, "_tf_bindsites.rds") %in% md$Title, info = s) + } + expect_true("genomewide_GC_mm10.rds" %in% md$Title) + expect_true(all(md$Genome == "mm10")) + expect_true(all(md$Species == "Mus musculus")) +}) + +test_that("getGenomeGC() only accepts this package's assembly", { + expect_error(getGenomeGC("hg38"), "only") + expect_error(getGenomeGC(c("mm10", "mm10")), "only") + expect_error(getGenomeGC(NA_character_), "only") +}) diff --git a/tests/testthat/test-annotationhub.R b/tests/testthat/test-annotationhub.R new file mode 100644 index 0000000..74c0db1 --- /dev/null +++ b/tests/testthat/test-annotationhub.R @@ -0,0 +1,45 @@ +# The AnnotationHub branch of the resolver, tested without network +# access by replacing the two small functions that talk to the hub. + +test_that("resources are looked up on AnnotationHub by title", { + old <- options(methylTFRAnnotationMm10.datadir = NULL) + on.exit(options(old), add = TRUE) + old_env <- Sys.getenv("METHYL_TFRANNOTATION_Mm10_DIR", unset = NA) + Sys.unsetenv("METHYL_TFRANNOTATION_Mm10_DIR") + on.exit( + if (!is.na(old_env)) Sys.setenv(METHYL_TFRANNOTATION_Mm10_DIR = old_env), + add = TRUE + ) + + fetched <- character(0) + local_mocked_bindings( + .hub_titles = function() c( + AH1 = "altius_motif_gcfreq.rds", + AH2 = "altius_tf_bindsites.rds", + AH3 = "genomewide_GC_mm10.rds" + ), + .hub_get = function(id) { + fetched <<- c(fetched, id) + paste("object", id) + } + ) + expect_identical(getGCfreq("altius"), "object AH1") + expect_identical(getTFbindsites("altius"), "object AH2") + expect_identical(getGenomeGC(), "object AH3") + expect_identical(fetched, c("AH1", "AH2", "AH3")) + expect_error(getGCfreq("cisbpv2"), "Resource not found on AnnotationHub") +}) + +test_that("the real AnnotationHub records are reachable", { + skip_on_cran() + skip_on_ci() + skip_if_offline() + skip_if_not(nzchar(Sys.getenv("METHYLTFR_TEST_HUB")), + "set METHYLTFR_TEST_HUB=1 to query AnnotationHub" + ) + titles <- methylTFRAnnotationMm10:::.hub_titles() + md <- utils::read.csv(system.file("extdata", "metadata.csv", + package = "methylTFRAnnotationMm10" + )) + expect_setequal(unname(titles), md$Title) +}) diff --git a/tests/testthat/test-local-resources.R b/tests/testthat/test-local-resources.R new file mode 100644 index 0000000..00a0f5d --- /dev/null +++ b/tests/testthat/test-local-resources.R @@ -0,0 +1,96 @@ +# The accessors read .rds files from a local directory when the +# methylTFRAnnotationMm10.datadir option (or the +# METHYL_TFRANNOTATION_Mm10_DIR environment variable) +# is set. These tests write small objects with the same structure as the +# AnnotationHub resources and check they come back unchanged. + +make_local_resources <- function(dir) { + tfbs <- GenomicRanges::GRangesList( + MA0001.1 = GenomicRanges::GRanges( + c("chr1:1001-1411", "chr2:5001-5411"), + strand = c("+", "-") + ) + ) + freq <- matrix(c(0.1, 0.2, 0.4, 0.2, 0.1), nrow = 5, ncol = 3) + gc <- GenomicRanges::GRanges(c("chr1:1-30", "chr1:31-60", "chr1:61-90")) + gc$GC_bias <- c(0.30, 0.43, 0.63) + gc$GC_bin <- c(1L, 3L, 5L) + for (s in availableMotifSets()) { + saveRDS( + list(MA0001.1 = freq), + file.path(dir, paste0(s, "_motif_gcfreq.rds")) + ) + base <- sub("_distal$", "", s) + saveRDS(tfbs, file.path(dir, paste0(base, "_tf_bindsites.rds"))) + } + saveRDS(gc, file.path(dir, "genomewide_GC_mm10.rds")) + list(tfbs = tfbs, freq = freq, gc = gc) +} + +with_local_dir <- function(code) { + dir <- tempfile("annot_") + dir.create(dir) + on.exit(unlink(dir, recursive = TRUE), add = TRUE) + old <- options(methylTFRAnnotationMm10.datadir = dir) + on.exit(options(old), add = TRUE) + ref <- make_local_resources(dir) + code(dir, ref) +} + +test_that("accessors read every resource from a local directory", { + with_local_dir(function(dir, ref) { + for (s in availableMotifSets()) { + freq <- getGCfreq(s) + expect_type(freq, "list") + expect_identical(freq$MA0001.1, ref$freq) + expect_equal(unname(colSums(freq$MA0001.1)), rep(1, 3)) + } + tfbs <- getTFbindsites(toupper(availableMotifSets()[1])) + expect_s4_class(tfbs, "GRangesList") + expect_identical(tfbs, ref$tfbs) + + gc <- getGenomeGC() + expect_s4_class(gc, "GRanges") + cols <- names(GenomicRanges::mcols(gc)) + expect_true(all(c("GC_bias", "GC_bin") %in% cols)) + expect_identical(gc, ref$gc) + expect_identical(getGenomeGC(toupper("mm10")), gc) + }) +}) + +test_that("a missing local file gives an informative error", { + with_local_dir(function(dir, ref) { + file.remove(file.path(dir, "genomewide_GC_mm10.rds")) + expect_error(getGenomeGC(), "Annotation file not found") + }) +}) + +test_that("the environment variable is used when the option is unset", { + dir <- tempfile("annot_env_") + dir.create(dir) + on.exit(unlink(dir, recursive = TRUE), add = TRUE) + ref <- make_local_resources(dir) + old_opt <- options(methylTFRAnnotationMm10.datadir = NULL) + on.exit(options(old_opt), add = TRUE) + old_env <- Sys.getenv("METHYL_TFRANNOTATION_Mm10_DIR", unset = NA) + Sys.setenv(METHYL_TFRANNOTATION_Mm10_DIR = dir) + on.exit( + if (is.na(old_env)) Sys.unsetenv("METHYL_TFRANNOTATION_Mm10_DIR") + else Sys.setenv(METHYL_TFRANNOTATION_Mm10_DIR = old_env), + add = TRUE + ) + expect_identical(methylTFRAnnotationMm10:::.local_dir(), dir) + expect_identical(getGenomeGC(), ref$gc) +}) + +test_that("no local directory is used when neither option nor env var is set", { + old_opt <- options(methylTFRAnnotationMm10.datadir = NULL) + on.exit(options(old_opt), add = TRUE) + old_env <- Sys.getenv("METHYL_TFRANNOTATION_Mm10_DIR", unset = NA) + Sys.unsetenv("METHYL_TFRANNOTATION_Mm10_DIR") + on.exit( + if (!is.na(old_env)) Sys.setenv(METHYL_TFRANNOTATION_Mm10_DIR = old_env), + add = TRUE + ) + expect_null(methylTFRAnnotationMm10:::.local_dir()) +}) diff --git a/vignettes/methylTFRAnnotationMm10.Rmd b/vignettes/methylTFRAnnotationMm10.Rmd index c7835a2..5bc077d 100644 --- a/vignettes/methylTFRAnnotationMm10.Rmd +++ b/vignettes/methylTFRAnnotationMm10.Rmd @@ -1,5 +1,7 @@ --- title: "methylTFRAnnotationMm10" +author: "Irem B. Gunduz" +date: "`r Sys.Date()`" output: BiocStyle::html_document vignette: > %\VignetteIndexEntry{methylTFRAnnotationMm10} @@ -13,78 +15,212 @@ knitr::opts_chunk$set(collapse = TRUE, comment = "#>") # Overview -`methylTFRAnnotationMm10` provides the genome annotations `methylTFR` needs to -compute bias-corrected transcription factor deviation scores on Mm10: - -- **binding sites**, one `GRanges` per motif, each range extended - so that methylation can be read across the footprint window; -- **motif GC frequency tables**, recording how each motif's binding - sites distribute across genome-wide GC quintiles; -- **the genome-wide GC distribution**, which assigns each - methylation call to a GC bin. - -The GC tables are what make the deviation score bias-corrected. A -motif whose binding sites sit in GC-rich sequence will overlap CpG -islands more often than one that does not, and CpG islands are -hypomethylated; without the correction that difference in sequence -composition would read as a difference in factor activity. +`methylTFRAnnotationMm10` provides the genome annotations that +[methylTFR](https://github.com/EpigenomeInformatics/methylTFR) needs to +compute GC-bias-corrected transcription factor (TF) deviation scores on +mm10. It contains three kinds of resources, each with its own accessor: + +| Accessor | Returns | Help page | +|---|---|---| +| `getTFbindsites()` | TF binding sites, one `GRanges` per motif | `?tf_bindsites` | +| `getGCfreq()` | motif GC frequency tables, one matrix per motif | `?motif_gcfreq` | +| `getGenomeGC()` | genome-wide GC content in 30 nt windows | `?genomewide_GC` | + +The GC tables are what make the deviation score bias-corrected. A motif +whose binding sites sit in GC-rich sequence overlaps CpG islands more +often than one that does not, and CpG islands are hypomethylated. Without +a correction, that difference in sequence composition would look like a +difference in TF activity. + +The resources are several gigabytes in total, so they are hosted on +AnnotationHub instead of being shipped with the package. The first call +to an accessor downloads the resource; later calls read it from the local +AnnotationHub cache. + +# Installation + +```r +if (!requireNamespace("BiocManager", quietly = TRUE)) { + install.packages("BiocManager") +} +BiocManager::install("methylTFRAnnotationMm10") +``` -The data are too large to ship inside the package, so they are -hosted on AnnotationHub and downloaded on first use. Subsequent -calls are served from the local AnnotationHub cache. +# Example data used in this vignette +Downloading the real resources would take too long for a vignette, so +this vignette works on small synthetic objects that have exactly the +structure of the real ones. Every accessor can read `.rds` files from a +local directory instead of AnnotationHub when the option +`methylTFRAnnotationMm10.datadir` is set. We write the synthetic objects to a +temporary directory and point the option at it. With the option unset +(the default), the same calls below would download the real data. -```{r dummy, include = FALSE} -# HIDDEN CHUNK: Set up mock data so the vignette chunks can evaluate -mock_dir <- tempdir() +```{r mock-data} +suppressPackageStartupMessages(library(GenomicRanges)) +library(methylTFRAnnotationMm10) -# 1. Mock TF bindsites -mock_grl <- GenomicRanges::GRangesList( - mock_motif_1 = GenomicRanges::GRanges("chr1:100-200") +mock_dir <- file.path(tempdir(), "methylTFRAnnotationMm10_vignette") +dir.create(mock_dir, showWarnings = FALSE) + +# Binding sites: a GRangesList with one GRanges per motif. Each range is +# a motif match extended by 200 bp on either side. +mock_tfbs <- GRangesList( + MA0001.1 = GRanges( + c("chr1:10001-10411", "chr1:52001-52411", "chr2:3001-3411"), + strand = c("+", "-", "+") + ), + MA0002.1 = GRanges(c("chr1:20001-20415", "chr3:8001-8415"), + strand = c("-", "+") + ) ) -saveRDS(mock_grl, file.path(mock_dir, "altius_tf_bindsites.rds")) - -# 2. Mock GC frequencies -mock_gcfreq <- list(mock_motif_1 = matrix(0.25, nrow = 5, ncol = 4)) +saveRDS(mock_tfbs, file.path(mock_dir, "altius_tf_bindsites.rds")) + +# GC frequency tables: one 5-row matrix per motif. Rows are GC +# quintiles, columns are positions of a 30 nt window along the binding +# site, and every column sums to 1. We use 6 positions to keep it short. +set.seed(1) +mock_freq <- function(n_pos) { + m <- matrix(runif(5 * n_pos), nrow = 5) + sweep(m, 2, colSums(m), "/") +} +mock_gcfreq <- list(MA0001.1 = mock_freq(6), MA0002.1 = mock_freq(6)) saveRDS(mock_gcfreq, file.path(mock_dir, "altius_motif_gcfreq.rds")) -# 3. Mock Genome GC -mock_gr <- GenomicRanges::GRanges("chr1:1-100") -mock_gr$GC_bias <- 0.5 -mock_gr$GC_bin <- 1L -saveRDS(mock_gr, file.path(mock_dir, "genomewide_GC_Mm10.rds")) +# Genome-wide GC: non-overlapping 30 nt windows with their GC fraction +# and GC quintile (1 = lowest, 5 = highest). +mock_gc <- GRanges("chr1", IRanges(start = seq(1, by = 30, length.out = 10), + width = 30)) +mock_gc$GC_bias <- round(runif(10, 0.2, 0.7), 2) +breaks <- quantile(mock_gc$GC_bias, probs = seq(0, 1, 0.2)) +mock_gc$GC_bin <- findInterval(mock_gc$GC_bias, breaks, + rightmost.closed = TRUE) +metadata(mock_gc) <- list(gc_breaks = breaks, bin_scope = "genome", + step = 30L) +saveRDS(mock_gc, file.path(mock_dir, "genomewide_GC_mm10.rds")) + +old_opt <- options(methylTFRAnnotationMm10.datadir = mock_dir) +list.files(mock_dir) +``` + +# Available motif sets -# Temporarily point the package to tempdir -old_opt <- getOption("methylTFRAnnotationMm10.datadir") -options(methylTFRAnnotationMm10.datadir = mock_dir) +`availableMotifSets()` lists the motif sets that can be passed to +`getTFbindsites()` and `getGCfreq()`: + +```{r motif-sets} +availableMotifSets() ``` -# Usage +# Binding sites: `getTFbindsites()` -```{r usage, eval = FALSE} -library(methylTFRAnnotationMm10) +`getTFbindsites()` returns a `GRangesList` named by motif ID, with one +`GRanges` of binding sites per motif. Each range covers the motif match +plus 200 bp on either side: the footprint window over which methylTFR +reads methylation. All ranges of a motif therefore have the same width. +```{r tfbs} tf_bindsites <- getTFbindsites("altius") +tf_bindsites +``` + +The number of binding sites per motif, and the width of each motif's +window: + +```{r tfbs-summary} +lengths(tf_bindsites) +vapply(width(tf_bindsites), unique, integer(1)) +``` + +# GC frequency tables: `getGCfreq()` + +`getGCfreq()` returns a named list with one matrix per motif, using the +same motif IDs as `getTFbindsites()`. Each matrix has five rows, one per +genome-wide GC quintile (row 1 lowest GC, row 5 highest). Each column is +one position of a 30 nt window sliding along the binding site. An entry +is the fraction of the motif's binding sites whose window at that +position falls into that GC quintile, so every column sums to one. In the +real data a matrix has several hundred columns. + +```{r gcfreq} gcfreqs <- getGCfreq("altius") +names(gcfreqs) +dim(gcfreqs$MA0001.1) +round(gcfreqs$MA0001.1, 2) +colSums(gcfreqs$MA0001.1) +``` + +methylTFR combines these fractions with the average methylation it +observes in each GC quintile to get the methylation a motif would show +from GC content alone, and reports the deviation from that expectation. + +# Genome-wide GC distribution: `getGenomeGC()` + +`getGenomeGC()` returns a `GRanges` of non-overlapping 30 nt windows. +`GC_bias` is the GC fraction of each window and `GC_bin` its genome-wide +GC quintile. methylTFR overlaps each methylation call with these windows +to assign it to a GC bin. The package serves a single assembly, so no +argument is needed; `getGenomeGC("mm10")` is accepted too. + +```{r genome-gc} gc_dist <- getGenomeGC() +gc_dist +table(gc_dist$GC_bin) ``` -These are passed straight to `methylTFR::run_methyltfr()`. +The quintile boundaries are stored with the object, so that the genome +table and the motif tables always use the same scale: -# Available motif sets +```{r genome-gc-breaks} +metadata(gc_dist)$gc_breaks +``` -```{r available_motifs, eval = FALSE} -methylTFRAnnotationMm10:::.MOTIF_SETS +# Using the annotations with methylTFR + +The three objects are passed to `methylTFR::run_methyltfr()`. With real +data this looks like: + +```r +library(methylTFR) +deviations <- run_methyltfr( + "samples.tsv", # sample annotation + "samples_dir", # directory with methylation calls + tf_bindsites = getTFbindsites("jaspar2020"), + gcfreqs = getGCfreq("jaspar2020"), + gc_dist = getGenomeGC(), + filetype = "EPP" +) ``` -# Session info +See the methylTFR vignettes for a complete analysis. -```{r session_info} -sessionInfo() +# Reading the real data from AnnotationHub + +Once the local-directory option is removed, the accessors go to +AnnotationHub. The records can also be browsed directly: + +```r +library(AnnotationHub) +ah <- AnnotationHub() +query(ah, "methylTFRAnnotationMm10") ``` +If you have downloaded the `.rds` files by hand, set +`options(methylTFRAnnotationMm10.datadir = "/path/to/files")` (or the +environment variable `METHYL_TFRANNOTATION_Mm10_DIR`) to read them from disk. + +# Cleaning up -```{r cleanup, include = FALSE} -# HIDDEN CHUNK: Clean up the options -options(methylTFRAnnotationMm10.datadir = old_opt) +Restore the option and remove the synthetic files: + +```{r cleanup} +options(old_opt) +unlink(mock_dir, recursive = TRUE) +getOption("methylTFRAnnotationMm10.datadir") +``` + +# Session info + +```{r session-info} +sessionInfo() ``` From bd136210e956aa05cb4fa4eeb917a350235846f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cigunduz=E2=80=9D?= Date: Tue, 29 Sep 2026 16:47:14 +0200 Subject: [PATCH 2/2] Address Bioconductor review; bump version --- .Rbuildignore | 3 ++- CITATION.cff | 37 +++++++++++++++++++++++++++ vignettes/methylTFRAnnotationMm10.Rmd | 4 --- 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 CITATION.cff diff --git a/.Rbuildignore b/.Rbuildignore index 01b8059..b5171a0 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -12,4 +12,5 @@ ^\.RData$ ^\.Ruserdata$ ^\.Rproj\.user$ -^\.github \ No newline at end of file +^\.github +^CITATION\.cff$ diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..491b5a2 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,37 @@ +cff-version: 1.2.0 +message: "If you use methylTFRAnnotationMm10, please cite it as below." +type: software +title: "methylTFRAnnotationMm10: methylTFR annotations for mm10" +abstract: >- + Precomputed transcription factor binding sites, motif GC frequency tables + and a genome-wide GC distribution for the mouse (mm10) genome, for use with the + methylTFR package. The data are hosted on AnnotationHub and retrieved on + first use. +authors: + - family-names: Gunduz + given-names: Irem B. + email: irembgunduz@gmail.com + orcid: "https://orcid.org/0000-0003-2641-0916" +version: 0.99.10 +date-released: "2026-09-29" +doi: 10.18129/B9.bioc.methylTFRAnnotationMm10 +license: Artistic-2.0 +repository-code: "https://github.com/EpigenomeInformatics/methylTFRAnnotationMm10" +keywords: + - R + - Bioconductor + - AnnotationHub + - DNA methylation + - transcription factor binding sites +references: + - type: software + title: "methylTFR: Quantification of DNA methylation signatures in TFBS" + authors: + - family-names: Gunduz + given-names: Irem B. + - family-names: Murugan + given-names: Sarath Kumar + - family-names: Mueller + given-names: Fabian + doi: 10.18129/B9.bioc.methylTFR + repository-code: "https://github.com/EpigenomeInformatics/methylTFR" diff --git a/vignettes/methylTFRAnnotationMm10.Rmd b/vignettes/methylTFRAnnotationMm10.Rmd index 5bc077d..52e3e97 100644 --- a/vignettes/methylTFRAnnotationMm10.Rmd +++ b/vignettes/methylTFRAnnotationMm10.Rmd @@ -9,10 +9,6 @@ vignette: > %\VignetteEncoding{UTF-8} --- -```{r setup, include = FALSE} -knitr::opts_chunk$set(collapse = TRUE, comment = "#>") -``` - # Overview `methylTFRAnnotationMm10` provides the genome annotations that