knitr hook to separate 000's, but not for years - r

I define a hook at the top of my rnw to separate '000s with commas:
knit_hooks$set(inline = function(x) {
prettyNum(x, big.mark=",")
})
However, there are some numbers that I don't want to format like this, such as years. Is there a better way to write the hook, or a way to override the hook when I print \Sexpr{nocomma} in the example below?
\documentclass{article}
\begin{document}
<<setup>>=
library(knitr)
options(scipen=999) # turn off scientific notation for numbers
opts_chunk$set(echo=FALSE, warning=FALSE, message=FALSE)
knit_hooks$set(inline = function(x) {
prettyNum(x, big.mark=",")
})
wantcomma <- 1234*5
nocomma <- "September 1, 2014"
#
The hook will separate \Sexpr{wantcomma} and \Sexpr{nocomma}, but I don't want to separate years.
\end{document}
Output:
The hook will separate 6,170 and September 1, 2,014, but I don’t want to separate years.

If the only things your don't want comma-separated are strings that have years in, use:
knit_hooks$set(inline = function(x) {
if(is.numeric(x)){
return(prettyNum(x, big.mark=","))
}else{
return(x)
}
})
That works for your calendar string. But suppose you want to just print a year number on its own? Well, how about using the above hook and converting to character:
What about \Sexpr{2014}? % gets commad
What about \Sexpr{as.character(2014)}? % not commad
or possibly (untested):
What about \Sexpr{paste(2014)}? % not commad
which converts the scalar to character and saves a bit of typing. We're not playing code golf here though...
Alternatively a class-based method:
comma <- function(x){structure(x,class="comma")}
nocomma <- function(x){structure(x,class="nocomma")}
options(scipen=999) # turn off scientific notation for numbers
opts_chunk$set(echo=FALSE, warning=FALSE, message=FALSE)
knit_hooks$set(inline = function(x) {
if(inherits(x,"comma")) return(prettyNum(x, big.mark=","))
if(inherits(x,"nocomma")) return(x)
return(x) # default
})
wantcomma <- 1234*5
nocomma1 <- "September 1, 2014" # note name change here to not clash with function
Then just wrap your Sexpr in either comma or nocomma like:
The hook will separate \Sexpr{comma(wantcomma)} and \Sexpr{nocomma(nocomma1)}, but I don't want to separate years.
If you want the default to commaify then change the line commented "# default" to use prettyNum. Although I'm thinking I've overcomplicated this and the comma and nocomma functions could just compute the string format themselves and then you wouldn't need a hook at all.
Without knowing exactly your cases I don't think we can write a function that infers the comma-sep scheme - for example it would have to know that "1342 cases in 2013" needs its first number commad and not its second...

Related

Inserting r code into text that is not hardcoded in Rmarkdown file

I am creating a document with Rmarkdown. The document will have lots of different versions and all of the text for the different versions has been written in an Excel spreadsheet, which is then read into the Rmarkdown file. Inside the text, which sometimes differs between reports, there are keywords in square brackets which need to be replaced with r code. I am having trouble getting the rcode to evaluate inside the text and print out properly in the Rmarkdown output.
# Text like that in the Excel spreadsheet
report_text <- ("There are [NumberFruit] fruits in the fruitbowl. [HighestPercent]% of the fruit are [HighestPercentType].")
#Extract variables within the square brackets
variables <- str_extract_all(report_text, "\\[[A-Z,a-z]+\\]")
# Define all varaibles - the variables are the same in each report. The data in the actual report differs and is defined from a dataframe.
for (i in unlist(variables)){
if(grepl("NumberFruit", i)){
NumberFruit <- 10
} else if(grepl("HighestPercent", i)){
HighestPercent <- 56
} else if(grepl("HighestPercentType", i)){
HighestPercentType <- "apples"
} else if (length(unlist(variables)) > 3){
stop("Additional VARIABLE:",i, "has not been assigned")
}
}
Once the variables have been defined I would normally use something like below, but as the text isn't hardcoded into the Rmarkdown file this approach isn't possible.
final_text <- paste0("There are ",NumberFruit, " fruits in the fruitbowl. ",HighestPercent, "% are ",HighestPercentType, ".")
So I have tried formatting the text as per the paste option above, but this does not produce the desired output.
report_text2 <- gsub('\\[', '",',(gsub('\\]', ', "', report_text)))
#Also tried
report_text2 <- paste0(gsub('\\[', '",',(gsub('\\]', ', "', report_text))))
I then use r final text in the Rmarkdown text to create the text in the report. Both versions of the above code have the same result shown below.
Current Rmarkdown output: There are ",NumberFruit," fruits in the fruitbowl. ",HighestPercent, "% of the fruit are ",HighestPercentType, "."
Desired Rmarkdown output: There are 10 fruits in the fruitbowl. 56% of the fruit are apples.
I have googled for clues on what else to try but have not been able to find anything and am a bit stuck on where to go from here. Any advice on how to get this working would be greatly appreciated. I do not normally deal with text strings and feel like I am missing something fundamental here.
If we need to interpolate the variables created in RMD file, an option is with glue
library(glue)
library(stringr)
final_text <- glue::glue(str_replace_all(str_replace_all(report_text,
"\\[", "{"), "\\]", "}"))
Full code in RMD file
---
title: "test"
author: "akrun"
date: "16/03/2021"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## R Markdown
```{r test1}
report_text <- "There are [NumberFruit] fruits in the fruitbowl. [HighestPercent]% of the fruit are [HighestPercentType]."
NumberFruit <- 10
HighestPercent <- 56
HighestPercentType <- "apples"
```
```{r test2}
library(stringr)
final_text <- glue::glue(str_replace_all(str_replace_all(report_text, "\\[", "{"), "\\]", "}"))
```
`r final_text`
-output

knitr changes (1) to <ol> when rendering html?

The following content of a .Rmd file:
---
title: "Untitled"
output:
html_document: default
---
```{r cars}
mtcars$am <- sprintf("(%s)", as.character(mtcars$am))
knitr::kable(mtcars, format = "html")
```
Will show ordered lists <ol><li></li></ol> in the am column, instead of the numbers in brackets (as produced with the sprintf) after rendering to html.
Is this intended? How can I work around this and have numbers in brackets show as they are in the html output?
The output of knitr::kable seems to be fine, showing:
<td style="text-align:left;"> (1) </td>
Details:
Using knitr 1.20
RStudio Server 1.1.453
note that removing format = "html" does not resolve the issue as in the real-life context I would like to do advanced formatting with css e.g. based on the classes of the produced tables
A quick workaround solution based on Michael Harper's accepted answer may be a method like so:
replacechars <- function(x) UseMethod("replacechars")
replacechars.default <- function(x) x
replacechars.character <- function(x) {
x <- gsub("(", "&lpar;", x, fixed = TRUE)
x <- gsub(")", "&rpar;", x, fixed = TRUE)
x
}
replacechars.factor <- function(x) {
levels(x) <- replacechars(levels(x))
x
}
replacechars.data.frame <- function(x) {
dfnames <- names(x)
x <- data.frame(lapply(x, replacechars), stringsAsFactors = FALSE)
names(x) <- dfnames
x
}
Example use:
mtcars <- datasets::mtcars
# Create a character with issues
mtcars$am <- sprintf("(%s)", as.character(mtcars$am))
# Create a factor with issues
mtcars$hp <- as.factor(mtcars$hp)
levels(mtcars$hp) <- sprintf("(%s)", levels(mtcars$hp))
replacechars(mtcars)
If you don't want to remove the format="html" argument, you could try using the HTML character entities for the parentheses (&lpar and &rpar) and then add the argument escape = FALSE:
```{r cars}
mtcars$am <- sprintf("&lpar;%s&rpar;", as.character(mtcars$am))
knitr::kable(mtcars, format = "html", escape = FALSE)
```
Still not entirely sure of what is causing the error though. It seems that the specific combination of parentheses is being processed strangely by knitr.
An alternative solution is to escape the parentheses, e.g.,
mtcars$am <- sprintf("\\(%s)", as.character(mtcars$am))
Then you won't need escape = FALSE.
See https://pandoc.org/MANUAL.html#backslash-escapes in Pandoc's Manual.

knitr: Add figure notes

I have a figure that looks like this:
<<foo, fig.lp='', fig.cap='name', fig.subcap=c('left', 'right'),>>=
plot1
plot2
#
Now I would like to display a set of notes about this figure right below (i.e. a multiline text). Is there any convenient way to do this within the figure environment created by knitr?
As already pointed out in the comments above, there is currently no solution to my problem. I have filed a feature request.
I know this is a really late answer but here is what I ended up doing for the same type of a problem.
I defined a custom hook that would plot the image as I want it
# Custom knitr hook to add notes to the plot
knit_hooks$set(plot = function(x, options) {
paste("\n\\end{kframe}\n\\begin{figure}\n",
"\\includegraphics[width=\\maxwidth]{",
opts_knit$get("base.url"), paste(x, collapse = "."),
"}\n",
"\\textsc{Note} -- here is some car stuff with notes",
"\\caption{", options$fig.cap, "}\n",
"\n\\end{figure}\n\\begin{kframe}\n",
sep = '')
})
Here is the full .Rnw
\documentclass{article}
\usepackage[font=large,labelfont=sc]{caption}
\begin{document}
<<setup, echo=FALSE, message=FALSE, results='hide'>>=
suppressPackageStartupMessages({
library(ggplot2)
})
opts_chunk$set(echo=FALSE)
opts_chunk$set(results="hide")
#
<<foo, fig.cap='with notes', fig.height=4, fig.width=6>>=
# save a regular plotting function
regular_plot <- knit_hooks$get("plot")
# Custom knitr hook to add notes to the plot
knit_hooks$set(plot = function(x, options) {
paste("\n\\end{kframe}\n\\begin{figure}\n",
"\\includegraphics[width=\\maxwidth]{",
opts_knit$get("base.url"), paste(x, collapse = "."),
"}\n",
"\\textsc{Note} -- here is some car stuff with notes",
"\\caption{", options$fig.cap, "}\n",
"\n\\end{figure}\n\\begin{kframe}\n",
sep = '')
})
ggplot(data = mtcars) + geom_point(aes(disp,mpg))
#
<<bar, fig.cap='without notes', fig.height=4, fig.width=6>>=
# restore regular plotting function
knit_hooks$set(plot = regular_plot)
ggplot(data = mtcars) + geom_point(aes(disp,mpg))
#
\end{document}
and here is the resulting PDF:
akhmed answer was absolutely amazing!
I made an slighly modification for Rmd to get caption first and to generalize for every chunk in the document.
We have to add these lines at the beginning:
knit_hooks$set(plot = function(x, options, .notes = notes, .sources = sources) {
paste("\n\n\\begin{figure}\n",
"\\includegraphics[width=\\maxwidth]{",
opts_knit$get("base.url"), paste(x, collapse = "."),
"}\n",
"\\caption{",options$fig.cap,"}","\\label{fig:",opts_current$get("label"),"}","\\textsc{}",
"\n\\textsc{Notas} -- ",.notes,
"\n\\textsc{Fuentes} -- ", .sources,
"\n\\end{figure}\n",
sep = '')
})
Then in every chunk we only write the notes and sources of the plot
notes = "Notes to explain the plot"
sources = "Explain the sources"
Again, thanks a lot akhmed!!
Pd: I use "\\textsc{}" to generate an space among caption and notes & sources.
It would be nice to generalize this to use subcaptions with many figures in the same plot.
The solution from #akhmed was enormously helpful for me. I needed to make a few additional customizations which I pass along as an answer (it was too long for a comment).
First, I wanted a little more control over the margins for the note and found adding the minipage environment helped (\\begin{minipage} below set at 6 inches wide).
Second, I added a couple of minor formatting additions by setting a typeface size and left justifying the text (\\small and \\begin{flushleft} below).
Finally, for some figures, I wanted to use the fig.pos="h!" or figure position = "here" option of Knitr / Latex and it took me a minute to realize that this hook overwrites that chunk option so I manually added it as \\begin{figure}[h!].
Again, my thanks to #akhmed for offering this solution.
knit_hooks$set(plot = function(x, options) {
paste("\n\\end{kframe}\n\\begin{figure}[h!]\n",
"\\includegraphics[width=\\maxwidth]{",
opts_knit$get("base.url"), paste(x, collapse = "."),
"}\n",
"\\begin{minipage}{6in}\\small\\begin{flushleft}Note: Lorem ipsum \\end{flushleft}\\end{minipage}",
"\\caption{", options$fig.cap, " \\label{", options$fig.lp, opts_current$get("label"), "}}\n",
"\n\\end{figure}\n\\begin{kframe}\n",
sep = '')
})

Hmisc latex fuction need to remove the first line

Im using Hmisc in rmarkdown file. when I create a table this is what I do
---
output: pdf_document
---
```{r Arrests Stats, results ='asis', message = FALSE, warning = FALSE, echo = FALSE}
# render the table
options(digits=1)
library(Hmisc)
latex(head(mtcars), file="")
```
The latex output has the first row showing as below
%latex.default(cstats, title= title....
\begin{table}...
.
.
.
\end{tabular}
Notice the '%' I need to figure out to remove the first line as it shows on the PDF document when its weaved
Looks like that's hard-coded into latex.default (cat("%", deparse(sys.call()), "%\n", file = file, append = file != "", sep = "") is in the body, with no conditional surrounding it).
I think your best guess then would be to capture.output the cat-d output and strip the comment yourself.
cat(capture.output(latex(head(mtcars), file=''))[-1], sep='\n')
The capture.output catches all the stuff that latex(...) cats, the [-1] removes the first line (being the '%latex.default'), the cat prints out everything else, with newline separator.
You might define your own mylatex to do this, and be a little more clever (e.g. instead of blindly stripping the first line of the output, you could only strip it if it started with '%').
mylatex <- function (...) {
o <- capture.output(latex(...))
# this will strip /all/ line-only comments; or if you're only
# interested in stripping the first such comment you could
# adjust accordingly
o <- grep('^%', o, inv=T, value=T)
cat(o, sep='\n')
}
mylatex(head(mtcars), file='')

How do I print superscripts in a table using xtable and sweave?

So my problem statement is as follows :
I have defined a data frame in my Sweave document (.Rnw extension) using the following code:
<<label=table2_1,echo=FALSE>>=
table2_1_rows <- c('Students with compulsory Evaluations',
'Teachers with compulsory evaluations1',
'Teachers without Evaluation2',
'Students without compulsory evaluations3'
)
table2_1_data <- c(1,2,3,4)
table2_1final <- data.frame(table2_1_rows,table2_1_data)
#
<<label=tab1,echo=FALSE,results=tex>>=
print(xtable(table2_1final,caption= ' ',align="|c|c|c|c|c|c|"),include.rownames=FALSE)
#
How do I make xtable print the numbers 1,2,3 (following the word Evaluation) as superscripts?
The actual superscript is quite easy. \\textsuperscript{1} in conjunction with sanitize.text.function = identity will get you there.
I had to make some other changes to your example. There are too many columns in align and the underscores in the variable names cause problems compiling tex.
<<label=table2_1,echo=FALSE>>=
require(xtable)
table2_1_rows <- c('Students with compulsory Evaluations',
'Teachers with compulsory evaluations\\textsuperscript{1}',
'Teachers without Evaluation\\textsuperscript{2}',
'Students without compulsory evaluations\\textsuperscript{3}'
)
table2_1_data <- c(1,2,3,4)
table2_1final <- data.frame(table2_1_rows,table2_1_data)
names(table2_1final) <- c("rows", "data")
#
<<label=tab1,echo=FALSE,results=tex>>=
print(xtable(table2_1final,caption= ' ',align="|c|c|c|"),include.rownames=FALSE,
sanitize.text.function = identity)
#

Resources