Search code examples
rscatter-plotdensity-plot

scatterplot matrix with overlaid contourplot of bivariate density gets stuck in lattice r


I'm using lattice to create a scatterplot matrix with an overlaid contour plot of a bivariate kernel density function. The following code is giving a strange behavior, where the contour plots are only drawn partially, starting from the bottom and getting cut off at the top. How much gets drawn depends on the value of n in MASS::kde2d.

library(lattice)
library(MASS)

splom(iris, upper.panel = function(x, y, ...) {
  if(is.numeric(x) & is.numeric(y)){

    # calculate bivariate kernel density 
    f1 <- kde2d(x = x, y = y, n = 20) #, lims = c(0, 10 ,0, 10))

    f <- data.frame(x = f1$x, y = rep(f1$y, each = length(f1$x)), 
                    z = as.vector(f1$z))

    panel.contourplot(x = f$x, y = f$y, z = f$z,  
                      contour = TRUE, ...)
    }
    panel.xyplot(x, y, ...)
  })

Poking around and printing summaries of the intermediate values seems to indicate that the functions are behaving as expected and giving values in the expected range. Any idea what's going on?


Solution

  • Okay, so it turns out the ... was passing the old subscripts argument to the new panel function. Since the iris data had 25*25 = 125 subscripts, the panel.contourplot was only considering the first 125 elements of its x, y, and z arguments. The following takes care of that.

    splom(iris, upper.panel = function(x, y, subscripts, ...) {
      if(is.numeric(x) & is.numeric(y)){
    
        # calculate bivariate kernel density 
        v <- current.panel.limits() # allows full bleed by setting limits explicitly
        f1 <- kde2d(x = x, y = y, n = 50, lims = c(v$xlim, v$ylim))
    
        f <- data.frame(x = f1$x, y = rep(f1$y, each = length(f1$x)), 
                        z = as.vector(f1$z))
        panel.contourplot(f$x, f$y, f$z, contour = TRUE, 
                          subscripts = 1:dim(f)[1], ...)
      }
      panel.xyplot(x, y, subscripts = subscripts, ...)
    })
    

    While we're at it, I threw in some code to make the levelplot take up the entire panel instead of having nasty white borders around the edges. Much better!