17

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?

Henrik
  • 65,555
  • 14
  • 143
  • 159
R_User
  • 10,682
  • 25
  • 79
  • 120
  • Maybe working with `layout()` (e.g., [here](http://www.statmethods.net/advgraphs/layout.html)) in combination with `xpd=TRUE` could work. – Henrik Jun 11 '13 at 10:18
  • There was a command that tells you the dimension of the plotting area. – R_User Jun 11 '13 at 10:39

4 Answers4

15

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

Vincent Zoonekynd
  • 31,893
  • 5
  • 69
  • 78
  • 3
    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`). – Andy Barbour Mar 02 '14 at 23:26
10

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

PeterK
  • 1,185
  • 1
  • 9
  • 23
Greg Snow
  • 48,497
  • 6
  • 83
  • 110
  • 1
    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.` – R_User Jun 12 '13 at 06:53
  • 1
    @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 Jun 12 '13 at 15:54
  • 1
    @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). – Greg Snow Jun 12 '13 at 17:02
  • 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()`. – R_User Jun 13 '13 at 08:39
  • @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. – Greg Snow Jun 13 '13 at 15:40
  • `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. – R_User Jun 14 '13 at 06:11
  • take a look at `Hmisc::subplot()` – ivan866 Jun 09 '22 at 01:04
  • @ivan866, the subplot function in the Hmisc package is a copy of the one in the TeachingDemos package. – Greg Snow Jun 09 '22 at 17:07
  • 1
    @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). – Greg Snow Jun 09 '22 at 17:16
  • @GregSnow does it work with `mfrow` / `mfg` par()? the manual page for subplot says it doesn't; i tried it and all the subplots except the first one called get rendered on the very last row/column of the 'mfrow' figure – ivan866 Aug 18 '22 at 20:30
  • @ivan866, no the methods used by `subplot` are not compatible with multiple plots using `mfrow` and friends. – Greg Snow Aug 19 '22 at 00:32
6

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
John Garreth
  • 1,112
  • 2
  • 10
  • 17
1

For me worked the example from the oce library: (update, as 08-2022 the link does not work, but the pasted example should work)

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))
Antoni
  • 2,542
  • 20
  • 21