inst/sake/server.r

library(shiny)
library(shinyBS)
library(ggplot2)
library(data.table)
library(magrittr)
library(corrplot)
library(biomaRt)
library(BiocParallel)
library(AnnotationHub)
library(RColorBrewer)
library(matrixStats)
library(tools)
library(snowfall)
library(NMF)
library(DT)
library(gage)
library(gageData)
library(pathview)

shinyServer(function(input, output, session) {
  fileinfo <- reactive({
    if (input$selectfile == 'upload'){
      inFile <- input$file1
      req(inFile)
      validate(
        need(!is.null(inFile), "Please upload a gene count data set")
      )
      filename <- basename(inFile$name)
      filepath <- inFile$datapath
    }else if (input$selectfile == "preload"){
      req(input$inputdata)
      validate(
        need(input$inputdata!="", "Please select a gene count data set")
      )
      filename <- basename(input$inputdata)
      filepath <- file.path(raw_fd, input$inputdata)
    }else if (input$selectfile == "saved"){
      inFile <- input$rda
      req(inFile)
      validate(
        need(!is.null(inFile), "Please upload a .rda file")
      )
      filename <- basename(inFile$name)
      filepath <- inFile$datapath
    }else{
      return()
    }

    fileinfo <- list()
    fileinfo[['name']] <- filename
    fileinfo[['path']] <- filepath
    return(fileinfo)
  })

  ### Output Prefix ###
  File <- reactive({
    if (input$selectfile == 'upload'){
      File <- input$file1$name
    }else if (input$selectfile == "preload"){
      File <- input$inputdata
    }else if (input$selectfile == "saved"){
      File <- input$rda$name
    }else{
      return()
    }
    return(File)
  })
  File_ext <- reactive({
    file_ext(File())
  })
  file_prefix <- reactive({
    if(input$selectfile == "saved"){
      gsub(paste0(".", File_ext()), "", File())
    }else{
      paste( gsub(paste0(".", File_ext()), "", File()),
             paste0("k", input$num_cluster),
             paste0(input$top.num,input$algorithm),
             paste0("nrun", input$nrun),
             paste0("predict_", input$predict),
             sep=".")
    }
  })

  rda <- reactive({
    req(fileinfo())
    req(input$selectfile == "saved")
    filepath <- fileinfo()[['path']]
    validate(
      need(File_ext() == "rda" || File_ext() == "RData",
           message = paste("file extension", File_ext(), "unknown, please try again"))
    )
    rda <- local({load(filepath); environment()})

    # start checking for all the required variables
    validate(
      need(!is.null(rda$rawdata), "Variable 'rawdata' does not exist in your .rda file, please check it and upload again") %then%
      need(nrow(rda$rawdata) > 0, "Variable 'rawdata' in your .rda file is empty, please check it and upload again") %then%
      need(!is.null(rda$nmfres), "Variable 'nmfres' does not exist in your .rda file, please check it and upload again")
      # need(!is.null(rda$tsne_2d), "Variable 'tsne_2d' does not exist in your .rda file, please check it and upload again") %then%
      # need(!is.null(rda$tsne_3d), "Variable 'tsne_3d' does not exist in your .rda file, please check it and upload again") #%then%
      # need(!is.null(rda$dds), "Variable 'dds' does not exist in your .rda file, please check it and upload again")
    )
    return(rda)
  })

  observeEvent(rda(), {
    tsne_2d$data <- rda()$tsne_2d
    tsne_3d$data <- rda()$tsne_3d
  })

  rawdata <- reactive({
    req(fileinfo())
    filepath <- fileinfo()[['path']]
    n <- 2
    withProgress(message = 'Loading data', value = 0, {
      incProgress(1/n, detail = "Usually takes ~10 seconds")
      if(input$selectfile == "saved"){
        rawdata <- rda()$rawdata
      }else{
        rawdata <- myfread.table(filepath, check.platform=T, sep=input$sep, detect.file.ext=FALSE)
      }
    })
    colnames(rawdata) <- gsub("\\.", "-", colnames(rawdata))
    return (rawdata)
  })

  #' Raw data
  output$rawdatatbl = DT::renderDataTable({
    rawdata <- rawdata()
    validate(
      need(ncol(rawdata)>1,
           "Please modify the parameters on the left and try again!") %then%
        need(!any(duplicated(t(rawdata))),
             "There are columns with the exact same value in your data, Please check and try again!")
    )
    withProgress(message = 'Displying datatable', value = NULL, {
      DT::datatable(rawdata[1:20, 1:min(500, ncol(rawdata))], rownames= TRUE,
                    options = list(scroll = TRUE,
                                   scrollX = TRUE,
                                   scrollY = TRUE,
                                   pageLength = 8
                    )
      ) %>% formatRound(1:ncol(rawdata), 3)
    })
  }, server=TRUE)

  #' Info box
  output$nameBox <- renderInfoBox({
    infoBox(
      "FILENAME", fileinfo()[['name']], icon = icon("list"),
      color = "green"
    )
  })
  output$sampleBox <- renderInfoBox({
    rawdata <- rawdata()
    infoBox(
      "SAMPLE", ncol(rawdata), icon = icon("list"),
      color = "blue"
    )
  })
  output$featureBox <- renderInfoBox({
    rawdata <- rawdata()
    infoBox(
      "FEATURE", nrow(rawdata), icon = icon("list"),
      color = "yellow"
    )
  })

  # transform_data <- reactiveValues(data=NULL, rawdata=NULL)
  transform_data <- reactiveValues(data=data.frame(a=NULL, b=NULL),
                                   rawdata=data.frame(a=NULL, b=NULL))
  run_selsamp <- reactiveValues(go=FALSE)

  run_trans <- function(rawdata){
    transdata <- rawdata

    n <- 2
    withProgress(message = 'Transforming data', value = 0, {
      incProgress(1/n, detail = "Takes around 5~10 seconds")

      if(input$normdata=="takeRPM"){
        transdata <- rpm(transdata, colSums(transdata))
      }else if(input$normdata=="takesizeNorm"){
        sf <- norm_factors(transdata)
        transdata <- t(t(transdata)/sf)
      }else if(input$normdata=="takeuq"){
        transdata <- uq(transdata)
      }

      transdata <- rmv_constant_0(transdata, pct=0.95, minimum=0)

      if(input$transformdata=="takevst"){
        transdata <- vst(round(transdata))
      }else if(input$transformdata=="takelog"){
        validate(
          need(!any(transdata<0), "Please make sure all the numbers in the data are positive values")
        )
        transdata <- log2(transdata + 1)
      }
    })
    return(transdata)
  }

  observeEvent(rawdata(), {
    transdata <- run_trans(rawdata())
    transform_data$data <- transdata
    transform_data$rawdata <- rawdata()
  })
  observeEvent(input$run_transf, {
    transdata <- run_trans(rawdata())
    transform_data$data <- transdata
    transform_data$rawdata <- rawdata()
  })

  output$dl_transformdata <- downloadHandler(
    filename <- function() {
      paste(file_prefix(), "filtered.txt", sep=".")
    },
    content = function(file) {
      data <- transform_data$data
      colnames(data) <- gsub("\\.", "-", colnames(data))
      write.table(data, file = file, quote=F, row.names=T, sep="\t")
    }
  )

  output$readDistrib <- renderPlotly({
    validate(
      need(dim(transform_data$data)[1] > 1, "Did you upload your file or select preloaded data yet?")
    )
    total <- colSums(transform_data$data)
    genecov <- colSums(transform_data$rawdata > 0)
    run_selsamp$go <- TRUE
    textlab <- paste(names(total),
                     paste("Total:", signif(total/1000000,3), "Million"),
                     paste("Gene Cov:", genecov),
                     sep="<br>")
    plot_ly(x=genecov, y=total, text=textlab, source="readdist", #type="scattergl",
            mode="markers", hoverinfo="text",
            themes="Catherine", marker = list(size=10, opacity=0.65)) %>%
            layout(yaxis = list(title = "Total transcripts counts", zeroline=TRUE),
                   xaxis = list(title = "Gene Coverage", zeroline=TRUE),
                   dragmode = "select",
                   showlegend=FALSE)
  })

  sel_samp <- reactive({
    # Get subset based on selection
    event.data <- event_data("plotly_selected", source="readdist")

    # If NULL dont do anything
    if(is.null(event.data) == T) return(NULL)
    if(length(event.data$curveNumber) == 0) return(NULL)

    # Get selected samples
    sel_idx <- subset(event.data, curveNumber == 0)$pointNumber + 1
    non_sel_idx <- subset(event.data, curveNumber == 0)$pointNumber + 1
    if(max(sel_idx) > ncol(transform_data$data)) return(NULL)

    res <- data.frame("Total_Reads"=colSums(transform_data$data)[sel_idx],
                      "Gene_Coverage"=colSums(transform_data$rawdata>0)[sel_idx])
    return(res)
  })

  output$selsamp_tb <- DT::renderDataTable({
    DT::datatable(sel_samp(),
                  rownames= TRUE,
                  options = list(scrollX = TRUE,
                                 scrollY = TRUE,
                                 dom = 'tipr',
                                 pageLength = 5
                  )
    )
  }, server=TRUE)

  output$selsamp_txt <- renderPrint({
    cat("Selected samples\n\n")
    idx <- input$selsamp_tb_rows_selected
    if(length(idx) > 0){
      cat(paste(rownames(sel_samp())[idx], sep = ', '))
    }else{
      cat(NULL)
    }
  })

  ### Filter selected samples from user
  observeEvent(input$run_filtsamp, {
    idx <- input$selsamp_tb_rows_selected
    filtsamp <- rownames(sel_samp())[idx]
    if(!is.null(filtsamp)){
      idx <- match(filtsamp, colnames(transform_data$data))
      if(length(idx) > 0){
        temp <- transform_data$data[, -idx]
        transform_data$data <- temp
        temp <- transform_data$rawdata[, -idx]
        transform_data$rawdata <- temp
      }else{
        warning("Can not find selected sample names in transform_data")
      }
    }
  })

  merged <- reactive({
    rawdata <- transform_data$data
    genelist <- NULL

    if(!is.null(input$selected_samples) && sum(input$selected_samples %in% colnames(rawdata))==length(input$selected_samples)){
      rawdata <- rawdata[, input$selected_samples]
    }

    if(input$list_type=='Top Ranks'){
      select.genes <- select_top_n(apply(rawdata,1,input$math), n=input$top.num, bottom=ifelse(input$orders=="bottom", T, F))
      genelist <- names(select.genes)
    }else if(input$list_type=='Upload gene list'){
      validate(
        need(!is.null(input$genefile), "Please upload a file with list of genes, with column name 'Gene' ")
      )
      genefile <- read.table(input$genefile$datapath, header=T, sep="\t")

      ext <- file_ext(input$genefile[1])
      n <- 2
      withProgress(message = 'Loading gene list', value = 0, {
        incProgress(1/n, detail = "Usually takes ~10 seconds")
        if (ext=="csv"){
          genefile <- read.table(input$genefile$datapath, header=T, sep =',', stringsAsFactors = FALSE)
        }else if (ext=="out" || ext=="tsv" || ext=="txt"){
          genefile <- read.table(input$genefile$datapath, header=T, sep ='\t', stringsAsFactors = FALSE)
        }else{
          print("File format doesn't support, please try again")
          return(NULL)
        }
      })
      validate(
        need(sum(colnames(genefile)=='Gene'), "Please make sure genefile contains column name 'Gene'")
      )
      validate(
        need(length(intersect(genefile$Gene, rownames(rawdata)))>=2, "We did not find enough genes that overlap with the expression data (<2). Please check again and provide a new list! ")
      )
      genelist <- genefile$Gene
    }else if(input$list_type == 'Whole transcriptome'){
      genelist <- rownames(rawdata)
    }

    idx <- unique(match(genelist, rownames(rawdata)))
    merged <- rawdata[idx[!is.na(idx)], ]
    return(merged)
  })

  output$filterdatatbl = DT::renderDataTable({
    filter_data <- merged()
    DT::datatable(filter_data, rownames= TRUE,
                  selection = 'single',
                  options = list(scrollX = TRUE,
                                 scroller = TRUE,
                                 pageLength = 5
                  )
    ) %>% formatRound(1:ncol(filter_data), 2)
  }, server=TRUE)


  callModule(feature, "sample", reactive({ merged() }))
  callModule(network, "gene", reactive({ merged() }))


  #' Scatter plot
  observeEvent(transform_data$data, {
    transform_data <- transform_data$data
    updateSelectizeInput(session, 'cor_sample1',
                         server = TRUE,
                         choices = sort(as.character(colnames(transform_data))),
                         selected = sort(as.character(colnames(transform_data)))[1]
    )
    updateSelectizeInput(session, 'cor_sample2',
                         server = TRUE,
                         choices = sort(as.character(colnames(transform_data))),
                         selected = sort(as.character(colnames(transform_data)))[2]
    )
  })
  scatterData <- reactive({
    transform_data <- transform_data$data
    validate(
      need(input$cor_sample1 != "", "Please select one sample for X axis"),
      need(input$cor_sample2 != "", "Please select one sample for Y axis")
    )
    x <- transform_data[, input$cor_sample1]
    y <- transform_data[, input$cor_sample2]
    if(input$scatter_log){
      if(input$transformdata == "takelog"){
        message("Already did log transformation")
      }else{
        x <- log2(x+1)
        y <- log2(y+1)
      }
    }
    res <- data.frame(x=x, y=y)
    return(res)
  })
  output$scatterPlot <- renderPlotly({
    transform_data <- transform_data$data
    x <- scatterData()$x %>% as.numeric
    y <- scatterData()$y %>% as.numeric
    plot_range <- c(0, min(x,y):max(x,y), max(x,y) + 0.2)
    reg = lm(y ~ x)
    modsum = summary(reg)
    r <- signif(cor(x,y), 3)
    R2 = signif(summary(reg)$r.squared, 3)
    textlab <- paste(paste0("x = ", signif(x,3)), paste0("y = ", signif(y,3)), rownames(transform_data), sep="<br>")

    n <- 2
    withProgress(message = 'Generating Scatter Plot', value = 0, {
      # p <- (x=~x, y=~y, text=~textlab, type="scattergl", #source = "scatter",
      #              mode="markers", hoverinfo="text",
      #              #themes="Catherine",
      #              marker = list(size=8, opacity=0.65)) %>%
      p <- plot_ly() %>%
       add_trace(x=~x, y=~y, text=~textlab, type="scattergl",
                   mode="markers", hoverinfo="text",
                   marker = list(size=8, opacity=0.65)) %>%
       add_trace(x=~plot_range, y=~plot_range, type="scattergl", mode = "lines", name = "Ref", line = list(width=2)) %>%
            layout(title = paste("cor:", r),
                   xaxis = list(title = input$cor_sample1, zeroline=TRUE),
                   yaxis = list(title = input$cor_sample2, zeroline=TRUE),
                   showlegend=FALSE)
    })
    # if(input$show_r2){
    #   p <- p %>% layout( annotations = list(x = x, y = y, text=paste0("R2=", R2), showarrow=FALSE) )
    # }

    if(input$show_fc){
      df <- data.frame(x = plot_range[plot_range>=1], y=plot_range[plot_range>=1]-1)
      p <- p %>%
        add_trace(data=df, x= ~x, y= ~y, type = "scattergl", mode = "lines", name = "2-FC", line = list(width=1.5, color="#fec44f")) %>%
        add_trace(data=df, x= ~y, y= ~x, type = "scattergl", mode = "lines", name = "2-FC", line = list(width=1.5, color="#fec44f"))
    }
    p #%>% toWebGL()
  })
  output$scatterdatatbl = DT::renderDataTable({
    x <- scatterData()$x
    y <- scatterData()$y
    if(input$transformdata == "takelog"){
      logFC = x - y
    }else{
      esp <- 0.001
      logFC = log2((x+esp)/(y+esp))
    }
    GeneCard <- paste0("<a href='http://www.genecards.org/cgi-bin/carddisp.pl?gene=",
                              rownames(transform_data$data), "'>", rownames(transform_data$data), "</a>")
    scatter_data <- data.frame(Gene=GeneCard,
                               X = x,
                               Y = y,
                               logFC = logFC,
                               meanFC = mean(x+y)*logFC) %>%
                    dplyr::arrange(desc(abs(logFC)))
    DT::datatable(scatter_data, rownames= FALSE,
                  options = list(scrollX = TRUE,
                                 pageLength = 8),
                  escape = FALSE
    ) %>% formatRound(2:5, 3)
  }, server=TRUE)


  ### Main part of the program ###
  nmf_seed <- eventReactive(input$runNMF, {
    nmf_seed <- as.numeric(input$nmf_seed)
    if(nmf_seed == 0){
      set.seed(as.integer(Sys.time()))
      sample(1:999999, 1)
    }
  })
  nmf_res <- eventReactive(input$runNMF, {
    merged <- merged()
    validate(
      need(is.numeric(input$num_cluster) & is.whole(input$num_cluster) & input$num_cluster>1, "Please provide a valid positive numeric integer value for 'Num of clusters(K)'") %then%
      need(input$num_cluster <= ncol(merged), "Number of clusters(K) must be smaller than number of samples\nPlease Try selecting another K") %then%
      need(is.numeric(input$nrun) & is.whole(input$nrun) & input$nrun>1, "Please provide a valid numeric integer value for 'nrun'")
    )

    if(input$selectfile == "saved"){
      nmfres <- rda()$nmfres
    }else{
      if(ncol(merged) >= 300){
        # toggleModal(session, "nmfModal1", toggle = "open")

        # Run NMF through YABI
        # test_yabi <- try(system("yabish login yabi buffalo! backends"))
        # Will remove yabi later, right now just don't let yabi command start and run NMF on the shiny01 server
        test_yabi <- try(system("naikai"))
        if(test_yabi == 0){
          createAlert(session, "YabiAlert", "YabiAlert1", title = "Running NMF on Amazon Cloud", style = "success",
                      content = paste0("Sample size is ", ncol(merged),
                                       ". We are running tiis job on the cloud and will notify you when the results are ready.<br>"),
                      append = FALSE)
          # pop up window asking for email
          data_path <- "/mnt/sake-uploads"
          # data_path <- "~/Desktop/sake-uploads"
          output <- file.path(data_path, paste0(file_prefix(), ".txt"))
          write.table(merged, output, sep="\t", quote=F)
          command <- paste("su - centos -c '/home/centos/yabish_NMF_email.sh",
                           "-d", output,
                           "-t", nrow(merged),
                           "-k", input$num_cluster,
                           "-r", input$nrun,
                           "-m", input$mode,
                           "-n", "FALSE",
                           "-a", input$algorithm,
                           "-s", input$nmf_seed,
                           "-c", 8,
                           "-x", "FALSE",
                           "-f", 0,
                           "-q", "FALSE'")
          # send email to the user and stop sake
          # t1 <- isolate(try(system(command, intern = TRUE)))
          print("Sending command to yabi")
          t1 <- try(system(command, wait = FALSE))
          print(paste0("Command sent:", command))
          if(t1 == 0){
            Sys.sleep(5)
            closeAlert(session, "YabiAlert1")
            Sys.sleep(5)
            print("closing YabiAlert and stopping sake")
            # stopApp(11)
            stop(paste("sent jobs to yabi and stop sake:", command))
          }else{
            createAlert(session, "YabiAlert", "YabiAlert2", title = "Error message from running Yabi", style = "danger",
                        content = t1,
                        append = FALSE)
            Sys.sleep(5)
            stop(paste("command creates erros:", command))
          }
        }else{
          createAlert(session, "NMFAlert", "NMFAlert1", title = "WARNING", style = "warning",
                      content = paste0("Sample size is ", ncol(merged), ". This might take long to run. <br>"),
                      append = FALSE)
        }
      }

      # Run NMF on local server
      ptm <- proc.time()
      withProgress(message = 'Running NMF', value = 0, {
        incProgress(1/2, detail = "Takes around 30~60 seconds for each K")
        nmfres <- myNMF(merged,
                        mode=input$mode,
                        cluster=input$num_cluster,
                        nrun=input$nrun,
                        ncores = cores(),
                        algorithm = input$algorithm,
                        seed = nmf_seed()
        )
        b <- proc.time() - ptm
        print("### Time to run NMF ### ")
        print(b)
      })

      closeAlert(session, "NMFAlert1")
      closeAlert(session, "YabiAlert2")
    }
    return(nmfres)
  })

  ### Download NMF ###
  observeEvent(input$runNMF, {
    output$dlNMF_UI <- renderUI({
      tagList(
        column(width=2, br(), downloadButton("dlNMF", "Download NMF result", class = 'dwnld'))
      )
    })
  })

  output$dlNMF <- downloadHandler(
    filename <- function() {
      paste( file_prefix(), "nmf_results.zip", sep=".")
    },
    content = function(file) {
      tmpdir <- tempdir()
      current_dir <- getwd()
      setwd(tmpdir)

      filenames <- sapply(c("nmf_Groups", "nmf_Features", "Original_plus_NMF"),
                          function(x) paste(file_prefix(), x, "csv", sep="."))
      write.csv(nmf_groups(), filenames[1], quote=F, row.names=F)
      write.csv(nmf_features(), filenames[2], quote=F, row.names=F)
      write.csv(ori_plus_nmfResult()[["Total"]], filenames[3], quote=F)

      for(i in 1:(length(ori_plus_nmfResult())-1)){
        filenames <- c(filenames, paste(file_prefix(), paste0("onlyNMF", i), "csv", sep="."))
        write.csv(ori_plus_nmfResult()[[paste("NMF", i)]], filenames[length(filenames)], quote=F)
      }

      nmfres <- nmf_res()
      rawdata <- rawdata()
      nmfres_filename <- paste(file_prefix(), "nmfres", "rda", sep=".")
      save(rawdata, nmfres, file=nmfres_filename)
      zip(zipfile=file, files=c(filenames, nmfres_filename))
      setwd(as.character(current_dir))
    },
    contentType = "application/zip"
  )

  ### Estimate K ###
  runSummary <- reactive({
    req(class(nmf_res()) == "NMF.rank")
    nmf_res <- nmf_res()
    summary <- nmf_summary(nmf_res)
    if(input$mode=="estim"){
      summary <- t(summary)
    }
    return(summary)
  })
  output$estimSummary = DT::renderDataTable({
    runSummary <- runSummary()
    DT::datatable(runSummary, rownames= TRUE,
                  extensions = 'Buttons',
                  options = list(dom = 'Bfrtip',
                                 buttons =
                                   list('copy', 'print', list(
                                     extend = 'collection',
                                     buttons = list(list(extend='csv',
                                                         filename = filename),
                                                    list(extend='excel',
                                                         filename = filename),
                                                    list(extend='pdf',
                                                         filename= filename)),
                                     text = 'Download'
                                   )),
                                 scrollX = TRUE,
                                 pageLength = 12
                  )
    ) %>% formatRound(1:ncol(runSummary), 2)
  }, server=FALSE)

  output$estimPlot <- renderPlot({
    validate(
      need(class(nmf_res()) == "NMF.rank", "Seems like you haven't run NMF 'estim run' yet\nOr the loaded .rda file is from 'real run' result")
    )
    nmf_res <- nmf_res()
    if(input$estimtype=="consensus"){
      consensusmap(nmf_res)
    }else if(input$estimtype=="quality"){
      plot(nmf_res)
    }
  }, height=777)


  ### NMF plots ###
  output$nmfplot <- renderPlot({
    validate(
      need(class(nmf_res()) == "NMFfitX1", "Seems like you haven't run NMF 'real' run yet\nOr the loaded .rda file is from 'estim run' result")
    )
    nmf_res <- nmf_res()
    nmf_plot(nmf_res, type=input$plottype, silorder=input$nmfplot_silhouette,
             subsetRow = ifelse(input$select_feature_num>0, as.numeric(input$select_feature_num), TRUE ))
  },height = 777)

  output$silhouetteplot <- renderPlot({
    validate(
      need(class(nmf_res()) == "NMFfitX1", "Seems like you haven't run NMF 'real' run yet")
    )
    nmf_res <- nmf_res()
    nmf_silhouette_plot(nmf_res, type=input$plottype)
  },height = 777)

  output$dl_nmf_estimplot <- downloadHandler(
    filename <- function() {
      paste(file_prefix(), input$mode, "nmf.pdf", sep=".")
    },
    content = function(file) {
      nmf_res <- nmf_res()
      pdf(file, width=input$estim_pdf_w, height=input$estim_pdf_h)
      if(input$mode == "estim"){
        consensusmap(nmf_res)
        print(plot(nmf_res))
      }
      dev.off()
    }
  )

  output$dl_nmf_realplot <- downloadHandler(
    filename <- function() {
      paste(file_prefix(), input$mode, "nmf.pdf", sep=".")
    },
    content = function(file) {
      nmf_res <- nmf_res()
      pdf(file, width=input$real_pdf_w, height=input$real_pdf_h)
      if(input$mode == "real"){
        nmf_plot(nmf_res, type="samples", silorder=input$nmfplot_silhouette)
        nmf_plot(nmf_res, type="features", silorder=input$nmfplot_silhouette,
                 subsetRow = ifelse(input$select_feature_num>0, as.numeric(input$select_feature_num), TRUE ))
        nmf_plot(nmf_res, type="consensus")
        if(!is.null(tsne_2d$data)){
          print(nmfplottsne())
        }
      }
      dev.off()
    }
  )

  nmf_groups <- reactive({
    validate(
      need(class(nmf_res()) == "NMFfitX1", "Seems like you haven't run NMF 'real' run yet")
    )
    withProgress(message = 'Extracting Groups info', value = NULL, {
      nmf_extract_group(nmf_res(), type=input$predict)
    })
  })
  output$nmfGroups <- DT::renderDataTable({
    nmf_groups <- nmf_groups()
    filename <- "nmf_groups"
    colnames(nmf_groups)[which(colnames(nmf_groups)=="nmf_subtypes")] <- "Group"
    # Get subset based on selection from scatter plot
    event.data <- event_data("plotly_selected", source = "nmfGroups")
    if(is.null(event.data) == TRUE){
      sel_idx <- NULL
    }else{
      sel_idx <- subset(event.data, curveNumber == 0)$pointNumber + 1
    }
    DT::datatable(nmf_groups, rownames=FALSE, escape=-1,
                  selection = list(selected = sel_idx),
                  options = list(dom = 'rtip',
                                 scrollX = TRUE,
                                 pageLength = 8,
                                 order=list(list(1,'asc'))
                  )
    ) %>% formatRound(3:ncol(nmf_groups), 3)
  }, server=FALSE)

  # Add t-SNE plot next to NMF group
  observeEvent(input$run_nmftSNE, {
    plot_data <- plot_data()[['plot_data']]
    nsamples <- ncol(plot_data()[['plot_data']])
    if(input$nmftsne_perplexity*3 > (nsamples-1)) {
           createAlert(session, "perplexityAlert", "perplexityAlert1", title = "WARNING", style = "warning",
                       content = paste("Perpleixty", input$nmftsne_perplexity,
                                       "is too large compared to num of samples", nsamples),
                       append = FALSE)
    }else{
      closeAlert(session, "perplexityAlert1")

      withProgress(message = 'Running t-SNE', value = NULL, {
        incProgress(1/3, detail = "For t-SNE 2D")
        try(
          tsne_2d$data <- run_tsne(plot_data, iter=input$tsne_iter, dims=2,
                                   initial_dims = input$tsne_pca_num, theta = input$tsne_theta,
                                   perplexity=input$nmftsne_perplexity, cores=cores())
        )
        incProgress(2/3, detail = "For t-SNE 3D")
        try(
          tsne_3d$data <- run_tsne(plot_data, iter=input$tsne_iter, dims=3,
                                   initial_dims = input$tsne_pca_num, theta = input$tsne_theta,
                                   perplexity=input$nmftsne_perplexity, cores=cores())
        )
      })
    }
  })

  nmfplottsne <- reactive({
    plot_data <- plot_data()[['plot_data']]
    nsamples <- ncol(plot_data)
    validate(
      need(input$mode=="real", "This part won't work for 'estim' module\nPlease Try NMF 'Real' run") %then%
      need(!is.null(tsne_2d$data), "Please hit 'Run t-SNE' button")
    )
    color <- NULL
    # Will add an option to select by default column names from samples
    if(length(nmf_groups()$nmf_subtypes)>0){
      idx <- match(colnames(plot_data), nmf_groups()$Sample_ID)
      nmf_subtypes <- nmf_groups()$nmf_subtypes[idx]
      # color <- create.brewer.color(nmf_subtypes, length(unique(nmf_subtypes)), "naikai")
      color <- nmf_subtypes
    }else if(length(ColSideColors()[['name']][,1])>0){
      color <- ColSideColors()[['name']][,1]
    }else{
      stop("Error: color scheme undefined")
    }
    # # default by user selection
    point_size <- input$plot_point_size - 5
    if(input$nmf_prob){
      if(input$predict=="consensus"){
        point_size <- point_size + point_size * (1-nmf_groups()$sil_width)
      }else if(input$predict == "samples"){
        point_size <- point_size + point_size * (1-nmf_groups()$prob)
      }
    }
    nmf_tsne_res <- tsne_2d$data
    naikai <- plot_tsne(nmf_tsne_res, color=color, alpha=input$plot_point_alpha,
                        add.label = input$plot_label, save.plot=F, real.plot=F,
                        add.legend = input$plot_legend,
                        point.size=point_size, label.size=input$plot_label_size)
    ### Check if user select rows (samples)
    s = input$nmfGroups_rows_selected
    if (length(s)){
      if(!input$nmf_prob){
        sub_color <- create.brewer.color(nmf_groups()$nmf_subtypes, length(unique(color)), "naikai")
        sub_tsne_res <- parse_tsne_res(nmf_tsne_res) %>% as.data.frame %>% dplyr::slice(s)
        naikai <- naikai + geom_point(data=sub_tsne_res, aes(x=x, y=y), size=point_size+2, colour=sub_color[s], alpha=input$plot_point_alpha)
      }
    }
    p <- ggplotly(naikai, source="nmfGroups") %>% layout(dragmode = "select")
    return(p)
  })
  output$nmftsneplot <- renderPlotly({
    withProgress(message = 'Genrating t-SNE plot', value = NULL, {
      nmfplottsne()
    })
  })


  output$nmfgrp_var <- renderPlotly({
    nmf_groups <- nmf_groups()
    plot_data <- plot_data()[['plot_data']]
    idx <- match(colnames(plot_data), nmf_groups$Sample_ID)

    colnames(plot_data) <- paste0("NMF", nmf_groups$nmf_subtypes[idx])
    dd <- plot_data %>% t %>% reshape2::melt(.) %>% group_by(Var2, Var1) %>% summarise(var=var(value))

    num_clus <- dd$Var1 %>% levels %>% length
    mycolor <- create.brewer.color(1:num_clus, num=num_clus, name="naikai")
    dd$Var1 <- factor(dd$Var1, levels = paste0("NMF", 1:num_clus))

    a <- ggplot(dd, aes(var, fill=Var1, colour=Var1)) +
          geom_density(alpha=input$cl_alpha) + scale_x_log10() +
          scale_colour_manual(values = mycolor) +
          scale_fill_manual(values = mycolor) +
          labs(x="Log (variance)", y="Density", colour="", fill="") +
          theme_bw()
    p <- plotly_build(a)

    # remove duplicated name
    p$data <- lapply(p$data, FUN = function(x){
      x$name <-  sub("\\((\\S+\\d),.*", "\\1", x$name, perl=TRUE)
      x$line$width <- 1.2
      return(x)
    })
    p$layout$legend$font$size <- as.numeric(input$cl_lgsize)
    p$layout$xaxis$tickfont$size <- input$cl_tickfont
    p$layout$xaxis$titlefont$size <- input$cl_axisfont
    p$layout$xaxis$titlefont$family <- "Arial"
    p$layout$yaxis$tickfont$size <- input$cl_tickfont
    p$layout$yaxis$titlefont$size <- input$cl_axisfont
    p$layout$yaxis$titlefont$family <- "Arial"

    p
  })

  output$nmfgrp_expgene <- renderPlotly({
    rawdata <- rawdata()
    nmf_groups <- nmf_groups()
    idx <- match(colnames(rawdata), nmf_groups$Sample_ID)
    rawdata <- rawdata[, !is.na(idx)]
    colnames(rawdata) <- paste0("NMF", nmf_groups$nmf_subtypes[idx[!is.na(idx)]])
    dd <- data.frame(Group=colnames(rawdata), Genes=colSums(rawdata>0))

    num_clus <- dd$Group %>% levels %>% length
    mycolor <- create.brewer.color(1:num_clus, num=num_clus, name="naikai")

    a <- ggplot(dd, aes(Genes, fill=Group, colour=Group)) +
          geom_density(alpha=input$cl_alpha) +
          scale_fill_manual(values = mycolor, guide="none") +
          scale_color_manual(values = mycolor) +
          labs(x="No. of expressed genes", y="Density", colour="", fill="") +
          theme_bw()
    p <- plotly_build(a)

    # remove duplicated name
    p$data <- lapply(p$data, FUN = function(x){
      x$name <-  sub("\\((\\S+\\d),.*", "\\1", x$name, perl=TRUE)
      x$line$width <- 1.2
      return(x)
    })
    p$layout$legend$font$size <- as.numeric(input$cl_lgsize)
    p$layout$xaxis$tickfont$size <- input$cl_tickfont
    p$layout$xaxis$titlefont$size <- input$cl_axisfont
    p$layout$xaxis$titlefont$family <- "Arial"
    p$layout$yaxis$tickfont$size <- input$cl_tickfont
    p$layout$yaxis$titlefont$size <- input$cl_axisfont
    p$layout$yaxis$titlefont$family <- "Arial"

    p
  })

  output$nmfgrp_coef <- renderPlotly({
    nmf_groups <- nmf_groups()
    plot_data <- plot_data()[['plot_data']]
    idx <- match(colnames(plot_data), nmf_groups$Sample_ID)
    nmf_groups <- nmf_groups[idx, ]

    colnames(plot_data) <- paste0("NMF", nmf_groups$nmf_subtypes)
    pair_cor <- function(x) {
      res <- WGCNA::cor(x, nThreads = 4)
      return(res[lower.tri(res)])
    }

    num_clus <- nmf_groups$nmf_subtypes %>% unique %>% length
    mycolor <- create.brewer.color(1:num_clus, num=num_clus, name="naikai")
    num_poss_cor <- nmf_groups$nmf_subtypes %>% table %>% apply(., 1, function(x) x * (x-1) / 2) %>% sum
    group_cor <- data.frame(matrix(NA_real_, nrow=num_poss_cor, ncol=2))

    j <- 1
    for(i in 1:num_clus){
      idx <- nmf_groups$nmf_subtypes == i
      res <- pair_cor(plot_data[, idx])
      group_cor[j:(j+length(res)-1), ] <- cbind(paste0("NMF", i), res)
      j <- j + length(res)
    }
    colnames(group_cor) <- c("NMF", "Cor")
    group_cor$Cor <- as.numeric(group_cor$Cor)

    a <- ggplot(data=group_cor, aes(x=NMF, y=Cor, color=NMF)) +
      geom_boxplot(aes(color = NMF)) +
      stat_summary(fun.y=mean, shape=19, col='black', geom='point') +
      theme_bw() +
      scale_colour_manual(values = mycolor) +
      labs(x="", y="Cor", colour="")
    # ggplotly(a)

    p <- plotly_build(a)
    # remove outliers for plotly boxplot
    p$data <- lapply(p$data, FUN = function(x){
      if(x$type == "box"){
        x$marker = list(opacity = 0)
      }
      return(x)
    })
    p$layout$legend$font$size <- as.numeric(input$cl_lgsize)
    p$layout$xaxis$tickfont$size <- input$cl_tickfont
    p$layout$xaxis$titlefont$size <- input$cl_axisfont
    p$layout$xaxis$titlefont$family <- "Arial"
    p$layout$yaxis$tickfont$size <- input$cl_tickfont
    p$layout$yaxis$titlefont$size <- input$cl_axisfont
    p$layout$yaxis$titlefont$family <- "Arial"

    p
  })

  nmf_features <- reactive({
    validate(
      need(input$mode=="real", "This part won't work for eastimating k\nPlease Try NMF 'Realrun'") %then%
      need(class(nmf_res())=="NMFfitX1", "Please rerun NMF with 'real run'")
    )
    nmf_res <- nmf_res()
    # add more options to select features here. then compare with ClaNC
    # Add rank by MAD expression
    if(input$select_method=="total"){
      res <- nmf_extract_feature(nmf_res, method="total") %>% unique
    }
    else if(input$select_method=="default"){
      res <- nmf_extract_feature(nmf_res, method=input$select_method, manual.num = as.numeric(input$select_feature_num)) %>%  unique
      validate(
        need(nrow(res)!=1 & !is.na(res$Gene), "None of the features passed through default NMF filtering criteria, please manually specify number of genes from each group or use 'select features by Rank'") %then%
        need(nrow(res)>1, "Not enought features are selected by default! \nMaybe try with more input features or try select features by Rank")
      )
    }else if(input$select_method=="rank"){
      res <- nmf_extract_feature(nmf_res, rawdata = merged(), method=input$select_method, manual.num = as.numeric(input$select_feature_num),
                          FScutoff=input$select_FScutoff, math=input$select_math) %>% unique
      validate(
        need(nrow(res)>1, "Not enought features! \nMaybe try lower the FeatureScore cutoff")
      )
    }
    return(res)
  })
  nmf_features_annot <- reactive({
    data <- nmf_features() %>% as.data.table
    merged <- NULL

    n <- 2
    withProgress(message = 'Extracting features info', value = 0, {
      incProgress(1/n, detail = "Takes around 5~10 seconds")

      ext.func.data <- ext_gene_function(data$Gene, ensembl = ensembl())
      setkey(data, "Gene")
      setkey(ext.func.data, "hgnc_symbol")
      merged <- ext.func.data[data, nomatch=0, allow.cartesian = TRUE]
      merged <- as.data.frame(merged) %>%
                dplyr::arrange(Group, desc(featureScore))
      merged <- merged[, c(4,2,7,6,8)]
      merged <- unique(merged)
      merged$featureScore <- round(merged$featureScore, 3)
      merged$prob <- round(merged$prob, 3)
    })
    return(merged)
  })
  output$nmfFeatures <- DT::renderDataTable({
    filename <- "nmf_features"
    DT::datatable(nmf_features_annot(), rownames=FALSE, escape=-1,
                  extensions = 'Buttons', selection = 'multiple',
                  options = list(dom = 'Bfrtip',
                                 buttons =
                                   list('copy', 'print', list(
                                     extend = 'collection',
                                     buttons = list(list(extend='csv',
                                                         filename = filename),
                                                    list(extend='excel',
                                                         filename = filename),
                                                    list(extend='pdf',
                                                         filename= filename)),
                                     text = 'Download'
                                   )),
                                 pageLength = 50,
                                 autoWidth = TRUE
                  )
    )
  }, server = FALSE)

  output$nmf_vioplot <- renderPlotly({
    req(nmf_features_annot())
    validate(
      need(!is.null(input$nmfFeatures_rows_selected), "Please select a gene")
    )
    withProgress(message = 'Generating box/violin plot', value = NULL, {
      gene <- nmf_features_annot()[input$nmfFeatures_rows_selected, "GeneCard"] %>%
              gsub(".*'>(.*)</a>", "\\1", .)
      gene.data <- transform_data$rawdata[gene, ]

      gene.data <- gene.data %>%
        tibble::rownames_to_column() %>%
        dplyr::rename(genename = rowname) %>%
        tidyr::gather(Sample, Expr, -genename) %>%
        dplyr::mutate(NMF = factor(paste0("NMF", rep(nmf_groups()$nmf_subtypes, each=length(gene)))))
      if(input$sel_vioscale == "raw"){
        print('Use original scale')
      }else if(input$sel_vioscale == "log2"){
        gene.data$Expr <- log2(gene.data$Expr+1)
      }else if(input$sel_vioscale == "log10"){
        gene.data$Expr <- log10(gene.data$Expr+1)
      }

      # match the coloring from PCA, t-SNE, and heatmap
      num_clus <- gene.data$NMF %>% levels %>% length
      mycolor <- create.brewer.color(1:num_clus, num=num_clus, name="naikai")

      if(input$sel_grp == "Manual"){
          col.num <- as.numeric(factor(mycolor, levels = mycolor))
          col.lvl <- as.character(mycolor)
          col.lvl <- col.lvl[as.numeric(input$sel_grp_order)]
          mycolor <- col.lvl[col.num]
      }
      # add option to select either boxplot or violin plot
      if(input$sel_vioplot == "violin"){
        a <- ggplot(data=gene.data, aes(x=NMF, y=Expr, color=NMF)) +
          geom_violin(aes(color = NMF), scale="width", width=0.6, show.legend = FALSE) +
          geom_jitter(aes(color=NMF), alpha=0.6, width=0.1, show.legend = FALSE) +
          theme_bw() +
          facet_wrap(~ genename, ncol = input$sel_ncol) +
          scale_colour_manual(values = mycolor) +
          labs(x="", y="", colour="")
      }else if(input$sel_vioplot == "box"){
        a <- ggplot(data=gene.data, aes(x=NMF, y=Expr, color=NMF)) +
          geom_boxplot(aes(color = NMF), show.legend = FALSE) +
          geom_jitter(aes(color=NMF), alpha=0.6, width=0.1, show.legend = FALSE) +
          theme_bw() +
          facet_wrap(~ genename, ncol = input$sel_ncol) +
          scale_colour_manual(values = mycolor) +
          labs(x="", y="", colour="")
      }else{
        warning(paste("Unknown plot type:", input$sel_vioplot, "Please check again"))
      }

      # custom range for Y-axis is only being triggered when ymax > 0
      if(!is.na(input$sel_ymax) & input$sel_ymax>0){
        validate(
          need(input$sel_ymax > input$sel_ymin, "Please make sure 'ymax' is greater than 'ymin'")
        )
        a <- a + ylim(c(input$sel_ymin, input$sel_ymax))
      }

      p <- plotly_build(a)
      # remove outliers for plotly boxplot
      p[[1]]$data <- lapply(p[[1]]$data, FUN = function(x){
        if(x$type == "box"){
          x$marker = list(outliercolor = 0)
        }
        return(x)
      })

      p[[1]]$layout$margin$l <- p[[1]]$layout$margin$l + 5
      p[[1]]$layout$legend$font$size <- input$sel_legend_size
      p[[1]]$layout$showlegend = input$sel_show_legend

      # loop through ncol(Xaxis) and gene (Yaxis)
      num_xaxis <- grep("xaxis", names(p[[1]]$layout)) %>% length
      num_yaxis <- grep("yaxis", names(p[[1]]$layout)) %>% length
      for(i in 1:num_xaxis){
        p[[1]]$layout[[paste0("xaxis", ifelse(i>1, i, ""))]]$tickfont$size <- input$sel_xfont_size
      }
      for(i in 1:num_yaxis){
        p[[1]]$layout[[paste0("yaxis", ifelse(i>1, i, ""))]]$tickfont$size <- input$sel_yfont_size
      }
      for(i in 1:length(gene)){
        p[[1]]$layout$annotations[[i]]$font$size <- input$sel_title_size
      }

      p
    })
  })

  ori_plus_nmfResult <-reactive({
    rawdata <- rawdata()
    validate(
      need(!is.null(nmf_groups()), "Please run NMF and get the result first")
    )
    res <- list()
    idx <- match(colnames(rawdata), nmf_groups()$Sample_ID)
    newdata <- rawdata
    colnames(newdata) <- paste(paste0("NMF",nmf_groups()$nmf_subtypes[idx]), colnames(newdata), sep="_")
    res[["Total"]] <- newdata

    # add separate files for each NMF group
    for(i in 1:length(unique(nmf_groups()$nmf_subtypes))){
      subidx <- nmf_groups()$nmf_subtypes == i
      res[[paste("NMF", i)]] <- newdata[, idx[subidx]]
    }

    return(res)
  })

  ### Statistics ###
  output$runStatSummary <- renderTable({
    runSummary()
    }, sanitize.text.function = function(x) x, options = list(orderClasses = TRUE, lengthMenu = c(10,25,50,100, NROW(merged)), pageLength=25)
  )
  output$runStatPlot <- renderPlot({
    nmf_res <- nmf_res()
    plot(nmf_res)
  }, height=666)


  ensembl <- reactive({
    # Need to add a testing here when the BioMart is down
    # Or find offline annotation db
    # ensembl = useMart(biomart="ENSEMBL_MART_ENSEMBL", host="grch37.ensembl.org", path="/biomart/martservice" ,dataset="hsapiens_gene_ensembl")
    ensembl = useMart(biomart="ENSEMBL_MART_ENSEMBL", host="jul2016.archive.ensembl.org", path="/biomart/martservice" ,dataset="hsapiens_gene_ensembl")
  })

  #' Heatmap setting
  geneListInfo <- reactive({
    rawdata <- transform_data$data
    gene.list <- NULL
    gene.groups <- NULL

    if(input$heatmapGene=='nmf'){
      nmf_features <- nmf_features()
      validate(
        need(nrow(nmf_features)>0, "NMF not run yet? \nPlease run NMF classification and then try again!")
      )
      gene.list <- nmf_features$Gene
    }
    else if(input$heatmapGene=='rank'){
      select.genes <- select_top_n(apply(rawdata,1,input$heat.math), n=as.numeric(input$heat.top.num), bottom=F)
      gene.list <- names(select.genes)
    }
    else if(input$heatmapGene=='preload'){
      validate(
        need(!is.null(input$predefined_list), "Please select at least one predefined gene list")
      )
      for(i in 1:length(input$predefined_list)){
        gene.list <- c(gene.list, pre_genelist[[ input$predefined_list[i] ]]$Gene)
        # if file contains 'Group' info in one of the columns, use that for RowColors
        if("Group" %in% colnames(pre_genelist[[ input$predefined_list[i] ]])){
          gene.groups <- c(gene.groups, pre_genelist[[ input$predefined_list[i] ]]$Group)
        }
      }
    }
    else if(input$heatmapGene=='manual'){
      validate(
        need(!is.null(input$manual_genes), "You didn't select any genes. Please select genes from the data") %then%
          need(length(input$manual_genes)>=2, "Please select at least two genes! ")
      )
      gene.list <- input$manual_genes
    }
    else if(input$heatmapGene=='upload'){
      validate(
        need(!is.null(input$heatmapGeneFile), "Please upload a file with list of genes, with column name 'Gene' ")
      )
      ext <- file_ext(input$heatmapGeneFile)[1]
      if (ext=="csv"){
        genefile <- read.csv(input$heatmapGeneFile$datapath, header=T, stringsAsFactors = FALSE)
      }else if (ext=="out" || ext=="tsv" || ext=="txt"){
        genefile <- read.table(input$heatmapGeneFile$datapath, header=T, sep="\t", stringsAsFactors = FALSE)
      }else{
        print("File format doesn't support, please use '.csv', '.txt', '.out', '.tsv' and try again")
        return(NULL)
      }
      validate(
        need(sum(colnames(genefile)=='Gene'), "Please make sure genefile contains column name 'Gene'")
      )
      gene.list <- genefile$Gene
      if("Group" %in% colnames(genefile)){
        gene.groups <- genefile$Group
      }
    }

    validate(
      need(length(intersect(gene.list, rownames(rawdata)))>=2, "We did not find enough genes that overlap with the expression data (<2). \nPlease check again and provide a new list! \nMaybe this is from species other than Human (default gene list are from Human)")
    )

    # only select unique gene name on rawdata
    idx <- unique(match(gene.list, rownames(rawdata)))
    na.genes <- gene.list[is.na(idx)]
    if(length(na.genes)>0){
      print(c("These genes cannot be found in the data:", paste(na.genes, collapse=", ")))
    }
    data.rownames <- rownames(rawdata)[idx[!is.na(idx)]]

    # match back those gene names to genelist to get idx for gene.groups
    ii <- match(data.rownames, gene.list)
    if(!is.null(gene.groups)){
      gene.groups <- gene.groups[ii]
    }
    gene.list <- data.rownames

    # finish loading or selecting gene list file
    res <- list()
    res[['list']] <- gene.list
    res[['group']] <- gene.groups
    return(res)
  })


  OrdColopts <- reactive({
    c("Default", "Hierarchical", "Filename", "GeneExpr")
  })
  ColClrByopts <- reactive({
    c("Filename")
  })
  observeEvent(rawdata(), {
    updateSelectizeInput(session, 'OrdCol',
                         server = TRUE,
                         choices = as.character(OrdColopts()),
                         selected = "Hierarchical"
    )
    updateSelectizeInput(session, 'ColClrBy',
                         server = TRUE,
                         choices = as.character(ColClrByopts()),
                         selected = "Filename"
    )
  })
  observeEvent(input$runNMF, {
    updateSelectizeInput(session, 'OrdCol',
                         server = TRUE,
                         choices = as.character(c(OrdColopts(), "NMF Group")),
                         selected = "NMF Group"
    )
    updateSelectizeInput(session, 'ColClrBy',
                         server = TRUE,
                         choices = as.character(c(ColClrByopts(), "NMF Group")),
                         selected = "NMF Group"
    )
  })

  # separate out this for PCA and t-SNE plots
  plot_data <- reactive({
    rawdata <- transform_data$data
    genelist <- geneListInfo()[['list']]

    idx <- match(genelist, rownames(rawdata))
    plot_data <- rawdata[idx,]

    res <- list()
    res[["plot_data"]] <- plot_data
    res[["genelist"]] <- genelist
    return(res)
  })

  heatmap_data <- reactive({
    rawdata <- transform_data$data
    genelist <- geneListInfo()[['list']]
    gene.groups <- geneListInfo()[['group']]

    # only select unique gene name on rawdata
    idx <- match(genelist, rownames(rawdata))
    heatmap_data <- rawdata[idx,]

    if(input$OrdCol == 'Filename'){    # Add options to sort by column groups
      column.names <- strsplit(colnames(heatmap_data), split="\\_")
      validate(
        need(input$sortcolumn_num <= length(column.names[[1]]), "Group num is too big, select a smaller number!")
      )
      idx <- order(sapply(column.names, function(x) x[[input$sortcolumn_num]]))
    }else if(input$OrdCol == 'GeneExpr'){ # Add options to sort by expression value of specific gene
      validate(
        need(input$sortgene_by!="", "Please select a gene!")
      )
      idx <- order(subset(heatmap_data, rownames(heatmap_data)==input$sortgene_by))
    }
    else if(input$OrdCol == 'NMF Group') { # Add options to sort by nmf groups
      idx <- nmf_groups()$nmf_subtypes %>% order
    }else{
      idx <- colnames(heatmap_data)
    }
    heatmap_data <- heatmap_data[, idx]

    res <- list()
    res[["heatmap_data"]] <- heatmap_data
    res[["genelist"]] <- genelist
    res[["gene.groups"]] <- gene.groups
    return(res)
  })

  color <- reactive({
    color <- switch(input$heatColorScheme,
                    "RdBu_Brewer" = colorRampPalette(rev(brewer.pal(input$color_num, "RdBu")))(51),
                    "RdYlBu_Brewer" = colorRampPalette(rev(brewer.pal(input$color_num, "RdYlBu")))(51),
                    "Red_Green" = colorRampPalette(c("red2", "white", "green"))(input$color_num),
                    "Red_Blue" = colorRampPalette(c("blue", "white", "red"))(input$color_num),
                    "Rainbow" = colorRampPalette(c("lightblue", "blue", "white", "gold2", "red2"))(input$color_num),
                    "Black_White" = colorRampPalette(c("black", "white"))(input$color_num)
    )
    return(color)
  })
  ColScheme <- reactive({
    ColScheme <- list()
    ColScheme <- c(input$ColScheme1, input$ColScheme2, input$ColScheme3, input$ColScheme4, input$ColScheme5, input$ColScheme6)
    return(ColScheme)
  })

  ColSideColors <- reactive({
    heatmap_data <- heatmap_data()[['heatmap_data']]
    res <- list()
    if(input$ColClrBy=='NMF Group'){
      req(nmf_groups)
      # rematch the sample order to the sorted order based on selection from input$OrdCol
      idx <- match(colnames(heatmap_data), nmf_groups()$Sample_ID)
      nmf_subtypes <- nmf_groups()$nmf_subtypes[idx]
      res[['color']] <- create.brewer.color(nmf_subtypes, length(unique(nmf_subtypes)), "naikai") %>% as.matrix
      res[['name']] <- paste0("NMF", nmf_subtypes) %>% as.matrix
    }else if(input$ColClrBy == 'Filename'){
      res <- name_to_color(colnames(heatmap_data), split_pattern ="\\_",
                           num_color = 6,
                           ColScheme = ColScheme()[1:6] )
                           # num_color = input$ColSideColorsNum,
                           # ColScheme = ColScheme()[1:input$ColSideColorsNum] )
      # add options to select which column to show
      if(is.null(input$SelColSideColors)){
        res[['color']] <- NULL
        res[['name']] <- NULL
      }else{
        res[['color']] <- res[['color']][, as.numeric(input$SelColSideColors)] %>% as.matrix()
        res[['name']] <- res[['name']][, as.numeric(input$SelColSideColors)] %>% as.matrix()
      }
    }
    return(res)
  })

  RowSideColors <- reactive({
    groups <- heatmap_data()[['gene.groups']]
    row.color <- NULL
    res <- list()
    res[['color']] <- NULL
    res[['name']] <- NULL

    if(!is.null(groups)){
      row.color <- rbind(row.color, create.brewer.color(groups, input$RowColScheme.num, input$RowColScheme))
      res[["color"]] <- as.matrix(row.color)
      res[["name"]] <- as.matrix(groups)
    }
    return(res)
  })

  Colv <- reactive({
    Colv <- NULL
    validate(
      need(nrow(heatmap_data()[['heatmap_data']] > 2), "Did not get enough genes that overlapped with your data\nPlease try another gene list or use rank from data set")
    )
    if(input$OrdCol=="Hierarchical"){
      Colv <- heatmap_data()[['heatmap_data']] %>% t %>% dist(method=input$distance) %>% hclust(method=input$linkage) %>% as.dendrogram
      ColMean <- colMeans(heatmap_data()[['heatmap_data']], na.rm=T)
      Colv <- reorderfun(Colv, ColMean)
    }
    return(Colv)
  })

  Rowv <- reactive({
    Rowv <- NULL
    validate(
      need(nrow(heatmap_data()[['heatmap_data']] > 2), "Did not get enough genes that overlapped with your data\nPlease try another gene list or use rank from data set")
    )
    if(input$OrdRow=="hier"){
      Rowv <- heatmap_data()[['heatmap_data']] %>% dist(method=input$distance) %>% hclust(method=input$linkage) %>% as.dendrogram
      RowMean <- rowMeans(heatmap_data()[['heatmap_data']], na.rm=T)
      Rowv <- reorderfun(Rowv, RowMean)
    }
    return(Rowv)
  })

  dendrogram <- reactive({
    if(is.null(Rowv()) & is.null(Colv())){
      return("none")
    }else if(is.null(Rowv()) & !is.null(Colv())){
      return("column")
    }else if(!is.null(Rowv()) & is.null(Colv())){
      return("row")
    }else if(!is.null(Rowv()) & !is.null(Colv())){
      return("both")
    }
  })

  ### Observe and update input selection ###
  observe({
    transform_data <- transform_data$data
    if(!is.null(transform_data)){
      if (length(rownames(transform_data)) > 1){
        # update the render function for selectize - update manual if user choose to select its own markers
        updateSelectizeInput(session, 'manual_genes',
                             server = TRUE,
                             choices = as.character(rownames(transform_data))
        )
      }
    }
  })
  observe({
    if (!is.null(geneListInfo()[['list']])){
      updateSelectizeInput(session, 'sortgene_by',
                           server = TRUE,
                           choices = as.character(geneListInfo()[['list']])
      )
    }
  })

  output$d3heatmap <- renderD3heatmap({
    heatmap_data <- heatmap_data()[['heatmap_data']]
    ColSideColors <- ColSideColors()[["color"]]
    if(!is.null(ColSideColors)){
      ColSideColors <- t(ColSideColors[, ncol(ColSideColors):1])
    }
    myd3Heatmap(
      as.matrix(heatmap_data),
      type="heatmap",
      dist.m=input$distance,
      hclust.m=input$linkage,
      scale="row",
      scale.method=input$scale_method,
      scale.first=input$scale_first,
      RowSideColors = RowSideColors()[["color"]],
      ColSideColors = ColSideColors,
      color = color(),
      Colv = Colv(),
      Rowv = Rowv(),
      cexCol=input$cexRow,
      cexRow=input$cexCol,
      na.rm = T,
      show_grid = FALSE,
      dendrogram = dendrogram(),
      dendro.ord = "manual",
      anim_duration = 0
    )
  })

  plotInput <- function(){
    heatmap_data <- heatmap_data()[['heatmap_data']]
    filename <- paste(input$type, input$math, paste0(input$orders, paste0(nrow(merged()), "genes")), sep=".")
    myHeatmap.3(as.matrix(heatmap_data),
                type="heatmap",
                color=color(),
                RowSideColors = RowSideColors()[["color"]],
                RowSideColors.name=RowSideColors()[["name"]],
                ColSideColors = ColSideColors()[["color"]],
                ColSideColorsSize=input$ColSideColorsSize,
                ColSideColors.name=ColSideColors()[["name"]],
                cor.method=input$cor.method,
                dist.m=input$distance,
                hclust.m=input$linkage,
                title=input$title,
                Colv = Colv(),
                Rowv = Rowv(),
                scale="row",
                scale.method=input$scale_method,
                scale.first=input$scale_first,
                dendro.ord="manual",
                dendrogram=dendrogram(),
                cexCol=input$cexCol,
                cexRow=input$cexRow,
                col.legend = input$col_legend,
                row.legend = input$row_legend,
                file.prefix=filename,
                save.image=F
    )
  }

  output$dl_heatmap<- downloadHandler(
    filename <- function() {
      paste(file_prefix(), "heatmap.pdf", sep="_")
    },
    content = function(file) {
      pdf(file, width = input$heat_pdf_wd, height = input$heat_pdf_ht)
      plotInput()
      dev.off()
    }
  )

  plot_colopts <- reactive({
    c("Default", "Filename", "GeneExpr", "ReadDepth", "NumExpresGenes")
  })
  observeEvent(rawdata(), {
    updateSelectizeInput(session, 'pt_col',
                         server = TRUE,
                         choices = as.character(plot_colopts()),
                         selected = "Filename"
    )
    choices <- rawdata() %>% colnames() %>% strsplit("_") %>% .[[1]] %>% length %>% seq(1, .) %>% as.character()
    updateSelectizeInput(session, 'pt_file_grp',
                         server = TRUE,
                         choices = choices,
                         selected = choices[1]
    )
  })
  observeEvent(input$pt_file_grp, {
    # this should change with respect to the selection of filename group
    choices <- rawdata() %>% colnames() %>% strsplit("_") %>%
      sapply(., function(x) x[as.numeric(input$pt_file_grp)]) %>% unique %>% length %>%
      seq(1, .) %>% as.character()
    updateSelectizeInput(session, 'pt_grp_order',
                         server = TRUE,
                         choices = choices,
                         selected = choices[1]
    )
  })
  observeEvent(transform_data$data, {
    updateSelectizeInput(session, 'pt_allgene',
                         server = TRUE,
                         choices = as.character(rownames(transform_data$data)),
                         selected = as.character(rownames(transform_data$data)[1])
    )
  })

  observeEvent(input$runNMF,{
    updateSelectizeInput(session, 'pt_col',
                         server = TRUE,
                         choices = as.character(c(plot_colopts(), "NMF Group", "NMF Feature")),
                         selected = 'NMF Group'
    )
    updateSelectizeInput(session, 'pt_nmfgene',
                         server = TRUE,
                         choices = as.character(nmf_features()$Gene),
                         selected = as.character(nmf_features()$Gene[1])
    )
  })
  observeEvent(nmf_groups()$nmf_subtypes, {
    choices <- nmf_groups()$nmf_subtypes %>% unique %>% length %>% seq(1, .) %>% as.character()
    updateSelectizeInput(session, 'sel_grp_order',
                         server = TRUE,
                         choices = choices,
                         selected = choices[1]
    )
  })

  point_col <- reactive({
    # col <- toRGB("steelblue")
    col <- "steelblue"
    plot_data <- plot_data()[['plot_data']]
    group <- NULL
    if(input$pt_col == "Default"){
      group <- "Default"
    }else if(input$pt_col == "NMF Group"){
      req(nmf_groups)
      idx <- match(colnames(plot_data), nmf_groups()$Sample_ID)
      nmf_subtypes <- nmf_groups()$nmf_subtypes[idx]
      col <- create.brewer.color(nmf_subtypes, length(unique(nmf_subtypes)), "naikai")
      group <- paste0("NMF", nmf_subtypes)
    }else if(input$pt_col == "NMF Feature"){
      req(input$pt_nmfgene)
      idx <- match(colnames(plot_data), colnames(transform_data$data))
      gene_data <- transform_data$data[input$pt_nmfgene, idx] %>% as.numeric
      col <- create.brewer.color(gene_data, num = 9, name="YlOrRd")
      group <- input$pt_nmfgene
    }else if(input$pt_col == "Filename"){
      filename <- colnames(plot_data)
      filename <- sapply(strsplit(filename, "_"), function(x) paste0(x[as.numeric(input$pt_file_grp)], collapse = "_"))
      col <- create.brewer.color(filename, length(unique(filename)), "naikai") %>% as.matrix
      group <- filename %>% as.matrix

      # add option to manual select the order of coloring
      if(input$pt_sel_grp_order == "Manual"){
        col <- col[, 1]
        col.num <- as.numeric(factor(col, levels = unique(col) %>% as.character))
        col.lvl <- unique(col)
        col.lvl <- col.lvl[as.numeric(input$pt_grp_order)]
        col <- col.lvl[col.num] %>% as.matrix
      }
    }else if(input$pt_col == "GeneExpr"){
      req(input$pt_allgene)
      idx <- match(colnames(plot_data), colnames(transform_data$data))
      gene_data <- transform_data$data[input$pt_allgene, idx] %>% as.numeric
      col <- create.brewer.color(gene_data, num = 9, name="YlOrRd")
      group <- input$pt_allgene
    }else if(input$pt_col == "ReadDepth"){
      req(rawdata())
      idx <- match(colnames(plot_data), colnames(rawdata()))
      gene_data <- rawdata()[, idx] %>% colSums() %>% as.numeric
      col <- create.brewer.color(gene_data, num = 9, name="YlOrRd")
      group <- "Read Depth"
    }else if(input$pt_col == "NumExpresGenes"){
      req(rawdata())
      idx <- match(colnames(plot_data), colnames(rawdata()))
      gene_data <- colSums(rawdata()[, idx]>0) %>% as.numeric
      col <- create.brewer.color(gene_data, num = 9, name="YlOrRd")
      group <- "Num Expressed Genes"
    }else{
      warning("Wrong point color assigning method!")
    }

    res <- list()
    res[['color']] <- col
    res[['group']] <- group
    return(res)
  })

  pca_data <- reactive({
    data <- plot_data()[['plot_data']]
    withProgress(message = 'Calculating PCs', value = NULL, {
      pc <- prcomp(t(data), center = TRUE)

      vars<- pc$sdev^2
      vars<- signif(vars/sum(vars) * 100, 2)
      title = paste(paste0("PC", 1:length(vars)), paste0("(", vars, "% Var)"))

      projection <- as.data.frame(pc$x)
    })

    res <- list()
    res[['proj']] <- projection
    res[['title']] <- title
    return(res)
  })

  #' PCA plot
  output$pcaPlot_2D <- renderPlotly({
    projection <- pca_data()[['proj']]
    title <- pca_data()[['title']]

    validate(
      need(as.numeric(input$pca_x) != as.numeric(input$pca_y), "PC on the X-axis is the same as PC on the Y-axis.\nPlease select different number for these two axes")
    )
    idx <- c(as.numeric(input$pca_x), as.numeric(input$pca_y))

    withProgress(message = 'Generating 2d PCA plot', value = NULL, {
      projection <- projection %>%
        dplyr::select(idx)  %>%
        set_colnames(c("pca_x", "pca_y")) %>%
        mutate(color = point_col()[['color']] %>% as.character(),
               group = point_col()[['group']] %>% as.character(),
               Sample = colnames(plot_data()[['plot_data']])) %>%
        arrange(group)

      if(input$plot_label){
        t <- list( size=input$plot_label_size, color=toRGB("grey50") )
        p <- plot_ly(projection, x = ~pca_x, y = ~pca_y, type="scatter", mode="markers+text",
                     color = ~group, colors = ~unique(color),
                     text = ~Sample, hoverinfo="text", textfont=t, textposition="top middle",
                     marker = list(color = ~color, size=input$plot_point_size+3, opacity=input$plot_point_alpha))
      }else{
        p <- plot_ly(projection, x = ~pca_x, y = ~pca_y, type="scatter", mode="markers",
                     color = ~group, colors = ~unique(color),
                     text = ~Sample, hoverinfo="text",
                     marker = list(color = ~color, size=input$plot_point_size+3, opacity=input$plot_point_alpha)) #%>%
        # toWebGL()
      }
      p %>% layout(showlegend = input$plot_legend,
                   xaxis = list(title = title[idx[1]], zeroline=FALSE),
                   yaxis = list(title = title[idx[2]], zeroline=FALSE))
    })
  })
  output$pcaPlot_3D <- renderPlotly({
    projection <- pca_data()[['proj']]
    title <- pca_data()[['title']]
    idx <- c(1,2,3)

    withProgress(message = 'Generating 3d PCA plot', value = NULL, {
      projection <- projection %>%
        dplyr::select(idx)  %>%
        set_colnames(c("pca_x", "pca_y", "pca_z")) %>%
        mutate(color = point_col()[['color']] %>% as.character(),
               group = point_col()[['group']] %>% as.character(),
               Sample = colnames(plot_data()[['plot_data']])) %>%
        arrange(group)

      if(input$plot_label){
        p <- plot_ly(data=projection, x = ~pca_x, y = ~pca_y, z = ~pca_z, type="scatter3d", mode="markers+text",
                      color = ~group, colors = ~unique(color), text = ~Sample, hoverinfo="text",
                      marker = list(color = ~color, size=input$plot_point_size, opacity=input$plot_point_alpha))
      }else{
        p <- plot_ly(data=projection, x = ~pca_x, y = ~pca_y, z = ~pca_z, type="scatter3d", mode="markers",
                color = ~group, colors = ~unique(color), text = ~Sample, hoverinfo="text",
                marker = list(color = ~color, size=input$plot_point_size, opacity=input$plot_point_alpha)) #%>%
        # toWebGL()
      }

      p %>% layout(showlegend = input$plot_legend,
                   scene = list(
                     xaxis = list(title = title[1]),
                     yaxis = list(title = title[2]),
                     zaxis = list(title = title[3]))
      )
    })
  })

  #' t-SNE plot
  cores <- reactive({
    avail_cores <- parallel::detectCores()
    if(avail_cores==1){
      return(1)
    }else if(as.numeric(input$ncores) <= avail_cores){
      return(as.numeric(input$ncores))
    }else{
      return(avail_cores)
    }
  })
  tsne_2d <- reactiveValues(data=NULL)
  tsne_3d <- reactiveValues(data=NULL)

  observeEvent(input$runtSNE, {
    nsamples <- ncol(plot_data()[['plot_data']])
    if(input$tsne_perplexity*3 > (nsamples-1)) {
      createAlert(session, "visualperplexityAlert", "visualperplexityAlert1", title = "WARNING", style = "warning",
                  content = paste("Perpleixty", input$tsne_perplexity,
                                  "is too large compared to num of samples", nsamples),
                  append = FALSE)
    }else {
      closeAlert(session, "visualperplexityAlert1")

      plot_data <- plot_data()[['plot_data']]
      withProgress(message = 'Running t-SNE', value = NULL, {
        incProgress(1/3, detail = "For t-SNE 2D")
        tsne_2d$data <- run_tsne(plot_data, iter=input$tsne_iter, dims=2,
                                 perplexity=input$tsne_perplexity, cores=cores())
        incProgress(2/3, detail = "For t-SNE 3D")
        tsne_3d$data <- run_tsne(plot_data, iter=input$tsne_iter, dims=3,
                                 perplexity=input$tsne_perplexity, cores=cores())
      })
    }
  })

  plot_tsne_2d <- reactive({
    nsamples <- ncol(plot_data()[['plot_data']])
    validate(
      need(!is.null(tsne_2d$data), "Please click 'Run t-SNE' button")
    )
    tsne_out <- isolate(tsne_2d$data)
    withProgress(message = 'Genrating t-SNE plot', value = 0, {
      incProgress(2/3, detail = "Usually takes 15~20 seconds")
      color <- "steelblue"

      projection <- parse_tsne_res(tsne_out) %>%
        data.frame %>%
        mutate(group = point_col()[['group']] %>% as.character(),
               color = point_col()[['color']] %>% as.character(),
               Sample = rownames(.)) %>%
        arrange(group)

      min.cost <- signif(tsne_out$itercosts[length(tsne_out$itercosts)], 2)
      if(input$plot_label){
        t <- list( size=input$plot_label_size, color=toRGB("grey50") )
        p <- plot_ly(projection, x = ~x, y = ~y, type="scatter", mode="markers+text",
                     color = ~group, colors = ~unique(color),
                     text= ~Sample, hoverinfo="text", textposition="top middle", textfont=t,
                     marker = list(color = ~color, size=input$plot_point_size+3, opacity=input$plot_point_alpha))
      }else{
        p <- plot_ly(projection, x = ~x, y = ~y, type="scatter", mode="markers", text= ~Sample, hoverinfo="text",
                     color = ~group, colors = ~unique(color),
                     marker = list(color = ~color, size=input$plot_point_size+3, opacity=input$plot_point_alpha)) #%>%
          # toWebGL()
      }
      p %>% layout(showlegend = input$plot_legend,
                   title = title,
                   xaxis = list(title = "Component 1", zeroline=FALSE),
                   yaxis = list(title = "Component 2", zeroline=FALSE))
    })
  })
  output$tsneplot_2d <- renderPlotly({
    plot_tsne_2d()
  })
  output$tsneplot_3d <- renderPlotly({
    nsamples <- ncol(plot_data()[['plot_data']])
    if(is.null(tsne_3d$data)) return ()

    projection <- isolate(as.data.frame(tsne_3d$data$Y)) %>%
        set_colnames(c("tsne_1", "tsne_2", "tsne_3")) %>%
        mutate(group = point_col()[['group']] %>% as.character(),
               color = point_col()[['color']] %>% as.character(),
               Sample = rownames(.)) %>%
        arrange(group)

    if(input$plot_label){
      p <- plot_ly(data=projection, x = ~tsne_1, y = ~tsne_2, z = ~tsne_3, type="scatter3d", mode="markers+text",
                   color = ~group, colors = ~unique(color), text= ~Sample, hoverinfo="text",
                   marker = list(color = ~color, size=input$plot_point_size, opacity=input$plot_point_alpha))
    }else{
      p <- plot_ly(data=projection, x = ~tsne_1, y = ~tsne_2, z = ~tsne_3, type="scatter3d", mode="markers",
                   color = ~group, colors = ~unique(color), text= ~Sample, hoverinfo="text",
                   marker = list(color = ~color, size=input$plot_point_size, opacity=input$plot_point_alpha)) %>%
        toWebGL()
    }

    p %>% layout(showlegend = input$plot_legend,
                 scene = list(
                   xaxis = list(title = "Component 1"),
                   yaxis = list(title = "Component 2"),
                   zaxis = list(title = "Component 3"))
    )
  })


  ### DESeq2 ###
  observeEvent(input$runNMF, {
    validate(
      need(is.numeric(input$num_cluster), "num of cluster is not a valid numeric number")
    )
    updateSelectizeInput(session, 'de_group1',
                         server = TRUE,
                         choices = as.character(paste0("NMF", sort(unique(nmf_groups()$nmf_subtypes)))),
                         selected = "NMF1"
    )
    updateSelectizeInput(session, 'de_group2',
                         server = TRUE,
                         choices = as.character(paste0("NMF", sort(unique(nmf_groups()$nmf_subtypes)))),
                         selected = "NMF2"
    )
  })

  deseq_res <- eventReactive(input$runDESeq, {
    if(input$selectfile == "saved"){
      if(!is.null(rda()$dds)){
        return(rda()$dds)
      }
    }

    rawdata <- rawdata()
    if(input$de_conv2int)
      rawdata <- round(rawdata)

    if(!all(sapply(rawdata, is.whole))){
      createAlert(session, "alert", "exampleAlert", title = "WARNING", style = "warning",
                  content = "Data contains non-integer value, Please use raw count data for DESeq2. <br>
                             Or Use the option above to 'Force convert to integer' <b>(Results are not statistically rigorous)</b>",
                  append = FALSE)
      return(NULL)
    }else{
      closeAlert(session, "exampleAlert")
    }

    # since the user might have filtered samples during the QC steps,
    # rematch sample_ID and only kept the ones that are presented in NMF res
    idx <- match(nmf_groups()$Sample_ID, colnames(rawdata))
    rawdata <- rawdata[, idx]

    register(BiocParallel::MulticoreParam(workers = cores()))
    colData <- data.frame(Group = paste0("NMF", nmf_groups()$nmf_subtypes))
    ddsfeatureCounts <- DESeq2::DESeqDataSetFromMatrix(countData = rawdata,
                                                       colData = colData,
                                                       design = ~ Group)
    ### Set betaPrior=FALSE to go with MLE LFC to get simple LFC = (avg in group2/ avg in group1)
    # dds <- DESeq(ddsfeatureCounts, parallel=T, betaPrior=FALSE)
    withProgress(message = 'Running DESeq2', value = NULL, {
      dds <- DESeq2::DESeq(ddsfeatureCounts, parallel=T)
    })
    return(dds)
  })

  filt_deseq_res <- reactive({
    req(deseq_res())
    withProgress(message = 'Summarizing DESeq2 result', value = NULL, {
      register(BiocParallel::MulticoreParam(workers = 8))
      DESeq2::results(deseq_res(), parallel = TRUE,
                      contrast = c("Group", input$de_group1, input$de_group2)) %>%
        as.data.frame %>%
        subset(padj <= input$de_alpha) %>%
        .[order(.$padj), ]
    })
  })

  output$deseq_table <- DT::renderDataTable({
    validate(
      need(nrow(filt_deseq_res()) > 0, "No significant DE results found, Maybe try lower your alpha threshold?")
    )
    filename <- fileinfo()[['name']]
    GeneCard <- paste0("<a href='http://www.genecards.org/cgi-bin/carddisp.pl?gene=",
                       rownames(filt_deseq_res()), "'>", rownames(filt_deseq_res()), "</a>")
    filt_res <- data.frame(Gene=GeneCard,
                           filt_deseq_res())
    DT::datatable(filt_res, selection='single', rownames=FALSE,
                  extensions = 'Buttons',
                  escape = FALSE,
                  options = list(dom = 'Bfrtip',
                                 buttons =
                                   list('colvis', 'copy', 'print', list(
                                     extend = 'collection',
                                     buttons = list(list(extend='csv',
                                                         filename = filename),
                                                    list(extend='excel',
                                                         filename = filename)),
                                     text = 'Download'
                                   )),
                                 scrollY = TRUE,
                                 pageLength = 12,
                                 autoWidth = TRUE
                  )
    ) %>% formatRound(2:ncol(filt_res), 3)
  }, server = FALSE)

  output$deseq_boxplot <- renderPlotly({
    req(deseq_res())
    validate(
      need(!is.null(input$deseq_table_rows_selected), "Please select a gene")
    )
    withProgress(message = 'Generating boxplot', value = NULL, {
      norm.data <- DESeq2::counts(deseq_res(), normalized=TRUE)
      gene <- rownames(filt_deseq_res())[input$deseq_table_rows_selected]
      gene.data <- norm.data[gene, ]
      gene.data <- data.frame(Sample = names(gene.data),
                              Expr = gene.data,
                              NMF = factor(paste0("NMF", nmf_groups()$nmf_subtypes)))

      # match the coloring from PCA, t-SNE, and heatmap
      num_clus <- gene.data$NMF %>% levels %>% length
      mycolor <- create.brewer.color(1:num_clus, num=num_clus, name="naikai")

      a <- ggplot(data=gene.data, aes(x=NMF, y=Expr, color=NMF)) +
        geom_boxplot(aes(color = NMF)) +
        geom_jitter(aes(color=NMF, text=Sample), alpha=0.6, width=0.1) +
        theme_bw() +
        scale_colour_manual(values = mycolor) +
        labs(title=gene, x="", y="Expression", colour="")
      p <- plotly_build(a)
      # remove outliers for plotly boxplot
      p$data <- lapply(p$data, FUN = function(x){
        if(x$type == "box"){
          x$marker = list(opacity = 0)
        }
        return(x)
      })
      p$layout$margin$l <- p$layout$margin$l + 15
      p$layout$annotations[[1]]$x <- -0.1
      p$layout$xaxis$tickfont$size <- 8
      p$layout$annotations[[1]]$font$size <- 11
      p$layout$yaxis$tickfont$size <- 10
      p
    })
  })


  ### Pathway analysis ###
  nmf_group_feature_rank <- reactive({
    validate(
      need(is.numeric(input$num_cluster), "num of cluster is not a numeric value") %then%
      need(!is.null(nmf_groups()), "Please run NMF first")
    )
    rawdata <- transform_data$data
    # rawdata <- rawdata[rowMeans(rawdata) >= input$min_rowMean, ]
    nmf_group <- nmf_groups()$nmf_subtypes
    num_nmf_subtypes <- nmf_group %>% unique %>% length

    if(input$rank_method=="featureScore"){
      print('Do we want to use featureScore, there are only few genes')
    }else if(input$rank_method=="logFC"){
      nmf_feature_rank <- data.frame(matrix(NA_real_, nrow=nrow(rawdata), ncol=num_nmf_subtypes))

      for(i in 1:num_nmf_subtypes){
        idx <- nmf_group==i
        nmf_feature_rank[, i] <- rawdata %>% as.data.frame %>%
                                  dplyr::mutate(logFC = log(rowMeans(.[idx])/rowMeans(.[!idx]))) %>%
                                  dplyr::select(logFC)
      }
      colnames(nmf_feature_rank) <- paste0("NMF", 1:num_nmf_subtypes)
      rownames(nmf_feature_rank) <- rownames(rawdata)
    }
    return(nmf_feature_rank)
  })

  geneset.file <- reactive({
    validate(
      need(!is.null(input$predefined_list), "Please select at least one predefined gene sets")
    )
    file <- pre_geneset[[ input$predefined_list ]]
    return (file)
  })

  ncpus <- reactive({
    cpus <- c(16,8,6,4,2,1)
    cpus[min(which(sapply(cpus, function(x) input$nPerm%%x)==0))]
  })

  observeEvent(input$runNMF, {
    updateSelectizeInput(session, 'pathway_group',
                         server = TRUE,
                         choices = as.character(paste0("NMF", sort(unique(nmf_groups()$nmf_subtypes))))
    )
  })
  pathview.species <- reactive({
    species <- input$species
    if(species == "Human" | species == "human" | species == "hg19" | species == "hsa"){
      pathview.species <- "hsa"
    }else if(species == "Mouse" | species == "mouse" | species == "mm9" | species == "mmu"){
      pathview.species <- "mmu"
    }else if(species == "Drosophila" | species == "dm3" | species == "dm6"){
      pathview.species <- "dm3"
    }
  })
  id.org <- reactive({
    species <- input$species
    if(species == "Human" | species == "human" | species == "hg19" | species == "hsa"){
      id.org <- "Hs"
    }else if(species == "Mouse" | species == "mouse" | species == "mm9" | species == "mmu"){
      id.org <- "Mm"
    }
  })
  kegg.gs <- reactive({
    species <- input$species
    withProgress(message = 'Extracting KEGG pathway information', value = NULL, {
      if(species == "Human" | species == "human" | species == "hg19" | species == "hsa"){
        kg.human<- kegg.gsets("human")
        kegg.gs<- kg.human$kg.sets[kg.human$sigmet.idx]
      }else if(species == "Mouse" | species == "mouse" | species == "mm9" | species == "mmu"){
        kg.mouse<- kegg.gsets("mouse")
        kegg.gs<- kg.mouse$kg.sets[kg.mouse$sigmet.idx]
      }
    })
    return(kegg.gs)
  })
  go.gs <- reactive({
    species <- input$species
    withProgress(message = 'Extracting GO term information', value = NULL, {
      if(species == "Human" | species == "human" | species == "hg19" | species == "hsa"){
        go.gs <- go.gsets(species="human", pkg.name=NULL, id.type = "eg")
      }else if(species == "Mouse" | species == "mouse" | species == "mm9" | species == "mmu"){
        go.gs <- go.gsets(species="mouse", pkg.name=NULL, id.type = "eg")
      }
    })
    return(go.gs)
  })

  gage.exp.fc <- eventReactive(input$run_enrich, {
    rankdata <- nmf_group_feature_rank()
    num_groups <- ncol(rankdata)
    emp <- matrix(NA, nrow(rankdata), 1)
    res <- rep(list(emp), num_groups)
    names(res) <- colnames(rankdata)

    withProgress(message = 'Calculating LogFC between groups', value = NULL, {
      for (i in 1:num_groups){
        # In logFC, error might come from the fact that some of them are Inf or -Inf
        idx <- is.finite(rankdata[, i])
        sub_rankdata <- rankdata[idx, i, drop=F]
        # ID conversion
        id.map.sym2eg <- id2eg(ids=rownames(sub_rankdata), category = "SYMBOL", org=id.org())
        gene.entrez <- mol.sum(mol.data = sub_rankdata, id.map = id.map.sym2eg, sum.method = "mean")
        res[[i]] <- gene.entrez[, 1]
        res[[i]] <- res[[i]][!is.na(res[[i]])]
      }
    })
    return(res)
  })

  go.Res <- reactive({
    gage.exp.fc <- gage.exp.fc()
    gsets <- NULL
    res <- rep(list(NA), length(gage.exp.fc))
    names(res) <- names(gage.exp.fc)

    if(input$EnrichType == 'GO'){
      gsets <- go.gs()
      gsets <- gsets$go.sets[ gsets$go.subs[[input$goTerm]] ]
    }else if(input$EnrichType == 'KEGG'){
      gsets <- kegg.gs()
    }

    validate(
      need(length(gsets)>0, "Did not find any Gene Sets, maybe your gene count data is not from 'Human'?")
    )

    withProgress(message = 'Running enrichment analysis', value = NULL, {
      for (i in 1:length(gage.exp.fc)){
        exp.fc <- gage.exp.fc[[i]]
        goRes <- gage(exp.fc, gsets = gsets, ref = NULL, samp = NULL, same.dir=ifelse(input$samedir, TRUE, FALSE))
        res[[i]] <- goRes
      }
    })
    return(res)
  })

  go_summary <- reactive({
    goRes <- go.Res()
    genes <- goRes[[1]]$greater %>% rownames
    res <- list()
    num_genes <- 5
    hi_res <- lo_res <- matrix(NA, num_genes * length(goRes), length(goRes))
    colnames(hi_res) <- colnames(lo_res) <- names(goRes)
    total_hi_names <- total_lo_names <- rep(NA, num_genes * length(goRes))

    for(i in 1:length(goRes)){
      range <- ((i-1) * num_genes + 1) : (i * num_genes)
      hi_genenames <- goRes[[i]]$greater %>% rownames %>% head(n = num_genes)
      hi_res[range, ] <- sapply(goRes, function(x) x$greater[hi_genenames, "p.val"])
      total_hi_names[range] <- hi_genenames

      if(!is.na(goRes[[i]]$less[1])){
        lo_genenames <- goRes[[i]]$less %>% rownames %>% head(n = num_genes)
        lo_res[range, ] <- sapply(goRes, function(x) x$less[lo_genenames, "p.val"])
        total_lo_names[range] <- lo_genenames
      }
    }
    rownames(hi_res) <- total_hi_names
    rownames(lo_res) <- total_lo_names
    # save only the unique GO:Term
    res[["hi_sub_pval"]] <- hi_res[!duplicated(rownames(hi_res)), ]
    res[["lo_sub_pval"]] <- lo_res[!duplicated(rownames(lo_res)), ]

    # save total results
    res[["hi_total_pval"]] <- sapply(goRes, function(x) x$greater[genes, "p.val"])
    res[["hi_total_qval"]] <- sapply(goRes, function(x) x$greater[genes, "q.val"])
    res[["lo_total_pval"]] <- sapply(goRes, function(x) x$less[genes, "p.val"])
    res[["lo_total_qval"]] <- sapply(goRes, function(x) x$less[genes, "q.val"])

    return(res)
  })

  output$goplot_hi <- renderPlotly({
    req(go_summary())
    validate(
      need(!is.na(go_summary()[["hi_sub_pval"]]), "Did not find any GO terms for greater, maybe you select the wrong 'species'?")
    )
    withProgress(message = 'Generating summary plot for GO term (Hi)', value = NULL, {
      aa <- isolate(
        melt(go_summary()[["hi_sub_pval"]]) %>%
        set_colnames(c("GO", "NMF", "P")) %>%
        mutate(P = replace(P, is.na(P), 1))
      )

      if(input$go_logscale){
        p <- plot_ly(aa, x = ~NMF, y = ~GO, z = ~log10(P), type = "heatmap", colors = input$go_colorscale)
      }else{
        p <- plot_ly(aa, x = ~NMF, y = ~GO, z = ~P, type = "heatmap", colors = input$go_colorscale)
      }

      m <- list( l = input$go_leftmargin, r = 10, b = 40, t = 20)
      l <- list(margin = m,
                xaxis = list(title = "", tickfont=list(family = "Arial", size = input$go_xfontsize), zeroline=FALSE),
                yaxis = list(title = "", tickfont=list(family = "Arial", size = input$go_yfontsize), zeroline=FALSE))

      colorbar_format <- list(family = "Arial", size = 8)
      naikai <- plotly_build(p)
      naikai$x$data[[1]]$colorbar <- list(title = "P", thickness = input$go_barwidth, len=0.5, titlefont=colorbar_format, tickfont=colorbar_format)
      naikai$x$layout <- l
      naikai
    })
  })

  output$goplot_lo <- renderPlotly({
    req(go_summary())
    validate(
      need(!is.na(go_summary()[["lo_sub_pval"]]), "Did not find any GO terms for less, maybe you select the wrong 'species'?")
    )
    withProgress(message = 'Generating summary plot for GO term (Low)', value = NULL, {
      aa <- isolate(
        melt(go_summary()[["lo_sub_pval"]]) %>%
        set_colnames(c("GO", "NMF", "P")) %>%
        mutate(P = replace(P, is.na(P), 1))
      )

      if(input$go_logscale){
        p <- plot_ly(aa, x = ~NMF, y = ~GO, z = ~log10(P), type = "heatmap", colors = input$go_colorscale)
      }else{
        p <- plot_ly(aa, x = ~NMF, y = ~GO, z = ~P, type = "heatmap", colors = input$go_colorscale)
      }

      m <- list( l = input$go_leftmargin, r = 10, b = 40, t = 20)
      l <- list(margin = m,
                   xaxis = list(title = "", tickfont=list(family = "Arial", size = input$go_xfontsize), zeroline=FALSE),
                   yaxis = list(title = "", tickfont=list(family = "Arial", size = input$go_yfontsize), zeroline=FALSE))

      colorbar_format <- list(family = "Arial", size = 8)
      naikai <- plotly_build(p)
      naikai$x$data[[1]]$colorbar <- list(title = "P", thickness = input$go_barwidth, len=0.5, titlefont=colorbar_format, tickfont=colorbar_format)
      naikai$x$layout <- l
      naikai
    })
  })

  go_table <- reactive({
    goRes <- go.Res()
    validate(
      need(input$pathway_group!="", "Please select the enrichment result for the group you are interest in")
    )
    goRes <- goRes[[input$pathway_group]]
    if (is.null(goRes$greater)){
      stop("No significant gene sets found using this q.val cutoff")
    }else{
      return(goRes$greater)
    }
  })

  output$go_summary <- DT::renderDataTable({
    filename <- paste(input$EnrichType, input$pathway_group, input$goTerm, sep = "_")
    idx <- go_table()[, "q.val"] <= input$qval_cutoff
    go_table <- go_table()[which(!is.na(idx) & idx), ]
    if(nrow(go_table) == 0){
      stop("No significant gene sets found using this q.val cutoff")
    }
    go_table <- go_table[, c(3,4,2,5)]
    datatable(go_table, selection = 'single',
              extensions = c('Buttons', 'Responsive'),
              options = list(dom = 'Bfrtip',
                             buttons =
                               list('colvis', 'copy', 'print', list(
                                 extend = 'collection',
                                 buttons = list(list(extend='csv',
                                                     filename = filename),
                                                list(extend='excel',
                                                     filename = filename)),
                                 text = 'Download'
                               )),
                             pageLength = 6,
                             autoWidth = TRUE
                             # columnDefs = list(list(width = '10px', targets = "_all"))
              )
    ) %>% formatRound(1:3, 3)
  }, server = FALSE)


  output$pathviewImg <- renderImage({
    go_table <- go_table()
    s.idx <- input$go_summary_rows_selected[length(input$go_summary_rows_selected)]
    validate(
      need(length(s.idx)>0, "Please select one KEGG pathway from above.")
    )
    withProgress(message = 'Rendering pathview map', value = NULL, {
      path.pid <- substr(rownames(go_table)[s.idx], 1, 8)
      out.suffix <- "nmf"
      pathview(gene.data=gage.exp.fc()[[input$pathway_group]], pathway.id=path.pid, species=pathview.species(), out.suffix=out.suffix)
    })

    filename <- path.pid
    outfile <- paste(filename, out.suffix, 'png', sep=".")
    # Return a list containing the filename
    list(src = outfile,
         contentType = 'image/png',
         width = 1000,
         height = 777,
         alt = "This is alternate text")
  }, deleteFile = FALSE)

  nmf_feature_rank_for_gsea <- reactive({
    species <- input$species
    nmf_feature_rank <- nmf_group_feature_rank()
    # check if genenames contains all CAPITAL LETTERS (after removing all numeric characters)
    cap_genes <- grepl("^[[:upper:]]+$", gsub('[[:digit:]]+', '', rownames(nmf_feature_rank))) %>% sum
    cap_pct <- cap_genes/ nrow(nmf_feature_rank)
    # some of the genes might contains '.', '-', 'orf', etc, so we use percentage of genes qualified as filtering criteria

    # if(species == "Mouse" | species == "mouse" | species == "mm9" | species == "mmu"){
    if(cap_pct < 0.1){
      n <- 2
      withProgress(message = 'Assuming the input gene symbols are from Mouse, Converting to Human..', value = 0, {
        incProgress(1/n, detail = "Takes around 10~15 seconds")
        mouse_human_convert_genes <- gene_convert_mouse_to_human(rownames(nmf_feature_rank))
        ## After conversion, some of the mouse genes might point to the same human gene
        ## So select thoide mouse genes that has highest mean expression across groups?
        idx <- duplicated(mouse_human_convert_genes$HGNC.symbol)
        if (sum(idx)>0){
          warning("Some of the mouse genes are mapped back to the same human genes..")
        }
        max_dup_mean_lfc_genes <-
          cbind(mouse_human_convert_genes, nmf_feature_rank) %>%
          mutate(avg_lfc=apply(nmf_feature_rank, 1, mean),
                 abs_lfc=apply(nmf_feature_rank, 1, function(x) max(abs(x)))
          ) %>%
          dplyr::group_by(HGNC.symbol) %>%
          dplyr::filter(min_rank(desc(avg_lfc))==1) %>%
          dplyr::slice(which.max(abs_lfc)) %>%
          ungroup    # this will give Error: corrupt 'grouped_df', workaround is to ungroup it below
        print(dim(max_dup_mean_lfc_genes))
        ## Return final de-duplicated genes only
        nmf_feature_rank <- max_dup_mean_lfc_genes %>%
          dplyr::select(starts_with("NMF")) %>%
          as.data.frame
        rownames(nmf_feature_rank) <- max_dup_mean_lfc_genes$HGNC.symbol
      })
    }
    return(nmf_feature_rank)
  })

  output$downloadPathData <- downloadHandler(
    filename <- function() {
      paste( file_prefix(), "pathway.zip", sep=".")
    },
    content = function(file) {
      tmpdir <- tempdir()
      current_dir <- getwd()
      setwd(tempdir())

      ### Save GO Term results
      go_summary <- go_summary()
      total_idx <- grep("total", names(go_summary))
      go_filenames <- paste(file_prefix(), paste0("Enrichment_", names(go_summary)[total_idx]), "txt", sep=".")
      for(i in 1:length(total_idx)){
        write.table(go_summary[[ total_idx[i] ]], go_filenames[i], quote=F, row.names = TRUE, col.names=NA, sep="\t")
      }
      ### Rank file for GSEA
      # modify it to save one file for each rank ###
      nmf_group_feature_rank <- nmf_feature_rank_for_gsea()
      num_samples <- ncol(nmf_group_feature_rank)
      filenames <- paste(file_prefix(), paste0("group_feature_rank", 1:num_samples), "rnk", sep=".")
      for (i in 1:num_samples){
        sub.data <- data.frame(Gene=rownames(nmf_group_feature_rank), LogFC=nmf_group_feature_rank[,i])
        idx <- is.finite(sub.data$LogFC)
        write.table(sub.data[idx, ], filenames[i], quote=F, row.names = F, sep="\t")
      }

      zip(zipfile=file, files=c(go_filenames, filenames))
      setwd(as.character(current_dir))
    },
    contentType = "application/zip"
  )

})
naikai/sake documentation built on Feb. 15, 2023, 11 p.m.