Raw File
splom.R


### Copyright 2001  Deepayan Sarkar <deepayan@stat.wisc.edu>
###
### This file is part of the lattice library for R.
### It is made available under the terms of the GNU General Public
### License, version 2, or at your option, any later version,
### incorporated herein by reference.
###
### This program is distributed in the hope that it will be
### useful, but WITHOUT ANY WARRANTY; without even the implied
### warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
### PURPOSE.  See the GNU General Public License for more
### details.
###
### You should have received a copy of the GNU General Public
### License along with this program; if not, write to the Free
### Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
### MA 02111-1307, USA





prepanel.default.splom <-
    function(x, y, type, ...)
{
    list(xlim = c(0,1),
         ylim = c(0,1),
         dx = 1,
         dy = 1)
}




panel.splom <-
    function(...)
    panel.xyplot(...)





panel.pairs <-
    function(z, panel = "panel.splom", groups = NULL,
             panel.subscripts,
             subscripts,
             pscales = 5,
             ...)
{
    panel <- 
        if (is.function(panel)) panel 
        else if (is.character(panel)) get(panel)
        else eval(panel)

    axis.line <- trellis.par.get("axis.line")
    axis.text <- trellis.par.get("axis.text")
    n.var <- ncol(z)

    if(n.var>0) {
        ## there must be a better way to do the foll:
        lim <- list(1:n.var)
        for(i in 1:n.var) {
            lim[[i]] <- extend.limits(range(as.numeric(z[,i]), na.rm = TRUE))
        }
        ## should be further complicated by allowing for customization by
        ## prepanel functions --- prepanel(z[i], z[j]) etc
    }
    ## maybe (ideally) this should be affected by scales

    draw <- is.numeric(pscales) && pscales!=0 # whether axes to be drawn

    splom.layout <- grid.layout(nrow=n.var, ncol=n.var)

    if(n.var > 0 && any(subscripts)) {

        push.viewport(viewport(layout=splom.layout))

        for(i in 1:n.var)
            for(j in 1:n.var)
            {
                push.viewport(viewport(layout.pos.row = n.var-i+1,
                                       layout.pos.col = j,
                                       clip = TRUE,
                                       ##gp = gpar(fontsize = fontsize.small),
                                       xscale = lim[[j]],
                                       yscale = lim[[i]]))

                if(i == j)
                {
                    if (!is.null(colnames(z)))
                        grid.text(colnames(z)[i])
                    ##gp = gpar(fontsize = 10))
                    if (draw) {
                        ## plot axes

                        if (is.factor(z[,i])) {
                            axls <- 1:nlevels(z[,i])
                            nal <- length(axls)/2+.5

                            for(tt in seq(along=axls)) {
                                if(tt <= nal) {
                                    
                                    grid.lines(y = unit(rep(axls[tt],2), "native"),
                                               x = unit(c(1,1),"npc") - unit(c(0,.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = levels(z[,i])[tt],
                                              x = unit(1,"npc") - unit(.5, "lines"),
                                              y = unit(axls[tt], "native"),
                                              just = c("right", "centre"))
                                    
                                    grid.lines(x = unit(rep(axls[tt],2), "native"),
                                               y = unit(c(0,.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = levels(z[,i])[tt],
                                              rot = 90,
                                              y = unit(0.5, "lines"),
                                              x = unit(axls[tt], "native"),
                                              just = c("left", "centre"))
                                    
                                }
                                if(tt >=nal) {
                                    
                                    grid.lines(y = unit(rep(axls[tt],2), "native"),
                                               x = unit(c(0,0.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = levels(z[,i])[tt],
                                              x = unit(0.5, "lines"),
                                              y = unit(axls[tt], "native"),
                                              just = c("left", "centre"))
                                    
                                    grid.lines(x = unit(rep(axls[tt],2), "native"),
                                               y = unit(c(1,1),"npc") - unit(c(0,.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = levels(z[,i])[tt], rot = 90,
                                              y = unit(1,"npc") - unit(.5, "lines"),
                                              x = unit(axls[tt], "native"),
                                              just = c("right", "centre"))
                                    
                                }
                                
                            }
                            
                        }
                        else {
                        
                            axls <-
                                if (is.list(pscales) && !is.null(pscales[[i]]$at))
                                    pscales[[i]]$at
                                else
                                    lpretty(lim[[i]], n = pscales)

                            labels <-
                                if (is.list(pscales) && !is.null(pscales[[i]]$lab))
                                    pscales[[i]]$lab
                            ## should be rendered like factors ?
                                else
                                    as.character(axls)

                            axid <- axls>lim[[i]][1] & axls <lim[[i]][2]
                            axls <- axls[axid]
                            labels <- labels[axid]
                            nal <- length(axls)/2+.5

                            for(tt in seq(along=axls)) {
                                if(tt <= nal) {
                                    
                                    grid.lines(y = unit(rep(axls[tt],2), "native"),
                                               x = unit(c(1,1),"npc") - unit(c(0,.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = as.character(axls[tt]),
                                              x = unit(1,"npc") - unit(.5, "lines"),
                                              y = unit(axls[tt], "native"),
                                              just = c("right", "centre"))
                                    
                                    grid.lines(x = unit(rep(axls[tt],2), "native"),
                                               y = unit(c(0,.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = as.character(axls[tt]),
                                              y = unit(0.5, "lines"),
                                              x = unit(axls[tt], "native"),
                                              just = c("centre", "bottom"))
                                    
                                }
                                if(tt >=nal) {
                                    
                                    grid.lines(y = unit(rep(axls[tt],2), "native"),
                                               x = unit(c(0,0.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = as.character(axls[tt]),
                                              x = unit(0.5, "lines"),
                                              y = unit(axls[tt], "native"),
                                              just = c("left", "centre"))
                                    
                                    grid.lines(x = unit(rep(axls[tt],2), "native"),
                                               y = unit(c(1,1),"npc") - unit(c(0,.25), "lines"),
                                               gp = gpar(col = axis.line$col))
                                    
                                    grid.text(label = as.character(axls[tt]),
                                              y = unit(1,"npc") - unit(.5, "lines"),
                                              x = unit(axls[tt], "native"),
                                              just = c("centre", "top"))
                                    
                                }
                                
                            }
                        }    
                    }

                    grid.rect()

                }
                else
                {
                    if(!panel.subscripts)
                        panel(x=as.numeric(z[subscripts, j]),
                              y=as.numeric(z[subscripts, i]), ...)

                    else panel(x=as.numeric(z[subscripts, j]),
                               y=as.numeric(z[subscripts, i]),
                               groups = groups,
                               subscripts = subscripts, ...)

                    grid.rect()
                }
                pop.viewport()
            }
        pop.viewport()
    }
}




splom <-
    function(formula,
             data = parent.frame(),
             aspect = 1,
             between = list(x = 0.5, y = 0.5),
             layout = NULL,
             panel = "panel.splom",
             prepanel = NULL,
             scales = list(),
             strip = TRUE,
             groups = NULL,
             xlab = "Scatter Plot Matrix",
             xlim,
             ylab = NULL,
             ylim,
             superpanel = "panel.pairs",
             pscales = 5,
             varnames,
             ...,
             subscripts = !is.null(groups),
             subset = TRUE)
{

    ## dots <- eval(substitute(list(...)), data, parent.frame())
    dots <- list(...)

    if (!is.function(panel)) panel <- eval(panel)
    if (!is.function(strip)) strip <- eval(strip)

    prepanel <-
        if (is.function(prepanel)) prepanel 
        else if (is.character(prepanel)) get(prepanel)
        else eval(prepanel)

    ## Step 1: Evaluate x, y, etc. and do some preprocessing
    


    formname <- deparse(substitute(formula))
    formula <- eval(substitute(formula), data, parent.frame())

    form <-
        if (inherits(formula, "formula"))
            latticeParseFormula(formula, data)
        else 
            list(left = NULL,
                 right = as.data.frame(formula),
                 condition = NULL,
                 left.name = "",
                 right.name = formname)


    ##form <- latticeParseFormula(formula, data)

    cond <- form$condition


    number.of.cond <- length(cond)
    x <- as.data.frame(form$right)
    if (number.of.cond == 0) {
        strip <- FALSE
        cond <- list(as.factor(rep(1, nrow(x))))
        layout <- c(1,1,1)
        number.of.cond <- 1
    }
    if (!missing(varnames)) colnames(x) <-
        eval(substitute(varnames), data, parent.frame())
    
    groups <- eval(substitute(groups), data, parent.frame())
    subset <- eval(substitute(subset), data, parent.frame())
    if ("subscripts" %in% names(formals(eval(panel)))) subscripts <- TRUE
    subscr <- seq(along=x[,1])
    x <- x[subset,, drop = TRUE]
    subscr <- subscr[subset, drop = TRUE]
    

    ## create a skeleton trellis object with the
    ## less complicated components:

    foo <- do.call("trellis.skeleton",
                   c(list(aspect = aspect,
                          between = between,
                          panel = superpanel,
                          strip = strip,
                          xlab = xlab,
                          ylab = ylab), dots))

    dots <- foo$dots # arguments not processed by trellis.skeleton
    foo <- foo$foo
    foo$call <- match.call()
    foo$fontsize.normal <- 10
    foo$fontsize.small <- 8

    ## This is for cases like xlab/ylab = list(cex=2)
    if (is.list(foo$xlab) && !is.characterOrExpression(foo$xlab$label))
        foo$xlab$label <- "Scatter Plot Matrix"
    if (is.list(foo$ylab) && !is.characterOrExpression(foo$ylab$label))
        foo$ylab <- NULL

    ## Step 2: Compute scales.common (leaving out limits for now)

    ## It is not very clear exactly what effect scales is supposed
    ## to have. Not much in Trellis (probably), but there are certain
    ## components which are definitely relevant, and certail others
    ## (like log) which can be used in innovative ways. However, I'm
    ## postponing all that to later, if at all,and for now TOTALLY
    ## ignoring scales
    
    ##scales <- eval(substitute(scales), data, parent.frame())
    ##if (is.character(scales)) scales <- list(relation = scales)
    scales <- list(relation = "same", draw = FALSE)
    foo <- c(foo, 
             do.call("construct.scales", scales))


    ## Step 3: Decide if limits were specified in call:

    have.xlim <- !missing(xlim)
    if (!is.null(foo$x.scales$limit)) {
        have.xlim <- TRUE
        xlim <- foo$x.scales$limit
    }
    have.ylim <- !missing(ylim)
    if (!is.null(foo$y.scales$limit)) {
        have.ylim <- TRUE
        ylim <- foo$x.scales$limit
    }
    if (have.xlim || have.ylim) {
        warning("Limits cannot be explicitly specified")
    }
    have.xlim <- TRUE
    have.ylim <- TRUE
    xlim <- c(0,1)
    ylim <- c(0,1)
    
    ## Step 4: Decide if log scales are being used:

    have.xlog <- !is.logical(foo$x.scales$log) || foo$x.scales$log
    have.ylog <- !is.logical(foo$y.scales$log) || foo$y.scales$log
#    if (have.xlog) {
#        xlog <- foo$x.scales$log
#        xbase <-
#            if (is.logical(xlog)) 10
#            else if (is.numeric(xlog)) xlog
#            else if (xlog == "e") exp(1)
#
#        x <- log(x, xbase)
#        if (have.xlim) xlim <- log(xlim, xbase)
#    }
#    if (have.ylog) {
#        ylog <- foo$y.scales$log
#        ybase <-
#            if (is.logical(ylog)) 10
#            else if (is.numeric(ylog)) ylog
#            else if (ylog == "e") exp(1)
#
#        y <- log(y, ybase)
#        if (have.ylim) ylim <- log(ylim, ybase)
#    }
    
    ## Step 5: Process cond

    cond <- lapply(cond, as.factorOrShingle, subset, drop = TRUE)
    cond.max.level <- unlist(lapply(cond, nlevels))


    ## id.na used only to see if any plotting is needed. Not used
    ## subsequently, unlike other functions

    id.na <- FALSE
    for (j in 1:ncol(x))
        id.na <- id.na | is.na(x[,j])
    for (var in cond)
        id.na <- id.na | is.na(var)
    if (!any(!id.na)) stop("nothing to draw")
    ## Nothing simpler ?

    foo$condlevels <- lapply(cond, levels)

    ## Step 6: Evaluate layout, panel.args.common and panel.args


    foo$panel.args.common <-
        c(list(z = x,
               panel = panel,
               panel.subscripts = subscripts,
               groups = groups, # xscales = foo$x.scales, yscales = foo$y.scales,
               pscales = pscales),
          dots)

    layout <- compute.layout(layout, cond.max.level, skip = foo$skip)
    plots.per.page <- max(layout[1] * layout[2], layout[2])
    number.of.pages <- layout[3]
    foo$skip <- rep(foo$skip, length = plots.per.page)
    foo$layout <- layout
    nplots <- plots.per.page * number.of.pages

    foo$panel.args <- as.list(1:nplots)
    cond.current.level <- rep(1,number.of.cond)
    panel.number <- 1 # this is a counter for panel number
    for (page.number in 1:number.of.pages)
        if (!any(cond.max.level-cond.current.level<0))
            for (plot in 1:plots.per.page) {

                if (foo$skip[plot]) foo$panel.args[[panel.number]] <- FALSE
                else if(!any(cond.max.level-cond.current.level<0)) {

                    ##id <- !id.na
                    for(i in 1:number.of.cond)
                    {
                        var <- cond[[i]]
                        id <- if (is.shingle(var))
                            ((var >=
                              levels(var)[[cond.current.level[i]]][1])
                             & (var <=
                                levels(var)[[cond.current.level[i]]][2]))
                        else (as.numeric(var) == cond.current.level[i])
                    }

                    foo$panel.args[[panel.number]] <-
                        list(subscripts = subscr[id])

                    cond.current.level <-
                        cupdate(cond.current.level,
                                cond.max.level)
                }

                panel.number <- panel.number + 1
            }

    foo <- c(foo,
             limits.and.aspect(prepanel.default.splom,
                               prepanel = prepanel, 
                               have.xlim = have.xlim, xlim = xlim, 
                               have.ylim = have.ylim, ylim = ylim, 
                               x.relation = foo$x.scales$relation,
                               y.relation = foo$y.scales$relation,
                               panel.args.common = foo$panel.args.common,
                               panel.args = foo$panel.args,
                               aspect = aspect,
                               nplots = nplots))

    class(foo) <- "trellis"
    foo
}

back to top