How to add an inset (subplot) to "topright" of an R plot?

15,644

Solution 1

Look at the subplot function in the TeachingDemos package. It may make what you are trying to do easier.

Here is an example:

library(TeachingDemos)
d0 <- data.frame(x = rnorm(150, sd=5), y = rnorm(150, sd=5))
d0_inset <- data.frame(x = rnorm(1500, sd=5), y = rnorm(1500, sd=5))

plot(d0)
subplot( 
  plot(d0_inset, col=2, pch='.', mgp=c(1,0.4,0),
    xlab='', ylab='', cex.axis=0.5), 
  x=grconvertX(c(0.75,1), from='npc'),
  y=grconvertY(c(0,0.25), from='npc'),
  type='fig', pars=list( mar=c(1.5,1.5,0,0)+0.1) )

enter image description here

Solution 2

You can use par("usr") to get the limits of the plot, in user coordinates, and grconvert[XY] to convert them to normalized device coordinates (NDC, between 0 and 1), before using them with par(fig=...).

plot(d0)
u <- par("usr")
v <- c(
  grconvertX(u[1:2], "user", "ndc"),
  grconvertY(u[3:4], "user", "ndc")
)
v <- c( (v[1]+v[2])/2, v[2], (v[3]+v[4])/2, v[4] )
par( fig=v, new=TRUE, mar=c(0,0,0,0) )
plot(d0_inset, axes=FALSE, xlab="", ylab="")
box()

Topright inset

Solution 3

use par("plt") to find out the area of the plotting region (seems to be similar to vincents answer). Strangely: fig sets the size of the plotting area of the inset. So, if show the axis, the size of the inset will be larger than your 25%.

# datasets
d0 <- data.frame(x = rnorm(150, sd=5), y = rnorm(150, sd=5))
d0_inset <- data.frame(x = rnorm(1500, sd=5), y = rnorm(1500, sd=5))

# ranges
xlim <- range(d0$x)
ylim <- range(d0$y)

# plot
plot(d0)

# calculate position of inset
plotdim <- par("plt")
xleft    = plotdim[2] - (plotdim[2] - plotdim[1]) * 0.25
xright   = plotdim[2]  #
ybottom  = plotdim[4] - (plotdim[4] - plotdim[3]) * 0.25  #
ytop     = plotdim[4]  #

# set position for inset
par(
  fig = c(xleft, xright, ybottom, ytop)
  , mar=c(0,0,0,0)
  , new=TRUE
  )

# add inset
plot(d0_inset, col=2) # inset bottomright

Solution 4

For me worked the example from the oce library: http://finzi.psych.upenn.edu/library/oce/html/plotInset.html

See the example:

library(oce)

## power law in linear and log form
x <- 1:10
y <- x^2
plot(x, y, log='xy',type='l')
plotInset(3, 1, 10, 8,
          expr=plot(x,y,type='l',cex.axis=3/4,mgp=c(3/2,1/2,0)),
          mar=c(2.5,2.5,1,1))

## CTD data with location
data(ctd) 
plot(ctd, which="TS")
plotInset(29.9, 2.7, 31, 10,
          expr=plot(ctd, which='map',
          coastline="coastlineWorld",
          span=5000, mar=NULL, cex.axis=3/4))
Share:
15,644
R_User
Author by

R_User

I really like programming in R, although the documentation is rarly helpful,...

Updated on June 12, 2022

Comments

  • R_User
    R_User about 2 years

    I'd like to have an inset within a plot that makes up 25% of the width and height of the plotting area (area where the graphs are).

    I tried:

    # datasets
    d0 <- data.frame(x = rnorm(150, sd=5), y = rnorm(150, sd=5))
    d0_inset <- data.frame(x = rnorm(1500, sd=5), y = rnorm(1500, sd=5))
    
    # ranges
    xlim <- range(d0$x)
    ylim <- range(d0$y)
    
    # plot
    plot(d0)
    
    # add inset
    par(fig = c(.75, 1, .75, 1), mar=c(0,0,0,0), new=TRUE)
    plot(d0_inset, col=2) # inset bottomright
    

    This puts the inset to absolute topright and also uses 25% of the device-width. How can I change it to the coordinates and width of the area where the graphs are?

  • R_User
    R_User about 11 years
    Even with subplot It is very hard to control the size of the inset: The rectangle defined by x, y, size, vadj, and hadj will be used as the plotting area of the new plot. Any tick marks, axis labels, main and sub titles will be outside of this rectangle.
  • Greg Snow
    Greg Snow about 11 years
    @Sven, if you set type='fig' instead of the default 'plt' then all the labels, ticks, etc. will be inside the rectangle specified.
  • Greg Snow
    Greg Snow about 11 years
    @Sven, I added an example using subplot above. This assumes that you wanted it in the bottom right as in your comment (easy to change to other corners) and that you wanted it to take up 1/4 linear space (1/16th of the area, also easy to change).
  • R_User
    R_User about 11 years
    If you use split.screen() to have more than one plot, it seems that subplot uses the with/height added up from all plots. Thus, you need to adjust the coordinates in grconvertX().
  • Greg Snow
    Greg Snow about 11 years
    @Sven, I have never had much luck getting split.screen to play nicely, so I have not used it for years. Mixing any of the different tools for working with graphics takes a certain amount of care.
  • R_User
    R_User about 11 years
    split.screen() is nice, but it can take a while to find out the reasons for some strange behaviour. Thats why I posted the hint above. Another tip would be: use par() before switching the screens.
  • Andy Barbour
    Andy Barbour over 10 years
    Nice example. I would note that one could add rect(u[2], u[4], (u[1]+u[2])/2, (u[3]+u[4])/2, col="white") before the second par command to fill the inset area with white (and draw a black border, eliminating the need for box).
  • ivan866
    ivan866 about 2 years
    take a look at Hmisc::subplot()
  • Greg Snow
    Greg Snow about 2 years
    @ivan866, the subplot function in the Hmisc package is a copy of the one in the TeachingDemos package.
  • Greg Snow
    Greg Snow about 2 years
    @ivan866, actually the function in Hmisc is a copy of an older version, so the TeachingDemos version is preferred (more up to date, more functionality).