Iteratively producing latex tables in knitr - r

I'm working on iteratively producing LaTeX tables using knitr. All is well except I'm left with extra markup before each table. Here's a simple example, though this would ideally work as a template for more complex problems, ie different-size tables, varying data sets etc.
What can I do to get rid of the extra text before each table?
\documentclass{article}
\usepackage{setspace, relsize}
\usepackage[margin=.5in, landscape]{geometry}
\usepackage{pdfpages}
\begin{document}
<<setup, include=FALSE>>=
opts_chunk$set(echo=FALSE, warning = FALSE, message = FALSE, cache = FALSE, error = FALSE)
library("ggplot2")
library("knitr")
library("Hmisc")
mytable_function = function(mydata){
foo = as.matrix(head(mydata))
colnames(foo) = names(mydata)
rownames(foo) = c("First", "Second", "Third", "Fourth", "Fifth", "Sixth")
return(foo)
}
template <- "<<thisthing-{{i}}>>=
mytable = mytable_function(iris[iris$Species == unique(iris$Species)[i],])
latex(mytable, file = '',
title = '',
where = '!h',
caption = 'This is a table',
col.just = rep('r', ncol(mytable)))
#"
for(i in 1:3){
cat(knit(text = knit_expand(text = template, i = i, quiet = TRUE)))
}
#
\end{document}
Fwiw here's a similar question I asked a while ago but because I'm producing tables here and not figures I think this is a slightly different solution.
Print a list of dynamically-sized plots in knitr

The provided code does not match the output you presented. Actually, it produces no output whatsoever.
Step 0: Reproduce output from the question
include=FALSE on the only chunk in the document is quite fatal … replace by echo=FALSE.
The main chunk (setup) as well as the template chunk need results="asis".
message=FALSE should be a chunk option of setup. Setting it as default options within setup won't affect messages from the current chunk.
Step 1: Immediate issue
This line
cat(knit(text = knit_expand(text = template, i = i, quiet = TRUE)))
shoud be
cat(knit(text = knit_expand(text = template, i = i), quiet = TRUE))
quiet is an argument of knit, not knit_expand.
Step 2: A better solution
Although this works, it's an overly complicated overkill. The answer you linked to dynamically generated chunks because fig.height is not vectorized the way it would be required for that case. Here, we can just use a single chunk:
\documentclass{article}
\begin{document}
<<setup, echo = FALSE, results='asis', message = FALSE>>=
mytable_function = function(mydata){
foo = as.matrix(head(mydata))
colnames(foo) = names(mydata)
rownames(foo) = c("First", "Second", "Third", "Fourth", "Fifth", "Sixth")
return(foo)
}
for(i in 1:3){
mytable = mytable_function(iris[iris$Species == unique(iris$Species)[i],])
Hmisc::latex(mytable,
file = '',
title = '',
where = '!h',
caption = 'This is a table',
col.just = rep('r', ncol(mytable)))
}
#
\end{document}

Related

Having issues with R markdown printing background data

Below is the code that is presenting the problem.
I can't seem to find a way to have it not print the source data before the table, house. Is this a code chunk option issue such as not having include = False in the code? To produce, you would create a new R markdown document and then put the code below in the first two code chunks (usually gray background color).
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
pkgs <- c("tidyverse","tidyquant","RODBC", "RODBCDBI",
"spdplyr","rgdal","readxl","Rcpp",
"RcppRoll", "ggforce","scales", "DBI","ggthemes",
"cowplot","gt","data.table","gridExtra","ggrepel","htmltab","tigris","tidycensus")
loader <- function(x){
for( i in x ){
if(!require(i, character.only = TRUE) ){
install.packages(i, dependencies = TRUE)
require(i, character.only = TRUE)
}
}
}
loader(pkgs)
#lapply(pkgs,library,character.only = TRUE)
```
## Housing Price
```{r, echo=FALSE,comment=FALSE,message=FALSE}
# load data
dt <- fread("http://www.freddiemac.com/fmac-resources/research/docs/fmhpi_master_file.csv")
dt[,hpa_yoy:=Index_SA/shift(Index_SA,12)-1,.(GEO_Type,GEO_Code,GEO_Name)]
dt_nv<- dt[GEO_Name=="NV" & Year>2010,]
head(dt_nv)
dcast(dt_nv,Year~Month,value.var="hpa_yoy")
dt_nv[,mname:=factor(month.abb[Month],levels=month.abb)]
dt_tab <- dcast(dt_nv,Year~mname,value.var="hpa_yoy")
house<- gt(dt_tab) %>%
opt_row_striping()%>%
fmt_percent(
columns = c(month.abb),
decimals = 1,
use_seps = FALSE
) %>%
tab_header(title="Nevada House Price Growth",
subtitle="12-month % change in Freddie Mac House Price Index" )%>%
tab_options(
data_row.padding = px(9),
row_group.padding = px(9),
table.font.size = px(11),
source_notes.font.size = px(11),
source_notes.padding = px(1))
```
## Including Plots
Housing Prices
```{r, echo=FALSE, message=FALSE}
house
```
Really unclear what the question is and I am not installing all of these packages, but from what I can tell, you need to put include=FALSE into the header of the second chunk.
The lines:
dt[,hpa_yoy:=Index_SA/shift(Index_SA,12)-1,.(GEO_Type,GEO_Code,GEO_Name)]
and head(dt_nv)
that are causing problems because they are not assigned to any object, R is just printing the table described by these line, I think.
If you are desperate, make this code eval=FALSE and then copy the code into another chunk with include=FALSE so the first block is there for show, while the actual block is evaluated without being shown in the document.

Is there a way to have the output of a function in R be an R markdown chunk?

I'm working on a project to make it easier to create flex/shiny dashboards from qualtrics surveys. I'd really like to be able to write a couple functions that would let co-workers who have less experience with R be able to make similar documents without having to know Rmarkdown syntax.
For example, if someone wanted to make a one page dashboard with a scatterplot, I'd like to be able to have them use a couple functions like (make_dashboard, make_page) etc:
make_dashboard(
title = "Qualtrics Report Dashboard",
page 1 = make_page(header = "Page 1", format = "column", render = "plot",
data = survey_data, variables = c("var1", "var2"))
)
which would then create a rmd file with this:
---
title: "Qualtrics Report Dashboard"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: scroll
runtime: shiny
---
Page 1
=====================================
renderPlot( {
ggplot(data = survey_data, mapping = aes_string(x = var1,
y = var2)) +
geom_point() +
labs(x = get_label(get(var1, survey_data)),
y = get_label(get(var2, survey_data)))
}
)
I haven't gotten very far with trying to write these functions / implement this logic, because I'm not even sure if I'm thinking about it in the right way - is it possible to create rmarkdown chunks with functions like this?
I've looked at other posts 1 and 2 about child documents in knitr, but I don't really want every chunk to be the same, rather have the person be able to change certain aspects (e.g. type of plot, data, etc.).
Not sure if this will be useful to anyone else, but I ended up using whisker (https://github.com/edwindj/whisker), which can render strings into documents to construct an Rmd in the style of flexdashboard.
TLDR: Essentially I made functions that create strings of text matching the building blocks of flexdashboard. With whisker, you can pass in variables by encasing words in the string with two bracket parentheses and then assigning their values with a list of var_name = value for each variable in the string, e.g.
template <- "My name is {{name}}."
d <- list(name = "Emily")
cat(whisker.render(template, data = d))
print(d)
My name is Emily
I used a combination of this and the str_c from stringr to construct strings for different elements of the flexdashboard, allowing the user to input variables like title, variable for plots, etc. that then could be rendered into the string using whisker. Then, I joined all of those strings together and render it into an Rmd file. Honestly, I am not sure this is actually easier for people who don't know R to use, and I'll probably end up doing something different, but I wanted to share in case anyone is thinking about this.
Example: running the chunk below creates a file called "test_dashboard.Rmd" with the text format for a flexdashboard with a 1 input sidebar and a single page with one plot.
```
make_dashboard(title = "Test Dashboard",
sidebar = make_sidebar(sidebar_title = "here is the input",
input_type = "multi-select",
input_name = "Interesting Var #1"),
page1 = make_page(page_title = "Cool Plots!",
element_one = make_plot(plot_title = "this is my plot",
type = "bivariate",
vars = c("cool_var1",
"cool_var2"))),
fn = "test_dashboard")
```
OUTPUT:
```
---
title: Test Dashboard
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: scroll
runtime: shiny
---
\```{r setup, include=FALSE}
library(flexdashboard)
library(tidytext)
library(tidyverse)
library(janitor)
library(DT)
library(gghighlight)
library(knitr)
library(shiny)
library(qualtRics)
library(curl)
library(sjlabelled)
library(naniar)
library(scales)
library(lme4)
library(MASS)
library(snakecase)
\```
\```{r global, include=FALSE}
#setting global options for table scrolling and plot theme
options(DT.options = list(scrollY="100vh"))
theme_set(theme_minimal())
#this fetches all of your survey info
surveys <- all_surveys()
#this saves the survey responses into
docusign_survey <- fetch_survey(surveyID = surveys$id[1],
verbose = TRUE,
label = TRUE,
breakout_sets = TRUE,
force_request = TRUE)
#this saves the question text into a dataframe
questions <- survey_questions(surveyID = surveys$id[1])
rename_df <- rename_variables(docusign_survey)
#this renames all of the variables
docusign_survey <- docusign_survey %>%
rename_at(as.vector(rename_df$old_name), ~ as.vector(rename_df$new_labels))
#new variables
new_var <- rename_df$new_labels
#which are multi_select?
multi_select <- rename_df %>%
filter(ms == 1) %>%
dplyr::select(new_labels)
#relabel those NAs as No
docusign_survey <- docusign_survey %>%
purrr::modify_at(multi_select$new_labels, na_to_y)
\```
Sidebar {.sidebar}
=====================================
here is the input
\```{r}
selectInput("p_var_1", label = "Interesting Var #1",
choices = new_var,
multiple = TRUE)
\```
Cool Plots!
=====================================
Column {.tabset}
-------------------------------------
### this is my plot
\```{r}
renderPlot( {
make_bivariate_plot(docusign_survey, input$cool_var1, input$cool_var2)
})
\```
```
Functions
make_dashboard()
I saved the parts that will repeat every time, probably will want to make them editable for changes in scrolling, etc. but just trying to make proof of concept at the moment.
```
make_dashboard <- function(title, sidebar, page1, fn){
load("data/top_matter.rda")
load("data/libraries.rda")
load("data/main_chunk.rda")
initial_bit <- stringr::str_c(top_matter, libraries, main_chunk, sep = "\n\n")
intermediate <- stringr::str_c(initial_bit, sidebar, sep = "\n\n")
total <- stringr::str_c(intermediate, page1, sep = "\n\n")
data <- list(title = title)
out_fn <- paste0("./", fn, ".Rmd")
writeLines(whisker.render(total, data), con = out_fn)
}
```
make_sidebar()
```
make_sidebar <- function(sidebar_title, input_type, input_name){
top_sidebar <-
'Sidebar {.sidebar}
=====================================
'
sidebar_text <- str_c(top_sidebar, sidebar_title, sep = "\n\n")
if(input_type == "multi-select"){
ms <- "TRUE"
} else {
ms <- "FALSE"
}
input_one <- make_select_input(input_name, ms)
sidebar_total <- str_c(sidebar_text, "```{r}", input_one, "```", sep = "\n\n")
return(sidebar_total)
}
```
make_page()
```
make_page <- function(page_title, element_one){
top_page <-
'{{page_title}}
=====================================
Column {.tabset}
-------------------------------------'
add_element <- stringr::str_c(top_page, element_one, sep = "\n\n")
data <- list(page_title = page_title)
page <- whisker.render(add_element, data = data)
return(page)
}
```
make_plot()
```
make_plot <- function(plot_title, type = c("univariate", "bivariate"), vars){
top_plot_piece <-' {{plot_title}}
\```{r}
renderPlot( {
'
if(type == "univariate"){
plot_piece <-
'make_univariate_plot(docusign_survey, input${{vars}})
})
\```'
total_plot <- stringr::str_c(top_plot_piece, plot_piece, sep = "\n\n")
data <- list(plot_title = plot_title,
vars = vars)
plot_chunk <- whisker.render(total_plot, data = data)
} else{
plot_piece <-
'make_bivariate_plot(docusign_survey, input${{var_1}}, input${{var_2}})
})
\```'
total_plot <- stringr::str_c(top_plot_piece, plot_piece, sep = "\n\n")
data <- list(plot_title = plot_title,
var_1 = vars[1],
var_2 = vars[2])
plot_chunk <- whisker.render(total_plot, data = data)
}
return(plot_chunk)
}
```

Create sections through a loop with knitr

See this reproducible example :
---
title: "test"
output: html_document
---
## foo
```{r}
plot(1:3)
```
## bar
```{r}
plot(4:7)
```
## baz
```{r}
plot(8:12)
```
I want to be able to automate the creation of these sections as I can't know how many they will be before going further in my analysis.
My input to get this would be :
my_list <- list(foo = 1:3, bar = 4:7, baz = 8:12)
my_fun <- plot
my_depth <- 2
And the ideal answer (though I'm welcoming any improvement) would help me build a mdapply function so that I could just run:
```{r}
mdapply(X = my_list, FUN = my_fun, title_depth = my_depth)
```
And get the same output.
R package pander can generate Pandoc's markdown on the fly.
The key is to use the chunk option results='asis' to tell R Markdown to render pander's output as Markdown.
You just need to be careful to generate valid Markdown!
Try this:
---
title: "Test sections"
output: html_document
---
## A function that generates sections
```{r}
library(pander)
create_section <- function() {
# Inserts "## Title (auto)"
pander::pandoc.header('Title (auto)', level = 2)
# Section contents
# e.g. a random plot
plot(sample(1000, 10))
# a list, formatted as Markdown
# adding also empty lines, to be sure that this is valid Markdown
pander::pandoc.p('')
pander::pandoc.list(letters[1:3])
pander::pandoc.p('')
}
```
## Generate sections
```{r, results='asis'}
n_sections <- 3
for (i in seq(n_sections)) {
create_section()
}
```
It still looks hackish, but Markdown has its limits...
It seems like I found a way!
The whole idea is to pass what would be typed by hand as a string inside of knit(text=the_string) used in inline code.
So the function basically pastes a bunch of strings together, with a bit of substitute magic to have a function that feels like it's part of the apply family.
Parameter depth decides how many # you want.
Parameter options contains the chunk options, as a vector.
A vector shouldn't be able to contain logical and characters together but here it doesn't matter as it will all be coerced to character anyway, so c(echo= FALSE, results="hide") is fine.
I expect that it's easy to break but seems to work fine when treated gently.
---
title: "test"
output: html_document
---
```{r setup, include = FALSE}
library(knitr)
mdapply <- function(X, FUN, depth, options=""){
FUN <- as.character(substitute(FUN))
list_name <- as.character(substitute(X))
if(options != "")
options <- paste(",",names(options),"=",options,collapse="")
build_chunk <- function(nm)
{
paste0(
paste0(rep("#",depth), collapse=""),
" ",
nm,
"\n\n```{r", options, "}\n",
FUN,
"(", list_name, "[['", nm, "']])\n```")
}
parts <- sapply(names(X), build_chunk)
whole <- paste(parts, collapse="\n\n")
knit(text=whole)
}
```
```{r code}
my_list <- list(foo = 1:3, bar = 4:7, baz = 8:12)
```
`r mdapply(my_list, plot, 2, c(echo=FALSE))`
I would actually suggest a solution that works a little bit different, i.e. create the R-Markdown file from an R-script and then render it from the same R-script:
# function that creates the markdown header
rmd_header <- function(title){
paste0(
"---
title: \"", title, "\"
output: html_document
---
"
)
}
# function that creates the Rmd code for the plots
rmd_plot <- function(my_list, my_fun){
paste0(
"
## ", names(my_list), "
```{r}
", deparse(substitute(my_fun)), "(", deparse(substitute(my_list)), "[[", seq_along(my_list), "]])
```
"
)
}
# your objects
my_list <- list(foo = 1:3, bar = 4:7, baz = 8:12)
my_fun <- plot
my_depth <- 2 # I actually don't get what this is for
# now write everything into an rmd file
cat(rmd_header("Your Title")
, rmd_plot(my_list, plot)
, file = "test.rmd")
# and then create the html from that
rmarkdown::render("test.rmd", output_file = "test.html")
One thing to mention here: the indentation in the Rmd file does matter and when you copy the code here, make sure that R-Studio inserts it in the R-script as intended (because often it doesn't).
Taking a similar approach to #Georgery... but in a somewhat over-engineered fashion (also somewhat more general?). Anyway, here it goes.
make_template <- function(my_list, my_fun, my_depth, my_title, my_output_type, my_template_file){
require(glue)
n <- length(my_list)
# --- Rmd header ---
make_header <- function(my_title, my_output_type){
#
my_header <- glue(
"---", "\n",
"title: ", deparse({my_title}), "\n",
"output: ", deparse({my_output_type}), "\n",
"---", "\n",
"\n",
"\n"
)
return(my_header)
}
# --- one section only ---
make_section <- function(i){
one_section <- glue(
"\n",
"\n",
paste0(rep("#", times = {my_depth}), collapse = ""), " ", names({my_list})[[i]], "\n",
"\n",
"```{{r}}", "\n",
paste0({my_fun}, "(", deparse({my_list}[[i]]), ")"), "\n",
"```", "\n",
"\n",
"\n"
)
return(one_section)
}
# --- produce whole template ---
my_header <- make_header(my_title, my_output_type)
all_my_sections <- ""
for (i in seq_along(my_list)) {
all_my_sections <- paste0(all_my_sections, make_section(i))
}
my_template <- paste0(my_header, "\n", "\n", all_my_sections)
# --- write out
cat(my_template, file = my_template_file)
}
# --- try it
make_template(my_list = list(foo = 1:3, bar = 4:7, baz = 8:12, glop = 1:7),
my_fun = "plot",
my_depth = 4,
my_title = "super cool title",
my_output_type = "html_document",
my_template_file = "my_template_file.Rmd"
)

Separate columns for text and code/output in Markdown

I am writing a small exercise book with Markdown. I would like to have the final output with the plots on a column and the document text on the other. Similar problems are addressed here and here. Unfortunately, they mainly desire one output per column. I would like to produce the output on a column and the text on the other. Really interesting is Docco, but it apprently shows the code output with the text.
A possible solution would be the RPres markdown horizontal rule: using ***, it creates two easy to use columns. But I do find documentation on its implementation in Markdown documents.
Here an image showing my results so far and an example of my code:
```{r setoption, cache=TRUE, warning=FALSE, message=FALSE, fig.width=12}
knitr::opts_chunk$set(cache=TRUE, warning=FALSE, message=FALSE, fig.width=4, echo = FALSE)
```
```{r, loadlibraries}
library(knitr)
library(lattice)
```
### Exercise 1 - 22/4/'16
Is the data set shown in the following figure symmetric or skewed? How many modes does this data set have?
```{r 1.1}
e1 <- rep(seq(1, 6, 1), c(6, 4, 2, 2, 4, 6))
barchart(table(e1), horizontal = FALSE, xlab = "", ylab = "Frequency")
```
**Solution:**
The data set is symmetric. Furthermore, it has two modes.
### Exercise 2 - 22/4/'16
Describe the shape of the dataset shown in the following figure.
```{r 2.1}
e2 <- rep(seq(1, 9, 1), c(6, 5, 4, 3, 2, 1, 1, 1, 1))
barchart(table(e2), ylab = "Frequency", horizontal = FALSE)
```
**Solution:**
The dataset is right skewed, also said right skewed, with one mode.
As you're asking for columns, my answer will be: table.
Using pipe_tables, figure and text can be alinged next to each other. However, this comes at a price:
The cells of pipe tables cannot contain block elements like paragraphs and lists, and cannot span multiple lines.
If this limitation is acceptable, pipe_tables provide a quite straightforward solution:
```{r img, fig.show = "hide", echo = FALSE}
library(knitr)
hist(rnorm(1000))
```
Figure|Explanation
-------------------------------|-------------------------
`r include_graphics(paste0(opts_chunk$get("fig.path"), "img-1.png"))`|Histogram of 1000 draws from a standard normal density.
Although the column headers cannot be omitted, you can leave them blank if desired.
Note that I initially suppress the plot (fig.show = "hide") and use include_graphics to include it afterwards. Otherwise, there would be a newline after the plot which disrupts the table.
(In knitr 1.12.3, include_graphics doesn't seem to work properly with inline code chunks. However, the current development version 1.12.25 works well.)
Extension
I hacked together an extension that allows to use a single chunk to generate and show the plots and some more features:
```{r setup, echo = FALSE}
library(knitr)
FigureNextToText <- function(number, # number of plot in chunk
text,
alt = "", # alternative text for image
label = opts_current$get("label"), # set explicitly when using inline!
ext = ".png",
headerL = " ", headerR = " ", # empty string confuses pandoc if only right header is set
widthL = 30, widthR = 30,
...) {
path <- fig_chunk(label = label, ext = ext, number = number, ...)
template <- "%s|%s
%s|%s
![%s](%s)|%s\r\n\r\n"
output <- sprintf(
template,
headerL, headerR,
paste0(rep("-", widthL), collapse = ""), paste0(rep("-", widthR), collapse = ""),
alt, path, text
)
return(asis_output(output))
}
```
```{r img, fig.show = "hide", echo = FALSE, results = "asis"}
library(knitr)
hist(rnorm(1000))
hist(runif(n = 1000, min = 0, max = 10))
FigureNextToText(1, text = "Histogram of draws from standard normal density.", widthL = 50, widthR = 10)
FigureNextToText(2, text = "Histogram of draws from uniform distribution.", headerR = "Explanation", alt = "Histogram 2.")
```
Some text.
`r FigureNextToText(2, text = "The same plot, this time inline.", label = "img", headerR = "Explanation", alt = "Histogram 2.")`
Some more text.
I know that the setup looks a little bit scary, but once FigureNextToText is defined, it can be called quite simply, e.g.:
FigureNextToText(2, text = "Histogram of draws from uniform distribution.", headerR = "Explanation", alt = "Histogram 2.")
Finding the correct values for widthL and widthR is somewhat cumbersome. This is because their effect depends on the number of characters in the cell, i.e. the filename of the image in the MD file and the alt text, too.

How can I include hyperlinks in a table within an Sweave document?

I have a data frame containing hyperlinks that I would like to present as clickable links using Sweave. I know about xtable, but am not sure how to use it to treat the contents of a data frame as LaTeX commands.
One strategy is to use the sanitize.text.function from the print function in xtable.
Setting sanitize.text.function = function(x){x} causes print simply to echo the contents of the data frame for later interpretation by LaTeX:
\documentclass{article}
\usepackage{hyperref}
\begin{document}
\title{Example of how to include hyperlinks in Sweave with \texttt{xtable}}
\author{David R. Lovell}
\maketitle
<<load-packages, include=FALSE>>=
require(xtable)
#
<<read-data, tidy=FALSE>>=
hits <- read.table(textConnection(
"Count,Link,Title
1031,http://australianbioinformatics.net/jobs,Jobs
796,http://australianbioinformatics.net/,Home"),
stringsAsFactors=FALSE, sep=",", header=TRUE)
#
<<print-xtable, echo = FALSE, results = 'asis'>>=
print(
xtable(
hits,
align="rrll",
caption="Top content on \\href{http://australianbioinformatics.net}{AustralianBioinformatics.net} in May 2014."
),
include.rownames=FALSE
)
#
<<print-xtable-href, echo = FALSE, results = 'asis'>>=
linkedHits <- transform(hits, href=paste("\\href{", Link, "}{", Title, "}", sep=""))
print(
xtable(
subset(linkedHits, select=c(Count, href)),
align="rrl",
caption="Top content on \\href{http://australianbioinformatics.net}{AustralianBioinformatics.net} in May 2014,
now with added hyperlinks."
),
include.rownames=FALSE,
sanitize.text.function = function(x){x}
)
#
\end{document}
...which produces this PDF output:

Resources