Search code examples
rif-statementlatexknitrsweave

If-Else Statement in knitr/Sweave using R variable as conditional (Part 2)


I am extending a question I posted here:

If-Else Statement in knitr/Sweave using R variable as conditional

I would like to use an if-else syntax in LaTeX so that, depending on the value of an R variable (say x), one of two LaTeX text paragraphs are output. If x>0, then the LaTeX paragraph has a figure and a table. However, if x<0, then the LaTeX paragraph has just a figure (and no table).

I have a MWE that works and is based on the checked answer at the previous post:

\documentclass[12pt,english,nohyper]{tufte-handout}
\usepackage{tabularx}
\usepackage{longtable}

\begin{document}

<<setup, echo = FALSE>>=
library(knitr)
library(xtable)
library(ggplot2)
knit_patterns$set(header.begin = NULL)
@

<<echo=FALSE,results='asis'>>=
fname="myOutput.pdf"
pdf(fname,width=4,height=4)
print(qplot(mpg,cyl,data=mtcars))
{dev.off();invisible()}
cat(sprintf('\\begin{marginfigure}
\\includegraphics[width=0.98\\linewidth]{%s}
\\caption{\\label{mar:dataMtcars}Comments about mtcar dataset.}
\\end{marginfigure}',sub('\\.pdf','',fname)))
@

<<echo=FALSE,results='asis'>>=
x<- rnorm(1)
if (x>0){
  myDF = data.frame(a=rnorm(1:5),b=rnorm(1:5),c=rnorm(1:5))
  print(xtable(myDF,caption='Data frame comments', label='tab:myDataFrame'),floating=FALSE, tabular.environment = "longtable",include.rownames=FALSE)
}
@

<<condition, echo=FALSE>>=
if(x>0){
  text <- "Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of \\Sexpr{x} was greater than 0. Table \\ref{tab:myDataFrame} shows my data frame."
  }else{
  text <- "Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of \\Sexpr{x} was less than 0. We do not show a data frame."
  }
@

Testing the code: 

<<print, results='asis', echo=FALSE>>=
cat(text)
@

\end{document}

I then save this MWE as something like testKnitr.Rnw, and run:

knit(input = "testKnitr.Rnw", output = "intermediate.Rnw")
knit2pdf(input = "intermediate.Rnw", output = "doc_final.tex")

I am wondering if I am creating this code efficiently, especially because it was pointed out to me in my previous post that - in the case of just using \Sexpr{} - there were easier solutions. It is a bit cumbersome to knit twice here.

Is there an easier way to incorporate my if/else statement to display one of two paragraphs containing the variable value, figures, and/or tables, all based on the value of the variable? Thank you.


Solution

  • Similar to the related question that has been linked above dynamically generating \Sexpr{} and parsing the intermediate output a second time is not necessary in this scenario.

    Many things happen in the code from the question, but this is the key chunk (where x gets a random value assigned in a previous chunk):

    <<condition, echo=FALSE>>=
    if (x > 0) {
      text <- "Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of \\Sexpr{x} was greater than 0. Table \\ref{tab:myDataFrame} shows my data frame."
      } else {
        text <- "Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of \\Sexpr{x} was less than 0. We do not show a data frame."
        }
    @
    

    Direct substitutes for printing \Sexpr

    Straighforward alternatives to \Sexpr{} are paste and sprintf (I use one in the if branch and one in the else branch just for demonstration):

    <<condition, echo=FALSE>>=
    if (x > 0) {
      text <- paste("Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of", x, "was greater than 0. Table \\ref{tab:myDataFrame} shows my data frame.")
      } else {
        text <- sprintf("Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of %f was less than 0. We do not show a data frame.", x)
        }
    @
    

    This works well when the text to be conditionally displayed is not too long. Otherwise, it might be more convenient to use LaTeX if-statements. This has the advantage that not too much code is wrapped in characer strings / code chunks. Then, \Sexpr{} might again be useful.

    Using LaTeX conditional expressions

    There are many ways to write conditional expressions in LaTeX, but the most simple form will be enough.

    • First, define a new if that we'll call ifPositive: \newif\ifPositive.
    • Second, use R to insert \Positivetrue or \Positivefalse into the document.
    • Third, use \ifPositive … \else … \fi.

      \newif\ifPositive
      
      <<condtion2, echo = FALSE, results = "asis">>=
      if (x > 0) {
        cat("\\Positivetrue")
      } else {
        cat("\\Positivefalse")
      }
      @
      
      \ifPositive
      Figure \ref{mar:dataMtcars} shows the mtcars data set. The x value of \Sexpr{x} was greater than 0. Table \ref{tab:myDataFrame} shows my data frame.
      \else
      Figure \ref{mar:dataMtcars} shows the mtcars data set. The x value of \Sexpr{x} was less than 0. We do not show a data frame.
      \fi
      

    Other remarks

    In the question there is a chunk that generates myOutput.pdf. There, pdf() and dev.off() are used. This is no good style and kills kittens. There is no need to bypass knitr's mechanisms for producing plots; not even when custom environments like marginfigure instead of figure are to be used. The chunk should be replaced by something like this:

    <<dataMtcars, echo=FALSE, fig.env = "marginfigure", out.width = "0.98\\linewidth", fig.cap = "Comments about mtcar dataset.", fig.lp = "mar:">>=
    print(qplot(mpg,cyl,data=mtcars))
    @
    

    fig.lp = "mar:" is only necessary in order to produce the exactly same label as in the question mar:dataMtcars. It could be skipped if the label fig:dataMtcars was also okay.

    Document

    Finally, here the complete document:

    \documentclass[12pt,english,nohyper]{tufte-handout}
    \usepackage{tabularx}
    \usepackage{longtable}
    
    \begin{document}
    
    <<setup, echo = FALSE>>=
    library(knitr)
    library(xtable)
    library(ggplot2)
    @
    
    <<dataMtcars, echo=FALSE, fig.env = "marginfigure", out.width = "0.98\\linewidth", fig.cap = "Comments about mtcar dataset.", fig.lp = "mar:">>=
    print(qplot(mpg,cyl,data=mtcars))
    @
    
    <<echo=FALSE,results='asis'>>=
    x <- rnorm(1)
    if (x > 0) {
      myDF <- data.frame(a = rnorm(1:5), b = rnorm(1:5), c = rnorm(1:5))
      print(xtable(myDF, caption= 'Data frame comments', label='tab:myDataFrame'), floating = FALSE, tabular.environment = "longtable", include.rownames=FALSE)
    }
    @
    
    <<condition, echo=FALSE>>=
    if (x > 0) {
      text <- paste("Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of", x, "was greater than 0. Table \\ref{tab:myDataFrame} shows my data frame.")
      } else {
        text <- sprintf("Figure \\ref{mar:dataMtcars} shows the mtcars data set. The x value of %f was less than 0. We do not show a data frame.", x)
        }
    @
    
      Testing the code:
    
    <<print, results='asis', echo=FALSE>>=
      cat(text)
    @
    
    \paragraph{Alternative:}
    
    \newif\ifPositive
    
    <<condtion2, echo = FALSE, results = "asis">>=
    if (x > 0) {
      cat("\\Positivetrue")
    } else {
      cat("\\Positivefalse")
    }
    @
    
    \ifPositive
    Figure \ref{mar:dataMtcars} shows the mtcars data set. The x value of \Sexpr{x} was greater than 0. Table \ref{tab:myDataFrame} shows my data frame.
    \else
    Figure \ref{mar:dataMtcars} shows the mtcars data set. The x value of \Sexpr{x} was less than 0. We do not show a data frame.
    \fi
    
    \end{document}