How to generate full qmd file from child files? - r-markdown

I have a quarto file with several child documents. I can render them just fine.
But I would like to generate a single qmd file that contains the full document.
So basically I need some kind of qmd to qmd converter. Is there a way to do that?
Sections like this insert the children:
```{r}
#| label = "child1",
#| eval = TRUE,
#| child = "child1.qmd"
```

Seems noone has a "nice" solution. So here is a hack to put things together "manually".
It finds the code blocks with a child defined and replaces that code box with the text from the references child.
merge_children <- function(file, path_prefix, outpath, child_pattern = "[[:blank:]]*#\\|[[:blank:]]*child[[:blank:]]*=.*,+[[:blank:]]*", child_capture = "[[:blank:]]*#\\|[[:blank:]]*child.*=.*,+[[:blank:]]*"){
# read file
txt <- readLines(file)
# find children
child_defs <- txt %>% grep(child_pattern,.)
# find children paths
child_paths <- map_chr(child_defs, function(x){
txt[x] %>%
gsub(child_capture,"\\1", .) %>%
gsub('.*\\"(.*)\\".*',"\\1", .)
}
)
if(length(child_paths)==0){
writeLines(txt, outpath)
return(message("No children found. Will just copy file."))
}
child_paths <- paste0(path_prefix, child_paths)
#find start and end of relevant code sections
code_start <- txt %>% grep("^[[:blank:]]*```\\{r.*",.)
code_end <- txt %>% grep("^[[:blank:]]*```[[:blank:]]*$",.)
# read children
txt_children <- map(child_paths, readLines)
indeces <- sort(c(1, code_start, code_end+1))
txt_split <- split(txt, rep(seq(indeces), diff(c(indeces, length(txt)+1))))
has_child <- map_lgl(txt_split, ~any(grepl(child_pattern,..1))) %>% as.vector %>% which
# replace children
if(length(has_child) != length(txt_children)) stop("children replacement indeces not equal to amount of children")
txt_split[has_child] <- txt_children
# put back together
txt_new <- txt_split %>% unlist()
writeLines(txt_new, outpath)
}

Related

How to PDF render Quarto books with dynamic content?

I am writing my thesis using a Quarto book in HTML, which has some dynamic content (leaflet maps, plotly dynamic graphs). However, eventually, I will need to export the book in PDF/LaTeX, or at least Word (and then I can copy and paste into LaTeX).
When I try to export to PDF I of course run into this error:
Functions that produce HTML output found in document targeting pdf
output. Please change the output type of this document to HTML.
Alternatively, you can allow HTML output in non-HTML formats by adding
this option to the YAML front-matter of your rmarkdown file:
always_allow_html: true
Note however that the HTML output will not be visible in non-HTML
formats.
I did try to add the always_allow_html: true in my YAML file, but I get the same exact error. I also tried the conditional rendering with {.content-hidden unless-format="pdf"}, but I can't seem to get it working.
Has anyone experienced the same issue?
Using .content-visible when-format="html" and .content-visible when-format="pdf" works very smoothly.
---
title: "Conditional Rendering"
format:
html: default
pdf: default
---
## Conditional Content in Quarto
::: {.content-visible when-format="html"}
```{r}
#| message: false
library(plotly)
library(ggplot2)
p <- ggplot(mtcars, aes(wt, mpg))
p <- p + geom_point(aes(colour = factor(cyl)))
ggplotly(p)
```
```{r}
#| message: false
#| fig-pos: "H"
#| fig-width: 4
#| fig-height: 3
library(leaflet)
# took this example from leaflet docs
m <- leaflet() %>%
addTiles() %>% # Add default OpenStreetMap map tiles
addMarkers(lng=174.768, lat=-36.852, popup="The birthplace of R")
m # Print the map
```
:::
::: {.content-visible when-format="pdf"}
```{r}
library(plotly)
library(ggplot2)
p <- ggplot(mtcars, aes(wt, mpg))
p <- p + geom_point(aes(colour = factor(cyl)))
p
```
:::
I use constructs like below
p <- ggplot()
if (interactive() || opts_knit$get("rmarkdown.pandoc.to") == "html") {
ggplotly(p)
} else {
p
}
Stumbled across this one too. I'm currently checking the output format of pandoc globally
```{r, echo = F}
output <- knitr::opts_knit$get("rmarkdown.pandoc.to")
```
and then evaluate chunks conditionally:
(leaflet example from here.)
```{r, echo = F, eval = output != "latex"}
library(leaflet)
leaflet() %>%
addTiles() %>% # Add default OpenStreetMap map tiles
addMarkers(lng=174.768, lat=-36.852, popup="The birthplace of R")
```
This is optional if you want a note on a missing component in the PDF version:
```{r, echo = F, eval = output == "latex", results = "asis"}
cat("\\textit{Please see the HTML version for interactive content.}")
```
Edit
I just checked, this also works with Quarto documents for me using the below YAML header.
---
title: "Untitled"
format:
html:
theme: cosmo
pdf:
documentclass: scrreprt
---

How to filter databases in list based on column with different name

I have a list that includes different databases with different informations.
The first column of every database includes the informations that I need to create graphs. I need to filter information based on external vector referred to first column.
For example:
mtcars2 <- mtcars %>% rownames_to_column("cars_model") %>% as.data.frame()
mtcars3 <- mtcars %>% rownames_to_column("cars_model_second") %>% as.data.frame()
list_two_database <- list(mtcars2, mtcars3)
model_to_select <- c("Fiat 128", "Honda Civic", "Lotus Europa")
Is there a way to filter the list based on THE FIRST COLUMN OF EACH DATABASE included in the list (cars_model and cars_model_second) WITHOUT RENAME THE COLUMN ITSELF?
The goal is to obtain a list that includes the two databases each with the three model.
Thank you in advance
The following works by extracting the first column name as a string first_col and then converting this string into a form that can be used within dplyr:
mtcars2 <- mtcars %>% rownames_to_column("cars_model") %>% as.data.frame()
mtcars3 <- mtcars %>% rownames_to_column("cars_model_second") %>% as.data.frame()
list_two_database <- list(mtcars2, mtcars3)
model_to_select <- c("Fiat 128", "Honda Civic", "Lotus Europa")
func = function(df){
first_col = colnames(df)[1]
filter(df, !!sym(first_col) %in% model_to_select)
}
lapply(list_two_database, func)
Notes:
sym(.) is used to turn a text string into a symbol
!! only works inside dplyr commands and turns symbols into variables
Used together you have something like:
var = "my_col"
df %>% filter(!!sym(var) == 1)
Which is equivalent to df %>% filter(my_col == 1)

Select All/Deselect All Option Leaflet for R Overlay

I have a r leaflet map that has multiple layers and each layer can be checked or unchecked. I am trying to find a way to have a select all/deselect all option.
Below is my code where the overlays are reactive groups.
df() is a reactive dataframe
myLocation() is a reactive location (long,lat)
So in the map in the upper right hand corner is where I want a select all/ deselect all option
groups <- reactive({as.character(unique(df()$Folder))})
groupColors <- reactive({
colorFactor(palette = rainbow(length(groups())),
domain = df()$Folder)
})
output$mymap <- renderLeaflet({
leaflet() %>%
addTiles() %>%
leaflet::addMarkers(lng=c(myLocation()[1]), lat=c(myLocation()[2]),
popup = paste("Lat/Long: ",myLocation()[2],"/",myLocation()[1]),
popupOptions = popupOptions(maxWidth = 1000, closeOnClick = TRUE))
%>%
############more code here
#this section is where a select all/ deselect option has to be placed
addLayersControl(overlayGroups=groups(),options =
layersControlOptions(collapsed = TRUE))
})
Following shiny tutorial... try using leafletProxy to modify your existing map and use the hideGroup() function from leaflet to add/remove the entire group.

Rmarkdown Shiny Limits

I have a Rmarkdown document with an embedded shiny application (runtime: shiny) which I'd like to upload to shinyapps.io (eventually). When I build the document locally, it fails to completely build, as in the page stops halfway through the document. I've confirmed that if I remove some large leaflet objects in the middle of the document then the build finishes.
I'm working on making the leaflet objects smaller, but I've seen that there is a memory limit on Shiny apps that can be reconfigured (options(shiny.maxRequestSize=30*1024^2) for 30 MB). Supposedly this is supposed to go in the server section of an app, but if the entire document is an app, does this go in the yaml, or in a setup chunk, or somewhere else?
I was able to make an MWE that illustrates my basic environment, though it does not reproduce the error. The maps chunk shows a leaflet map of census tracts for each of the 50 states and DC, and then there's a true shiny app following.
---
title: "Test RMD"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(leaflet)
library(shiny)
library(tigris)
library(htmltools)
library(RColorBrewer)
options(shiny.maxRequestSize=30*1024^2)
```
# leaflet maps
```{r maps, echo=T,results='asis'}
us_states <- unique(fips_codes$state)[1:51] # for small, set to 2
createMaps <- function(state){
stmap <- tracts(state, cb = TRUE)
leaflet(stmap) %>% addTiles() %>% addPolygons()
}
htmltools::tagList(lapply(us_states, function(x) createMaps(x) ))
```
# Shiny application
```{r tabsets, echo=FALSE}
shinyApp(
ui = bootstrapPage(
tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
leafletOutput("map", width = "100%", height = "100%"),
absolutePanel(top = 10, right = 10,
sliderInput("range", "Magnitudes", min(quakes$mag), max(quakes$mag),
value = range(quakes$mag), step = 0.1
),
selectInput("colors", "Color Scheme",
rownames(subset(brewer.pal.info, category %in% c("seq", "div")))
),
checkboxInput("legend", "Show legend", TRUE)
)
),
server = function(input, output, session) {
# Reactive expression for the data subsetted to what the user selected
filteredData <- reactive({
quakes[quakes$mag >= input$range[1] & quakes$mag <= input$range[2],]
})
# This reactive expression represents the palette function,
# which changes as the user makes selections in UI.
colorpal <- reactive({
colorNumeric(input$colors, quakes$mag)
})
output$map <- renderLeaflet({
# Use leaflet() here, and only include aspects of the map that
# won't need to change dynamically (at least, not unless the
# entire map is being torn down and recreated).
leaflet(quakes) %>% addTiles() %>%
fitBounds(~min(long), ~min(lat), ~max(long), ~max(lat))
})
# Incremental changes to the map (in this case, replacing the
# circles when a new color is chosen) should be performed in
# an observer. Each independent set of things that can change
# should be managed in its own observer.
observe({
pal <- colorpal()
leafletProxy("map", data = filteredData()) %>%
clearShapes() %>%
addCircles(radius = ~10^mag/10, weight = 1, color = "#777777",
fillColor = ~pal(mag), fillOpacity = 0.7, popup = ~paste(mag)
)
})
# Use a separate observer to recreate the legend as needed.
observe({
proxy <- leafletProxy("map", data = quakes)
# Remove any existing legend, and only if the legend is
# enabled, create a new one.
proxy %>% clearControls()
if (input$legend) {
pal <- colorpal()
proxy %>% addLegend(position = "bottomright",
pal = pal, values = ~mag
)
}
})
}
)
```
I guess my main question is if the options() call is going in a place where Shiny can see it. It's also possible that if I made the application itself bigger that it would cause problems; I can try to get to that this evening.

Remove Hashes in R Output from R Markdown and Knitr

I am using RStudio to write my R Markdown files. How can I remove the hashes (##) in the final HTML output file that are displayed before the code output?
As an example:
---
output: html_document
---
```{r}
head(cars)
```
You can include in your chunk options something like
comment=NA # to remove all hashes
or
comment='%' # to use a different character
More help on knitr available from here: http://yihui.name/knitr/options
If you are using R Markdown as you mentioned, your chunk could look like this:
```{r comment=NA}
summary(cars)
```
If you want to change this globally, you can include a chunk in your document:
```{r include=FALSE}
knitr::opts_chunk$set(comment = NA)
```
Just HTML
If your output is just HTML, you can make good use of the PRE or CODE HTML tag.
Example
```{r my_pre_example,echo=FALSE,include=TRUE,results='asis'}
knitr::opts_chunk$set(comment = NA)
cat('<pre>')
print(t.test(mtcars$mpg,mtcars$wt))
cat('</pre>')
```
HTML Result:
Welch Two Sample t-test
data: mtcars$mpg and mtcars$wt
t = 15.633, df = 32.633, p-value < 0.00000000000000022
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
14.67644 19.07031
sample estimates:
mean of x mean of y
20.09062 3.21725
Just PDF
If your output is PDF, then you may need some replace function. Here what I am using:
```r
tidyPrint <- function(data) {
content <- paste0(data,collapse = "\n\n")
content <- str_replace_all(content,"\\t"," ")
content <- str_replace_all(content,"\\ ","\\\\ ")
content <- str_replace_all(content,"\\$","\\\\$")
content <- str_replace_all(content,"\\*","\\\\*")
content <- str_replace_all(content,":",": ")
return(content)
}
```
Example
The code also needs to be a little different:
```{r my_pre_example,echo=FALSE,include=TRUE,results='asis'}
knitr::opts_chunk$set(comment = NA)
resultTTest <- capture.output(t.test(mtcars$mpg,mtcars$wt))
cat(tidyPrint(resultTTest))
```
PDF Result
PDF and HTML
If you really need the page work in both cases PDF and HTML, the tidyPrint should be a little different in the last step.
```r
tidyPrint <- function(data) {
content <- paste0(data,collapse = "\n\n")
content <- str_replace_all(content,"\\t"," ")
content <- str_replace_all(content,"\\ ","\\\\ ")
content <- str_replace_all(content,"\\$","\\\\$")
content <- str_replace_all(content,"\\*","\\\\*")
content <- str_replace_all(content,":",": ")
return(paste("<code>",content,"</code>\n"))
}
```
Result
The PDF result is the same, and the HTML result is close to the previous, but with some extra border.
It is not perfect but maybe is good enough.