I'm using the exams package to generate some exams (engineering). My source files are in Rnw format so the underlying engine is Sweave.
In R side, I'm using units package (to check dimensionality correctness) and I've created a function (called units2tex) to convert a magnitude from units to LaTeX equivalent. So, when I calculate a variable, I have to create a copy for LaTeX:
<<>>=
R = set_units(1,ohm)
I = set_units(1,A)
V = R*I
R_tex = units2tex(R)
I_tex = units2tex(I)
#V_tex = units2tex(V)
#
For $R = \Sexpr{R_tex}$, the answer is \Sexpr{units2tex(V)}.
In both cases (to print R and V) I have to explicitly call units2tex function somewhere. It's is a lot of typing (more to type, more places to be wrong) because there are a lot of steps and intermediate temporal values.
Is it possible to easily create versions of \Sexpr with automatic function calls?
More concretely:
Is is possible to create \SexprT{V} to function like \Sexpr{units2tex(V)}?
Is is possible to create \SexprT{V,digits=2} (or similar) to function like \Sexpr{units2tex(V,digits=2)}?
In both cases without losing the use of original \Sexpr.
My current options and its cons:
Explicitly call to units2tex(...) and duplicate variables (units version and LaTeX version). It is my current approach. A lot to typing, prone to forget conversions or generate conversions that they are not used latter.
use very short function names: \Sexpr{t(V)}. Difficult to remember the meaning of each translation function. Pollution of the namespace. I still remember last time I use a c function (after a and b ones) and break original c function.
Use some kind of preprocessor and made the changes. As I said before, all ecosystem is to work with exams package. This package executes Sweave in the background, I do not want to alter the pipeline of the files to generate the tests. I would write a *.Rnw2 file and have to create a script in ¿sed? ¿R?, read files, modify values and generate the *.Rnw file and then call exams2pdf functions... There is problems if I forgot to update Rnw exams files. I'd use a Makefile to coordinate, but I have to switch between a terminal and Rstudio... This option is, maybe the best (after the creation of \Sexpr versions) but the solution obscures the pipeline and difficult quick questions generation to check them.
So, the best approach is: Is it possible to create variations of \Sexpr?
Full example: file: "question.Rnw"
\usepackage[utf8]{inputenc}
\usepackage[OT1]{fontenc}
<<echo=FALSE, results=hide>>=
library(units)
units2tex <- function(x) {
# this function is not important here. Simplified version the original version much complex
paste0(x,deparse_unit(x))
}
#
\begin{question}
<<echo=FALSE, results=hide>>=
In <- set_units(1e-3,A)
Pn <- set_units(sample(c(2,1,0.5,0.25,0.125),1),W)
In_tex <- units2tex(In)
Pn_tex <- units2tex(Pn)
#
A resistor nah nah nah current $I_n=\Sexpr{In_tex}$ nah, nah, nah $P_n=\Sexpr{Pn_tex}$
What is V?
<<echo=FALSE, results=hide>>=
Vn <- set_units(Pn/In,V)
Vn_tex <- units2tex(Vn)
#
\begin{answerlist}
\item $V = \Sexpr{Vn_tex}$
\item $V = \Sexpr{Pn_tex}$
\item $V = \Sexpr{In_tex}$
\end{answerlist}
\end{question}
\begin{solution}
To calculate $V$, we do $V= P_n/I_n = \frac{\Sexpr{Pn_tex}}{\Sexpr{In_tex}} = \Sexpr{Vn_tex}$
\begin{answerlist}
\item Ok
\item Bad
\item Wrong
\end{answerlist}
\end{solution}
\exname{question1}
\extype{schoice}
\exsolution{100}
\exshuffle{TRUE}
And to compile:
library(exams)
exams2html(c("question1.Rnw"), encoding="utf-8",template='plain8.html',mathjax = TRUE)
In Rnw exercises the content of \Sexpr{} is turned into character by explicitly calling as.character() on it. As this is a generic function you could easily define your own method, say,
as.character.units <- function(x, ...) units2tex(x, ...)
and then simply using \Sexpr{V} will automatically call your units2tex() function behind the scenes.
Note that the same does not work for Rmd exercises. So i you want to mix Rnw and Rmd exercises, then using a shortcut function name such as the following might be better:
u2t <- function(x, ...) units2tex(x, ...)
This would then need to be called as \Sexpr{u2t(V)} and `r u2t(V)`, respectively.
To reduce the pollution namespace:
a <- list(t=function(x){units2tex(x)},d=function(x){units2tex(x,digits=2)})
and later \Sexpr{a$t(V)}
It's almost shorter than \Sexpr{V_tex} but I do not have to define V_tex.
Related
I'm creating a small R package that will allow a user to create exams with R code for tables and figures, multiple question types, randomly ordered questions, and randomly ordered responses on multiple choice items. Inserting R code into LaTeX is not problematic, but one issue I've run into is the need to "slap" together text ingested via R with LaTeX commands. Consider this example:
I have this in a LaTeX file:
\newcommand{\question}[1]{
\begin{minipage}{\linewidth}
\item
{#1}
\end{minipage}
}
I read the content with readr::read_file and store it in a variable. I then have the contents of the questions in a .json file:
...
{
"type" : "mc",
"section_no" : 1,
"points" : 2,
"question" : "What is the best beer?",
"correct_answer" : "Hamm's",
"lure_1" : "Miller Lite",
"lure_2" : "PBR",
"lure_3" : "Naturdays",
"lure_4" : "Leine's"
},
...
which I read with jsonlite::fromJSON (which converts to a dataframe), do some massaging, and store in a variable. Let's call the questions and their available options questions. What I've been doing is putting the necessary LaTeX content together with the character string manually with
question.tex <- paste0("\\question{", question[i], "\\\\")
to achieve this in the knitted .tex file:
\question{What is the best beer?\\A. PBR\\B. Naturdays\\C. Miller Lite\\D. Leine's\\E. Hamm's\\}
but I'm thinking there has to be a better way to do this. I'm looking for a function that will allow for a more seamless passing of arguments to my LaTeX command, something like knitr::magic_func(latex.command, question[i]) to achieve the result above. Does this exist?
Maybe I am asking for an extra level of abstraction that knitr doesn't have (or wasn't designed to have)? Or perhaps there's a better way? I guess at this point I'm not far away from being able to create a function that reads the LaTeX command name, number of arguments, and inserts text appropriately, but better to not reinvent the wheel! Also, I think this question could be generalized to simpler commands like \title, \documentclass, etc.
Small MWE:
## (more backslashes since we need to escape in R)
tex.command <- "\\newcommand{\\question}[1]{
\\begin{minipage}{\\linewidth}
\\item
{#1}
\\end{minipage}
}"
q <- "What is the best beer?\\\\A. PBR\\\\B. Naturdays\\\\C. Miller Lite\\\\D. Leine's\\\\E. Hamm's\\\\"
## some magic function here?
magic_func(tex.command, q)
## desired result
"\\question{What is the best beer?\\\\A. PBR\\\\B. Naturdays\\\\C. Miller Lite\\\\D. Leine's\\\\E. Hamm's\\\\\\\\"
Using knitr and R Markdown, I can produce a tabularised output from a matrix using the following command:
```{r results='asis'}
kable(head(x))
```
However, I’m searching for a way to make the kable code implicit since I don’t want to clutter the echoed code with it. Essentially, I want this:
```{r table=TRUE}
head(x)
```
… to yield a formatted tabular (rather than the normal output='markdown') output.
I actually thought this must be pretty straightforward since it’s a pretty obvious requirement, but I cannot find any way to achieve this, either via the documentation or on the web.
My approach to create an output hook fails because once the data arrives at the hook, it’s already formatted and no longer the raw data. Even when specifying results='asis', the hook obtains the output as a character string and not as a matrix. Here’s what I’ve tried:
default_output_hook <- knit_hooks$get('output')
knit_hooks$set(output = function (x, options)
if (! is.null(options$table))
kable(x)
else
default_output_hook(x, options)
)
But like I said, this fails since x is not the original matrix but rather a character string, and it doesn’t matter which value for the results option I specify.
Nowadays one can set df_print in the YAML header:
---
output:
html_document:
df_print: kable
---
```{r}
head(iris)
```
I think other answers are from a time when the following didn't work, but now we can just do :
```{r results='asis', render=pander::pander}
head(x)
```
Or set this for all chunks in the setup chunk, for instance :
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, render=pander::pander)
```
Lacking a better solution I’m currently re-parsing the character string representation that I receive in the hook. I’m posting it here since it kind of works. However, parsing a data frame’s string representation is never perfect. I haven’t tried the following with anything but my own data and I fully expect it to break on some common use-cases.
reparse <- function (data, comment, ...) {
# Remove leading comments
data <- gsub(sprintf('(^|\n)%s ', comment), '\\1', data)
# Read into data frame
read.table(text = data, header = TRUE, ...)
}
default_output_hook <- knit_hooks$get('output')
knit_hooks$set(output = function (x, options)
if (is.null(options$table))
default_output_hook(x, options)
else {
extra_opts <- if (is.list(options$table)) options$table else list()
paste(kable(do.call(reparse, c(x, options$comment, extra_opts))),
collapse = '\n')
}
)
This will break if the R markdown comment option is set to a character sequence containing a regular expression special char (e.g. *), because R doesn’t seem to have an obvious means of escaping a regular expression.
Here’s a usage example:
```{r table=TRUE}
data.frame(A=1:3, B=4:6)
```
You can pass extra arguments to the deparse function. This is necessary e.g. when the table contains NA values because read.table by default interprets them as strings:
```{r table=list(colClasses=c('numeric', 'numeric'))}
data.frame(A=c(1, 2, NA, 3), B=c(4:6, NA))
```
Far from perfect, but at least it works (for many cases).
Not exactly what you are looking for, but I am posting an answer here (that could not fit in a comment) as your described workflow is really similar to what my initial goal and use-case was when I started to work on my pander package. Although I really like the bunch of chunk options that are available in knitr, I wanted to have an engine that makes creating documents really easy, automatic and without any needed tweaks. I am aware of the fact that knitr hooks are really powerful, but I just wanted to set a few things in my Rprofile and let the literate programming tool its job without further trouble, that ended to be Pandoc.brew for me.
The main idea is to specify a few options (what markdown flavour are you using, what's your decimal mark, favorite colors for your charts etc), then simply write your report in a brew syntax without any chunk options, and the results of your code would be automatically transformed to markdown. Then convert that to pdf/docx/odt etc. with Pandoc.
I am using knitr for a report wherein I have a lot of inline output text, mostly numeric values, using \Sexpr{}. I want to highlight All these inline outputs in my generated pdf.
Example code:
\documentclass[12pt]{article}
\begin{document}
<<echo=FALSE, include=FALSE>>=
N <- 100 # Total
N_f <- 60 # Women
#
There were \Sexpr{N} people in the company, \Sexpr{N_f} women and \Sexpr{N - N_f} men.
\end{document}
Hence, in the output all the number should be highlighted, i.e. with a shaded background (similar to using with \hl{} with the \usepackage{soul}).
It seems to me that the solution would use one of the inline output hooks. Another possibility might be to write a LaTeX function which search all the \Sexpr{...} expressions in the entire document and highlights them in the generated pdf. I am still learning and can not figure out how to implement these.
Thanks for any help or hints.
Note: The knitr page by yihui talks about manipulation of the numeric value (scientific notation, digits after decimal points) which I have got covered.
The output hook inline can be used to style output from \Sexpr{}. This is as simple as
knit_hooks$set(inline = function(x) { sprintf("\\textbf{%s}", x)})
Just define an arbitrary function that takes an argument x and returns the string to be printed. In this example I used \textbf to make the output bold, but this can be extended to any LaTeX commands.
In this answer, Yihui suggests an improvement that still takes the default inline hook into account. This ensures rounding as usually performed by the default hook:
hook_inline <- knit_hooks$get('inline')
knit_hooks$set(inline = function(x) { sprintf("\\textbf{%s}", hook_inline(x))})
Using knitr and R Markdown, I can produce a tabularised output from a matrix using the following command:
```{r results='asis'}
kable(head(x))
```
However, I’m searching for a way to make the kable code implicit since I don’t want to clutter the echoed code with it. Essentially, I want this:
```{r table=TRUE}
head(x)
```
… to yield a formatted tabular (rather than the normal output='markdown') output.
I actually thought this must be pretty straightforward since it’s a pretty obvious requirement, but I cannot find any way to achieve this, either via the documentation or on the web.
My approach to create an output hook fails because once the data arrives at the hook, it’s already formatted and no longer the raw data. Even when specifying results='asis', the hook obtains the output as a character string and not as a matrix. Here’s what I’ve tried:
default_output_hook <- knit_hooks$get('output')
knit_hooks$set(output = function (x, options)
if (! is.null(options$table))
kable(x)
else
default_output_hook(x, options)
)
But like I said, this fails since x is not the original matrix but rather a character string, and it doesn’t matter which value for the results option I specify.
Nowadays one can set df_print in the YAML header:
---
output:
html_document:
df_print: kable
---
```{r}
head(iris)
```
I think other answers are from a time when the following didn't work, but now we can just do :
```{r results='asis', render=pander::pander}
head(x)
```
Or set this for all chunks in the setup chunk, for instance :
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, render=pander::pander)
```
Lacking a better solution I’m currently re-parsing the character string representation that I receive in the hook. I’m posting it here since it kind of works. However, parsing a data frame’s string representation is never perfect. I haven’t tried the following with anything but my own data and I fully expect it to break on some common use-cases.
reparse <- function (data, comment, ...) {
# Remove leading comments
data <- gsub(sprintf('(^|\n)%s ', comment), '\\1', data)
# Read into data frame
read.table(text = data, header = TRUE, ...)
}
default_output_hook <- knit_hooks$get('output')
knit_hooks$set(output = function (x, options)
if (is.null(options$table))
default_output_hook(x, options)
else {
extra_opts <- if (is.list(options$table)) options$table else list()
paste(kable(do.call(reparse, c(x, options$comment, extra_opts))),
collapse = '\n')
}
)
This will break if the R markdown comment option is set to a character sequence containing a regular expression special char (e.g. *), because R doesn’t seem to have an obvious means of escaping a regular expression.
Here’s a usage example:
```{r table=TRUE}
data.frame(A=1:3, B=4:6)
```
You can pass extra arguments to the deparse function. This is necessary e.g. when the table contains NA values because read.table by default interprets them as strings:
```{r table=list(colClasses=c('numeric', 'numeric'))}
data.frame(A=c(1, 2, NA, 3), B=c(4:6, NA))
```
Far from perfect, but at least it works (for many cases).
Not exactly what you are looking for, but I am posting an answer here (that could not fit in a comment) as your described workflow is really similar to what my initial goal and use-case was when I started to work on my pander package. Although I really like the bunch of chunk options that are available in knitr, I wanted to have an engine that makes creating documents really easy, automatic and without any needed tweaks. I am aware of the fact that knitr hooks are really powerful, but I just wanted to set a few things in my Rprofile and let the literate programming tool its job without further trouble, that ended to be Pandoc.brew for me.
The main idea is to specify a few options (what markdown flavour are you using, what's your decimal mark, favorite colors for your charts etc), then simply write your report in a brew syntax without any chunk options, and the results of your code would be automatically transformed to markdown. Then convert that to pdf/docx/odt etc. with Pandoc.
I am writing a short Sweave document that outputs into a Beamer presentation, in which I am using the sagetex package to solve an equation for two parameters in the beta binomial distribution, and I need to assign the parameter values into the R session so I can do additional processing on those values. The following code excerpt shows how I am interacting with sage:
<<echo=false,results=hide>>=
mean.raw <- c(5, 3.5, 2)
theta <- 0.5
var.raw <- mean.raw + ((mean.raw^2)/theta)
#
\begin{frame}[fragile]
\frametitle{Test of Sage 2}
\begin{sagesilent}
var('a1, b1, a2, b2, a3, b3')
eqn1 = [1000*a1/(a1+b1)==\Sexpr{mean.raw[1]}, ((1000*a1*b1)*(1000+a1+b1))/((a1+b1)^2*(a1+b1+1))==\Sexpr{var.raw[1]}]
eqn2 = [1000*a2/(a2+b2)==\Sexpr{mean.raw[2]}, ((1000*a2*b2)*(1000+a2+b2))/((a2+b2)^2*(a2+b2+1))==\Sexpr{var.raw[2]}]
eqn3 = [1000*a3/(a3+b3)==\Sexpr{mean.raw[3]}, ((1000*a3*b3)*(1000+a3+b3))/((a3+b3)^2*(a3+b3+1))==\Sexpr{var.raw[3]}]
s1 = solve(eqn1, a1,b1)
s2 = solve(eqn2, a2,b2)
s3 = solve(eqn3, a3,b3)
\end{sagesilent}
Solutions of Beta Binomial Parameters:
\begin{itemize}
\item $\sage{s1[0]}$
\item $\sage{s2[0]}$
\item $\sage{s3[0]}$
\end{itemize}
\end{frame}
Everything compiles just fine, and in that slide I am able to see the solutions to the three equations respective parameters in that itemized list (for example the first item in the itemized list from that beamer slide is outputted as [a1=(328/667), b1=(65272/667)] (I am not able to post an image of the beamer slide but I hope you get the idea).
I would like to save the parameter values a1,b1,a2,b2,a3,b3 into R objects so that I can use them in simulations. I cannot find any documentation in the sagetex package on how to save output from sage commands into variables for use with other programs (in this case R). Any suggestions on how to get these values into R?
Wow, you are really mixing two worlds ;)
The only idea I can give you is the "solution_dict=True" parameter for the solve command. Then you get a Python dictionary whicht might help you to just output the value. But I have no idea what exactly Sweave does and when which step of the process rewrites what.
In general, it might be better if you would write it in sagetex only and call R via the rpy2 Python wrapper. But that might be too much work for you - maybe just for one slide and then stitch them together via some pdf-merging?