2

Working with knitr has introduced a new problem -- many of my R scripts include picture generating code, and the plotting code slows things down when i source the code.

My idea is to move plotting code into a group that only runs if the code is being executed at the upper level, and not run when the code is sourced by another R-script, via the source() idiom. Is this possible?

I found this old SO question, however interactive() will always be TRUE in my case, so the accepted answer does not work.

My case is as follows: I have a file, myKnit.rnw, and run it by sending it from vim to R, using the vim-r-plugin. Thus, interactive() is always going to be TRUE, and length(sys.frames()) will be non-zero -- as the vim-r-plugin basically works via applying base::source(...) to a temporary file.

The solution i am looking for is an R equivalent to the python idiom if __name__ == __main__.

Thus when myKnit.rnw runs and sources myscript.r via source("~/R/myscript.r"), the if evaluates to FALSE and the plotting code in myscript.r does not run.

In python terms, __name__ (or whatever we call it) would not be __main__ when myKnit.rnw sources myscript.r, but would be true when i send myscript.r to the console from vim.

example knitr code:

\documentclass{beamer}
\begin{document}
\title{A Minimal Example}
\author{ricardo}

\maketitle

\begin{frame}[fragile]
source the code and then use the plot

<<source_plotScript, include=FALSE>>=
source("~/rwd/plotScript.r")
@
a histogram!
<<histy, fig.width=7, fig.height=5, messages=FALSE, warnings=FALSE>>=
print(pp)
@
\end{frame}
\end{document}

and here's the plot script that's sourced:

require(ggplot2)
set.seed(1)
x <- rnorm(100)
pp <- qplot(x, geom = 'histogram')

pdf("seed1Hist.pdf")
print(pp)
dev.off()

Solution with system specific flags, reflecting Yihui's comment

fromSource <- function()
{
    knitSysSwitch <- function()
    { 
        switch(Sys.info()[['sysname']], 
               Windows = 'source', 
               Darwin = 'base::source')
    }
    length(sys.frame()) >= 4 && sys.call(1)[[1]] == knitSysSwitch()
}
Community
  • 1
  • 1
ricardo
  • 8,195
  • 7
  • 47
  • 69
  • 1
    Can you show us a minimum example of your .rnw file that produces this? It will simplify things to have everyone work from the same example. – Thomas Aug 03 '13 at 07:47
  • @Thomas, i beg your pardon -- i just made a simple example at home on my mac, and had no problems ... suggests that it's either a windows thing, or there's some other problem with the code. I'll check at work and confirm -- else i'll delete this question. – ricardo Aug 03 '13 at 09:36
  • @Thomas, I looked at things carefully on my mac at home, and though the error does not occur, the issue remains that lots of plotting code slows things down a lot (especially using `ggplot2`) so the work-around is still very desirable. Made a small edit to reflect changed catalyst & added example code. – ricardo Aug 05 '13 at 09:05

1 Answers1

2

I guess you are looking for something like this:

if (length(sys.frames()) >= 4 && sys.call(1)[[1]] == quote(base::source)) {
  # plot them
}

When the code is evaluated via source(), there are at least four frames in the stack. sys.call()[[1]] extracts the function symbol in the call, which I think is similar to __name__ in Python.

BTW, perhaps you are aware of this: when you are working in knitr, you can turn on the cache using the chunk option cache=TRUE to speed up the time-consuming plotting code.

Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
  • +1, accepted. I work between `mac` and `windows`, and the `source` behaviour of the `vim-r-plugin` varies, so i added the following helper `knitSysSwitch <- function(){ switch(Sys.info()[['sysname']], Windows = 'source', Darwin = 'base::source')}`, and edited the test to be `if (length(sys.frames()) >= 4 && sys.call(1)[[1]] == knitSysSwitch())`. – ricardo Aug 06 '13 at 00:19
  • 1
    @ricardo or wrap everything into one function, e.g. `if (from_source()) {plot...}`, and `from_source = function() {...Sys.info()...sys.call()...whatever...}` – Yihui Xie Aug 06 '13 at 00:34