rstudio / flexdashboard

Easy interactive dashboards for R
https://pkgs.rstudio.com/flexdashboard/
Other
816 stars 301 forks source link

Build a page via loop #80

Open jrowen opened 8 years ago

jrowen commented 8 years ago

Is it possible to dynamically build a page using a loop? I've tried the approach below in an 'asis' block, but it doesn't seem to work.

cat(title, ' {data-icon="fa-line-chart"}\n')
cat("=====================================\n")
cat('\n')

for (i in seq_along(fields)) {
  if (i %% col_no != 0) {
    cat("Row\n")
    cat("-------------------------------------\n")
    cat('\n') 
  }

  cat("### ", fields[[i]], "\n")
  cat('\n')

  create_plot(fields[i])
  cat('\n')
}

There's a similar question on Stack Overflow.

jjallaire commented 8 years ago

No, that's not possible right now.

On Fri, Aug 5, 2016 at 10:25 AM, Jonathan Owen notifications@github.com wrote:

Is it possible to dynamically build a page using a loop? I've tried the approach below in an 'asis' block, but it doesn't seem to work.

cat(title, ' {data-icon="fa-line-chart"}\n') cat("=====================================\n") cat('\n')

for (i in seq_along(fields)) { if (i %% col_no != 0) { cat("Row\n") cat("-------------------------------------\n") cat('\n') }

cat("### ", fields[[i]], "\n") cat('\n')

create_plot(fields[i]) cat('\n') }

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rstudio/flexdashboard/issues/80, or mute the thread https://github.com/notifications/unsubscribe-auth/AAGXx17aOQKm-b462HvT1AZ4gxZ9BxaSks5qc0fogaJpZM4Jdu5d .

ThoDuyNguyen commented 8 years ago

I think you could take workaround by writing additional script which generates the markdown file dynamically and then render it.

dantonnoriega commented 8 years ago

My colleague @eamcvey pointed me here. I was able to autogenerate content by building r chunks explicitly then kniting them inline with r paste(knitr::knit(text = out)). This amazing line of code was found in an SO post.

In my case, I wanted to produce a series of graphs, each with a separate tab, with different content. Each graph was similar but there were numerous (about 15) and I didn't want to copy/paste all of the separate chunks.

Here is a gist you can download of a more simple example. (The code is also below but note that I add \ before each chunk so that it rendered as a single block of code.) I built a much more complicated function to build graphs, but the idea of the R chunks can be carried forward to any list object containing htmlwidgets as elements.

---
title: "Loop to Auto Build Tabs Containing htmlwidgets"
output: flexdashboard::flex_dashboard
---

\```{r setup, echo =FALSE, eval = TRUE}
library(tidyverse)
library(flexdashboard)
library(highcharter)

labels <- mtcars %>% names # these will serve as labels for each tab

# create a bunch of random, nonsensical line graphs
hcs <- purrr::map(.x = mtcars, ~highcharter::hchart(mtcars, y = .x, type = 'line')) %>%
    setNames(labels) # assign names to each element to use later as tab titles
\```

Page
====================

Column {.tabset .tabset-fade}
-----------------------------

<!-- loop to build each tabs (in flexdashboard syntax) -->
<!-- each element of the list object `out` is a single tab written in rmarkdown -->
<!-- you can see this running the next chunk and typing `cat(out[[1]])` -->

\```{r, echo = FALSE, eval = TRUE}

out <- lapply(seq_along(hcs), function(i) {

  a1 <- knitr::knit_expand(text = sprintf("### %s\n", names(hcs)[i])) # tab header, auto extracts names of `hcs`
  a2 <- knitr::knit_expand(text = "\n```{r}") # start r chunk
  a3 <- knitr::knit_expand(text = sprintf("\nhcs[[%d]]", i)) # extract graphs by "writing" out `hcs[[1]]`, `hcs[[2]]` etc. to be rendered later
  a4 <- knitr::knit_expand(text = "\n```\n") # end r chunk

  paste(a1, a2, a3, a4, collapse = '\n') # collapse together all lines with newline separator

})

\```

<!-- source: https://goo.gl/fnfXuO -->
<!-- As I mentioned in the SO post, I don't quite understand why it has to be -->
<!-- 'r paste(knitr::knit(...)' vs just 'r knitr::knit(...)' but hey, it works -->

`r paste(knitr::knit(text = paste(out, collapse = '\n')))`
jrowen commented 8 years ago

This was very helpful, thanks.

EMIjess commented 7 years ago

@jrowen If you want to have less ugly code (not all of your statements wrapped in knitr::knit_expand(), and you are willing to have multiple files, you can do this just as you would in a typical markdown document.

For complicated pages, I find a separate document easier to manage.

In your dashboard, you might have this:

```{r run-out, include=FALSE} out = NULL options(knitr.duplicate.label = 'allow') for (i in seq_along(fields)) { out = c(out, knit_expand('mychild.Rmd')) } ```

`r paste(knit_child(text = out), collapse = '')`

and then mychild.Rmd could be (no header information)

`` \### \r fields[[i]]` ```{r} create_plot(fields[i]) ```

Somtom commented 5 years ago

@EMIjess thx for your comment. I used parts of it for a project at work were we needed to dynamically create flexdashboard pages depending on the data for the report. I basically used a subpage.RMD template file similar to your mychild.RMD.

Although the issue was opened some time ago, I want to share a small example with you which might help others: https://somtom.github.io/post/using-dynamically-rendered-r-markdown-childs-for-reports/

GabeChurch commented 5 years ago

@dantonnoriega passed along some good advice. Here is some code that will allow you to produce an arbitrary number of charts side-by-side AKA multiple (2) charts per Row. There doesn't seem to be a lot of info on doing it so here is what I did. I use purrr map over lapply because it is more for-loopy. Note the flexdashboard scroll setting is important for this to work. I'm also converting the ggplots to plotly interactive graphs (that auto-size with flexdashboard).

I'm using this method with my spark_hist function to build a dashboard of histograms for hive tables with sparklyR. Here is my R package if you are interested, hope to have it on CRAN soon. https://github.com/GabeChurch/sparkedatools

---
title: "CC Fraud Distributions"
output: 
  flexdashboard::flex_dashboard:
    orientation: rows
    vertical_layout: scroll
---

\```{r setup, echo =FALSE, eval = TRUE}
library(flexdashboard)
library(tidyverse)
library(sparklyr)
library(dplyr)
library(plotly)
library(sparkedatools)

Sys.setenv(SPARK_HOME="/usr/hdp/3.1.0.0-78/spark2")
conf = spark_config()
conf$'sparklyr.jars.default'= "/home/gchurch/R/sparkeda_2.11-1.0.0.jar"
conf$spark.default.parallelism=10 
sc = spark_connect(master = "yarn-client", config = conf, version = '2.3.2')
\```

\```{r, echo = FALSE, eval = TRUE}

tbl_cache(sc, "sample_data.cc_fraud")
cc_fraud = tbl(sc, sql("select * from sample_data.cc_fraud"))
returned = spark_hist(cc_fraud, 10L, FALSE) %>% map(function(x){
  ggplotly(x) 
})

out = (1:length(returned)) %>% map(function(i){
  a1 = knitr::knit_expand(text = "\nRow \n--------------------------------------------")
  a2 <- knitr::knit_expand(text = sprintf("\n### Chart %s\n```{r Chart%s}", i, i)) # start r chunk
  a3 <- knitr::knit_expand(text = sprintf("\n returned[[%d]]", i)) 
  a4 <- knitr::knit_expand(text = "\n```\n") # end r chunk
  #If i is not divisible by two we will create a new flexdashboard "row"
  if (i %% 2) { #odds
    paste(a1, a2, a3, a4, collapse = '\n')
  }else{ #evens
    paste(a2, a3, a4, collapse = '\n')
  }
})
\```

`r paste(knitr::knit(text = paste(out, collapse = '\n')))`
CoertSchrijver commented 5 years ago

Great suggestions and thank you for the answers so far. However, I am currently trying to do the exact same thing in a reactive environment. The plots I am trying to display in each tab depend on user input. The plots are 18 ggplotly-histograms stored in one reactiveValues()-variable named 'plots'. I am able to access each histogram using plots$histogram_10 for example. However, when I try to make these tabs they should be run in a reactive environment. For simplicity I am not trying to run the plots yet but print 'hi' in each tab. I am afraid that knitting the results from the out-variable to Rmd is blocked due to the reactive environment. This is the code I am (partly) using:

```{r, echo = FALSE, eval = TRUE}
out <- reactiveValues()
reactive(out$tab <- lapply(seq_along(plots), function(i) {
  a1 <- knitr::knit_expand(text = sprintf("### %s\n", names(plots)[i])) # tab header, auto extracts names of `hcs`
  a2 <- knitr::knit_expand(text = "\n```{r}") # start r chunk
  a3 <- knitr::knit_expand(text = print('hi')) # extract graphs by "writing" out `hcs[[1]]`, `hcs[[2]]` etc. to be rendered later
  a4 <- knitr::knit_expand(text = "\n```\n") # end r chunk
  paste(a1, a2, a3, a4, collapse = '\n') # collapse together all lines with newline separator
})
)

```## Aantal patienten per specialisme per jaar {.tabset}
}
<!-- source: https://goo.gl/fnfXuO -->
<!-- As I mentioned in the SO post, I don't quite understand why it has to be -->
<!-- 'r paste(knitr::knit(...)' sometimes vs 'r knitr::knit(...)' but oh well -->
`r reactive({knitr::knit(text = paste(out$tab, collapse = '\n'))})` 

I assume the last line is where it goes wrong: r reactive({knitr::knit(text = paste(out$tab, collapse = '\n'))})

The output I get is, but no new tabs ### Histogram_10 ```{r} hi ``` ### Histogram_11 ```{r} hi ``` ### Histogram_12 ```{r} hi ```

The dashboard also prints 'hi' three times, but not in seperate tabs.

Output

Help would be much appreciated!!

austin-hz commented 4 years ago

Great suggestions and thank you for the answers so far. However, I am currently trying to do the exact same thing in a reactive environment. The plots I am trying to display in each tab depend on user input. The plots are 18 ggplotly-histograms stored in one reactiveValues()-variable named 'plots'. I am able to access each histogram using plots$histogram_10 for example. However, when I try to make these tabs they should be run in a reactive environment. For simplicity I am not trying to run the plots yet but print 'hi' in each tab. I am afraid that knitting the results from the out-variable to Rmd is blocked due to the reactive environment. This is the code I am (partly) using:

```{r, echo = FALSE, eval = TRUE}
out <- reactiveValues()
reactive(out$tab <- lapply(seq_along(plots), function(i) {
  a1 <- knitr::knit_expand(text = sprintf("### %s\n", names(plots)[i])) # tab header, auto extracts names of `hcs`
  a2 <- knitr::knit_expand(text = "\n```{r}") # start r chunk
  a3 <- knitr::knit_expand(text = print('hi')) # extract graphs by "writing" out `hcs[[1]]`, `hcs[[2]]` etc. to be rendered later
  a4 <- knitr::knit_expand(text = "\n```\n") # end r chunk
  paste(a1, a2, a3, a4, collapse = '\n') # collapse together all lines with newline separator
})
)

```## Aantal patienten per specialisme per jaar {.tabset}
}
<!-- source: https://goo.gl/fnfXuO -->
<!-- As I mentioned in the SO post, I don't quite understand why it has to be -->
<!-- 'r paste(knitr::knit(...)' sometimes vs 'r knitr::knit(...)' but oh well -->
`r reactive({knitr::knit(text = paste(out$tab, collapse = '\n'))})` 

I assume the last line is where it goes wrong: r reactive({knitr::knit(text = paste(out$tab, collapse = '\n'))})

The output I get is, but no new tabs ### Histogram_10 ```{r} hi ``` ### Histogram_11 ```{r} hi ``` ### Histogram_12 ```{r} hi ```

The dashboard also prints 'hi' three times, but not in seperate tabs.

Output

Help would be much appreciated!!

I am trying to do the same thing. I can't get it to work. It seems the last line "r reactive({knitr::knit(text = paste(out$tab, collapse = '\n'))}) " would not take renderPlotly. Any help would be much appreciated!

shirdekel commented 3 years ago

Has anyone subsequently worked out how to knit as above within a reactive environment?