Strange behavior in shiny RMarkdown - r

I'm having some very strange behavior when knitting a shiny RMarkdown HTML document. In the MWE below I simply (1) include a slider input, (2) include a column in the df as the value of the input and then (3) return the df. The MWE below returns the error Error in : Column "b" must be length 1 (the number of rows), not 0. If I take out the multiplication of the input value when I assign a value to b, the document knits with no problem. MWE:
---
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(dplyr)
```
Testing
```{r eruptions, echo=FALSE}
# Input slider
sliderInput("test", "Test", value = 0.5, min = 0, max = 1, width = "100%")
# Pre-define df
output <- reactiveValues(data = data.frame(a = 20))
observe({
output$data <- output$data %>%
# Manipulate df
mutate(b = input$test * 2)
})
# Return df
renderDataTable({output$data})
```
Does anyone know what could be causing this strange behavior?

Here's what I suspect is going on ---
I think that the first time your observe is run through, that input$test is NULL.
If your mutate sets a column to NULL, it would just remove that column.
If you use input$test * 2 that will return an empty vector numeric(0). Trying to add a column with an empty vector would give the error.
If you add req(input$test) at the beginning of your observe function, does it work?

Related

Creating a dynamic number of flexdashboard tabs using reactive()

I am wondering if it is possible to create a number of tabs in flexdashboard which change according to some user input. As a toy example, I am trying to plot histograms of every numeric numeric column of a user-selected dataset, with each numeric column getting its own tab for a histogram. If the user changes the dataset, the dashboard should update and increase/decrease the number of tabs in the tabset, if the number of numeric columns in the newly selected dataset changes. For simplicity, I only included two datasets.
---
title: "Reactive GGPlot Tabs"
output:
flexdashboard::flex_dashboard:
theme:
version: 4
bootswatch: cosmo
runtime: shiny
---
```{r}
pacman::p_load(tidyverse, shiny, flexdashboard)
```
Sidebar {.sidebar}
-------------------------------------
<h5>Required Input</h5>
```{r}
selectInput(inputId = "dataset", label = "Data:",
choices = c('mtcars', 'iris'), selected = 'iris')
dataset = reactive({
set = as.character(input$dataset)
if(set=='iris'){data = iris} else{data = mtcars}
return(data)
})
```
```{r setup}
chart = reactive({
data = dataset()
num_cols <- unlist(lapply(data, is.numeric))
data = data[,num_cols] # only keep numeric columns for histogram
labels <- data %>% names # these will serve as labels for each tab
chart <- purrr::map(.x = data, ~ggplot(data, aes(x=.x))+geom_histogram()+theme_bw()) %>% setNames(labels) # assign names to each element to use later as tab titles
return(chart)
})
```
## Column {.tabset .tabset-fade}
```{r}
out <- reactive({
lapply(seq_along(chart()), function(i) {
a1 <- knitr::knit_expand(text = sprintf("### %s\n", names(chart())[i])) # tab header, auto extracts names of `chart`, %s indicates string
a2 <- knitr::knit_expand(text = "\n```{r, message = F, warning = F}") # start r chunk
a3 <- knitr::knit_expand(text = sprintf("\nchart()[[%d]]", i)) # extract graphs by "writing" out `chart[[1]]`, `chart[[2]]` etc. to be rendered later, %d indicates integer variable
a4 <- knitr::knit_expand(text = "\n```\n") # end r chunk
paste(a1, a2, a3, a4, collapse = '\n') # collapse together all lines with newline separator
})
})
```
`r reactive({knitr::knit(text = paste(out(), collapse = '\n'))})`
When this code is run, the dashboard displays the markdown chunk code, but does not render plots or tabs.
I am able to sightly modify this code to work without reactive, but I need the number of tabs to increase/decrease according to the chosen dataset. Also, I am not allowed to use facet_wrap.
Is this possible?

Officedown - Can't print plots (validate_key error)

I'm testing the Officedown-package, but encounter some issues. From the two examples below, the first one produces a word-file, but with warning. The second one stops due to an error I don't understand.
Example 1
---
title: "Style text with officedown"
output:
officedown::rdocx_document: default
---
```{r}
library(officedown)
library(officer)
library(ggplot2)
library(flextable)
ft <- fp_text(color = 'red', bold = TRUE)
```
# Test
The **officedown** package is
`r ftext('awesome', ft)`!
```{r}
df <- data.frame(A = 1:3,
B = 1:3)
mytable <- flextable(df, theme_fun = theme_box)
mytable
```
Warning 1
Example 2
Just one chunk is added to the previous example. Here I make and print a ggplot, but the same error occurs if the chunk only contains df (print the dataframe)
```{r plot, fig.cap="a cap"}
p <- ggplot(df, aes(A,B))+geom_point()+theme_bw()
print(p)
```
Rather silly to find the solution minutes after you turn to stackoverflow because google doesn't provide the solution:
I installed the cachem-package. I assume it should have come with the officedown-installation, but apparently it didn't.

RMarkdown plot doesn't show in final knit output

I'm working with RMarkdown and created a report using the gt package. Before I knit the document, the graph output shows 2 parts: "R Console" and "shiny.tag.list". While the "shiny.tag.list" has the table, R Console is what appears after i knit the document, "no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf". How do I get it to show the table? Is there a way to address the R Console comments so it won't appear?
PS. Knit output is in Microsoft Word.
gt_tbl <-
df_tbl %>%
gt(rowname_col="date")
# removes date so you can add a line alignment for that column
gt_tbl <-
gt_tbl %>%
tab_stubhead(label="date")
# adds the line alignment + new label for column you just took out name for
gt_tbl <-
gt_tbl %>%
tab_header(
title=md("**Weekly Screener Report**"),
subtitle= {current_range}) %>%
tab_style(
style=list(
cell_fill(color="light blue"),
cell_text(weight="bold")
),
locations = cells_body(
columns=vars(week_total)
)
)
# md("**text**") bolds "text"
# tab_style() adds color where you want it, can be done to rows as well
# tab_header is for title and subtitle
# Create 2 row groups w/ 'tab_row_group()' function
# Group the rows in opposite order you want to see it in
gt_tbl <-
gt_tbl %>%
tab_row_group(
group="current",
rows= 5:8
) %>%
tab_row_group(
group="former",
rows= 1:4
)
# view table
gt_tbl
before_knit_rconsole
before_knit_plot
after_knit_output
I can reproduce the error.
But first, what you see are in your output are warnings. You can get rid of these by setting the header of the chunk as follows:
{r, warning=FALSE}
A reproducible example:
---
title: "test"
output: word_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(gt)
```
```{r, warning=FALSE}
gt_tbl <-
gt::gtcars
gt_tbl %>% gt(rowname_col = "mfr")
```
When I run this code, I don't get the table printed out in the .docx. If I change to pdf_document the table appears.
A quick search indicated that word outputs are not supported (https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html). But I might be wrong.

Filter multiple columns by value using Checkbox group

How can I use a checkbox group to filter several columns by value?
See example below: I want to return only the rows for which the columns indicated by the checkbox group have the value "yes".
---
title: "test"
runtime: shiny
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
```
```{r}
checkboxGroupInput("checkGroup", label = h3("Checkbox group"),
choices = list("col1", "col2"))
```
```{r}
df <- data.frame(c(1,2,3),c("no","yes","yes"),c("no","no","yes"))
colnames(df)<-c("id","col1","col2")
```
```{r}
renderDataTable({
df
})
```
ie when selecting "col1', the output should contain only row row#2 and 3,
when selecting "col2', the output should be: row#3,
when selecting both "col1" and "col2", the output should be: row#2 and 3.
I could write an if statement for each variable but I'd rather not (I have 10 or so). Surely there must be a better way?
This is where you'd need to develop a filter condition for your data based off of the input values. I'm going to use the dplyr any the filter_at function to show you how.
This also incorporates some reactive concepts used in shiny. If you're not too familiar with them, I would recommend doing some reading.
---
title: "test"
runtime: shiny
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
library(dplyr)
```
```{r}
checkboxGroupInput("checkGroup", label = h3("Checkbox group"),
choices = list("col1", "col2"))
```
```{r}
df <- data.frame("id" = c(1,2,3), "col1" = c("no","yes","yes"),"col2" = c("no","no","yes"))
```
```{r}
renderDataTable({
filter_at(df, input$checkGroup, any_vars(. == "yes"))
})
```
Within the last code chunk is where you'd use filter_at, and since you're doing this based off of inputs, it needs to be within the renderDataTable reactive function.
What this does is:
filter your df
at the variables in your selections from input$checkGroup (your checkbox input)
selecting any variables from the indicated selection that are equivalent to "yes". (e.g any_vars(. == "yes)). The dot is a placeholder for the variables indicated in the inputs you selected.

How to limit the number of rows printed in kable

When using rmarkdown to render pdf document, we can use three options for printing data.frame: default, kable and tibble (see here)
With the default option, it is possible to limit the number of rows printed, with the option: max.print
For tibble, we can use: dplyr.print_max
I can't find a way to limit the number of rows for kable. Is it possible?
kable renders the full data frame passed to it as a table in the output document. AFAIK there's no argument that will limit the number of rows. However, you can preselect the number of rows in the output table (for example, kable(head(dat)) or kable(dat[1:5, ])). If you want to avoid having to select the rows each time, you could write a helper function to limit the number of rows printed. For example:
---
output: pdf_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
library(knitr)
```
```{r}
my_kable = function(x, max.rows=6, ...) {
kable(x[1:max.rows, ], ...)
}
```
```{r}
my_kable(mtcars, caption="My first caption")
```
```{r}
iris$Sepal.Length = 1000 * iris$Sepal.Length
my_kable(iris, 3, caption="My second caption", format.args=list(big.mark=","))
```
A really simple solution is to wrap kable around head:
kable(head(mtcars, n = 5))
You can create a custom method for printing data frames like this.
Adapted from Yihui Xie's explanation here
---
title: "Untitled"
output: html_document
---
```{r include=FALSE}
knit_print.data.frame <- function(x, ...) {
head(x, 5) |>
knitr::kable() |>
paste(collapse = "\n") |>
knitr::asis_output()
}
registerS3method(
genname = "knit_print",
class = "data.frame",
method = knit_print.data.frame,
envir = asNamespace("knitr")
)
```
```{r}
iris
tibble::as_tibble(iris)
```

Resources