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

4

18

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?

Despotism answered 11/6, 2013 at 9:51 Comment(2)
Maybe working with layout() (e.g., here) in combination with xpd=TRUE could work.Last
There was a command that tells you the dimension of the plotting area.Despotism
A
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

Agamogenesis answered 11/6, 2013 at 22:0 Comment(11)
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.Despotism
@Sven, if you set type='fig' instead of the default 'plt' then all the labels, ticks, etc. will be inside the rectangle specified.Agamogenesis
@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).Agamogenesis
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().Despotism
@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.Agamogenesis
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.Despotism
take a look at Hmisc::subplot()Shiny
@ivan866, the subplot function in the Hmisc package is a copy of the one in the TeachingDemos package.Agamogenesis
@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).Agamogenesis
@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' figureShiny
@ivan866, no the methods used by subplot are not compatible with multiple plots using mfrow and friends.Agamogenesis
S
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

Stagecoach answered 11/6, 2013 at 10:54 Comment(1)
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).Cerussite
A
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

Agamogenesis answered 11/6, 2013 at 22:0 Comment(11)
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.Despotism
@Sven, if you set type='fig' instead of the default 'plt' then all the labels, ticks, etc. will be inside the rectangle specified.Agamogenesis
@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).Agamogenesis
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().Despotism
@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.Agamogenesis
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.Despotism
take a look at Hmisc::subplot()Shiny
@ivan866, the subplot function in the Hmisc package is a copy of the one in the TeachingDemos package.Agamogenesis
@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).Agamogenesis
@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' figureShiny
@ivan866, no the methods used by subplot are not compatible with multiple plots using mfrow and friends.Agamogenesis
B
7

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
Bianco answered 11/6, 2013 at 12:34 Comment(0)
C
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))
Cutaneous answered 9/3, 2016 at 6:49 Comment(2)
the link is broken; 2022.08Shiny
@Shiny thanks for noticing. I have put a notice. It is not working anymore, but I put it to quote where I found it. Nevertheless, the example should work.Cutaneous

© 2022 - 2024 — McMap. All rights reserved.