###############################################################
# Setting validity and show methods.
.check_inputs <- function(anchor1, anchor2, regions, same.length=TRUE) {
if (!all(is.finite(anchor1)) || !all(is.finite(anchor2))) {
return("all anchor indices must be finite integers")
}
if (!all(anchor1 >= 1L) || !all(anchor2 >= 1L)) {
return('all anchor indices must be positive integers')
}
nregs <- length(regions)
if ( !all(anchor1 <= nregs) || !all(anchor2 <= nregs)) {
return("all anchor indices must refer to entries in 'regions'")
}
if (same.length && length(anchor1)!=length(anchor2)) {
return("first and second anchor vectors have different lengths")
}
return(TRUE)
}
setValidity2("GInteractions", function(object) {
if (is.unsorted(regions(object))) { # Don't move into .check_inputs, as resorting comes after checking validity in various methods.
return("'regions' should be sorted")
}
msg <- .check_inputs(anchor1(object), anchor2(object), regions(object))
if (is.character(msg)) { return(msg) }
### Length of anchors versus object is automatically checked by 'parallel_slot_names.'
if (!is.null(names(object))) {
if (length(names(object))!=length(object)) {
stop("'NAMES' must be NULL or have length equal to that of the object")
}
}
return(TRUE)
})
setMethod("parallel_slot_names", "GInteractions", function(x) {
c("anchor1", "anchor2", "NAMES", callNextMethod())
})
# For coercion to an environment:
setMethod("parallelVectorNames", "GInteractions", function(x) {
c("anchor1", "anchor2", "regions", "names")
})
scat <- function(fmt, vals=character(), exdent=2, ...) {
vals <- ifelse(nzchar(vals), vals, "''")
lbls <- paste(S4Vectors:::selectSome(vals), collapse=" ")
txt <- sprintf(fmt, length(vals), lbls)
cat(strwrap(txt, exdent=exdent, ...), sep="\n")
}
setMethod("show", "GInteractions", function(object){
showGInteractions(object, margin=" ", print.seqinfo=TRUE, print.classinfo=TRUE)
})
showGInteractions <- function(x, margin="", print.seqinfo=FALSE, print.classinfo=FALSE) {
lx <- length(x)
nr <- length(regions(x))
nc <- .safeNMcols(x)
cat(class(x), " object with ",
lx, " ", ifelse(lx == 1L, "interaction", "interactions"), " and ",
nc, " metadata ", ifelse(nc == 1L, "column", "columns"),
":\n", sep="")
out <- makePrettyMatrixForCompactPrinting(x, .makeNakedMatFromGInteractions)
# Ripped from GenomicRanges:::showGenomicRanges (with some mods).
if (print.classinfo) {
.COL2CLASS <- c(seqnames1 = "Rle", ranges1 = "IRanges", " "="", seqnames2="Rle", ranges2="IRanges")
extraColumnNames <- GenomicRanges:::extraColumnSlotNames(x)
.COL2CLASS <- c(.COL2CLASS, getSlots(class(x))[extraColumnNames])
classinfo <- makeClassinfoRowForCompactPrinting(x, .COL2CLASS)
classinfo[," "] <- ""
stopifnot(identical(colnames(classinfo), colnames(out)))
out <- rbind(classinfo, out)
}
if (nrow(out) != 0L) {
rownames(out) <- paste0(margin, rownames(out))
}
print(out, quote=FALSE, right=TRUE, max=length(out))
if (print.seqinfo) {
cat(margin, "-------\n", sep="")
ncr <- .safeNMcols(regions(x))
cat(margin, "regions: ", nr, " ranges and ", ncr, " metadata ", ifelse(ncr==1L, "column", "columns"), "\n", sep="")
cat(margin, "seqinfo: ", summary(seqinfo(x)), "\n", sep="")
}
}
.makeNakedMatFromGInteractions <- function(x) {
lx <- length(x)
nc <- .safeNMcols(x)
ans <- cbind(.pasteAnchor(anchors(x, type="first"), append="1"),
" "=rep.int("---", lx),
.pasteAnchor(anchors(x, type="second"), append="2"))
if (nc > 0L) {
tmp <- do.call(data.frame, c(lapply(mcols(x), showAsCell), list(check.names=FALSE)))
ans <- cbind(ans, `|`=rep.int("|", lx), as.matrix(tmp))
}
ans
}
.safeNMcols <- function(x) {
nc <- ncol(mcols(x))
if (is.null(nc)) { nc <- 0L }
return(nc)
}
.pasteAnchor <- function(x, append) {
out <- cbind(as.character(seqnames(x)), showAsCell(ranges(x)))
colnames(out) <- paste0(c("seqnames", "ranges"), append)
out
}
###############################################################
# Ordered equivalents, where swap state is enforced.
setValidity2("StrictGInteractions", function(object) {
if (any(anchor1(object) > anchor2(object))) {
stop("'anchor1' cannot be greater than 'anchor2'")
}
return(TRUE)
})
setValidity2("ReverseStrictGInteractions", function(object) {
if (any(anchor1(object) < anchor2(object))) {
stop("'anchor1' cannot be less than 'anchor2'")
}
return(TRUE)
})
###############################################################
# Constructors
.enforce_order <- function(anchor1, anchor2) {
swap <- anchor2 < anchor1
if (any(swap)) {
temp <- anchor1[swap]
anchor1[swap] <- anchor2[swap]
anchor2[swap] <- temp
}
return(list(anchor1=anchor1, anchor2=anchor2))
}
.resort_regions <- function(anchor1, anchor2, regions) {
if (is.unsorted(regions)) {
o <- order(regions)
new.pos <- seq_along(o)
new.pos[o] <- new.pos
anchor1 <- new.pos[anchor1]
anchor2 <- new.pos[anchor2]
regions <- regions[o]
}
return(list(anchor1=anchor1, anchor2=anchor2, regions=regions))
}
.new_GInteractions <- function(anchor1, anchor2, regions, metadata, mode=c("normal", "strict", "reverse")) {
mode <- match.arg(mode)
elementMetadata <- make_zero_col_DFrame(length(anchor1))
# Checking odds and ends.
anchor1 <- as.integer(anchor1)
anchor2 <- as.integer(anchor2)
msg <- .check_inputs(anchor1, anchor2, regions)
if (is.character(msg)) { stop(msg) }
out <- .resort_regions(anchor1, anchor2, regions)
anchor1 <- out$anchor1
anchor2 <- out$anchor2
regions <- out$regions
# Checking what the mode should be.
if (mode=="normal") {
cls <- "GInteractions"
} else {
if (mode=="strict") {
cls <- "StrictGInteractions"
out <- .enforce_order(anchor1, anchor2)
} else {
cls <- "ReverseStrictGInteractions"
out <- rev(.enforce_order(anchor1, anchor2))
}
anchor1 <- out[[1]]
anchor2 <- out[[2]]
}
new(cls,
anchor1=anchor1,
anchor2=anchor2,
regions=regions,
elementMetadata=elementMetadata,
metadata=as.list(metadata))
}
setMethod("GInteractions", c("numeric", "numeric", "GRanges"),
function(anchor1, anchor2, regions, metadata=list(), mode="normal", ...) {
out <- .new_GInteractions(anchor1, anchor2, regions=regions, metadata=metadata, mode=mode)
extraCols <- DataFrame(...)
if (ncol(extraCols) == 0L) {
extraCols <- make_zero_col_DFrame(length(anchor1))
}
mcols(out) <- extraCols
out
})
.collate_GRanges <- function(...) {
incoming <- list(...)
obj.dex <- rep(factor(seq_along(incoming)), lengths(incoming))
combined <- do.call(c, incoming)
# Sorting and re-indexing.
o <- order(combined)
refdex <- integer(length(o))
refdex[o] <- seq_along(combined)
combined <- combined[o]
# Removing duplicates and re-indexing.
is.first <- !duplicated(combined)
new.pos <- cumsum(is.first)
combined <- combined[is.first]
refdex <- new.pos[refdex]
return(list(indices=split(refdex, obj.dex), ranges=combined))
}
setMethod("GInteractions", c("GRanges", "GRanges", "GenomicRanges_OR_missing"),
function(anchor1, anchor2, regions, metadata=list(), mode="normal", ...) {
# Stripping metadata and putting it somewhere else.
mcol1 <- mcols(anchor1)
mcols(anchor1) <- NULL
colnames(mcol1) <- sprintf("anchor1.%s", colnames(mcol1))
mcol2 <- mcols(anchor2)
mcols(anchor2) <- NULL
colnames(mcol2) <- sprintf("anchor2.%s", colnames(mcol2))
# Additional Interaction-specific metadata
extraCols <- DataFrame(...)
if (ncol(extraCols) == 0L) {
extraCols <- make_zero_col_DFrame(length(anchor1))
}
if (missing(regions)) {
# Making unique regions to save space (metadata is ignored)
collated <- .collate_GRanges(anchor1, anchor2)
regions <- collated$ranges
anchor1 <- collated$indices[[1]]
anchor2 <- collated$indices[[2]]
} else {
anchor1 <- match(anchor1, regions)
anchor2 <- match(anchor2, regions)
if (any(is.na(anchor1)) || any(is.na(anchor2))) {
stop("anchor regions missing in specified 'regions'")
}
}
out <- .new_GInteractions(anchor1=anchor1, anchor2=anchor2,
regions=regions, metadata=metadata, mode=mode)
mcols(out) <- cbind(extraCols, mcol1, mcol2)
out
}
)
setMethod("GInteractions", c("missing", "missing", "GenomicRanges_OR_missing"),
function(anchor1, anchor2, regions, metadata=list(), mode="normal", ...) {
if (missing(regions)) { regions <- GRanges() }
out <- .new_GInteractions(integer(0), integer(0), regions, metadata, mode=mode)
mcols(out) <- DataFrame(...)[integer(0),,drop=FALSE]
out
})
###############################################################
# Concatenation
setMethod("bindROWS", "GInteractions",
function(x, objects=list(), use.names=TRUE, ignore.mcols=FALSE, check=TRUE) {
objects <- S4Vectors:::prepare_objects_to_bind(x, objects)
all.objects <- c(list(x), objects)
# Taken from XVector:::concatenate_XVectorList_objects. Note that the
# object may not be valid at this point due to anchor1/anchor2
# reordering for the Strict subclasses, hence check=FALSE.
ans <- callNextMethod(x, objects, use.names=use.names,
ignore.mcols=ignore.mcols, check=FALSE)
# Coercing everyone to the same region set.
all.regions <- lapply(all.objects, FUN=regions)
all.anchor1 <- lapply(all.objects, FUN=anchor1)
all.anchor2 <- lapply(all.objects, FUN=anchor2)
unified <- .coerce_to_union(all.regions, all.anchor1, all.anchor2)
unchecked_regions(ans) <- unified$region
unchecked_anchor1(ans) <- unlist(unified$anchor1)
unchecked_anchor2(ans) <- unlist(unified$anchor2)
# Coerce to the same strictness, if different inputs were supplied.
# We need to decide whether or not to trigger the validObject() in the new() inside as().
old_val <- S4Vectors:::disableValidity()
if (old_val!=check) {
on.exit(S4Vectors:::disableValidity(old_val))
S4Vectors:::disableValidity(check)
}
as(ans, class(x))
})
.coerce_to_union <- function(all.regions, all.anchor1, all.anchor2) {
issame <- TRUE
if (length(all.regions) > 1L) {
if (length(unique(lengths(all.regions)))>1L) {
issame <- FALSE
} else {
ref <- all.regions[[1]]
for (alt in 2:length(all.regions)) {
if (any(all.regions[[alt]]!=ref)) {
issame <- FALSE
break
}
}
}
}
if (!issame) {
collated <- do.call(.collate_GRanges, all.regions)
for (i in seq_along(all.regions)) {
all.anchor1[[i]] <- collated$indices[[i]][all.anchor1[[i]]]
all.anchor2[[i]] <- collated$indices[[i]][all.anchor2[[i]]]
}
out.range <- collated$ranges
} else {
if (length(all.regions)) {
out.range <- all.regions[[1]]
} else {
out.range <- GRanges()
}
}
return(list(region=out.range, anchor1=all.anchor1, anchor2=all.anchor2))
}
###############################################################
# Other methods
setMethod("order", "GInteractions", function(..., na.last=TRUE, decreasing=FALSE) {
all.ids <- unlist(lapply(list(...), anchors, id=TRUE), recursive=FALSE)
do.call(order, c(all.ids, list(na.last=na.last, decreasing=decreasing)))
})
setMethod("duplicated", "GInteractions", function(x, incomparables=FALSE, fromLast=FALSE, ...)
# Stable sort required here: first entry in 'x' is always non-duplicate if fromLast=FALSE,
# and last entry is non-duplicate if fromLast=TRUE.
{
if (!length(x)) { return(logical(0)) }
a1 <- anchors(x, id=TRUE, type="first")
a2 <- anchors(x, id=TRUE, type="second")
o <- order(a1, a2)
if (fromLast) { o <- rev(o) }
is.dup <- c(FALSE, diff(a1[o])==0L & diff(a2[o])==0L)
is.dup[o] <- is.dup
return(is.dup)
})
setMethod("as.data.frame", "GInteractions", function (x, row.names=NULL, optional=FALSE, ...) {
all1 <- anchors(x, type="first", id=TRUE)
all2 <- anchors(x, type="second", id=TRUE)
used <- logical(length(regions(x)))
used[all1] <- TRUE
used[all2] <- TRUE
new.index <- cumsum(used) # Accelerate by only converting what we need.
regs.dframe <- as.data.frame(unname(regions(x)[used]), optional=optional, ...)
a1.dframe <- regs.dframe[new.index[all1],]
colnames(a1.dframe) <- paste0(colnames(a1.dframe), "1")
a2.dframe <- regs.dframe[new.index[all2],]
colnames(a2.dframe) <- paste0(colnames(a2.dframe), "2")
if (missing(row.names)) { row.names <- names(x) }
data.frame(a1.dframe, a2.dframe, mcols(x), row.names=row.names)
})
###############################################################
# End
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.