I have a LaTeX template, which I use with exam2pdf and where I would like to have the date of the exam as a parameter, whose value should be passed from R. Is that possible?
Yes, you can do this in two and a half ways:
(1) Via the header argument
You can set exams2pdf(..., header = ..., template = ...) where the content of the header is inserted into the template by replacing the %% \exinput{header} placeholder. Thus, when writing the template you can decide where exactly the header code ends up in the LaTeX code and you can make sure that the appropriate commands/packages are available. The header can then be specified in the following ways:
LaTeX code:
You can include something like header = "\\command{value}". There could be more complex pieces of LaTeX code, involving multiple lines, etc.
List of commands and values:
Instead of the full LaTeX code it might be more R-like to use a list specification like header = list(command = "value"). This is transformed internally to the LaTeX code mentioned above.
List of functions:
Finally you can also have a specification like header = list(command = valuefun) where valuefun is a function(i) so that you return a different string for the i-th random version of the exam.
List of all of the above:
A list consisting of unnamed character strings, named character string, and named functions can be used as well, combining all three specifications above.
More details are provided in the vignette("exams", package = "exams") which explains the design of exams2pdf() and how it can be leveraged. It also includes some examples which you can also copy to your working directory via exams_skeleton(write = "exams2pdf", ...). You can look at the exam.tex LaTeX template that is shipped with the package to see how you can insert a date and an ID (depending on the i-th iteration) into the PDF. For example:
exams2pdf("capitals.Rmd", template = "exam.tex",
header = list(Date = "2022-02-22", ID = function(i) paste("\\#", i)))
(2) Write a template generator
For the purposes from your question strategy (1) should be sufficient, I guess. However, if you need more control over what is done in the LaTeX template, then my recommendation would be to write a dynamic template generator. This is how exams2nops() is set up. It takes a lot of arguments that can be set by the user and then proceeds as follows:
Depending on the user arguments a corresponding nops.tex template is written in a temporary directory.
Then exams2pdf(..., template = "/path/to/nops.tex") is called to use this custom temporary template.
Some details, especially the counter for the i-th ID is still handled through the header argument.
Related
I am using exams2pdf() to generate two PDF files as:
exams2pdf(file = "ICvar.Rmd",
name = "icvar",
engine = "knitr",
verbose = FALSE,
texdir = "tmptex",
template = c("exam", "solution")
)
But I get this error:
Error in base::file(out_tex[j], open = "w+", encoding = encoding) : invalid 'description' argument
Any ideas why?
Also, is it possible to use custom templates in exams2nops() like template = c("exam", "solution") to produce two PDF files, the first with the questions; the second with the solutions? I read the vignette but could not find any information and adding template to options in exams2nops() does nothing.
The problem is that you only provide a single name = "icvar" but actually need two distinct names for template = "exam" and template = "solution", respectively. Hence, lacking a second name lead to the somewhat cryptic error message. A simple solution is to provide a vector of two name = c("icex", "icsol"), say.
Additionally, I have just committed a fix to the development version on R-Forge that points this out more clearly in ?exams2pdf, throws an intelligible warning, and provides a workaround. If you use your code above, name is changed to name = c("icvar_exam", "icvar_solution") automatically.
As for exams2nops(): Internally this essentially sets up a standardized template via make_nops_template() and then calls exams2pdf(). No additional templates can be provided. The reason for this is that all the convenient options in the NOPS template (e.g., adding an intro, selecting the language, switching to twocolumn layout, etc.) would only work for the NOPS template but not the other templates provided. Therefore, if you want to produce a solution sheet you have to use another call to exams2pdf() (or exams2html() or exams2pandoc()) after setting the same random seeds as for exams2nops().
When I try to generate the exams' solution with the exams2nops(...template="solution"...) I get the following error message:
Error in exams2pdf(file, n = n, nsamp = nsamp, dir = dir, name = name, :
formal argument "template" matched by multiple actual arguments
How can I produce an exams' solution with the exams2nops?
You cannot do that in one go, you need two runs after setting the same seed, e.g.,
set.seed(1)
exams2nops(my_exam)
set.seed(1)
exams2pdf(my_exam, template = "my_solution.tex")
You can use the solution.tex provided within the package as a starting point for my_solution.tex. But you may want to translate it to your natural language, use the name of your university, possibly insert a logo, add your actual exam name, possibly some into text etc. In exams2pdf() you need to add these things in the template LaTeX file directly.
won't the template="solution" not work in the exams2pdf? Also, can we do something like:
usepackage = "pdfpages", intro = intro2,... ?
I'm using exams2nops() from R/exams to prepare multiple variants of an exam (as part of an open-book-exam). I'd like to obscure the variant/group of exam a test-taker is assigned to (in order to prevent "team work" during the open book exam: "Hey guys, who else is in group 1 ?!").
By default, the exams2nops() function will print the exam ID automatically on the title page (in my case: 20112600001):
It appears there's no argument in the exams2nops() function to prevent the exam ID to be printed on the title page of the PDF exam. I am unsure where to adapt the underlying TeX template.
So my question is: How to suppress the exam ID on the title page of the PDF for NOPS exams?
You are correct that this is not possible in exams2nops(), the simple reason being that the ID is essential for automatically evaluating NOPS exams after scanning them. Thus, if you want to scan the NOPS PDF file you must not remove the ID. The standard strategy for making team work on the same ID impossible would be to simply generate a different random PDF with a different ID for every participant.
If you are not actually scanning the exam, then I would recommend using exams2pdf() rather than exams2nops() and simply "roll your own" LaTeX template. If you want to take inspiration from the NOPS template, then you can create one on the fly (here with 2 exercises) via:
make_nops_template(2, file = "mynops.tex")
Note, however, that this has quite a few options that can be controlled through appropriate header commands in exams2pdf(), e.g.,
exams2pdf(c("anova", "boxplots"),
template = "mynops.tex",
header = list(
nopsinstitution = "Sauer School of Statistics",
nopstitle = "Exam",
nopscourse = " (AWM)",
"newcommand{\\mylogo}" = ""
)
)
In addition to the elements above, one would usually specify Date, ID and the NOPS language comments (see ?nops_language). But rather than using the header argument for this I would recommend to edit mynops.tex "by hand" and hard-code all the relevant aspects, including omitting the ID.
I am currently trying to utilize parameterized reports to allow users to input a dataset (and a few more variables of interest) that will then be fed into an R script that performs and outputs a variety of analyses. These datasets will have information on multiple subjects, and the goal is to produce one report for each subject within the dataset. Thus, I utilize a for loop that loops through the Usernames within the dataset (called map). I then input a .Rmd file which is responsible for the bulk of the analysis. The for loop essentially refers to this .Rmd file for the 50 or so subjects, and outputs the 50 or so reports.
for (id in unique(map$UserName)){
# bunch of code for processing
render(input = "../lib/scripthtml.Rmd",output_file = paste0('report.',
id, '.html'),"html_document",
output_dir = "Script_output", params = "ask") }
What I am currently trying to do is I am trying to utilize parameterized reports in Shiny to allow for the user to input their own dataset (map). Thus, I specified a parameter and utilized params = ask in the render step. The main issue lies here:
Since the render step is under the for loop, it is basically run for each subject. As a result, the params ask interface loads up 50 times, asking for the user to provide their dataset each time.
Is there anyway I can avoid this? How can I get a user to supply their dataset file as a parameter, then utilize it for all 50 reports?
All your variables may be passed through in your render command, I do this for thousands of reports currently.
YAML of .Rmd template
This may include default values for certain parameters depending on your requirements, for illustrative purposes I have left them as empty strings here.
---
params:
var1: ""
var2: ""
var3: ""
---
Loading data set
In shiny, you can have the file input once and re-use for each report. Passing elements of the data frame to the render command in the next section.
Pseudo code for render in for loop
for (i in 1:n) {
rmarkdown::render(
"template.Rmd",
params = list(
var1 = df$var1[i],
var2 = df$var2[i],
var3 = df$var3[i]
),
output_file = out_file
)
}
Note: within a shiny app, you will need to use df()$var1 assuming the file input will become a reactive function.
You can then use the parameters throughout your template using the params$var1 convention.
I am using tableNominal{reporttools} to produce frequency tables. The way I understand it, tableNominal() produces latex code which has to be copied and pasted onto a text file and then saved as .tex. But is it possible to simple export the table produced as can be done in print(xtable(table), file="path/outfile.tex"))?
You may be able to use either latex or latexTranslate from the "Hmisc" package for this purpose. If you have the necessary program infrastructure the output gets sent to your TeX engine. (You may be able to improve the level of our answers by adding specific examples.)
Looks like that function does not return a character vector, so you need to use a strategy to capture the output from cat(). Using the example in the help page:
capture.output( TN <- tableNominal(vars = vars, weights = weights, group = group,
cap = "Table of nominal variables.", lab = "tab: nominal") ,
file="outfile.tex")