Reputation: 3488
if I'm making forest plots for a diagnostic test accuracy meta-analysis:
dat<-data.frame(TP=sample(c(25:100),20,replace=TRUE),
FN=sample(c(25:100),20,replace=TRUE),
FP=sample(c(25:100),20,replace=TRUE),
TN=sample(c(25:100),20,replace=TRUE))
I want to plot the DOR:
library(mada)
fit.DOR.DSL <- madauni(dat)
forest(fit.DOR.DSL)
But I want to customize the x axis upper and lower limit and tick marks. The forest()
plot doesn't have any obvious arguments. It allows for passing on additional graphical parameters, and I've tried throwing some random darts like xlim=c()
, at=c()
, etc. I feel as if I'm throwing darts in the dark.
Anyone know the magical argument I need to throw in here? I have no experience passing "additional graphical parameters".
As always, thanks!
Here's my updated functions, the maintainer, Philipp Doebler, was very helpful, and includes some other stuff I wanted the function to do.
#instead of using forest() for mada package, use forest.madad()
#and forest.madauni(), from this file, they allow for control over
#the x-axis and the additional of (1) summary sens and spec in forest.madad()
#and a verticle line at the summar statistic in either forest.madad() or
#forest.madauni()
myforest <-
function (x, ci, plotci = TRUE, main = "Forest plot", xlab = NULL,
SumLine=SumLine, digits = 2L, snames = NULL, subset = NULL, pch = 15, cex = 1,
cipoly = NULL, polycol = NA, plotxaxis = TRUE, ...)
{
stopifnot(length(x) == dim(ci)[1], all(!is.na(c(ci, x))),
is.logical(plotci))
if (!is.null(snames)) {
stopifnot(length(snames) == length(x))
}
if (is.null(snames)) {
snames <- paste("Study", 1:length(x))
}
if (!is.null(subset)) {
stopifnot(length(subset) > 0, is.integer(subset))
}
if (is.null(subset)) {
subset <- 1:length(x)
}
if (!is.null(cipoly)) {
stopifnot(length(cipoly) == length(x))
cireg <- which(!cipoly)
cipoly <- which(cipoly)
}
if (is.null(cipoly)) {
cireg <- 1:length(x)
}
x <- x[subset]
lb <- ci[subset, 1]
ub <- ci[subset, 2]
snames <- snames[subset]
if (is.null(xlab)) {
xlab <- ""
}
N <- length(x)
plotrange <- max(ub) - min(lb)
if (plotci) {
xlim <- c(min(lb) - plotrange * 1.2, max(ub) + plotrange *
1.2)
}
else {
xlim <- c(min(lb) - plotrange * 1.2, max(ub) + plotrange *
0.4)
}
ylim <- c(0.5, N + 1)
plot(NA, NA, xlim = xlim, ylim = ylim, xlab = xlab, ylab = "",
yaxt = "n", xaxt = "n", xaxs = "i", bty = "n", main = main,
...)
abline(h = ylim[2], ...)
if(!is.null(SumLine)) abline(v = x[length(x)],lty=2, ...)
for (i in cireg) {
points(x[i], (N:1)[i], pch = pch)
arrows(lb[i], (N:1)[i], ub[i], angle = 90, code = 3,
length = 0.05, ...)
}
for (i in cipoly) {
polygon(x = c(lb[i], x[i], ub[i], x[i]), y = c((N:1)[i],
(N:1)[i] + 0.25, (N:1) [i], (N:1)[i] - 0.25), col = polycol)
}
text(x = xlim[1], N:1, labels = snames[1:N], pos = 4, cex = cex)
if (plotci) {
citext <- format(round(cbind(x, ci), digits = digits),
nsmall = digits)
citext <- paste(citext[, 1], " [", citext[, 2], ", ",
citext[, 3], "]", sep = "")
text(x = xlim[2], N:1, labels = citext, pos = 2, cex = cex)
}
if(plotxaxis){
axis(1, at = round(seq(from = min(lb), to = max(ub), length.out = 5),
digits), cex = cex)
}
return(invisible(NULL))
}
forest.madauni <-
function (x, log = TRUE, plotxaxis = TRUE,SumLine=NULL, snames=NULL, ...)
{
fit <- x
stopifnot(class(fit) == "madauni")
descr <- fit$descr
level <- fit$descr$level
summ <- summary(fit, level = level)
forest.x <- fit$theta
forest.ci <- switch(fit$type, DOR = descr$DOR$DOR.ci, negLR = descr$negLR$negLR.ci,
posLR = descr$posLR$posLR.ci)
forest.x <- c(forest.x, summ$CIcoef[1, 1])
forest.ci <- rbind(forest.ci, summ$CIcoef[1, 2:3])
if (!exists("snames")) {
snames <- descr$names
if (is.null(snames)) {
snames <- paste("Study", 1:descr$nobs)
}
snames <- c(snames, paste("Summary (", fit$method, ")",
sep = ""))
}
xlab = switch(fit$type, DOR = "diagnostic odds ratio", negLR = "negative likelihood ratio",
posLR = "positive likelihood ratio")
if (log) {
forest.x <- log(forest.x)
forest.ci <- log(forest.ci)
xlab <- paste("log", xlab)
}
myforest(x = forest.x, ci = forest.ci, snames = snames, SumLine=SumLine,
xlab = xlab, cipoly = c(rep(FALSE, descr$nobs), TRUE),
plotxaxis = plotxaxis,
...)
}
forest.madad <-
function (x, IncludeSummary=NULL,SumLine=NULL,type = "sens", log = FALSE, plotxaxis = TRUE, ...)
{
dat<-x$data
fitmod<-summary(reitsma(dat))$coefficients[3:4,c(1,5,6)]
sensEstimate<-fitmod[1,]
specEstimate<-1-fitmod[2,]
d <- x
stopifnot(class(d) == "madad")
stopifnot(type %in% c("DOR", "sens", "spec", "posLR", "negLR"))
if (type == "DOR") {
ci <- d$DOR$DOR.ci
x <- d$DOR$DOR
}
if (type == "sens") {
if(!is.null(IncludeSummary)) ci <- rbind(d$sens$sens.ci,sensEstimate[2:3])
if(!is.null(IncludeSummary)) x <- c(d$sens$sens,sensEstimate[1])
if(is.null(IncludeSummary)) ci <- d$sens$sens.ci
if(is.null(IncludeSummary)) x <- d$sens$sens
}
if (type == "spec") {
if(!is.null(IncludeSummary)) ci <- rbind(d$spec$spec.ci,specEstimate[c(3,2)])
if(!is.null(IncludeSummary)) x <- c(d$spec$spec,specEstimate[1])
if(is.null(IncludeSummary)) ci <- d$spec$spec.ci
if(is.null(IncludeSummary)) x <- d$spec$spec
}
if (type == "posLR") {
ci <- d$posLR$posLR.ci
x <- d$posLR$posLR
}
if (type == "negLR") {
ci <- d$negLR$negLR.ci
x <- d$negLR$negLR
}
if (log) {
x <- log(x)
ci <- log(ci)
}
if(is.null(IncludeSummary)) polySum<-rep(FALSE, length(x))
if(!is.null(IncludeSummary)) polySum<-c(rep(FALSE, length(x)-1),TRUE)
myforest(x, ci, plotxaxis = plotxaxis, cipoly=polySum,SumLine=SumLine, ...)
}
And then I created the forest plot with a call such as:
forest.madad(madad(SingleSmall),
SumLine=TRUE,
type="sens",
plotxaxis = FALSE,
IncludeSummary=TRUE,
snames=c(paste(c(rep("A",3),rep("E",16)),SingleCutpoint$Author),"Summary Estimate"),
main="Sensitivity")
axis(1, at = seq(0 ,1, by = .25))
Where I indicate I don't want the function to plot the x axis, and then I call axis afterwards. Note I'm also requesting a summary statistic and a line at the summary estimate. You can ignore those arguments if you'd like.
Upvotes: 2
Views: 1546
Reputation: 37879
Well apparently you cannot change those (or at least not immediately).
If you have a look at the source code of the forestmada
function (this is the function that gets called when you use the forest
function) you will see that you cannot. Run getAnywhere(forest.madad)
to view the forest
function and mada::forestmada
to view forestmada
function. Or just check ?forest
.
So, this is what gets called inside the forestmada
function:
if (plotci) {
xlim <- c(min(lb) - plotrange * 1.2, max(ub) + plotrange *
1.2)
}
else {
xlim <- c(min(lb) - plotrange * 1.2, max(ub) + plotrange *
0.4)
}
So even if you include any xlim
or ylim
arguments these will end up getting overwritten by the above.
Exactly the same with the at
argument:
axis(1, at = round(seq(from = min(lb), to = max(ub), length.out = 5),
digits), cex = cex)
The only way would be to copy the source code and try to alter it yourself, but this will need a lot of work.
I tried myself a bit but apparently all of these arguments are used in a specific way later on, so whenever I changed anything I got a lot warnings and errors, and apparently this is why the author decided to have them calculated in a specific way.
Hope this helps.
Upvotes: 1