29

enter image description here

As the sketch map above, you can imagine the upper one is a plot of parameter space, for example, the mean and variance for normal distribution, and the lower one is the corresponding density plot. Any hints for doing this? Thank you~

UPDATE: As an enhancement, can I build a interactive version for this? Say, whenever I mouse-over a point, R shows the corresponding plot beneath.

Ziyuan
  • 4,215
  • 6
  • 48
  • 77
  • 1
    Nice question and illustration! – poplitea Dec 05 '12 at 01:11
  • 1
    The `plotrix` package has a `zoomInPlot` function though your question doesn't appear to be answered by this function, the source code may be useful. – Tyler Rinker Dec 05 '12 at 01:15
  • @TylerRinker, it's not actually a `zoomInPlot`, because the "zoomed" plot is not a part of the original plot, it's generated according to the point I select (or a fixed point, if I cannot make an interactive version) – Ziyuan Dec 05 '12 at 01:30
  • 2
    @ziyuang I posted as a comment for this reason. Some assembly required. I'm guessing the `zoomInPlot` plot is actually two different plots made to look like a zoom. You'll have to tear apart the `zoomInPlot` code and can create the plot you're after. As far as interactive this will require even more digging. – Tyler Rinker Dec 05 '12 at 01:35
  • I love @Julius R-only solution, but would personally prefer an html-version such as: http://vis.supstat.com/2012/11/contour-plots-with-d3-and-r/ – Dieter Menne Dec 05 '12 at 17:06
  • @DieterMenne, yes I also read this before I posted the question. – Ziyuan Dec 05 '12 at 17:51

2 Answers2

15

Here is an interactive version, you can click on a point and then corresponding density plot appears. Mainly used ?identify and as @Tyler suggested ?zoomInPlot.

Some more details on how it works: rxlim and rylim defined at the very beginning is the size of rectangle which surrounds the selected point, so one might want to change the factor /20. Possibility of multiple clicks is nontrivial: identify() detects clicks only in the "recent" plot, i.e.

par(mfrow = c(1,2))
plot(1:10) # 1
plot(1:10) # 2
identifyPch(1:10)

detects clicks only in the plot #2 (here identifyPch() is from ?identify). For this issue par(mfg=c(1, 1)) was used:

mfg

A numerical vector of the form c(i, j) where i and j indicate which figure in an array of figures is to be drawn next (if setting) or is being drawn (if enquiring). The array must already have been set by mfcol or mfrow.

enter image description here

zoom <- function (x, y, xlim, ylim, xd, yd) 
{
  rxlim <- x + c(-1, 1) * (diff(range(xd))/20)
  rylim <- y + c(-1, 1) * (diff(range(yd))/20)
  par(mfrow = c(1, 2))
  plot(xd, yd, xlab = "mean", ylab = "sd")
  xext <- yext <- rxext <- ryext <- 0
  if (par("xaxs") == "r") {
    xext <- diff(xlim) * 0.04
    rxext <- diff(rxlim) * 0.04
  }
  if (par("yaxs") == "r") {
    yext <- diff(ylim) * 0.04
    ryext <- diff(rylim) * 0.04
  }
  rect(rxlim[1] - rxext, rylim[1] - ryext, rxlim[2] + rxext, 
       rylim[2] + ryext)
  xylim <- par("usr")
  xypin <- par("pin")
  rxi0 <- xypin[1] * (xylim[2] - (rxlim[1] - rxext))/diff(xylim[1:2])
  rxi1 <- xypin[1] * (xylim[2] - (rxlim[2] + rxext))/diff(xylim[1:2])
  y01i <- xypin[2] * (xylim[4] - (rylim[2] + ryext))/diff(xylim[3:4])
  y02i <- xypin[2] * ((rylim[1] - ryext) - xylim[3])/diff(xylim[3:4])
  mu <- x
  curve(dnorm(x, mean = mu, sd = y), from = -4 * y + mu, to = 4 * y + mu, 
        xlab = paste("mean:", round(mu, 2), ", sd: ", round(y, 2)), ylab = "")
  xypin <- par("pin")
  par(xpd = NA)
  xylim <- par("usr")
  xymai <- par("mai")
  x0 <- xylim[1] - diff(xylim[1:2]) * (xymai[2] + xymai[4] + 
                                         rxi0)/xypin[1]
  x1 <- xylim[1] - diff(xylim[1:2]) * (xymai[2] + xymai[4] + 
                                         rxi1)/xypin[1]
  y01 <- xylim[4] - diff(xylim[3:4]) * y01i/xypin[2]
  y02 <- xylim[3] + diff(xylim[3:4]) * y02i/xypin[2]
  par(xpd = TRUE)
  xend <- xylim[1] - diff(xylim[1:2]) * xymai[2]/(2 * xypin[1])
  xprop0 <- (xylim[1] - xend)/(xylim[1] - x0)
  xprop1 <- (xylim[2] - xend)/(xylim[2] - x1)
  par(xpd = NA)
  segments(c(x0, x0, x1, x1), 
           c(y01, y02, y01, y02), 
           c(xend, xend, xend, xend), 
           c(xylim[4] - (xylim[4] - y01) * xprop0, 
             xylim[3] + (y02 - xylim[3]) * xprop0, 
             xylim[4] - (xylim[4] - y01) * xprop1, 
             xylim[3] + (y02 - xylim[3]) * xprop1))
  par(mfg = c(1, 1))
  plot(xd, yd, xlab = "mean", ylab = "sd")
}

ident <- function(x, y, ...)
{
  ans <- identify(x, y, n = 1, plot = FALSE, ...)
  if(length(ans)) {
    zoom(x[ans], y[ans], range(x), range(y), x, y)
    points(x[ans], y[ans], pch = 19)
    ident(x, y)
  }
}

x <- rnorm(10)
y <- rnorm(10, mean = 5)
par(mfrow = c(1, 2))
plot(x, y, xlab = "mean", ylab = "sd")
ident(x, y)
Julius Vainora
  • 47,421
  • 9
  • 90
  • 102
  • +1. Works now, after your revision. (I had reported error, but have deleted the comment) – Dieter Menne Dec 05 '12 at 17:03
  • @ziyuang, I have just edited the function. There was one problem - you could not click on the same point more than one time.. also the code is shorter now. – Julius Vainora Dec 05 '12 at 22:59
  • +1 - that is a really amazing litle function. I think I am going to use it (with credit of course) to demonstrate to some work colleagues why R is an awesome graphical tool. – thelatemail Dec 05 '12 at 23:15
  • @Julius, why do I need to draw the left plot 2 times (`plot(xd, yd, xlab = "mean", ylab = "sd")`)? – Ziyuan Dec 08 '12 at 02:18
  • 1
    @ziyuang, try to omit either of these lines and see what happens: the first one refreshes the window, prepares for new lines. The second one is for the problem where I mentioned `par(mfg=c(1, 1))` in my answer: if you omit this line, you no longer can click on the points in the first graph, i.e. you plot once again as to "select" the right part of the window for `ident()`. – Julius Vainora Dec 08 '12 at 11:09
  • @Julius, can you further explain how to convert the rectangle coordinates from plot on left to plot on right? – Ziyuan Dec 09 '12 at 16:20
  • @Julius, particularly, what is `rxi0`, `rxi1`, `y01i` and `y02i`? – Ziyuan Dec 09 '12 at 17:16
  • @ziyuang, this part of the code is taken from `zoomInPlot()` from `plotrix`. I will try to explain it for you better tomorrow, for now see `?par` and `pin`, `usr`, `mai` that are used there. – Julius Vainora Dec 09 '12 at 20:52
  • 1
    @ziyuang, unfortunately I still don't have an answer for you, anyway there seems to be too many details in one place. But of course it is possible to figure it out piece by piece, see `xpd` in `?par` if you haven't yet, and you might find useful functions `grconvertX`, `grconvertY`, also `cnvrt.coords` from `TeachingDemos` and http://stackoverflow.com/questions/9985013/how-do-you-draw-a-line-across-a-multiple-figure-environment-in-r – Julius Vainora Dec 10 '12 at 20:56
9

iplots package comes pretty close to this, though there's technically no 'zooming'. iplots has interactive linked plots implemented with a Java GUI. You can select points on one plot and the same points become highlighted in other plots. The website for the package is here.

library(iplots)
data(Cars93)
iplot(Cars93$Horsepower, Cars93$MPG.city)
ihist(Cars93$Horsepower)

Here, I've selected some points in the left scatterplot, turning them red, and they also become highlighted in the right histogram (histogram is the closest they have to a density plot).

enter image description here

MattBagg
  • 10,268
  • 3
  • 40
  • 47
  • Thanks for your reply. This also worth investigation. – Ziyuan Dec 05 '12 at 17:50
  • No prob -- I was a little embarrassed to post a package name and four lines of code when I was pretty certain someone would take up the challenge to code an awesome function from scratch. But iplots is a great tool that deserves wider use. – MattBagg Dec 05 '12 at 18:33