Learn R Programming

FSA (version 0.8.20)

ageBias: Compute and view possible differences between paired sets of ages.

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.

Usage

ageBias(formula, data, ref.lab = tmp$Enames, nref.lab = tmp$Rname,
  method = stats::p.adjust.methods, sig.level = 0.05, min.n.CI = 3)

# S3 method for ageBias summary(object, what = c("table", "symmetry", "Bowker", "EvansHoenig", "McNemar", "bias", "diff.bias", "n"), flip.table = FALSE, zero.print = "-", digits = 3, cont.corr = c("none", "Yates", "Edwards"), ...)

# S3 method for ageBias plot(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/7, 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, 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, col.numbers = "black", ...)

Arguments

formula

A formula of the form nrefvar~refvar, where nrefvar and refvar generically represent variables that contain the paired “nonreference” and “reference” age estimates, respectively. See details.

data

A data.frame that minimally contains the paired age estimates given in formula.

ref.lab

A string label for the reference age estimates.

nref.lab

A string label for the nonreference age estimates.

method

A string for which method to use to adjust p-values for multiple comparisons. See ?p.adjust.methods.

sig.level

A numeric value to determine whether a p-value indicates a significant result. The confidence level used in plot is 100*(1-sig.level).

min.n.CI

A numeric value (default is 3) that is the smallest sample size for which a confidence interval will be computed.

what

A string that indicates what type of summary to print or plot to construct. See details.

flip.table

A logical that indicates whether the age-agreement table should be ‘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.

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.

digits

A numeric for the minimum number of digits to print when showing what="bias" or what="diff.bias" in summary.

cont.corr

A string that indicates the continuity correction method to be used with (only) McNemar test. If "none" (default) then no continuity correction is used, if "Yates" then 0.5 is used, and if "Edwards" then 1 is used.

Additional arguments for methods.

x, object

An object of class ageBias, usually a result from ageBias.

xvals

A string for whether the x-axis values are reference ages or mean of the reference and nonreference ages.

xlab, ylab

A string label for the x-axis (reference) or y-axis (non-reference) age estimates, respectively.

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.

xaxt, yaxt

A string which specifies the x- and y-axis types. Specifying “n” suppresses plotting of the axis. See ?par.

col.agree

A string or numeric for the color of the 1:1 or zero (if difference=TRUE) reference line.

lwd.agree

A numeric for the line width of the 1:1 or zero (if difference=TRUE) reference line.

lty.agree

A numeric for the line type of the 1:1 or zero (if difference=TRUE) reference line.

lwd

A numeric that controls the separate ‘lwd’ argument (e.g., lwd.CI and lwd.range).

sfrac

A numeric that controls the separate ‘sfrac’ arguments (e.g., sfrac.CI and sfrac.range). See sfrac in plotCI of plotrix.

show.pts

A logical for whether or not the raw data points are plotted.

pch.pts

A numeric for the plotting character of the raw data points.

cex.pts

A character expansion value for the size of the symbols for pch.pts.

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 transparency.

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 col.pts.

show.CI

A logical for whether confidence intervals should be plotted or not.

col.CI

A string or numeric for the color of confidence interval bars that are considered non-significant.

col.CIsig

A string or numeric for the color of confidence interval bars that are considered significant.

lwd.CI

A numeric for the line width of the confidence interval bars.

sfrac.CI

A numeric for the size of the ends of the confidence interval bars. See sfrac in plotCI of plotrix.

show.range

A logical for whether or not vertical bars that represent the range of the data points are plotted.

col.range

A string or numeric for the color of the range intervals.

lwd.range

A numeric for the line width of the range intervals.

sfrac.range

A numeric for the size of the ends of the range intervals. See sfrac in plotCI of plotrix.

pch.mean

A numeric for the plotting character used for the mean values when the means are considered insignificant.

pch.mean.sig

A numeric for the plotting character for the mean values when the means are considered significant.

cex.mean

A character expansion value for the size of the mean symbol in pch.mean and pch.mean.sig.

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.

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.

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 xHist=TRUE or yHist=TRUE).

col.hist

A string for the color of the bars in the marginal histograms (only used if xHist=TRUE or yHist=TRUE).

allowAdd

A logical that will allow the user to add items to the main (i.e., not the marginal histograms) plot panel (if TRUE). Defaults to FALSE.

show.n

A logical for whether the sample sizes for each level of the x-axis variable is shown (=TRUE, default) or not (=FALSE).

nYpos

A numeric for the relative Y position of the sample size values when show.n=TRUE. For example, if nYpos=1.03 then the sample size values will be centered at 3 percent above the top end of the y-axis.

cex.n

A character expansion value for the size of the sample size values.

cex.numbers

A character expansion value for the size of the numbers plotted when what="numbers" is used.

col.numbers

A string for the color of the numbers plotted when what="numbers" is used.

Value

ageBias returns a list with the following items:

  • data A data.frame with the original paired age estimates and the difference between those estimates.

  • agree The age-agreement table.

  • bias A data.frame that contains the bias statistics.

  • bias.diff A data.frame that contains the bias statistics for the differences.

  • ref.lab A string that contains an optional label for the age estimates in the columns (reference) of the age-agreement table.

  • nref.lab A string that contains an optional label for the age estimates in the rows (non-reference) of the age-agreement table.

summary returns the result if what= contains one item, otherwise it returns nothing. Nothing is returned by plot or plotAB, but see details for a description of the plot that is produced.

Testing

Tested all symmetry test results against results in Evans and Hoenig (2008), the McNemar and Evans-Hoenig results against results from compare2 in fishmethods, and all results using the AlewifeLH data set from FSAdata against results from the online resource at http://www.nefsc.noaa.gov/fbp/age-prec/.

IFAR Chapter

4-Age Comparisons. Note that plot has changed since IFAR was published. Some of the original functionality is in plotAB. See examples.

Details

Generally, one of the two age estimates will be identified as the “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 “constant” age used in the t-tests and age bias plots (i.e., the x-axis). See further details below.

The age-agreement table is constructed with what="table" in summary. The agreement table can be “flipped” (i.e., the rows in descending rather than ascending order) with flip.table=TRUE. By default, the tables are shown with zeros replaced by dashes. This behavior can be changed with zero.print.

Three statistical tests of symmetry for the age-agreement table can be computed with what= in summary. The “unpooled” or Bowker test as described in Hoenig et al. (1995) is constructed with what="Bowker", the “semi-pooled” or Evans-Hoenig test as described in Evans and Hoenig (1998) is constructed with what="EvansHoenig", and the “pooled” or McNemar test as described in Evans and Hoenig (1998) is constructed with what="McNemar". All three tests are computed with what="symmetry".

Four types of plots for visualizing differences between sets of two age estimates may be created. Two of these plots may be constructed with plotAB(). In the 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 plotAB() plot (using 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 sig.level= given in the original ageBias call (i.e., confidence level is 100*(1-sig.level)). Confidence intervals are only shown if the sample size is greater than the value in min.n.CI= (also from the original call to ageBias). Confidence intervals plotted in red with an open dot (by default; these can be changed with col.CIsig and 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 show.n=TRUE. The position of the sample sizes is controlled with nYpos=, whereas their size is controlled with 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 what="numbers" in plotAB. The size of the numbers is controlled with 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 plot (rather than plotAB) using xvals= to control what is plotted on the x-axis (i.e., xvals="reference" uses the reference ages, whereas 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 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 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 show.range=FALSE. A confidence interval for difference in ages can be added with show.CI=FALSE. The color and symbol coding for the means and CIs is the same as described above for plotAB. Individual points are plotted if show.pts=TRUE, where there color is controlled by col.pts= and 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 allowAdd=TRUE is used. For example, the Bland-Altman-like plot can be augmented with a horizontal 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 between the differences and the means. See the examples for use of allowAdd=TRUE. It is recommended to save the plotting parameters (e.g., op <- par(no.readonly=TRUE)) before using plot with allowAdd=TRUE so that the original parameters can be reset (e.g., 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 what="bias" in 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 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 what="diff.bias" in summary.

The sample size present in the age-agreement table is found with what="n".

References

Ogle, D.H. 2016. Introductory Fisheries Analyses with R. Chapman & Hall/CRC, Boca Raton, FL.

Campana, S.E., M.C. Annand, and J.I. McMillan. 1995. Graphical and statistical methods for determining the consistency of age determinations. Transactions of the American Fisheries Society 124:131-138. [Was (is?) available from http://www.bio.gc.ca/otoliths/documents/Campana%20et%20al%201995%20TAFS.pdf.]

Evans, G.T. and J.M. Hoenig. 1998. Testing and viewing symmetry in contingency tables, with application to readers of fish ages. Biometrics 54:620-629.

Hoenig, J.M., M.J. Morgan, and C.A. Brown. 1995. Analysing differences between two age determination methods by tests of symmetry. Canadian Journal of Fisheries and Aquatic Sciences 52:364-368.

McBride, R.S. 2015. Diagnosis of paired age agreement: A simulation approach of accuracy and precision effects. ICES Journal of Marine Science 72:2149-2167.

Muir, A.M., M.P. Ebener, J.X. He, and J.E. Johnson. 2008. A comparison of the scale and otolith methods of age estimation for lake whitefish in Lake Huron. North American Journal of Fisheries Management 28:625-635. [Was (is?) available from http://www.tandfonline.com/doi/abs/10.1577/M06-160.1]

See Also

See agePrecision for measures of precision between pairs of age estimates. See compare2 in fishmethods for similar functionality.

Examples

Run this code
# NOT RUN {
data(WhitefishLC)
ab1 <- ageBias(scaleC~otolithC,data=WhitefishLC,ref.lab="Otolith Age",nref.lab="Scale Age")
summary(ab1)
summary(ab1,what="symmetry")
summary(ab1,what="Bowker")
summary(ab1,what="EvansHoenig")
summary(ab1,what="McNemar")
summary(ab1,what="McNemar",cont.corr="Yates")
summary(ab1,what="bias")
summary(ab1,what="diff.bias")
summary(ab1,what="n")
summary(ab1,what=c("n","symmetry","table"))
# show the zeros (rather than dashes)
summary(ab1,what="table",zero.print="0")
# flip the table -- ease of comparison to age bias plot
summary(ab1,what="table",flip.table=TRUE)

#############################################################
## 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
plotAB(ab1,show.n=TRUE)
## Move sample sizes (and change text size)
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=8,
               ref.lab="Otolith Age",nref.lab="Scale Age")
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)

# }

Run the code above in your browser using DataLab