#' Initialize a new Trajectory object.
#'
#' @param input trajectory to model cell progression. Wrapped result
#' of a trajectory inference by the dynverse/dynwrap library
#' @return Trajectory object
Trajectory <- function(input) {
# Create the adjacency matrix
network <- as.data.frame(input$milestone_network)
milestone_ids <- union(unique(network$from), unique(network$to))
adjMat <- matrix(0.0, nrow = length(milestone_ids), ncol = length(milestone_ids),
dimnames = list(milestone_ids, milestone_ids))
for (i in seq(nrow(network))){
from <- as.character(network[i, "from"])
to <- as.character(network[i, "to"])
edgeLength <- network[i, "length"]
adjMat[from, to] <- edgeLength
adjMat[to, from] <- edgeLength
}
progressions <- as.data.frame(input$progressions)
rownames(progressions) <- progressions$cell_id
progressions$cell_id <- NULL
colnames(progressions) <- gsub("percentage", "position",
colnames(progressions))
if (!("from" %in% colnames(progressions))){
stop("input Trajectory missing 'from' column")
}
if (!("to" %in% colnames(progressions))){
stop("input Trajectory missing 'to' column")
}
if (length(setdiff(unique(progressions$from), milestone_ids)) > 0) {
stop("milestones in progressions$from don't match those in milestone_network")
}
if (length(setdiff(unique(progressions$to), milestone_ids)) > 0) {
stop("milestones in progressions$to don't match those in milestone_network")
}
.Object <- new("Trajectory", adjMat = adjMat, progressions = progressions)
return(.Object)
}
#' Compute KNN weights based on geodesic distances for Trajectory objects
#' @importFrom stats quantile
#' @importFrom Matrix rowSums
#' @importFrom Matrix sparseMatrix
#' @importFrom matrixStats rowMaxs
#' @param object a Trajectory object
#' @param K Number of neighbors to consider.
#' @return a list of two items:
#' indices: matrix, cells X neighbors
#' Each row specifies indices of nearest neighbors
#' weights: matrix, cells X neighbors
#' Corresponding weights to nearest neighbors
setMethod("computeKNNWeights", signature(object = "Trajectory"),
function(object, K = round(sqrt(nrow(object)))) {
edgePos <- object@progressions$position
names(edgePos) <- rownames(object@progressions)
edgeAssoc <- t(object@progressions[, c("from", "to")])
colnames(edgeAssoc) <- rownames(object@progressions)
distmat <- calculateTrajectoryDistances(adjMat = object@adjMat,
edgeAssoc = edgeAssoc,
edgePos = edgePos)
kQuantile <- K / nrow(object@progressions)
knnmat <- apply(distmat, 1, function(d) {
partition <- quantile(d, kQuantile, na.rm=T)
d[d > partition] <- Inf
return(d)
})
nn <- t(apply(distmat, 1, function(r) {
order(r)[1:K]
}))
d <- lapply(seq(nrow(nn)), function(i) {
distmat[i, nn[i, ]]
})
d <- do.call(rbind, d)
d[is.na(d)] = 0
sigma <- rowMaxs(d)
sigma[sigma == 0] <- 1.0 # occurs if all nearest neighbors at same point
sparse_weights <- exp(-1 * (d * d) / sigma ^ 2)
# Normalize row sums = 1
weightsNormFactor <- rowSums(sparse_weights)
weightsNormFactor[weightsNormFactor == 0] <- 1.0
sparse_weights <- sparse_weights / weightsNormFactor
rownames(nn) <- rownames(object)
rownames(d) <- rownames(object)
return(list(indices = nn, weights = sparse_weights))
})
#' Generate meta-data associated with this trajectory
#'
#' Creates a categorical variable mapping cells to edges
#' and numeric variables for their position along edges
#'
#' @importFrom igraph graph_from_adjacency_matrix
#' @importFrom igraph ends
#' @importFrom igraph E
#'
#' @param trajectory Trajectory on which to operate
#' @return metaData dataframe with meta-data
createTrajectoryMetaData <- function(trajectory){
adjMat <- trajectory@adjMat
progressions <- trajectory@progressions
net <- igraph::graph_from_adjacency_matrix(adjMat, weighted = TRUE,
mode = "undirected")
edges <- igraph::ends(net, igraph::E(net), names = TRUE)
meta <- data.frame(row.names = rownames(progressions))
meta[, "TrajectoryEdge"] <- ""
for (i in seq(nrow(edges))) {
from <- edges[i, 1]
to <- edges[i, 2]
cells <- progressions[
(progressions$from == from) & (progressions$to == to),
, drop = FALSE
]
cells_i <- progressions[
(progressions$from == to) & (progressions$to == from),
, drop = FALSE
]
cells_i$position <- 1 - cells_i$position
cells <- rbind(cells, cells_i)
edge_name <- paste(from, to, sep = "->")
position_var <- paste0("Position: ", edge_name)
meta[rownames(cells), "TrajectoryEdge"] <- edge_name
meta[, position_var] <- NA
meta[rownames(cells), position_var] <- cells$position
}
meta$TrajectoryEdge <- as.factor(meta$TrajectoryEdge)
return(meta)
}
#' Generate 2d representations of a trajectory model
#'
#' Creates 2d layouts of the milestone network and translates the
#' cell positions into these layouts
#'
#' @importFrom igraph graph_from_adjacency_matrix
#' @importFrom igraph ends
#' @importFrom igraph layout_with_fr
#' @importFrom igraph layout_with_dh
#' @importFrom igraph layout_as_tree
#' @importFrom igraph layout_with_mds
#' @importFrom igraph E
#' @importFrom igraph V
#'
#' @param trajectory Trajectory on which to operate
#' @return trajectoryProjections list of TrajectoryProjection
generateTrajectoryProjections <- function(trajectory) {
progressions <- trajectory@progressions
adjMat <- trajectory@adjMat
net <- igraph::graph_from_adjacency_matrix(adjMat, weighted = TRUE,
mode = "undirected")
adjMatInv <- adjMat ** -1
adjMatInv[is.infinite(adjMatInv)] <- 0
# some algorithms use weights to represent 'inverse' distance
invnet <- igraph::graph_from_adjacency_matrix(adjMatInv, weighted = TRUE,
mode = "undirected")
edges <- igraph::ends(net, igraph::E(net), names = TRUE)
adjMatBinary <- (adjMat > 0) * 1
tp_list <- list()
# layout with Fruchterman-Reingold algorithm
vData <- igraph::layout_with_fr(invnet)
rownames(vData) <- igraph::V(net)$name
pData <- translateCellPositions(progressions, vData, edges)
tp <- TrajectoryProjection(name = "FR", pData = pData, vData = vData,
adjMat = adjMatBinary)
tp_list <- c(tp_list, tp)
# layout with Davidson-Harel
vData <- igraph::layout_with_dh(net)
rownames(vData) <- igraph::V(net)$name
pData <- translateCellPositions(progressions, vData, edges)
tp <- TrajectoryProjection(name = "DH", pData = pData, vData = vData,
adjMat = adjMatBinary)
tp_list <- c(tp_list, tp)
# layout with Davidson-Harel
vData <- igraph::layout_as_tree(net)
rownames(vData) <- igraph::V(net)$name
pData <- translateCellPositions(progressions, vData, edges)
tp <- TrajectoryProjection(name = "Tree", pData = pData, vData = vData,
adjMat = adjMatBinary)
tp_list <- c(tp_list, tp)
# layout with MDS
vData <- igraph::layout_with_mds(net)
rownames(vData) <- igraph::V(net)$name
pData <- translateCellPositions(progressions, vData, edges)
tp <- TrajectoryProjection(name = "MDS", pData = pData, vData = vData,
adjMat = adjMatBinary)
tp_list <- c(tp_list, tp)
names(tp_list) <- vapply(tp_list, function(x) x@name, FUN.VALUE = "")
return(tp_list)
}
#' Translate cell positions
#'
#' Maps cell positions between edges
#'
#' @importFrom stats rnorm
#' @param progressions data.frame describing cell positions between milestones
#' @param vData Mx2 matrix mapping miletones into 2d
#' @param edges Edges x 2 matrix describing connectivity between edges
#' @return pData Cells x 2 matrix with cell positions in 2d
translateCellPositions <- function(progressions, vData, edges) {
pData <- lapply(seq(nrow(edges)), function(i) {
from <- edges[i, 1]
to <- edges[i, 2]
edge_dist <- sum( (vData[from, ] - vData[to, ]) ** 2) ** .5
cells <- progressions[
(progressions$from == from) & (progressions$to == to),
, drop = FALSE
]
cells_i <- progressions[
(progressions$from == to) & (progressions$to == from),
, drop = FALSE
]
cells_i$position <- 1 - cells_i$position
cells <- rbind(cells, cells_i)
coordinates <- matrix(numeric(nrow(cells) * 2),
nrow = nrow(cells), ncol = 2,
dimnames = list(rownames(cells), c("x", "y")))
coordinates[, "x"] <- cells$position * edge_dist
coordinates[, "y"] <- rnorm(nrow(coordinates), sd = .1)
dx <- vData[to, 1] - vData[from, 1]
dy <- vData[to, 2] - vData[from, 2]
sinTheta <- dy / edge_dist
cosTheta <- dx / edge_dist
R <- matrix(c(cosTheta, sinTheta, -1 * sinTheta, cosTheta), nrow = 2)
coordinates <- coordinates %*% t(R) # rotation
coordinates <- t(t(coordinates) + vData[from, ]) # offset
return(coordinates)
})
pData <- do.call(rbind, pData)
pData <- pData[rownames(progressions), , drop = FALSE]
return(pData)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.