diff --git a/NAMESPACE b/NAMESPACE index b9868bdd..1a5b711f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -147,6 +147,7 @@ export(nlsTracePlot) export(paletteChoices) export(pcumsum) export(perc) +export(plotAB) export(plotBinResp) export(poiCI) export(psdAdd) diff --git a/NEWS.md b/NEWS.md index 725e3640..437ce1a6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,5 @@ # FSA 0.8.13 ongoing -* `ageBias()`: Modified. Changed default pch for the points, default pch for the mean points, added `pch.mean.sig=` so that the mean points can be different for ages that show a significant difference, and color for the agreement line. Changed how `col.pts=` is handled. Changed default for `show.n=` to `FALSE` (suggest that the user add the new marginal histograms). Removed `sunflower` as an option in `what=`. Added `xHist=`, `yHist=`, `col.hist=`, and `hist.panel.size=` to the `plot()` function which allows the user to add marginal histograms to the x- and y-axes of the age-bias plots. +* `ageBias()`: Modified. A complete rebuild of `plot`. Major changes are to add `plotAB()` which is primarily used to make the "legacy" age bias plots of Campana, removal of the "sunflower" plot option, new sets of defaults for many of the arguments that reflect my preferences for visualizing age comparisons (which includes defaulting to plotting differences in ages), addition of the ability to add marginal histograms (`xHist=`, `yHist=`, `col.hist=`, and `hist.panel.size=`), better handling of axis ticks and labels (primarily to show ticks at integers and make sure 0 is included for differences), and allowing the ability to add "summary layers" to the main plot (see `allowAdd=`). Many examples were added. Some functionality from previous versions will be broken. * `capFirst()`: Modified. Changed some `if()`s with `class()`es to `inherits()`. * `compIntercepts()`: Modified. Replaced two `dim()` calls with `nrow()`. * `fact2num()`: Modified. Changed some `if()`s with `class()`es to `inherits()`. @@ -13,6 +13,7 @@ * `mrOpen()`: Modified. Changed some `if()`s with `class()`es to `is.CapHist()`. * `nlsTracePlot()`: Modified. Changed some `if()`s with `class()`es to `inherits()`. * `perc()`: Modified. Changed some `if()`s with `class()`es to `inherits()`. +* `plotAB()`: Added. See description above for `ageBias()`. * `plotBinResp()`: Modified. Changed how default transparency level is calculated and set the maximum transparency to 50 (changed from 500). Fixed bug in how the width of the proportions windows were calculated by default. These changes will affect `fitPlot()` for logistic regression models. * `psdAdd()`: Modified. Changed some `if()`s with `class()`es to `inherits()`. * `residPlot()`: Modified. Changed default for `loess=` from `TRUE` to `FALSE`. Changed some `if()`s with `class()`es to `inherits()`. diff --git a/R/ageComparisons.R b/R/ageComparisons.R index 82584597..bff0d7a1 100644 --- a/R/ageComparisons.R +++ b/R/ageComparisons.R @@ -13,41 +13,43 @@ #' @param flip.table A logical that indicates whether the age-agreement table should be \sQuote{flipped} (i.e., rows are reversed so that younger ages are at the bottom of the table). This makes the table more directly comparable to the age bias plot. #' @param zero.print A string for what should be printed in place of the zeros on an age-agreement table. The default is to print a single dash. #' @param digits A numeric for the minimum number of digits to print when showing \code{what="bias"} or \code{what="diff.bias"} in \code{summary}. -#' @param cont.corr A string that indicates the continuity correction method to be used with (only) McNemar test. If \code{"none"} (default) then no continuity correction is used, if \code{"Yates"} then 0.5 is used, and if \code{"Edwards"} then 1 is used. +#' @param cont.corr A string that indicates the continuity correction method to be used with (only) McNemar test. If \code{"none"} (default) then no continuity correction is used, if \code{"Yates"} then 0.5 is used, and if \code{"Edwards"} then 1 is used. +#' @param xvals A string for whether the x-axis values are reference ages or mean of the reference and nonreference ages. #' @param x,object An object of class \code{ageBias}, usually a result from \code{ageBias}. -#' @param difference A logical for whether or not the difference between the two age estimates should be used. See details. -#' @param yHist A logical for whether a histogram of the y-axis variable should be added to the right margin of the age bias plot. See details. -#' @param xHist A logical for whether a histogram of the x-axis variable should be added to the top margin of the age bias plot. See details. -#' @param hist.panel.size A numeric between 0 and 1 that indicates the proportional size of histograms (relative to the entire plotting pane) in the plot margins (only used if \code{xHist=TRUE} or \code{yHist=TRUE}). -#' @param col.hist A string for the color of the bars in the marginal histograms (only used if \code{xHist=TRUE} or \code{yHist=TRUE}). #' @param xlab,ylab A string label for the x-axis (reference) or y-axis (non-reference) age estimates, respectively. +#' @param xlim,ylim A numeric vector of length 2 that contains the limits of the x-axis (reference ages) or y-axis (non-reference ages), respectively. +#' @param xaxt,yaxt A string which specifies the x- and y-axis types. Specifying \dQuote{n} suppresses plotting of the axis. See \code{?par}. +#' @param col.agree A string or numeric for the color of the 1:1 or zero (if \code{difference=TRUE}) reference line. +#' @param lwd.agree A numeric for the line width of the 1:1 or zero (if \code{difference=TRUE}) reference line. +#' @param lty.agree A numeric for the line type of the 1:1 or zero (if \code{difference=TRUE}) reference line. #' @param lwd A numeric that controls the separate \sQuote{lwd} argument (e.g., \code{lwd.CI} and \code{lwd.range}). #' @param sfrac A numeric that controls the separate \sQuote{sfrac} arguments (e.g., \code{sfrac.CI} and \code{sfrac.range}). See \code{sfrac} in \code{\link[plotrix]{plotCI}} of \pkg{plotrix}. +#' @param show.pts A logical for whether or not the raw data points are plotted. +#' @param pch.pts A numeric for the plotting character of the raw data points. +#' @param col.pts A string or numeric for the color of the raw data points. The default is to use black with the transparency found in \code{transparency}. +#' @param cex.pts A character expansion value for the size of the symbols for \code{pch.pts}. +#' @param transparency A numeric (between 0 and 1) for the level of transparency of the raw data points. This number of points plotted on top of each other will represent the color in \code{col.pts}. #' @param pch.mean A numeric for the plotting character used for the mean values when the means are considered insignificant. #' @param pch.mean.sig A numeric for the plotting character for the mean values when the means are considered significant. #' @param cex.mean A character expansion value for the size of the mean symbol in \code{pch.mean} and \code{pch.mean.sig}. +#' @param show.range A logical for whether or not vertical bars that represent the range of the data points are plotted. +#' @param col.range A string or numeric for the color of the range intervals. +#' @param lwd.range A numeric for the line width of the range intervals. +#' @param sfrac.range A numeric for the size of the ends of the range intervals. See \code{sfrac} in \code{\link[plotrix]{plotCI}} of \pkg{plotrix}. #' @param show.CI A logical for whether confidence intervals should be plotted or not. #' @param col.CI A string or numeric for the color of confidence interval bars that are considered non-significant. #' @param col.CIsig A string or numeric for the color of confidence interval bars that are considered significant. #' @param lwd.CI A numeric for the line width of the confidence interval bars. #' @param sfrac.CI A numeric for the size of the ends of the confidence interval bars. See \code{sfrac} in \code{\link[plotrix]{plotCI}} of \pkg{plotrix}. -#' @param show.range A logical for whether or not vertical bars that represent the range of the data points are plotted. -#' @param col.range A string or numeric for the color of the range intervals. -#' @param lwd.range A numeric for the line width of the range intervals. -#' @param sfrac.range A numeric for the size of the ends of the range intervals. See \code{sfrac} in \code{\link[plotrix]{plotCI}} of \pkg{plotrix}. -#' @param show.pts A logical for whether or not the raw data points are plotted. -#' @param pch.pts A numeric for the plotting character of the raw data points. -#' @param col.pts A string or numeric for the color of the raw data points. The default is to use black with the transparency found in \code{transparency}. -#' @param transparency A numeric (between 0 and 1) for the level of transparency of the raw data points. This number of points plotted on top of each other will represent the color in \code{col.pts}. -#' @param col.agree A string or numeric for the color of the 1:1 or zero (if \code{difference=TRUE}) reference line. -#' @param lwd.agree A numeric for the line width of the 1:1 or zero (if \code{difference=TRUE}) reference line. -#' @param lty.agree A numeric for the line type of the 1:1 or zero (if \code{difference=TRUE}) reference line. -#' @param cex.numbers A character expansion value for the size of the numbers plotted when \code{what="numbers"} is used. +#' @param yHist A logical for whether a histogram of the y-axis variable should be added to the right margin of the age bias plot. See details. +#' @param xHist A logical for whether a histogram of the x-axis variable should be added to the top margin of the age bias plot. See details. +#' @param hist.panel.size A numeric between 0 and 1 that indicates the proportional size of histograms (relative to the entire plotting pane) in the plot margins (only used if \code{xHist=TRUE} or \code{yHist=TRUE}). +#' @param col.hist A string for the color of the bars in the marginal histograms (only used if \code{xHist=TRUE} or \code{yHist=TRUE}). #' @param show.n A logical for whether the sample sizes for each level of the x-axis variable is shown (\code{=TRUE}, default) or not (\code{=FALSE}). #' @param nYpos A numeric for the relative Y position of the sample size values when \code{show.n=TRUE}. For example, if \code{nYpos=1.03} then the sample size values will be centered at 3 percent above the top end of the y-axis. #' @param cex.n A character expansion value for the size of the sample size values. -#' @param xlim,ylim A numeric vector of length 2 that contains the limits of the x-axis (reference ages) or y-axis (non-reference ages), respectively. -#' @param xaxt,yaxt A string which specifies the x- and y-axis types. Specifying \dQuote{n} suppresses plotting of the axis. See \code{?par}. +#' @param cex.numbers A character expansion value for the size of the numbers plotted when \code{what="numbers"} is used. +#' @param allowAdd A logical that will allow the user to add items to the main (i.e., not the marginal histograms) plot panel (if \code{TRUE}). Defaults to \code{FALSE}. #' @param \dots Additional arguments for methods. #' #' @details Generally, one of the two age estimates will be identified as the \dQuote{reference} set. In some cases this may be the true ages, the ages from the more experienced reader, the ages from the first reading, or the ages from the structure generally thought to provide the most accurate results. In other cases, such as when comparing two novice readers, the choice may be arbitrary. The reference ages will form the columns of the age-agreement table and will be the \dQuote{constant} age used in the t-tests and age bias plots (i.e., the x-axis). See further details below. @@ -55,14 +57,16 @@ #' The age-agreement table is constructed with \code{what="table"} in \code{summary}. The agreement table can be \dQuote{flipped} (i.e., the rows in descending rather than ascending order) with \code{flip.table=TRUE}. By default, the tables are shown with zeros replaced by dashes. This behavior can be changed with \code{zero.print}. #' #' Three statistical tests of symmetry for the age-agreement table can be computed with \code{what=} in \code{summary}. The \dQuote{unpooled} or Bowker test as described in Hoenig et al. (1995) is constructed with \code{what="Bowker"}, the \dQuote{semi-pooled} or Evans-Hoenig test as described in Evans and Hoenig (1998) is constructed with \code{what="EvansHoenig"}, and the \dQuote{pooled} or McNemar test as described in Evans and Hoenig (1998) is constructed with \code{what="McNemar"}. All three tests are computed with \code{what="symmetry"}. -#' -#' An age bias plot, as originally defined by Campana et al. (1995) but somewhat modified here, is constructed with \code{what="bias"} (the default) in \code{plot}. The reference variable from the \code{ageBias} call is plotted on the x-axis. Plotted confidence intervals are computed for the mean of the nonreference ages at each of the reference ages. The level of confidence is controlled by \code{sig.level=} given in the original \code{ageBias} call (i.e., confidence level is 100*(1-\code{sig.level})). Confidence intervals are only shown if the sample size is greater than the value in \code{min.n.CI=}. Confidence intervals plotted in red with an open dot (by default; these can be changed with \code{col.CIsig} and \code{pch.mean.sig}, respectively) do not contain the reference age (see discussion of t-tests below). Vertical lines that connect the minimum to the maximum observed values of the y-axis variable at each age of the x-axis variable are plotted in grey if \code{show.range=TRUE}. Individual points are plotted if \code{show.pts=TRUE}. The 1:1 (45 degree) agreement line is shown for comparative purposes. The sample sizes at each age of the x-axis variable are shown if \code{show.n=TRUE}. The position of the sample sizes is controlled with \code{nYpos=}. -#' -#' A modified age bias plot, nearly as defined by Muir et al. (2008), is constructed as defined above but by also including \code{difference=TRUE} in \code{plot} so that the y-axis is the difference in the paired reference and non-reference ages from the \code{ageBias} call (specifically, nonreference-reference). #' -#' The frequency of observations at each unique (x,y) coordinate are shown by using \code{what="numbers"} in \code{plot}. -#' -#' Histograms of the y- and x-axis variables may be added to the base age bias plot by including \code{yHist=TRUE} and \code{xHist=TRUE}, respectively. +#' Four types of plots for visualizing differences between sets of two age estimates may be created. Two of these plots may be constructed with \code{plotAB()}. In the \code{plotAB()} plots, the reference ages are plotted on the x-axis and the nonreference ages are on the y-axis. The 1:1 (45 degree) agreement line is shown for comparative purposes. The default \code{plotAB()} plot (using \code{what="bias"}) was inspired by the age bias plot introduced by Campana et al. (1995). The default settings for this age bias plot show the mean and confidence interval for the nonreference ages at each of the reference ages. The level of confidence is controlled by \code{sig.level=} given in the original \code{ageBias} call (i.e., confidence level is 100*(1-\code{sig.level})). Confidence intervals are only shown if the sample size is greater than the value in \code{min.n.CI=} (also from the original call to \code{ageBias}). Confidence intervals plotted in red with an open dot (by default; these can be changed with \code{col.CIsig} and \code{pch.mean.sig}, respectively) do not contain the reference age (see discussion of t-tests below). Sample sizes at each reference age are shown if \code{show.n=TRUE}. The position of the sample sizes is controlled with \code{nYpos=}, whereas their size is controlled with \code{cex.n}. Arguments may be used to nearly replicate the age bias plot as introduced by Campana et al. (1995) as shown in the examples. +#' +#' The frequency of observations at each unique (x,y) coordinate are shown by using \code{what="numbers"} in \code{plotAB}. The size of the numbers is controlled with \code{cex.numbers}. +#' +#' Muir et al. (2008) modified the age bias plot by plotting the difference between the two ages on the y-axis (still against a reference age on the x-axis). McBride (2015) introduced the Bland-Altman plot for comparing fish ages where the difference in ages is plotted on the y-axis versus the mean of the ages on the x-axis. Modifications of these plots are created with \code{plot} (rather than \code{plotAB}) using \code{xvals=} to control what is plotted on the x-axis (i.e., \code{xvals="reference"} uses the reference ages, whereas \code{xvals="mean"} uses the mean of the two ages for the x-axis). In both plots, a horizontal agreement line at a difference of zero is shown for comparative purposes. In addition, a histogram of the differences is shown in the right margin (can be excluded with \code{yHist=FALSE}.) A histogram of the reference ages is shown by default in the top margin for the modified age bias plot, but not for the modified Bland-Altman-like plot (the presence of this histogram is controlled with \code{xHist=}). +#' +#' By default, the modified age bias plot shows the mean and range for the nonreference ages at each of the reference ages. Means shown with an open dot are mean age differences that are significantly different from zero. The ranges of differences in ages at each reference age can be removed with \code{show.range=FALSE}. A confidence interval for difference in ages can be added with \code{show.CI=FALSE}. The color and symbol coding for the means and CIs is the same as described above for \code{plotAB}. Individual points are plotted if \code{show.pts=TRUE}, where there color is controlled by \code{col.pts=} and \code{transparency=}. See examples for use of these arguments. +#' +#' Both the main (i.e., not the marginal histograms) can be "added to" after the plot is constructed if \code{allowAdd=TRUE} is used. For example, the Bland-Altman-like plot can be augmented with a horizonal line at the mean difference in ages, a line for the regression between the differences and the means, or a generalized additive model that describes the relationship betwen the diffrences and the means. See the examples for use of \code{allowAdd=TRUE}. It is recommended to save the plotting parameters (e.g., \code{op <- par(no.readonly=TRUE)}) before using \code{plot} with \code{allowAdd=TRUE} so that the original parameters can be reset (e.g., \code{par(op)}); see the examples. #' #' Individual t-tests to determine if the mean age of the nonreference set at a particular age of the reference set is equal to the reference age (e.g., is the mean age of the nonreference set at age-3 of the reference set statistically different from 3?) are shown with \code{what="bias"} in \code{summary}. The results provide a column that indicates whether the difference is significant or not as determined by adjusted p-values from the t-tests and using the significance level provided in \code{sig.level} (defaults to 0.05). Similar results for the difference in ages (e.g., is the mean reference age minus nonreference age at a nonreference age of 3 different from 0?) are constructed with \code{what="diff.bias"} in \code{summary}. #' @@ -78,7 +82,7 @@ #' \item nref.lab A string that contains an optional label for the age estimates in the rows (non-reference) of the age-agreement table. #'} #' -#' \code{summary} returns the result if \code{what=} contains one item, otherwise it returns nothing. Nothing is returned by \code{plot}, but see details for a description of the plot that is produced. +#' \code{summary} returns the result if \code{what=} contains one item, otherwise it returns nothing. Nothing is returned by \code{plot} or \code{plotAB}, but see details for a description of the plot that is produced. #' #' @section Testing: Tested all symmetry test results against results in Evans and Hoenig (2008), the McNemar and Evans-Hoenig results against results from \code{\link[fishmethods]{compare2}} in \pkg{fishmethods}, and all results using the \code{\link[FSAdata]{AlewifeLH}} data set from \pkg{FSAdata} against results from the online resource at http://www.nefsc.noaa.gov/fbp/age-prec/. #' @@ -86,7 +90,7 @@ #' #' @seealso See \code{\link{agePrecision}} for measures of precision between pairs of age estimates. See \code{\link[fishmethods]{compare2}} in \pkg{fishmethods} for similar functionality. #' -#' @section IFAR Chapter: 4-Age Comparisons. +#' @section IFAR Chapter: 4-Age Comparisons. \bold{Note that \code{plot} has changed since IFAR was published. Some of the original functionality is in \code{plotAB}. See examples.} #' #' @references Ogle, D.H. 2016. \href{http://derekogle.com/IFAR}{Introductory Fisheries Analyses with R}. Chapman & Hall/CRC, Boca Raton, FL. #' @@ -122,58 +126,131 @@ #' # flip the table -- ease of comparison to age bias plot #' summary(ab1,what="table",flip.table=TRUE) #' -#' ## default plot -#' plot(ab1) -#' ## demonstrates squaring up the axes -#' plot(ab1,ylim=c(-1,23),xlim=c(-1,23)) -#' ## plot with the data points shown -#' plot(ab1,show.pts=TRUE) -#' ## plot with the range shown -#' plot(ab1,show.range=TRUE) -#' ## plot with just the range -#' plot(ab1,show.range=TRUE,show.CI=FALSE) -#' ## plot with just the points shown -#' plot(ab1,show.pts=TRUE,show.CI=FALSE) -#' ## plot just the means -#' plot(ab1,show.CI=FALSE) -#' ## plot with no difference in significance bar colors -#' plot(ab1,col.CIsig="black") -#' ## plot with no difference in points for significance ages -#' plot(ab1,pch.mean.sig=19) -#' plot(ab1,pch.mean=21) -#' ## plot with no difference in points or colors for significance ages -#' plot(ab1,pch.mean.sig=19,col.CIsig="black") -#' plot(ab1,pch.mean=21,col.CIsig="black") +#' ############################################################# +#' ## Traditional Age-Bias Plot (from Campana et al. (1995)) +#' # Default +#' plotAB(ab1) +#' # very close to Campana et al. (2001) +#' plotAB(ab1,pch.mean.sig=19,col.CIsig="black",sfrac=0.01) +#' # Squared up the axes +#' plotAB(ab1,pch.mean.sig=19,col.CIsig="black",ylim=c(-1,23),xlim=c(-1,23)) #' ## Show sample sizes -#' plot(ab1,show.n=TRUE) +#' plotAB(ab1,show.n=TRUE) #' ## Move sample sizes (and change text size) -#' plot(ab1,show.n=TRUE,nYpos=0.02,cex.n=0.5) +#' plotAB(ab1,show.n=TRUE,nYpos=0.02,cex.n=0.5) +#' +#' ## Traditional numbers plot +#' plotAB(ab1,what="numbers") +#' +#' ############################################################# +#' ## Differences Plot (originally inspired by Muir et al. (2008)) +#' # Default (ranges, open circles for sig diffs, marginal hists) +#' plot(ab1) +#' # Show CIs for means (with and without ranges) +#' plot(ab1,show.CI=TRUE) +#' plot(ab1,show.CI=TRUE,show.range=FALSE) +#' # show points (with and without CIs) +#' plot(ab1,show.CI=TRUE,show.range=FALSE,show.pts=TRUE) +#' plot(ab1,show.range=FALSE,show.pts=TRUE) +#' # Use same symbols for all means (with ranges) +#' plot(ab1,pch.mean.sig=19) +#' # Use same symbols/colors for all means/CIs (without ranges) +#' plot(ab1,show.range=FALSE,show.CI=TRUE,pch.mean.sig=19,col.CIsig="black") +#' # Remove histograms +#' plot(ab1,xHist=FALSE) +#' plot(ab1,yHist=FALSE) +#' plot(ab1,xHist=FALSE,yHist=FALSE) #' ## Suppress confidence intervals for n < a certain value #' ## must set this in the original ageBias() call -#' ab2 <- ageBias(scaleC~otolithC,data=WhitefishLC,min.n.CI=5, +#' ab2 <- ageBias(scaleC~otolithC,data=WhitefishLC,min.n.CI=8, #' ref.lab="Otolith Age",nref.lab="Scale Age") -#' plot(ab2) +#' plot(ab2,show.CI=TRUE,show.range=FALSE) +#' +#' +#' ############################################################# +#' ## Differences Plot (originally inspired by Bland-Altman plots +#' ## in McBride (2015)) +#' plot(ab1,xvals="mean") +#' ## Modify axis limits +#' plot(ab1,xvals="mean",xlim=c(1,17)) +#' ## Add and remove histograms +#' plot(ab1,xvals="mean",xHist=TRUE) +#' plot(ab1,xvals="mean",xHist=TRUE,yHist=FALSE) +#' plot(ab1,xvals="mean",yHist=FALSE) #' -#' ## plot of differences -#' ## (note could use all of the same modifications as demonstrated above) -#' plot(ab1,difference=TRUE) +#' ############################################################# +#' ## Adding post hoc analyses to the main plot +#' # get original graphing parameters to be reset at the end +#' op <- par(no.readonly=TRUE) #' -#' ## Add marginal histograms (can also use all modifications from above) -#' # add both -#' plot(ab1,difference=TRUE,xHist=TRUE,yHist=TRUE) -#' # just for y-axis variable -#' plot(ab1,difference=TRUE,yHist=TRUE) -#' # just for x-axis variable -#' plot(ab1,difference=TRUE,xHist=TRUE) -#' # adjust the size of the panels for the histograms -#' # and adjust placement of the "n" label. -#' plot(ab1,difference=TRUE,xHist=TRUE,yHist=TRUE,hist.panel.size=1/4) -#' # also works without differences -#' plot(ab1,xHist=TRUE,yHist=TRUE) +#' # get raw data +#' tmp <- ab1$d +#' head(tmp) +#' +#' # Add mean difference (w/ approx. 95% CI) +#' bias <- mean(tmp$diff)+c(-1.96,0,1.96)*se(tmp$diff) +#' plot(ab1,xvals="mean",xlim=c(1,17),allowAdd=TRUE) +#' abline(h=bias,lty=2,col="red") +#' par(op) +#' +#' # Same as above, but without margoinal histogram, and with +#' # 95% agreement lines as well (1.96SDs) +#' # (this is nearly a replicate of a Bland-Altman plot) +#' bias <- mean(tmp$diff)+c(-1.96,0,1.96)*se(tmp$diff) +#' agline <- mean(tmp$diff)+c(-1.96,1.96)*sd(tmp$diff) +#' plot(ab1,xvals="mean",yHist=FALSE,allowAdd=TRUE) +#' abline(h=bias,lty=2,col="red") +#' abline(h=agline,lty=3,lwd=2,col="blue") +#' par(op) +#' +#' # Add linear regression line of differences on means (w/ approx. 95% CI) +#' lm1 <- lm(diff~mean,data=tmp) +#' xval <- seq(0,19,0.1) +#' pred1 <- predict(lm1,data.frame(mean=xval),interval="confidence") +#' bias1 <- data.frame(xval,pred1) +#' head(bias1) +#' plot(ab1,xvals="mean",xlim=c(1,17),allowAdd=TRUE) +#' lines(lwr~xval,data=bias1,lty=2,col="red") +#' lines(upr~xval,data=bias1,lty=2,col="red") +#' lines(fit~xval,data=bias1,lty=2,col="red") +#' par(op) +#' +#' # Add loess of differences on means (w/ approx. 95% CI as a polygon) +#' lo2 <- loess(diff~mean,data=tmp) +#' xval <- seq(min(tmp$mean),max(tmp$mean),0.1) +#' pred2 <- predict(lo2,data.frame(mean=xval),se=TRUE) +#' bias2 <- data.frame(xval,pred2) +#' bias2$lwr <- bias2$fit-1.96*bias2$se.fit +#' bias2$upr <- bias2$fit+1.96*bias2$se.fit +#' head(bias2) +#' plot(ab1,xvals="mean",xlim=c(1,17),allowAdd=TRUE) +#' with(bias2,polygon(c(xval,rev(xval)),c(lwr,rev(upr)), +#' col=col2rgbt("red",1/10),border=NA)) +#' lines(fit~xval,data=bias2,lty=2,col="red") +#' par(op) +#' +#' # Same as above, but polygon and line behind the points +#' plot(ab1,xvals="mean",xlim=c(1,17),col.pts="white",allowAdd=TRUE) +#' with(bias2,polygon(c(xval,rev(xval)),c(lwr,rev(upr)), +#' col=col2rgbt("red",1/10),border=NA)) +#' lines(fit~xval,data=bias2,lty=2,col="red") +#' points(diff~mean,data=tmp,pch=19,col=col2rgbt("black",1/8)) +#' par(op) +#' +#' # Can also be made with the reference ages on the x-axis +#' lo3 <- loess(diff~otolithC,data=tmp) +#' xval <- seq(min(tmp$otolithC),max(tmp$otolithC),0.1) +#' pred3 <- predict(lo3,data.frame(otolithC=xval),se=TRUE) +#' bias3 <- data.frame(xval,pred3) +#' bias3$lwr <- bias3$fit-1.96*bias3$se.fit +#' bias3$upr <- bias3$fit+1.96*bias3$se.fit +#' plot(ab1,show.range=FALSE,show.pts=TRUE,col.pts="white",allowAdd=TRUE) +#' with(bias3,polygon(c(xval,rev(xval)),c(lwr,rev(upr)), +#' col=col2rgbt("red",1/10),border=NA)) +#' lines(fit~xval,data=bias3,lty=2,col="red") +#' points(diff~otolithC,data=tmp,pch=19,col=col2rgbt("black",1/8)) +#' par(op) #' -#' ## "Numbers" plot -#' plot(ab1,what="number",col.agree="gray50") -#' #' @rdname ageBias #' @export ageBias <- function(formula,data,ref.lab=tmp$Enames,nref.lab=tmp$Rname, @@ -184,17 +261,19 @@ ageBias <- function(formula,data,ref.lab=tmp$Enames,nref.lab=tmp$Rname, if (!tmp$Rclass %in% c("numeric","integer")) STOP("LHS variable must be numeric.") if (!tmp$metExpNumE) STOP("'ageBias' must have only one RHS variable.") if (!tmp$Eclass %in% c("numeric","integer")) STOP("RHS variable must be numeric.") - ## get variable names separately (r=ref, nr=nonref) + ## get variable names separately nref.name <- tmp$Rname ref.name <- tmp$Enames ## rename dataframe of just ages (for simplicity) d <- tmp$mf ## sample size n <- nrow(d) - ## add differences data + ## add differences data for each fish d$diff <- d[,nref.name]-d[,ref.name] + ## add mean data for each fish + d$mean <- rowMeans(d[,c(nref.name,ref.name)]) - ## Summarizations of nrdata by rdata (more 'true' structure) + ## Summarizations of nonref data by ref data (more 'true' structure) # turn off warnings for not using factor data (turn back on below) options(warn=-1) # Summary stats of cdata by ages of rdata @@ -216,7 +295,8 @@ ageBias <- function(formula,data,ref.lab=tmp$Enames,nref.lab=tmp$Rname, ## Put together an output list d <- list(data=d,agree=agree.table,bias=bias.df,bias.diff=bias2.df, - ref.lab=ref.lab,nref.lab=nref.lab) + ref.name=ref.name,ref.lab=ref.lab, + nref.name=nref.name,nref.lab=nref.lab) class(d) <- "ageBias" d } @@ -421,142 +501,115 @@ iEvansHoenig <- function(obj) { } ## End internal Evans Hoenig's Test function - #' @rdname ageBias #' @export -plot.ageBias <- function(x,what=c("bias","numbers"),difference=FALSE, - yHist=FALSE,xHist=FALSE,hist.panel.size=1/8,col.hist="gray90", - xlab=x$ref.lab,ylab=x$nref.lab, +plot.ageBias <- function(x,xvals=c("reference","mean"), + xlab=ifelse(xvals=="reference",x$ref.lab,"Mean Age"), + ylab=paste(x$nref.lab,"-",x$ref.lab), + xlim=NULL,ylim=NULL, + yaxt=graphics::par("yaxt"),xaxt=graphics::par("xaxt"), + col.agree="gray60",lwd.agree=lwd,lty.agree=2, lwd=1,sfrac=0, - pch.mean=19,pch.mean.sig=21,cex.mean=lwd, - show.CI=TRUE,col.CI="black",col.CIsig="red", + show.pts=NULL,pch.pts=20,cex.pts=ifelse(xHist|yHist,1.5,1), + col.pts="black",transparency=1/10, + show.CI=FALSE,col.CI="black",col.CIsig="red", lwd.CI=lwd,sfrac.CI=sfrac, - show.range=FALSE,col.range="gray70",lwd.range=lwd,sfrac.range=sfrac, - show.pts=FALSE,pch.pts=20,col.pts="black",transparency=1/10, - col.agree="gray80",lwd.agree=lwd,lty.agree=2, - show.n=FALSE,nYpos=1.03,cex.n=0.75,cex.numbers=0.9, - xlim=NULL,ylim=NULL, - yaxt=graphics::par("yaxt"), - xaxt=graphics::par("xaxt"),...) { # nocov start - what <- match.arg(what) + show.range=NULL,col.range=ifelse(show.CI,"gray70","black"), + lwd.range=lwd,sfrac.range=sfrac, + pch.mean=19,pch.mean.sig=ifelse(show.CI|show.range,21,19), + cex.mean=lwd, + yHist=TRUE,xHist=NULL,hist.panel.size=1/8,col.hist="gray90", + allowAdd=FALSE,...) { # nocov start + ## Handle some defaults + xvals <- match.arg(xvals) + if (is.null(show.pts)) show.pts <- ifelse(xvals=="reference",FALSE,TRUE) + if (is.null(show.range)) show.range <- ifelse(xvals=="reference",TRUE,FALSE) + if (is.null(xHist)) xHist <- ifelse(xvals=="reference",TRUE,FALSE) + ## Plot preparations + op <- graphics::par(no.readonly=TRUE) + iABSetLayout(yHist,xHist,hist.panel.size) col.pts <- col2rgbt(col.pts,transparency) - op <- iABSetLayout(yHist,xHist,hist.panel.size) - switch(what, - bias={ - axlmts <- iABAxisLmts(x,xlim,ylim,difference,show.range,show.pts,show.CI) - iAgeBiasPlot(x,difference,xlab,ifelse(!difference,ylab,paste(ylab,"-",xlab)), - xHist,yHist, - pch.mean,pch.mean.sig,cex.mean, - show.CI,col.CI,col.CIsig,lwd.CI,sfrac.CI, - show.range,col.range,lwd.range,sfrac.range, - show.pts,pch.pts,col.pts, - col.agree,lwd.agree,lty.agree, - show.n,nYpos,cex.n, - axlmts,yaxt,...) - }, - numbers={ - if (difference) { - WARN("'difference' not used with 'numbers' plot.") - difference <- FALSE - } - axlmts <- iABAxisLmts(x,xlim,ylim,difference, - show.range=FALSE,show.pts=TRUE,show.CI=FALSE) - iAgeBiasNumPlot(x,xlab,ylab,axlmts,lwd.agree,lty.agree,col.agree, - cex.numbers,yaxt,xaxt,...) } - ) # end switch - if (xHist) iABAddXHist(x,col.hist,axlmts,op,yHist) - if (yHist) iABAddYHist(x,difference,col.hist,axlmts,op,xHist) - graphics::layout(1,widths=1,heights=1) - graphics::par(op) + ## Route based on plot type + if (xvals=="reference") { + iDiffAB(x,xlab,ylab,xlim,ylim,yaxt,xaxt, + col.agree,lwd.agree,lty.agree, + show.pts,pch.pts,cex.pts,col.pts, + show.CI,col.CI,col.CIsig,lwd.CI,sfrac.CI, + show.range,col.range,lwd.range,sfrac.range, + pch.mean,pch.mean.sig,cex.mean, + yHist,xHist,col.hist,op,...) + } else { + iDiffBA(x,xlab,ylab,xlim,ylim,yaxt,xaxt, + col.agree,lwd.agree,lty.agree, + pch.pts,cex.pts,col.pts,yHist,xHist,col.hist,op,...) + } + if(!allowAdd) { + graphics::layout(1,widths=1,heights=1) + graphics::par(op) + } } # nocov end #============================================================= -# This internal function is used to produce the age bias plot. -# This is called by ageBias(). +# This internal function is used to find appropriate axis +# limits for the age bias plot. #============================================================= -iAgeBiasPlot <- function(obj,difference,xlab,ylab,xHist,yHist, - pch.mean,pch.mean.sig,cex.mean, - show.CI,col.CI,col.CIsig,lwd.CI,sfrac.CI, - show.range,col.range,lwd.range,sfrac.range, - show.pts,pch.pts,col.pts, - col.agree,lwd.agree,lty.agree,show.n,nYpos,cex.n, - axlmts,yaxt,...) { # nocov start +iABAxisLmts <- function(x,xlim,ylim,difference,show.range,show.pts,show.CI) { # nocov start # identify whether difference data should be used or not, put in a tmp data frame - if (!difference) d <- obj$bias - else d <- obj$bias.diff - # Plot more tick marks - graphics::par(lab=c(length(d[,1]),length(d$mean),7)) - # Set base plot with Mean of 2nd vs. 1st age range - graphics::plot(d$mean~d[,1],xlim=axlmts$xlim,ylim=axlmts$ylim,xlab=xlab,ylab=ylab, - col="white",yaxt="n",...) - # Helps keep y-axis as integers (needed for difference plot) - if (yaxt!="n") {graphics::axis(2,seq(axlmts$ylim[1],axlmts$ylim[2],1))} - # agreement line -- horizontal for difference and 45 degree for bias plot - if (difference) graphics::abline(h=0,lwd=lwd.agree,lty=lty.agree,col=col.agree) - else graphics::abline(a=0,b=1,lwd=lwd.agree,lty=lty.agree,col=col.agree) - # add individual points if asked for - if (show.pts) { - if (difference) graphics::points(obj$d[,2],obj$d[,3],col=col.pts,pch=pch.pts) - else graphics::points(obj$d[,2],obj$d[,1],col=col.pts,pch=pch.pts) - } - # add range of individual points if asked for - if (show.range) { - plotrix::plotCI(x=d[,1],y=d$mean,li=d$min,ui=d$max,add=TRUE, - slty=1,scol=col.range,pch=NULL, - lwd=lwd.range,gap=0,sfrac=sfrac.range) - } - # add CIs for mean if asked for - if (show.CI) { - if (any(d$sig)) { # for ages that are signficantly different - plotrix::plotCI(x=d[,1][d$sig],y=d$mean[d$sig],li=d$LCI[d$sig],ui=d$UCI[d$sig], - add=TRUE,slty=1,scol=col.CIsig,pch=NULL, - lwd=lwd.CI,gap=0,sfrac=sfrac.CI) + if (!difference) { d <- x$bias } else { d <- x$bias.diff } + # If no xlim given then make xlim the range of x values + # which are given in the first position of d (note that + # d is the age bias statistics) + if (!is.null(xlim)) xlmt <- xlim + else xlmt <- range(d[,1],na.rm=TRUE) + # If no ylim is given then make ylim. Making ylim depends + # on whether differences are used and whether ranges are + # shown or not. + if (!is.null(ylim)) ylmt <- ylim + else { + # build up a vector that will ultimately find the range + # to form the axes. Begin by filling with the means. + tmp.min <- tmp.max <- d$mean + # if show.cis then add in LCI and UCI. CIs will be shown + # for all real age bias plots. This is primarily for use + # with the numbers plot + if (show.CI) { + tmp.min <- c(tmp.min,d$LCI) + tmp.max <- c(tmp.max,d$UCI) } - if (any(!d$sig)) {# for ages that are not significantly different - plotrix::plotCI(x=d[,1][!d$sig],y=d$mean[!d$sig], - li=d$LCI[!d$sig],ui=d$UCI[!d$sig], - add=TRUE,slty=1,scol=col.CI,pch=NULL, - lwd=lwd.CI,gap=0,sfrac=sfrac.CI) + # if show.range or show.pts then add in min and max + if (show.range | show.pts) { + tmp.min <- c(tmp.min,d$min) + tmp.max <- c(tmp.max,d$max) } + # if not differences then add in xlmts + tmp.min <- c(tmp.min,xlmt) + # Find floor of min and ceiling of max to make the limits + ylmt <- c(floor(min(tmp.min,na.rm=TRUE)),ceiling(max(tmp.max,na.rm=TRUE))) } - # Add means - if (any(d$sig)) { # for ages that are signficantly different - graphics::points(x=d[,1][d$sig],y=d$mean[d$sig],pch=pch.mean.sig, - cex=cex.mean,col=ifelse(show.CI,col.CIsig,col.CI),bg="white") - } - if (any(!d$sig)) {# for ages that are not significantly different - graphics::points(x=d[,1][!d$sig],y=d$mean[!d$sig],pch=pch.mean, - cex=cex.mean,col=col.CI,bg="white") - } - # show the sample sizes at the top if asked for - if (show.n & !xHist) graphics::text(d[,1],graphics::grconvertY(nYpos,"npc"), - d$n,cex=cex.n,xpd=TRUE) + # return values + list(xlim=xlmt,ylim=ylmt) } # nocov end #============================================================= -# Internal function to produce the age bias numbers plot. +# This internal function is used to find "pretty" ticks and +# labels for the axes of age bias plots. #============================================================= -iAgeBiasNumPlot <- function(obj,xlab,ylab,axlmts, - lwd.agree,lty.agree,col.agree, - cex.numbers,yaxt,xaxt,...) { # nocov start - # convert age-agreement table into a data frame with all zeros removed - # y,x in d[,1] and d[,2], respectively - # lables in d[,3] - d <- as.data.frame(obj$agree) - d[,1] <- fact2num(d[,1]) - d[,2] <- fact2num(d[,2]) - d <- d[d[,3]>0,] - # make an empty plot - graphics::plot(d[,2],d[,1],type="n",xlab=xlab,ylab=ylab, - xlim=axlmts$xlim,ylim=axlmts$ylim,yaxt="n",xaxt="n",...) - # Helps keep axes as integers - if (yaxt!="n") {graphics::axis(2,seq(axlmts$ylim[1],axlmts$ylim[2],1))} - if (xaxt!="n") {graphics::axis(1,seq(axlmts$xlim[1],axlmts$xlim[2],1))} - # add the one-to-one line - graphics::lines(axlmts$xlim,axlmts$xlim,lwd=lwd.agree,lty=lty.agree,col=col.agree) - # add the numbers at each point - graphics::text(d[,2],d[,1],labels=d[,3],cex=cex.numbers) +iABAxisLbls <- function(x) { # nocov start + xlmt <- x$xlim + ylmt <- x$ylim + # create pretty axis tickes and labels (integers and will include zero) + xlbls.lmt <- c(floor(xlmt[1]),ceiling(xlmt[2])) + xlbl1 <- seq(xlbls.lmt[1],xlbls.lmt[2],1) + xlbl2 <- pretty(xlbls.lmt) + if(any(apply(matrix(xlbl2),1,iGetDecimals)>0)) xlbl2 <- xlbl1 + ylbls.lmt <- c(floor(ylmt[1]),ceiling(ylmt[2])) + ylbl1 <- seq(ylbls.lmt[1],ylbls.lmt[2],1) + ylbl2 <- pretty(ylbls.lmt) + if(any(apply(matrix(ylbl2),1,iGetDecimals)>0)) ylbl2 <- ylbl1 + # return values + list(xlim=xlmt,xticks=xlbl1,xlbls=xlbl2, + ylim=ylmt,yticks=ylbl1,ylbls=ylbl2) } # nocov end @@ -565,23 +618,17 @@ iAgeBiasNumPlot <- function(obj,xlab,ylab,axlmts, # layout if yHist=TRUE or xHist=TRUE. #============================================================= iABSetLayout <- function(yHist,xHist,hist.size) { # nocov start - tmp <- graphics::par()$mar if (yHist & xHist) { # Both marginal hists - graphics::layout(matrix(c(2,0,1,3),ncol=2,byrow=TRUE), - widths=c(1-hist.size,hist.size), - heights=c(hist.size,1-hist.size)) - op <- graphics::par(mar=c(tmp[1],tmp[2],0,0)) + graphics::layout(matrix(c(1,0,3,2),ncol=2,byrow=TRUE), + widths=c(1-hist.size,hist.size), + heights=c(hist.size,1-hist.size)) } else if (yHist) { # Only marginal hist on Y-axis - graphics::layout(matrix(c(1,2),ncol=2),widths=c(1-hist.size,hist.size),heights=1) - op <- graphics::par(mar=c(tmp[1],tmp[2],tmp[3],0)) + graphics::layout(matrix(c(2,1),ncol=2),widths=c(1-hist.size,hist.size),heights=1) } else if (xHist) { # Only marginal hist on X-axis - graphics::layout(matrix(c(2,1),ncol=1),widths=1,heights=c(hist.size,1-hist.size)) - op <- graphics::par(mar=c(tmp[1],tmp[2],0,tmp[4])) + graphics::layout(matrix(c(1,2),ncol=1),widths=1,heights=c(hist.size,1-hist.size)) } else { graphics::layout(1,widths=1,heights=1) # no marginal hists (return normal layout) - op <- graphics::par(mar=tmp) } - op # return old pars } # nocov end @@ -589,9 +636,7 @@ iABSetLayout <- function(yHist,xHist,hist.size) { # nocov start # This internal function is used to create the histogram for # the x-axis margin if xHist=TRUE. #============================================================= -iABAddXHist <- function(x,col.hist,axlmts,op,yHist) { # nocov start - # Get the data to plot ... in second column of x$data - xdat <- x$data[,2] +iABAddXHist <- function(xdat,col.hist,axlmts,op,yHist) { # nocov start # Set graphing parameters for this panel # the bottom is set to zero to butt up against the age bias plot, # the left and right are set to the same as the age bias plot which @@ -630,9 +675,7 @@ iABAddXHist <- function(x,col.hist,axlmts,op,yHist) { # nocov start # This internal function is used to create the histogram for # the y-axis margin if yHist=TRUE. #============================================================= -iABAddYHist <- function(x,difference,col.hist,axlmts,op,xHist) { # nocov start - # Get the data to plot - if (difference) { ydat <- x$data[,3] } else { ydat <- x$data[,1] } +iABAddYHist <- function(ydat,col.hist,axlmts,op,xHist) { # nocov start # Set graphing parameters for this panel # the left is set to zero to butt up against the age bias plot, # the bottom and top are set to the same as the age bias plot which @@ -668,47 +711,215 @@ iABAddYHist <- function(x,difference,col.hist,axlmts,op,xHist) { # nocov start #============================================================= -# This internal function is used to find appropriate axis -# limits for the age bias plot. +# Internal function to produce the age bias plot using differences. +# This was inspired by Muir et al. (2008) #============================================================= -iABAxisLmts <- function(x,xlim,ylim,difference,show.range,show.pts,show.CI) { # nocov start - # identify whether difference data should be used or not, put in a tmp data frame - if (!difference) { d <- x$bias } else { d <- x$bias.diff } - # If no xlim given then make xlim the range of x values - # which are given in the first position of d (note that - # d is the age bias statistics) - if (!is.null(xlim)) xlmt <- xlim - else xlmt <- range(d[,1],na.rm=TRUE) - # If no ylim is given then make ylim. Making ylim depends - # on whether differences are used and whether ranges are - # shown or not. - if (!is.null(ylim)) ylmt <- ylim - else { - # build up a vector that will ultimately find the range - # to form the axes. Begin by filling with the means. - tmp.min <- tmp.max <- d$mean - # if show.cis then add in LCI and UCI. CIs will be shown - # for all real age bias plots. This is primarily for use - # with the numbers plot - if (show.CI) { - tmp.min <- c(tmp.min,d$LCI) - tmp.max <- c(tmp.max,d$UCI) +iDiffAB <- function(x,xlab,ylab,xlim,ylim,yaxt,xaxt, + col.agree,lwd.agree,lty.agree, + show.pts,pch.pts,cex.pts,col.pts, + show.CI,col.CI,col.CIsig,lwd.CI,sfrac.CI, + show.range,col.range,lwd.range,sfrac.range, + pch.mean,pch.mean.sig,cex.mean, + yHist,xHist,col.hist,op,...) { # nocov start + ## Get data + sumd <- x$bias.diff + rawd <- x$data[,c(x$ref.name,"diff")] + ## Find useful axis limits + axlmts <- iABAxisLmts(x,xlim,ylim,show.range,show.pts,show.CI,difference=TRUE) + axlmts <- iABAxisLbls(axlmts) + ## Add axis histograms if asked for + if (xHist) iABAddXHist(rawd[,1],col.hist,axlmts,op,yHist) + if (yHist) iABAddYHist(rawd[,2],col.hist,axlmts,op,xHist) + ## Make Main Plot + graphics::par(mar=c(op$mar[1],op$mar[2], + ifelse(xHist,0,op$mar[3]),ifelse(yHist,0,op$mar[4]))) + # Set base plot with Mean of 2nd vs. 1st age range + graphics::plot(sumd$mean~sumd[,x$ref.name],xlim=axlmts$xlim,ylim=axlmts$ylim, + xlab=xlab,ylab=ylab,col="white",yaxt="n",xaxt="n",...) + # Add axes (integer ticks, prettier labels (incl. 0)) + if (xaxt!="n") { + graphics::axis(1,at=axlmts$xticks,labels=NA) + graphics::axis(1,at=axlmts$xlbls) + } + if (yaxt!="n") { + graphics::axis(2,at=axlmts$yticks,labels=NA) + graphics::axis(2,at=axlmts$ylbls) + } + # agreement line -- horizontal for difference and 45 degree for bias plot + graphics::abline(h=0,lwd=lwd.agree,lty=lty.agree,col=col.agree) + # add individual points if asked for + if (show.pts) { + graphics::points(x$d[,x$ref.name],x$d[,"diff"], + col=col.pts,pch=pch.pts,cex=cex.pts) + } + # add range of individual points if asked for + if (show.range) { + plotrix::plotCI(x=sumd[,x$ref.name],y=sumd$mean,li=sumd$min,ui=sumd$max, + add=TRUE,slty=1,scol=col.range,pch=NULL, + lwd=lwd.range,gap=0,sfrac=sfrac.range) + } + # add CIs for mean if asked for + if (show.CI) { + if (any(sumd$sig)) { # for ages that are signficantly different + plotrix::plotCI(x=sumd[,x$ref.name][sumd$sig],y=sumd$mean[sumd$sig], + li=sumd$LCI[sumd$sig],ui=sumd$UCI[sumd$sig], + add=TRUE,slty=1,scol=col.CIsig,pch=NULL, + lwd=lwd.CI,gap=0,sfrac=sfrac.CI) } - # if show.range or show.pts then add in min and max - if (show.range | show.pts) { - tmp.min <- c(tmp.min,d$min) - tmp.max <- c(tmp.max,d$max) + if (any(!sumd$sig)) {# for ages that are not significantly different + plotrix::plotCI(x=sumd[,x$ref.name][!sumd$sig],y=sumd$mean[!sumd$sig], + li=sumd$LCI[!sumd$sig],ui=sumd$UCI[!sumd$sig], + add=TRUE,slty=1,scol=col.CI,pch=NULL, + lwd=lwd.CI,gap=0,sfrac=sfrac.CI) + } + } + # Add means + if (show.CI | show.range) { + if (any(sumd$sig)) { # for ages that are signficantly different + graphics::points(x=sumd[,x$ref.name][sumd$sig],y=sumd$mean[sumd$sig], + pch=pch.mean.sig,cex=cex.mean, + col=ifelse(show.CI,col.CIsig,col.CI),bg="white") + } + if (any(!sumd$sig)) {# for ages that are not significantly different + graphics::points(x=sumd[,x$ref.name][!sumd$sig],y=sumd$mean[!sumd$sig], + pch=pch.mean,cex=cex.mean,col=col.CI,bg="white") } - # if not differences then add in xlmts - tmp.min <- c(tmp.min,xlmt) - # Find floor of min and ceiling of max to make the limits - ylmt <- c(floor(min(tmp.min,na.rm=TRUE)),ceiling(max(tmp.max,na.rm=TRUE))) } - # return values - list(xlim=xlmt,ylim=ylmt) } # nocov end +#============================================================= +# Internal function to produce the age bias plot using differences +# but with mean ages on the x-axis. This was inspired by the +# Bland-Altman plots in McBride (2015). +#============================================================= +iDiffBA <- function(x,xlab,ylab,xlim,ylim,yaxt,xaxt, + col.agree,lwd.agree,lty.agree, + pch.pts,cex.pts,col.pts, + yHist,xHist,col.hist,op,...) { # nocov start + rawd <- x$data[,c("mean","diff")] + if (is.null(xlim)) xlim <- range(rawd[,1]) + if (is.null(ylim)) ylim <- range(rawd[,2]) + axlmts <- list(xlim=xlim,ylim=ylim) + axlmts <- iABAxisLbls(axlmts) + ## Add axis histograms if asked for + if (xHist) iABAddXHist(rawd[,1],col.hist,axlmts,op,yHist) + if (yHist) iABAddYHist(rawd[,2],col.hist,axlmts,op,xHist) + ## Make Main Plot + graphics::par(mar=c(op$mar[1],op$mar[2], + ifelse(xHist,0,op$mar[3]),ifelse(yHist,0,op$mar[4]))) + # Set base plot with Mean of 2nd vs. 1st age range + graphics::plot(rawd[,2]~rawd[,1],xlim=axlmts$xlim,ylim=axlmts$ylim, + xlab=xlab,ylab=ylab,col="white",yaxt="n",xaxt="n",...) + # Add axes (integer ticks, prettier labels (incl. 0)) + if (xaxt!="n") { + graphics::axis(1,at=axlmts$xticks,labels=NA) + graphics::axis(1,at=axlmts$xlbls) + } + if (yaxt!="n") { + graphics::axis(2,at=axlmts$yticks,labels=NA) + graphics::axis(2,at=axlmts$ylbls) + } + # agreement line -- horizontal for difference and 45 degree for bias plot + graphics::abline(h=0,lwd=lwd.agree,lty=lty.agree,col=col.agree) + # add individual points if asked for + graphics::points(rawd[,2]~rawd[,1],col=col.pts,pch=pch.pts,cex=cex.pts) +} # nocov end + + +#' @rdname ageBias +#' @export +plotAB <- function(x,what=c("bias","Campana","numbers"), + xlab=x$ref.lab,ylab=x$nref.lab,xlim=NULL,ylim=NULL, + yaxt=graphics::par("yaxt"),xaxt=graphics::par("xaxt"), + col.agree="gray60",lwd.agree=lwd,lty.agree=2,lwd=1,sfrac=0, + pch.mean=19,pch.mean.sig=21,cex.mean=lwd, + col.CI="black",col.CIsig="red",lwd.CI=lwd,sfrac.CI=sfrac, + show.n=FALSE,nYpos=1.03,cex.n=0.75,cex.numbers=0.75,...) { # nocov start + what <- match.arg(what) + axlmts <- iABAxisLmts(x,xlim,ylim,difference=FALSE,show.range=FALSE, + show.pts=FALSE,show.CI=TRUE) + if (what!="numbers") { + iPlotABCamp(x,xlab,ylab,axlmts,yaxt,xaxt, + col.agree,lwd.agree,lty.agree,lwd,sfrac, + pch.mean,pch.mean.sig,cex.mean,col.CI, + col.CIsig,lwd.CI,sfrac.CI,show.n,nYpos,cex.n,...) + } else { + iPlotABNum(x,xlab,ylab,axlmts,yaxt,xaxt, + lwd.agree,lty.agree,col.agree,cex.numbers,...) + } +} # nocov end + + +#============================================================= +# Internal function to produce the age bias plot (like Campana). +#============================================================= +iPlotABCamp <- function(x,xlab,ylab,axlmts,yaxt,xaxt, + col.agree,lwd.agree,lty.agree,lwd,sfrac, + pch.mean,pch.mean.sig,cex.mean,col.CI, + col.CIsig,lwd.CI,sfrac.CI, + show.n,nYpos,cex.n,...) { # nocov start + sumd <- x$bias + # Plot more tick marks + graphics::par(lab=c(length(sumd[,x$ref.name]),length(sumd$mean),7)) + # Set base plot with Mean of 2nd vs. 1st age range + graphics::plot(sumd$mean~sumd[,x$ref.name],xlim=axlmts$xlim,ylim=axlmts$ylim, + xlab=xlab,ylab=ylab,col="white",yaxt="n",...) + # Helps keep y-axis as integers (needed for difference plot) + if (yaxt!="n") {graphics::axis(2,seq(axlmts$ylim[1],axlmts$ylim[2],1))} + # agreement line -- horizontal for difference and 45 degree for bias plot + graphics::abline(a=0,b=1,lwd=lwd.agree,lty=lty.agree,col=col.agree) + # add CIs for mean + if (any(sumd$sig)) { # for ages that are signficantly different + plotrix::plotCI(x=sumd[,x$ref.name][sumd$sig],y=sumd$mean[sumd$sig], + li=sumd$LCI[sumd$sig],ui=sumd$UCI[sumd$sig], + add=TRUE,slty=1,scol=col.CIsig,pch=NULL, + lwd=lwd.CI,gap=0,sfrac=sfrac.CI) + } + if (any(!sumd$sig)) {# for ages that are not significantly different + plotrix::plotCI(x=sumd[,x$ref.name][!sumd$sig],y=sumd$mean[!sumd$sig], + li=sumd$LCI[!sumd$sig],ui=sumd$UCI[!sumd$sig], + add=TRUE,slty=1,scol=col.CI,pch=NULL, + lwd=lwd.CI,gap=0,sfrac=sfrac.CI) + } + # Add means + if (any(sumd$sig)) { # for ages that are signficantly different + graphics::points(x=sumd[,x$ref.name][sumd$sig],y=sumd$mean[sumd$sig], + pch=pch.mean.sig,cex=cex.mean,col=col.CIsig,bg="white") + } + if (any(!sumd$sig)) {# for ages that are not significantly different + graphics::points(x=sumd[,x$ref.name][!sumd$sig],y=sumd$mean[!sumd$sig], + pch=pch.mean,cex=cex.mean,col=col.CI,bg="white") + } + # show the sample sizes at the top if asked for + if (show.n) graphics::text(sumd[,x$ref.name],graphics::grconvertY(nYpos,"npc"), + sumd$n,cex=cex.n,xpd=TRUE) +} # nocov end + +#============================================================= +# Internal function to produce the age bias numbers plot. +#============================================================= +iPlotABNum <- function(obj,xlab,ylab,axlmts,yaxt,xaxt, + lwd.agree,lty.agree,col.agree,cex.numbers,...) { # nocov start + # convert age-agreement table into a data frame with all zeros removed + # y,x in d[,1] and d[,2], respectively + # lables in d[,3] + d <- as.data.frame(obj$agree) + d[,1] <- fact2num(d[,1]) + d[,2] <- fact2num(d[,2]) + d <- d[d[,3]>0,] + # make an empty plot + graphics::plot(d[,2],d[,1],type="n",xlab=xlab,ylab=ylab, + xlim=axlmts$xlim,ylim=axlmts$ylim,yaxt="n",xaxt="n",...) + # Helps keep axes as integers + if (yaxt!="n") {graphics::axis(2,seq(axlmts$ylim[1],axlmts$ylim[2],1))} + if (xaxt!="n") {graphics::axis(1,seq(axlmts$xlim[1],axlmts$xlim[2],1))} + # add the one-to-one line + graphics::lines(axlmts$xlim,axlmts$xlim,lwd=lwd.agree,lty=lty.agree,col=col.agree) + # add the numbers at each point + graphics::text(d[,2],d[,1],labels=d[,3],cex=cex.numbers) +} # nocov end + #' @title Compute measures of precision among sets of ages. diff --git a/man/ageBias.Rd b/man/ageBias.Rd index 11ccaa0d..044b3225 100644 --- a/man/ageBias.Rd +++ b/man/ageBias.Rd @@ -6,6 +6,7 @@ \alias{summary.ageBias} \alias{summary.ageBias} \alias{plot.ageBias} +\alias{plotAB} \title{Compute and view possible differnces between paired sets of ages.} \usage{ ageBias(formula, data, ref.lab = tmp$Enames, nref.lab = tmp$Rname, @@ -16,17 +17,27 @@ ageBias(formula, data, ref.lab = tmp$Enames, nref.lab = tmp$Rname, zero.print = "-", digits = 3, cont.corr = c("none", "Yates", "Edwards"), ...) -\method{plot}{ageBias}(x, what = c("bias", "numbers"), difference = FALSE, - yHist = FALSE, xHist = FALSE, hist.panel.size = 1/8, - col.hist = "gray90", xlab = x$ref.lab, ylab = x$nref.lab, lwd = 1, +\method{plot}{ageBias}(x, xvals = c("reference", "mean"), + xlab = ifelse(xvals == "reference", x$ref.lab, "Mean Age"), + ylab = paste(x$nref.lab, "-", x$ref.lab), xlim = NULL, ylim = NULL, + yaxt = graphics::par("yaxt"), xaxt = graphics::par("xaxt"), + col.agree = "gray60", lwd.agree = lwd, lty.agree = 2, lwd = 1, + sfrac = 0, show.pts = NULL, pch.pts = 20, cex.pts = ifelse(xHist | + yHist, 1.5, 1), col.pts = "black", transparency = 1/10, show.CI = FALSE, + col.CI = "black", col.CIsig = "red", lwd.CI = lwd, sfrac.CI = sfrac, + show.range = NULL, col.range = ifelse(show.CI, "gray70", "black"), + lwd.range = lwd, sfrac.range = sfrac, pch.mean = 19, + pch.mean.sig = ifelse(show.CI | show.range, 21, 19), cex.mean = lwd, + yHist = TRUE, xHist = NULL, hist.panel.size = 1/8, + col.hist = "gray90", allowAdd = FALSE, ...) + +plotAB(x, what = c("bias", "Campana", "numbers"), xlab = x$ref.lab, + ylab = x$nref.lab, xlim = NULL, ylim = NULL, + yaxt = graphics::par("yaxt"), xaxt = graphics::par("xaxt"), + col.agree = "gray60", lwd.agree = lwd, lty.agree = 2, lwd = 1, sfrac = 0, pch.mean = 19, pch.mean.sig = 21, cex.mean = lwd, - show.CI = TRUE, col.CI = "black", col.CIsig = "red", lwd.CI = lwd, - sfrac.CI = sfrac, show.range = FALSE, col.range = "gray70", - lwd.range = lwd, sfrac.range = sfrac, show.pts = FALSE, pch.pts = 20, - col.pts = "black", transparency = 1/10, col.agree = "gray80", - lwd.agree = lwd, lty.agree = 2, show.n = FALSE, nYpos = 1.03, - cex.n = 0.75, cex.numbers = 0.9, xlim = NULL, ylim = NULL, - yaxt = graphics::par("yaxt"), xaxt = graphics::par("xaxt"), ...) + col.CI = "black", col.CIsig = "red", lwd.CI = lwd, sfrac.CI = sfrac, + show.n = FALSE, nYpos = 1.03, cex.n = 0.75, cex.numbers = 0.75, ...) } \arguments{ \item{formula}{A formula of the form \code{nrefvar~refvar}, where \code{nrefvar} and \code{refvar} generically represent variables that contain the paired \dQuote{nonreference} and \dQuote{reference} age estimates, respectively. See details.} @@ -57,27 +68,33 @@ ageBias(formula, data, ref.lab = tmp$Enames, nref.lab = tmp$Rname, \item{x, object}{An object of class \code{ageBias}, usually a result from \code{ageBias}.} -\item{difference}{A logical for whether or not the difference between the two age estimates should be used. See details.} +\item{xvals}{A string for whether the x-axis values are reference ages or mean of the reference and nonreference ages.} -\item{yHist}{A logical for whether a histogram of the y-axis variable should be added to the right margin of the age bias plot. See details.} +\item{xlab, ylab}{A string label for the x-axis (reference) or y-axis (non-reference) age estimates, respectively.} -\item{xHist}{A logical for whether a histogram of the x-axis variable should be added to the top margin of the age bias plot. See details.} +\item{xlim, ylim}{A numeric vector of length 2 that contains the limits of the x-axis (reference ages) or y-axis (non-reference ages), respectively.} -\item{hist.panel.size}{A numeric between 0 and 1 that indicates the proportional size of histograms (relative to the entire plotting pane) in the plot margins (only used if \code{xHist=TRUE} or \code{yHist=TRUE}).} +\item{xaxt, yaxt}{A string which specifies the x- and y-axis types. Specifying \dQuote{n} suppresses plotting of the axis. See \code{?par}.} -\item{col.hist}{A string for the color of the bars in the marginal histograms (only used if \code{xHist=TRUE} or \code{yHist=TRUE}).} +\item{col.agree}{A string or numeric for the color of the 1:1 or zero (if \code{difference=TRUE}) reference line.} -\item{xlab, ylab}{A string label for the x-axis (reference) or y-axis (non-reference) age estimates, respectively.} +\item{lwd.agree}{A numeric for the line width of the 1:1 or zero (if \code{difference=TRUE}) reference line.} + +\item{lty.agree}{A numeric for the line type of the 1:1 or zero (if \code{difference=TRUE}) reference line.} \item{lwd}{A numeric that controls the separate \sQuote{lwd} argument (e.g., \code{lwd.CI} and \code{lwd.range}).} \item{sfrac}{A numeric that controls the separate \sQuote{sfrac} arguments (e.g., \code{sfrac.CI} and \code{sfrac.range}). See \code{sfrac} in \code{\link[plotrix]{plotCI}} of \pkg{plotrix}.} -\item{pch.mean}{A numeric for the plotting character used for the mean values when the means are considered insignificant.} +\item{show.pts}{A logical for whether or not the raw data points are plotted.} -\item{pch.mean.sig}{A numeric for the plotting character for the mean values when the means are considered significant.} +\item{pch.pts}{A numeric for the plotting character of the raw data points.} -\item{cex.mean}{A character expansion value for the size of the mean symbol in \code{pch.mean} and \code{pch.mean.sig}.} +\item{cex.pts}{A character expansion value for the size of the symbols for \code{pch.pts}.} + +\item{col.pts}{A string or numeric for the color of the raw data points. The default is to use black with the transparency found in \code{transparency}.} + +\item{transparency}{A numeric (between 0 and 1) for the level of transparency of the raw data points. This number of points plotted on top of each other will represent the color in \code{col.pts}.} \item{show.CI}{A logical for whether confidence intervals should be plotted or not.} @@ -97,19 +114,21 @@ ageBias(formula, data, ref.lab = tmp$Enames, nref.lab = tmp$Rname, \item{sfrac.range}{A numeric for the size of the ends of the range intervals. See \code{sfrac} in \code{\link[plotrix]{plotCI}} of \pkg{plotrix}.} -\item{show.pts}{A logical for whether or not the raw data points are plotted.} +\item{pch.mean}{A numeric for the plotting character used for the mean values when the means are considered insignificant.} -\item{pch.pts}{A numeric for the plotting character of the raw data points.} +\item{pch.mean.sig}{A numeric for the plotting character for the mean values when the means are considered significant.} -\item{col.pts}{A string or numeric for the color of the raw data points. The default is to use black with the transparency found in \code{transparency}.} +\item{cex.mean}{A character expansion value for the size of the mean symbol in \code{pch.mean} and \code{pch.mean.sig}.} -\item{transparency}{A numeric (between 0 and 1) for the level of transparency of the raw data points. This number of points plotted on top of each other will represent the color in \code{col.pts}.} +\item{yHist}{A logical for whether a histogram of the y-axis variable should be added to the right margin of the age bias plot. See details.} -\item{col.agree}{A string or numeric for the color of the 1:1 or zero (if \code{difference=TRUE}) reference line.} +\item{xHist}{A logical for whether a histogram of the x-axis variable should be added to the top margin of the age bias plot. See details.} -\item{lwd.agree}{A numeric for the line width of the 1:1 or zero (if \code{difference=TRUE}) reference line.} +\item{hist.panel.size}{A numeric between 0 and 1 that indicates the proportional size of histograms (relative to the entire plotting pane) in the plot margins (only used if \code{xHist=TRUE} or \code{yHist=TRUE}).} -\item{lty.agree}{A numeric for the line type of the 1:1 or zero (if \code{difference=TRUE}) reference line.} +\item{col.hist}{A string for the color of the bars in the marginal histograms (only used if \code{xHist=TRUE} or \code{yHist=TRUE}).} + +\item{allowAdd}{A logical that will allow the user to add items to the main (i.e., not the marginal histograms) plot panel (if \code{TRUE}). Defaults to \code{FALSE}.} \item{show.n}{A logical for whether the sample sizes for each level of the x-axis variable is shown (\code{=TRUE}, default) or not (\code{=FALSE}).} @@ -118,10 +137,6 @@ ageBias(formula, data, ref.lab = tmp$Enames, nref.lab = tmp$Rname, \item{cex.n}{A character expansion value for the size of the sample size values.} \item{cex.numbers}{A character expansion value for the size of the numbers plotted when \code{what="numbers"} is used.} - -\item{xlim, ylim}{A numeric vector of length 2 that contains the limits of the x-axis (reference ages) or y-axis (non-reference ages), respectively.} - -\item{xaxt, yaxt}{A string which specifies the x- and y-axis types. Specifying \dQuote{n} suppresses plotting of the axis. See \code{?par}.} } \value{ \code{ageBias} returns a list with the following items: @@ -134,7 +149,7 @@ ageBias(formula, data, ref.lab = tmp$Enames, nref.lab = tmp$Rname, \item nref.lab A string that contains an optional label for the age estimates in the rows (non-reference) of the age-agreement table. } -\code{summary} returns the result if \code{what=} contains one item, otherwise it returns nothing. Nothing is returned by \code{plot}, but see details for a description of the plot that is produced. +\code{summary} returns the result if \code{what=} contains one item, otherwise it returns nothing. Nothing is returned by \code{plot} or \code{plotAB}, but see details for a description of the plot that is produced. } \description{ Constructs age-agreement tables, statistical tests to detect differences, and plots to visualize potential differences in paired age estimates. Ages may be from, for example, two readers of the same structure, one reader at two times, two structures (e.g., scales, spines, otoliths), or one structure and known ages. @@ -146,13 +161,15 @@ The age-agreement table is constructed with \code{what="table"} in \code{summar Three statistical tests of symmetry for the age-agreement table can be computed with \code{what=} in \code{summary}. The \dQuote{unpooled} or Bowker test as described in Hoenig et al. (1995) is constructed with \code{what="Bowker"}, the \dQuote{semi-pooled} or Evans-Hoenig test as described in Evans and Hoenig (1998) is constructed with \code{what="EvansHoenig"}, and the \dQuote{pooled} or McNemar test as described in Evans and Hoenig (1998) is constructed with \code{what="McNemar"}. All three tests are computed with \code{what="symmetry"}. -An age bias plot, as originally defined by Campana et al. (1995) but somewhat modified here, is constructed with \code{what="bias"} (the default) in \code{plot}. The reference variable from the \code{ageBias} call is plotted on the x-axis. Plotted confidence intervals are computed for the mean of the nonreference ages at each of the reference ages. The level of confidence is controlled by \code{sig.level=} given in the original \code{ageBias} call (i.e., confidence level is 100*(1-\code{sig.level})). Confidence intervals are only shown if the sample size is greater than the value in \code{min.n.CI=}. Confidence intervals plotted in red with an open dot (by default; these can be changed with \code{col.CIsig} and \code{pch.mean.sig}, respectively) do not contain the reference age (see discussion of t-tests below). Vertical lines that connect the minimum to the maximum observed values of the y-axis variable at each age of the x-axis variable are plotted in grey if \code{show.range=TRUE}. Individual points are plotted if \code{show.pts=TRUE}. The 1:1 (45 degree) agreement line is shown for comparative purposes. The sample sizes at each age of the x-axis variable are shown if \code{show.n=TRUE}. The position of the sample sizes is controlled with \code{nYpos=}. +Four types of plots for visualizing differences between sets of two age estimates may be created. Two of these plots may be constructed with \code{plotAB()}. In the \code{plotAB()} plots, the reference ages are plotted on the x-axis and the nonreference ages are on the y-axis. The 1:1 (45 degree) agreement line is shown for comparative purposes. The default \code{plotAB()} plot (using \code{what="bias"}) was inspired by the age bias plot introduced by Campana et al. (1995). The default settings for this age bias plot show the mean and confidence interval for the nonreference ages at each of the reference ages. The level of confidence is controlled by \code{sig.level=} given in the original \code{ageBias} call (i.e., confidence level is 100*(1-\code{sig.level})). Confidence intervals are only shown if the sample size is greater than the value in \code{min.n.CI=} (also from the original call to \code{ageBias}). Confidence intervals plotted in red with an open dot (by default; these can be changed with \code{col.CIsig} and \code{pch.mean.sig}, respectively) do not contain the reference age (see discussion of t-tests below). Sample sizes at each reference age are shown if \code{show.n=TRUE}. The position of the sample sizes is controlled with \code{nYpos=}, whereas their size is controlled with \code{cex.n}. Arguments may be used to nearly replicate the age bias plot as introduced by Campana et al. (1995) as shown in the examples. + +The frequency of observations at each unique (x,y) coordinate are shown by using \code{what="numbers"} in \code{plotAB}. The size of the numbers is controlled with \code{cex.numbers}. -A modified age bias plot, nearly as defined by Muir et al. (2008), is constructed as defined above but by also including \code{difference=TRUE} in \code{plot} so that the y-axis is the difference in the paired reference and non-reference ages from the \code{ageBias} call (specifically, nonreference-reference). +Muir et al. (2008) modified the age bias plot by plotting the difference between the two ages on the y-axis (still against a reference age on the x-axis). McBride (2015) introduced the Bland-Altman plot for comparing fish ages where the difference in ages is plotted on the y-axis versus the mean of the ages on the x-axis. Modifications of these plots are created with \code{plot} (rather than \code{plotAB}) using \code{xvals=} to control what is plotted on the x-axis (i.e., \code{xvals="reference"} uses the reference ages, whereas \code{xvals="mean"} uses the mean of the two ages for the x-axis). In both plots, a horizontal agreement line at a difference of zero is shown for comparative purposes. In addition, a histogram of the differences is shown in the right margin (can be excluded with \code{yHist=FALSE}.) A histogram of the reference ages is shown by default in the top margin for the modified age bias plot, but not for the modified Bland-Altman-like plot (the presence of this histogram is controlled with \code{xHist=}). -The frequency of observations at each unique (x,y) coordinate are shown by using \code{what="numbers"} in \code{plot}. +By default, the modified age bias plot shows the mean and range for the nonreference ages at each of the reference ages. Means shown with an open dot are mean age differences that are significantly different from zero. The ranges of differences in ages at each reference age can be removed with \code{show.range=FALSE}. A confidence interval for difference in ages can be added with \code{show.CI=FALSE}. The color and symbol coding for the means and CIs is the same as described above for \code{plotAB}. Individual points are plotted if \code{show.pts=TRUE}, where there color is controlled by \code{col.pts=} and \code{transparency=}. See examples for use of these arguments. -Histograms of the y- and x-axis variables may be added to the base age bias plot by including \code{yHist=TRUE} and \code{xHist=TRUE}, respectively. +Both the main (i.e., not the marginal histograms) can be "added to" after the plot is constructed if \code{allowAdd=TRUE} is used. For example, the Bland-Altman-like plot can be augmented with a horizonal line at the mean difference in ages, a line for the regression between the differences and the means, or a generalized additive model that describes the relationship betwen the diffrences and the means. See the examples for use of \code{allowAdd=TRUE}. It is recommended to save the plotting parameters (e.g., \code{op <- par(no.readonly=TRUE)}) before using \code{plot} with \code{allowAdd=TRUE} so that the original parameters can be reset (e.g., \code{par(op)}); see the examples. Individual t-tests to determine if the mean age of the nonreference set at a particular age of the reference set is equal to the reference age (e.g., is the mean age of the nonreference set at age-3 of the reference set statistically different from 3?) are shown with \code{what="bias"} in \code{summary}. The results provide a column that indicates whether the difference is significant or not as determined by adjusted p-values from the t-tests and using the significance level provided in \code{sig.level} (defaults to 0.05). Similar results for the difference in ages (e.g., is the mean reference age minus nonreference age at a nonreference age of 3 different from 0?) are constructed with \code{what="diff.bias"} in \code{summary}. @@ -163,7 +180,7 @@ The sample size present in the age-agreement table is found with \code{what="n"} } \section{IFAR Chapter}{ - 4-Age Comparisons. + 4-Age Comparisons. \bold{Note that \code{plot} has changed since IFAR was published. Some of the original functionality is in \code{plotAB}. See examples.} } \examples{ @@ -184,57 +201,130 @@ summary(ab1,what="table",zero.print="0") # flip the table -- ease of comparison to age bias plot summary(ab1,what="table",flip.table=TRUE) -## default plot -plot(ab1) -## demonstrates squaring up the axes -plot(ab1,ylim=c(-1,23),xlim=c(-1,23)) -## plot with the data points shown -plot(ab1,show.pts=TRUE) -## plot with the range shown -plot(ab1,show.range=TRUE) -## plot with just the range -plot(ab1,show.range=TRUE,show.CI=FALSE) -## plot with just the points shown -plot(ab1,show.pts=TRUE,show.CI=FALSE) -## plot just the means -plot(ab1,show.CI=FALSE) -## plot with no difference in significance bar colors -plot(ab1,col.CIsig="black") -## plot with no difference in points for significance ages -plot(ab1,pch.mean.sig=19) -plot(ab1,pch.mean=21) -## plot with no difference in points or colors for significance ages -plot(ab1,pch.mean.sig=19,col.CIsig="black") -plot(ab1,pch.mean=21,col.CIsig="black") +############################################################# +## Traditional Age-Bias Plot (from Campana et al. (1995)) +# Default +plotAB(ab1) +# very close to Campana et al. (2001) +plotAB(ab1,pch.mean.sig=19,col.CIsig="black",sfrac=0.01) +# Squared up the axes +plotAB(ab1,pch.mean.sig=19,col.CIsig="black",ylim=c(-1,23),xlim=c(-1,23)) ## Show sample sizes -plot(ab1,show.n=TRUE) +plotAB(ab1,show.n=TRUE) ## Move sample sizes (and change text size) -plot(ab1,show.n=TRUE,nYpos=0.02,cex.n=0.5) +plotAB(ab1,show.n=TRUE,nYpos=0.02,cex.n=0.5) + +## Traditional numbers plot +plotAB(ab1,what="numbers") + +############################################################# +## Differences Plot (originally inspired by Muir et al. (2008)) +# Default (ranges, open circles for sig diffs, marginal hists) +plot(ab1) +# Show CIs for means (with and without ranges) +plot(ab1,show.CI=TRUE) +plot(ab1,show.CI=TRUE,show.range=FALSE) +# show points (with and without CIs) +plot(ab1,show.CI=TRUE,show.range=FALSE,show.pts=TRUE) +plot(ab1,show.range=FALSE,show.pts=TRUE) +# Use same symbols for all means (with ranges) +plot(ab1,pch.mean.sig=19) +# Use same symbols/colors for all means/CIs (without ranges) +plot(ab1,show.range=FALSE,show.CI=TRUE,pch.mean.sig=19,col.CIsig="black") +# Remove histograms +plot(ab1,xHist=FALSE) +plot(ab1,yHist=FALSE) +plot(ab1,xHist=FALSE,yHist=FALSE) ## Suppress confidence intervals for n < a certain value ## must set this in the original ageBias() call -ab2 <- ageBias(scaleC~otolithC,data=WhitefishLC,min.n.CI=5, +ab2 <- ageBias(scaleC~otolithC,data=WhitefishLC,min.n.CI=8, ref.lab="Otolith Age",nref.lab="Scale Age") -plot(ab2) - -## plot of differences -## (note could use all of the same modifications as demonstrated above) -plot(ab1,difference=TRUE) - -## Add marginal histograms (can also use all modifications from above) -# add both -plot(ab1,difference=TRUE,xHist=TRUE,yHist=TRUE) -# just for y-axis variable -plot(ab1,difference=TRUE,yHist=TRUE) -# just for x-axis variable -plot(ab1,difference=TRUE,xHist=TRUE) -# adjust the size of the panels for the histograms -# and adjust placement of the "n" label. -plot(ab1,difference=TRUE,xHist=TRUE,yHist=TRUE,hist.panel.size=1/4) -# also works without differences -plot(ab1,xHist=TRUE,yHist=TRUE) - -## "Numbers" plot -plot(ab1,what="number",col.agree="gray50") +plot(ab2,show.CI=TRUE,show.range=FALSE) + + +############################################################# +## Differences Plot (originally inspired by Bland-Altman plots +## in McBride (2015)) +plot(ab1,xvals="mean") +## Modify axis limits +plot(ab1,xvals="mean",xlim=c(1,17)) +## Add and remove histograms +plot(ab1,xvals="mean",xHist=TRUE) +plot(ab1,xvals="mean",xHist=TRUE,yHist=FALSE) +plot(ab1,xvals="mean",yHist=FALSE) + +############################################################# +## Adding post hoc analyses to the main plot +# get original graphing parameters to be reset at the end +op <- par(no.readonly=TRUE) + +# get raw data +tmp <- ab1$d +head(tmp) + +# Add mean difference (w/ approx. 95\% CI) +bias <- mean(tmp$diff)+c(-1.96,0,1.96)*se(tmp$diff) +plot(ab1,xvals="mean",xlim=c(1,17),allowAdd=TRUE) +abline(h=bias,lty=2,col="red") +par(op) + +# Same as above, but without margoinal histogram, and with +# 95\% agreement lines as well (1.96SDs) +# (this is nearly a replicate of a Bland-Altman plot) +bias <- mean(tmp$diff)+c(-1.96,0,1.96)*se(tmp$diff) +agline <- mean(tmp$diff)+c(-1.96,1.96)*sd(tmp$diff) +plot(ab1,xvals="mean",yHist=FALSE,allowAdd=TRUE) +abline(h=bias,lty=2,col="red") +abline(h=agline,lty=3,lwd=2,col="blue") +par(op) + +# Add linear regression line of differences on means (w/ approx. 95\% CI) +lm1 <- lm(diff~mean,data=tmp) +xval <- seq(0,19,0.1) +pred1 <- predict(lm1,data.frame(mean=xval),interval="confidence") +bias1 <- data.frame(xval,pred1) +head(bias1) +plot(ab1,xvals="mean",xlim=c(1,17),allowAdd=TRUE) +lines(lwr~xval,data=bias1,lty=2,col="red") +lines(upr~xval,data=bias1,lty=2,col="red") +lines(fit~xval,data=bias1,lty=2,col="red") +par(op) + +# Add loess of differences on means (w/ approx. 95\% CI as a polygon) +lo2 <- loess(diff~mean,data=tmp) +xval <- seq(min(tmp$mean),max(tmp$mean),0.1) +pred2 <- predict(lo2,data.frame(mean=xval),se=TRUE) +bias2 <- data.frame(xval,pred2) +bias2$lwr <- bias2$fit-1.96*bias2$se.fit +bias2$upr <- bias2$fit+1.96*bias2$se.fit +head(bias2) +plot(ab1,xvals="mean",xlim=c(1,17),allowAdd=TRUE) +with(bias2,polygon(c(xval,rev(xval)),c(lwr,rev(upr)), + col=col2rgbt("red",1/10),border=NA)) +lines(fit~xval,data=bias2,lty=2,col="red") +par(op) + +# Same as above, but polygon and line behind the points +plot(ab1,xvals="mean",xlim=c(1,17),col.pts="white",allowAdd=TRUE) +with(bias2,polygon(c(xval,rev(xval)),c(lwr,rev(upr)), + col=col2rgbt("red",1/10),border=NA)) +lines(fit~xval,data=bias2,lty=2,col="red") +points(diff~mean,data=tmp,pch=19,col=col2rgbt("black",1/8)) +par(op) + +# Can also be made with the reference ages on the x-axis +lo3 <- loess(diff~otolithC,data=tmp) +xval <- seq(min(tmp$otolithC),max(tmp$otolithC),0.1) +pred3 <- predict(lo3,data.frame(otolithC=xval),se=TRUE) +bias3 <- data.frame(xval,pred3) +bias3$lwr <- bias3$fit-1.96*bias3$se.fit +bias3$upr <- bias3$fit+1.96*bias3$se.fit +plot(ab1,show.range=FALSE,show.pts=TRUE,col.pts="white",allowAdd=TRUE) +with(bias3,polygon(c(xval,rev(xval)),c(lwr,rev(upr)), + col=col2rgbt("red",1/10),border=NA)) +lines(fit~xval,data=bias3,lty=2,col="red") +points(diff~otolithC,data=tmp,pch=19,col=col2rgbt("black",1/8)) +par(op) } \references{ diff --git a/tests/testthat/test_AgeComparisons_MSG.R b/tests/testthat/test_AgeComparisons_MSG.R index 80b83228..eb88aa27 100644 --- a/tests/testthat/test_AgeComparisons_MSG.R +++ b/tests/testthat/test_AgeComparisons_MSG.R @@ -9,7 +9,8 @@ test_that("ageBias() messages",{ suppressWarnings(ab1 <- ageBias(scaleC~otolithC,data=WhitefishLC)) expect_error(summary(ab1,what="derek"),"should be one of") expect_error(summary(ab1,what="McNemar",cont.corr="derek"),"should be one of") - expect_error(plot(ab1,what="derek"),"should be one of") + expect_error(plot(ab1,xvals="derek"),"should be one of") + expect_error(plotAB(ab1,what="derek"),"should be one of") }) test_that("agePrecision() messages",{