Convenient wrapper for plots, lines, and points

244 views Asked by At

One of the things that most bugs me about R is the separation of the plot, points, and lines commands. It's somewhat irritating to have to change plot to whatever variant for the first plot done, and to have to replot from scratch if you failed to have set the correct the ylim and xlim initially. Wouldn't it be nice to have one command that:

  1. Picks lines, points or both via an argument, as in plot(..., type = "l") ?

  2. By default, chooses whether to create a new plot, or add to an existing one according to whether the current device is empty or not.

  3. Rescales axes automatically if the added elements to the plot exceed the current bounds.

Has anyone done anything like this? If not, and there's no strong reason why this isn't possible, I'll answer this myself in a bit...

2

There are 2 answers

0
Greg Snow On

Some possible functionality that may help with what you want:

The matplot function uses base graphics and will plot several sets of points or lines in one step, figuring out the correct ranges in one step.

There is an update method for lattice graphics that can be used to add/change things in the plot and will therefore result in automatic recalculation of things like limits and axes.

If you add additional information (useing +) to a ggplot2 plot, then the things that are automatically calculated will be recalculated.

You already found zoomplot and there is always the approach of writing your own function like you did.

0
Fhnuzoag On

Anyway, this is what I came up with: (It uses zoomplot from TeachingDemos)

 fplot <- function(x, y = NULL, type = "l", new = NULL, xlim, ylim, zoom = TRUE,...){
   require(TeachingDemos)
   if (is.null(y)){
if (length(dim(x)) == 2){
    y = x[,2]
    x = x[,1]
} else {
       y = x
       x = 1:length(y)
     } 
}

   if ( is.null(new) ){
   #determine whether to make a new plot or not
   new = FALSE
   if (is.null(recordPlot()[[1]])) new = TRUE
   }
   if (missing(xlim)) xlim = range(x)
   if (missing(ylim)) ylim = range(y)

   if (new){
   plot(x, y, type = type, xlim = xlim, ylim = ylim, ...)
   } else {
    if (type == "p"){
        points(x,y, ...)
    } else {
        lines(x,y, type = type, ...)
    }
    if (zoom){
    #rescale plot
    xcur = par("usr")[1:2]
    ycur = par("usr")[3:4]
    #shrink coordinates and pick biggest
    xcur = (xcur - mean(xcur)) /1.08 + mean(xcur)
    ycur = (ycur - mean(ycur)) /1.08 + mean(ycur)
    xlim = c(min(xlim[1], xcur[1]), max(xlim[2], xcur[2]))
    ylim = c(min(ylim[1], ycur[1]), max(ylim[2], ycur[2]))
    #zoom plot
    zoomplot(xlim, ylim)
    }
   }
 }

So you can do, e.g.

dev.new()
fplot(1:4)
fplot(1:4 +1, col = 2)
fplot(0:400/100 + 1, sin(0:400/10), type = "p")
dev.new()
for (k in 1:20) fplot(sort(rnorm(20)), type = "b", new = (k==1) )

par(mfrow) and log axis don't currently work well with zooming, but, it's a start...