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='')
Related
I have a 500 x 500 dataset which I would like to be represented in kables in my PDF output. I've arbitrarily chosen 50 x 50 Kables for the output.
Each kabel cell will have an image in it^. Some of these examples will work with pure text so including the image is required in the minimum code.
In the code below, there is the setup and the 3 approaches I've taken:
the 500x500 approach which is obviously too big,
the list method which I can't get to actually render the tables,
the separate, dynamically named, objects^ approach which will render a table if called by exact name but can't get to render from a dynamic name.
The generation of the kables are in different code chunks to the output for ease of positioning in the PDF.
I have tried all these with results = 'asis' and it made no difference.
---
title: "Minimum Example"
date: "2022-11-19"
output: pdf_document
---
\section*{A Bit of Setup}
\renewcommand{\arraystretch}{1.7}
```{r setup}
library(kableExtra)
library(dplyr)
library(tidyr)
library(stringr)
library(png)
library(knitr)
# Image is displayed in each cell of the Kable, creating an example image
png(file="./ExampleImage.png")
par(bg='grey')
bob <- plot.new()
dev.off()
# Define size of data which needs to be represented in a kable.
totalSize_x <- 500
totalSize_y <- 500
```
\section*{Approach 1: Way too big to be displayed}
```{r Approach 1: The Big Boy}
TheBigBoi <- matrix('\\includegraphics[scale=1]{./ExampleImage.png}',totalSize_x, totalSize_y)
kable(TheBigBoi,
format = "latex",
escape = FALSE,
longtable = FALSE) |>
column_spec(1,border_left = T) |>
column_spec(totalSize_y,border_right = T) |>
kable_styling(latex_options=c("scale_down"))
# Gives the Errors
# Error: C stack usage 12426279 is too close to the limit
# Execution halted
```
\section*{Approach 2: A Nice List}
```{r Approach 2: A Nice List}
# Personally, I want this one to work as it's the least stupid solution.
maxKableCells <- 50 # As in that looks good for the images, I understand Kables can be bigger if they want to
TheLittleOne <- matrix('\\includegraphics[scale=1]{./ExampleImage.png}', maxKableCells, maxKableCells)
num_iterations <- (totalSize_x/maxKableCells) * (totalSize_y/maxKableCells)
myList <- vector(mode = "list", length = num_iterations)
for (i in 1:(num_iterations)){
myList[i] <- kable(TheLittleOne,
format = "latex",
escape = FALSE,
longtable = FALSE) |>
column_spec(1,border_left = T) |>
column_spec(maxKableCells,border_right = T) |>
kable_styling(latex_options=c("scale_down"))
}
```
Gonna put some text here
```{r Approach 2 Output}
myList[1] # - ! Undefined control sequence.
#l.177 ``\textbackslash begin\{table\}\n
# \textbackslash centering\n\textbacksl...
```
```{r Approach 2 Output Continued}
noquote(myList[1]) # - just prints out the latex code with extra slashes
```
\section*{Approach 3: Objectively the worst way to acheive this}
```{r Approach 3: Individual Kable Objects AKA Foolishness}
# The most stupid solution but the closest to working
maxKableCells <- 50 # As in that looks good for the images, I understand Kables can be bigger if they want to
TheLittleOne <- matrix('\\includegraphics[scale=1]{./ExampleImage.png}', maxKableCells, maxKableCells)
num_iterations <- (totalSize_x/maxKableCells) * (totalSize_y/maxKableCells)
for (i in 1:(num_iterations)){
assign(paste0('kable_', i), kable(TheLittleOne,
format = "latex",
escape = FALSE,
longtable = FALSE) |>
column_spec(1,border_left = T) |>
column_spec(maxKableCells,border_right = T) |>
kable_styling(latex_options=c("scale_down")))
}
```
Some filler text
```{r Approach 3 Output}
kable_1 # - This one works!
for (i in 1:num_iterations){
paste0('kabel_', i) #Returns nothing
print(paste0('kabel_', i)) # Prints like "kabel_1"
print(noquote(paste0('kabel_', i))) #Prints like kabel_1
noquote(paste0('kabel_', i)) # Returns nothing
}
```
^ I am aware this is a stupid thing to do, this whole thing is stupid but sometimes the tools we use are not our own choice.
Let's say I have the following rmd:
---
title: "Table won't work"
author: "Exhausted student"
date: "2022/01/28"
output:
bookdown::word_document2
---
```{r table, echo=F, warning=F, message=F}
library(tidyverse)
a <- tibble(
constants = c("c", "NA", "h", "e", "H2O"),
values = c(2.998e8, 6.022e23, 6.626e-34, -1.602e-19, 18.02)
)
knitr::kable(a, digits = 35)
```
which produces this table in Word.
The Problem
I need the scientific format to use superscripts and multiply sign (i.e. 2.998 × 108), and some cells requires subscript (e.g. NA and H2O).
The final table should look like this. How can I do that?
What I've tried/would never try
huxtable package and its markdown() function: I managed to format some contents as H~2~O, then enable markdown across table by huxtable(a) %>% `markdown<-`(TRUE). Which did not recognize the syntax, and apparently would not work in forseeable future according to the author.
flextable and as_sub(): Produces right format. I pass the lables to flextable::compose(), where the labels were something like as_paragraph(list_values = list("H", as_sub("2"), "O"). The code is obviously too lengthy; plus i have to manipulate cells one-by-one. Technically still doable, but I do have tables with 100+ cells needed formatting.
Output first, format in Word later: Again, needed manipulation one-by-one. Would be an option if it would work everything out automatically.
Convincing the school bureaucrats to accept html/pdf/latex as output: this is forever an impossible option.
Format outside word then export formatted table as image: strictly forbidden in the reports.
Edit: the table works now! Great thanks to Maël's answer, but please check my own findings to see my final result:
You can use tildes (~) to put in subscript and carets (^) for superscripts; and use sprintf to get the expected digit format:
---
title: "Table won't work"
author: "Exhausted student"
date: "2022/01/28"
output:
bookdown::word_document2
---
```{r table, echo=F, warning=F, message=F}
library(tidyverse)
expSup <- function(x, digits=3) {
sprintf(paste0("%05.", digits, "f x 10^%d^"), x/10^floor(log10(abs(x))), floor(log10(abs(x))))
}
a <- tibble(
constants = c("c", "N~A~", "h", "e", "H~2~0"),
values = expSup(c(2.998e8, 6.022e-23, 6.626e-34, -1.602e-19, 18.02))
)
knitr::kable(a)
```
An option that probably falls in the no-go zone for this open issue:
Create a html document,
Insert html tags for subscripts,
Open the html file (not the viewer),
ctrl+c, then ctrl+v in your word file.
---
output: html_document
---
```{r table, echo=F, warning=F, message=F}
library(tidyverse)
library(gt)
a <- tibble(
constants = c("c", "N<sub>A</sub>", "h","e","H<sub>2</sub>O"),
values = c(2.998e8, 6.022e23, 6.626e-34, -1.602e-19, 18.02)
)
a %>%
mutate(constants = map(constants, html)) %>%
gt() %>%
fmt_scientific(values)
The expSup() function in Maël's answer converted the scientific formats into markdown format. For my script, I modified the function a little:
exp_sup <- function(x, digits = 3) {
sprintf(paste0("%05.", digits, "f $\\times$ 10^%d^"), x / 10^floor(log10(abs(x))), floor(log10(abs(x))))
}
I changed "f x 10^%d^" to "f $\\times$ 10^%d^", so that it displays proper multiply symbol (×).
Using the format in flextable
The format works great in Kable. However, a large portion of my workflow requires flextable to make caption/cross reference/publication style/etc. Unfortunately, although the expSup function automatically formats scientific notations into markdowns, it cannot make markdown syntax work in flextable.
However, ftExtra::colformat_md() can. Hence, by combining the modified exp_sup() function with ftExtra, I was finally able to produce an academic-looking table:
Below is the code for my final output; if you are also trying to produce reproducible academic reports with lots of tables in Word format, hope this helps!
---
title: "The tables work!"
author: "Satisfied Student"
date: "2022/01/28"
output:
bookdown::word_document2:
reference_docx: styleRef.docx
---
```{r setup, include = F}
library(easypackages)
packages(
"tidyverse",
"flextable", # This works best for my workflow
"ftExtra", # For markdown formatting work in flextable
"officer" # You can customize appearance/format/etc. of caption *prefixes*
)
knitr::opts_chunk$set(
warning = FALSE,
message = FALSE,
echo = FALSE,
# Make the table caption format definable in reference_docx styles
tab.cap.style = "Table Caption",
# Make "Table 1:" prefixes not bold
tab.cap.fp_text = fp_text_lite(bold = FALSE)
# The tab.cap settings MUST be in a separate chunk from tables
)
# Converts scientific format to markdown
exp_sup <- function(x, digits = 3) {
sprintf(paste0("%05.", digits, "f $\\times$ 10^%d^"), x / 10^floor(log10(abs(x))), floor(log10(abs(x))))
}
# The $\\times$ makes proper multiply symbols
```
```{r table}
a <- tibble(
constants = c("c", "N~A~", "h", "e", "H~2~O"),
values = c(2.998e8, 6.022e23, 6.626e-34, -1.602e-19, 18.02)
)
a %>%
mutate(values = exp_sup(values)) %>%
flextable() %>%
set_caption(
caption = "(ref:foo)", # Produces formatted caption text
style = "Table Caption"
) %>%
colformat_md() %>% # Subscript/superscript works in flextable now!
theme_booktabs() %>% # The 3-part-table used in academics
align(align = "center", part = "all") %>% #Align everything to center
set_table_properties(layout = "autofit") # Comfortable width/height every cell
```
(ref:foo) A scientifically formatted `flextable` with ^superscripts^ and ~subscripts~
Your code should look like this:
```
{r table, echo=F, warning=F, message=F}
library(tidyverse)
a <- tibble(
constants = c("c", "NA", "h", "e", "$H_2O$"),
values = c("$2.998 * {10^{8}}$", "$6.022 * {10^{-23}}$", "$6.626 * {10^{-34}}$", "$-1.602 * {10 ^{-19}}$", "$1.802 * {10^{1}}$")
)
knitr::kable(format = "html", a, digits = 35)
```
Which will give you the output like this:
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
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 = '')
})
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...