How to copy tableOutput to clipboard? - r

I'm trying to copy the table output to the clipboard on a click of a button. I tried looking into the rclipboard package, but it doesn't appear to be able to copy output, in my limited understanding.
I added an actionButton with an icon to the screenshot to show what I'm trying to achieve. Right now the button doesn't do anything.
Code:
library(shiny)
library(dplyr)
df <- mtcars
one <- function(.data, var, na = TRUE) {
return({
.data %>%
group_by(.data[[var]]) %>%
filter(!is.na(.data[[var]])) %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
}
ui <- fluidPage(
selectInput("var", label = "Select Variable", choices = c("", names(df))),
tableOutput("value")
)
server <- function(input, output) {
output$value <- renderTable({
if(input$var != '') {
data <- df %>% one(input$var, na = input$check)
return(data)
}
}, spacing = "xs", bordered = TRUE)
}
shinyApp(ui, server)

Perhaps you can use copy button from DT to copy the whole table. You can also copy only selected rows. Try this
library(shiny)
library(dplyr)
library(DT)
df <- mtcars
one <- function(.data, var, na = TRUE) {
return({
.data %>%
group_by(.data[[var]]) %>%
filter(!is.na(.data[[var]])) %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
}
ui <- fluidPage(
selectInput("var", label = "Select Variable", choices = c("", names(df))),
DTOutput("valu", width = "15%")
#tableOutput("value")
)
server <- function(input, output) {
output$valu <- renderDT({
if(input$var != '') {
data <- df %>% one(input$var, na = input$check)
DT::datatable(data,
class = 'cell-border stripe',
rownames = FALSE,
extensions = c("Buttons", "Select"),
selection = 'none',
options =
list(
select = TRUE,
dom = "Bt", ## remove f to remove search ## Brftip
buttons = list(
list(
extend = "copy",
text = 'Copy'#,
#exportOptions = list(modifier = list(selected = TRUE))
)
)
)) %>% formatStyle(
0,
target = "row",
fontWeight = styleEqual(1, "bold")
)
}
}, server = FALSE)
output$value <- renderTable({
if(input$var != '') {
data <- df %>% one(input$var, na = input$check)
return(data)
}
}, spacing = "xs", bordered = TRUE)
}
shinyApp(ui, server)

Related

rhandsontable in shiny: change separator of thousands and decimals

I would like to know how to change the thousand separator to a point and the decimal separator to a comma when using the rhandsontable function.
Here's a small, reproducible example.
library(tidyverse)
library(shiny)
library(shinydashboard)
library(rhandsontable)
data_test <- iris
ui <- dashboardPage(
skin = "black",
dashboardHeader(
title = "Test",
titleWidth = 100
),
dashboardSidebar(collapsed = TRUE),
dashboardBody(
fluidRow(column(
width = 3,
selectInput(
inputId = "specie",
label = "Specie",
choices = unique(data_test$Species),
multiple = FALSE
)),
rHandsontableOutput("rTable")))
)
server <- function(input, output, session) {
rv <- reactiveValues(table1 = NULL)
observeEvent(input$specie,{
data <- data_test %>%
filter(Species == input$specie) %>%
mutate(Sepal.Length2 = 0,
Sepal.Width2 = 0,
Petal.Length2 = 0,
Petal.Width2 = 0,
amount = 50,
percentage = 100) %>%
select(1:4,6:11)
rv$table1<- data})
observe({
if (!is.null(input$rTable)){
mytable <- as.data.frame(hot_to_r(input$rTable))
mytable[,7] <- mytable[,5]*100
mytable[,8] <- mytable[,6]*100
numberofrows <- nrow(mytable)
rv$table1 <- mytable}
})
output$rTable <- renderRHandsontable({
rhandsontable(rv$table1) %>%
hot_col("Sepal.Length", format = "0.0,0") %>%
hot_col("Sepal.Width", format = "0.0,0") %>%
hot_col("Petal.Length", format = "0.0,0") %>%
hot_col("Petal.Width", format = "0.0,0") %>%
hot_col("Sepal.Length2", format = "0.0,0") %>%
hot_col("Sepal.Width2", format = "0.0,0") %>%
hot_col("Petal.Length2", format = "0.0,0") %>%
hot_col("Petal.Width2", format = "0.0,0") %>%
hot_col("amount", format = "$0.0,0") %>%
hot_col("percentage", format = "0,0") %>%
hot_cols(colWidths = 130)
})
}
shinyApp(ui = ui, server=server)
I tried the hot_col functions as seen in the example but this doesn't solve my problem, I also tried other functions like prettyNum, format, formatC, etc but I couldn't solve it either.
Any help is much appreciated.
I would use the d3.format library, as follows:
library(rhandsontable)
library(shiny)
mydata <- as.data.frame(
matrix(runif(40, 0, 100000), nrow = 10, ncol = 4)
)
ui <- fluidPage(
tags$head(
tags$script(src = "https://cdn.jsdelivr.net/npm/d3-format#3")
),
rHandsontableOutput("mytable")
)
server <- function(input, output) {
output$mytable <- renderRHandsontable({
rhandsontable(mydata,rowHeaderWidth = 100)%>%
hot_cols(
renderer = 'function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.NumericRenderer.apply(this, arguments);
var locale = d3.formatLocale({
decimal: ",",
thousands: ".",
grouping: [3]
});
var fformat = locale.format(",");
td.innerHTML = fformat(value);
}')
})
}
shinyApp(ui,server)

Filter a reactive object, based on other reactive object, while both depend on a third reactive object

I´m trying to filter a reactive object "#4" using other reactive object "#3" with no success, and I think the problem is that they both depend on another reactive "#2". This picture should help:
Here is the reprex:
library(shiny)
library(DT)
library(dplyr)
dat <- as.data.frame( list(
X = c("A", "A", "B", "B", "C"),
Y = c(1,2,3,4,5)
))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("myinput", "Input:",min = 1, max = 5,value = 3)
),
mainPanel(
br(),
fluidRow(column(2, DTOutput('table_data'))),
br(),
fluidRow(column(2, DTOutput('table_filtered'))),
br(),
fluidRow(column(2, DTOutput('table_filtered_not_A'))),
br(),br(),
fluidRow(column(2, DTOutput('table_grouped')))
)
)
)
server <- function(input, output) {
dat_rv <- reactiveValues(df = dat)
dat_filtered <- reactive({
dat_rv$df %>%
filter(
!isTruthy( input$myinput ) | Y <= input$myinput
)
})
dat_not_A <- reactive({
dat_not_A <- dat_filtered() %>%
filter(X != "A") %>%
select(X)
})
dat_grouped <- reactive({
dat_grouped <- dat_filtered() %>%
filter(X %in% dat_not_A()) %>% # HERE IS THE PROBLEM?
group_by(X) %>%
summarise(Y = sum(Y))
return(dat_grouped)
})
output$table_data = renderDT(dat, options = list(dom = 't'), rownames = FALSE)
output$table_filtered = renderDT(dat_filtered(), options = list(dom = 't'), rownames = FALSE)
output$table_filtered_not_A = renderDT(dat_not_A(), options = list(dom = 't'), rownames = FALSE)
output$table_grouped = renderDT(dat_grouped(), options = list(dom = 't'), rownames = FALSE)
}
shinyApp(ui = ui, server = server)
I have also tried to use isolate but it has not work. Am i missing something?
Best regards.
You need to use:
dat_grouped <- reactive({
dat_grouped <- dat_filtered() %>%
filter(X %in% unique(dat_not_A()$X)) %>%
group_by(X) %>%
summarise(Y = sum(Y))
return(dat_grouped)
})
And not:
dat_grouped <- reactive({
dat_grouped <- dat_filtered() %>%
filter(X %in% dat_not_A()) %>%
group_by(X) %>%
summarise(Y = sum(Y))
return(dat_grouped)
})

Referencing a dynamic input ID in Shiny

In the example below, I am trying to produce a box and plot for each group within a dataset, using lapply within a renderUI function. However, some of these groups require an additional filter as they have sub-groupings.
This means creating a selectInput inside the box for those groups only and having the corresponding chart reference that selectInput only.
Here's the reproducible example... my problem is in the lapply loop creating a selectInput with the inputID of paste("selector_",i) and then immediately referencing this in the data to be output inside the corresponding box with input$(what goes here?)
library(shiny)
library(shinydashboard)
library(tidyverse)
library(nycflights13)
library(DT)
data <- planes %>%
select(manufacturer, type, model, year, seats) %>%
unique() %>%
filter(!is.na(year)) %>%
mutate(year = as.character(year))
ui <- dashboardPage(
dashboardHeader(title = "Testing"),
dashboardSidebar(),
dashboardBody(
fluidRow(
column(12, selectInput("type","Type", choices = unique(data$type)),
uiOutput("mytabs"))
)
)
)
server <- function(input, output) {
data_filtered <- reactive({
req(input$type)
data %>%
filter(type == input$type)
})
mfrs <- reactive({
data_filtered() %>%
select(manufacturer) %>%
unique() %>%
pull()
})
output$mytabs = renderUI({
fluidRow(
lapply(mfrs(), function(i) {
dt <- data_filtered() %>%
filter(manufacturer == i) %>%
arrange(year) %>%
select(model, year, seats)
models <- dt %>%
filter(!is.na(model)) %>%
select(model) %>%
unique() %>%
pull()
if(length(models) > 1) {
box(id = paste0('card', i), title = paste(i),
selectInput(inputId = paste0("selector_",i), "Question",
choices = models, selected = models[1]),
DT::datatable(dt[dt$qntext == input$the_one_above],
width = "100%", rownames = F,
options = list(
columnDefs = list(list(width = '40px', targets = "_all"))
))
)
} else {
box(id = paste0('card', i), title = paste(i),
DT::datatable(dt,
width = "100%", rownames = F,
options = list(
columnDefs = list(list(width = '40px', targets = "_all"))
))
)
}
})
)
})
}
shinyApp(ui, server)
As I am not sure what qns means, I have assigned qns to be models. Try this code:
data <- planes %>%
select(manufacturer, type, model, year, seats) %>%
unique() %>%
filter(!is.na(year)) %>%
mutate(year = as.character(year))
ui <- dashboardPage(
dashboardHeader(title = "Testing"),
dashboardSidebar(uiOutput("myqns")),
dashboardBody(
fluidRow(
column(12, selectInput("type","Type", choices = unique(data$type)), uiOutput("mytabs"))
)
)
)
server <- function(input, output) {
data_filtered <- reactive({
req(input$type)
data %>%
filter(type == input$type)
})
mfrs <- reactive({
req(data_filtered())
data_filtered() %>%
select(manufacturer) %>%
unique() %>%
pull()
})
output$myqns <- renderUI({
req(mfrs())
lapply(1:length(mfrs()), function(i) {
dt <- data_filtered() %>%
filter(manufacturer == mfrs()[i]) %>%
arrange(year) %>%
select(model, year, seats)
models <- dt %>%
filter(!is.na(model)) %>%
select(model) %>%
unique() %>%
pull()
qns <- models
selectInput(inputId = paste0("selector_",i), paste("Question",i), choices = as.list(qns), selected = 1)
})
})
output$mytabs = renderUI({
req(mfrs())
fluidRow(
lapply(1:length(mfrs()), function(i) {
req(input[[paste0("selector_",i)]])
dt <- data_filtered() %>%
filter(manufacturer == mfrs()[i]) %>%
arrange(year) %>%
select(model, year, seats)
models <- dt %>%
filter(!is.na(model)) %>%
select(model) %>%
unique() %>%
pull()
qns <- models
if(length(models) > 1) {
box(id = paste0('card', i), title = paste(mfrs()[i]),
# selectInput(inputId = paste0("selector_",i), "Question",
# choices = qns, selected = qns[1]),
DT::datatable(dt[dt$model == input[[paste0("selector_",i)]], ],
width = "100%", rownames = F,
options = list(
columnDefs = list(list(width = '40px', targets = "_all"))
))
)
} else {
box(id = paste0('card', i), title = paste(mfrs()[i]),
DT::datatable(dt,
width = "100%", rownames = F,
options = list(
columnDefs = list(list(width = '40px', targets = "_all"))
))
)
}
})
)
})
}
shinyApp(ui, server)
Answered by the awesome Paul Campbell... using modules.
library(shinydashboard)
library(tidyverse)
library(highcharter)
library(nycflights13)
# Modules ===============================================
# UI and server module for box with chart
box_chart_UI <- function(id, title) {
ns <- NS(id)
box(
title = title, height = 550,
highcharter::highchartOutput(ns("chart"))
)
}
box_chart <- function(input, output, session, df) {
output$chart <- renderHighchart({
validate(need(nrow(df) > 0, "No data"))
hchart(df, "column", hcaes(year, seats))
})
}
# UI and server module for box with chart and filter
box_chart_filter_UI <- function(id, title, filters, filter_lab = "Model") {
ns <- NS(id)
box(
title = title, height = 550,
selectInput(inputId = ns("selector"), label = filter_lab, choices = filters),
highchartOutput(ns("chart"))
)
}
box_chart_filter <- function(input, output, session, df) {
output$chart <- renderHighchart({
req(input$selector)
df_chart <- df %>% filter(model == input$selector)
validate(need(nrow(df_chart) > 0, "No data"))
hchart(df_chart, "column", hcaes(year, seats))
})
}
# Main App ===============================================
# load app data
data <- planes %>%
select(manufacturer, type, model, year, seats) %>%
unique() %>%
filter(!is.na(year)) %>%
mutate(year = as.character(year))
ui <- dashboardPage(
dashboardHeader(title = "Testing"),
dashboardSidebar(),
dashboardBody(
fluidRow(
column(
width = 12,
selectInput("type", "Type", choices = unique(data$type))
)
),
uiOutput("mytabs")
)
)
server <- function(input, output, session) {
data_filtered <- reactive({
req(input$type)
data %>% filter(type == input$type)
})
mfrs <- reactive({
data_filtered() %>%
distinct(manufacturer) %>%
pull()
})
# first load all the UI module functions
output$mytabs <- renderUI({
fluidRow(
lapply(1:length(mfrs()), function(i) {
models <- data_filtered() %>%
filter(manufacturer == mfrs()[i], !is.na(model)) %>%
distinct(model) %>%
pull() %>%
sort()
# depending on how many models, load the correct UI module
if (length(models) > 1) {
box_chart_filter_UI(id = i, title = mfrs()[i], filters = models)
} else {
box_chart_UI(id = i, title = mfrs()[i])
}
})
)
})
# now separately load the module server functions
# need to do this inside an observe due to reactive objects
observe({
lapply(1:length(mfrs()), function(i) {
dt <- data_filtered() %>%
filter(manufacturer == mfrs()[i]) %>%
arrange(year) %>%
select(model, year, seats)
models <- dt %>%
filter(!is.na(model)) %>%
distinct(model) %>%
pull() %>%
sort()
# depending on how many models, load the correct server module
if (length(models) > 1) {
callModule(box_chart_filter, id = i, df = dt)
} else {
callModule(box_chart, id = i, df = dt)
}
})
})
}
shinyApp(ui, server)

How to use a user-written function within a shiny app?

I'm trying to get a function work within a shiny app, but it doesn't work as expected.
Outside of the app it works fine,
But within the app, it doesn't work:
Is it because the input$var isn't working as expected? (The checkbox also doesn't work and I'm still trying to figure that out.) My main question is about the function.
Code:
library(shiny)
if (interactive()) {
one <- function(.data, var, na = TRUE) {
if (na == FALSE)
return({
.data %>%
group_by({{var}}) %>%
drop_na() %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
if (na == TRUE)
return({
.data %>%
group_by({{var}}) %>%
# drop_na() %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
}
ui <- fluidPage(
selectInput("var", label = "Select Variable", choices = c(" ", names(mtcars))),
checkboxInput("check", "Display missing", FALSE),
tableOutput("value")
)
server <- function(input, output) {
output$value <- renderTable({
req(input$var)
if (input$check) ({
mtcars %>%
one(input$var, na = TRUE)
})
if(!input$check) ({
mtcars %>%
one(input$var, na = FALSE)
})
})
}
shinyApp(ui, server)
}
Dataset with missing values:
df <- data.frame(col1 = c(1:3, NA),
col2 = c("this", NA,"is", "text"),
col3 = c(TRUE, FALSE, TRUE, TRUE),
col4 = c(2.5, 4.2, 3.2, NA),
stringsAsFactors = FALSE)
input$var is a character value whereas one function is written for unquoted variables. You can change your function to work for character values.
Other changes that I did in the code are -
Replace na == FALSE and na == TRUE to !na and na respectively.
Since you want to keep the first value in selectInput as blank, used if(input$var != '') instead of req(input$var) because input$var would always have a value.
library(shiny)
library(dplyr)
if (interactive()) {
one <- function(.data, var, na = TRUE) {
if (!na)
return({
.data %>%
group_by(.data[[var]]) %>%
filter(!is.na(.data[[var]])) %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
if (na)
return({
.data %>%
group_by(.data[[var]]) %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
}
ui <- fluidPage(
selectInput("var", label = "Select Variable", choices = c("", names(df))),
checkboxInput("check", "Display missing", FALSE),
tableOutput("value")
)
server <- function(input, output) {
output$value <- renderTable({
if(input$var != '') {
data <- df %>% one(input$var, na = input$check)
return(data)
}
})
}
shinyApp(ui, server)
}
Use get() to accomplish your needs. Also, you can use .data[[!!input$var]] to get the appropriate name in the header of the displayed table.
one <- function(.data, var, na = TRUE) {
if (na == FALSE)
return({
.data %>%
group_by({{var}}) %>%
filter(!is.na({{var}})) %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
if (na == TRUE)
return({
.data %>%
group_by({{var}}) %>%
# drop_na() %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
}
ui <- fluidPage(
selectInput("var", label = "Select Variable", choices = c(" ",names(mtcars))),
checkboxInput("check", "Display missing", FALSE),
tableOutput("value")
)
server <- function(input, output) {
output$value <- renderTable({
if (!is.null(input$var)) {
if (input$var == " " | is.na(input$var)) {
df <- mtcars ## choose what you want to display when input$var is missing; NULL if you want to show nothing
}else {
df <- mtcars %>% one(.data[[!!input$var]], na = req(input$check))
}
}else df <- NULL
df
})
}
shinyApp(ui, server)

How to put 2 possibles eventReactive in only one variable

I am building a Shiny app which generate a dataframe from a database through the specific function my_function.
I want to use an eventReactive() to attribute the result of my_function depending on different inputs. My problem is that there are 2 ways to select these inputs which are structured in 2 different panels (I need this structure), so I have 2 actionButton that allow me to run my_function, and 1 variable for each eventReactive. Is there a way to put them in only 1 variable ?
df_all is a dataframe with several columns like "VAR1", "YEAR", "TYPE", "AGE" ... I need to filter depending on the inputs.
For the moment I have tried :
library(shiny)
library(shinydashboard)
library(DT)
library(dplyr)
df_all <- data.frame(
VAR1 = c(rep("A", 2), "B", "C")
YEAR = (rep(2001, 3), 2002)
TYPE = c("t1", "t2", "t2", "t1")
)
my_function <- function(arg1, arg2, arg3)
{
df = data.frame(
v1 = paste(arg1, arg2)
v2 = arg3
)
return(df)
}
shinyUI(dashboardPage(
dashboardHeader("title"),
dashboardSidebar(
sidebarMenu(id = "menu",
menuItem("Item1", tabName = "item1")
)),
dashboardBody(
tabItems(
tabItem(tabName = "item1",
selectInput(inputId = "var1", label = NULL, choices = c("A", "B", "C")),
tabsetPanel(
tabPanel("Item1-Panel1",
uiOutput("ui_year1"),
uiOutput("ui_type1"),
div(actionButton(inputId = "extra1", label = "Run", icon = icon("play")))),
tabPanel("Item1-Panel2",
uiOutput("ui_year2"),
uiOutput("ui_type2"),
div(actionButton(inputId = "extra2", label = "Run", icon = icon("play")))),
tabPanel("Item1-Panel3",
DT::dataTableOutput("tableau_ext1"),
DT::dataTableOutput("tableau_ext2"),
downloadButton("downloadCSV", "Save (CSV)"))
))))))
shinyServer(function(input, output) {
output$ui_year1 <- renderUI({
checkboxGroupInput(inputId = "year1", label = NULL, choices = df_all %>% filter(CULTURE == input$var1) %>% select(YEAR) %>% distinct() %>% pull()
})
output$ui_type1 <- renderUI({
checkboxGroupInput(inputId = "type1", label = NULL, choices = sort(df_all %>% filter(VAR1 == input$cult, YEAR %in% input$year1) %>% select(TYPE) %>% distinct() %>% pull())
})
output$ui_year2 <- renderUI({
checkboxGroupInput(inputId = "year2", label = NULL, choices = df_all %>% filter(VAR1 == input$var1) %>% select(YEAR) %>% distinct() %>% pull()
})
output$ui_type2 <- renderUI({
checkboxGroupInput(inputId = "type2", label = NULL, choices = sort(df_all %>% filter(VAR1 == input$cult, YEAR %in% input$year2) %>% select(TYPE) %>% distinct() %>% pull())
})
df1 <- eventReactive(input$extra1, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year1),
arg3 = as.character(input$type1))
})
df2 <- eventReactive(input$extra2, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year2),
arg3 = as.character(input$type2))
})
})
I tried to attribute the 2 eventReactive in 1 variable df, because I want to see and save the dataframe generated by my_function with :
shinyServer([...]
df <- eventReactive(input$extra1, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year1),
arg3 = as.character(input$type1))
})
df <- eventReactive(input$extra2, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year2),
arg3 = as.character(input$type2))
})
output$tableau_ext1 <- DT::renderDataTable({
df()
})
output$downloadCSV <- downloadHandler(
filename = function() {
paste0(input$year1, "_", input$type1, ".csv")
},
content = function(file) {
write.csv2(df(), file, row.names = FALSE)
}
)
)
But it didn't worked... If someone knows how to solve my problem, I will be grateful for his help :)
Building off of this thread the following seems to achieve the desired behavior (if I understand everything correctly):
library(shiny)
my_fun <- function() {
x <- sample(x=nrow(iris), size = 6)
x
}
ui <- fluidPage(
tabsetPanel(
tabPanel(title = "panel1",
actionButton("go1", "go 1")),
tabPanel(title = "panel2",
actionButton("go2", "go 2"))
),
mainPanel(dataTableOutput("tab"))
)
server <- function(input, output) {
df <- eventReactive(c(input$go1, input$go2), {
iris[my_fun(),]
}, ignoreNULL = FALSE, ignoreInit = TRUE)
output$tab <- renderDataTable({
df()
})
}
shinyApp(ui, server)
See also ?eventReactive for the ignoreNULL and ignoreInit options.
Edit: Two functions, one eventReactive, and keep track of tabs to know what to render.
library(shiny)
library(dplyr)
go1_fun <- function() {
x <- filter(iris, Species == "setosa") %>% head
x
}
go2_fun <- function() {
x <- filter(iris, Species == "virginica") %>% head
x
}
ui <- fluidPage(
tabsetPanel(id = "tabs",
tabPanel(title = "panel1",
actionButton("go1", "go 1")),
tabPanel(title = "panel2",
actionButton("go2", "go 2"))
),
mainPanel(dataTableOutput("tab"))
)
server <- function(input, output, session) {
df1 <- reactive({
if (req(input$go1)) {
x <- go1_fun()
}
return(x)
})
df2 <- reactive({
if (req(input$go2)) {
x <- go2_fun()
}
return(x)
})
tab_to_render <- eventReactive(c(input$go1, input$go2), {
if (input$tabs == "panel1") x <- df1()
if (input$tabs == "panel2") x <- df2()
return(x)
}, ignoreNULL = FALSE, ignoreInit = TRUE)
output$tab <- renderDataTable({
tab_to_render()
})
}
shinyApp(ui, server)

Resources