R/pseudotime.R

#' Compute the pseudotime associted with a path
#'
#' @param ProjStruct A projection structure list as obtained by the project_point_onto_graph function
#' @param NodeSeq string, a sequence of nodes that forms a path in the graph.
#'
#' @description Compute the pseudotime associated to the points over the specified path.
#' Note that the pseudotime of points not associted with any edges of the specified path 
#' will be set to NA.
#'
#' @return a list containing three elements:
#' \itemize{
#'  \item{Pt:}{ numerical vector, the pseudotime associted with each points in the specified path}
#'  \item{PathLen:}{ The total length of the path}
#'  \item{NodePos:}{ The pseudotime associated with the of the nodes of the graph}
#' }
#' @export
#'
#' @examples
#' 
getPseudotime <- function(ProjStruct, NodeSeq){
  
  Pt <- rep(NA, nrow(ProjStruct$X_projected))
  tLen <- 0
  NodePos <- 0
  
  for(i in 2:length(NodeSeq)){
    SelEdgID <- which(
      apply(
        apply(ProjStruct$Edges, 1, function(x) {
          x %in% NodeSeq[(i-1):i]
        }), 2, all)
    )
    
    # print(paste(i, SelEdgID, tLen))
    
    if(length(SelEdgID) != 1){
      stop("Path not found in the graph")
    }
    
    Selected <- ProjStruct$EdgeID == SelEdgID
    if(all(ProjStruct$Edges[SelEdgID,] == NodeSeq[(i-1):i])){
      rev <- FALSE
    } else {
      rev <- TRUE
    }
    
    Pos <- ProjStruct$ProjectionValues[Selected]
    Pos[Pos <= 0] <- 0
    Pos[Pos >= 1] <- 1
    
    if(rev){
      Pos <- 1 - Pos
    }
    
    if(sum(Selected)>0){
      Pt[Selected] <- tLen + Pos*ProjStruct$EdgeLen[SelEdgID]
    }
    
    tLen <- tLen + ProjStruct$EdgeLen[SelEdgID]
    NodePos <- cbind(NodePos, tLen)
  }
  
  return(list(Pt = Pt, PathLen = tLen, NodePos = as.vector(NodePos)))
  
}









#' Compare feature values over different branches
#'
#' @param X the data matrix, note that features must be names, i.e. column names must be non-empty
#' @param Paths a list of paths used to construct the pseudotime. Note that the beginning of the pseudotime
#' will be placed in correspondence of the fisrst node for each path. Hence the comparison is meaningfull
#' only if all the path begin from the same node (Although, this will not be enforced by the function) 
#' @param TargetPG the ElPiGraph structure to be used
#' @param Partition A partition vactor attributing each point to at most one node
#' @param PrjStr A projecturion structure, as produced by project_point_onto_graph
#' @param Main string, the main title of the plot
#' @param Features either a string vector or a positive integer. If a string vector, it will be interpreted as
#' the name of the features to plot (matched to the colnames of X). If positive integer, it will be interpreted
#' as the number of the topmost interesting features according to the metric specified by Mode
#' @param ylab string, the label of the y axis
#' @param alpha numeric between 0 and 1, the trasparency of the points
#' @param ScalePT boolean, should the pseudotime be normalized across the branches?
#' @param Mode string, the feature seletion mode used to determine the most interesting genes. It can be
#'  \itemize{
#'  \item{'Var'}{ The genes with the largest variance across the points associated to the paths}
#'  \item{'Fano'}{ The genes with the largest Fano factor (variance over mean) across the points associated to the paths}
#'  \item{'Mad'}{ The genes with the largest median absolute deviation across the points associated to the paths}
#'  \item{'Mad/Median'}{ The genes with the largest ratio of median absolute deviation to median across the points associated to the paths}
#'  \item{'MI'}{ The genes with the largest mutual information (across all the paths)}
#'  \item{'MI.DB'}{ The genes with the largest mutual inforlation (across differential paths)}
#'  \item{'KW'}{ The genes with the largest difference, as computed by the Kruskal-Wallis test (across differential paths)}
#'  \item{'Cor.DB'}{ The genes whose product of Spearman correlation (w.r.t. the pseudotime ordering) is the largest}
#' }
#' @param Log boolean, should a log scale be used on the y axis
#' @param BootPG if not NULL, a list of resampled ElPiGraph structures that will be used to compute confidence intervals
#' @param GroupsLab character of factor, category information for the cells present in the matrix.
#' @param TrajCol boolean, should the points be colored by trajectory? If FALSE GroupsLab will be used.
#' @param Conf the confidence level.
#' @param AllBP boolean. If TRUE, the confidence level be displayed for all the branching points present on the selected branches.
#' If FALSE, the confidence level will be displayed only for the branching points 
#' @param facet_rows integer, the number of rows per panel
#' @param facet_cols integer, the number of cols per panel
#' @param Span the span parameter of the loess fitter
#' @param PlotOrg boolean, should the original gene expression be reported. If false oly the smooth will be plotted.
#' @param ModePar additional parameters for the feature selection mode used
#' @param n.cores integer, the number of parallel processes to use.
#' @param ClusType string, the type of cluster to use if n.cores is larger than 1
#' @param ReturnGenes boolean, should the list of genes be returned instead of the plots?
#' @param OnlyIsomorphicBoot boolean. If TRUE only reampled ElPiGraph structures with the same number of branching points of the target
#' structure will be used to compute confidence intervals. If FALSE, all of the structures will be used.
#' @param TrimmingRadius numeric, the trimming radius used to project the position of the branching points of the resampled trees on the
#' pseudotime
#'
#' @return
#' @export
#'
#' @examples
CompareOnBranches <- function(X,
                              Paths,
                              TargetPG,
                              BootPG = NULL,
                              OnlyIsomorphicBoot = TRUE,
                              GroupsLab = NULL,
                              PlotOrg = TRUE,
                              TrajCol = FALSE,
                              Conf = .95,
                              AllBP = FALSE,
                              TrimmingRadius = Inf,
                              Partition,
                              PrjStr,
                              Main = "",
                              ylab = "Feature value",
                              alpha = .3,
                              ScalePT = FALSE,
                              Features = 4,
                              facet_rows = 3,
                              facet_cols = 3,
                              Mode = "Var",
                              ModePar = NULL,
                              Log = FALSE,
                              Span = .1,
                              n.cores = 1,
                              ClusType = "SOCK",
                              ReturnGenes = FALSE) {
  
  if(is.null(GroupsLab)){
    GroupsLab = factor(rep("N/A", nrow(X)))
  }
  
  if(is.null(names(GroupsLab))){
    names(GroupsLab) = rownames(X)
  }
  
  CombDF <- NULL
  
  if(is.numeric(Features) & (Mode == "Var" | Mode == "Fano")){
    
    SharedPath <- unique(unlist(Paths, use.names = FALSE))
    tX <- X[Partition %in% as.integer(SharedPath), ]
    
    if(n.cores == 1){
      GenesByVar <- apply(tX, 2, var)
    } else {
      if(ClusType == "FORK"){
        cl <- parallel::makeForkCluster(n.cores)
        GenesByVar <- parallel::parApply(cl, tX, 2, var)
      } else {
        cl <- parallel::makePSOCKcluster(n.cores)
        parallel::clusterExport(cl, "tX")
        GenesByVar <- parallel::parApply(cl, tX, 2, var)
      }
      parallel::stopCluster(cl)
    }
    
    if(length(GenesByVar) <= Features){
      Features = length(GenesByVar)
    }
    
    if(Mode == "Fano"){
      print("Feature selection by Fano factor")
      GenesByVar <- GenesByVar/colMeans(tX)
      if(any(!is.finite(GenesByVar))){
        GenesByVar[!is.finite(GenesByVar)] <- 0
      }
    } else {
      print("Feature selection by variance")
    }
    
    if(ReturnGenes){
      return(sort(GenesByVar, decreasing = TRUE))
    }
    
    Features <- names(sort(GenesByVar, decreasing = TRUE))[1:Features]
    
  }
  
  if(is.numeric(Features) & (Mode == "Mad" | Mode == "Mad/Median")){
    
    SharedPath <- unique(unlist(Paths, use.names = FALSE))
    tX <- X[Partition %in% as.integer(SharedPath), ]
    
    if(n.cores == 1){
      GenesByVar <- apply(tX, 2, mad)
    } else {
      if(ClusType == "FORK"){
        cl <- parallel::makeForkCluster(n.cores)
        GenesByVar <- parallel::parApply(cl, tX, 2, mad)
      } else {
        cl <- parallel::makePSOCKcluster(n.cores)
        parallel::clusterExport(cl, "tX")
        GenesByVar <- parallel::parApply(cl, tX, 2, mad)
      }
      parallel::stopCluster(cl)
    }
    
    if(length(GenesByVar) <= Features){
      Features = length(GenesByVar)
    }
    
    if(Mode == "Mad/Median"){
      
      print("Feature selection by Mad / Median")
      
      GenesByVar <- GenesByVar/apply(tX, 2, median)
      if(any(!is.finite(GenesByVar))){
        GenesByVar[!is.finite(GenesByVar)] <- 0
      }
      
    } else {
      print("Feature selection by Mad")
    }
    
    if(ReturnGenes){
      return(sort(GenesByVar, decreasing = TRUE))
    }
    
    Features <- names(sort(GenesByVar, decreasing = TRUE))[1:Features]
    
  }
  
  if(is.numeric(Features) & Mode == "MI"){
    
    print("Feature selection by mutual information")
    
    Path_MI <- sapply(Paths, function(x){
      PtOnPath <- getPseudotime(ProjStruct = PrjStr, NodeSeq = x)
      
      PtVect <- PtOnPath$Pt
      Seleced <- !is.na(PtVect)
      
      FactoredPT <- factor(PtVect[Seleced])
      
      if(n.cores == 1){
        AllMI <- sapply(1:ncol(X), function(j) {
          infotheo::mutinformation(X = factor(X[Seleced,j]), FactoredPT)
        })
      } else {
        if(ClusType == "FORK"){
          cl <- parallel::makeForkCluster(n.cores)
          AllMI <- parallel::parSapply(cl, 1:ncol(X), function(j) {
            infotheo::mutinformation(X = factor(X[Seleced,j]), FactoredPT)
          })
        } else {
          cl <- parallel::makePSOCKcluster(n.cores)
          parallel::clusterExport(cl, c("X", "Selected", "FactoredPT"))
          AllMI <- parallel::parSapply(cl, 1:ncol(X), function(j) {
            infotheo::mutinformation(X = factor(X[Seleced,j]), FactoredPT)
          })
        }
        parallel::stopCluster(cl)
      }
      
      return(AllMI)
      
    })
    
    if(is.null(ModePar)){
      ModePar = "max"
    }
    
    if(!any(ModePar %in% c("min", "max"))){
      ModePar = "max"
    }
    
    if(ModePar == "max"){
      MiVect <- apply(Path_MI, 1, max)
    }
    if(ModePar == "min"){
      MiVect <- apply(Path_MI, 1, min)
    }
    
    names(MiVect) <- colnames(X)
    OrdMI <- order(MiVect, decreasing = TRUE)
    
    if(ReturnGenes){
      RetVect <- MiVect[OrdMI]
      return(RetVect)
    }
    
    Features <- colnames(X)[OrdMI[1:Features]]
    
  }
  
  if(is.numeric(Features) & Mode == "MI.DB"){
    
    print("Feature selection by mutual information on differential branches")
    
    SharedPath <- unique(unlist(Paths, use.names = FALSE))
    for(i in 1:length(Paths)){
      SharedPath <- intersect(SharedPath, Paths[[i]])
    }
    
    DiffPath <- list()
    for(i in 1:length(Paths)){
      DiffPath[[i]] <- (Paths[[i]])[!(Paths[[i]] %in% SharedPath)]
    }
    
    Path_MI <- sapply(DiffPath, function(x){
      PtOnPath <- getPseudotime(ProjStruct = PrjStr, NodeSeq = x)
      
      PtVect <- PtOnPath$Pt
      Seleced <- !is.na(PtVect)
      
      FactoredPT <- factor(PtVect[Seleced])
      
      
      if(n.cores == 1){
        AllMI <- sapply(1:ncol(X), function(j) {
          infotheo::mutinformation(X = factor(X[Seleced,j]), FactoredPT)
        })
      } else {
        if(ClusType == "FORK"){
          cl <- parallel::makeForkCluster(n.cores)
          AllMI <- parallel::parSapply(cl, 1:ncol(X), function(j) {
            infotheo::mutinformation(X = factor(X[Seleced,j]), FactoredPT)
          })
        } else {
          cl <- parallel::makePSOCKcluster(n.cores)
          parallel::clusterExport(cl, c("X", "Selected", "FactoredPT"))
          AllMI <- parallel::parSapply(cl, 1:ncol(X), function(j) {
            infotheo::mutinformation(X = factor(X[Seleced,j]), FactoredPT)
          })
        }
        parallel::stopCluster(cl)
      }
      
      return(AllMI)
      
    })
    
    if(is.null(ModePar)){
      ModePar = "max"
    }
    
    if(!any(ModePar %in% c("min", "max"))){
      ModePar = "max"
    }
    
    if(ModePar == "max"){
      MiVect <- apply(Path_MI, 1, max)
    }
    if(ModePar == "min"){
      MiVect <- apply(Path_MI, 1, min)
    }
    
    names(MiVect) <- colnames(X)
    OrdMI <- order(MiVect, decreasing = TRUE)
    
    if(ReturnGenes){
      RetVect <- MiVect[OrdMI]
      return(RetVect)
    }
    
    Features <- colnames(X)[OrdMI[1:Features]]
    
  }
  
  if(is.numeric(Features) & Mode == "KW"){
    
    print("Feature selection by Kruskal-Wallis rank sum test on differential branches")
    
    SharedPath <- unique(unlist(Paths, use.names = FALSE))
    for(i in 1:length(Paths)){
      SharedPath <- intersect(SharedPath, Paths[[i]])
    }
    
    DiffPath <- list()
    for(i in 1:length(Paths)){
      DiffPath[[i]] <- (Paths[[i]])[!(Paths[[i]] %in% SharedPath)]
    }
    
    FilPart <- Partition
    FilPart[] <- 0
    
    for(i in 1:length(DiffPath)){
      if(is.null(ModePar)){
        FilPart[Partition %in% as.integer(DiffPath[[i]])] <- i
      } else {
        SelNodes <- c(
          max(1, length(DiffPath[[i]]) - ModePar),
          length(DiffPath[[i]])
        )
        FilPart[Partition %in% as.integer(DiffPath[[i]])[SelNodes]] <- i
      }
    }
    
    tX <- X[FilPart != 0, ]
    FilPart <- FilPart[FilPart != 0]
    
    if(n.cores == 1){
      PVVect <- apply(tX, 2, function(x){
        kruskal.test(x, FilPart)$p.val
      })
    } else {
      if(ClusType == "FORK"){
        cl <- parallel::makeForkCluster(n.cores)
        PVVect <- parallel::parApply(cl, tX, 2, function(x){
          kruskal.test(x, FilPart)$p.val
        })
      } else {
        cl <- parallel::makePSOCKcluster(n.cores)
        parallel::clusterExport(cl, c("tX", "FilPart"))
        PVVect <- parallel::parApply(cl, tX, 2, function(x){
          kruskal.test(x, FilPart)$p.val
        })
      }
      parallel::stopCluster(cl)
    }
    
    if(ReturnGenes){
      RetVect <- sort(PVVect)
      return(RetVect)
    }
    
    Features <- colnames(X)[order(PVVect, decreasing = FALSE)[1:Features]]
    
  }
  
  if(is.numeric(Features) & Mode == "Cor.DB"){
    
    print("Feature selection by diverging correlation on differential branches")
    
    SharedPath <- unique(unlist(Paths, use.names = FALSE))
    for(i in 1:length(Paths)){
      SharedPath <- intersect(SharedPath, Paths[[i]])
    }
    
    DiffPath <- list()
    for(i in 1:length(Paths)){
      DiffPath[[i]] <- (Paths[[i]])[!(Paths[[i]] %in% SharedPath)]
    }
    
    Path_MI <- sapply(DiffPath, function(x){
      PtOnPath <- getPseudotime(ProjStruct = PrjStr, NodeSeq = x)
      
      PtVect <- PtOnPath$Pt
      Seleced <- !is.na(PtVect)

      suppressWarnings(
        sapply(1:ncol(X), function(j) {
          cor(X[Seleced,j], PtVect[Seleced], method = "spe")
        })
      )
      
    })
    
    MiVect <- apply(Path_MI, 1, function(x){sign(prod(x, na.rm = TRUE))*max(abs(x), na.rm = TRUE)})
    
    OrdMI <- order(MiVect, decreasing = FALSE, na.last = TRUE)
    
    if(ReturnGenes){
      names(MiVect) <- colnames(X)
      RetVect <- sort(PVVect)
      return(RetVect)
    }
    
    Features <- colnames(X)[OrdMI[1:Features]]
    
  }
  
  
  tX <- X[, colnames(X) %in% Features]
  if(is.null(dim(tX))){
    dim(tX) <- c(length(tX), 1)
    rownames(tX) <- rownames(X)
    colnames(tX) <- Features
  }
  
  if(!is.null(BootPG)){
    BrPos <- lapply(BootPG, function(x){
      TB <- table(as.vector(x$Edges$Edges))
      return(x$NodePositions[as.integer(names(TB[TB>2])),])
    })
    
    TB <- table(as.vector(TargetPG$Edges$Edges))
    TargetBP <- as.integer(names(TB[TB>2]))
    
    BPMat <- TargetPG$NodePositions[TargetBP,]
    if(is.null(dim(BPMat))){
      dim(BPMat) <- c(1, length(BPMat))
    }
    
    NodeIdx <- lapply(BrPos, function(x){
      if(is.null(dim(x))){
        dim(x) <- c(1, length(x))
      }
      DMat <- distutils::PartialDistance(x, BPMat)
      
      if(OnlyIsomorphicBoot){
        if(ncol(DMat) != nrow(DMat)){
          return(NA)
        } else {
          apply(DMat, 1, which.min)
        }
      } else {
        apply(DMat, 1, which.min)
      }
      
    })
    
    BrPos <- BrPos[sapply(NodeIdx, function(x){all(!is.na(x))})]
    BrPos.Ass <- NodeIdx[sapply(NodeIdx, function(x){all(!is.na(x))})]
    BrPos.Ass <- unlist(BrPos.Ass)
    
    BrPos.Mat <- do.call(rbind, BrPos)
    
    tPart <- PartitionData(X = BrPos.Mat, NodePositions = TargetPG$NodePositions, TrimmingRadius = TrimmingRadius)
    
    tProj <- project_point_onto_graph(X = BrPos.Mat,
                                           NodePositions = TargetPG$NodePositions,
                                           Edges = TargetPG$Edges$Edges,
                                           Partition = tPart$Partition)
    
  }
  
  
  if(ScalePT){
    
    # Get the end point for each branch
    BP <- sapply(Paths, function(x){
      c(x[1], x[length(x)])
    })
    
    # Get the unique end points
    BP <- unique(as.vector(BP))
    
    # for each pair of paths
    for(i in 1:length(Paths)){
      for(j in 1:length(Paths)){
        if(i == j){
          next()
        }
        Range <- range(which(Paths[[i]] %in% Paths[[j]]))
        if(Range[1] == Range[2]){
          Range <- Range[1]
        }
        BP <- union(BP, (Paths[[i]])[Range])
      }
    }
    
    BPxPath <- sapply(Paths, function(x){
      sum(x %in% BP)
    })
    
    NormStep <- 1/(max(BPxPath)-1)
    
  }
  
  for(i in 1:length(Paths)){
    PtOnPath <- getPseudotime(ProjStruct = PrjStr, NodeSeq = Paths[[i]])
    
    PtVect <- PtOnPath$Pt
    names(PtVect) <- rownames(tX)
    MetlExp <- reshape::melt(tX)
    
    MetlExp$X1 <- as.character(MetlExp$X1)
    
    if(!is.null(names(Paths)[i])){
      TrjName <- names(Paths)[i]
    } else {
      TrjName <- i
    }
    
    PtCI <- NULL
    
    if(!is.null(BootPG)){
      PtOnPath.Boot <- getPseudotime(ProjStruct = tProj, NodeSeq = Paths[[i]])
      
      # PtVect.Boot <- PtOnPath.Boot$Pt
      
      # OrgBR <- names(which(table(TargetPG$Edges$Edges)>2))
      
      # BrPos <- PtOnPath.Boot$NodePos[which(Paths[[i]] %in% OrgBR)]
      # 
      # BrDist <- sapply(as.list(BrPos), function(x){
      #   abs(x - PtVect.Boot)
      # })
      # 
      # Associated <- apply(BrDist, 1, function(x){
      #   if(any(is.na(x))){
      #     return(0)
      #   } else {
      #     return(which.min(x))
      #   }
      # })
      
      Split.Pt <- split(PtOnPath.Boot$Pt, BrPos.Ass)
      
      PtCI <- do.call(rbind,
              lapply(Split.Pt, function(x){
                quantile(x, c(1-Conf/2, Conf/2), na.rm = TRUE)
              })
      )
      
    }
    
    CIDf <- NULL
    
    if(ScalePT){
      
      RenormPoints <- PtOnPath$NodePos[which(Paths[[i]] %in% BP)]
      
      if(RenormPoints[1] == 0 & length(RenormPoints) == 2){
        RenormPoints <- RenormPoints[-1]
      } else {
        RenormPoints[1] <- -1
      }
      
      if(!is.null(BootPG)){
        PtVect <- c(PtVect, as.vector(PtCI))
      }
      
      RenormPt <- PtVect
      
      if(length(RenormPoints) > 1){
        for(j in 1:(length(RenormPoints)-1)){
          
          ToRenorm <- RenormPt[PtVect>RenormPoints[j] &
                                 PtVect<=RenormPoints[j+1] &
                                 !is.na(PtVect)]
          
          if(RenormPoints[j] != -1){
            ToRenorm <- ToRenorm - RenormPoints[j]
            ToRenorm <- ToRenorm/(RenormPoints[j+1]  - RenormPoints[j])
          } else {
            ToRenorm <- ToRenorm/(RenormPoints[j+1])
          }
          
          if(j == (length(RenormPoints)-1) ){
            ToRenorm <- ToRenorm*(1-NormStep*(j-1)) + NormStep*(j-1)
          } else {
            ToRenorm <- ToRenorm*NormStep + NormStep*(j-1)
          }
          
          RenormPt[PtVect>RenormPoints[j] &
                     PtVect<=RenormPoints[j+1] &
                     !is.na(PtVect)] <- ToRenorm
        }
      } else {
        
        ToRenorm <- RenormPt[!is.na(PtVect)]
        ToRenorm <- ToRenorm/RenormPoints
        
        RenormPt[!is.na(PtVect)] <- ToRenorm
      }
      
      names(RenormPt) <- names(PtVect)
      
      if(!is.null(BootPG)){
        CIRenorm <- RenormPt[
          (length(RenormPt)-length(PtCI)+1):length(RenormPt)
          ]
        
        RenormPt <- RenormPt[1:(length(RenormPt)-length(PtCI))]
        
        CIDf <- rbind(
          CIDf, data.frame(Pt.low = CIRenorm[1:(length(CIRenorm)/2)],
                           Pt.high = CIRenorm[(length(CIRenorm)/2+1):length(CIRenorm)],
                           Bp = TargetBP)
        )
        
      }
      
      CombDF <- rbind(
        CombDF, data.frame(Pt = RenormPt[MetlExp$X1],
                           gene = MetlExp$X2,
                           exp = MetlExp$value,
                           traj = TrjName,
                           pop = GroupsLab[MetlExp$X1])
      )
      
    } else {
      CombDF <- rbind(
        CombDF, data.frame(Pt = PtVect[MetlExp$X1],
                           gene = MetlExp$X2,
                           exp = MetlExp$value,
                           traj = TrjName,
                           pop = GroupsLab[MetlExp$X1])
      )
      
      if(!is.null(BootPG)){
        CIDf <- rbind(
          CIDf, data.frame(Pt.low = PtCI[,1],
                           Pt.high = PtCI[,2],
                           Bp = TargetBP
          )
        )
      }
      
    }
    
  }
  
  CombDF$traj <- factor(CombDF$traj)
  
  CombDF <- CombDF[!is.na(CombDF$Pt), ]
  
  if(nrow(CombDF)>0){
    if(TrajCol){
      p <- ggplot2::ggplot(CombDF,
                           ggplot2::aes(x=Pt, y=exp, color = traj)) +
        ggplot2::scale_color_discrete("Branch")
    } else {
      p <- ggplot2::ggplot(CombDF,
                           ggplot2::aes(x=Pt, y=exp, color = pop))
    }
    
    if(PlotOrg){
      p <- p + ggplot2::geom_point(alpha = alpha)
    }
      
    
    if(ScalePT){
      p <- p + ggplot2::geom_smooth(ggplot2::aes(color = traj), method = "loess", span = Span) + 
        ggplot2::geom_vline(xintercept = seq(from=0, to=1, by=NormStep), linetype = 'dotted') +
        ggplot2::labs(title = Main, y = ylab, x = "Normalized pseudotime")
    } else {
      p <- p + ggplot2::geom_smooth(ggplot2::aes(color = traj), method = "loess", span = Span) +
        ggplot2::labs(title = Main, y = ylab, x = "Pseudotime")
    }
    
    if(Log){
      p <- p + ggplot2::scale_y_log10()
    }
    
    if(!is.null(BootPG)){
      
      if(!AllBP){
        
        TB.1 <- table(TargetPG$Edges$Edges)
        AllBr <- names(TB.1[TB.1>2])
        
        ToUse <- NULL
        
        for(i in AllBr){
          TT <- lapply(Paths, function(sel){
            
            id <- which(sel == i)
            
            if(length(id)==0){
              return(NULL)
            }
            
            if(id==0){
              id_low <- id
            } else {
              id_low <- id-1
            }
            
            if(id==length(sel)){
              id_high <- id
            } else {
              id_high <- id+1
            }
            
            return(
              sel[id_low:id_high]
            )
          })
          
          if(length(unique(unlist(TT)))>3){
            ToUse <- c(ToUse, i)
          }
          
        }
        
        CIDf <- CIDf[CIDf$Bp %in% ToUse,]
        
      }
      
      
      if(nrow(CIDf)>0){
        p <- p + ggplot2::geom_rect(data = CIDf,
                                    mapping = ggplot2::aes(xmin = Pt.low, xmax = Pt.high, ymin=-Inf, ymax=Inf), fill = "black",
                                    inherit.aes = FALSE, alpha = .25) + ggplot2::guides(fill = "none")
      }
      
    }
    
    Pages <- ceiling(length(Features) / (facet_rows*facet_cols))
    
    # print(length(Features))
    print(paste(Pages, "pages will be plotted"))
    
    if(Pages > 1){
      p <- lapply(as.list(1:Pages), function(i){
        p + ggforce::facet_wrap_paginate(~gene, nrow = facet_rows, ncol = facet_cols, scales = "free_y", page = i)
      })
    } else {
      p <- p + ggplot2::facet_wrap(~gene, scales = "free_y", nrow = facet_rows, ncol = facet_cols)
    }
    
    return(p)
    
  } else {
    
    return(NULL)
    
  }
  
}










# 
# 
# ExplorePtUnc <- function(X,
#                          Path,
#                          TargetPG,
#                          BootPG = NULL,
#                          Partition,
#                          Partition.Boot = list(),
#                          PrjStr,
#                          PrjStr.Boot = list(),
#                          Feature,
#                          Main = "",
#                          ylab = "Feature value",
#                          alpha = .3,
#                          ScalePT = FALSE,
#                          Log = FALSE) {
#   
#   CombDF <- NULL
#   
#   tX <- X[, colnames(X) %in% Features]
#   MetlExp <- reshape::melt(tX)
#   
#   
#   PtOnPath <- getPseudotime(ProjStruct = PrjStr, NodeSeq = Path)
#   PtVect <- PtOnPath$Pt
#   names(PtVect) <- rownames(tX)
#   
#   CombDF <- rbind(
#     CombDF, data.frame(Pt = PtVect[MetlExp$X1],
#                        gene = MetlExp$X2,
#                        exp = MetlExp$value,
#                        traj = 0)
#   ) 
#     
#   for(i in 1:length(PrjStr.Boot)){
#     
#   }
#   
#     
#     
#   }
#   
#   CombDF$traj <- factor(CombDF$traj)
#   
#   CombDF <- CombDF[!is.na(CombDF$Pt), ]
#   
#   if(nrow(CombDF)>0){
#     p <- ggplot2::ggplot(CombDF,
#                          ggplot2::aes(x=Pt, y=exp, color = traj)) + ggplot2::geom_point(alpha = alpha) +
#       ggplot2::scale_color_discrete("Branch") +
#       ggplot2::facet_wrap(~gene, scales = "free_y")
#     
#     if(ScalePT){
#       p <- p + ggplot2::geom_smooth(method = "loess", span = NormStep/2) + 
#         ggplot2::geom_vline(xintercept = seq(from=0, to=1, by=NormStep), linetype = 'dotted') +
#         ggplot2::labs(title = Main, y = ylab, x = "Normalized pseudotime")
#     } else {
#       p <- p + ggplot2::geom_smooth(method = "loess", span = .25) +
#         ggplot2::labs(title = Main, y = ylab, x = "Pseudotime")
#     }
#     
#     if(Log){
#       p <- p + ggplot2::scale_y_log10()
#     }
#     
#     return(p)
#     
#   } else {
#     
#     return(NULL)
#     
#   }
#   
# }













# 
# 
# 
# 
# 
# ExpOnPath <- function(ExpData,
#                       Group = NULL,
#                       Path,
#                       TargetPG,
#                       Partition,
#                       PrjStr,
#                       Main = "",
#                       genes = 4) {
# 
# 
#   if(is.null(Group)){
#     Group <- rep("N/A", ncol(ExpData))
#   }
# 
#   if(is.numeric(genes)){
# 
#     GenesByVar <- apply(ExpData, 1, var)
# 
#     if(length(GenesByVar) <= genes){
#       genes = length(GenesByVar)
#     }
# 
#     genes <- names(GenesByVar[order(GenesByVar, decreasing = TRUE)])[1:genes]
# 
#   }
# 
#   tExpData <- ExpData[genes, ]
# 
#   PtOnPath <- getPseudotime(ProjStruct = PrjStr, NodeSeq = Path)
# 
#   PtVect <- PtOnPath$Pt
#   names(PtVect) <- colnames(tExpData)
#   MetlExp <- reshape::melt(t(tExpData))
# 
#   CombDF <- data.frame(Pt = PtVect[as.character(MetlExp$X1)],
#                        gene = MetlExp$X2, exp = MetlExp$value,
#                        group = Group)
# 
#   CombDF <- CombDF[!is.na(CombDF$Pt), ]
# 
#   if(nrow(CombDF)>0){
#     p <- ggplot2::ggplot(CombDF,
#                          ggplot2::aes(x=Pt, y=exp)) +
#       ggplot2::geom_point(ggplot2::aes(color = group), alpha = .3) +
#       ggplot2::geom_smooth() + ggplot2::scale_color_discrete("Group") +
#       ggplot2::facet_wrap(~gene, scales = "free_y") +
#       ggplot2::labs(title = Main, y = "Gene expression", x = "Pseudotime")
# 
#     return(p)
# 
#   } else {
# 
#     return(NULL)
# 
#   }
# 
# }
# 
# 
Albluca/ElPiGraph.R documentation built on May 28, 2019, 11:02 a.m.