I have a modularized shiny app that displays in real time 4 variables. The monitor module takes as an input one big data frame, and displays the signal assigned. The 4 modules are stacked together in the ui:
tabBox(id = "monitoring_tabBox",
height = monitor_height_px,
width = "500px",
tabPanel(id = "layout1",
title = "Layout 1",
monitorModuleUI("sbto2"),
monitorModuleUI("icp"),
monitorModuleUI("map"),
monitorModuleUI("ptio2")
)
),
The problem is the following: the plots are not perfectly aligned across modules. Mainly because the y ticks values have different ranges (see how the icp and the ptio2 are aligned because both signals have two digits, without decimals)
I've seen several techniques to align ggplots but you need to take as an input the 4 plots, and then merge them all in a single plot / render. I would like to avoid this step to keep the modularized structure of the app.
Is there any way I can align those plots without having to marge them together? (i.e by constraining the length of the y ticks)
Thank you in advance !
Screenshot:
screenshot showing the 4 modules and the misalignment issue
Reproducible example:
library(shiny)
library(dplyr)
library(ggplot2)
library(tidyr)
# Sample Data
df <- data.frame(timestamp = seq(as.POSIXct("2020-06-01 10:00:00"), as.POSIXct("2020-06-01 12:00:00"), "1 min"),
sbto2 = round(10000*rnorm(121, 0, 2), 1),
map = round(100*rnorm(121, 0, 2), 1),
icp = round(10*rnorm(121, 0, 1.5), 1),
ptio2 = round(1000*rnorm(121, 0, 1.2), 1))
# monitorModule
monitorModuleUI <- function(id){
ns <- NS(id)
fluidRow(
column(8,
plotOutput(ns("monitoring_plot"),
height = "150px")
),
column(2,
uiOutput(ns("monitoring_text"))
)
)
}
monitorModule <- function(input, output, server, variable_name, df){
output$monitoring_plot <- renderPlot({
p()
})
p <- reactive({
df %>%
gather("var", "value",seq(2,5)) %>%
filter(var == variable_name) %>%
ggplot(aes(x = timestamp, y = value)) + geom_line() -> p
return(p)
})
output$monitoring_text <- renderUI({
value <- p()$data$value[nrow(p()$data)]
h1(strong(paste(value)), style = "font-size:90;")
})
}
# APP
ui <- shinyServer(fluidPage(
monitorModuleUI("sbto2"),
monitorModuleUI("icp"),
monitorModuleUI("ptio2"),
monitorModuleUI("map")
))
server <- shinyServer(function(input, output, session){
callModule(monitorModule, "sbto2", "sbto2", df)
callModule(monitorModule, "icp", "icp", df)
callModule(monitorModule, "ptio2", "ptio2", df)
callModule(monitorModule, "map", "map", df)
})
shinyApp(ui=ui,server=server)
One alternative would be to return a reactive plot from each module and then to organize them with the package {patchwork}
.
Here's an example:
library(shiny)
library(dplyr)
library(ggplot2)
library(tidyr)
library(patchwork)
# Sample Data
df <- data.frame(timestamp = seq(as.POSIXct("2020-06-01 10:00:00"), as.POSIXct("2020-06-01 12:00:00"), "1 min"),
sbto2 = round(10000*rnorm(121, 0, 2), 1),
map = round(100*rnorm(121, 0, 2), 1),
icp = round(10*rnorm(121, 0, 1.5), 1),
ptio2 = round(1000*rnorm(121, 0, 1.2), 1))
# monitorModule
monitorModuleUI <- function(id){
# ns <- NS(id)
# plotOutput(ns("monitoring_plot"),
# height = "150px")
}
monitorModule <- function(input, output, server, variable_name, df){
test <- reactive({
df %>%
gather("var", "value",seq(2,5)) %>%
filter(var == variable_name) %>%
ggplot(aes(x = timestamp, y = value)) + geom_line() -> p
return(p)
})
}
# APP
ui <- fluidPage(
monitorModuleUI("sbto2"),
monitorModuleUI("icp"),
monitorModuleUI("ptio2"),
monitorModuleUI("map"),
plotOutput("all_plots")
)
server <- function(input, output, session){
plot_1 <- callModule(monitorModule, "sbto2", "sbto2", df)
plot_2 <- callModule(monitorModule, "icp", "icp", df)
plot_3 <- callModule(monitorModule, "ptio2", "ptio2", df)
plot_4 <- callModule(monitorModule, "map", "map", df)
output$all_plots <- renderPlot({
plot_1() / plot_2() / plot_3() / plot_4()
})
}
shinyApp(ui=ui,server=server)