From 51ae01f7f11469ef538627c8faf59ac6d30026dc Mon Sep 17 00:00:00 2001 From: Richard Heery Date: Mon, 20 Apr 2026 16:08:29 +0100 Subject: [PATCH 1/2] Added expand method for GRanges to enable expansion upstream and downstream in a strand-aware manner and associated unit tests --- R/intra-range-methods.R | 37 ++++++++++++++++++++++- inst/unitTests/test_intra-range-methods.R | 28 +++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/R/intra-range-methods.R b/R/intra-range-methods.R index 4f71588..4569b2c 100644 --- a/R/intra-range-methods.R +++ b/R/intra-range-methods.R @@ -79,7 +79,7 @@ setMethod("flank", "GenomicRanges", ### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -### promoters() and terminators() +### promoters(), terminators() and expand ### ### Returns an IRanges **instance**. @@ -155,6 +155,41 @@ setMethod("terminators", "GenomicRanges", site="TES") ) +### Expand ranges in a GRanges object upstream and downstream by specified numbers of bases, taking account of strand. +### Unstranded ranges are treated like they on the "+" strand +.GenomicRanges_expand = function(x, upstream = 0, downstream = 0, trim_out_of_bounds = TRUE) { + + # Check that upstream and downstream are vectors of either length 1 or with the same length as x + if(!length(upstream) %in% c(1, length(x))){ + stop("upstream should be a vector of length 1 or the length of x")} + if(!length(downstream) %in% c(1, length(x))){ + stop("downstream should be a vector of length 1 or the length of x")} + + # Check if any regions would have negative widths after adjustment + if(any(width(x) + upstream + downstream < 0)){ + stop("Some regions would have a negative width after adjustment. This is not permitted.") + } + + # Shift genomic regions upstream either by upstream (strand is + or *) or downstream (strand is -) + x <- GenomicRanges::shift(x, + -ifelse(GenomicRanges::strand(x) == "-", downstream, upstream)) + + # Expand GRanges so that their width equals their original size plus upstream and downstream + x <- GenomicRanges::resize(x, + width = (GenomicRanges::width(x) + upstream + downstream), fix = "start", ignore.strand = T) + + # Remove any out-of-bounds regions and return x + if(trim_out_of_bounds){ + x <- GenomicRanges::trim(x) + } + return(x) +} + +setMethod("expand", "GenomicRanges", + function(x, upstream=0, downstream=0, trim_out_of_bounds=TRUE) + .GenomicRanges_expand(x, upstream, downstream, trim_out_of_bounds) +) + ### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ### reflect() diff --git a/inst/unitTests/test_intra-range-methods.R b/inst/unitTests/test_intra-range-methods.R index e618c5a..b2344b5 100644 --- a/inst/unitTests/test_intra-range-methods.R +++ b/inst/unitTests/test_intra-range-methods.R @@ -230,6 +230,34 @@ test_promoters_GenomicRanges <- function() checkIdentical(seqinfo(gr), seqinfo(current)) } +test_expand_GenomicRanges <- function() +{ + + # Test zero-length GRanges + gr0 <- GRanges(tx_id=integer(0)) + checkIdentical(expand(gr0, 5, 2), gr0) + + # Test that giving 0 for upstream and downstream returns original GRanges + gr <- setNames(GRanges(c("chr1:100-200:+", "chr1:100-200:*", "chr1:100-200:-"), tx_id=seq_len(3)), letters[1:3]) + checkIdentical(expand(gr, 0, 0), gr) + + # Test that expanding works in strand-wise manner + target <- setNames(GRanges(c("chr1:0-250:+", "chr1:0-250:*", "chr1:50-300:-"), tx_id=seq_len(3)), letters[1:3]) + checkIdentical(expand(gr, 100, 50), target) + + # Test that regions are contracted when negative values given to upstream or downstream + target <- setNames(GRanges(c("chr1:150-175:+", "chr1:150-175:*", "chr1:125-150:-"), tx_id=seq_len(3)), letters[1:3]) + checkIdentical(expand(gr, -50, -25), target) + + # Test providing vectors with length > 1 for upstream and downstream + target <- setNames(GRanges(c("chr1:75-250:+", "chr1:0-225:*", "chr1:0-250:-"), tx_id=seq_len(3)), letters[1:3]) + checkIdentical(expand(gr, c(25, 100, 50), c(50, 25, 100)), target) + + # Test that an error is thrown when upstream or downstream ahve length > 1, but not equal to the length of gr + testthat::expect_error(expand(gr, c(25, 100), c(50, 25, 100))) + testthat::expect_error(expand(gr, c(25, 100, 50), c(25, 100))) +} + test_restrict_GenomicRanges <- function() { gr <- make_test_GRanges() From e653a125b64c13212a2543931af1a629eb258a49 Mon Sep 17 00:00:00 2001 From: Richard Heery Date: Mon, 20 Apr 2026 16:22:44 +0100 Subject: [PATCH 2/2] Adding missing () in comment --- R/intra-range-methods.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/intra-range-methods.R b/R/intra-range-methods.R index 4569b2c..c18938c 100644 --- a/R/intra-range-methods.R +++ b/R/intra-range-methods.R @@ -79,7 +79,7 @@ setMethod("flank", "GenomicRanges", ### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -### promoters(), terminators() and expand +### promoters(), terminators() and expand() ### ### Returns an IRanges **instance**.