saffsd
saffsd

Reputation: 24292

How can I auto-title a plot with the R call that produced it?

R's plotting is great for data exploration, as it often has very intelligent defaults. For example, when plotting with a formula the labels for the plot axes are derived from the formula. In other words, the following two calls produce the same output:

plot(x~y)
plot(x~y, xlab="x", ylab="y")

Is there any way to get a similar "intelligent auto-title"?

For example, I would like to call

plot(x~y, main=<something>)

And produce the same output as calling

plot(x~y, main="plot(x~y)")

Where the <something> inserts the call used using some kind of introspection.

Is there a facility for doing this in R, either through some standard mechanism or an external package?

edit: One suggestion was to specify the formula as a string, and supply that as the argument to a formula() call as well as main. This is useful, but it misses out on parameters than can affect a plot, such as using subsets of data. To elaborate, I'd like

x<-c(1,2,3)
y<-c(1,2,3)
z<-c(0,0,1)
d<-data.frame(x,y,z)
plot(x~y, subset(d, z==0), main=<something>)

To have the same effect as

plot(x~y, subset(d, z==0), main="plot(x~y, subset(d, z==0))")

Upvotes: 7

Views: 918

Answers (3)

Josh O&#39;Brien
Josh O&#39;Brien

Reputation: 162321

I don't think this can be done without writing a thin wrapper around plot(). The reason is that R evaluates "supplied arguments" in the evaluation frame of the calling function, in which there's no way to access the current function call (see here for details).

By contrast, "default arguments" are evaluated in the evaluation frame of the function, from where introspection is possible. Here are a couple of possibilities (differing just in whether you want "myPlot" or "plot" to appear in the title:

## Function that reports actual call to itself (i.e. 'myPlot()') in plot title.
myPlot <- function(x,...) {
    cl <- deparse(sys.call())
    plot(x, main=cl, ...)
}

## Function that 'lies' and says that plot() (rather than myPlot2()) called it.
myPlot2 <- function(x,...) {
    cl <- sys.call()
    cl[[1]] <- as.symbol("plot")
    cl <- deparse(cl)
    plot(x, main=cl, ...)
}

## Try them out
x <- 1:10
y <- 1:10
par(mfcol=c(1,2))
myPlot(x,y)
myPlot2(y~x)

Here's a more general solution:

plotCaller <- function(plotCall, ...) {
    main <- deparse(substitute(plotCall))
    main <- paste(main, collapse="\n")
    eval(as.call(c(as.list(substitute(plotCall)), main=main, ...)))
}

## Try _it_ out

plotCaller(hist(rnorm(9999), breaks=100, col="red"))

library(lattice)
plotCaller(xyplot(rnorm(10)~1:10, pch=16))

## plotCaller will also pass through additional arguments, so they take effect
## without being displayed
plotCaller(xyplot(rnorm(10)~1:10), pch=16)

deparse will attempt to break deparsed lines if they get too long (the default is 60 characters). When it does this, it returns a vector of strings. plot methods assume that 'main' is a single string, so the line main <- paste(main, collapse='\n') deals with this by concatenating all the strings returned by deparse, joining them using \n.

Here is an example of where this is necessary:

plotCaller(hist(rnorm(9999), breaks=100, col="red", xlab="a rather long label",
    ylab="yet another long label"))

Upvotes: 7

Greg Snow
Greg Snow

Reputation: 49640

You might be thinking of the functionality of match.call. However that only really works when called inside of a function, not passed in as an argument. You could create your wrapper function that would call match.call then pass everything else on to plot or use substitute to capture the call then modify it with the call before evaluating:

x <- runif(25)
y <- rnorm(25, x, .1)

myplot <- function(...) {
    tmp <- match.call()
    plot(..., main=deparse(tmp))
}

myplot( y~x )
myplot( y~x, xlim=c(-.25,1.25) )

## or

myplot2 <- function(FUN) {
    tmp1 <- substitute(FUN)
    tmp2 <- deparse(tmp1)
    tmp3 <- as.list(tmp1)
    tmp4 <- as.call(c(tmp3, main=tmp2))
    eval(tmp4)
}

myplot2( plot(y~x) )
myplot2( plot(y~x, xlim=c(-.25,1.25) ) )

Upvotes: 3

user2005253
user2005253

Reputation:

Of course there is! Here ya go:

x = rnorm(100)
y = sin(x)

something = "y~x"

plot(formula(something),main=something)

enter image description here

Upvotes: 3

Related Questions