I have written a function that draws some plots, and returns a list, similar in style to the following format:
myfun <- function(x, y){
plot(x, y)
points(x+1, y+1)
abline(v=x[1])
mylist <- list(x=x,y=y,line=x[1])
return(mylist)
}
This works fine. However, in R, one generally plots from functions in the following way:
x <- rnorm(100)
y <- rnorm(100)
lin <- lm(x~y)
plot(lin)
i.e., one creates an object using the function, then uses plot(object) to get the plot. How can I set up my function to behave in this way? I've had a look at a few guides to writing R packages (including hadley's), but I couldn't find reference to this problem.
I would like to create this functionality so I can upload what I've created to CRAN or R-Forge.
You could create your own S3 class for it (
R provides a lot of object oriented systems (S3, S4, R5, R.oo, ...), see also: http://adv-r.had.co.nz/OO-essentials.html):
# create an object with an own class
lin = list(x=rnorm(100), y=rnorm(100))
class(lin) = "mylin"
# overload plotting function
plot.mylin = function(l) {
plot(l$x, l$y)
points(l$x+1, l$y+1, col=2)
abline(v=l$x[1])
}
# run it
plot(lin)
Related
I'm writing a short R package which contains a function. The function returns a list of vectors. I would like to use the plot function in order to plot by default a plot done with some of those vectors, add lines and add a new parameter.
As an example, if I use the survival package I can get the following:
library(survival)
data <- survfit(Surv(time, status == 2) ~ 1, data = pbc)
plot(data) # Plots the result of survfit
plot(data, conf.int = "none") # New parameter
In order to try to make a reproducible example:
f <- function(x, y){
b <- x^2
c <- y^2
d <- x+y
return(list(one = b, two = c, three = d))
}
dat <- f(3, 2)
So using plot(dat) I would like to get the same as plot(dat$one, dat$two). I would also like to add one more (new) parameter that could be set to TRUE/FALSE.
Is this possible?
I think you might be looking for classes. You can use the S3 system for this.
For your survival example, data has the class survfit (see class(data)). Then using plot(data) will look for a function called plot.survfit. That is actually a non-exported function in the survival package, at survival:::plot.survfit.
You can easily do the same for your package. For example, have a function that creates an object of class my_class, and then define a plotting method for that class:
f <- function(x, y){
b <- x^2
c <- y^2
d <- x+y
r <- list(one = b, two = c, three = d)
class(r) <- c('list', 'my_class') # this is the important bit.
r
}
plot.my_class <- function(x) {
plot(x$one, x$two)
}
Now your code should work:
dat <- f(3, 2)
plot(dat)
You can put anything in plot.my_class you want, including additional arguments, as long as your first argument is x and is the my_class object.
plot now calls plot.my_class, since dat is of class my_class.
You can also add other methods, e.g. for print.
There are many different plotting functions that can be called with plot for different classes, see methods(plot)
Also see Hadley's Advanced R book chapter on S3.
I'd like to spawn several graphics windows from within a function in R using ggplot graphics...
testf <- function(a, b) {
devAskNewPage(TRUE)
qplot(a, b);
# grid.newpage(recording = TRUE)
dev.new()
qplot(a, a+a);
# grid.newpage(recording = TRUE)
dev.new()
qplot(b, b+b);
}
library(ggplot2)
x <- rnorm(50)
y <- rnorm(50)
testf(x, y)
However, neither dev.new() nor grid.newpage() seems to flush the preceding plot.
I know that, in R, functions normally only produce the last thing they evaluate, but I'd like to understand the process better and to learn of any possible workarounds.
Thoughts?
The grid-based graphics functions in lattice and ggplot2 create a graph object, but do not display it. The print() method for the graph object produces the actual display, i.e.,
print(qplot(x, y))
solves the problem.
See R FAQ 7.22.
I use heatmap.2 from gplots to make a heatmap:
library(gplots)
# some fake data
m = matrix(c(0,1,2,3), nrow=2, ncol=2)
# make heatmap
hm = heatmap.2(m)
When I do 'heatmap.2' directly I get a plot that I can output to a device. How can I make the plot again from my variable 'hm'? Obviously this is a toy example, in real life I have a function that generates and returns a heatmap which I would like to plot later.
There are several alternatives, although none of them are particularly elegant. It depends on if the variables used by your function are available in the plotting environment. heatmap.2 doesn't return a proper "heatmap" object, although it contains the necessary information for plotting the graphics again. See str(hm) to inspect the object.
If the variables are available in your environment, you could just re-evaluate the original plotting call:
library(gplots)
# some fake data (adjusted a bit)
set.seed(1)
m = matrix(rnorm(100), nrow=10, ncol=10)
# make heatmap
hm = heatmap.2(m, col=rainbow(4))
# Below fails if all variables are not available in the global environment
eval(hm$call)
I assume this won't be the case though, as you mentioned that you are calling the plot command from inside a function and I think you're not using any global variables. You could just re-construct the heatmap drawing call from the fields available in your hm-object. The problem is that the original matrix is not available, but instead we have a re-organized $carpet-field. It requires some tinkering to obtain the original matrix, as the projection has been:
# hm2$carpet = t(m[hm2$rowInd, hm2$colInd])
At least in the case when the data matrix has not been scaled, the below should work. Add extra parameters according to your specific plotting call.
func <- function(mat){
h <- heatmap.2(mat, col=rainbow(4))
h
}
# eval(hm2$call) does not work, 'mat' is not available
hm2 <- func(m)
# here hm2$carpet = t(m[hm2$rowInd, hm2$colInd])
# Finding the projection back can be a bit cumbersome:
revRowInd <- match(c(1:length(hm2$rowInd)), hm2$rowInd)
revColInd <- match(c(1:length(hm2$colInd)), hm2$colInd)
heatmap.2(t(hm2$carpet)[revRowInd, revColInd], Rowv=hm2$rowDendrogram, Colv=hm2$colDendrogram, col=hm2$col)
Furthermore, I think you may be able to work your way to evaluating hm$call in the function's environment. Perhaps with-function would be useful.
You could also make mat available by attaching it to the global environment, but I think this is considered bad practice, as too eager use of attach can result in problems. Notice that in my example every call to func creates the original plot.
I would do some functional programming:
create_heatmap <- function(...) {
plot_heatmap <- function() heatmap.2(...)
}
data = matrix(rnorm(100), nrow = 10)
show_heatmap <- create_heatmap(x = data)
show_heatmap()
Pass all of the arguments you need to send to plot_heatmap through the .... The outer function call sets up an environment in which the inner function looks first for its arguments. The inner function is returned as an object and is now completely portable. This should produce the exact same plot each time!
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))")
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"))
Of course there is! Here ya go:
x = rnorm(100)
y = sin(x)
something = "y~x"
plot(formula(something),main=something)
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) ) )
I'd like to spawn several graphics windows from within a function in R using ggplot graphics...
testf <- function(a, b) {
devAskNewPage(TRUE)
qplot(a, b);
# grid.newpage(recording = TRUE)
dev.new()
qplot(a, a+a);
# grid.newpage(recording = TRUE)
dev.new()
qplot(b, b+b);
}
library(ggplot2)
x <- rnorm(50)
y <- rnorm(50)
testf(x, y)
However, neither dev.new() nor grid.newpage() seems to flush the preceding plot.
I know that, in R, functions normally only produce the last thing they evaluate, but I'd like to understand the process better and to learn of any possible workarounds.
Thoughts?
The grid-based graphics functions in lattice and ggplot2 create a graph object, but do not display it. The print() method for the graph object produces the actual display, i.e.,
print(qplot(x, y))
solves the problem.
See R FAQ 7.22.