I am dynamically generating fluidrows for a uiOutput because the user selection will determine how many rows there are. For each row, I have 3 columns - two are text and the third is a plot.
I've got the text working, but I"m struggling to figure out how to get the plot in there.
In the reprex below it's the same plot, but in my actual example I will need to use a table other than the one passed into map(), but filter it based on one of the .x values.
library(tidyverse)
ui <- fluidPage(
uiOutput("row_mt")
)
server <- function(input, output) {
output$row_mt <- renderUI({
mt_list <- mtcars %>%
rownames_to_column(var = "model") %>%
rowwise() %>%
group_split() %>%
map(~{
tagList(fluidRow(
column(4,
.x$model),
column(4,
.x$mpg),
column(4,
mtcars %>%
filter(cyl == .x$cyl) %>%
ggplot(aes(x = mpg, y = cyl)) + geom_point())
),
br()
)
})
tagList(mt_list)
})
}
shinyApp(ui, server)
You should try to create the plot with renderPlot, and then display it in the renderUI with a plotOutput.
Try this
server <- function(input, output) {
output$myplot <- renderPlot({
mtcars %>%
rownames_to_column(var = "model") %>%
rowwise() %>%
group_split() %>%
map(~{
mtcars %>%
filter(cyl == .x$cyl) %>%
ggplot(aes(x = mpg, y = cyl)) + geom_point()
})
})
output$row_mt <- renderUI({
mt_list <- mtcars %>%
rownames_to_column(var = "model") %>%
rowwise() %>%
group_split() %>%
map(~{
tagList(fluidRow(
column(4,
.x$model),
column(4,
.x$mpg),
column(4,
plotOutput("myplot", height=100, width=100))
),
br()
)
})
tagList(mt_list)
})
}
Related
I am creating a shiny App where it will do two things on mtcars dataset
group data based on user selected values and calculate the mean mpg
and then filter based on selected values to display the output
library(shiny)
library(dplyr)
ui <- fluidPage(
titlePanel(" APP"),
sidebarLayout(
sidebarPanel(selectInput("x","Select cylinder",choices = c(mtcars$cyl),multiple = TRUE),
selectInput("y","Select gear",choices = c(mtcars$gear),multiple = TRUE),
submitButton("Submit")),
mainPanel(
tableOutput("m")
)))
server <- function(input,output){
check <- reactive({
if(is.null(input$x) & is.null(input$y)){
mtcars %>% summarise(Average_mpg = mean(mpg))
}else if(!is.null(input$x) & is.null(input$y)){
a <- mtcars %>% group_by(cyl) %>% summarise(Average_mpg = mean(mpg))
a %>% filter(cyl==input$x)
}else if(is.null(input$x) & !is.null(input$y)){
a <- mtcars %>% group_by(gear) %>% summarise(Average_mpg = mean(mpg))
a %>% filter(gear==input$y)
}else{
a <- mtcars %>% group_by(gear,cyl) %>% summarise(Average_mpg = mean(mpg))
a %>% filter(cyl==input$x & gear==input$y)
}
})
output$m <- renderTable(
check()
)
}
shinyApp(ui = ui, server = server)
Currently I have hard coded all possible combination using if else statement and then realized its not efficient way. If the filters/widgets increase then its difficult to manage
for e.g. If I add one more filter here for variable "carb" in mtcars dataset I have to include all possible scenarios what the user will select and hard code it.
My actual app is having 5 -6 more filters.
Is there any way where whatever the user selects the app will group by on the fly and then filter and show results.
This is not a perfect approach as it still involves some copy & paste and duplicated code. But as a first step it gets rid of the if-else to filter your data:
library(shiny)
library(dplyr)
choices_cyl <- unique(mtcars$cyl)
choices_gear <- unique(mtcars$gear)
ui <- fluidPage(
titlePanel(" APP"),
sidebarLayout(
sidebarPanel(
selectInput("x", "Select cylinder", choices = choices_cyl, multiple = TRUE),
selectInput("y", "Select gear", choices = choices_gear, multiple = TRUE),
submitButton("Submit")
),
mainPanel(
tableOutput("m")
)
)
)
server <- function(input, output) {
check <- reactive({
cyls <- input$x
gears <- input$y
grps <- c("cyl", "gear")[c(!is.null(cyls), !is.null(gears))]
if (is.null(cyls)) cyls <- choices_cyl
if (is.null(gears)) gears <- choices_gear
mtcars %>%
filter(cyl %in% cyls, gear %in% gears) %>%
group_by(across(all_of(grps))) %>%
summarise(Average_mpg = mean(mpg))
})
output$m <- renderTable(
check()
)
}
shinyApp(ui = ui, server = server)
I am trying to create two corresponding selectInput lists. To do so I made two uiOutput in ui attached to renderUI in server. The renderUIs are linked to reactiveValues which should change according to input$* values.
And it does work until one point. The selection list is shrinking and can't go back to default (while in my opinion it should, based on second line of observeEvent).
I have a feeling that no matter what the input$* values are never null so the is.null() won't work.
I will apprecieate any help in this topic.
if (interactive()) {
library(dplyr)
library(shiny)
library(shinydashboard)
library(plotly)
library(DT)
library(tidyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput('hair_filter'),
uiOutput('species_filter')
),
mainPanel( tableOutput('hairs'),
tableOutput('species'),
textOutput('text'),
textOutput('text2'),
tableOutput('hairfiltertable'),
tableOutput('speciesfiltertable')
)
))
server <- function(input, output, session){
starwars_full <- starwars %>%
as.data.frame() %>%
tibble::rownames_to_column(var = 'ID') %>%
transform(ID=as.numeric(ID), height=as.numeric(height), mass=as.numeric(mass), birth_year=as.numeric(birth_year)) %>%
group_by(ID, name, height,mass,hair_color, skin_color, eye_color, birth_year,sex,homeworld,species, films, vehicles, starships) %>%
summarise('cnt_films'=lengths(films),'cnt_vehicles'=lengths(vehicles),'cnt_ships'=lengths(starships))
#creating list of hair colors based on selected species
rv3 <- reactiveValues(hair_list = starwars_full %>%
separate_rows(hair_color,sep=", ") %>%
arrange(hair_color) %>%
as.data.frame() %>%
select(hair_color,species, name) %>%
distinct()
)
observeEvent(input$selected_from_dropdown_species,{
if(isTruthy(input$selected_from_dropdown_species))
{
rv3$hair_list <- starwars_full %>%
separate_rows(hair_color,sep=", ") %>%
arrange(hair_color) %>%
as.data.frame() %>%
select(hair_color,species, name) %>%
distinct() %>%
filter(species %in% input$selected_from_dropdown_species)
rv6$selected_species <- input$selected_from_dropdown_species
}
else
{
rv3$hair_list <- starwars_full %>%
separate_rows(hair_color,sep=", ") %>%
arrange(hair_color) %>%
as.data.frame() %>%
select(hair_color,species, name) %>%
distinct()
rv6$selected_species <- NULL
}
})
#creating species list, based on selected hair colors
rv4 <- reactiveValues(specie_list = starwars_full %>%
separate_rows(species,sep=", ") %>%
arrange(species) %>% as.data.frame() %>%
select(hair_color,species, name) %>%
distinct()
)
observeEvent(input$selected_from_dropdown_color,{
if(isTruthy(input$selected_from_dropdown_color))
{
rv4$specie_list <- starwars_full %>%
separate_rows(species,sep=", ") %>%
arrange(species) %>% as.data.frame() %>%
select(hair_color,species, name) %>%
distinct() %>%
filter(hair_color %in% input$selected_from_dropdown_color)
rv5$selected_colors <- input$selected_from_dropdown_color
}
else
{
rv4$specie_list <- starwars_full %>%
separate_rows(species,sep=", ") %>%
arrange(species) %>% as.data.frame() %>%
select(hair_color,species, name) %>%
distinct()
rv5$selected_colors <- NULL
}
})
rv5 <- reactiveValues(selected_colors = NULL)
rv6 <- reactiveValues(selected_species = NULL)
#selecinput of hair color
output$hair_filter = renderUI({
selectInput("selected_from_dropdown_color",
label ="Hair colors:",
choices=rv3$hair_list$hair_color,
multiple=TRUE,
selected=isolate(rv5$selected_colors))
})
#selectinput for species
output$species_filter = renderUI({
selectInput("selected_from_dropdown_species",
label ="Species",
choices=rv4$specie_list$species,
multiple=TRUE,
selected=isolate(rv6$selected_species))
})
output$hairs = renderTable({input$selected_from_dropdown_color})
output$species = renderTable({input$selected_from_dropdown_species})
output$text = renderPrint({print(input$selected_from_dropdown_color)})
output$text2 = renderPrint({print(input$selected_from_dropdown_species)})
output$hairfiltertable = renderTable({rv3$hair_list})
output$speciesfiltertable = renderTable({rv4$specie_list})
}
shinyApp(ui,server)
}
Edit:
We can use selectizeGroup from shinyWidgets to achieve the desired behaviour.
library(tidyverse)
library(shiny)
library(shinydashboard)
library(plotly)
library(DT)
library(tidyr)
library(shinyWidgets)
starwars_full <- starwars %>%
as.data.frame() %>%
rownames_to_column(var = "ID") %>%
transform(ID = as.numeric(ID), height = as.numeric(height), mass = as.numeric(mass), birth_year = as.numeric(birth_year)) %>%
group_by(ID, name, height, mass, hair_color, skin_color, eye_color, birth_year, sex, homeworld, species, films, vehicles, starships) %>%
summarise("cnt_films" = lengths(films), "cnt_vehicles" = lengths(vehicles), "cnt_ships" = lengths(starships))
starwars_species_hair <- starwars_full %>%
separate_rows(hair_color, sep = ", ") %>%
separate_rows(species, sep = ", ") %>%
select(hair_color, species, name)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeGroupUI(
id = "my-filters",
params = list(
hair_color = list(inputId = "hair_color", title = "Hair color:"),
species = list(inputId = "species", title = "Species:")
)
)
),
mainPanel(DTOutput("resulting_table"))
)
)
server <- function(input, output, session) {
res_mod <- callModule(
module = selectizeGroupServer,
id = "my-filters",
data = starwars_species_hair,
vars = c("hair_color", "species")
)
output$resulting_table <- renderDT({
req(res_mod)
datatable(res_mod())
})
}
shinyApp(ui, server)
We can access selected values inside a reactive/observer by:
observe({
input[["my-filters-hair_color"]]
input[["my-filters-species"]]
)}
Please find the code below for a shiny App using ggplot2, I do not know how to sort them inside the server.R code.
WIth the below code I am able to display the bar chart and change it but ordering the data seems to be an issue.
ui.R
ui <- fluidPage(
titlePanel("Perdas no Gefin"),
theme = shinythemes::shinytheme('yeti'),
sidebarLayout(
sidebarPanel(selectInput('mes', 'Selecione o mês', unique(month(roi$Data_Contab))),
mainPanel(
tabsetPanel(
tabPanel('Word', plotly::plotlyOutput('contagem'))
))
)
)
server.R
server <- function(input, output, session){
rcontagem <- reactive({
roi %>%
filter(month(Data_Contab) == input$mes) %>%
unnest_tokens(word, Parecer) %>%
anti_join(stop_words2) %>%
count(word) %>%
na.omit() %>%
top_n(30) %>%
arrange(desc(n))
})
output$contagem <- plotly::renderPlotly({
rcontagem()%>%
ggplot(aes(x = word, y = n)) +
geom_col() +
# Flip the plot coordinates
coord_flip() +
ggtitle("Contagem de palavras")
})
}
shinyApp(ui = ui, server = server)
Plot without order:
I already tried this: Sorting of Bars-ggplot2 in shiny, but it didn't work, probably because I'm using reactive.
You could try something like this on your server side:
server <- function(input, output, session){
rcontagem <- reactive({
roi %>%
filter(month(Data_Contab) == input$mes) %>%
unnest_tokens(word, Parecer) %>%
anti_join(stop_words2) %>%
count(word) %>%
na.omit() %>%
top_n(30) %>%
arrange(desc(n))
})
output$contagem <- plotly::renderPlotly({
rcontagem()%>%
ggplot(aes(x = reorder(word,n), y = n)) +
geom_col() +
# Flip the plot coordinates
coord_flip() +
ggtitle("Contagem de palavras")
})
}
I am new to Shiny and have a basic shiny app using mtcars. I have multiple tabs with some input dropdowns and presenting the output as DT tables. This is all working fine, but I would now like to use some formatting like formattable. Some of the formatting I would like to include is basic percentage, decimal. Also, I would like to add some cell based highlighting. I have tried multiple formatting functions without any luck. I have added functions within the server side output, but I can not get the right combination. Below is my Shiny code:
library(shiny)
library(dplyr)
library(DT)
ui <- fluidPage(
titlePanel("mtcars"),
sidebarLayout(
sidebarPanel(
selectInput("cyl",
"cyl:",
c(unique(as.character(mtcars$cyl)))),
selectInput("gear",
"gear:",
c("All",
unique(as.character(mtcars$gear)))), width=2),
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("Summary", DT::dataTableOutput("Summary")),
tabPanel("Detail", DT::dataTableOutput("Detail"))))))
server <- function(input, output) {
output$Detail <- renderDataTable(datatable({
data <- mtcars
if (input$cyl != "All") {
data <- data[data$cyl == input$cyl,]
}
if (input$gear != "All") {
data <- data[data$gear == input$gear,]
}
data
}))
output$Summary <- renderDataTable({
mtcars %>%
filter(cyl==input$cyl) %>%
group_by(gear) %>%
summarise(mpg = median(mpg),
count = n()) %>%
ungroup() %>%
arrange(desc(count))
})}
shinyApp(ui = ui, server = server)
I'm not sure what you've tried so far with formattable, but you should be able to use it with DT in your shiny app.
Here is a quick example you can try. This makes the mpg column a percentage. Also, if colors the count column a shade of green.
Other vignettes are available for other options with formattable package.
output$Summary <- renderDataTable({
my_data <- mtcars %>%
filter(cyl==input$cyl) %>%
group_by(gear) %>%
summarise(mpg = median(mpg),
count = n()) %>%
ungroup() %>%
arrange(desc(count))
# Make percent, for example
my_data$mpg <- percent(my_data$mpg)
# Return formattable datatable
return(
as.datatable(
formattable(
my_data,
list(
count = color_tile("transparent", "green")
)
)
)
)
})
To complete Ben's answer, even if you say you want to use formattable, I think there are enough options in DT to customize the tables the way you want.
Here's your example (randomly customized since you didn't specify the formatting of the cells):
library(shiny)
library(dplyr)
library(DT)
ui <- fluidPage(
titlePanel("mtcars"),
sidebarLayout(
sidebarPanel(
selectInput("cyl",
"cyl:",
c(unique(as.character(mtcars$cyl)))),
selectInput("gear",
"gear:",
c("All",
unique(as.character(mtcars$gear)))), width=2),
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("Summary", DT::dataTableOutput("Summary")),
tabPanel("Detail", DT::dataTableOutput("Detail"))))))
server <- function(input, output) {
output$Detail <- renderDataTable(datatable({
data <- mtcars
if (input$cyl != "All") {
data <- data[data$cyl == input$cyl,]
}
if (input$gear != "All") {
data <- data[data$gear == input$gear,]
}
data
}))
output$Summary <- renderDataTable({
your_data <- mtcars %>%
filter(cyl==input$cyl) %>%
group_by(gear) %>%
summarise(mpg = median(mpg),
count = n()) %>%
ungroup() %>%
arrange(desc(count))
datatable(your_data) %>%
formatPercentage(columns = c("mpg", "gear")) %>%
formatRound(columns = c("count"), digits = 3) %>%
formatStyle(columns = "mpg",
valueColumns = "gear",
backgroundColor = styleEqual(c(3, 4, 5), c("red", "blue", "green")))
})}
shinyApp(ui = ui, server = server)
See here for more details, and here for several examples of color-styling.
I have multiple ggvis plots in Shiny.
I need to provide an action button, if the button is clicked all the plots need to be deleted.
Below is a sample code for ui.R and server.R:
ui.R
library(ggvis)
library(shiny)
shinyUI(fluidPage(
titlePanel("Plotting slopes"),
sidebarLayout(
sidebarPanel(
selectInput("segment", label = "Choose segment", choices = c("K 1", "K 2")),
actionButton("abutton","Delete plots")),
mainPanel(ggvisOutput("plot"), ggvisOutput("plot2"))
)
))
server.R
library(shiny)
library(ggvis)
mtcars$cyl = factor(mtcars$cyl)
df1 = subset(mtcars, am == 0)
df2 = subset(mtcars, am == 1)
shinyServer(function(input, output) {
dataInput = reactive({
switch(input$segment,
"K 1" = df1,
"K 2" = df2)
})
values = function(x){
if(is.null(x)) return(NULL)
dat = dataInput()
row = dat[dat$cyl %in% unique(x$cyl), ]
paste0("Ave Weight: ", mean(row$wt),"<br />",
"Ave Carb: ", mean(row$carb), "<br />")
}
vis1 = reactive({
dat = dataInput()
dat %>%
group_by(cyl) %>%
ggvis(~mpg, ~wt) %>%
layer_paths(stroke = ~cyl, strokeOpacity := 0.3,
strokeWidth := 5) %>%
add_tooltip(values, "hover")
})
vis1 %>% bind_shiny("plot")
vis2 = reactive({
dat = dataInput()
dat %>%
group_by(cyl) %>%
ggvis(~mpg, ~wt) %>%
layer_paths(stroke = ~cyl, strokeOpacity := 0.3,
strokeWidth := 5) %>%
add_tooltip(values, "hover")
})
vis2 %>% bind_shiny("plot2")
})
Screenshot of the current output:
If you tolerate the plots NOT deleted but LOOK LIKE deleted, I think it'll be easy to give bind_shiny() a blank graph.
server.R
:
vis2 %>% bind_shiny("plot2") # the same up to here
vis3 = mtcars %>% # preparation of a blank graph
ggvis(~mpg, ~wt, opacity := 0) %>%
layer_points() %>%
hide_axis("x") %>%
hide_axis("y")
observeEvent(input$abutton, { # When the button is clicked,
bind_shiny(vis3, "plot") # bind_shiny() reads and outputs a blank graph, vis3.
bind_shiny(vis3, "plot2") # When other Input is done, vis1 and vis2 return.
})
})