I've provided a small reproducible example below. I would like to generate tabs in quarto for each of the ggplot objects within a named list plots. The below quarto document would render the figures in their own 2nd level heading, but not in tabs as expected.
---
title: "Untitled"
format: html
---
```{r}
library(tidyverse)
data <- iris %>% as_tibble()
plots <- data %>%
group_nest(Species) %>%
deframe() %>%
map(., ~ {
ggplot(.x, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point()
})
```
# Iris Plots
::: {.panel-tabset}
```{r}
#| column: screen
#| fig-width: 12
#| fig-height: 8
#| fig-align: center
#| results: asis
iwalk(plots, ~ {
cat('## ', .y, '\n\n')
print(.x)
cat('\n\n')
})
```
:::
The document would correctly render the plots within tabs as expected when the chunk-options (all except results:asis) were removed.
# Iris Plots
::: {.panel-tabset}
```{r}
#| results: asis
iwalk(plots, ~ {
cat('## ', .y, '\n\n')
print(.x)
cat('\n\n')
})
```
:::
Updated Answer
Now if you want to generate tabsets which would expand the whole width of the screen, wrap the .panel-tabset div with the .column-screen div.
Note that, we have to use more : for .column-screen than that of .panel-tabset div. We have used three : for .panel-tabset, so we have to use four more of the : to create div for column-screen.
---
title: "Panel tabs"
format: html
---
```{r}
library(tidyverse)
data <- iris %>% as_tibble()
plots <- data %>%
group_nest(Species) %>%
deframe() %>%
map(., ~ {
ggplot(.x, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
theme_bw(
base_size = 18 # to increase the size of the plot elements
)
})
```
# Iris Plots
:::: {.column-screen}
::: {.panel-tabset}
```{r}
#| results: asis
#| fig-width: 14
#| fig-height: 6
iwalk(plots, ~ {
cat('## ', .y, '\n\n')
print(.x)
cat('\n\n')
})
```
:::
::::
Now the panel tabsets are expanded to the width of the screen.
Also Note that, I have increased the size of elements of the plot (e.g. axis.title, axis.text etc.) with base_size = 18 in plot-theme.
Old Answer
Your first approach would work if you just remove the column: screen and fig-align: center from the chunk option.
Because these two chunk options are preventing .panel-tabset from properly creating the divs for rendering tabsets.
So this works after removing these two chunk option (and you don't need fig-align: center since figures in tabsets are by default centered.
# Iris Plots
::: {.panel-tabset}
```{r}
#| fig-width: 12
#| fig-height: 8
#| results: asis
iwalk(plots, ~ {
cat('## ', .y, '\n\n')
print(.x)
cat('\n\n')
})
```
Related
I am new to shiny/flexdashboard and so far have been able to render plots and filter dataframe by using values from selectInput with help of req(input$user_input_value) .
ISSUE: To run kmeans I am taking user input for number of clusters which I am not able to code it in reactive format and getting error: object of type closure is not subsettable.
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
library(tidyverse)
library(tidytext)
library(scales)
library(glue)
library(widyr)
library(factoextra)
```
df
1 2 3 4
Angola -0.08260540 0.034325891 -0.02013353 -0.014063951
Armenia -0.06613693 -0.044308626 -0.13230387 -0.024534033
Azerbaijan -0.07562365 -0.003670707 0.05886792 -0.219660410
Bahrain -0.08275891 0.035843793 -0.02280102 -0.008044934
Bangladesh -0.08306371 0.032998297 -0.02634819 -0.017627316
Bosnia & Herzegovina -0.06303898 -0.050781511 -0.15183954 0.016794674
(Note: I have placed the csv file in github & mentioned its link below. For kmeans the character column should be used as rownames which represents country here.)
UPDATED df CREATION STEP
svd_dimen_all_wide <- read.csv(url("https://raw.githubusercontent.com/johnsnow09/covid19-df_stack-code/main/svd_dimen_all_wide.csv"))
svd_dimen_all_wide <- as.data.frame(svd_dimen_all_wide)
rownames(svd_dimen_all_wide) <- svd_dimen_all_wide$X
svd_dimen_all_wide <- svd_dimen_all_wide[,2:ncol(svd_dimen_all_wide)]
flexdashboard
---
title: "UN Country Votes"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
theme: space
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
library(tidyverse)
library(tidytext)
library(scales)
library(glue)
library(widyr)
library(factoextra)
Page NAme
=====================================
Inputs {.sidebar}
-----------------------------------------------------------------------
```{r}
selectInput("number_of_clusters", label = h3("Number of Clusters"),
choices = c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) ,
selected = 6)
```
Column {data-width=1000}
-----------------------------------------------------------------------
```{r include=FALSE}
set.seed(123)
km.res <- reactive({
# req(input$number_of_clusters)
kmeans(svd_dimen_all_wide, as.numeric(input$number_of_clusters), nstart = 25)
})
df_with_cluster <- cbind(svd_dimen_all_wide, cluster = km.res$cluster)
df_with_cluster <- rownames_to_column(df_with_cluster, "country")
df_with_cluster <- df_with_cluster %>%
select(country, cluster, everything())
```
UPDATED ATTEMPT:
renderPrint({
df_with_cluster <- cbind(svd_dimen_all_wide, cluster = km.res()$cluster)
df_with_cluster <- rownames_to_column(df_with_cluster, "country")
df_with_cluster <- df_with_cluster %>%
select(country, cluster, everything())
head(df_with_cluster)
})
### Comparison of Countries on Yes% of Bi Words
```{r}
renderPlot({
world_data %>%
left_join((df_with_cluster %>%
mutate(country_code = countrycode(country, "country.name", "iso2c"))
),
by = c("country_code")) %>%
filter(!is.na(cluster)) %>%
ggplot(aes(x = long, y = lat, group = group,
fill = as.factor(cluster))) +
geom_polygon() +
theme_map() +
scale_fill_discrete() +
labs(fill = "cluster",
title = "World Clusters based on UN voting",
caption = "created by ViSa") +
theme(plot.title = element_text(face = "bold", size = 16))
})
```
The problem is in a reactive chunk. The reactive expression km.res uses an input number of clusters, runs a model, and saves the output. (and let's end the code chunk here).
Next, decide what do you want to do with the output?
to print the result, use renderPrint
to show as a plot, use renderPlot,
to show as a table, user renderTable, etc.
Now Let's print the output of the model with renderPrint() the output can be accessed by calling the expression’s name followed by parenthesis, e.g., km.res()
Column {data-width=1000}
-----------------------------------------------------------------------
```{r include=FALSE}
km.res <- reactive({
req(input$number_of_clusters)
set.seed(123)
kmeans(svd_dimen_all_wide, as.numeric(input$number_of_clusters), nstart = 25)
})
```
###
```{r model}
renderPrint({
df_with_cluster <- cbind(svd_dimen_all_wide, cluster = km.res()$cluster)
head(df_with_cluster)
})
```
Here is my blog post very relevant to this problem https://towardsdatascience.com/build-an-interactive-machine-learning-model-with-shiny-and-flexdashboard-6d76f59a37f9?sk=922526470699966c3f47b24843404a15
I'm trying to create a dynamic number of tabs in my rmd with some content inside.
This one doesn't help.
Something like this:
---
title: "1"
output: html_document
---
```{r }
library(highcharter)
library(tidyverse)
iris %>%
dplyr::group_split(Species) %>%
purrr::map(.,~{
# create tabset for each group
..1 %>%
hchart("scatter", hcaes(x = Sepal.Length, y = Sepal.Width))
})
```
You can set results = 'asis' knitr option to generate the tabs in the map function using cat.
Getting Highcharter to work with asis was trickier :
Highchart needs to be called once before the asis chunck, probably to initialize properly, hence the first empty chart.
to print the chart in the asis chunck, the HTML output is sent in character format to cat
Try this:
---
title: "Test tabs"
output: html_document
---
`r knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, cache = F)`
```{r}
library(highcharter)
library(tidyverse)
# This empty chart is necessary to initialize Highcharter in the tabs
highchart(height = 1)
```
```{r, results = 'asis'}
cat('## Tabs panel {.tabset} \n')
invisible(
iris %>%
dplyr::group_split(Species) %>%
purrr::imap(.,~{
# create tabset for each group
cat('### Tab',.y,' \n')
cat('\n')
p <- hchart(.x,"scatter", hcaes(x = Sepal.Length, y = Sepal.Width))
cat(as.character(htmltools::tagList(p)))
})
)
```
Note that while this solution works well, it goes beyond the original use for asis
Easy question: How do I create a flexdashboard with 4 (or more) charts in chart stack in one column.
Documentation alludes that it should be possible.
I thought it would be as simple as:
---
title: "Dygraphs Linked Time Series"
output:
flexdashboard::flex_dashboard:
orientation: columns
social: menu
source_code: embed
---
```{r setup, include=FALSE}
library(dygraphs)
library(quantmod)
library(flexdashboard)
getSymbols(c("MSFT", "HPQ", "INTC"), from = "2014-01-01", auto.assign=TRUE)
```
### Microsoft
```{r}
dygraph(MSFT[,2:4], group = "stocks") %>%
dySeries(c("MSFT.Low", "MSFT.Close", "MSFT.High"), label = "MSFT")
```
### HP
```{r}
dygraph(HPQ[,2:4], group = "stocks") %>%
dySeries(c("HPQ.Low", "HPQ.Close", "HPQ.High"), label = "HPQ")
```
### Intel
```{r}
dygraph(INTC[,2:4], group = "stocks") %>%
dySeries(c("INTC.Low", "INTC.Close", "INTC.High"), label = "INTC")
```
### Fourth Row
```{r}
dygraph(INTC[,2:4], group = "stocks") %>%
dySeries(c("INTC.Low", "INTC.Close", "INTC.High"), label = "INTC")
```
All you have to do is insert a paragraph between
instead of
```
### Fourth Row
use
```
### Fourth Row
I managed to create a html document that creates dynamic tabsets based on a list of items. Adding one plot works fine on one tabset. How can I add now multiple plots on one tabset?
Hereby the code I started from but it only shows 1 plot per tabset when you knit the document to html output. obviously there is still something missing.
---
title: "R Notebook"
output:
html_document:
df_print: paged
editor_options:
chunk_output_type: inline
---
### header 1
```{r}
library(ggplot2)
df <- mtcars
pl_list <- list()
pl1 <- qplot(cyl, disp, data = df[1:12,])
pl2 <- qplot(mpg, cyl, data = df[13:20,])
pl3 <- qplot(mpg, cyl, data = df[21:30,])
pl4 <- qplot(mpg, cyl, data = df[1:12,])
pl_list[[1]] <- list(pl1, pl3, "one")
pl_list[[2]] <- list(pl2, pl4, "two")
```
### header {.tabset}
```{r, results = 'asis', echo = FALSE}
for (i in seq_along(pl_list)){
tmp <- pl_list[[i]]
cat("####", tmp[[3]], " \n")
print(tmp[1])
cat(" \n\n")
}
```
There are a couple of improvements you can do.
Create cat header function with arguments for text and level.
With it you don't need to call cat multiple times and it creates wanted number of # automatically.
catHeader <- function(text = "", level = 3) {
cat(paste0("\n\n",
paste(rep("#", level), collapse = ""),
" ", text, "\n"))
}
print plots using lapply.
Full code looks like this:
---
title: "R Notebook"
output:
html_document:
df_print: paged
editor_options:
chunk_output_type: inline
---
```{r, functions}
catHeader <- function(text = "", level = 3) {
cat(paste0("\n\n",
paste(rep("#", level), collapse = ""),
" ", text, "\n"))
}
```
### header 1
```{r}
library(ggplot2)
df <- mtcars
pl_list <- list()
pl1 <- qplot(cyl, disp, data = df[1:12,])
pl2 <- qplot(mpg, cyl, data = df[13:20,])
pl3 <- qplot(mpg, cyl, data = df[21:30,])
pl4 <- qplot(mpg, cyl, data = df[1:12,])
pl_list[[1]] <- list(pl1, pl3, "one")
pl_list[[2]] <- list(pl2, pl4, "two")
```
## header {.tabset}
```{r, results = "asis", echo = FALSE}
for(i in seq_along(pl_list)){
tmp <- pl_list[[i]]
# As you want to use tabset level here has to be lower than
# parent level (ie, parent is 2, so here you have to use 3)
catHeader(tmp[[3]], 3)
lapply(tmp[1:2], print)
}
```
What I would like to be able to do is to update the plot based on the output from the (DT-)table after filtering in the html.
For example - here is a screenshot of the table filtered for maz in the html:
I would like the scatter plot to update to only show the values shown in the filtered table.
Is this possible? I know I could achieve something like this using a shiny web app, but is it possible to embed some shiny code into the html to achieve this? (I have very limited experience using shiny/html so would be grateful for any pointers/ideas).
I am using R-markdown (and here is a link to the html produced):
---
title: "Filter interative plots from table results"
date: "`r format(Sys.time(), '%B %e, %Y')`"
output:
html_notebook:
theme: flatly
toc: yes
toc_float: yes
number_sections: true
df_print: paged
html_document:
theme: flatly
toc: yes
toc_float: yes
number_sections: true
df_print: paged
---
```{r setup, include=FALSE, cache=TRUE}
library(DT)
library(plotly)
library(stringr)
data(mtcars)
```
# Clean data
## Car names and models are now a string: "brand_model" in column 'car'
```{r include=FALSE}
mtcars$car <- rownames(mtcars)
mtcars$car <- stringr::str_replace(mtcars$car, ' ', '_')
rownames(mtcars) <- NULL
```
# Interactive table using DT
```{r rows.print=10}
DT::datatable(mtcars,
filter = list(position = "top"),
selection="none", #turn off row selection
options = list(columnDefs = list(list(visible=FALSE, targets=2)),
searchHighlight=TRUE,
pagingType= "simple",
pageLength = 10, #default length of the above options
server = TRUE, #enable server side processing for better performance
processing = FALSE)) %>%
formatStyle(columns = 'qsec',
background = styleColorBar(range(mtcars$qsec), 'lightblue'),
backgroundSize = '98% 88%',
backgroundRepeat = 'no-repeat',
backgroundPosition = 'center')
```
# Plot disp against mpg using plotly
```{r fig.width=8, fig.height=8}
p <- plot_ly(data = mtcars,
x = ~disp,
y = ~mpg,
type = 'scatter',
mode = 'markers',
text = ~paste("Car: ", car, "\n",
"Mpg: ", mpg, "\n"),
color = ~mpg,
colors = "Spectral",
size = ~-disp
)
p
```
Contrary to my first assessment, it is actually possible. There are multiple additions to your code. I will go through them chronologically:
You need to add runtime: shiny in the yaml-header to start shiny in any R-markdown file
Optional: I added some css style in case you need to adjust your shiny application to fit into certain screen sizes
Shiny-documents contain an UI-part, where you configure the user interface. Usually you just use a fluidPage function for that
The next part is the server.r-part where the interesting stuff happens:
We assign, i.e., your DT::datatable to an output-object (usually a list)
For each assignment we need to set a shinyID which we configure in ui.r and then add, i.e, output$mytable
I added an element which shows which rows are selected for debugging
The heart of all the changes is input$mytable_rows_all. All the controls we set up in the ui.r can be called inside the render-functions. In this particular case mytable refers to the shinyID I set for the DT::datatable in the UI-part and rows_all tells shiny to take all the rownumbers inside the shown table.
That way we just subset the data using mtcars[input$mytable_rows_all,]
To learn shiny I recommend Rstudio's tutorial. After learning and forgetting everything again I advise you to use the wonderful cheatsheet provided by Rstudio
The whole modified code looks like this:
---
title: "Filter interative plots from table results"
date: "`r format(Sys.time(), '%B %e, %Y')`"
runtime: shiny
output:
html_document:
theme: flatly
toc: yes
toc_float: yes
number_sections: true
df_print: paged
html_notebook:
theme: flatly
toc: yes
toc_float: yes
number_sections: true
df_print: paged
---
<style>
body .main-container {
max-width: 1600px !important;
margin-left: auto;
margin-right: auto;
}
</style>
```{r setup, include=FALSE, cache=TRUE}
library(stringr)
data(mtcars)
```
# Clean data
## Car names and models are now a string: "brand_model" in column 'car'
```{r include=FALSE}
mtcars$car <- rownames(mtcars)
mtcars$car <- stringr::str_replace(mtcars$car, ' ', '_')
rownames(mtcars) <- NULL
```
# Plot disp against mpg using plotly
```{r}
library(plotly)
library(DT)
## ui.r
motor_attributes=c('Cylinder( shape): V4','Cylinder( shape): V6','Cylinder( shape): V8','Cylinder( shape): 4,Straight Line','Cylinder( shape): 6,Straight Line','Cylinder( shape): 8,Straight Line','Transmission: manual','Transmission: automatic')
fluidPage(# selectizeInput('cyl','Motor characteristics:',motor_attributes,multiple=TRUE,width='600px'),
downloadLink('downloadData', 'Download'),
DT::dataTableOutput('mytable'),
plotlyOutput("myscatter"),
htmlOutput('Selected_ids'))
### server.r
output$mytable<-DT::renderDataTable({
DT::datatable(mtcars,
filter = list(position = "top"),
selection='none', #list(target='row',selected=1:nrow(mtcars)), #turn off row selection
options = list(columnDefs = list(list(visible=FALSE, targets=2)),
searchHighlight=TRUE,
pagingType= "simple",
pageLength = 10, #default length of the above options
server = TRUE, #enable server side processing for better performance
processing = FALSE)) %>%
formatStyle(columns = 'qsec',
background = styleColorBar(range(mtcars$qsec), 'lightblue'),
backgroundSize = '98% 88%',
backgroundRepeat = 'no-repeat',
backgroundPosition = 'center')
})
output$Selected_ids<-renderText({
if(length(input$mytable_rows_all)<1){
return()
}
selected_rows<-as.numeric(input$mytable_rows_all)
paste('<b> #Cars Selected: </b>',length(selected_rows),'</br> <b> Cars Selected: </b>',
paste(paste('<li>',rownames(mtcars)[selected_rows],'</li>'),collapse = ' '))
})
output$myscatter<-renderPlotly({
selected_rows<-as.numeric(input$mytable_rows_all)
subdata<-mtcars[selected_rows,]
p <- plot_ly(data = subdata,
x = ~disp,
y = ~mpg,
type = 'scatter',
mode = 'markers',
text = ~paste("Car: ", car, "\n",
"Mpg: ", mpg, "\n"),
color = ~mpg,
colors = "Spectral",
size = ~-disp
)
p
})
```