R/plt.main.R

Defines functions .plt.main

.plt.main <-
function(x, y, by=NULL,  # x and y dfs with vars x.call and y.call 
         cat.x=FALSE, cat.y=FALSE,
         object="point", stat="data",

         fill=getOption("pt_fill"),
         area_fill="transparent",
         color=getOption("pt_color"),

         pts_trans=0, col.segment=getOption("segment_color"),

         xy_tics=TRUE,
         xlab=NULL, ylab=NULL, main=NULL, main_cex=NULL, sub=NULL,
         rotate_x=0, rotate_y=0, offset=0.5, prop=FALSE,
         origin_x=NULL, origin_y=NULL,

         size=NULL, ln.width=1, shape=21, means=TRUE,
         segments=FALSE, segments_y=FALSE, segments_x=FALSE,

         type="regular", smooth_points=100, smooth_size=1,
         smooth_exp=0.25, smooth_bins=128,
         contour_n=8, contour_nbins=50,

         radius=0.15, power=0.6, size_cut=TRUE,
         bbl.txt.col=getOption("bubble_text_color"),
         col.low=NULL, col.hi=NULL,

         ID=NULL, ID_color="gray50", ID_size=0.60, out_ind=NULL,
         out_fill, out_color, out_shape, out_shape.miss,

         fit.line="off", fit_power=1, fit_color="gray55",
         fit_lwd=getOption("fit.lw"), fit_new=NULL,
         fit_se=1, se_fill="gray80", plot_errors=FALSE,

         ellipse=FALSE, ellipse_color="lightslategray",
         ellipse_fill="off", ellipse_lwd,

         run=FALSE, center_line="off", stack=FALSE,
         ts_unit=NULL, ts_agg=FALSE, do.agg="sum", ts_ahead=0,
         ts_fit=FALSE, n_date_tics=NULL,
         y.fit, y.hat, x.fit, x.hat, y.upr, y.lwr,
         mxf.x, mnf.y, mxf.y,

         freq.poly=FALSE, jitter_x=NULL, jitter_y=NULL,

         xlab.adj=0, ylab.adj=0, bm.adj=0, lm.adj=0,
         tm.adj=0, rm.adj=0,
         scale_x=NULL, scale_y=NULL, pad_x=c(0,0), pad_y=c(0,0),
         axis_fmt="K", axis_x_pre="", axis_y_pre="",
         legend_title=NULL,

         add=NULL, x1=NULL, x2=NULL, y1=NULL, y2=NULL,
         add_cex=NULL, add_lwd=1, add_lty="solid",
         add_color=NULL, add_fill=NULL, add_trans=NULL,

         quiet=FALSE, want.labels=TRUE, bubble.title=TRUE, ...)  {


# preliminaries -----------------------------------------------------------

  fill_bg <- getOption("panel_fill")
  date.var <- ifelse (.is.date(x[,1]), TRUE, FALSE)
  if (is.null(size)) size <- 1
  size.pt <- size * .9  # size is set in Plot.R, reduce a bit for here
  theme <- getOption("theme")

  # by.bub means that size is a variable to be plotted in by levels
  if (length(size) > 1  &&  !is.null(by)) {
    by.bub <- TRUE
    object <- "point"  # change from bubble to plot to get to by code
  }
  else
    by.bub <- FALSE


# data structures dimensions ----------------------------------------------

  # x and y come across here in their natural state, within each data frame
  # a time series has dates for x and numeric for y, factors are factors, etc

  # want labels set just for ttestPower, which provides its own labels
  # both x and y are plotted, even if only a single variable
  # for a 1-D bubble plot of a single factor var, y was set to 0's
  bubble1 <- ifelse (length(unique(y[,1])) == 1, TRUE, FALSE)

  unique.x <- ifelse (length(unique(x[,1])) == length(x[,1]), TRUE, FALSE)
  unique.y <- ifelse (length(unique(y[,1])) == length(y[,1]), TRUE, FALSE)
  n.by <- ifelse (is.null(by), 0, nlevels(by))

  do.ellipse <- ifelse (ellipse[1] > 0, TRUE, FALSE)

  # dimensions
  n.xcol <- ncol(x)
  n.ycol <- ncol(y)
  n.col <- max(n.xcol, n.ycol)
  nrows <- nrow(x)


# axis labels and title ---------------------------------------------------

  # size is a variable, these values passed to .plt.main
  # bubble.title=FALSE passed from .plt.bins to suppress title
  # bubble plot can be directly from object="point"
  if (length(size) > 1  &&  bubble.title) {
    sz.nm <- getOption("sizename")
    main.lab <- bquote(paste(italic(.(sz.nm)), ": Bubble size from ",
      .(min(size)), " to ", .(max(size)), sep=""))
  }
  else
    main.lab <- NULL

  # get lab_x_cex  lab_y_cex    Synchronize with Plot() code, put in own sub
  lab_cex <- getOption("lab_cex")
  lab_x_cex <- getOption("lab_x_cex")
  lab_y_cex <- getOption("lab_y_cex")
  lab_x_cex <- ifelse (is.null(lab_x_cex), lab_cex, lab_x_cex)
  lab_y_cex <- ifelse (is.null(lab_y_cex), lab_cex, lab_y_cex)

  # get x.lab and y.lab
  if (date.var) xx.lab <- xlab
  if (want.labels) {
    gl <- .getlabels(xlab, ylab, main, lab_x_cex=lab_x_cex,
                     lab_y_cex=lab_y_cex)
    x.name <- gl$xn; x.lbl <- gl$xl; x.lab <- gl$xb
    y.name <- gl$yn; y.lab <- gl$yb

    if (is.null(main.lab)) main.lab <- gl$mb
    sub.lab <- gl$sb
    by.name <- getOption("byname")
  }
  else {
    x.lab <- xlab
    y.lab <- ylab
    if (is.null(main.lab)) main.lab <- main
    sub.lab <- sub
    x.name <- NULL
  }
  if (!is.null(x.lab)) if (n.xcol > 1  &&  substr(x.lab, 1, 2) == "c(")
      x.lab <- NULL  # e.g., get rid of == "c(Female, Male)"
  if (date.var && is.null(xx.lab)) x.lab <- ""

  # set title for bubble plot if proportions
  if (object == "bubble"  &&  prop  &&  is.null(main)  &&  cat.y) {
    main.lab <- paste("Percentage of", y.name, "\nwithin each level of", x.name)
    main <- "not null"
  }

  if (!is.null(x.name)) if (x.name == "Index") {
    if (n.ycol > 1) y.lab <- ""  # labels are on the legend
    if (!is.null(x.lbl)) y.lab <- paste(x.name, ": ", x.lbl, sep="")
  }

  
# all processing in terms of numeric variables ----------------------------

  # categorical variables x and/or y are converted to factors in Plot()
  # convert factors to numeric, save levels, so x and y are always numeric
  # x will always be a matrix
  x.lvl <- NULL; y.lvl <- NULL  # if remain null, then not factors
  y.frcst <- NULL
  nm.x <- names(x)
  if (cat.x) {
    x.lvl <- levels(x[,1])
    x <- as.matrix(as.integer(unclass(x[,1])))
  }
  else if (!date.var) {
    x <- as.matrix(x)
    colnames(x) <- nm.x

   }
  else {  # date.var
    if (n.by == 0)
      x.dates <- x[,1]  # save actual dates for later
    else {  # x-axis tics just for one level of by
      cnt <- sum(by == levels(by)[1])
      x.dates <- x[1:cnt,1]  # assumes by is sorted by level
    }
  if (ts_ahead > 0) x.dates <- c(x.dates, x.hat[,1])

   x <- as.matrix(x[,1], ncol=1)  # x to numeric version of dates
  }  # end if date.var

  # y to a matrix with 3 possibilities for y: cat.y, not date.var, date.var
  nm.y <- names(y)
  if (cat.y) {
    y.lvl <- levels(y[,1])  #  put into alphabetical order
    y <- as.matrix(as.integer(unclass(y[,1])))
  }
  else if (!date.var) {
    nm.y <- names(y)
    y <- as.matrix(y)
    colnames(y) <- nm.y
  }
  else {  # data.var
    y <- as.matrix(y)
  }

  # y decimal digits
  digits_d <- .max.dd(y[,1]) + 1
  options(digits_d=digits_d)

  if (n.col > 1) center_line <- "off"   # no center_line for multiple plots

  cleveland <- FALSE
  if (!cat.x && cat.y && unique.y  ||  !cat.y && cat.x && unique.x)
    cleveland <- TRUE


# get minimum and maximum values of x and y -------------------------------

  mx.x.val.ln <- 1
  mx.y.val.ln <- 1
  if (!date.var) {
    x.val <- x.lvl  # x.val is NULL if x is numeric, ignored
    y.val <- y.lvl  # y.val ignored if y is numeric
  }
  else {  # date.var
    x.val <- x[,1]
    y.val <- NULL
  }

  mn.x <- ifelse (cat.x, 1, min(x, na.rm=TRUE))
  if (ts_ahead > 0) {  # forecast margin adjustments
    mx.x <- mxf.x
    mx.y <- mxf.y
    mn.y <- mnf.y
  }
  else {
    mx.x <- ifelse (cat.x, length(x.lvl), max(x, na.rm=TRUE))
    mn.y <- ifelse (cat.y, 1, min(y, na.rm=TRUE))
    mx.y <- ifelse (cat.y, length(y.lvl), max(y, na.rm=TRUE))
  }


# set margins -------------------------------------------------------------

  # get max number of lines in x value labels
  if (cat.x) {
    stuff <- .get.val.ln(x.lvl, x.name)
    mx.x.val.ln <- stuff$mx.val.ln
  }

  # get max number of lines in y value labels
  if (cat.y  &&  y.name != "row.names"  && !cleveland) {
    stuff <- .get.val.ln(y.val, y.name)
    mx.y.val.ln <- stuff$mx.val.ln
  }

  # --------- get size of y-axis labels
  axis_y_cex <- ifelse (is.null(getOption("axis_y_cex")),
    getOption("axis_cex"), getOption("axis_y_cex"))

  if (cat.y) {  # y-axis labels are characters
    yv <- unlist(strsplit(y.val, "\n", fixed=TRUE))
    max.y.width <- max(strwidth(yv, units="inches", cex=axis_y_cex))
    if (options("device") != "RStudioGD")  # not work in R, only RStudio
      max.y.width <- .09 * axis_y_cex * max(nchar(yv))
  }
  else {  # y-axis labels are numeric
    prety <- pretty(c(mn.y, mx.y))
    ind <- which(nchar(prety) == max(nchar(prety)))[1]  # get largest nchar
    mx.num <-  ifelse (!prop, as.character(prety[ind]), .fmt(prety, 2))
    max.y.width <- max(strwidth(mx.num, cex=axis_y_cex, units="inches"))
  }
  # --------

  margs <- .plt.marg(max.y.width, y.lab, x.lab, main.lab, sub.lab,
                rotate_x, mx.x.val.ln, mx.y.val.ln,
                lab_x_cex=lab_x_cex, lab_y_cex=lab_y_cex, 
                axis_y_pre=axis_y_pre)
  mm <- margs$lm  # left margin, lm is linear model
  tm <- margs$tm
  rm <- margs$rm
  bm <- margs$bm
  n.lab_x.ln <- margs$n.lab_x.ln
  n.lab_y.ln <- margs$n.lab_y.ln

  # room in right margin for the vertical legend
  if (n.xcol > 1  ||  n.ycol > 1  ||  !is.null(by)) {
    if (n.xcol > 1) nm.v <- nm.x
    if (n.ycol > 1) nm.v <- nm.y
    if (!is.null(by)) nm.v <- by.name
    big.nm <- max(nchar(nm.v))
    if (big.nm > 6) rm <- rm + (.05 * (big.nm - 6))
    if (n.ycol == 1) rm <- rm + .30  + (.65 * axis_y_cex)
    if (axis_y_cex > 1) if (!is.null(by)) rm <- rm + .1  # kludge
  }

  if (ts_ahead > 0) rm <- rm + 0.75  # make room for vertical legend

  if (center_line != "off") rm <- rm + .4  # room for center_line label
  rm <- rm + 0.10  # make room when the last axis date > last data value
  if ((options("device") != "RStudioGD")  &&  !is.null(by)) rm <- rm + .3

  if (date.var && n.by>1) rm <- rm - 0.05

  if (n.ycol>1 && date.var) {  # if not a time series, then no y label
    mm <- mm + 0.25  # reduce size of plot in the left to make room for label
    ylab.adj <- ylab.adj + 0.2  # move label right when calling .axlabs()
  }
  if (n.ycol > 1)
    rm <- rm + 0.80  # add right margin for legend

  if (offset > 0.5) bm <- bm + (-0.05 + 0.2 * offset)  # offset kludge

  if (n.xcol>1  && !date.var) {
    bm <- bm + 0.20  # allow for the bottom legend
    rm <- rm - 0.60  # remove superfluous right margin
  }

  if (type == "contour") {
    bm <- bm + .2
    mm <- mm + .2
  }

  # user manual adjustment
  bm <- bm + bm.adj
  mm <- mm + lm.adj
  tm <- tm + tm.adj
  rm <- rm + rm.adj

  orig.params <- par(no.readonly=TRUE)
  on.exit(par(orig.params))
  par(bg=getOption("window_fill"))
  par(mai=c(bm, mm, tm, rm))
  par(tcl=-0.28)  # axis tic length


# set-up region for coordinate system -------------------------------------

  # -----------------------
  # only with plot and type="n"
  # non-graphical parameters in ... Generate warnings when no plot

  # re-calibrate maximum of y-axis if stacking
  if (stack  &&  n.ycol > 1) {  # data in wide format, one col for each y
    y.tot <- apply(y, 1, sum, na.rm=TRUE)
    mx.y <- max(y.tot)
  }
  if (stack  &&  (n.ycol == 1 &&  n.by > 1)) {  # data in tidy format
    for (i in 1:n.by) {
      if (i > 1) yo <- yl
      yl <- subset(y, by==levels(by)[i])
      if (i > 1) yl[,1] <- yl[,1] + yo[,1]
    }
    mx.y <- max(yl)
  }

  # get min and max of x and y
  if (!is.null(scale_x)) {
     if (length(scale_x) == 2) scale_x[3] <- par("xaxp")[3]
     mn.x <- min(mn.x, scale_x[1])
     mx.x <- max(mx.x, scale_x[2])
  }
  if (!is.null(scale_y)) {
     if (length(scale_y) == 2) scale_y[3] <- par("yaxp")[3]
     mn.y <- min(mn.y, scale_y[1])
     mx.y <- max(mx.y, scale_y[2])
  }

  # adjust margins for fit line, will be computing fit lines twice
  # more efficient to do all computations here, then plot fit line later
  # if multiple x or y variables, just first variable is checked
  if (fit.line!="off" && is.null(scale_y) && !cat.y && !date.var) {
    pf <- .plt.fit(x[,1], y[,1], fit.line, fit_power, fit_new)  # fit line
    mn.yf <- min(pf$f.ln, na.rm=TRUE)  # f.ln are the fitted values
    mx.yf <- max(pf$f.ln, na.rm=TRUE)
    mn.y <- min(mn.y, mn.yf)
    mx.y <- max(mx.y, mx.yf)
  }

  if (!do.ellipse) {
    if (cat.x) {
      mn.x <- mn.x - .2
      mx.x <- mx.x + .2
    }
    if (cat.y) {
      mn.y <- mn.y - .2
      mx.y <- mx.y + .2
    }

    if (is.null(origin_x)) {  # set default for minimum x-value displayed
        origin_x <- mn.x
    }
    if (stat != "data" && (!all(y == 0))) mx.y <- mx.y + (.08 * (mx.y-mn.y))
    region <- matrix(c(origin_x, mx.x, mn.y, mx.y), nrow=2, ncol=2)
  }  # end no ellipse

  else {  # set plot with sufficient room for ellipse and data
    cxy <- cor(x[,1],y[,1], use="complete.obs")
    s.x <- sd(x[,1], na.rm=TRUE); s.y <- sd(y[,1], na.rm=TRUE)
    m.x <- mean(x[,1], na.rm=TRUE); m.y <- mean(y[,1], na.rm=TRUE)
    lvl <- max(ellipse)
    region <- ellipse(cxy, scale=c(s.x, s.y), centre=c(m.x, m.y), level=lvl)
    region <- rbind(region, c(mn.x, mn.y), c(mx.x, mx.y))
  }

  # revise min and max for x and y axes considering the ellipse
  mx.x <- max(region[,1])
  mn.x <- min(region[,1])
  mx.y <- max(region[,2])
  mn.y <- min(region[,2])
  # set y-axis origin for numeric variables
  if (!cat.y) {
    if (!is.null(origin_y)) {
      if (all(y, na.rm=TRUE) > 0) mn.y <- origin_y
      if (all(y, na.rm=TRUE) < 0) mx.y <- origin_y
    }
  }

  # add padding to x and y axes
  add.pad_lab <- FALSE
  if (all(pad_x == 0)) {  # pad extra for labels in 2-D plot
    if (!is.null(add))
      add.pad_lab <- ifelse ("labels" %in% add, TRUE, FALSE)
    if (add.pad_lab || (length(out_ind) > 0)) {
      if (all(pad_x == 0)) pad_x <- c(0.05, 0.05)
      if (all(pad_y == 0)) pad_y <- c(0.03, 0.03)
    }
  }

  # get plot region
  xp <- pretty(c(mn.x, mx.x))
  xP <- pad_x[2] * (xp[length(xp)] - xp[1])
  yp <- pretty(c(mn.y, mx.y))
  xN <- pad_x[1] * (xp[length(xp)] - xp[1])
  yP <- pad_y[2] * (yp[length(yp)] - yp[1])
  yN <- pad_y[1] * (yp[length(yp)] - yp[1])
  region <- rbind(region, c(mn.x-xN, mn.y-yN), c(mx.x+xP, mx.y+yP))


# set-up the coordinate system --------------------------------------------

  if (type != "contour")  # filled.contour does its own plot window
    plot(region, type="n", axes=FALSE, ann=FALSE, ...)
  rm(region)
  usr <- par("usr")

  
# axes ticks and labels axT1 and axT2--------------------------------------

  # set axT1: x-axis tics and values, Date var uses x.zoo.dates, defined later
  if (cat.x)
    axT1 <- seq_along(x.lvl)   # mark category values
  else {  # numerical
    if (!date.var) {
      if (stat == "count") {
        if (mx.x <= 3)  # just want integers for count axis
          n.best <- 3
        else if (mx.x > 3  &&  mx.x < 100)
          n.best <- 4
        else
          n.best <- 5
        axT1 <- pretty(origin_x:mx.x, n=n.best)
      }
      else {  # stat != "count"
        if (is.null(scale_x))
          axT1 <- pretty(c(origin_x, x), eps.correct=0)  # numeric, so all tics
        else {
          if (!run)
            axT1 <- axTicks(1, axp=scale_x)
          else
            axT1 <- seq(scale_x[1], scale_x[2], by=scale_x[3])
        }
      }  # stat not "count"
    }  # end !date.var
  }  # end numerical

  # set axT2: y-axis tics and values
  if (cat.y)
    axT2 <- seq_along(y.lvl)
  else {
    if (is.null(scale_y))
      axT2 <- pretty(c(mn.y, mx.y))  # more extreme values than axTicks
    else
      axT2 <- axTicks(2, axp=scale_y)
  }

  if (bubble1) {  # 1-D scatter plot of categorical variable
    axT2 <- NULL
    y.val <- NULL
    y.lab <- ""
  }

  # axes value labels
  if (xy_tics) {

    # adjust axis label from tick with mgp[2]
    # mgp does not work with rotate_x, see .axes()
    ax <- .axes_dim()  # get axis value parameters
    mgp2 <- -0.350 + (0.9 * ax$axis_x_cex)

    if (type != "contour") {
      my.mgp <- par("mgp")  # save to restore
      par(mgp = c(my.mgp[1], mgp2, my.mgp[3]))  # labels closer to axis
      adj <- .RSadj(axis_cex=ax$axis_x_cex); axis_x_cex <- adj$axis_cex

      if (!date.var) {  # call axis() for both axes
        .axes(x.lvl, y.lvl, axT1, axT2,
              rotate_x=rotate_x, rotate_y=rotate_y, offset=offset, 
              axis_fmt=axis_fmt, axis_x_pre=axis_x_pre, axis_y_pre=axis_y_pre,
              ...)
      }
    }  # end not contour

    if (date.var) {  # date.var, y-axis
      .axes(NULL, y.val, NULL, axT2, rotate_x=rotate_x, rotate_y=rotate_y,
            offset=offset, y.only=TRUE,  # y-axis
            axis_fmt=axis_fmt, axis_x_pre="no", axis_y_pre=axis_y_pre, ...)

      # date.var, x-axis
      len.xtics <- length(x.dates)
      indices <- seq_along(x.dates)  # retain if num of data pts < n.tics
      step_range <- integer(length(50))

      if (ts_unit == "years") {
        x.zoo.dates <- format(x.dates, "%Y")
        n.tics <- ifelse (is.null(n_date_tics), 10, n_date_tics)
      }
      else if (ts_unit == "quarters") {
        x.zoo.dates <- as.character(zoo::as.yearqtr(x.dates))
        n.tics <- ifelse (is.null(n_date_tics), 9, n_date_tics)
        if (len.xtics >= 4*n.tics) {
          for (i in 1:50) step_range[i] <- 4*i
          tics_range <- ceiling(len.xtics / step_range)
          step <- step_range[which.min(abs(tics_range-n.tics))]
          indices <- seq(1, len.xtics, by=step)
        }
      }
      else if (ts_unit == "months") {
        x.zoo.dates <- as.character(zoo::as.yearmon(x.dates))
        n.tics <- ifelse (is.null(n_date_tics), 7, n_date_tics)
        if (len.xtics >= 12*n.tics) {
          for (i in 1:50) step_range[i] <- 12*i
          tics_range <- ceiling(len.xtics / step_range)
          step <- step_range[which.min(abs(tics_range-n.tics))]
          indices <- seq(1, length(x.dates), by=step)
        }
      }
      else {  # e.g., days
         x.zoo.dates <- as.character(x.dates)
         n.tics <- ifelse (is.null(n_date_tics), 7, n_date_tics)
      }

      # subset the indices to display if needed
      if (len.xtics >= n.tics) {
        step <- ceiling(len.xtics / n.tics)
        indices <- seq(1, len.xtics, by=step)
      }

      # x.zoo.dates is the xts customized for char ts_unit date, Aug 2023
      # x.dates are the dates in Date format, e.g., 2023-08-01
      axis(1, x.zoo.dates, at=x.dates[indices], labels=x.zoo.dates[indices],
                col=ax$axis_x_color, cex.axis=axis_x_cex,
                col.axis=ax$axis_x_text_color, ...)  # x-axis
    }  # end date.var

    if (type != "contour") par(mgp = my.mgp)  # restore back to previous value
  }  # end xy_tics

  if (date.var) {  # create y.lab
    if (ts_unit != "unknown") {
      tu <- gsub("s$", "", ts_unit)
      if (tu == "days7") tu <- "days"
      substr(tu, 1, 1) <- toupper(substr(tu, 1, 1))
      if (do.agg) {
        if (ts_agg == "sum") show.agg <- "Total"
        if (ts_agg == "mean") show.agg <- "Mean"
      }
      else
        show.agg <- ""
      ynm <- ifelse (n.ycol==1, y.name, "")  # get rid of c("Sales", "Profit")
      y.lab <- paste(show.agg, ynm, "by", tu)
    }
  }  # end date.var

  # axes labels
  if (type != "contour")
    .axlabs(x.lab, y.lab, main.lab, sub.lab,
           x.val, xy_tics, offset=offset,
           lab_x_cex=lab_x_cex, lab_y_cex=lab_y_cex, main_cex,
           n.lab_x.ln, n.lab_y.ln, xlab.adj, ylab.adj, ...)


# set-up plot background --------------------------------------------------

  if (date.var)
    .plt.bck(usr, x.dates[indices], axT2, do.h=!bubble1)
  else {
    if (type != "contour")
      .plt.bck(usr, axT1, axT2, do.h=!bubble1)
  }


# set jitter for scatterplot with discrete levels -------------------------

#     do.jit <- FALSE
#     if (nrows < 100) {
#       if (max(table(x,y) > 1)) do.jit <- TRUE  # duplication
#     }

  if (object == "point") {
    # multiple sizes if n_bin>0
    if (size[1]>0 && n.xcol==1 && stat=="data") {
      if (is.null(jitter_x))  {  # ifelse() does not work
        if (length(unique(x[,1])) <= 14  &&  nrows > 14)
          jitter_x <- (diff(range(x[,1], na.rm=TRUE))) / 32
      }
      if (is.null(jitter_y))  {  # ifelse() does not work
        if (length(unique(y[,1])) <= 14  &&  nrows > 14)
          jitter_y <- (diff(range(y[,1], na.rm=TRUE))) / 32
      }
    }

    # for VBS need to set jitter, here only set if jitter is NULL
    if (is.null(jitter_x)) jitter_x <- 0
    if (is.null(jitter_y)) jitter_y <- 0

    # --- process jitter ---
    if (jitter_x > 0) {
      x.temp <- x[,1]
      x[,1] <- x + runif(length(x[,1]), -jitter_x, jitter_x)
    }
    if (jitter_y > 0) {
      y.temp <- y[,1]
      y[,1] <- y + runif(length(y[,1]), -jitter_y, jitter_y)
    }
  }


# plot points, lines (and area_fill) --------------------------------------

  # colors
  n.clrs <- max(n.col, n.by)

  ltype <- character(length=n.clrs)
  for (i in seq_along(ltype)) ltype[i] <- "solid"


  if (object == "point") {  # is point even if size=0, plotting just lines

    if (n.xcol == 1  &&  n.ycol == 1) {
      if (n.by <= 1) {  # pure single panel, data in wide format

        if (segments && ln.width>0)  # plot data line segments
            lines(x[,1], y[,1], col=color[1], lwd=ln.width, ...)
        if (area_fill[1] != "transparent") # fill area
          polygon(c(x[1],x,x[length(x)]), c(mn.y,y[,1],mn.y),
              col=area_fill, border="transparent")

        if (ts_ahead > 0) {  # forecast
          trns.red <- .maketrans("darkred", 0.25*256)
          lines(x.fit[,1], y.fit[,1], col=trns.red, lwd=ln.width)
          lines(c(x.fit[nrow(x.fit),1], x.hat[1,1]),  # connect fit with hat
                c(y.fit[nrow(y.fit),1], y.hat[1,1]),
                col=trns.red, lwd=ln.width)
          f.size.pt <- ifelse (size.pt<0.5, .65, size.pt)
          points(x.hat[,1], y.hat[,1], pch=shape, bg="darkred", cex=f.size.pt)
          lines(x.hat[,1], y.hat[,1], col="darkred", lwd=ln.width)
          lines(x.hat[,1], y.upr[,1], col=trns.red, lwd=ln.width)
          lines(x.hat[,1], y.lwr[,1], col=trns.red, lwd=ln.width)
          xx <- c(x.hat[,1], rev(x.hat[,1]))
          yy <- c(y.upr[,1], rev(y.lwr[,1]))
          polygon(xx, yy, border=NA, col=rgb(.6,0,0,.15))

          lgn.clr <- c("black", trns.red, "darkred")
          lgn.nm <- ifelse (nchar(y.name) < 6, y.name, "data")
          .plt.by.legend(c(lgn.nm, "model\nfit", "fore-\ncast"),
                         lgn.clr, lgn.clr, shp="lines", pts_trans, fill_bg,
                         usr, pt.size=size, legend_title=legend_title)
        }  # end forecast
      }  # n.by is 1

      else {  # n.by > 1,  tidy format, all y data in same column
        for (i in 1:n.by) {  # only stack=TRUE makes sense
          xl <- x[by==levels(by)[i], , drop=FALSE]
          if (i > 1) yo <- yl
          yl <- y[by==levels(by)[i], , drop=FALSE]

          if (stack) {
            if (i == 1) {
              xx <- c(xl[1],xl,xl[length(xl)])
              yy <- c(min(yl[,1]),yl[,1],min(yl[,1]))
              if (fill[1] != "transparent")
                polygon(xx, yy, col=area_fill[1], border="transparent")
            }
            if (i > 1) {
              yl[,1] <- yl[,1] + yo[,1]
              xx <- c(c(xl[1],xl,xl[length(xl)]),
                    rev(c(xl[1],xl,xl[length(xl)])))
              yy <- c(c(min(yl[,1]),yl[,1],min(yl[,1])),
                         rev(c(min(yo[,1]),yo[,1],min(yo[,1]))))
              if (fill[1] != "transparent")
               polygon(xx, yy, col=area_fill[i], border="transparent")
            }
          }  # end stack

          if (segments && ln.width>0) {
            if (stack) ln.width <- 1  # no user option
            lines(xl, yl[,1], col=color[i], lty=ltype[i], lwd=ln.width, ...)
          }  # end ln.width > 0
        }
      }  # end n.by > 1
    }  # end n.xcol and n.ycol = 1

    if (n.ycol > 1) {
      for (i in 1:n.ycol) {
        if (stack) {  # polygons
          if (i == 1) { 
            xx <- c(x[1],x,x[length(x)])
            yy <- c(mn.y,y[,1],mn.y)  # starts at the scale_y setting
            if (fill[1] != "transparent")
              polygon(xx, yy, col=area_fill[1], border="transparent")
          }
          if (i > 1) {
            y[,i] <- apply(y[,(i-1):i], 1, sum, na.rm=TRUE)  # sum to stack
            xx <- c(c(x[1],x,x[length(x)]), rev(c(x[1],x,x[length(x)])))
            yy <- c(c(min(y[,i]),y[,i],min(y[,i])),
                       rev(c(min(y[,i-1]),y[,i-1],min(y[,i-1]))))
            if (fill[1] != "transparent")
              polygon(xx, yy, col=area_fill[i], border="transparent")
          }
        }  # end stack polygons

        if (segments && ln.width>0) {  # lines
          lines(x[,1], y[,i], col=color[i], lty=ltype[i], lwd=ln.width, ...)
        }  # end ln.width > 0

      }  # end i loop
    }  # end n.ycol > 1

    if (n.xcol > 1) {
      if (segments && ln.width>0) for (i in 1:n.xcol)
        lines(as.numeric(x.val),y[,1], col=fill[i], lwd=ln.width, ...)
    }


    if (is.null(by)) {


# smooth or contour plot --------------------------------------------------

      if (type == "smooth") {  # 2-D kernel density plot
        # grid lines only plot after the sp
        clr.den <- colorRampPalette(c(getOption("window_fill"),
                                       getOption("bar_fill_cont")))
        if (!requireNamespace("KernSmooth", quietly=TRUE)) {
          stop("Package \"KernSmooth\" needed for this visualization\n",
               "Please install it:  install.packages(\"KernSmooth\")\n\n",
               call. = FALSE)
        }
        smoothScatter(x, y, nrpoints=smooth_points, nbin=smooth_bins,
                      transformation=function(x) x^(smooth_exp),
                      colramp=clr.den, cex=smooth_size, add=TRUE)
      }
 
      else if (type == "contour")
        .plt.contour(x, y, contour_n, contour_nbins,
           theme, scale_x, scale_y, pad_x, pad_y, x.lab, y.lab, 
           ellipse, ellipse_color, ellipse_lwd, fit.line, fit_color, fit_lwd,
           axis_x_pre, axis_y_pre, axis_fmt, xlab.adj, ylab.adj) 



# plot points, segments, means with no by --------------------------------- 

      else if (size.pt[1] > 0) {

        # plot points
        if (n.xcol == 1  &&  n.ycol == 1) {  # one x and one y variable
          if (length(out_ind) == 0)  # no outliers
            points(x[,1], y[,1], pch=shape, col=color[1], bg=fill[1],
                     cex=size.pt, ...)
          else {  # display outliers separately
            points(x[-out_ind,1], y[-out_ind,1],
               pch=shape, col=color[1], bg=fill[1], cex=size.pt, ...)
            points(x[out_ind,1], y[out_ind,1],
               pch=out_shape, col=out_color, bg=out_fill, cex=size.pt, ...)
            text(x[out_ind], y[out_ind], labels=ID[out_ind],
               pos=1, offset=0.4, col=ID_color, cex=ID_size)
          }
        }

        else if (n.ycol == 1) {  # one y
          for (i in 1:n.xcol) {  # one to many x's
              if (theme %in% c("gray", "white"))
                if (n.clrs == 2  &&  i == 2) fill[i] <- "transparent"
              points(x[,i], y[,1], pch=shape, col=color[i], bg=fill[i],
                     cex=size.pt, ...)
          }
        }

        else  if (n.xcol == 1) {  # one x
          for (i in 1:n.ycol) {  # one to many y's
              if (theme %in% c("gray", "white"))
                if (n.clrs == 2  &&  i == 2) fill[i] <- "transparent"
              points(x[,1],y[,i], pch=shape, col=color[i], bg=fill[i],
                     cex=size.pt, ...)  # one x
          }
        }

        # plot segments to axis
        if (segments_y) {
          if (n.xcol == 1) # line segments from points to axis
            segments(x0=0, y0=y, x1=x, y1=y,
                     lty=1, lwd=.75, col=col.segment)
          else if (n.xcol == 2)  # line segments between points
            segments(x0=x[,1], y0=y[,1], x1=x[,2], y1=y[,1],
                     lty=1, lwd=.75, col=col.segment)
        }

        if (!(stat %in% c("count", "prop", "%"))) {
          if (segments_x) {
            if (n.ycol == 1) # line segments from points to axis
              segments(y0=par("usr")[3], x0=x, y1=y, x1=x, lty=1, lwd=.75,
                       col=col.segment)
            else if (n.ycol == 2)  # line segments between points
              segments(y0=y[,1], x0=x[,1], y1=y[,2], x1=x[,1],
                       lty=1, lwd=.75, col=col.segment)
          }
        }
        else {  # stat is "count", etc.
          if (segments_x)
            if (n.xcol == 1)
              segments(y0=0, x0=x, y1=y, x1=x, lty=1, lwd=1, col=col.segment)
        }
        

# plot means --------------------------------------------------------------
        if (means  &&  stat == "data") {

        # get colors
          pch.avg <- ifelse (theme!="gray", 21, 23)
          bck.g <- ifelse (theme!="gray", rgb(130,90,70,
                          maxColorValue=255), "gray40")
          if (grepl(".black", theme, fixed=TRUE))
            bck.g <- "gray85"

          # restore un-jittered data
          if (jitter_x > 0) {
            x[,1] <- x.temp
            rm(x.temp)
          }
          if (jitter_y > 0) {
            y[,1] <- y.temp
            rm(y.temp)
          }

          m.lvl <- numeric(length = 0)

          # plot means for num y across levels of factor x
          if (cat.x && !cat.y && !unique.x) {
            for (i in seq_along(x.lvl))
              m.lvl[i] <- mean(y[x==i], na.rm=TRUE)
            abline(h=m.lvl, col="gray50", lwd=.5)
            points(m.lvl, pch=pch.avg, bg=bck.g, col=bck.g, cex=size.pt*1.5)
          }

          # plot means for num x across levels of factor y
          if (!cat.x && cat.y && !unique.y) {
            for (i in seq_along(y.lvl))
              m.lvl[i] <- mean(x[y==i], na.rm=TRUE)
            abline(v=m.lvl, col="gray50", lwd=.5)
            points(m.lvl, seq_len(length(y.lvl)), pch=pch.avg, bg=bck.g,
                   col=bck.g, cex=size.pt*1.5)
          }
        }  # end means

      }  # end not smooth, plot points with no by
      # -----------------------------------------


# plot center line --------------------------------------------------------
      if (center_line != "off") {
        if (center_line == "mean") {
          m.y <- mean(y[,1], na.rm=TRUE)
          lbl <- " mean"
        }
        else if (center_line == "median") {
          m.y <- median(y[,1], na.rm=TRUE)
          lbl <- " median"
        }
        else if (center_line == "zero") {
          m.y <- 0
          lbl <- ""
        }

        abline(h=m.y, col="gray50", lty="dashed")  # draw center line
        mtext(lbl, side=4, cex=.7, col="gray50", las=2, at=m.y, line=0.1)

        if (center_line == "zero") m.y <- median(y[,1], na.rm=TRUE)  # runs

      }  # end center_line

      else
        m.y <- median(y[,1], na.rm=TRUE)

      # run chart (and attempted interaction chart)
      if (segments && n.xcol==1 && n.ycol==1 && !date.var) {
        for (j in 1:(nrow(x)-1)) {
          segments(x0=x[j,1], y0=y[j,1],
                   x1=x[j+1,1], y1=y[j+1,1],
                   lty="solid", lwd=ln.width, col=color[i])
        }
      }  # end segments
    }  # end is.null by


# by grouping variable ----------------------------------------------------

    else {
      clr <- character(length(n.by))

      if (length(color) == 1)
        for (i in 1:n.by) clr[i] <- color  # all levels get same color
      else
        clr <- color

      shp <- integer(length(n.by))
      if (length(shape) == 1)
        for (i in 1:n.by) shp[i] <- shape
      else
        shp <- shape
#       if (length(color)==1 && length(fill)==1 && length(shape)==1) {
#         if (n.by <= 5)  # R presents only five filled points
#           shape.dft <- c(21,23,22,24,25)  # shape defaults
#         else
#           shape.dft <- c(1,0,5,2,6,8,7,9,10,12:14,11)  # shape defaults
#         for (i in 1:n.by) shp[i] <- shape.dft[i]  #  default shapes
#       }
      # plot points and segments for each group
      for (i in 1:n.by) {
        x.lv <- subset(x, by==levels(by)[i])
        y.lv <- subset(y, by==levels(by)[i])
        if (!by.bub)  {
          if (size.pt > 0)
            points(x.lv, y.lv[,1], pch=shp[i], col=clr[i], bg=fill[i],
                   cex=size.pt, lwd=0.75, ...)
        }
        else {  # size is a variable (not for BPFM, which does .dpmat() )
          size.lv <- subset(size, by==levels(by)[i])
          fill[i] <- .maketrans(fill[i], (1-pts_trans)*256)
          # size is a var and a by var
          .plt.bubble(x.lv, y.lv, size.lv, radius, power, fill[i], clr[i],
                      size_cut, prop, bbl.txt.col, object)
        }

        # interaction means plot, ts without a date var and segments=TRUE
        if (segments && !stack) {
          if (!is.null(ylab))  # thin line for ANOVA interaction plot
            if (grepl("Cell Means of", ylab, fixed=TRUE)) ln.width <- 0.75
          for (j in 1:(nrow(x.lv)-1)) {
            segments(x0=x.lv[j,1], y0=y.lv[j,1],
                     x1=x.lv[j+1,1], y1=y.lv[j+1,1],
                     lty="solid", lwd=ln.width, col=fill[i])  # not color[i]
          }
        }  # end segments
      }  # end 1:n.by

      # by legend
      if (size == 0) shape <- "lines"
      if (fill[1] == "transparent") fill <- color
      if (stack) {  # default pt.size is 0
        point.size <- ifelse (size > 0, 2.5 * axis_x_cex, 0)
        .plt.by.legend(levels(by), color, area_fill, shp=shape[1], pts_trans,
                       fill_bg, usr, pt.size=point.size,  # 22 is a rectangle
                       legend_title=legend_title)
      }
      else {  # not stack
        if (n.by > 1)
          .plt.by.legend(levels(by), color, fill, shape, pts_trans,
                         fill_bg, usr, legend_title=legend_title)
      }
    }  # end by

    
# legend (more than 1 x or y) ---------------------------------------------

    if (fill[1] == "transparent") fill <- color

    if (n.xcol > 1)  # horizontal legend, on x-axis
      .plt.legend(colnms=colnames(x), horiz=TRUE,
                  color, fill, shape, fill_bg, usr,
                  lab_cex=lab_x_cex, pt.size=1.25, legend_title)

    if (n.ycol > 1) {  # vertical legend, on y-axis
      if (stack)  # ifelse form only returns the first value of the vector
        the.fill <- area_fill
      else
        the.fill <- fill
      .plt.legend(colnms=colnames(y), horiz=FALSE,
                  color, the.fill, shape, fill_bg, usr,
                  lab_cex=lab_y_cex, pt.size=1.25, legend_title)
    }

  }  # object is point


# bubble or sunflower plot  -----------------------------------------------

  else if ((object %in% c("bubble", "sunflower"))) {
    if (!is.null(by)) {
      cat("\n"); stop(call.=FALSE, "\n------\n",
        "Parameter  by  not valid for bubble plot\n\n")
    }

    n.clrs <- 1  # for fit.line

    # colors
    if (is.null(col.low) || is.null(col.hi) || !bubble1) {
      if (fill[1] != "#46505A")  # default
        clr <- fill
      else
        clr <- getOption("bar_fill_cont")
      clr.color <- color
    }
    else {  # 1-var bubble plot and BPFM can have a color gradient
      color_palette <- colorRampPalette(c(col.low, col.hi))
      clr <- color_palette(length(unique(x)))
      clr.color <- "gray70"
    }

    # no value for size specified, do counts

    if (length(size) == 1) {
      mytbl <- table(x, y)  # get the counts, all x-y combinations
      n.count <- nrow(mytbl) * ncol(mytbl)
      if (prop) {
        count <- numeric(length=n.count)
        if (cat.y)
          mytbl <- prop.table(mytbl, 1)
        else
          mytbl <- mytbl / sum(mytbl)
      }
      else
        count <- integer(length=n.count)

      # melt the table of counts to a data frame with xx, yy, count
      xx <- integer(length=n.count)
      yy <- integer(length=n.count)
      k <- 0
      for (i in seq_len(nrow(mytbl))) {
        for (j in seq_len(ncol(mytbl))) {
          k <- k + 1
          count[k] <- mytbl[i,j]
          if (count[k] == 0) count[k] <- NA  # 0 count not plotted
          xx[k] <- as.numeric(rownames(mytbl)[i])  # row names are factors
          yy[k] <- as.numeric(colnames(mytbl)[j])
        }
      }
      if (prop) count <- round(count, 2)

      # for categorical vars, size not a var
      if (object == "bubble") {
        .plt.bubble(xx, yy, count, radius, power, clr, clr.color,
                    size_cut, prop, bbl.txt.col, object)
      }
      else if (object == "sunflower") {
        cords <- data.frame(xx, yy, count, stringsAsFactors=TRUE)
        cords <- na.omit(cords)
        sunflowerplot(cords$xx, cords$yy, number=cords$count,
            seg.col=clr.color, col=clr,
            col.axis=getOption("axis_x_color"), add=TRUE)
      }

    }  # end length(size) == 1

    # size is a variable (unless size is constant and bubble specified)
    # no by var
    else {
      .plt.bubble(x, y, size, radius, power, clr, clr.color,
                  size_cut, prop, bbl.txt.col, object)
    }
  }  # end bubble/sunflower


# ellipse option ----------------------------------------------------------
  if (do.ellipse) {

    for (i in 1:n.clrs) {

      if (n.clrs == 1) {  # one plot, all the data
          x.lv <- x[,1]
          y.lv <- y[,1]
      }

      else {  # multiple, pull out subset

        if (!is.null(by)) {  # multiple by plots
          ind <- which(by == levels(by)[i])
          x.lv <- x[ind]
          y.lv <- y[ind]
        }

        if (n.col > 1) {  # multiple variable plots
          if (n.xcol > 1) {
            x.lv <- x[,i]
            y.lv <- y[,1]
          }
          else {
            x.lv <- x[,1]
            y.lv <- y[,i]
          }
        }

        clr <- ifelse (length(color) == 1, color, color[i])

      }  # end multiple

      # ellipse stats
      cxy <- cor(x.lv, y.lv, use="pairwise.complete.obs")
      m.x <- mean(x.lv, na.rm=TRUE)
      m.y <- mean(y.lv, na.rm=TRUE)
      s.x <- sd(x.lv, na.rm=TRUE)
      s.y <- sd(y.lv, na.rm=TRUE)

      for (j in seq_along(ellipse)) { # for each ellipse for this by group
        e <- ellipse::ellipse(cxy, scale=c(s.x, s.y), centre=c(m.x, m.y),
                     level=ellipse[j])
        ln.type <- "solid"
        col.border <- ifelse (n.clrs == 1, ellipse_color, clr)
        polygon(e, border=col.border, col=ellipse_fill,
                lwd=ellipse_lwd, lty=ln.type)
      }  # jth ellipse
    }  # ith pattern
  }  # do.ellipse


# fit line option ---------------------------------------------------------

  if (fit.line != "off") {

    # if outliers noted then also do line w/o outliers
    fit.remove <- ifelse (length(out_ind) > 0, TRUE, FALSE)
    do.remove <- FALSE
    n.loops <- ifelse (n.clrs > 1, 1, as.numeric(fit.remove)+1)
    for (i.remv in 1:n.loops) {
      if (fit.remove) {
        fit_se[1] <- 0  # for line w/o outliers, no se band
        fit_lwd <- 1.5
        if (i.remv == 2) do.remove <- TRUE  # 2nd pass
      }

      mse.ln <- double(length=n.clrs)  # .ln is linear
      mse.nl <- double(length=n.clrs)  # .nl is nonlinear
      b0 <- double(length=n.clrs)
      b1 <- double(length=n.clrs)
      Rsq <- double(length=n.clrs)
      by.cat <- character(length=n.clrs)

      for (i.clr in 1:n.clrs) {  # double looping with i.remv and i

        if (n.clrs == 1) {  # one plot, all the data
          if (!date.var) {
            x.lv <- x[,1]
            y.lv <- y[,1]
          }
          else {  # date.var
            x.lv <- as.numeric(x.val)
            y.lv <- as.numeric(y[,i.clr])
          }
          clr <- fit_color
        }  # end n.clrs == 1

        else {  # multiple clrs, pull out subset

          if (!is.null(by)) {  # multiple by plots
            x.lv <- subset(x, by==levels(by)[i.clr])
            y.lv <- subset(y, by==levels(by)[i.clr])
          }

          if (n.col > 1) {  # multiple variable plots
            if (n.xcol > 1) {
              x.lv <- x[,i.clr]
              y.lv <- y[,1]
            }
            else {
              x.lv <- x[,1]
              y.lv <- y[,i.clr]
            }
          }

          clr <- ifelse (length(color) == 1, color, color[i.clr])
        }  # end multiple

        ln.type <- "solid"

        if (!is.null(out_ind)) if (do.remove) {
          x.lv <- x.lv[-out_ind]
          y.lv <- y.lv[-out_ind]
          ln.type <- ifelse (ln.type != "dashed", "dashed", "solid")
        }

        col.ln <- ifelse (n.clrs==1, fit_color, fill[i.clr])

        pf <- .plt.fit(x.lv, y.lv, fit.line, fit_power, fit_new)  # fit line

        x.lv <- pf$x.lv  # x and y get reduced in .plt.fit if NA
        y.lv <- pf$y.lv
        f.ln <- pf$f.ln  # fitted
        l.ln <- pf$l.ln  # linearized
        y.new <- pf$y.new  # computed from fitted function 

        if (i.remv == 1) {  # if outlier removal, only outlier line reported
          mse.ln[i.clr] <- pf$mse.ln
          mse.nl[i.clr] <- pf$mse.nl
          b0[i.clr] <- pf$b0
          b1[i.clr] <- pf$b1
          Rsq[i.clr] <- pf$Rsq
        }

        if (n.by > 0)
          by.cat[i.clr] <- levels(by)[i.clr]

        if (fit.line %in% c("exp", "quad", "log", "null"))
          fit_se[1] <- 0

        # se bands about each eligible fit line
        if (fit_se[1] != 0) {
          for (j in seq_along(fit_se)) {
            p.ln <- predict(l.ln, se=TRUE)
            prb <- (1 - fit_se[j]) / 2
            nrows <- length(y.lv)
            up.ln <- f.ln + (qt(prb,nrows-1) * p.ln$se.fit)
            dn.ln <- f.ln - (qt(prb,nrows-1) * p.ln$se.fit)
            polygon(c(x.lv, rev(x.lv)), c(up.ln, rev(dn.ln)),
                    col=se_fill, border="transparent")

          }  # end for each se plot
        }

        # plot fit line(s) on top of se bands
        if (!("transparent" %in% clr)) {
          if (n.clrs ==2  &&  (color[1] == color[2]))
            ln.type <- ifelse (i == 2, "dashed", "solid")
          lines(x.lv, f.ln, col=clr, lwd=fit_lwd, lty=ln.type)
        }
        else {
          lines(x.lv, f.ln, col=col.ln, lwd=fit_lwd, lty=ln.type)
        }

        # plot residuals option
        if (plot_errors) {
          red <- rgb(130,40,35, maxColorValue=255)
          pe.clr <- ifelse (theme %in% c("gray", "white"), "gray58", red)
          segments(y0=f.ln, y1=y.lv, x0=x.lv, x1=x.lv,
                   col=pe.clr, lwd=1)
        }
      }  # ith pattern (clr)
    }  # fit.remove

    if (!quiet) cat ("\n")
  }  # end fit.line
  else
    y.new <- NULL


# annotations -------------------------------------------------------------

  if (!is.null(add)) {

    if (add[1] == "means") {
      add[1] <- "v_line"
      add[2] <- "h_line"
      x1 <- "mean_x"
      y1 <- "mean_y"
    }

    if (!is.null(x1)) {
      if (date.var) x1 <- julian(x1)  # dates to date serial numbers
      if (length(which(x1 == "mean_x")) > 0) {  # get mean of x if needed
        x1[which(x1 == "mean_x")] <- mean(x, na.rm=TRUE)
        x1 <- as.numeric(x1)
      }
    }
    if (!is.null(y1)) {
      if (length(which(y1 == "mean_y")) > 0) {  # get mean of y if needed
        y1[which(y1 == "mean_y")] <- mean(y, na.rm=TRUE)
        y1 <- as.numeric(y1)
      }
    }

    if (!is.null(add)) {
        if (add[1] == "labels")
          text(x, y, labels=ID, pos=1, offset=0.4,
             pch=shape, col=getOption("add_color"), cex=getOption("add_cex"))
        else
          .plt.add(add, x1, x2, y1, y2,
               add_cex, add_lwd, add_lty, add_color, add_fill, add_trans)
    }
  }  # end annotations
  

# end, return -------------------------------------------------------------

  if (fit.line == "off") {
    mse.ln <- NULL;  mse.nl <- NULL;  b0 <- NULL;  b1 <- NULL;  Rsq <- NULL
    by.cat <- NULL
  }

  return(list(mse.ln=mse.ln, mse.nl=mse.nl, b0=b0, b1=b1, Rsq=Rsq,
              by.cat=by.cat, jitter_x=jitter_x, jitter_y=jitter_y,
              y.new=y.new))

}  # end plt.main

Try the lessR package in your browser

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

lessR documentation built on June 8, 2025, 10:35 a.m.