# Start of map.R ###############################################################
# as.data.frame.map ------------------------------------------------------------
#' Convert \code{map} to \code{data.frame}.
#'
#' @param x An \pkg{R/qtl} \code{map} object.
#' @param ... Unused arguments.
#' @param map.unit Explicitly sets \code{'map.unit'} attribute.
#'
#' @return A \code{data.frame} corresponding to the input \code{map} object.
#'
#' @export
#' @family map utility functions
#' @method as.data.frame map
#' @rdname as.data.frame.map
as.data.frame.map <- function(x, ..., map.unit=NULL) {
# If map unit specified, update that of object..
if ( ! is.null(map.unit) ) {
x <- setMapUnit(x, map.unit)
} else { # ..otherwise get map unit from object.
map.unit <- getMapUnit(x)
}
validateMap(x)
# Get sequences corresponding to individual map loci.
x.seqs <- pullLocusSeq(x)
# Get individual locus positions.
x.pos <- pullLocusPos(x)
# Get individual locus IDs.
x.ids <- pullLocusIDs(x)
to <- data.frame(chr=x.seqs, pos=x.pos, row.names=x.ids)
to <- orderMap(to)
if ( ! is.na(map.unit) ) {
to <- setMapUnit(to, map.unit)
}
return(to)
}
# as.data.frame.mapframe -------------------------------------------------------
#' Convert \code{mapframe} to \code{data.frame}.
#'
#' @param x A \code{mapframe} object.
#' @param ... Unused arguments.
#'
#' @return A \code{data.frame} corresponding to the input \code{mapframe} object.
#'
#' @export
#' @family map utility functions
#' @method as.data.frame mapframe
#' @rdname as.data.frame.mapframe
as.data.frame.mapframe <- function(x, ...) {
class(x) <- 'data.frame'
return(x)
}
# as.map -----------------------------------------------------------------------
#' Convert object to \code{map}.
#'
#' @param from Object containing map data.
#' @param map.unit Explicitly sets \code{'map.unit'} attribute. This must be
#' specified if an object does not have an existing \code{'map.unit'} attribute.
#'
#' @return An \pkg{R/qtl} \code{map} corresponding to the input object.
#'
#' @template section-map
#'
#' @export
#' @family map utility functions
#' @rdname as.map
as.map <- function(from, map.unit=NULL) {
UseMethod('as.map', from)
}
# as.map.data.frame ------------------------------------------------------------
#' @export
#' @method as.map data.frame
#' @rdname as.map
as.map.data.frame <- function(from, map.unit=NULL) {
if ( nrow(from) == 0 ) {
stop("cannot create map from empty data frame")
}
# If map unit specified, update that of object..
if ( ! is.null(map.unit) ) {
from <- setMapUnit(from, map.unit)
} else { # ..otherwise get map unit from object.
map.unit <- getMapUnit(from)
}
validateMapframe(from)
# Get normalised sequences corresponding to individual map loci.
norm.from.seqs <- normSeq( pullLocusSeq(from) )
# Get individual locus positions.
from.pos <- pullLocusPos(from) # NB: strips any map-unit suffixes.
# Get individual locus IDs.
from.ids <- pullLocusIDs(from)
if ( is.null(from.ids) ) {
stop("cannot create map from data frame - no locus IDs found")
}
# Get normalised map sequences.
norm.map.seqs <- sortSeq( unique(norm.from.seqs) )
if ( length(norm.map.seqs) < const$min.spm ) {
stop("cannot create map from data frame - too few sequences (min=",
const$min.spm, ")")
}
to <- list()
# Set map data for each sequence.
for ( norm.map.seq in norm.map.seqs ) {
# Get row indices of data frame for this sequence.
indices <- which( norm.from.seqs == norm.map.seq )
# Get map positions for this sequence.
seq.pos <- from.pos[indices]
if ( length(seq.pos) < const$min.lps ) {
stop("cannot create map from data frame - sequence has too few loci - '", seq, "'")
}
# Set map position names from locus IDs.
names(seq.pos) <- from.ids[indices]
# Sort map positions in numeric order.
seq.pos <- sort.int(seq.pos)
# Set class of map position vector.
class(seq.pos) <- 'A' # NB: assumes no 'X' chromosomes.
# Set map positions for sequence.
to[[norm.map.seq]] <- seq.pos
}
to <- setMapUnit(to, map.unit)
class(to) <- 'map'
return(to)
}
# as.map.list ------------------------------------------------------------------
#' @export
#' @method as.map list
#' @rdname as.map
as.map.list <- function(from, map.unit=NULL) {
# If map unit specified, update that of object..
if ( ! is.null(map.unit) ) {
from <- setMapUnit(from, map.unit)
} else { # ..otherwise get map unit from object.
map.unit <- getMapUnit(from)
}
validateMap(from)
# Get map sequences.
map.seqs <- names(from)
# Get normalised map sequences.
norm.map.seqs <- normSeq(map.seqs)
# Get vector mapping normalised sequence labels to their original form.
res2map <- structure(map.seqs, names=norm.map.seqs)
to <- list()
# Set map data for each sequence.
for ( norm.map.seq in sortSeq(norm.map.seqs) ) {
# Get map positions for this sequence.
seq.pos <- from[[ res2map[norm.map.seq] ]]
# Sort map positions in numeric order.
seq.pos <- sort.int(seq.pos)
# Set class of map position vector.
class(seq.pos) <- 'A' # NB: assumes no 'X' chromosomes.
# Set map positions for sequence.
to[[norm.map.seq]] <- seq.pos
}
to <- setMapUnit(to, map.unit)
class(to) <- 'map'
return(to)
}
# as.map.map -------------------------------------------------------------------
#' @export
#' @method as.map map
#' @rdname as.map
as.map.map <- function(from, map.unit=NULL) {
if ( ! is.null(map.unit) ) {
from <- setMapUnit(from, map.unit)
}
validateMap(from)
return(from)
}
# as.map.mapframe --------------------------------------------------------------
#' @export
#' @method as.map mapframe
#' @rdname as.map
as.map.mapframe <- function(from, map.unit=NULL) {
return( as.map.data.frame(from, map.unit=map.unit) )
}
# as.map.scanone ---------------------------------------------------------------
#' @export
#' @method as.map scanone
#' @rdname as.map
as.map.scanone <- function(from, map.unit=NULL) {
stopifnot( map.unit == 'cM' )
return( as.map.data.frame(from, map.unit='cM') )
}
# as.mapframe ------------------------------------------------------------------
#' Convert object to \code{mapframe}.
#'
#' @param from Object containing map data.
#' @param map.unit Explicitly sets \code{'map.unit'} attribute. This must be
#' specified if an object does not have an existing \code{'map.unit'} attribute.
#'
#' @return A \code{mapframe} corresponding to the input object.
#'
#' @template section-mapframe
#'
#' @export
#' @family map utility functions
#' @rdname as.mapframe
as.mapframe <- function(from, map.unit=NULL) {
UseMethod('as.mapframe', from)
}
# as.mapframe.data.frame -------------------------------------------------------
#' @export
#' @method as.mapframe data.frame
#' @rdname as.mapframe
as.mapframe.data.frame <- function(from, map.unit=NULL) {
# If map unit specified, update that of object..
if ( ! is.null(map.unit) ) {
from <- setMapUnit(from, map.unit)
} else { # ..otherwise get map unit from object.
map.unit <- getMapUnit(from)
}
validateMapframe(from)
seqcol.index <- getSeqColIndex(from)
poscol.index <- getPosColIndex(from)
# Ensure data frame rows are valid, if present.
if ( nrow(from) > 0 ) {
# Get normalised sequences corresponding to individual map loci.
norm.from.seqs <- normSeq( pullLocusSeq(from) )
# Get individual locus positions.
from.pos <- pullLocusPos(from) # NB: strips any map-unit suffixes.
from[[seqcol.index]] <- norm.from.seqs
from[[poscol.index]] <- from.pos
# Order data frame by sequence, then map position.
from <- from[ order(rankSeq(norm.from.seqs), from.pos), ]
}
# Ensure column headings are as expected for a mapframe.
colnames(from)[ c(seqcol.index, poscol.index) ] <- c('chr', 'pos')
# Ensure sequence and positions columns are leftmost.
if ( seqcol.index != 1 || poscol.index != 2 ) {
column.indices <- getColIndices(from)
other.indices <- column.indices[ -c(seqcol.index, poscol.index) ]
from[, column.indices] <- from[, c( seqcol.index, poscol.index, other.indices)]
}
from <- setMapUnit(from, map.unit)
class(from) <- c('mapframe', 'data.frame')
return(from)
}
# as.mapframe.list -------------------------------------------------------------
#' @export
#' @method as.mapframe list
#' @rdname as.mapframe
as.mapframe.list <- function(from, map.unit=NULL) {
return( as.mapframe( as.map(from, map.unit=map.unit) ) )
}
# as.mapframe.map --------------------------------------------------------------
#' @export
#' @method as.mapframe map
#' @rdname as.mapframe
as.mapframe.map <- function(from, map.unit=NULL) {
return( as.mapframe( as.data.frame(from), map.unit=map.unit ) )
}
# as.mapframe.mapframe ---------------------------------------------------------
#' @export
#' @method as.mapframe mapframe
#' @rdname as.mapframe
as.mapframe.mapframe <- function(from, map.unit=NULL) {
if ( ! is.null(map.unit) ) {
from <- setMapUnit(from, map.unit)
}
validateMapframe(from)
return(from)
}
# as.mapframe.scanone ----------------------------------------------------------
#' @export
#' @method as.mapframe scanone
#' @rdname as.mapframe
as.mapframe.scanone <- function(from, map.unit=NULL) {
stopifnot( map.unit == 'cM' )
return( as.mapframe.data.frame(from, map.unit='cM') )
}
# convertMapUnit ---------------------------------------------------------------
#' Convert map unit and rescale map positions.
#'
#' @param x Object containing map data.
#' @param map.unit New map unit.
#'
#' @return Input object with converted map unit and rescaled map positions.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname convertMapUnit
convertMapUnit <- function(x, map.unit) {
# If object is a data-frame, check that map units have
# been removed from position column names and data.
if ( is.data.frame(x) ) {
if ( ! is.na( getPosColNameMapUnit(x) ) ) {
stop("function 'convertMapUnit' cannot take object with map unit ",
"in column name - consider calling 'setMapUnit' instead")
}
if ( ! is.na( getPosColDataMapUnit(x) ) ) {
stop("function 'convertMapUnit' cannot take object with map unit ",
"in map positions - consider calling 'setMapUnit' instead")
}
}
# Get current mapkey.
map.key <- mapkeyOpt()
# Pull locus mapframe from object.
loc <- pullLoci(x)
# Apply mapkey to loci.
loc <- map.key(loc, map.unit)
# Set map unit directly.
# NB: map unit of object must match the locus mapframe, but we
# cannot set this through setMapUnit as it calls this function.
attr(x, 'map.unit') <- map.unit
# Replace loci of object.
x <- pushLoci(x, loc)
return(x)
}
# extractMarkers ---------------------------------------------------------------
#' Extract marker loci.
#'
#' @param x Object containing map data.
#'
#' @return Input object with pseudomarkers removed.
#'
#' @export
#' @family map utility functions
#' @rdname extractMarkers
extractMarkers <- function(x) {
return( subsetByLocusID(x, isMarkerID) )
}
# extractPseudomarkers ---------------------------------------------------------
#' Extract pseudomarker loci.
#'
#' @param x Object containing map data.
#'
#' @return Input object with only pseudomarkers remaining.
#'
#' @export
#' @family map utility functions
#' @rdname extractPseudomarkers
extractPseudomarkers <- function(x) {
return( subsetByLocusID(x, isPseudomarkerID) )
}
# findFlanking -----------------------------------------------------------------
#' Find map loci flanking map positions.
#'
#' @param x Object containing map data.
#' @param loc Locus \code{mapframe} specifying map positions.
#' @param expandtomarkers Expand the flanking interval to the nearest flanking
#' markers, or to the respective terminal loci.
#'
#' @return A \code{mapframe} containing two loci marking respectively the
#' lower and upper limit of the flanking interval.
#'
#' @export
#' @family map utility functions
#' @rdname findFlanking
findFlanking <- function(x, loc, expandtomarkers=FALSE) {
UseMethod('findFlanking', x)
}
# findFlanking.map -------------------------------------------------------------
#' @export
#' @rdname findFlanking
findFlanking.map <- function(x, loc, expandtomarkers=FALSE) {
stopifnot( 'mapframe' %in% class(loc) )
stopifnot( isBOOL(expandtomarkers) )
# Get map unit.
map.unit <- getMapUnit(x)
if ( is.na(map.unit) ) {
stop("map unit not found")
}
if ( getMapUnit(loc) != map.unit ) {
stop("map unit mismatch")
}
# Get locus mapframe sequence.
loc.seq <- pull.chr(loc)
# Check that locus mapframe refers to one sequence.
# NB: implicitly checks that loc has one or more rows.
stopifnot( length(loc.seq) == 1 )
# Get map sequences.
map.seqs <- names(x)
# Get normalised map sequences.
norm.map.seqs <- normSeq(map.seqs)
stopifnot( loc.seq %in% norm.map.seqs )
# Get vector mapping normalised sequence labels to their original form.
res2map <- structure(map.seqs, names=norm.map.seqs)
# Get all map positions for this sequence.
seq.pos <- x[[ res2map[loc.seq] ]]
exrange <- loc$pos[ ! sapply( loc$pos, inRange, range(seq.pos) ) ]
if ( length(exrange) > 0 ) {
stop("locus positions out of range - ", toString(exrange), "'")
}
if (expandtomarkers) {
# If expanding to markers, create mask in which
# markers and sequence endpoints are TRUE..
loc.mask <- isMarkerID( names(seq.pos) ) |
seq_along(seq.pos) %in% c(1, length(seq.pos))
} else {
# ..otherwise create mask with all TRUE.
loc.mask <- rep_len( TRUE, length(seq.pos) )
}
# Init flanking locus info.
flank.seq <- rep_len(loc.seq, 2)
flank.pos <- loc$pos[ c(1, nrow(loc)) ]
flank.ids <- character(2)
# Get lower flanking locus info.
lower.flanking <- seq.pos[ loc.mask & flank.pos[1] >= seq.pos ]
lower.diff <- flank.pos[1] - lower.flanking
lower.indices <- which( lower.diff == min(lower.diff) )
L <- lower.indices[ length(lower.indices) ]
flank.pos[1] <- lower.flanking[L]
flank.ids[1] <- names(lower.flanking)[L]
# Get upper flanking locus info.
upper.flanking <- seq.pos[ loc.mask & seq.pos >= flank.pos[2] ]
upper.diff <- upper.flanking - flank.pos[2]
upper.indices <- which( upper.diff == min(upper.diff) )
U <- upper.indices[1]
flank.pos[2] <- upper.flanking[U]
flank.ids[2] <- names(upper.flanking)[U]
return( mapframe(chr=flank.seq, pos=flank.pos,
row.names=flank.ids, map.unit=map.unit) )
}
# findFlanking.mapframe --------------------------------------------------------
#' @export
#' @rdname findFlanking
findFlanking.mapframe <- function(x, loc, expandtomarkers=FALSE) {
row.indices <- findFlankingRowIndices(x, loc, expandtomarkers=expandtomarkers)
return( mapframe(chr=as.character(x$chr[row.indices]), pos=x$pos[row.indices],
row.names=rownames(x)[row.indices], map.unit=getMapUnit(x)) )
}
# findFlanking.scanone ---------------------------------------------------------
#' @export
#' @rdname findFlanking
findFlanking.scanone <- function(x, loc, expandtomarkers=FALSE) {
return( findFlanking.mapframe(x, loc, expandtomarkers=expandtomarkers) )
}
# findFlankingRowIndices -------------------------------------------------------
#' Find row index of flanking \code{mapframe} loci.
#'
#' @param x A \code{mapframe} or \code{scanone} object.
#' @param loc Locus \code{mapframe} specifying map positions.
#' @param expandtomarkers Expand the flanking interval to the nearest flanking
#' markers, or to the respective terminal loci.
#'
#' @return Vector containing the row indices of flanking loci.
#'
#' @keywords internal
#' @rdname findFlankingRowIndices
findFlankingRowIndices <- function(x, loc, expandtomarkers=FALSE) {
stopifnot( 'mapframe' %in% class(x) || 'scanone' %in% class(x) )
stopifnot( 'mapframe' %in% class(loc) )
stopifnot( isBOOL(expandtomarkers) )
# Get map unit.
map.unit <- getMapUnit(x)
if ( is.na(map.unit) ) {
stop("map unit not found")
}
if ( getMapUnit(loc) != map.unit ) {
stop("map unit mismatch")
}
if ( nrow(x) == 0 ) {
stop("cannot find flanking row indices in mapframe with zero rows")
}
# Get normalised sequences corresponding to individual map loci.
x.seqs <- normSeq( pullLocusSeq(x) )
# Get locus mapframe sequence.
loc.seq <- pull.chr(loc)
# Check that loc mapframe refers to one sequence.
# NB: implicitly checks that loc has one or more rows.
stopifnot( length(loc.seq) == 1 )
# Get row indices of sequence loci.
indices <- which( x.seqs == loc.seq )
stopifnot( length(indices) > 0 )
# Get map positions for this sequence.
seq.pos <- x$pos[indices]
exrange <- loc$pos[ ! sapply( loc$pos, inRange, range(seq.pos) ) ]
if ( length(exrange) > 0 ) {
stop("locus positions out of range - ", toString(exrange), "'")
}
if ( expandtomarkers && hasRownames(x) ) {
# If expanding to markers and mapframe has locus IDs, create
# a mask in which markers and sequence endpoints are TRUE..
loc.mask <- isMarkerID(rownames(x)[indices]) |
seq_along(seq.pos) %in% c( 1, length(seq.pos) )
} else {
# ..otherwise create mask with all TRUE.
loc.mask <- rep_len( TRUE, length(seq.pos) )
}
# Get lower flanking locus index.
lower.pos <- loc$pos[1]
lower.flanking <- seq.pos[ loc.mask & lower.pos >= seq.pos ]
lower.diff <- lower.pos - lower.flanking
lower.indices <- which( lower.diff == min(lower.diff) )
lower.index <- lower.indices[ length(lower.indices) ] + min(indices) - 1
# Get upper flanking locus index.
upper.pos <- loc$pos[ nrow(loc) ]
upper.flanking <- seq.pos[ loc.mask & seq.pos >= upper.pos ]
upper.diff <- upper.flanking - upper.pos
upper.indices <- which( upper.diff == min(upper.diff) )
upper.index <- upper.indices[1] + min(indices) - 1 + length(indices) - length(upper.flanking)
return( c(lower.index, upper.index) )
}
# findLoci ---------------------------------------------------------------------
#' Find closest map loci.
#'
#' @param x Object containing map data.
#' @param loc Locus \code{mapframe} specifying map positions.
#'
#' @return A \code{mapframe} whose rows each contain the closest locus to the
#' map position specified in the corresponding row of the locus \code{mapframe}.
#'
#' @export
#' @family map utility functions
#' @rdname findLoci
findLoci <- function(x, loc) {
stopifnot( 'mapframe' %in% class(loc) )
prox.loci <- data.frame( matrix( ncol=2, nrow=nrow(loc),
dimnames=list(NULL, c('chr', 'pos')) ) )
# Find closest locus to each map position.
for ( i in getRowIndices(loc) ) {
# Get loci flanking this map position.
flanking <- findFlanking(x, loc[i, ])
# Get closest locus, taking the first in case of ties.
prox.loci[i, ] <- flanking[ which.min( abs(loc$pos[i] - flanking$pos) ), ]
}
return( as.mapframe(prox.loci, getMapUnit(loc)) )
}
# findLocusRowIndices ----------------------------------------------------------
#' Find row index of closest \code{mapframe} locus.
#'
#' @param x A \code{mapframe} object.
#' @param loc Locus \code{mapframe} specifying map positions.
#'
#' @return Vector of row indices, each element of which indicates the row of the
#' target \code{mapframe} object containing the closest locus to the map position
#' specified in the corresponding row of the locus \code{mapframe}.
#'
#' @keywords internal
#' @rdname findLocusRowIndices
findLocusRowIndices <- function(x, loc) {
stopifnot( 'mapframe' %in% class(loc) )
prox.indices <- integer( nrow(loc) )
# Find row indices of closest locus to each specified map position.
for ( i in getRowIndices(loc) ) {
# Get row indices of flanking loci.
row.indices <- findFlankingRowIndices(x, loc[i, ])
# Get row index of closest locus, taking the first in case of ties.
prox.indices[i] <- row.indices[ which.min( abs(loc$pos[i] - x$pos[row.indices]) ) ]
}
return(prox.indices)
}
# findMarkers ------------------------------------------------------------------
#' Find closest markers.
#'
#' @param x Object containing map data.
#' @param loc Locus \code{mapframe} specifying map positions.
#'
#' @return A \code{mapframe} whose rows each contain the closest marker to the
#' map position specified in the corresponding row of the locus \code{mapframe}.
#'
#' @export
#' @family map utility functions
#' @rdname findMarkers
findMarkers <- function(x, loc) {
findLoci(extractMarkers(x), loc)
}
# getSeqColIndex ---------------------------------------------------------------
#' Get sequence column index.
#'
#' Get sequence column index for a \code{mapframe} or equivalent
#' \code{data.frame}. The sequence column is taken to be the column
#' whose heading is \code{'chr'}. An error is raised if there is not
#' exactly one sequence column.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame}.
#'
#' @return Sequence column index.
#'
#' @keywords internal
#' @rdname getSeqColIndex
getSeqColIndex <- function(x) {
stopifnot( is.data.frame(x) )
stopif( anyDuplicated( colnames(x) ) )
seqcol.indices <- which( colnames(x) == 'chr' )
if ( length(seqcol.indices) == 1 ) {
seqcol.index <- seqcol.indices[1]
} else if ( length(seqcol.indices) == 0 ) {
stop("no sequence columns found")
} else {
stop("multiple sequence columns found")
}
return(seqcol.index)
}
# getDatColIndices -------------------------------------------------------------
#' Get data column indices.
#'
#' Get data column indices for a \code{mapframe} or equivalent \code{data.frame}.
#' Data columns are taken to be any columns that aren't a sequence or position
#' column.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame}.
#' @param datcolumns Optional parameter indicating which data columns to
#' consider. This must be a character vector of data column names, a logical
#' vector with length equal to the number of data columns, or a numeric vector
#' of indices \emph{with respect to the set of data columns}. If no data columns
#' are specified, all are considered.
#' @param strict Option indicating that the specified data columns must
#' be in the same order as they are in the \code{mapframe} object.
#'
#' @return Vector of data column indices.
#'
#' @keywords internal
#' @rdname getDatColIndices
getDatColIndices <- function(x, datcolumns=NULL, strict=FALSE) {
stopifnot( is.data.frame(x) )
column.indices <- getColIndices(x)
seqcol.mask <- column.indices == getSeqColIndex(x)
poscol.mask <- column.indices == getPosColIndex(x)
available <- column.indices[ ! ( seqcol.mask | poscol.mask ) ]
names(available) <- colnames(x)[available]
resolved <- getIndices(available, requested=datcolumns, strict=strict)
indices <- unname(available[resolved])
return(indices)
}
# getMapSteps ------------------------------------------------------------------
#' Get steps between loci.
#'
#' @param x Object containing map data.
#'
#' @return List of vectors, each element named for a given reference sequence,
#' and containing a vector of the step sizes between markers on that sequence.
#'
#' @export
#' @family map utility functions
#' @rdname getMapSteps
getMapSteps <- function(x) {
UseMethod('getMapSteps', x)
}
# getMapSteps.mapframe ---------------------------------------------------------
#' @export
#' @rdname getMapSteps
getMapSteps.mapframe <- function(x) {
# Get sequences corresponding to individual map loci.
x.seqs <- pullLocusSeq(x)
# Get individual locus positions.
x.pos <- pullLocusPos(x)
# Get map sequences.
map.seqs <- unique(x.seqs)
# Get map steps.
map.steps <- lapply(map.seqs, function(map.seq)
diff(x.pos[ x.seqs == map.seq ]) )
names(map.steps) <- map.seqs
return(map.steps)
}
# getMapSteps.map --------------------------------------------------------------
#' @export
#' @rdname getMapSteps
getMapSteps.map <- function(x) {
return( lapply(x, function(seq.pos) diff( as.numeric(seq.pos) ) ) )
}
# getMapSteps.scanone ----------------------------------------------------------
#' @export
#' @rdname getMapSteps
getMapSteps.scanone <- function(x) {
return( getMapSteps.mapframe(x) )
}
# getMapUnit -------------------------------------------------------------------
#' Get map unit.
#'
#' Get the map unit of the object. In most cases, this is taken from the
#' \code{'map.unit'} attribute of the object. For a \code{data.frame}, the
#' position column is also checked for map unit information; an error is
#' raised if \code{data.frame} map unit information is inconsistent.
#'
#' @param x Object containing map data.
#'
#' @return Map unit. Returns \code{NA} if map unit information not found.
#'
#' @template section-mapunits
#'
#' @export
#' @family map utility functions
#' @rdname getMapUnit
getMapUnit <- function(x) {
UseMethod('getMapUnit', x)
}
# getMapUnit.data.frame --------------------------------------------------------
#' @export
#' @rdname getMapUnit
getMapUnit.data.frame <- function(x) {
map.unit <- NA_character_
existing.mapunit <- getMapUnit.default(x)
heading.mapunit <- getPosColNameMapUnit(x)
positions.mapunit <- getPosColDataMapUnit(x)
for ( cue in list(existing.mapunit, heading.mapunit, positions.mapunit) ) {
if ( ! is.null(cue) && ! is.na(cue) ) {
if ( is.na(map.unit) ) {
map.unit <- cue
} else if ( cue != map.unit ) {
stop("data frame has inconsistent map unit information")
}
}
}
return(map.unit)
}
# getMapUnit.default -----------------------------------------------------------
#' @export
#' @rdname getMapUnit
getMapUnit.default <- function(x) {
map.unit <- attr(x, 'map.unit')
if ( is.null(map.unit) ) {
map.unit <- NA_character_
}
return(map.unit)
}
# getMapUnit.map ---------------------------------------------------------------
#' @export
#' @rdname getMapUnit
getMapUnit.map <- function(x) {
map.unit <- getMapUnit.default(x)
if ( is.na(map.unit) ) {
map.unit <- 'cM'
}
return(map.unit)
}
# getMapUnit.mapframe ----------------------------------------------------------
#' @export
#' @rdname getMapUnit
getMapUnit.mapframe <- function(x) {
return( getMapUnit.default(x) )
}
# getMapUnit.scanone -----------------------------------------------------------
#' @export
#' @rdname getMapUnit
getMapUnit.scanone <- function(x) {
map.unit <- getMapUnit.default(x)
if ( ! is.na(map.unit) && map.unit != 'cM' ) {
stop("scanone result has unexpected map unit - '", map.unit, "'")
}
return('cM')
}
# getMapUnitSuffix -------------------------------------------------------------
#' Get map unit suffix from a vector of map positions.
#'
#' @param x A character vector of map positions.
#'
#' @return Map unit indicated by the map positions. Returns \code{NA}
#' if the map position vector does not contain map unit suffixes.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname getMapUnitSuffix
getMapUnitSuffix <- function(x) {
stopifnot( is.character(x) )
map.unit <- NA_character_
map.type <- NULL
if ( length(x) > 0 ) {
positions <- stripWhite( unname(x) )
# Create genetic pos matrix, in which rows correspond to
# loci and columns correspond to genetic map patterns.
pos.matrix <- vapply( const$pattern$gmap, function(p)
sapply(positions, grepl, pattern=p, ignore.case=TRUE),
logical( length(positions) ) )
# If genetic pos matrix was simplified to a vector (as
# for a single map position), restore to a matrix.
if ( ! is.matrix(pos.matrix) ) {
pos.matrix <- matrix(pos.matrix, nrow=length(positions),
dimnames=list(positions, const$pattern$gmap) )
}
# For each locus, get column indices of
# first matching genetic map pattern.
unit.indices <- unique( sapply(seq_along(positions), function(i)
match(TRUE, pos.matrix[i, ]) ) )
if ( length(unit.indices) == 1 ) {
if ( ! is.na(unit.indices) ) {
map.type <- 'gmap'
}
} else {
stop("map positions have inconsistent/incomplete map unit information")
}
if ( is.null(map.type) ) {
# Create physical pos matrix, in which rows correspond to
# loci and columns correspond to physical map patterns.
pos.matrix <- vapply( const$pattern$pmap, function(p)
sapply(positions, grepl, pattern=p, ignore.case=TRUE),
logical( length(positions) ) )
# If physical pos matrix was simplified to a vector
# (as for a single map position), restore to a matrix.
if ( ! is.matrix(pos.matrix) ) {
pos.matrix <- matrix(pos.matrix, nrow=length(positions),
dimnames=list(positions, const$pattern$pmap) )
}
# For each locus, get column indices of first matching genetic
# map pattern. NB: must check in order (Mb > kb > bp).
unit.indices <- unique( sapply(seq_along(positions), function(i)
match(TRUE, pos.matrix[i, ])) )
if ( length(unit.indices) == 1 ) {
if ( ! is.na(unit.indices) ) {
map.type <- 'pmap'
}
} else {
stop("map positions have inconsistent/incomplete map unit information")
}
}
# Update map unit, if identified.
if ( ! is.null(map.type) ) {
map.unit <- rownames(const$map.info[[map.type]])[ unit.indices[1] ]
}
}
return(map.unit)
}
# getPosColDataMapUnit ---------------------------------------------------------
#' Get map unit from position column values.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame},
#' or a vector of map positions.
#'
#' @return Map unit indicated by the position column data. Returns
#' \code{NA} if map unit information not found in position column data.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname getPosColDataMapUnit
getPosColDataMapUnit <- function(x) {
UseMethod('getPosColDataMapUnit', x)
}
# getPosColDataMapUnit.character -----------------------------------------------
#' @rdname getPosColDataMapUnit
getPosColDataMapUnit.character <- function(x) {
return( getMapUnitSuffix(x) )
}
# getPosColDataMapUnit.data.frame ----------------------------------------------
#' @rdname getPosColDataMapUnit
getPosColDataMapUnit.data.frame <- function(x) {
return( getMapUnitSuffix( as.character(x[, getPosColIndex(x)]) ) )
}
# getPosColDataMapUnit.numeric -------------------------------------------------
#' @rdname getPosColDataMapUnit
getPosColDataMapUnit.numeric <- function(x) {
return(NA_character_)
}
# getPosColIndex ---------------------------------------------------------------
#' Get \code{mapframe} position column index.
#'
#' Get position column index for a \code{mapframe} or equivalent \code{data.frame}.
#' The position column is taken to be the leftmost matching column, where a
#' matching column has a heading containing the word \code{'pos'}. This can
#' also be in uppercase (i.e. \code{'POS'}), but can't be part of a larger
#' word, such as \code{'position'}. An error is raised if a position column
#' cannot be found.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame}.
#' @param nmax Maximum number of matching position columns. If specified, an
#' error is raised if the number of matching position columns is greater than
#' this maximum.
#'
#' @return Position column index.
#'
#' @keywords internal
#' @rdname getPosColIndex
getPosColIndex <- function(x, nmax=NULL) {
stopifnot( is.data.frame(x) )
stopif( anyDuplicated( colnames(x) ) )
poscol.indices <- which( grepl(const$pattern$poscol, colnames(x),
ignore.case=TRUE) )
if ( length(poscol.indices) == 0 ) {
stop("pos column not found")
}
if ( ! is.null(nmax) ) {
stopifnot( isSinglePositiveWholeNumber(nmax) )
if ( length(poscol.indices) > nmax ) {
stop("number of matching pos columns (", length(poscol.indices),
") exceeds maximum (", nmax, ")")
}
}
return(poscol.indices[1])
}
# getPosColNameMapUnit ---------------------------------------------------------
#' Get map unit from position column heading.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame},
#' or a position column heading.
#'
#' @return Map unit indicated by the position column heading. Returns
#' \code{NA} if map unit information not found in position column heading.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname getPosColNameMapUnit
getPosColNameMapUnit <- function(x) {
UseMethod('getPosColNameMapUnit', x)
}
# getPosColNameMapUnit.character -----------------------------------------------
#' @rdname getPosColNameMapUnit
getPosColNameMapUnit.character <- function(x) {
stopifnot( length(x) == 1 )
stopifnot( grepl(const$pattern$poscol, x, ignore.case=TRUE) )
map.unit <- NA_character_
poscol.words <- unlist( strsplit(x, '[^[:alpha:]]+') )
mapunit.mask <- const$known.map.units %in% poscol.words
matching.map.units <- const$known.map.units[mapunit.mask]
if ( length(matching.map.units) == 1 ) {
map.unit <- matching.map.units[1]
} else if ( length(matching.map.units) > 1 ) {
stop("pos column heading contains multiple map units - '", x, "'")
}
return(map.unit)
}
# getPosColNameMapUnit.data.frame ----------------------------------------------
#' @rdname getPosColNameMapUnit
getPosColNameMapUnit.data.frame <- function(x) {
return( getPosColNameMapUnit(colnames(x)[ getPosColIndex(x) ]) )
}
# gmapframe --------------------------------------------------------------------
#' Create a new genetic \code{mapframe}.
#'
#' @param ... Further arguments. These are passed to the \code{data.frame}
#' constructor. All arguments should be passed as keyword arguments.
#'
#' @return A new \code{mapframe} with genetic map positions.
#'
#' @template section-mapframe
#'
#' @export
#' @family map utility functions
#' @rdname gmapframe
gmapframe <- function(...) {
return( mapframe(map.unit='cM', ...) )
}
# inferMapStep -----------------------------------------------------------------
#' Infer map step size.
#'
#' @param x Object containing map data.
#' @param tol Tolerance for step equality.
#'
#' @return Inferred map step. Returns \code{NULL}
#' if the map step could not be inferred.
#'
#' @export
#' @family map utility functions
#' @rdname inferMapStep
inferMapStep <- function(x, tol=1e-5) {
map.steps <- unlist( getMapSteps(x) )
map.step <- inferStepSize(map.steps)
return(map.step)
}
# inMapOrder -------------------------------------------------------------------
#' Test if object is in map order.
#'
#' @param x Object containing map data.
#'
#' @return \code{TRUE} if map data of object is ordered by
#' sequence, then map position; \code{FALSE} otherwise.
#'
#' @export
#' @family map utility functions
#' @rdname inMapOrder
inMapOrder <- function(x) {
UseMethod('inMapOrder', x)
}
# inMapOrder.data.frame --------------------------------------------------------
#' @export
#' @rdname inMapOrder
inMapOrder.data.frame <- function(x) {
if ( nrow(x) > 0 ) {
x.seqs <- pullLocusSeq(x)
x.pos <- pullLocusPos(x)
if ( is.unsorted( orderSeq(x.seqs) ) ) {
return(FALSE)
}
map.seqs <- unique(x.seqs)
for ( map.seq in map.seqs ) {
if ( is.unsorted(x.pos[ x.seqs == map.seq ]) ) {
return(FALSE)
}
}
}
return(TRUE)
}
# inMapOrder.map ---------------------------------------------------------------
#' @export
#' @rdname inMapOrder
inMapOrder.map <- function(x) {
if ( is.unsorted( orderSeq( names(x) ) ) ) {
return(FALSE)
}
if ( any( sapply(x, is.unsorted) ) ) {
return(FALSE)
}
return(TRUE)
}
# intersectLoci ----------------------------------------------------------------
#' Find intersection set of loci.
#'
#' @param ... One or more objects containing map data.
#'
#' @return Locus \code{mapframe} with loci common to all input objects.
#'
#' @keywords internal
#' @rdname intersectLoci
intersectLoci <- function(...) {
loc.list <- list(...)
stopifnot( length(loc.list) > 0 )
intersection <- pullLoci(loc.list[[1]])
if ( length(loc.list) > 1 ) {
for ( i in 2:length(loc.list) ) {
intersection <- matchLoci(intersection, loc.list[[i]])
}
}
return(intersection)
}
# isGeneticMap -----------------------------------------------------------------
#' Test if object is a genetic \code{map}.
#'
#' @param x Test object.
#'
#' @return \code{TRUE} if object is a genetic \code{map} object;
#' \code{FALSE} otherwise.
#'
#' @keywords internal
#' @rdname isGeneticMap
isGeneticMap <- function(x) {
if ( 'map' %in% class(x) ) {
map.unit <- getMapUnit(x)
if ( ! is.na(map.unit) && const$known.map.types[map.unit] == 'gmap' ) {
return(TRUE)
}
}
return(FALSE)
}
# isGeneticMapframe ------------------------------------------------------------
#' Test if object is a genetic \code{mapframe}.
#'
#' @param x Test object.
#'
#' @return \code{TRUE} if object is a \code{mapframe} whose pos column contains
#' genetic map positions; \code{FALSE} otherwise.
#'
#' @keywords internal
#' @rdname isGeneticMapframe
isGeneticMapframe <- function(x) {
if ( 'mapframe' %in% class(x) ) {
map.unit <- getMapUnit(x)
if ( ! is.na(map.unit) && const$known.map.types[map.unit] == 'gmap' ) {
return(TRUE)
}
}
return(FALSE)
}
# isGeneticMapUnit -------------------------------------------------------------
#' Test if object is a known genetic map unit.
#'
#' @param x Test object.
#'
#' @return \code{TRUE} if object is a known genetic map unit;
#' \code{FALSE} otherwise.
#'
#' @keywords internal
#' @rdname isGeneticMapUnit
isGeneticMapUnit <- function(x) {
status <- tryCatch({
result <- validateMapUnit(x, map.type='gmap')
}, error=function(e) {
result <- FALSE
})
return(status)
}
# isPhysicalMap ----------------------------------------------------------------
#' Test if object is a physical \code{map}.
#'
#' @param x Test object.
#'
#' @return \code{TRUE} if object is a physical \code{map};
#' \code{FALSE} otherwise.
#'
#' @keywords internal
#' @rdname isPhysicalMap
isPhysicalMap <- function(x) {
if ( 'map' %in% class(x) ) {
map.unit <- getMapUnit(x)
if ( ! is.na(map.unit) && const$known.map.types[map.unit] == 'pmap' ) {
return(TRUE)
}
}
return(FALSE)
}
# isPhysicalMapframe -----------------------------------------------------------
#' Test if object is a physical \code{mapframe}.
#'
#' @param x Test object.
#'
#' @return \code{TRUE} if object is a \code{mapframe} whose pos column contains
#' physical map positions; \code{FALSE} otherwise.
#'
#' @keywords internal
#' @rdname isPhysicalMapframe
isPhysicalMapframe <- function(x) {
if ( 'mapframe' %in% class(x) ) {
map.unit <- getMapUnit(x)
if ( ! is.na(map.unit) && const$known.map.types[map.unit] == 'pmap' ) {
return(TRUE)
}
}
return(FALSE)
}
# isPhysicalMapUnit ------------------------------------------------------------
#' Test if object is a known physical map unit.
#'
#' @param x Test object.
#'
#' @return \code{TRUE} if object is a known physical map unit;
#' \code{FALSE} otherwise.
#'
#' @keywords internal
#' @rdname isPhysicalMapUnit
isPhysicalMapUnit <- function(x) {
status <- tryCatch({
status <- validateMapUnit(x, map.type='pmap')
}, error=function(e) {
result <- FALSE
})
return(status)
}
# isValidMapUnit ---------------------------------------------------------------
#' Test if object is a known map unit.
#'
#' @param x Test object.
#'
#' @return \code{TRUE} if object is a known genetic or physical map unit;
#' \code{FALSE} otherwise.
#'
#' @keywords internal
#' @rdname isValidMapUnit
isValidMapUnit <- function(x) {
status <- tryCatch({
result <- validateMapUnit(x)
}, error=function(e) {
result <- FALSE
})
return(status)
}
# makeMapFromDefaultMarkerIDs --------------------------------------------------
#' Make map from default marker IDs.
#'
#' @param marker.ids Vector of default marker IDs.
#'
#' @return An \pkg{R/qtl} \code{map} object.
#'
#' @template section-map
#'
#' @export
#' @keywords internal
#' @rdname makeMapFromDefaultMarkerIDs
makeMapFromDefaultMarkerIDs <- function(marker.ids) {
return( as.map( parseDefaultMarkerIDs(marker.ids) ) )
}
# makeMapFromDefaultQTLNames ---------------------------------------------------
#' Make map from default QTL names.
#'
#' @param qtl.names Vector of default QTL names.
#'
#' @return An \pkg{R/qtl} \code{map} object.
#'
#' @template section-map
#'
#' @export
#' @keywords internal
#' @rdname makeMapFromDefaultQTLNames
makeMapFromDefaultQTLNames <- function(qtl.names) {
return( as.map( parseDefaultQTLNames(qtl.names) ) )
}
# makeMapFromLocusIDs ----------------------------------------------------------
#' Make map from locus IDs.
#'
#' @param ids Vector of default marker IDs,
#' default QTL names, or pseudomarker IDs.
#'
#' @return An \pkg{R/qtl} \code{map} object. The returned object is a genetic
#' map if the input \code{ids} are default QTL names or pseudomarkers. A
#' physical map is returned if the input \code{ids} are default marker IDs.
#'
#' @template section-map
#'
#' @export
#' @rdname makeMapFromLocusIDs
makeMapFromLocusIDs <- function(ids) {
if ( all( isDefaultMarkerID(ids) ) ) {
m <- makeMapFromDefaultMarkerIDs(ids)
} else if ( all( isDefaultQTLName(ids) ) ) {
m <- makeMapFromDefaultQTLNames(ids)
} else if ( all( isPseudomarkerID(ids) ) ) {
m <- makeMapFromPseudomarkerIDs(ids)
} else {
stop("cannot make map from IDs of unknown type")
}
return(m)
}
# makeMapFromPseudomarkerIDs ---------------------------------------------------
#' Make map from pseudomarker IDs.
#'
#' @template section-map
#'
#' @param loc.ids Vector of pseudomarker IDs.
#'
#' @return An \pkg{R/qtl} \code{map} object.
#'
#' @export
#' @keywords internal
#' @rdname makeMapFromPseudomarkerIDs
makeMapFromPseudomarkerIDs <- function(loc.ids) {
return( as.map( parsePseudomarkerIDs(loc.ids) ) )
}
# makePlaceholderMap -----------------------------------------------------------
#' Make a placeholder map.
#'
#' Make a placeholder map from the given combination of locus sequences, map
#' unit, and map step size. If locus IDs are specified, the locus IDs of the
#' placeholder map will be set from these. Otherwise, locus IDs are set to
#' pseudomarker IDs or default marker IDs, according to whether map units
#' are for a genetic or physical map, respectively.
#'
#' Locus sequences and locus IDs are assumed to be ordered. As in \pkg{R/qtl},
#' any sequence with too few loci is extended, to ensure that all sequences
#' have enough loci.
#'
#' @param locus.seqs Sequences corresponding to individual loci.
#' @param locus.ids Individual locus IDs.
#' @param map.unit Map unit.
#' @param step Map step size.
#'
#' @template section-map
#'
#' @keywords internal
#' @rdname makePlaceholderMap
makePlaceholderMap <- function(locus.seqs, locus.ids=NULL, map.unit='cM',
step=5) {
stopifnot( is.character(locus.seqs) )
stopifnot( length(locus.seqs) > 0 )
stopifnot( isValidMapUnit(map.unit) )
stopifnot( isSinglePositiveNumber(step) )
# Get normalised locus sequences.
norm.locus.seqs <- normSeq(locus.seqs)
# Test if map unit is for a genetic map.
is.gmap <- isGeneticMapUnit(map.unit)
# Get run-length encoding of normalised locus sequences.
runs <- rle(norm.locus.seqs)
# Check for any sequence label that is
# not grouped together in a single run.
if ( anyDuplicated(runs$values) ) {
stop("map sequences are not ordered")
}
# Get placeholder-map sequences.
map.seqs <- runs$values
# Get index list for locus sequences.
# NB: groups indices by sequence.
index.list <- getRunIndexList(norm.locus.seqs)
# Generate placeholder locus positions.
locus.pos <- unlist( lapply( unname(index.list), function(indices)
seq(0, length=length(indices), by=step)) )
# Create placeholder mapframe.
placeholder <- mapframe(chr=norm.locus.seqs, pos=locus.pos,
map.unit=map.unit)
# If locus IDs specified, set from those..
if ( ! is.null(locus.ids) ) {
stopifnot( is.character(locus.ids) )
stopifnot( length(locus.ids) == length(locus.seqs) )
rownames(placeholder) <- locus.ids
} else { # ..otherwise generate default locus IDs.
if (is.gmap) {
rownames(placeholder) <- makePseudomarkerIDs(placeholder)
} else {
rownames(placeholder) <- makeDefaultMarkerIDs(placeholder)
}
}
# Check placeholder mapframe for short sequences.
short.seqs <- map.seqs[ sapply(getRunIndices(runs),
function(i) runs$lengths[i] < const$min.lps) ]
# If placeholder mapframe contains short sequences,
# extend these until they have enough loci.
if ( length(short.seqs) > 0 ) {
short.flank.seqs <- character()
short.flank.pos <- character()
# Get flanking locus sequences and
# positions for each short sequence.
for ( short.seq in short.seqs ) {
# Get loci in this sequence.
loc <- placeholder[ placeholder$chr == short.seq, ]
# Calculate shortfall in number of loci.
shortfall <- const$min.lps - nrow(loc)
# Get number of flanking steps needed to get enough loci.
num.steps <- (shortfall + 1) %/% 2
# Generate sequence of flanking positions in each direction.
lower.flank.pos <- seq(from=loc$pos[1] - step, by=-step,
length.out=num.steps)
upper.flank.pos <- seq(from=loc$pos[ nrow(loc) ] + step,
by=step, length.out=num.steps)
# Set flanking locus info for this sequence.
flank.pos <- c(lower.flank.pos, upper.flank.pos)
flank.seqs <- rep(loc$chr[1], length(flank.pos))
# Append to flanking locus info for all short sequences.
short.flank.seqs <- c(short.flank.seqs, flank.seqs)
short.flank.pos <- c(short.flank.pos, flank.pos)
}
# Create mapframe of flanking loci.
flanking <- mapframe(chr=short.flank.seqs, pos=short.flank.pos,
map.unit=map.unit)
# Generate default locus IDs.
if (is.gmap) {
rownames(flanking) <- makePseudomarkerIDs(flanking)
} else {
rownames(flanking) <- makeDefaultMarkerIDs(flanking)
}
placeholder <- rbind(placeholder, flanking)
placeholder <- orderMap(placeholder)
}
return( as.map(placeholder) )
}
# mapframe ---------------------------------------------------------------------
#' Create a new \code{mapframe}.
#'
#' @param map.unit Map unit for the new \code{mapframe}.
#' @param ... Further arguments. These are passed to the \code{data.frame}
#' constructor. All arguments should be passed as keyword arguments.
#'
#' @return A new \code{mapframe}.
#'
#' @template section-mapframe
#'
#' @export
#' @family map utility functions
#' @rdname mapframe
mapframe <- function(map.unit=NULL, ...) {
stopifnot( allKwargs(...) )
args <- list(...)
mapframe.colnames <- names(args)[ ! names(args) %in% const$dataframe.keywords ]
prev.state <- getOption('stringsAsFactors')
options(stringsAsFactors=FALSE)
if ( length(mapframe.colnames) == 0 ) {
x <- data.frame(chr=character(), pos=numeric(), ...)
} else {
x <- data.frame(...)
}
options(stringsAsFactors=prev.state)
return( as.mapframe(x, map.unit=map.unit) )
}
# mapkey -----------------------------------------------------------------------
#' Make new \code{mapkey} rescaling function.
#'
#' This function takes as input one or more maps - one of each supported
#' map type - and returns a function that can be used to rescale a locus
#' \code{mapframe} of map positions. The input maps must be comparable,
#' having the same set of markers on the same chromosomes, although the
#' marker names may be different. As in \pkg{R/qtl}, map positions are
#' interpolated if they are within the bounds of the component maps, and
#' extrapolated otherwise.
#'
#' @param ... One or more component maps, all with comparable marker loci.
#'
#' @return A \code{mapkey} function similar to the \pkg{R/qtl} function
#' \code{interpPositions}, taking a locus \code{mapframe} and map unit
#' parameter, and returning a \code{mapframe} that has been rescaled in
#' terms of the given map unit.
#'
#' @template ref-broman-2003
#'
#' @export
#' @family mapkey functions
#' @rdname mapkey
mapkey <- function(...) {
stopif( anyKwargs(...) )
maps <- list(...)
# If component maps specified, generate mapkey object..
if ( length(maps) > 0 ) {
# Ensure all maps are valid map objects,
# converting to a map object if necessary.
maps <- lapply(maps, as.map)
# Ensure all maps have normalised sequence labels.
maps <- lapply(maps, normSeq)
# Get map units.
map.units <- unname( sapply(maps, getMapUnit) )
if ( anyNA(map.units) ) {
stop("mapkey component maps must have valid map units")
}
# TODO: relax limitation of maps to basic map units.
if ( ! all(map.units %in% const$basic.map.unit) ) {
stop("mapkey component maps must be in basic map units")
}
# Get map types from map unit information.
map.types <- unname( sapply(map.units, function(map.unit)
const$known.map.types[[map.unit]]) )
# Check that each map type is represented only once.
stopif( anyDuplicated(map.types) )
# Set mapkey names from map units.
names(maps) <- map.units # NB: assumes all maps use different units
# Check that all maps contain the same sequences in the same order.
if ( ! all( sapply(maps[-1], function(m)
identical(names(m), names(maps[[1]])) ) ) ) {
stop("sequence mismatch between mapkey component maps")
}
# Check that all maps have a consistent number of markers in each sequence.
if ( ! all( sapply(maps[-1], function(m)
identical(lengths(m), lengths(maps[[1]])) ) ) ) {
stop("marker count mismatch between mapkey component maps")
}
map.key <- function(loc, map.unit) {
stopifnot( 'mapframe' %in% class(loc) )
validateMapUnit(map.unit)
# Get locus info.
locus.seqs <- pullLocusSeq(loc)
locus.pos <- pullLocusPos(loc)
# Get old and new map units.
old.map.unit <- getMapUnit(loc)
validateMapUnit(old.map.unit)
new.map.unit <- map.unit
# Get old and new map types.
old.map.type <- const$known.map.types[old.map.unit]
new.map.type <- const$known.map.types[new.map.unit]
# Get basic units of old and new map types.
old.basic.unit <- const$basic.map.unit[old.map.type]
new.basic.unit <- const$basic.map.unit[new.map.type]
stopifnot( old.basic.unit %in% names(maps) )
stopifnot( new.basic.unit %in% names(maps) )
# Get old and new maps.
oldmap <- maps[[old.basic.unit]]
newmap <- maps[[new.basic.unit]]
# Check that all sequences are in mapkey.
loc.seqs <- unique(locus.seqs)
missing.seqs <- loc.seqs[ ! loc.seqs %in% names(oldmap) ]
if ( length(missing.seqs) > 0 ) {
stop("sequences not found in mapkey - '", toString(missing.seqs), "'")
}
# Convert each locus position.
for ( i in getRowIndices(loc) ) {
# Get this locus position.
x <- locus.pos[i]
# Convert locus position to basic units, if needed.
if ( old.map.unit != old.basic.unit ) {
x <- x * const$map.info[[old.map.type]][old.map.unit, 'factor']
}
# Convert locus position from one map type to another, if needed.
if ( old.basic.unit != new.basic.unit ) {
locus.seq <- locus.seqs[i]
old.pos <- oldmap[[locus.seq]]
new.pos <- newmap[[locus.seq]]
# If locus is within the range of the old map,
# interpolate its position on the new map..
if ( inRange(x, range(old.pos)) ) {
# Get index of lower flanking locus.
xld <- x - old.pos
lower.indices <- which( xld == min( xld[ xld >= 0 ] ) )
lower.index <- lower.indices[ length(lower.indices) ]
# Get index of upper flanking locus.
xud <- old.pos - x
upper.indices <- which( xud == min( xud[ xud >= 0 ] ) )
upper.index <- upper.indices[1]
# Get flanking loci in old and new maps.
x1 <- old.pos[lower.index]
x2 <- old.pos[upper.index]
y1 <- new.pos[lower.index]
y2 <- new.pos[upper.index]
# If flanking loci effectively coincide,
# simply take the lower flanking value..
if ( abs(x2 - x1) < .Machine$double.eps^0.5 ) {
x <- y1
} else { # ..otherwise interpolate flanking loci.
x <- y1 + (x - x1) * (y2 - y1) / (x2 - x1)
}
} else { # ..otherwise extrapolate its position from map endpoints.
# Get terminal loci in old and new maps.
x1 <- old.pos[1]
x2 <- old.pos[ length(old.pos) ]
y1 <- new.pos[1]
y2 <- new.pos[ length(new.pos) ]
# Extrapolate from map endpoints.
if ( x < x1 ) {
x <- y1 - (x1 - x) * (y2 - y1) / (x2 - x1)
} else { # x > x2
x <- y2 + (x - x2) * (y2 - y1) / (x2 - x1)
}
}
}
# Convert locus position from basic units, if needed.
if ( new.map.unit != new.basic.unit ) {
x <- x / const$map.info[[new.map.type]][new.map.unit, 'factor']
}
locus.pos[i] <- unname(x)
}
loc <- pushLocusPos(loc, locus.pos)
attr(loc, 'map.unit') <- map.unit
return(loc)
}
class(map.key) <- c('mapkey', 'function')
} else { # ..otherwise return default mapkey object.
# Get name of current genome.
genome <- genomeOpt()
# Get default mapkey of current genome.
map.key <- const$default$mapkeys[[genome]]
}
return(map.key)
}
# mapkeyOpt --------------------------------------------------------------------
#' Set/get \pkg{shmootl} \code{mapkey} option.
#'
#' This function returns the current \pkg{shmootl} \code{mapkey}. If a new
#' \code{mapkey} is specified, the \pkg{shmootl} \code{mapkey} option is
#' updated with the given \code{mapkey} function.
#'
#' @param value New \code{mapkey} function.
#'
#' @return Current \code{mapkey} function.
#'
#' @export
#' @family mapkey functions
#' @family package option functions
#' @rdname mapkeyOpt
mapkeyOpt <- function(value) {
result <- getOption('shmootl.mapkey', default=mapkey())
stopifnot( 'mapkey' %in% class(result) )
if ( ! missing(value) && ! is.null(value) ) {
stopifnot( 'mapkey' %in% class(value) )
options(shmootl.mapkey=value)
}
return(result)
}
# mapsEqual -------------------------------------------------------------------
#' Test if two \pkg{R/qtl} \code{map} objects are essentially equal.
#'
#' This is based on the \pkg{R/qtl} function \code{comparecrosses},
#' and checks that two input \code{map} objects are essentially equal, having
#' identical reference sequences, marker names, map unit, and map positions
#' (within the given numeric tolerance).
#'
#' @param map1 An \pkg{R/qtl} \code{map} object.
#' @param map2 An \pkg{R/qtl} \code{map} object.
#' @param tol Numeric tolerance for comparing map positions.
#'
#' @return Returns \code{TRUE} if the two input \code{map} objects are identical
#' within the given tolerance. Otherwise, this function returns a character
#' vector listing observed differences between the two \code{map} objects.
#'
#' @template seealso-rqtl-manual
#'
#' @export
#' @family map utility functions
#' @rdname mapsEqual
mapsEqual <- function(map1, map2, tol=1e-5) {
stopifnot( 'map' %in% class(map1) )
stopifnot( 'map' %in% class(map2) )
stopifnot( isSingleNonNegativeNumber(tol) )
diffs <- character()
if ( length(map1) == length(map2) ) {
map1.names <- names(map1)
map2.names <- names(map2)
diff.names <- union( setdiff(map1.names, map2.names),
setdiff(map2.names, map1.names) )
if ( length(diff.names) > 0 ) {
diffs <- c(diffs, paste0("map sequence name mismatch - '",
toString(diff.names), "'"))
}
if ( all( lengths(map1) == lengths(map2) ) ) {
map1.ids <- pullLocusIDs(map1)
map2.ids <- pullLocusIDs(map2)
diff.ids <- union( setdiff(map1.ids, map2.ids),
setdiff(map2.ids, map1.ids) )
if ( length(diff.ids) > 0 ) {
diffs <- c(diffs, paste0("map sequence marker name mismatch - '",
toString(diff.ids), "'"))
}
map1.pos <- pullLocusPos(map1)
map2.pos <- pullLocusPos(map2)
if ( ! isTRUE( all.equal(map1.pos, map2.pos, tolerance=tol) ) ) {
diffs <- c(diffs, "map sequence marker position mismatch")
}
} else {
diffs <- c(diffs, "map sequence marker count mismatch")
}
} else {
diffs <- c(diffs, "map sequence count mismatch")
}
map1.unit <- getMapUnit(map1)
map2.unit <- getMapUnit(map2)
if ( ! identical(map1.unit, map2.unit) ) {
diffs <- c(diffs, "map unit mismatch")
}
return( if ( length(diffs) == 0 ) {TRUE} else {diffs} )
}
# matchLoci --------------------------------------------------------------------
#' Get object loci matching those specified.
#'
#' @param x Object containing map data.
#' @param loc Locus \code{mapframe} specifying map positions.
#'
#' @return Locus mapframe of those loci specified
#' that were found in the target object.
#'
#' @keywords internal
#' @rdname matchLoci
matchLoci <- function(x, loc) {
x.loci <- pullLoci(x)
indices <- matchLocusRowIndices(x.loci, loc)
return(x.loci[indices, ])
}
# matchLocusRowIndices ---------------------------------------------------------
#' Find matching locus row indices.
#'
#' @param x A \code{mapframe} or \code{scanone} object.
#' @param loc Locus \code{mapframe} specifying map positions.
#'
#' @return Vector of row indices with respect to the target \code{mapframe},
#' for which a matching locus exists in the locus mapframe.
#'
#' @keywords internal
#' @rdname matchLocusRowIndices
matchLocusRowIndices <- function(x, loc) {
stopifnot( 'mapframe' %in% class(x) || 'scanone' %in% class(x) )
stopifnot( 'mapframe' %in% class(loc) )
map.unit <- getMapUnit(x)
if ( is.na(map.unit) ) {
stop("map unit not found")
}
if ( getMapUnit(loc) != map.unit ) {
stop("map unit mismatch")
}
if ( nrow(x) == 0 ) {
stop("cannot match locus row indices in empty mapframe")
}
if ( nrow(loc) == 0 ) {
stop("locus mapframe must have at least one row")
}
# Get normalised sequences and positions of individual map loci.
x.seqs <- normSeq( pullLocusSeq(x) )
x.pos <- pullLocusPos(x)
# Get sequences and positions of specified loci.
loc.seqs <- pullLocusSeq(loc)
loc.pos <- pullLocusPos(loc)
indices <- rep.int(NA_integer_, nrow(x))
# Traverse both mapframes in sync, setting indices of matching loci.
# NB: we can assume mapframe loci will match in order, if at all.
i <- j <- 1
while ( i <= nrow(x) ) {
while ( ( loc.seqs[j] < x.seqs[i] || ( loc.seqs[j] == x.seqs[i] &&
loc.pos[j] < x.pos[i] ) ) && j < nrow(loc) ) {
j <- j + 1
}
while ( ( x.seqs[i] < loc.seqs[j] || ( x.seqs[i] == loc.seqs[j] &&
x.pos[i] < loc.pos[j] ) ) && i < nrow(x) ) {
i <- i + 1
}
# If closest locus is within numeric tolerance, set row index.
if ( x.seqs[i] == loc.seqs[j] &&
abs(x.pos[i] - loc.pos[j]) <= .Machine$double.eps^0.5 ) {
indices[i] <- i
}
i <- i + 1
}
return(indices[ ! is.na(indices) ])
}
# matchSeqRowIndices -----------------------------------------------------------
#' Find row indices of matching sequences.
#'
#' @param x A \code{mapframe} object.
#' @param sequences Sequences for which row indices should be returned.
#' @param simplify Simplify list return value to a vector.
#'
#' @return Vector of row indices corresponding to the specified sequences. An
#' empty vector is returned if no sequences match the input \code{mapframe}.
#'
#' @keywords internal
#' @rdname matchSeqRowIndices
matchSeqRowIndices <- function(x, sequences, simplify=FALSE) {
stopifnot( is.data.frame(x) )
if ( nrow(x) > 0 ) {
norm.seqs <- normSeq(sequences)
x.seqs <- normSeq( pullLocusSeq(x) )
index.list <- vector('list', length(sequences))
for ( i in seq_along(sequences) ) {
index.list[[i]] <- which( x.seqs == norm.seqs[i] )
}
if (simplify) {
indices <- sort( unlist(index.list) )
} else {
indices <- index.list
}
} else {
indices <- integer()
}
return(indices)
}
# orderMap ---------------------------------------------------------------------
#' Put object in map order.
#'
#' @param x Object containing map data.
#'
#' @return Input object ordered by the normalised form of
#' the reference sequence, then numerically by map position.
#'
#' @export
#' @family map utility functions
#' @rdname orderMap
orderMap <- function(x) {
UseMethod('orderMap', x)
}
# orderMap.data.frame ----------------------------------------------------------
#' @export
#' @rdname orderMap
orderMap.data.frame <- function(x) {
if ( nrow(x) > 0 ) {
x.seqs <- pullLocusSeq(x)
x.pos <- pullLocusPos(x)
x <- x[ order(rankSeq(x.seqs), x.pos), ]
}
return(x)
}
# orderMap.map -----------------------------------------------------------------
#' @export
#' @rdname orderMap
orderMap.map <- function(x) {
# Sort map sequences.
x <- subsetMap( x, sortSeq( names(x) ) )
# Sort map positions.
for ( i in seq_along(x) ) {
x[[i]] <- sort.int(x[[i]])
}
return(x)
}
# pullLoci ---------------------------------------------------------------------
#' Pull loci from object.
#'
#' @param x Object containing map data.
#'
#' @return Locus mapframe of individual loci.
#'
#' @keywords internal
#' @rdname pullLoci
pullLoci <- function(x) {
x.seqs <- normSeq( pullLocusSeq(x) )
x.pos <- pullLocusPos(x)
loc <- data.frame(chr=x.seqs, pos=x.pos)
class(loc) <- c('mapframe', 'data.frame')
attr(loc, 'map.unit') <- getMapUnit(x)
return(loc)
}
# pullLocusIDs -----------------------------------------------------------------
#' Pull individual locus IDs.
#'
#' @param x Object containing map data.
#'
#' @return Character vector of individual locus IDs.
#'
#' @keywords internal
#' @rdname pullLocusIDs
pullLocusIDs <- function(x) {
UseMethod('pullLocusIDs', x)
}
# pullLocusIDs.data.frame ------------------------------------------------------
#' @method pullLocusIDs data.frame
#' @rdname pullLocusIDs
pullLocusIDs.data.frame <- function(x) {
return( if ( nrow(x) > 0 && hasRownames(x) ) { rownames(x) } else { NULL } )
}
# pullLocusIDs.list ------------------------------------------------------------
#' @method pullLocusIDs list
#' @rdname pullLocusIDs
pullLocusIDs.list <- function(x) {
return( pullLocusIDs.map(x) )
}
# pullLocusIDs.map -------------------------------------------------------------
#' @method pullLocusIDs map
#' @rdname pullLocusIDs
pullLocusIDs.map <- function(x) {
return( unlist( lapply(x, function(loci) names(loci)), use.names=FALSE ) )
}
# pullLocusPos -----------------------------------------------------------------
#' Pull individual locus positions.
#'
#' @param x Object containing map data.
#'
#' @return Numeric vector of individual locus positions.
#'
#' @keywords internal
#' @rdname pullLocusPos
pullLocusPos <- function(x) {
UseMethod('pullLocusPos', x)
}
# pullLocusPos.data.frame ------------------------------------------------------
#' @method pullLocusPos data.frame
#' @rdname pullLocusPos
pullLocusPos.data.frame <- function(x) {
poscol.index <- getPosColIndex(x)
if ( is.numeric(x[, poscol.index]) ) {
locus.pos <- x[, poscol.index]
} else {
locus.pos <- setPosColDataMapUnit(x[, poscol.index], NULL)
}
return(locus.pos)
}
# pullLocusPos.list ------------------------------------------------------------
#' @method pullLocusPos list
#' @rdname pullLocusPos
pullLocusPos.list <- function(x) {
return( pullLocusPos.map(x) )
}
# pullLocusPos.map -------------------------------------------------------------
#' @method pullLocusPos map
#' @rdname pullLocusPos
pullLocusPos.map <- function(x) {
return( unlist( lapply(x, function(loci) unname(loci) ) ) )
}
# pullLocusPos.mapframe --------------------------------------------------------
#' @method pullLocusPos mapframe
#' @rdname pullLocusPos
pullLocusPos.mapframe <- function(x) {
return( x[, getPosColIndex(x)] )
}
# pullLocusSeq -----------------------------------------------------------------
#' Pull sequence labels for individual loci.
#'
#' @param x Object containing map data.
#'
#' @return Character vector of sequence labels associated with individual loci.
#'
#' @keywords internal
#' @rdname pullLocusSeq
pullLocusSeq <- function(x) {
UseMethod('pullLocusSeq', x)
}
# pullLocusSeq.data.frame ------------------------------------------------------
#' @method pullLocusSeq data.frame
#' @rdname pullLocusSeq
pullLocusSeq.data.frame <- function(x) {
return( as.character(x[, getSeqColIndex(x)]) )
}
# pullLocusSeq.list ------------------------------------------------------------
#' @method pullLocusSeq list
#' @rdname pullLocusSeq
pullLocusSeq.list <- function(x) {
return( pullLocusSeq.map(x) )
}
# pullLocusSeq.map -------------------------------------------------------------
#' @method pullLocusSeq map
#' @rdname pullLocusSeq
pullLocusSeq.map <- function(x) {
return( unlist( lapply( seq_along(x), function(i)
rep_len(names(x)[i], length(x[[i]])) ) ) )
}
# pullMap ----------------------------------------------------------------------
#' Pull map from object.
#'
#' @param x Object containing map data.
#'
#' @return An \pkg{R/qtl} \code{map} extracted from the input object.
#'
#' @keywords internal
#' @rdname pullMap
pullMap <- function(x) {
UseMethod('pullMap', x)
}
# pullMap.cross ----------------------------------------------------------------
#' @method pullMap cross
#' @rdname pullMap
pullMap.cross <- function(x) {
return( qtl::pull.map(x) )
}
# pullMap.geno -----------------------------------------------------------------
#' @method pullMap geno
#' @rdname pullMap
pullMap.geno <- function(x) {
return( as.map( lapply(x, function(obj)
obj$map), map.unit='cM' ) )
}
# pushLoci ---------------------------------------------------------------------
#' Replace loci of object.
#'
#' @param x Object containing map data.
#' @return loc Locus mapframe of individual loci.
#'
#' @return Input object with individual loci replaced.
#'
#' @keywords internal
#' @rdname pushLoci
pushLoci <- function(x, loc) {
stopifnot( 'mapframe' %in% class(loc) )
stopifnot( ncol(loc) == 2 )
object.mapunit <- getMapUnit(x)
loc.mapunit <- getMapUnit(loc)
if ( ! is.na(object.mapunit) && loc.mapunit != object.mapunit ) {
stop("map unit mismatch")
}
x <- pushLocusSeq(x, pullLocusSeq(loc))
x <- pushLocusPos(x, pullLocusPos(loc))
if ( is.na(object.mapunit) ) {
attr(x, 'map.unit') <- loc.mapunit
}
return(x)
}
# pushLocusIDs -----------------------------------------------------------------
#' Replace individual locus IDs.
#'
#' @param x Object containing map data.
#' @param value Character vector of individual locus IDs.
#'
#' @return Input object with individual locus IDs replaced.
#'
#' @keywords internal
#' @rdname pushLocusIDs
pushLocusIDs <- function(x, value) {
stopifnot( all( isValidID(value) ) )
stopif( anyDuplicated(value) )
UseMethod('pushLocusIDs', x)
}
# pushLocusIDs.data.frame ------------------------------------------------------
#' @method pushLocusIDs data.frame
#' @rdname pushLocusIDs
pushLocusIDs.data.frame <- function(x, value) {
# Get total number of markers in object.
x.totmar <- nrow(x)
# Get total number of markers in value.
value.totmar <- length(value)
if ( value.totmar != x.totmar ) {
stop("cannot push ", value.totmar, " locus IDs into ", x.totmar, " loci")
}
rownames(x) <- value
return(x)
}
# pushLocusIDs.list ------------------------------------------------------------
#' @method pushLocusIDs list
#' @rdname pushLocusIDs
pushLocusIDs.list <- function(x, value) {
return( pushLocusIDs.map(x, value) )
}
# pushLocusIDs.map -------------------------------------------------------------
#' @method pushLocusIDs map
#' @rdname pushLocusIDs
pushLocusIDs.map <- function(x, value) {
# Get total number of markers in object.
x.totmar <- sum( lengths(x) )
# Get total number of markers in value.
value.totmar <- length(value)
if ( value.totmar != x.totmar ) {
stop("cannot push ", value.totmar, " locus IDs into ", x.totmar, " loci")
}
map.seqs <- names(x)
seq.index.list <- getRunIndexList( unlist( lapply( seq_along(x), function(i)
rep_len(map.seqs[i], length(x[[i]])) ) ) )
for ( map.seq in map.seqs ) {
names(x[[map.seq]]) <- value[ seq.index.list[[map.seq]] ]
}
return(x)
}
# pushLocusPos -----------------------------------------------------------------
#' Replace individual locus positions.
#'
#' @param x Object containing map data.
#' @param value Numeric vector of individual locus positions.
#'
#' @return Input object with individual locus positions replaced.
#'
#' @keywords internal
#' @rdname pushLocusPos
pushLocusPos <- function(x, value) {
UseMethod('pushLocusPos', x)
}
# pushLocusPos.data.frame ------------------------------------------------------
#' @method pushLocusPos data.frame
#' @rdname pushLocusPos
pushLocusPos.data.frame <- function(x, value) {
object.mapunit <- getMapUnit(x)
value.mapunit <- getPosColDataMapUnit(value)
if ( ! is.na(object.mapunit) && ! is.na(value.mapunit) &&
value.mapunit != object.mapunit ) {
stop("map unit mismatch")
}
value <- setPosColDataMapUnit(value, NULL)
stopifnot( is.numeric(value) )
# Get total number of markers in object.
x.totmar <- nrow(x)
# Get total number of markers in value.
value.totmar <- length(value)
if ( value.totmar != x.totmar ) {
stop("cannot push ", value.totmar, " locus positions into ", x.totmar, " loci")
}
poscol.index <- getPosColIndex(x)
x[, poscol.index] <- value
stopifnot( inMapOrder(x) )
if ( is.na(object.mapunit) ) {
attr(x, 'map.unit') <- value.mapunit
}
return(x)
}
# pushLocusPos.list ------------------------------------------------------------
#' @method pushLocusPos list
#' @rdname pushLocusPos
pushLocusPos.list <- function(x, value) {
return( pushLocusPos.map(x, value) )
}
# pushLocusPos.map -------------------------------------------------------------
#' @method pushLocusPos map
#' @rdname pushLocusPos
pushLocusPos.map <- function(x, value) {
object.mapunit <- getMapUnit(x)
value.mapunit <- getPosColDataMapUnit(value)
if ( ! is.na(object.mapunit) && ! is.na(value.mapunit) &&
value.mapunit != object.mapunit ) {
stop("map unit mismatch")
}
value <- setPosColDataMapUnit(value, NULL)
stopifnot( is.numeric(value) )
# Get total number of markers in object.
x.totmar <- sum( lengths(x) )
# Get total number of markers in value.
value.totmar <- length(value)
if ( value.totmar != x.totmar ) {
stop("cannot push ", value.totmar, " locus positions into ", x.totmar, " loci")
}
map.seqs <- names(x)
seq.index.list <- getRunIndexList( unlist( lapply( seq_along(x), function(i)
rep_len(map.seqs[i], length(x[[i]])) ) ) )
for ( map.seq in map.seqs ) {
seq.pos <- value[ seq.index.list[[map.seq]] ]
attributes(seq.pos) <- attributes(x[[map.seq]])
x[[map.seq]] <- seq.pos
}
stopifnot( inMapOrder(x) )
if ( is.na(object.mapunit) ) {
attr(x, 'map.unit') <- value.mapunit
}
return(x)
}
# pushLocusSeq -----------------------------------------------------------------
#' Replace sequence labels of individual loci.
#'
#' @param x Object containing map data.
#' @param value Character vector of sequence labels for individual loci.
#'
#' @return Input object with individual locus sequence labels replaced.
#'
#' @keywords internal
#' @rdname pushLocusSeq
pushLocusSeq <- function(x, value) {
stopifnot( all( isValidID(value) ) )
UseMethod('pushLocusSeq', x)
}
# pushLocusSeq.data.frame ------------------------------------------------------
#' @method pushLocusSeq data.frame
#' @rdname pushLocusSeq
pushLocusSeq.data.frame <- function(x, value) {
seqcol.index <- getSeqColIndex(x)
# Get key info about object.
x.runs <- rle( as.character(x[, seqcol.index]) ) # run-length encoding
x.nmar <- x.runs$lengths # number of markers by sequence
x.totmar <- nrow(x) # total number of markers
# Get key info about value.
value.runs <- rle(value) # run-length encoding
value.nmar <- value.runs$lengths # number of markers by sequence
value.totmar <- length(value) # total number of markers
if ( value.totmar != x.totmar ) {
stop("cannot push ", value.totmar, " locus sequences into ", x.totmar, " loci")
}
if ( ! identical(value.nmar, x.nmar) ) { # NB: checks equal number of sequences
stop("cannot push locus sequences - sequence boundaries not preserved")
}
x[, seqcol.index] <- value
return(x)
}
# pushLocusSeq.list ------------------------------------------------------------
#' @method pushLocusSeq list
#' @rdname pushLocusSeq
pushLocusSeq.list <- function(x, value) {
return( pushLocusSeq.map(x, value) )
}
# pushLocusSeq.map -------------------------------------------------------------
#' @method pushLocusSeq map
#' @rdname pushLocusSeq
pushLocusSeq.map <- function(x, value) {
# Get key info about object.
x.nmar <- unname( lengths(x) ) # number of markers by sequence
x.totmar <- sum(x.nmar) # total number of markers
# Get key info about value.
value.runs <- rle(value) # run-length encoding
value.nmar <- value.runs$lengths # number of markers by sequence
value.totmar <- length(value) # total number of markers
if ( value.totmar != x.totmar ) {
stop("cannot push ", value.totmar, " locus sequences into ", x.totmar, " loci")
}
if ( ! identical(value.nmar, x.nmar) ) { # NB: checks equal number of sequences
stop("cannot push locus sequences - sequence boundaries not preserved")
}
names(x) <- value.runs$values
return(x)
}
# pushMap ----------------------------------------------------------------------
#' Push map into object.
#'
#' @param x Object that can contain map data.
#' @param map An \pkg{R/qtl} \code{map} object.
#'
#' @return Input object incorporating the given map data.
#'
#' @keywords internal
#' @rdname pushMap
pushMap <- function(x, map) {
UseMethod('pushMap', x)
}
# pushMap.geno -----------------------------------------------------------------
#' @method pushMap geno
#' @rdname pushMap
pushMap.geno <- function(x, map) {
stopifnot( 'map' %in% class(map) )
map.unit <- 'cM'
if ( getMapUnit(map) != map.unit ) {
stop("cross map positions must be in centiMorgans (e.g. '47 cM')")
}
norm.map.seqs <- normSeq( names(map) )
geno.map <- as.map( lapply(x, function(obj) obj$map), map.unit=map.unit )
geno.map.seqs <- names(geno.map)
if ( length(norm.map.seqs) != length(geno.map.seqs) ||
any( norm.map.seqs != geno.map.seqs ) ) {
stop("map sequence mismatch")
}
for ( map.seq in norm.map.seqs ) {
geno.seq.ids <- names(geno.map[[map.seq]])
map.seq.ids <- names(map[[map.seq]])
if ( length(map.seq.ids) != length(geno.seq.ids) ||
any( map.seq.ids != geno.seq.ids ) ) {
stop("marker mismatch")
}
seq.map <- map[[map.seq]]
class(seq.map) <- 'numeric'
x[[map.seq]]$map <- seq.map
}
return(x)
}
# setMapUnit -------------------------------------------------------------------
#' Set map unit.
#'
#' Set the map unit of the object. If the object already has a map unit, map
#' positions are rescaled from the original map unit to the new map unit. If
#' a \code{data.frame} contains map unit information in headings or map
#' positions, these are updated to reflect the new map unit.
#'
#' @param x Object containing map data.
#' @param map.unit Map unit.
#'
#' @return Input object with the specified map unit.
#'
#' @template section-mapunits
#'
#' @export
#' @family map utility functions
#' @rdname setMapUnit
setMapUnit <- function(x, map.unit) {
validateMapUnit(map.unit)
UseMethod('setMapUnit', x)
}
# setMapUnit.data.frame --------------------------------------------------------
#' @export
#' @rdname setMapUnit
setMapUnit.data.frame <- function(x, map.unit) {
existing.mapunit <- getMapUnit(x)
heading.mapunit <- getPosColNameMapUnit(x)
positions.mapunit <- getPosColDataMapUnit(x)
if ( ! is.na(heading.mapunit) ) {
x <- setPosColNameMapUnit(x, NULL)
}
if ( ! is.na(positions.mapunit) ) {
x <- setPosColDataMapUnit(x, NULL)
}
# Ensure that existing map unit is explicit attribute.
if ( ! 'map.unit' %in% names( attributes(x) ) ) {
attr(x, 'map.unit') <- map.unit
}
if ( ! is.na(existing.mapunit) ) {
x <- convertMapUnit(x, map.unit)
} else {
attr(x, 'map.unit') <- map.unit
}
if ( ! is.na(heading.mapunit) ) {
x <- setPosColNameMapUnit(x, map.unit)
}
if ( ! is.na(positions.mapunit) ) {
x <- setPosColDataMapUnit(x, map.unit)
}
return(x)
}
# setMapUnit.list --------------------------------------------------------------
#' @export
#' @rdname setMapUnit
setMapUnit.list <- function(x, map.unit) {
attr(x, 'map.unit') <- map.unit
return(x)
}
# setMapUnit.map ---------------------------------------------------------------
#' @export
#' @rdname setMapUnit
setMapUnit.map <- function(x, map.unit) {
existing.mapunit <- getMapUnit(x)
if ( ! is.na(existing.mapunit) ) {
x <- convertMapUnit(x, map.unit)
} else {
attr(x, 'map.unit') <- map.unit
}
return(x)
}
# setMapUnit.mapframe ----------------------------------------------------------
#' @export
#' @rdname setMapUnit
setMapUnit.mapframe <- function(x, map.unit) {
existing.mapunit <- getMapUnit(x)
if ( ! is.na(existing.mapunit) ) {
x <- convertMapUnit(x, map.unit)
} else {
attr(x, 'map.unit') <- map.unit
}
return(x)
}
# setPosColDataMapUnit ---------------------------------------------------------
#' Set map unit of position column data.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame},
#' or a vector of map positions.
#' @param map.unit Map unit. Set to \code{NULL} to remove
#' map unit information from position column data.
#'
#' @return Input object with the specified map
#' unit appended to position column data.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname setPosColDataMapUnit
setPosColDataMapUnit <- function(x, map.unit) {
# TODO: optimise this function.
if ( ! is.null(map.unit) ) {
validateMapUnit(map.unit)
}
UseMethod('setPosColDataMapUnit', x)
}
# setPosColDataMapUnit.character -----------------------------------------------
#' @rdname setPosColDataMapUnit
setPosColDataMapUnit.character <- function(x, map.unit) {
# Remove any existing map unit suffixes.
existing.mapunit <- getPosColDataMapUnit(x)
if ( ! is.na(existing.mapunit) ) {
map.type <- const$known.map.types[existing.mapunit]
u <- which( rownames(const$map.info[[map.type]]) == existing.mapunit )
mapunit.pattern <- const$map.info[[map.type]]$pattern[u]
res <- suppressWarnings( as.numeric(
sub(mapunit.pattern, '', x, ignore.case=TRUE) ) )
} else {
res <- suppressWarnings( as.numeric(x) )
}
invalid <- unique(x[ is.na(res) ])
if ( length(invalid) > 0 ) {
stop("invalid map positions - '", toString(invalid), "'")
}
# Append map unit suffixes.
if ( length(res) > 0 && ! is.null(map.unit) ) {
res <- paste(as.character(res), map.unit)
}
return(res)
}
# setPosColDataMapUnit.data.frame ----------------------------------------------
#' @rdname setPosColDataMapUnit
setPosColDataMapUnit.data.frame <- function(x, map.unit) {
existing.mapunit <- getMapUnit(x)
if ( ! is.null(map.unit) && ! is.na(existing.mapunit) &&
map.unit != existing.mapunit ) {
stop("pos column data map-unit mismatch")
}
poscol.index <- getPosColIndex(x)
x[, poscol.index] <- setPosColDataMapUnit(x[, poscol.index], map.unit)
return(x)
}
# setPosColDataMapUnit.numeric -------------------------------------------------
#' @rdname setPosColDataMapUnit
setPosColDataMapUnit.numeric <- function(x, map.unit) {
return( setPosColDataMapUnit( as.character(x), map.unit) )
}
# setPosColNameMapUnit ---------------------------------------------------------
#' Set map unit of position column heading.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame},
#' or a position column heading.
#' @param map.unit Map unit. Set to \code{NULL} to remove
#' map unit information from position column heading.
#'
#' @return Input object with the specified map
#' unit added to position column heading.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname setPosColNameMapUnit
setPosColNameMapUnit <- function(x, map.unit) {
if ( ! is.null(map.unit) ) {
validateMapUnit(map.unit)
}
UseMethod('setPosColNameMapUnit', x)
}
# setPosColNameMapUnit.character -----------------------------------------------
#' @rdname setPosColNameMapUnit
setPosColNameMapUnit.character <- function(x, map.unit) {
stopifnot( length(x) == 1 )
existing.mapunit <- getPosColNameMapUnit(x)
if ( ! is.null(map.unit) ) {
# If pos column has an existing map unit, replace it..
if ( ! is.na(existing.mapunit) ) {
x <- sub(existing.mapunit, map.unit, x)
} else { # ..otherwise insert map unit in parentheses.
x <- paste0(x, ' (', map.unit, ')')
}
} else if ( ! is.na(existing.mapunit) ) {
# Compose pattern allowing for text before and after map
# unit, all possible enclosing brackets, and any whitespace.
pattern <- paste0('^(.*?)[[:space:]]*[[({<]?[[:space:]]*',
existing.mapunit, '[[:space:]]*[])}>]?[[:space:]]*(.*?)$')
# Match pattern, get any flanking text.
m <- regexec(pattern, x)
flank.text <- (regmatches(x, m))[[1]][2:3]
flank.text <- flank.text[ flank.text != '' ]
# If no flanking text, reset pos column heading.
if ( length(flank.text) == 0 ) {
parts <- 'pos'
}
# Merge parts of pos column heading.
x <- paste(flank.text, collapse=' ')
}
return(x)
}
# setPosColNameMapUnit.data.frame ----------------------------------------------
#' @rdname setPosColNameMapUnit
setPosColNameMapUnit.data.frame <- function(x, map.unit) {
existing.mapunit <- getMapUnit(x)
if ( ! is.null(map.unit) && ! is.na(existing.mapunit) &&
map.unit != existing.mapunit ) {
stop("pos column heading map unit mismatch")
}
poscol.index <- getPosColIndex(x)
colnames(x)[poscol.index] <- setPosColNameMapUnit(
colnames(x)[poscol.index], map.unit)
return(x)
}
# setupDefaultMapkeys ----------------------------------------------------------
#' Setup default \code{mapkey} objects.
#'
#' Default \code{mapkey} objects are generated from genome sequence info. Simple
#' component maps are created from reference sequence length info: a genetic map
#' from the \code{'maplengths'} column, and a physical map from the
#' \code{'seqlengths'} column. These are used to create a basic
#' \code{mapkey} object for each genome.
#'
#' @keywords internal
#' @rdname setupDefaultMapkeys
setupDefaultMapkeys <- function() {
stopifnot( exists('const') )
stopif( environmentIsLocked(const) )
known.map.types <- unique(const$known.map.types)
default.mapkeys <- list()
# Create default mapkey for each genome.
for ( genome in names(const$seqtab) ) {
seqtab <- const$seqtab[[genome]]
map.seqs <- seqtab$seqids
cmaps <- vector('list', length(known.map.types))
# Create component map for each known map type.
for ( i in seq_along(known.map.types) ) {
map.type <- known.map.types[i]
# Get basic map unit for this map type.
map.unit <- const$basic.map.unit[map.type]
# Get key map info.
if ( map.type == 'gmap' ) {
makeLocusIDs <- makePseudomarkerIDs
map.starts <- rep_len(0, length(map.seqs))
map.ends <- seqtab$maplengths
} else if ( map.type == 'pmap' ) {
makeLocusIDs <- makeDefaultMarkerIDs
map.starts <- rep_len(1, length(map.seqs))
map.ends <- seqtab$seqlengths
} else {
stop("unknown map type - '", map.type, "'")
}
# Create map data-frame from sequence start- and end-points.
map.table <- data.frame( chr=c(map.seqs, map.seqs),
pos=c(map.starts, map.ends) )
map.table <- map.table[ order(rankSeq(map.table$chr), map.table$pos), ]
rownames(map.table) <- makeLocusIDs(map.table)
# Get locus info.
locus.seqs <- pullLocusSeq(map.table)
locus.pos <- pullLocusPos(map.table)
locus.ids <- pullLocusIDs(map.table)
cmap <- list()
# Set map data for each sequence.
for ( map.seq in map.seqs ) {
# Get indices for this sequence.
indices <- which( locus.seqs == map.seq )
# Get map positions for this sequence.
seq.pos <- locus.pos[indices]
# Set map position names from locus IDs.
names(seq.pos) <- locus.ids[indices]
# Set class of map position vector.
class(seq.pos) <- 'A' # NB: assumes no 'X' chromosomes.
# Set map positions for sequence.
cmap[[map.seq]] <- seq.pos
}
# Set map unit of component map.
attr(cmap, 'map.unit') <- map.unit
# Set class of component map.
class(cmap) <- 'map'
cmaps[[i]] <- cmap
}
# Create default mapkey from component maps.
default.mapkeys[[genome]] <- do.call(mapkey, cmaps)
}
const$default[['mapkeys']] <- default.mapkeys
return( invisible() )
}
# subsetMap --------------------------------------------------------------------
#' Subset \pkg{shmootl} \code{map} object.
#'
#' The \pkg{R/qtl} package implements a \code{subset.map} method, but that
#' method strips some attributes from the \code{map} object. This function
#' transfers any stripped attributes to the subsetted \code{map} object.
#'
#' @param x A \code{map} object.
#' @param ... Arguments passed to \code{subset}.
#'
#' @return Subsetted \code{map} object.
#'
#' @keywords internal
#' @rdname subsetMap
subsetMap <- function(x, ...) {
stopifnot( 'map' %in% class(x) )
others <- otherattributes(x)
x <- subset(x, ...)
otherattributes(x) <- others
return(x)
}
# validateGeneticMapUnit -------------------------------------------------------
#' Validate genetic map unit.
#'
#' @param x Map unit, or object with a \code{'map.unit'} attribute.
#'
#' @return \code{TRUE} if map unit is a valid genetic map unit;
#' otherwise, returns error.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname validateGeneticMapUnit
validateGeneticMapUnit <- function(x) {
return( validateMapUnit(x, map.type='gmap') )
}
# validateMap ------------------------------------------------------------------
#' Validate \code{map} object.
#'
#' @param x A \code{map} or equivalent \code{list}.
#'
#' @return \code{TRUE} if object is a valid \code{map} or equivalent
#' \code{list}; otherwise, returns first error.
#'
#' @template section-map
#'
#' @keywords internal
#' @rdname validateMap
validateMap <- function(x, ...) {
UseMethod('validateMap', x)
}
# validateMap.list -------------------------------------------------------------
#' @rdname validateMap
validateMap.list <- function(x) {
if ( length(x) < const$min.spm ) {
stop("map-like object has too few sequences (min=", const$min.spm, ")")
}
map.seqs <- names(x)
if ( is.null(map.seqs) ) {
stop("map-like object has no sequence labels")
}
tryCatch({
norm.map.seqs <- normSeq(map.seqs) # NB: validates sequence labels.
}, error=function(e) {
stop("map-like object has invalid sequence labels")
})
if ( anyDuplicated(map.seqs) ) {
stop("map-like object has duplicate sequence labels")
}
if ( anyDuplicated(norm.map.seqs) ) {
stop("map-like object has inconsistent sequence labels")
}
empty.seqs <- map.seqs[ is.na(x) ]
if ( length(empty.seqs) > 0 ) {
stop("map-like object has empty sequences - '",
toString(empty.seqs), "'")
}
nonnum.seqs <- map.seqs[ ! sapply(x, is.numeric) ]
if ( length(nonnum.seqs) > 0 ) {
stop("map-like object has non-numeric sequence vectors - '",
toString(nonnum.seqs), "'")
}
short.seqs <- map.seqs[ lengths(x) < const$min.lps ]
if ( length(short.seqs) > 0 ) {
stop("map-like object has too few loci in sequences - '",
toString(short.seqs), "'")
}
absent.ids <- map.seqs[ ! sapply(x, hasNames) ]
if ( length(absent.ids) > 0 ) {
stop("locus IDs not found for sequences - '",
toString(absent.ids), "'")
}
x.ids <- pullLocusIDs(x)
if ( ! all( isValidID(x.ids) ) ) {
stop("map-like object has invalid locus IDs")
}
if ( anyDuplicated(x.ids) ) {
stop("map-like object has duplicate locus IDs")
}
tryCatch({
x.pos <- pullLocusPos(x) # NB: validates map positions.
}, error=function(e) {
stop("map-like object has invalid map positions")
})
validateMapUnit(x)
if ( isPhysicalMap(x) ) {
subzero <- unique( x.pos[ x.pos < 0 ] )
if ( length(subzero) > 0 ) {
stop("map-like object has invalid physical map positions - '",
toString(subzero), "'")
}
}
return(TRUE)
}
# validateMap.map --------------------------------------------------------------
#' @rdname validateMap
validateMap.map <- function(x, package=c('qtl', 'shmootl')) {
package <- match.arg(package)
if ( length(x) < const$min.spm ) {
stop("map has too few sequences (min=", const$min.spm, ")")
}
map.seqs <- names(x)
if ( is.null(map.seqs) ) {
stop("map has no sequence labels")
}
if ( package == 'qtl' ) {
tryCatch({
norm.map.seqs <- normSeq(map.seqs) # NB: validates sequence labels.
}, error=function(e) {
stop("qtl map has invalid sequence labels")
})
} else {
invalid <- map.seqs[ ! isNormSeq(map.seqs) ]
if ( length(invalid) > 0 ) {
stop("shmootl map has invalid sequence labels - '", toString(invalid), "'")
}
}
dup.seqs <- map.seqs[ duplicated(map.seqs) ]
if ( length(dup.seqs) > 0 ) {
stop("map has duplicate sequence labels - '", toString(dup.seqs), "'")
}
empty.seqs <- map.seqs[ is.na(x) ]
if ( length(empty.seqs) > 0 ) {
stop("map has empty sequences - '", toString(empty.seqs), "'")
}
nonnum.seqs <- map.seqs[ ! sapply(x, is.numeric) ]
if ( length(nonnum.seqs) > 0 ) {
stop("map has non-numeric sequence vectors - '", toString(nonnum.seqs), "'")
}
short.seqs <- map.seqs[ lengths(x) < const$min.lps ]
if ( length(short.seqs) > 0 ) {
stop("map has too few loci in sequences - '", toString(short.seqs), "'")
}
absent.ids <- map.seqs[ ! sapply(x, hasNames) ]
if ( length(absent.ids) > 0 ) {
stop("locus IDs not found for sequences - '", toString(absent.ids), "'")
}
x.ids <- pullLocusIDs(x)
if ( ! all( isValidID(x.ids) ) ) {
stop("map has invalid locus IDs")
}
if ( anyDuplicated(x.ids) ) {
stop("map has duplicate locus IDs")
}
x.pos <- pullLocusPos(x)
if ( anyNA(x.pos) ) {
stop("map has invalid map positions")
}
validateMapUnit(x)
if ( isPhysicalMap(x) ) {
subzero <- unique( x.pos[ x.pos < 0 ] )
if ( length(subzero) > 0 ) {
stop("map has invalid physical map positions - '",
toString(subzero), "'")
}
}
if ( ! inMapOrder(x) ) {
stop("map must be ordered by sequence and map position")
}
if ( package == 'qtl' ) {
if ( ! all( sapply(x, class) %in% c('A', 'X') ) ) {
stop("map sequences must be of class 'A' or 'X'")
}
} else {
if ( ! all( sapply(x, class) == 'A' ) ) { # NB: assumes no 'X' chromosomes.
stop("shmootl map sequences must be of class 'A'")
}
if ( is.na( getMapUnit(x) ) ) {
stop("shmootl map must have map unit info")
}
}
return(TRUE)
}
# validateMapframe -------------------------------------------------------------
#' Validate \code{mapframe} object.
#'
#' @param x A \code{mapframe} or equivalent \code{data.frame}.
#'
#' @return \code{TRUE} if object is a valid \code{mapframe} or equivalent
#' \code{data.frame}; otherwise, returns first error.
#'
#' @template section-mapframe
#'
#' @keywords internal
#' @rdname validateMapframe
validateMapframe <- function(x, ...) {
UseMethod('validateMapframe', x)
}
# validateMapframe.data.frame --------------------------------------------------
#' @rdname validateMapframe
validateMapframe.data.frame <- function(x) {
seqcol.index <- getSeqColIndex(x) # NB: confirms object has sequence column.
poscol.index <- getPosColIndex(x) # NB: confirms object has position column.
if ( nrow(x) > 0 ) {
x.ids <- pullLocusIDs(x)
if ( ! is.null(x.ids) ) {
if ( ! all( isValidID(x.ids) ) ) {
stop("mapframe-like object has invalid locus IDs")
}
if ( anyDuplicated(x.ids) ) {
stop("mapframe-like object has duplicate locus IDs")
}
}
tryCatch({
norm.map.seqs <- normSeq( pull.chr(x) ) # NB: validates sequence labels.
}, error=function(e) {
stop("mapframe-like object has invalid sequence labels")
})
if ( anyDuplicated(norm.map.seqs) ) {
stop("mapframe-like object has inconsistent sequence labels")
}
tryCatch({
x.pos <- pullLocusPos(x) # NB: validates map positions.
}, error=function(e) {
stop("mapframe-like object has invalid map positions")
})
validateMapUnit(x)
if ( isPhysicalMapframe(x) ) {
subzero <- unique( x.pos[ x.pos < 0 ] )
if ( length(subzero) > 0 ) {
stop("mapframe-like object has invalid physical map positions - '",
toString(subzero), "'")
}
}
}
return(TRUE)
}
# validateMapframe.mapframe ----------------------------------------------------
#' @rdname validateMapframe
validateMapframe.mapframe <- function(x) {
mapframe.colnames <- colnames(x)
if ( mapframe.colnames[1] != 'chr' ) {
stop("name of first mapframe column must be 'chr', not '",
mapframe.colnames[1], "'")
}
if ( mapframe.colnames[2] != 'pos' ) {
stop("name of second mapframe column must be 'pos', not '",
mapframe.colnames[2], "'")
}
if ( nrow(x) > 0 ) {
x.ids <- pullLocusIDs(x)
if ( ! is.null(x.ids) ) {
if ( ! all( isValidID(x.ids) ) ) {
stop("mapframe has invalid locus IDs")
}
if ( anyDuplicated(x.ids) ) {
stop("mapframe has duplicate locus IDs")
}
}
map.seqs <- pull.chr(x)
invalid <- map.seqs[ ! isNormSeq(map.seqs) ]
if ( length(invalid) > 0 ) {
stop("mapframe has invalid sequence labels - '",
toString(invalid), "'")
}
x.pos <- pullLocusPos(x)
if ( ! is.numeric(x.pos) ) {
stop("mapframe has non-numeric map positions")
}
if ( anyNA(x.pos) ) {
stop("mapframe has invalid map positions")
}
validateMapUnit(x)
if ( isPhysicalMapframe(x) ) {
subzero <- unique( x.pos[ x.pos < 0 ] )
if ( length(subzero) > 0 ) {
stop("mapframe has invalid physical map positions - '",
toString(subzero), "'")
}
}
if ( ! inMapOrder(x) ) {
stop("mapframe must be ordered by sequence and position")
}
}
if ( is.na( getMapUnit(x) ) ) {
stop("mapframe must have map unit info")
}
return(TRUE)
}
# validateMapUnit --------------------------------------------------------------
#' Validate map unit.
#'
#' @param x Map unit, or object with a \code{'map.unit'} attribute.
#' @param map.type Map type for which map unit should be validated. If not
#' specified, map unit is validated for all map types.
#'
#' @return \code{TRUE} if the specified map unit is a valid map unit
#' for the given map type(s); otherwise, returns error.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname validateMapUnit
validateMapUnit <- function(x, map.type=NULL) {
UseMethod('validateMapUnit', x)
}
# validateMapUnit.character ----------------------------------------------------
#' @rdname validateMapUnit
validateMapUnit.character <- function(x, map.type=NULL) {
if ( ! is.null(map.type) ) {
if ( ! map.type %in% names(const$map.info) ) {
stop("unknown map type - '", map.type, "'")
}
known.map.units <- rownames(const$map.info[[map.type]])
} else {
known.map.units <- const$known.map.units
}
if ( length(x) != 1 || is.na(x) || ! x %in% known.map.units ) {
stop("invalid map unit - '", toString(x), "'")
}
return(TRUE)
}
# validateMapUnit.default ------------------------------------------------------
#' @rdname validateMapUnit
validateMapUnit.default <- function(x, map.type=NULL) {
map.unit <- getMapUnit(x)
if ( length(map.unit) == 1 && is.na(map.unit) ) {
stop("no map unit found")
}
validateMapUnit(map.unit, map.type=map.type)
return(TRUE)
}
# validatePhysicalMapUnit ------------------------------------------------------
#' Validate physical map unit.
#'
#' @param x Map unit, or object with a \code{'map.unit'} attribute.
#'
#' @return \code{TRUE} if map unit is a valid physical map unit;
#' otherwise, returns error.
#'
#' @template section-mapunits
#'
#' @keywords internal
#' @rdname validatePhysicalMapUnit
validatePhysicalMapUnit <- function(x) {
return( validateMapUnit(x, map.type='pmap') )
}
# End of map.R #################################################################
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.