Search code examples
rknitrscriptedmain

Scripted main behaviour in R?


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()
}

Solution

  • 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.