Aligning text with a variable size graphic in R

I am very new to using the power of R to generate graphical output.

I use the forest () function in the metaphor package to create forest plots for my meta-analyzes. I am generating multiple plots using a loop and then saving them via png ().

for (i in 1:ncol(df)-2)){
  dat <- escalc(measure="COR", ri=ri, ni=ni, data=df) # Calcultes Effect Size
  res_re <- rma.uni(yi, vi, data=dat, method="DL", slab=paste(author)) # Output of meta-analysis

  png(filename=path, width=8.27, height=11.69, units ="in", res = 210)
  forest(res_re, showweight = T, addfit= T, cex = .9) 
  text(-1.6, 18, "Author(s) (Year)", pos=4)     
  text( 1.6, 18, "Correlation [95% CI]", pos=2)
  dev.off()
}

      

This works great if the plot size is equal. However, each iteration of the loop combines a different amount of research in the forest. Thus, the text elements are not in the right place, and the forest area with many explorations looks a little strange. I have two questions:

  • How can I auto-align "Author (s) (Year)" and "Correlation [95% CI]" automatically to resize the forest parcel so that the headers are above the top line of the forest table?
  • How can I scale the size of the forest patch so that the width and size of the text elements are the same for all plots, and a new line (height change) will be added for each additional exploration?

Each forest area should look like this:

enter image description here

+2


source to share


2 answers


Here's what you need to do to get this to work:

  • I would fix xlim

    on the graphs to have room to place the "Author (s) (Year)" and "Correlation [95% CI]" headers. After you've created the forest lot, take a look at par()$usr[1:2]

    . Use these values ​​as a starting point for customization xlim

    to fit all of your stories. Then use these two values ​​for two calls text()

    .

  • There are k

    lines in every graph . Headings should be two rows higher. So use text(<first xlim value>, res_re$k+2, "Author(s) (Year)", pos=4)

    andtext(<second xlim value>, res_re$k+2, "Correlation [95% CI]", pos=2)

  • Set cex

    in text()

    to the same value you specified when calling forest()

    .

  • The last part is tricky. You fixed cex

    , so the size of the text elements should be the same in different areas. But if there is more research, then the lines k

    are packed into less space, so they become less split. If I understand you correctly, you want the row spacing to be equal between the graphs by adjusting the actual height of the graph. Essentially, this will require doing it height

    in a png()

    function call k

    . For each additional exploration, an additional amount has to be added in height

    to keep the line spacing constant, so something along the lines height=<some factor> + res_re$k * <some factor>

    . But the increase in height as a functionk

    can also be non-linear. Getting this right will take a lot of effort and error. There might be a clever way to figure it out programmatically (digging in ?par

    and out, maybe ?strheight

    ).



So to make it easier for others to call, the last part of your question boils down to this: How do I adjust the height

build device value so that the absolute line spacing in plot(1:10)

and plot(1:20)

stays the same? This is an interesting question in itself, so I'm going to post this as a separate question.

+1


source


ad 4: In Wolfgang's question ( Constant absolute line spacing in R-plots ) you will find how to make the height of the plot depending on the number of lines in it.

For forest()

it will be slightly different, since this function internally changes values par("mar")

.

However, if you are setting the fields to zero, you need to include the attribute yaxs="i"

in the forest()

-function so that the y-axis is segmented for the data range and nothing else. The device must be set to height (length(ma$yi)+4.5)*fact*res

with fact

both inches / line (see below) and res

pixels / inches (resolution).



4.5

depends on whether meta-analysis remained in the model addfit=T

and intercept=T

(in this case, forest()

internally sets ylim <- c(-1.5, k + 3)

). Otherwise, you will have to use 2.5

(than ylim <- c(0.5, k + 3)

).

If you feel like using fields, you would do the following (I edited the next part after I found out some kind of error):

res <- 'your desired resolution' # pixels per inch
fact <- par("mai")[1]/par("mar")[1] # calculate inches per line
### this following part is copied from inside the forest()-function.
# forest() modifies the margin internally in the same way.
par.mar <- par("mar")
par.mar.adj <- par.mar - c(0, 3, 1, 1)
par.mar.adj[par.mar.adj < 0] <- 0
###
ylim <- c(-1.5, length(ma$yi)+3) # see above
ylim.abs <- abs(ylim[1])+abs(ylim[2])-length(ma$yi) # calculate absolute distance of ylim-argument
pixel.bottom <- (par.mar.adj[1])*fact*res # calculate pixels to add to bottom and top based on the margin that is internally used by forest().
pixel.top <- (par.mar.adj[3])*fact*res
png(filename='path', 
    width='something meaningful', 
    height=((length(ma$yi)+ylim.abs)*fact*res) + pixel.bottom + pixel.top, 
    res=res)
par(mar=par.mar) # make sure that inside the new device the margins you want to define are actually used.
forest(res_re, showweight = T, addfit= T, cex = .9, yaxs="i")
...
dev.off()

      

+1


source







All Articles