R/fluorescence_workflows.R

Defines functions fl.workflow flFit

Documented in flFit fl.workflow

#' Perform a fluorescence curve analysis on all samples in the provided dataset.
#'
#' \code{flFit} performs all computational fluorescence fitting operations based on the user input.
#'
#' @param fl_data Either... \enumerate{ \item a \code{grodata} object created with \code{\link{read_data}} or \code{\link{parse_data}},
#'   \item a list containing a \code{'time'} matrix (for x_type == "time") or \code{'growth'} dataframe (for x_type == "growth") and a \code{'fluorescence'} dataframes,
#'   or \item a dataframe containing (normalized) fluorescence values (if a \code{time} matrix or \code{growth} dataframe is provided as separate argument).}
#' @param time (optional) A matrix containing time values for each sample.
#' @param growth (optional) A dataframe containing growth values for each sample and sample identifiers in the first three columns.
#' @param control A \code{fl.control} object created with \code{\link{fl.control}}, defining relevant fitting options.
#' @param parallelize Run linear fits and bootstrapping operations in parallel using all but one available processor cores
#' @param ... Further arguments passed to the shiny app.
#'
#' @return An \code{flFit} object that contains all fluorescence fitting results, compatible with
#'   various plotting functions of the QurvE package.
#' \item{raw.x}{Raw x matrix passed to the function as \code{time} (for x_type = 'time') or \code{growth} (for x_type = 'growth').}
#' \item{raw.fl}{Raw growth dataframe passed to the function as \code{data}.}
#' \item{flTable}{Table with fluorescence parameters and related statistics for each fluorescence curve evaluation performed by the function. This table, which is also returned by the generic \code{summary.flFit} method applied to a \code{flFit} object, is used as an input for \code{\link{fl.drFit}}.}
#' \item{flFittedLinear}{List of all \code{flFitLinear} objects, generated by the call of \code{\link{flFitLinear}}. Note: access to each object in the list via double brace: flFittedLinear\[\[#n\]\].}
#' \item{flFittedSplines}{List of all \code{flFitSpline} objects, generated by the call of \code{\link{flFitSpline}}. Note: access to each object via double brace: flFittedSplines\[\[#n\]\].}
#' \item{flBootSplines}{List of all \code{flBootSpline} objects, generated by the call of \code{\link{flBootSpline}}. Note: access to each object via double brace: flFittedSplines\[\[#n\]\].}
#' \item{control}{Object of class \code{fl.control} containing list of options passed to the function as \code{control}.}
#'
#' @details
#' Common response parameters used in dose-response analysis:<br><br><b>Linear fit:</b><br>- max_slope.linfit: Fluorescence increase rate<br>- lambda.linfit: Lag time<br>- dY.linfit: Maximum Fluorescence - Minimum Fluorescence<br>- A.linfit: Maximum fluorescence<br><br><b>Spline fit:</b><br>- max_slope.spline: Fluorescence increase rate<br>- lambda.spline: Lag time<br>- dY.spline: Maximum Fluorescence - Minimum Fluorescence<br>- A.spline: Maximum fluorescence<br>- integral.spline: Integral<br><br><b>Parametric fit:</b><br>- max_slope.model: Fluorescence increase rate<br>- lambda.model: Lag time<br>- dY.model: Maximum Fluorescence - Minimum Fluorescence<br>- A.model: Maximum fluorescence<br>- integral.model: Integral'
#'
#' @family workflows
#' @family fluorescence fitting functions
#' @family dose-response analysis functions
#'
#' @export
#'
#' @importFrom foreach %dopar%
#'
#' @examples
#' # load example dataset
#' input <- read_data(data.growth = system.file("lac_promoters_growth.txt", package = "QurvE"),
#'                    data.fl = system.file("lac_promoters_fluorescence.txt", package = "QurvE"),
#'                    csvsep = "\t",
#'                    csvsep.fl = "\t" )
#'
#' # Define fit controls
#' control <- fl.control(fit.opt = "s",
#'              x_type = "time", norm_fl = TRUE,
#'              dr.parameter = "max_slope.spline",
#'              dr.method = "model",
#'              suppress.messages = TRUE)
#'
#' # Run curve fitting workflow
#' res <- flFit(fl_data = input$norm.fluorescence,
#'              time = input$time,
#'              control = control,
#'              parallelize = FALSE)
#'
#' summary(res)
#'
flFit <- function(fl_data, time = NULL, growth = NULL, control= fl.control(), parallelize = TRUE, ...)
{
  old.options <- options()
  on.exit(options(old.options))
  # Define objects based on additional function calls
  call <- match.call()

  ## remove strictly defined arguments
  call$time <- call$growth <- call$fl_data <- call$control<- call$parallelize <- NULL


  arglist <- sapply(call, function(x) x)
  arglist <- unlist(arglist)[-1]
  ## Assign additional arguments (...) as R objects
  if(length(arglist) > 0){
    for(i in 1:length(arglist)){
      assign(names(arglist)[i], arglist[[i]])
    }
  }

  x_type <- control$x_type

  if(!(class(fl_data) %in% c("list", "grodata"))){
    if (x_type == "time" && is.numeric(as.matrix(time)) == FALSE)
      stop("Need a numeric matrix for 'time' (for x_type = 'time') or a grodata object created with read_data() or parse_data() in the 'fl_data' argument.")
    if (x_type == "growth" && is.numeric(as.matrix(growth[-1:-3])) == FALSE)
      stop("Need a dataframe for 'growth' (for x_type = 'growth') or a grodata object created with read_data() or parse_data() in the 'fl_data' argument.")
    if (is.numeric(as.matrix(fl_data[-1:-3])) == FALSE)
      stop("Need a dataframe for 'fl_data' or a grodata object created with read_data() or parse_data() in the 'fl_data' argument.")
  } else if(!is.null(fl_data)){
    time <- fl_data$time
    growth <- fl_data$growth
    fl_data <- fl_data$fluorescence
  }
  # /// check if start growth values are above min.growth in all samples
  if(!is.null(growth) && length(growth) > 1){
    max.growth <- unlist(lapply(1:nrow(growth), function (x) max(as.numeric(as.matrix(growth[x,-1:-3]))[!is.na(as.numeric(as.matrix(growth[x,-1:-3])))])))
    if(is.numeric(control$min.growth) && control$min.growth != 0){
      if(!is.na(control$min.growth) && all(as.numeric(max.growth) < control$min.growth)){
        stop(paste0("The chosen global start growth value (min.growth) is larger than every value in your dataset.\nThe maximum value in your dataset is: ",
                    max(as.numeric(max.growth))))
      }
    }
  }

  # /// check input parameters
  if (is(control)!="fl.control") stop("control must be of class fl.control!")

  # Check presence of data for chosen fits
  if(x_type == "growth" && is.null(growth))
    stop("To perform a fits on fluorescence vs. growth data, please provide a 'growth' data matrix of the same dimensions as 'fl_data'.")
  if(x_type == "time" && is.null(time))
    stop("To perform a fits on fluorescence vs. time data, please provide a 'time' data matrix of the same dimensions as 'fl_data'.")
  # /// check number of datasets
  if(control$x_type == "growth"){
    if ( (dim(growth)[1])!=(dim(fl_data)[1]) ) stop("flFit: Different number of datasets in fl_data and growth")
    x <- growth[,-(1:3)]
  }
  if(control$x_type == "time"){
    if ( (dim(time)[1])!=(dim(fl_data)[1]) ) stop("flFit: Different number of datasets in fl_data and time")
    x <- time
  }


  # /// check fitting options
  if (!all(control$fit.opt %in% c("s", "l"))){
    options(warn=1)
    if(control$suppress.messages==F) message("fit.opt must contain 's', and/or 'l'. Changed to c('s', 'l') (both fit methods)!")
    fit.opt=c('s', 'l')
    options(warn=0)
  }

  # /// Initialize some parameters
  out.table       <- NULL
  fitnonpara.all  <- list()
  fitlinear.all <- list()
  boot.all        <- list()
  fitted.param    <- NULL
  fitted.nonparam <- NULL
  bootstrap.param <- NULL
  reliability_tag_linear <- NA
  reliability_tag_nonpara <- NA

  if(control$interactive == FALSE && parallelize == TRUE &&
     dim(fl_data)[1] > 30 &&
     (
       ("l" %in% control$fit.opt) || ("a"  %in% control$fit.opt) ||
       ("s" %in% control$fit.opt && control$nboot.fl > 0)
     )
  ){
    x.ls    <- lapply(1:nrow(x), function(j) x[j, ][!is.na(x[j, ])][!is.na(fl_data[j, -1:-3])])
    wells.ls <- lapply(1:nrow(fl_data), function(j) as.numeric(fl_data[j, -1:-3][!is.na(x[j, ])][!is.na(fl_data[j, -1:-3])]))
    IDs.ls    <- lapply(1:nrow(fl_data), function(j) as.matrix(fl_data[j, 1:3]))
    wellnames.ls <- lapply(1:nrow(fl_data), function(j) paste(as.character(fl_data[j,1]), as.character(fl_data[j,2]),as.character(fl_data[j,3]), sep=" | "))

    # Set up computing clusters (all available processor cores - 1)
    cl <- parallel::makeCluster(parallel::detectCores(all.tests = FALSE, logical = TRUE)-1)
    doParallel::registerDoParallel(cl)

    # Perform linear fits in parallel
    if (("l" %in% control$fit.opt) || ("a"  %in% control$fit.opt)){
      fitlinear.all <- foreach::foreach(i = 1:dim(fl_data)[1]
      ) %dopar% {
        if(control$x_type == "growth"){
          QurvE::flFitLinear(growth = x.ls[[i]], fl_data = wells.ls[[i]], ID = IDs.ls[[i]], control = control)
        } else {
          QurvE::flFitLinear(time = x.ls[[i]], fl_data = wells.ls[[i]], ID = IDs.ls[[i]], control = control)
        }
      }
    } else {
      # /// generate list with empty objects
      fitlinear.all <- lapply(1:nrow(fl_data), function(j) list(raw.x = x.ls[[j]],
                                                                raw.fl_data = wells.ls[[j]],
                                                                filt.x = NA,
                                                                filt.fl_data = NA,
                                                                log.fl_data = NA,
                                                                ID = IDs.ls[[j]],
                                                                FUN = NA,
                                                                fit = NA,
                                                                par = c(y0 = NA, y0_lm = NA, mumax = 0, mu.se = NA, lag = NA, tmax_start = NA, tmax_end = NA,
                                                                        t_turn = NA, mumax2 = NA, y0_lm2 = NA, lag2 = NA, tmax2_start = NA,
                                                                        tmax2_end = NA),
                                                                ndx = NA, ndx2 = NA,
                                                                quota = NA,
                                                                rsquared = NA, rsquared2 = NA,
                                                                control = control,
                                                                fitFlag = FALSE, fitFlag2 = FALSE)
      )
    }

    # Perform spline bootstrappings in parallel
    if ((("s" %in% control$fit.opt) || ("a"  %in% control$fit.opt) ) &&
        (control$nboot.fl > 10) ){
      boot.all <- foreach::foreach(i = 1:dim(fl_data)[1]
      ) %dopar% {
        if(control$x_type == "growth"){
          QurvE::flBootSpline(growth = x.ls[[i]], fl_data = wells.ls[[i]], ID = IDs.ls[[i]], control = control)
        } else {
          QurvE::flBootSpline(time = x.ls[[i]], fl_data = wells.ls[[i]], ID = IDs.ls[[i]], control = control)
        }
      }
    }
    else{
      # /// create empty gcBootSpline  object
      boot.all            <- lapply(1:nrow(fl_data), function(j) list(raw.x=x.ls[[j]],
                                                                      raw.fl_data=wells.ls[[j]],
                                                                      ID =IDs.ls[[j]],
                                                                      boot.x=NA,
                                                                      boot.y=NA,
                                                                      boot.gcSpline=NA,
                                                                      lambda=NA, mu=NA, A=NA, integral=NA,
                                                                      bootFlag=FALSE, control=control
      )
      )
    }
    parallel::stopCluster(cl = cl)

    # Assign classes to list elements
    for(i in 1:length(fitlinear.all)){
      class(fitlinear.all[[i]]) <- "flFitLinear"
    }
    # for(i in 1:length(fitpara.all)){
    #   class(fitpara.all[[i]]) <- "gcFitModel"
    # }
    # for(i in 1:length(fitnonpara.all)){
    #   class(fitnonpara.all[[i]]) <- "gcFitSpline"
    # }
    for(i in 1:length(boot.all)){
      class(boot.all[[i]]) <- "flBootSpline"
    }
  }

  reliability_tag <- c()

  # /// loop over all wells

  for (i in 1:dim(fl_data)[1]){
    # Progress indicator for shiny app
    if(exists("shiny") && shiny == TRUE){
      shiny::incProgress(
        amount = 1/(dim(fl_data)[1]),
        message = "Computations completed")
    }

    # /// conversion, to handle even data.frame inputs
    actx    <-
      as.numeric(as.matrix(x[i, ]))[!is.na(as.numeric(as.matrix(x[i, ])))][!is.na(as.numeric(as.matrix((fl_data[i, -1:-3]))))]
    actwell <-
      as.numeric(as.matrix((fl_data[i, -1:-3])))[!is.na(as.numeric(as.matrix(x[i, ])))][!is.na(as.numeric(as.matrix((fl_data[i, -1:-3]))))]

    ID    <- as.matrix(fl_data[i,1:3])
    wellname <- paste(as.character(fl_data[i,1]), as.character(fl_data[i,2]),as.character(fl_data[i,3]), sep=" | ")
    if(control$suppress.messages==FALSE){
      cat("\n\n")
      cat(paste("=== ", as.character(i), ". [", wellname, "] fluorescence curve =================================\n", sep=""))
      cat("----------------------------------------------------\n")
    }
    if(parallelize == FALSE || control$interactive == TRUE ||
       dim(fl_data)[1] <= 30 ||
       !("l" %in% control$fit.opt || "a" %in% control$fit.opt || ("s" %in% control$fit.opt && control$nboot.fl > 10))
    ){
      # /// Linear regression fl_data
      if ("l" %in% control$fit.opt){
        if(control$x_type == "growth"){
          fitlinear          <- flFitLinear(growth = actx, fl_data = actwell, ID = ID, control = control)
        } else {
          fitlinear          <- flFitLinear(time = actx, fl_data = actwell, ID = ID, control = control)
        }
        fitlinear.all[[i]] <- fitlinear
      }
      else{
        # /// generate empty object
        fitlinear <- list(x.in = actx, fl.in = actwell,
                          raw.x = actx, raw.fl = actwell,
                          filt.x = actx, filt.fl = actwell,
                          ID = ID, FUN = grow_exponential, fit = NA, par = c(
                            y0 = NA, dY= NA, A = NA, y0_lm = NA, max_slope = 0, tD = NA, slope.se = NA, lag = NA, x.max_start = NA, x.max_end = NA,
                            x.turn = NA, max_slope2 = NA, tD2 = NA, y0_lm2 = NA, lag2 = NA, x.max2_start = NA,
                            x.max2_end = NA), ndx = NA, ndx.in = NA, ndx2 = NA, ndx2.in = NA, quota = 0.95, rsquared = NA, rsquared2 = NA, control = control, fitFlag = FALSE, fitFlag2 = FALSE)
        class(fitlinear)   <- "flFitLinear"
        fitlinear.all[[i]] <- fitlinear
      }
      # /// plot linear fit
      if ((control$interactive == TRUE)) {
        if (("l" %in% control$fit.opt) || ("a"  %in% control$fit.opt)) {
          answer_satisfied <- "n"
          reliability_tag_linear <- NA
          while ("n" %in% answer_satisfied) {
            try(plot(fitlinear, log = ""))
            mtext(side = 3, line = 0, adj = 0,
                  outer = FALSE,
                  cex = 1,
                  wellname)
            answer_satisfied <- readline("Are you satisfied with the linear fit (y/n)?\n\n")
            if ("n" %in% answer_satisfied) {
              test_answer <- readline("Enter: t0, h, quota, min.growth, R2, RSD, tmax, growth.max                >>>>\n\n [Skip (enter 'n'), or adjust fit parameters (see ?flFitLinear).\n Leave {blank} at a given position if standard parameters are desired.]\n\n")
              if ("n" %in% test_answer) {
                if(control$suppress.messages==FALSE){
                  cat("\n Tagged the linear fit of this sample as unreliable !\n\n")
                }
                reliability_tag_linear              <- FALSE
                fitlinear$reliable <- FALSE
                fitlinear.all[[i]]$reliable    <- FALSE
                answer_satisfied <- "y"
              } # end if ("n" %in% test_answer)
              else {
                new_params <- unlist(strsplit(test_answer, split = ","))
                t0_new <- ifelse(!is.na(as.numeric(new_params[1])), as.numeric(new_params[1]), control$t0)
                h_new <- if(!is.na(as.numeric(new_params[2]))){
                  as.numeric(new_params[2])
                } else {
                  control$lin.h
                }
                quota_new <- ifelse(!is.na(as.numeric(new_params[3])), as.numeric(new_params[3]), 0.95)
                min.growth_new <- ifelse(!is.na(as.numeric(new_params[4])), as.numeric(new_params[4]), control$min.growth)
                R2_new <- ifelse(!is.na(as.numeric(new_params[5])), as.numeric(new_params[5]), control$lin.R2)
                RSD_new <- ifelse(!is.na(as.numeric(new_params[6])), as.numeric(new_params[6]), control$lin.RSD)
                tmax_new <- ifelse(!is.na(as.numeric(new_params[7])), as.numeric(new_params[7]), control$tmax)
                max.growth_new <- ifelse(!is.na(as.numeric(new_params[8])), as.numeric(new_params[8]), control$max.growth)
                control_new <- control
                control_new$t0 <- t0_new
                control_new$lin.h <- h_new
                control_new$lin.R2 <- R2_new
                control_new$lin.RSD <- RSD_new
                control_new$tmax <- tmax_new
                control_new$max.growth <- max.growth_new

                if(is.numeric(min.growth_new)){
                  if(!is.na(min.growth_new) && all(as.vector(actwell) < min.growth_new)){
                    message(paste0("Start growth values need to be greater than 'min.growth'.\nThe minimum start value in your dataset is: ",
                                   min(as.vector(actwell)),". 'min.growth' was not adjusted."), call. = FALSE)
                  } else if(!is.na(min.growth_new)){
                    control_new$min.growth <- min.growth_new
                  }
                }
                if ("l" %in% control$fit.opt){
                  if(control$x_type == "growth"){
                    fitlinear          <- flFitLinear(growth = actx, fl_data = actwell, ID = ID, control = control_new, quota = quota_new)
                  } else {
                    fitlinear          <- flFitLinear(time = actx, fl_data = actwell, ID = ID, control = control_new, quota = quota_new)
                  }
                  fitlinear.all[[i]] <- fitlinear
                }
                fitlinear.all[[i]] <- fitlinear
              } #end else
            } # end if ("n" %in% test_answer)
            else{
              reliability_tag_linear <- TRUE
              fitlinear$reliable <- TRUE
              fitlinear.all[[i]]$reliable <- TRUE
              if(control$suppress.messages==FALSE){
                cat("Sample was (more or less) o.k.\n")
              }
            } # end else
          } # end while ("n" %in% answer_satisfied)
        } # end if (("l" %in% control$fit.opt) || ("a"  %in% control$fit.opt))
      } # end if ((control$interactive == TRUE))
      else {
        reliability_tag_linear <- TRUE
        fitlinear$reliable <- TRUE
        fitlinear.all[[i]]$reliable <- TRUE
      }
    } # # control$interactive == TRUE || dim(fl_data)[1] <= 30


    # /// Non parametric fit
    if ("s" %in% control$fit.opt){
      if(control$x_type == "growth"){
        nonpara             <- flFitSpline(growth = actx, fl_data = actwell, ID = ID, control = control)
      } else {
        nonpara             <- flFitSpline(time = actx, fl_data = actwell, ID = ID, control = control)
      }
      fitnonpara.all[[i]] <- nonpara
    }
    else{
      # /// generate empty object
      nonpara             <- list(raw.x = actx, raw.fl = actwell,
                                  fit.x = rep(NA, length(actx)), fit.fl = rep(NA, length(actwell)),
                                  parameters = list(A = NA, dY = NA, max_slope = NA, x.max = NA, lambda = NA, b.tangent = NA, max_slope2 = NA, x.max2 = NA,
                                                    lambda2 = NA, b.tangent2 = NA, integral = NA),
                                  spline = NA, reliable = NULL, fitFlag = FALSE, fitFlag2 = FALSE,
                                  control = control)
      class(nonpara)      <- "flFitSpline"
      fitnonpara.all[[i]] <- nonpara
    }
    # /// plotting parametric fit
    if ((control$interactive == TRUE)) {
      # /// plotting nonparametric fit
      if (("s" %in% control$fit.opt) || ("a"  %in% control$fit.opt)) {
        if (nonpara$fitFlag == TRUE) {
          answer_satisfied <- "n"
          reliability_tag_nonpara <- NA
          while ("n" %in% answer_satisfied) {
            plot(nonpara, add=FALSE, raw=TRUE,slope = TRUE, colData=1, cex=1, plot=T, export=F)
            answer_satisfied <- readline("Are you satisfied with the spline fit (y/n)?\n\n")
            if ("n" %in% answer_satisfied) {
              test_answer <- readline("Enter: smooth.fl, t0, min.growth, tmax, max.growth                        >>>> \n\n [Skip (enter 'n'), or smooth.fl, t0, and min.growth (see ?fl.control).\n Leave {blank} at a given position if standard parameters are desired.]\n\n ")
              if ("n" %in% test_answer) {
                if(control$suppress.messages==FALSE){
                  cat("\n Tagged the linear fit of this sample as unreliable !\n\n")
                }
                reliability_tag_nonpara              <- FALSE
                nonpara$reliable <- FALSE
                fitnonpara.all[[i]]$reliable    <- FALSE
                fitnonpara.all[[i]]$FitFlag    <- FALSE
                answer_satisfied <- "y"
              } # end if ("n" %in% test_answer)
              else{
                new_params <- unlist(strsplit(test_answer, split = ","))
                if(!is.na(as.numeric(new_params[2])) && as.numeric(new_params[2]) != ""){
                  t0_new <- as.numeric(new_params[2])
                } else {
                  t0_new <- control$t0
                }
                smooth.fl_new <- as.numeric(new_params[1])
                control_new <- control
                if(!is.na(smooth.fl_new) && smooth.fl_new != ""){
                  control_new$smooth.fl <- smooth.fl_new
                }
                control_new$t0 <- t0_new
                min.growth_new <- as.numeric(new_params[3])
                if(!is.na(min.growth_new)){
                  if(is.numeric(min.growth_new) && min.growth_new != 0 && all(as.vector(actx) < min.growth_new)){
                    message(paste0("Start growth values need to be below 'min.growth'.\nThe minimum start value in your dataset is: ",
                                   min(as.vector(growth[,4])),". 'min.growth' was not adjusted."), call. = FALSE)
                  } else if(!is.na(min.growth_new)){
                    control_new$min.growth <- min.growth_new
                  }
                }
                tmax_new <- as.numeric(new_params[4])
                if(!is.na(tmax_new) && tmax_new != ""){
                  control_new$tmax <- tmax_new
                }
                max.growth_new <- as.numeric(new_params[5])
                if(!is.na(max.growth_new) && max.growth_new != ""){
                  control_new$max.growth <- max.growth_new
                }
                if(control$x_type == "growth"){
                  nonpara             <- flFitSpline(growth = actx, fl_data = actwell, ID = ID, control = control_new)
                } else {
                  nonpara             <- flFitSpline(time = actx, fl_data = actwell, ID = ID, control = control_new)
                }
                fitnonpara.all[[i]] <- nonpara
              } #end else
            } # end if ("n" %in% answer_satisfied)
            else{
              reliability_tag_nonpara <- TRUE
              nonpara$reliable <- TRUE
              fitnonpara.all[[i]]$reliable <- TRUE
              fitnonpara.all[[i]]$FitFlag <- TRUE
              if(control$suppress.messages==FALSE){
                cat("Sample was (more or less) o.k.\n")
              }
            } # end else
          } # end while ("n" %in% answer_satisfied)
        } # end if (nonpara$fitFlag == TRUE)
      } # end if (("s" %in% control$fit.opt) || ("a"  %in% control$fit.opt) )
    } # end of if((control$interactive == TRUE))
    else{
      reliability_tag_nonpara <- TRUE
    }

    if(parallelize == FALSE || control$interactive == TRUE ||
       dim(fl_data)[1] <= 30 ||
       !("l" %in% control$fit.opt || "a" %in% control$fit.opt || ("s" %in% control$fit.opt && control$nboot.fl > 10))
    ){
      # /// Beginn Bootstrap
      if ((("s" %in% control$fit.opt) ) &&
          (control$nboot.fl > 0) && (reliability_tag_nonpara ==TRUE) && nonpara$fitFlag == TRUE){
        if(control$x_type == "growth")   bt <- flBootSpline(growth = actx, fl_data = actwell, ID = ID, control = control)
        if(control$x_type == "time")      bt <- flBootSpline(time = actx, fl_data = actwell, ID = ID, control = control)
        boot.all[[i]] <- bt
      } # /// end of if (control$nboot.fl ...)
      else{
        # /// create empty flBootSpline  object
        bt            <- list(raw.x=actx, raw.fl=actwell, ID =ID, boot.x=NA, boot.y=NA, boot.flSpline=NA,
                              lambda=NA, mu=NA, A=NA, integral=NA, bootFlag=FALSE, control=control)
        class(bt)     <- "flBootSpline"
        boot.all[[i]] <- bt
      }
    } # if(interactive == TRUE || dim(fl_data)[1] <= 30 ||
    reliability_tag <- c(reliability_tag, any(reliability_tag_linear, reliability_tag_nonpara))
    # create output table
    # description     <- data.frame(TestId=fl_data[i,1], AddId=fl_data[i,2],concentration=fl_data[i,3],
    #                               reliability_tag=reliability_tag,
    #                               log.x.spline=control$log.x.spline, log.y.spline=control$log.y.spline,
    #                               log.x.lin=control$log.x.lin, log.y.spline=control$log.y.lin, nboot.fl=control$nboot.fl)
    #
    # fitted          <- cbind(description, summary.flFitLinear(fitlinear), summary.flFitSpline(nonpara), summary.flBootSpline(bt))
    #
    # out.table       <- rbind(out.table, fitted)
    # class(out.table) <- c("data.frame", "flTable")

  } # /// end of for (i in 1:dim(fl_data)[1])

  # Assign names to list elements
  names(fitlinear.all) <- names(fitnonpara.all) <- names(boot.all) <- paste0(as.character(fl_data[,1]), " | ", as.character(fl_data[,2]), " | ", as.character(fl_data[,3]))

  # create output table
  description     <- lapply(1:nrow(fl_data), function(x) data.frame(TestId = fl_data[x,1], AddId = fl_data[x,2],concentration = fl_data[x,3],
                                                                    reliability_tag = reliability_tag[x],
                                                                    log.x.spline = control$log.x.spline,
                                                                    log.y.spline = control$log.y.spline,
                                                                    log.x.lin = control$log.x.lin,
                                                                    log.y.lin  =control$log.y.lin,
                                                                    nboot.fl = control$nboot.fl
  )
  )

  fitted          <- lapply(1:length(fitlinear.all), function(x) cbind(description[[x]],
                                                                       summary.flFitLinear(fitlinear.all[[x]]),
                                                                       summary.flFitSpline(fitnonpara.all[[x]]),
                                                                       summary.flBootSpline(boot.all[[x]])
  )
  )
  df <- data.frame()

  out.table       <- do.call(rbind, fitted)
  class(out.table) <- c("data.frame", "flTable")
  flFit           <- list(raw.x = x, raw.fl = fl_data, flTable = out.table, flFittedLinear = fitlinear.all, flFittedSplines = fitnonpara.all, flBootSplines = boot.all, control=control)

  class(flFit)    <- "flFit"
  invisible(flFit)
}

#' Run a complete fluorescence curve analysis and dose-reponse analysis workflow.
#'
#' \code{fl.workflow} runs \code{\link{fl.control}} to create a \code{fl.control} object and then performs all computational fitting operations based on the user input. Finally, if desired, a final report is created in PDF or HTML format that summarizes all results obtained.
#'
#' @param grodata A \code{grodata} object created with \code{\link{read_data}} or \code{\link{parse_data}}, containing fluorescence data and data for the independent variable (i.e., time or growth).
#' @param time (optional) A matrix containing time values for each sample (if a \code{fl_data} dataframe is provided as separate argument).
#' @param growth (optional) A dataframe containing growth data (if a \code{fl_data} matrix is provided as separate argument).
#' @param fl_data (optional) A dataframe containing fluorescence data (if a \code{time} matrix or \code{growth} dataframe is provided as separate argument).
#' @param ec50 (Logical) Perform dose-response analysis (\code{TRUE}) or not (\code{FALSE}).
#' @param mean.grp (\code{"all"}, a string vector, or a list of string vectors) Define groups to combine into common plots in the final report based on sample identifiers (if \code{report == TRUE}). Partial matches with sample/group names are accepted. Note: The maximum number of sample groups (with unique condition/concentration indicators) is 50. If you have more than 50 groups, option \code{"all"} will produce the error \code{! Insufficient values in manual scale. [Number] needed but only 50 provided}.
#' @param mean.conc (A numeric vector, or a list of numeric vectors) Define concentrations to combine into common plots in the final report (if \code{report == TRUE}).
#' @param fit.opt (Character or character vector) Indicates whether the program should perform a linear regression (\code{"l"}), model fit (\code{"m"}), spline fit (\code{"s"}), or all (\code{"a"}). Combinations can be freely chosen by providing a character vector, e.g. \code{fit.opt = c("l", "s")} Default:  \code{fit.opt = c("l", "s")}.
#' @param x_type (Character) Which data type shall be used as independent variable? Options are \code{'growth'} and \code{'time'}.
#' @param norm_fl (Logical) use normalized (to growth) fluorescence data in fits. Has an effect only when \code{x_type = 'time'}
#' @param t0 (Numeric) Minimum time value considered for linear and spline fits (if \code{x_type = 'time'}).
#' @param tmax (Numeric) Maximum time value considered for linear and spline fits (if \code{x_type = 'time'})..
#' @param min.growth (Numeric) Indicate whether only values above a certain threshold should be considered for linear regressions or spline fits (if \code{x_type = 'growth'}).
#' @param max.growth (Numeric) Indicate whether only growth values below a certain threshold should be considered for linear regressions or spline fits (if \code{x_type = 'growth'}).
#' @param log.x.lin (Logical) Indicates whether _ln(x+1)_ should be applied to the independent variable for _linear_ fits. Default: \code{FALSE}.
#' @param log.x.spline (Logical) Indicates whether _ln(x+1)_ should be applied to the independent variable for _spline_ fits. Default: \code{FALSE}.
#' @param log.y.lin (Logical) Indicates whether _ln(y/y0)_ should be applied to the fluorescence data for _linear_ fits. Default: \code{FALSE}
#' @param log.y.spline (Logical) Indicates whether _ln(y/y0)_ should be applied to the fluorescence data for _spline_ fits. Default: \code{FALSE}
#' @param lin.h (Numeric) Manually define the size of the sliding window used in \code{\link{flFitLinear}}. If \code{NULL}, h is calculated for each samples based on the number of measurements in the fluorescence increase phase of the plot.
#' @param lin.R2 (Numeric) \ifelse{html}{\out{R<sup>2</sup>}}{\eqn{R^2}} threshold for \code{\link{flFitLinear}}.
#' @param lin.RSD (Numeric) Relative standard deviation (RSD) threshold for the calculated slope in \code{\link{flFitLinear}}.
#' @param lin.dY (Numeric) Threshold for the minimum fraction of growth increase a linear regression window should cover. Default: 0.05 (5%).
#' @param biphasic (Logical) Shall \code{\link{flFitLinear}} and \code{\link{flFitSpline}} try to extract fluorescence parameters for two different phases (as observed with, e.g., regulator-promoter systems with varying response in different growth stages) (\code{TRUE}) or not (\code{FALSE})?
#' @param interactive (Logical) Controls whether the fit for each sample and method is controlled manually by the user. If \code{TRUE}, each fit is visualized in the _Plots_ pane and the user can adjust fitting parameters and confirm the reliability of each fit per sample. Default: \code{TRUE}.
#' @param dr.parameter (Character or numeric) The response parameter in the output table to be used for creating a dose response curve. See \code{\link{fl.drFit}} for further details. Default: \code{"max_slope.spline"}, which represents the maximum slope of the spline fit Typical options include: \code{"max_slope.linfit"}, \code{"dY.linfit"}, \code{"max_slope.spline"}, and \code{"dY.spline"}.
#' @param dr.method (Character) Perform either a smooth spline fit on response parameter vs. concentration data (\code{"spline"}) or fit a biosensor response model (proposed by Meyer et al., 2019).
#' @param dr.have.atleast (Numeric) Minimum number of different values for the response parameter one should have for estimating a dose response curve. Note: All fit procedures require at least six unique values. Default: \code{6}.
#' @param smooth.dr (Numeric) Smoothing parameter used in the spline fit by smooth.spline during dose response curve estimation. Usually (not necessesary) in (0; 1]. See \code{\link{smooth.spline}} for further details. Default: \code{NULL}.
#' @param log.x.dr (Logical) Indicates whether \code{ln(x+1)} should be applied to the concentration data of the dose response curves. Default: \code{FALSE}.
#' @param log.y.dr (Logical) Indicates whether \code{ln(y+1)} should be applied to the response data of the dose response curves. Default: \code{FALSE}.
#' @param nboot.dr (Numeric) Defines the number of bootstrap samples for EC50 estimation. Use \code{nboot.dr = 0} to disable bootstrapping. Default: \code{0}.
#' @param nboot.fl (Numeric) Number of bootstrap samples used for nonparametric curve fitting with \code{\link{flBootSpline}}. Use \code{nboot.fl = 0} to disable the bootstrap. Default: \code{0}
#' @param smooth.fl (Numeric) Parameter describing the smoothness of the spline fit; usually (not necessary) within (0;1]. \code{smooth.gc=NULL} causes the program to query an optimal value via cross validation techniques. Especially for datasets with few data points the option \code{NULL} might cause a too small smoothing parameter. This can result a too tight fit that is susceptible to measurement errors (thus overestimating slopes) or produce an error in \code{\link{smooth.spline}} or lead to overfitting. The usage of a fixed value is recommended for reproducible results across samples. See \code{\link{smooth.spline}} for further details. Default: \code{0.55}
#' @param growth.thresh (Numeric) Define a threshold for growth. Only if any growth value in a sample is greater than \code{growth.thresh} (default: 1.5) times the start growth, further computations are performed. Else, a message is returned.
#' @param suppress.messages (Logical) Indicates whether messages (information about current fluorescence curve, EC50 values etc.) should be displayed (\code{FALSE}) or not (\code{TRUE}). This option is meant to speed up the high-throughput processing data. Note: warnings are still displayed. Default: \code{FALSE}.
#' @param neg.nan.act (Logical) Indicates whether the program should stop when negative fluorescence values or NA values appear (\code{TRUE}). Otherwise, the program removes these values silently (\code{FALSE}). Improper values may be caused by incorrect data or input errors. Default: \code{FALSE}.
#' @param clean.bootstrap (Logical) Determines if negative values which occur during bootstrap should be removed (\code{TRUE}) or kept (\code{FALSE}). Note: Infinite values are always removed. Default: \code{TRUE}.
#' @param report (Character or NULL) Create a PDF (\code{'pdf'}) and/or HTML (\code{'html'}) report after running all computations. Define \code{NULL} if no report should be created. Default: (\code{c('pdf', 'html')})
#' @param out.dir {Character or \code{NULL}} Define the name of a folder in which all result files (tables and reports) are stored. If \code{NULL}, the folder will be named with a combination of "FluorescenceResults_" and the current date and time.
#' @param out.nm {Character or \code{NULL}} Define the name of the report files. If \code{NULL}, the files will be named with a combination of "FluorescenceReport_" and the current date and time.
#' @param export.fig (Logical) Export all figures created in the report as separate PNG and PDF files (\code{TRUE}) or not (\code{FALSE}). Only effective if \code{report = TRUE}.
#' @param export.res (Logical) Create tab-separated TXT files containing calculated parameters and dose-response analysis results as well as an .RData file for the resulting `flFitRes` object.
#' @param parallelize Run linear fits and bootstrapping operations in parallel using all but one available processor cores
#' @param ... Further arguments passed to the shiny app.
#'
#' @return A \code{flFitRes} object that contains all computation results, compatible with various plotting functions of the QurvE package and with \code{\link{fl.report}}.
#' \item{time}{Raw time matrix passed to the function as \code{time} (if no \code{grofit} object is provided. Else, extracted from \code{grofit}).}
#' \item{data}{Raw data dataframe passed to the function as \code{grodata}.}
#' \item{flFit}{\code{flFit} object created with the call of \code{\link{flFit}} on fluorescence data.}
#' \item{drFit}{\code{drFit} or \code{drFitfl} object created with the call of \code{\link{growth.drFit}} or \code{\link{fl.drFit}} for fluorescence data (based on the \code{dr.method} argument in \code{control}; see \code{\link{fl.control}}).}
#' \item{expdesign}{Experimental design table inherited from \code{grodata} or created from the identifier columns (columns 1-3) in \code{data}.}
#' \item{control}{Object of class \code{fl.control} created with the call of \code{\link{fl.control}}.}
#'
#' @export
#'
#' @examples
#' # load example dataset
#' input <- read_data(data.growth = system.file("lac_promoters_growth.txt", package = "QurvE"),
#'                    data.fl = system.file("lac_promoters_fluorescence.txt", package = "QurvE"),
#'                    csvsep = "\t",
#'                    csvsep.fl = "\t")
#'
#' # Run workflow
#' res <- fl.workflow(grodata = input, ec50 = FALSE, fit.opt = "s",
#'                    x_type = "time", norm_fl = TRUE,
#'                    dr.parameter = "max_slope.spline",
#'                    suppress.messages = TRUE,
#'                    parallelize = FALSE)
#'
#' plot(res, data.type = "raw", legend.ncol = 3, basesize = 15)
#'
fl.workflow <- function(grodata = NULL,
                        time = NULL,
                        growth = NULL,
                        fl_data = NULL,
                        ec50 = TRUE,
                        mean.grp = NA,
                        mean.conc = NA,
                        fit.opt = c("l", "s"),
                        x_type = c("growth", "time"),
                        norm_fl = TRUE,
                        t0 = 0,
                        tmax = NA,
                        min.growth = 0,
                        max.growth = NA,
                        log.x.lin = FALSE,
                        log.x.spline = FALSE,
                        log.y.lin = FALSE,
                        log.y.spline = FALSE,
                        lin.h = NULL,
                        lin.R2 = 0.97,
                        lin.RSD = 0.07,
                        lin.dY = 0.05,
                        biphasic = FALSE,
                        interactive = FALSE,
                        dr.parameter = "max_slope.spline",
                        dr.method = c("model", "spline"),
                        dr.have.atleast = 5,
                        smooth.dr = NULL,
                        log.x.dr = FALSE,
                        log.y.dr = FALSE,
                        nboot.dr = 0,
                        nboot.fl = 0,
                        smooth.fl = 0.75,
                        growth.thresh = 1.5,
                        suppress.messages = FALSE,
                        neg.nan.act = FALSE,
                        clean.bootstrap = TRUE,
                        report = NULL,
                        out.dir = NULL,
                        out.nm = NULL,
                        export.fig = FALSE,
                        export.res = FALSE,
                        parallelize = TRUE,
                        ...)
{
  if(ec50 == TRUE){
    dr.parameter.fit.method <- gsub(".+\\.", "", dr.parameter)
    if((dr.parameter.fit.method == "spline" && !any(fit.opt %in% c("s"))) ||
       (dr.parameter.fit.method == "linfit" && !any(fit.opt %in% c("l")))
    )
      message("The chosen 'dr.parameter' is not compatible with the selected fitting options ('fit.opt'). Dose-response analysis will not be performed.")
  }
  if(exists("lin.h") && !is.null(lin.h) && (is.na(lin.h) || lin.h == "")) lin.h <- NULL

  if ( isTRUE(export.fig) && is.null(report) ){
    message(
      "The export of plots as separate files (`export.fig = TRUE`) is only valid if `report != NULL`."
    )
  }

  # Define objects based on additional function calls
  call <- match.call()

  ## remove strictly defined arguments
  call$grodata <- call$time <- call$growth <- call$fl_data <- call$ec50 <- call$mean.grp <- call$mean.conc <- call$neg.nan.act <- call$clean.bootstrap <- call$suppress.messages <- call$export.res <-
    call$fit.opt <- call$t0 <- call$min.growth <- call$log.x.lin <- call$log.x.spline <- call$log.y.spline <- call$log.y.lin <- call$biphasic <- call$norm_fl <- call$x_type <- call$tmax <- call$max.growth <-
    call$lin.h <- call$lin.R2 <- call$lin.RSD <- call$lin.dY <- call$interactive <- call$nboot.fl <- call$smooth.fl <- call$dr.method <- call$growth.thresh <- call$parallelize <-
    call$dr.have.atleast <- call$dr.parameter  <- call$smooth.dr  <- call$log.x.dr  <- call$log.y.dr <- call$nboot.dr <- call$report <- call$out.dir <- call$out.nm <- call$export.fig <- NULL


  arglist <- sapply(call, function(x) x)
  arglist <- unlist(arglist)[-1]
  ## Assign additional arguments (...) as R objects
  if(length(arglist) > 0){
    for(i in 1:length(arglist)){
      assign(names(arglist)[i], arglist[[i]])
    }
  }

  x_type <- match.arg(x_type)
  dr.method <- match.arg(dr.method)



  if(!is.null(grodata) && !(is(grodata)=="list") && !(is(grodata)=="grodata")){
    if (is.numeric(as.matrix(time)) == FALSE)
      stop("Need a numeric matrix for 'time' or a grodata object created with read_data() or parse_data().")
    if (is.numeric(as.matrix(fl_data[-1:-3])) == FALSE)
      stop("Need a numeric matrix for 'fl_data' or a grodata object created with read_data() or parse_data().")
    if (is.logical(ec50) == FALSE)
      stop("Need a logical value for 'ec50'")
  } else {
    if(!is.null(grodata$time)) time <- grodata$time
    if(!is.null(grodata$growth)) growth <- grodata$growth
    if(!is.null(grodata$expdesign)) expdesign <- grodata$expdesign
    if(!is.null(grodata$fluorescence)) fluorescence <- grodata$fluorescence
    # if(!is.null(grodata$fluorescence2)) fluorescence2 <- grodata$fluorescence2
    if(!is.null(grodata$norm.fluorescence)) norm.fluorescence <- grodata$norm.fluorescence
    # if(!is.null(grodata$norm.fluorescence2)) norm.fluorescence2 <- grodata$norm.fluorescence2

    if(!is.null(time)) time <- time
    if(!is.null(growth)) growth <- growth
    if(!is.null(fl_data)) fluorescence <- fl_data
  }
  control <- fl.control(fit.opt = fit.opt, norm_fl = norm_fl, x_type = x_type, t0 = t0, min.growth = min.growth, log.x.lin = log.x.lin,
                        log.x.spline = log.x.spline, log.y.lin = log.y.lin, log.y.spline = log.y.spline, tmax = tmax, max.growth = max.growth,
                        lin.h = lin.h, lin.R2 = lin.R2, lin.RSD = lin.RSD, lin.dY = lin.dY, dr.have.atleast = dr.have.atleast,
                        smooth.dr = smooth.dr, log.x.dr = log.x.dr, log.y.dr = log.y.dr, nboot.dr = nboot.dr,
                        biphasic = biphasic, interactive = interactive, nboot.fl = nboot.fl, dr.parameter = dr.parameter, dr.method = dr.method, clean.bootstrap = clean.bootstrap,
                        smooth.fl = smooth.fl, growth.thresh = growth.thresh, suppress.messages = suppress.messages, neg.nan.act = neg.nan.act)
  nboot.fl <- control$nboot.fl
  nboot.dr <- control$nboot.dr
  out.flFit <- NA
  out.drFit <- NA

  # /// fit of fluorescence curves -----------------------------------
  if(norm_fl == TRUE && x_type == "time" && (!is.null(norm.fluorescence) && length(norm.fluorescence) > 1 && !all(is.na(norm.fluorescence)))){
    if ((control$suppress.messages==FALSE)){
      cat("\n\n")
      cat(paste("=== Performing Fits for fluorescence =================================\n"))
      cat("----------------------------------------------------\n")
    }
    if(exists("shiny") && shiny == TRUE){
      out.flFit <- flFit(time = time, growth = growth, fl_data = norm.fluorescence, control = control, shiny = TRUE, parallelize = parallelize)
    } else {
      out.flFit <- flFit(time = time, growth = growth, fl_data = norm.fluorescence, control = control, shiny = FALSE, parallelize = parallelize)
    }

  } else if (!is.null(fluorescence) && length(fluorescence) > 1 && !all(is.na(fluorescence))){
    if(exists("shiny") && shiny == TRUE){
      out.flFit <- flFit(time = time, growth = growth, fl_data = fluorescence, control = control, shiny = TRUE, parallelize = parallelize)
    } else {
      out.flFit <- flFit(time = time, growth = growth, fl_data = fluorescence, control = control, shiny = FALSE, parallelize = parallelize)
    }
  }
  # if(norm_fl == TRUE && x_type == "time" && (!is.null(norm.fluorescence2) && length(norm.fluorescence2) > 1 && !all(is.na(norm.fluorescence2)))){
  #   if ((control$suppress.messages==FALSE)){
  #     cat("\n\n")
  #     cat(paste("=== Performing Fits for Fluorescence 2 =================================\n"))
  #     cat("----------------------------------------------------\n")
  #   }
  #   if(exists("shiny") && shiny == TRUE){
  #     out.flFit2 <- flFit(time = time, growth = growth, fl_data = norm.fluorescence2, control = control, shiny = TRUE)
  #   } else {
  #     out.flFit2 <- flFit(time = time, growth = growth, fl_data = norm.fluorescence2, control = control, shiny = FALSE)
  #   }
  # } else if (!is.null(fluorescence2) && length(fluorescence2) > 1 && !all(is.na(fluorescence2))){
  #   if ((control$suppress.messages==FALSE)){
  #     cat("\n\n")
  #     cat(paste("=== Performing Fits for Fluorescence 2 =================================\n"))
  #     cat("----------------------------------------------------\n")
  #   }
  #   if(exists("shiny") && shiny == TRUE){
  #     out.flFit2 <- flFit(time = time, growth = growth, fl_data = fluorescence2, control = control, shiny = TRUE)
  #   } else {
  #     out.flFit2 <- flFit(time = time, growth = growth, fl_data = fluorescence2, control = control, shiny = FALSE)
  #   }
  # }

  # /// Estimate EC50 values
  if (ec50 == TRUE &&
      !((dr.parameter.fit.method == "spline" && !any(fit.opt %in% c("s"))) ||
        (dr.parameter.fit.method == "linfit" && !any(fit.opt %in% c("l")))
      )
  ) {
    if (!is.null(fluorescence) && length(fluorescence) > 1 && !all(is.na(fluorescence))){
      if ((control$suppress.messages==FALSE)){
        cat("\n\n")
        cat(paste("=== Performing Dose-Response Analysis for  fluorescence =================================\n"))
        cat("----------------------------------------------------\n")
      }
      if(control$dr.method == "spline"){
        out.drFit <- growth.drFit(summary.flFit(out.flFit), control)
        boot.ec1 <- out.drFit$boot.ec
      } else {
        out.drFit <- fl.drFit(summary.flFit(out.flFit), control)
        boot.ec1 <- NA
      }
      EC50.table1 <- out.drFit$drTable
    }
    # if (!is.null(fluorescence2) && length(fluorescence2) > 1 && !all(is.na(fluorescence2))){
    #   if ((control$suppress.messages==FALSE)){
    #     cat("\n\n")
    #     cat(paste("=== Performing Dose-Response Analysis for  Fluorescence 2 =================================\n"))
    #     cat("----------------------------------------------------\n")
    #   }
    #   if(control$dr.method == "spline"){
    #     out.drFit2 <- growth.drFit(summary.flFit(out.flFit2), control)
    #     boot.ec2 <- out.drFit2$boot.ec
    #   } else {
    #     out.drFit2 <- fl.drFit(summary.flFit(out.flFit2), control)
    #     boot.ec2 <- NA
    #   }
    #   EC50.table2 <- out.drFit2$drTable
    # }
  }
  # ///
  na.obj <- NA
  flFitRes <- list(time = time, data = grodata, flFit = get(ifelse(exists("out.flFit"), "out.flFit", "na.obj")),
                   # flFit2 = get(ifelse(exists("out.flFit2"), "out.flFit2", "na.obj")),
                   drFit = get(ifelse(exists("out.drFit"), "out.drFit", "na.obj")),
                   # drFit2 = get(ifelse(exists("out.drFit2"), "out.drFit2", "na.obj")),
                   expdesign = expdesign, control = control)
  class(flFitRes) <- "flFitRes"

  if(!exists("shiny") || shiny != TRUE){
    if(!is.null(out.dir)){
      wd <- paste0(out.dir)
    } else {
      wd <- paste(getwd(), "/FluorescenceResults_", format(Sys.time(),
                                                           "%Y%m%d_%H%M%S"), sep = "")
    }
    if(export.res)
      dir.create(wd, showWarnings = FALSE)

    if (!is.null(fluorescence) && length(fluorescence) > 1 && !all(is.na(fluorescence))){
      flTable <- data.frame(apply(flFitRes[["flFit"]][["flTable"]],2,as.character))
      res.table.fl <- cbind(flTable[,1:3], Filter(function(x) !all(is.na(x)),flTable[,-(1:3)]))
      if(export.res){
        export_Table(table = res.table.fl, out.dir = wd, out.nm = "results.fl1")
        message(paste0("\nResults of fluorescence analysis saved as tab-delimited text file in:\n",
                       "...", gsub(".+/", "", wd), "/results.fl1.txt\n"))
      }
      # Export grouped results table
      if(("l" %in% control$fit.opt) || ("a"  %in% control$fit.opt) ){
        table_linear_group <- table_group_fluorescence_linear(res.table.fl)
        names <- gsub("<sub>", "_", gsub("</sub>|<sup>|</sup>", "", gsub("<br>", " ", colnames(table_linear_group))))
        table_linear_group <- as.data.frame(lapply(1:ncol(table_linear_group), function(x) gsub("<strong>", "", gsub("</strong>", "", table_linear_group[,x]))))
        colnames(table_linear_group) <- names
        if(export.res)
          export_Table(table = table_linear_group, out.dir = wd, out.nm = "grouped_results_fluorescence_linear")
      }

      if(("s" %in% control$fit.opt) || ("a"  %in% control$fit.opt) ){
        table_spline_group <- table_group_fluorescence_spline(res.table.fl)
        names <- gsub("<sub>", "_", gsub("</sub>|<sup>|</sup>", "", gsub("<br>", " ", colnames(table_spline_group))))
        table_spline_group <- as.data.frame(lapply(1:ncol(table_spline_group), function(x) gsub("<strong>", "", gsub("</strong>", "", table_spline_group[,x]))))
        colnames(table_spline_group) <- names
        if(export.res)
          export_Table(table = table_spline_group, out.dir = wd, out.nm = "grouped_results_fluorescence_spline")
      }
    }
    # if (!is.null(fluorescence2) && length(fluorescence2) > 1 && !all(is.na(fluorescence2))){
    #   flTable2 <- data.frame(apply(flFitRes[["flFit2"]][["flTable"]],2,as.character))
    #   res.table.fl2 <- cbind(flTable2[,1:3], Filter(function(x) !all(is.na(x)),flTable2[,-(1:3)]))
    #   if(export.res)
    #     export_Table(table = res.table.fl2, out.dir = wd, out.nm = "results.fl2")
    #   cat(paste0("Results of fluorescence 2 analysis saved as tab-delimited text file in:\n",
    #              wd, "/results.fl2.txt\n"))
    #   # Export grouped results table
    #   if(("l" %in% control$fit.opt) || ("a"  %in% control$fit.opt) ){
    #     table_linear_group <- table_group_fluorescence_linear(res.table.fl2)
    #     names <- gsub("<sub>", "_", gsub("</sub>|<sup>|</sup>", "", gsub("<br>", " ", colnames(table_linear_group))))
    #     table_linear_group <- as.data.frame(lapply(1:ncol(table_linear_group), function(x) gsub("<strong>", "", gsub("</strong>", "", table_linear_group[,x]))))
    #     colnames(table_linear_group) <- names
    #     if(export.res)
    #       export_Table(table = table_linear_group, out.dir = wd, out.nm = "grouped_results_fluorescence2_linear")
    #   }
    #
    #   if(("s" %in% control$fit.opt) || ("a"  %in% control$fit.opt) ){
    #     table_spline_group <- table_group_fluorescence_spline(res.table.fl2)
    #     names <- gsub("<sub>", "_", gsub("</sub>|<sup>|</sup>", "", gsub("<br>", " ", colnames(table_spline_group))))
    #     table_spline_group <- as.data.frame(lapply(1:ncol(table_spline_group), function(x) gsub("<strong>", "", gsub("</strong>", "", table_spline_group[,x]))))
    #     colnames(table_spline_group) <- names
    #     if(export.res)
    #       export_Table(table = table_spline_group, out.dir = wd, out.nm = "grouped_results_fluorescence2_spline")
    #   }
    # }

    if (ec50 == TRUE &&
        !((dr.parameter.fit.method == "spline" && !any(fit.opt %in% c("s"))) ||
          (dr.parameter.fit.method == "linfit" && !any(fit.opt %in% c("l")))
        )
    ) {
      if (!is.null(fluorescence) && length(fluorescence) > 1 && !all(is.na(fluorescence))){
        if(!is.null(EC50.table1) && length(EC50.table1) > 1) {
          res.table.dr_fl1 <- Filter(function(x) !all(is.na(x)),EC50.table1)
          if(export.res){
            export_Table(table = res.table.dr_fl1, out.dir = wd, out.nm = "results.fl_dr1")
            message(paste0("\nResults of EC50 analysis for fluorescence saved as tab-delimited in:\n",
                           "...", gsub(".+/", "", wd), "/results.fl_dr1.txt\n"))
          }
        }
      }
      # if (!is.null(fluorescence2) && length(fluorescence2) > 1 && !all(is.na(fluorescence2))){
      #   if(!is.null(EC50.table2) && length(EC50.table2) > 1) {
      #     res.table.dr_fl2 <- Filter(function(x) !all(is.na(x)),EC50.table2)
      #     if(export.res)
      #       export_Table(table = res.table.dr_fl2, out.dir = wd, out.nm = "results.fl_dr2")
      #
      #     cat(paste0("Results of EC50 analysis for fluorescence 2 saved as tab-delimited in:\n",
      #                wd, "/results.fl_dr2.txt\n"))
      #   }
      # }


    } else {
      res.table.dr_fl1 <- NULL
      res.table.dr_fl2 <- NULL
    }
    # Export RData object
    if(export.res)
      export_RData(flFitRes, out.dir = wd)

    if(any(report %in% c('pdf', 'html'))){
      try(fl.report(flFitRes, out.dir = gsub(paste0(getwd(), "/"), "", wd), mean.grp = mean.grp, mean.conc = mean.conc, ec50 = ec50,
                    export = export.fig, format = report, out.nm = out.nm, parallelize = parallelize))
    }
  }

  invisible(flFitRes)
}

Try the QurvE package in your browser

Any scripts or data that you put into this service are public.

QurvE documentation built on May 29, 2024, 3 a.m.