Nothing
#' Indicator visualisation dashboard
#'
#' Generates an interactive visualisation of one or two indicators at a time. Requires Shiny and an active R session.
#' This dashboard is useful for quickly exploring indicator data, and seeing e.g. an untreated indicator distribution
#' against its treated equivalent.
#'
#' This function requires an interactive R session. Otherwise it will simply generate a message to that effect.
#'
#' @param COIN The COIN object
#'
#' @import shiny
#' @importFrom plotly plot_ly plotlyOutput layout add_trace renderPlotly
#' @importFrom reactable reactable renderReactable
#'
#' @examples
#' # Only run in interactive mode
#' if(interactive()){
#' # build ASEM COIN
#' ASEM <- build_ASEM()
#' # view dashboard
#' indDash(ASEM)
#' }
#'
#' @return Interactive app (if running an interactive R session). This app is purely exploratory and does not return anything back to R.
#'
#' @export
indDash <- function(COIN){
if(interactive()){
# first, get the indicator data from input object
out <- getIn(COIN)
if(out$otype != "COINobj"){stop("indDash() only supports COINs as inputs.")}
ind_data_only <- out$ind_data_only
ind_data <- out$ind_data
ind_codes <- out$IndCodes
# this is used to label scatter plot. Will need to be generalised.
code_yr <- out$ind_data$UnitName
rfac = 0.1 # parameter to adjust the fixed range when matching axes
# data set names, include denoms if possible
if(is.null(COIN$Input$Denominators)){
dsetnames <- names(COIN$Data)
} else {
dsetnames <- c("Denominators", names(COIN$Data))
}
###--------- Define the UI ---------------
ui <- fluidPage(
sidebarPanel(
h2("Indicator Viewer"),
hr(),
h4("Indicator 1"),
selectInput("dset1", "Data set", choices= dsetnames),
selectInput("vr1", "Indicator", choices=colnames(ind_data_only)),
h4("Indicator 2"),
selectInput("dset2", "Data set", choices= dsetnames),
selectInput("vr2", "Indicator", choices=colnames(ind_data_only)),
checkboxInput(inputId = "vrmatch", label = "Match indicators (select using indicator 1)", value = FALSE),
textOutput("info"),
checkboxInput(inputId = "axmatch", label = "Plot indicators on the same axis range", value = FALSE),
hr(),
h4("Treated indicators"),
reactable::reactableOutput("treatinfoall")
),
mainPanel(
fluidRow(
column(6,
plotly::plotlyOutput("histo", height = "250px"),
plotly::plotlyOutput("violin", height = "300px"),
tableOutput("treatinfo1")
),
column(6,
plotly::plotlyOutput("histo2", height = "250px"),
plotly::plotlyOutput("violin2", height = "300px"),
tableOutput("treatinfo2")
)
),
br(),
fluidRow(
column(6,
plotly::plotlyOutput("scatter")
),
column(6,
plotly::plotlyOutput("histo12")
)
)
)
)
###------ Define the server code -----------
server <- function(input, output, session) {
## ---- First initialise and modify some values ---- ##
# initialise reactive values: the data sets
idata1 <- reactiveVal(ind_data)
idata2 <- reactiveVal(ind_data)
# initialise reactive values: the indicator codes for each dset
icodes1 <- reactiveVal(ind_codes)
icodes2 <- reactiveVal(ind_codes)
# get reactive values: the indicator names (with data set added, for plots)
iname1 <- reactive(paste0(isel1()," - ", input$dset1))
iname2 <- reactive(paste0(isel2()," - ", input$dset2))
# get reactive values: the selected indicator codes (for internal reference)
isel1 <- reactive({
if (exists(input$vr1,idata1())){
return(input$vr1)
} else {
return(icodes1()[1])
}
})
isel2 <- reactive({
if (input$vrmatch){
isel_2 <- isel1()
} else {
isel_2 <- input$vr2
}
if (exists(isel_2,idata2())){
return(isel_2)
} else {
return(icodes2()[1])
}
})
# get reactive values: matching axes (only used in plots if requested)
axlims <- reactive({
# get ranges of both selected indicators
r1 <- range(idata1()[isel1()], na.rm = TRUE)
r2 <- range(idata2()[isel2()], na.rm = TRUE)
# build new range which includes both
r12 <- c( min(r1[1],r2[1], na.rm = TRUE), max(r1[2],r2[2], na.rm = TRUE) )
# add a little bit on each end
ran12 <- r12[2]-r12[1]
r12[1] <- r12[1]-rfac*ran12
r12[2] <- r12[2]+rfac*ran12
return(r12)
})
# update data set 1 to selected one plus codes
observeEvent(input$dset1,{
out1 <- getIn(COIN, input$dset1)
idata1(out1$ind_data)
icodes1(out1$IndCodes)
})
# update data set 2 to selected one
observeEvent(input$dset2,{
out2 <- getIn(COIN, input$dset2)
idata2(out2$ind_data)
icodes2(out2$IndCodes)
})
## ---- Plots ---- ##
# Violin plot v1
output$violin <- plotly::renderPlotly({
fig <- plot_ly(data = idata1(), y = ~get(isel1()), type = 'violin',
box = list(visible = T),
meanline = list(visible = T),
x0 = iname1(),
points = 'all',
pointpos = -1.5,
jitter = 0.1,
hoveron = "violins+points+kde"
)
# match axes with other variable if requested
if (input$axmatch==TRUE){
fig <- fig %>% plotly::layout( yaxis = list(title = "", zeroline = F, range = axlims()) )
} else {
fig <- fig %>% plotly::layout( yaxis = list(title = "", zeroline = F) )
}
fig
})
# Histogram v1
output$histo <- plotly::renderPlotly({
fig <- plot_ly(data = idata1(), x = ~get(isel1()), type = "histogram")
# match axes with other variable if requested
if (input$axmatch==TRUE){
fig <- fig %>% plotly::layout(bargap=0.1, xaxis = list(title = iname1(), range = axlims()))
} else {
fig <- fig %>% plotly::layout(bargap=0.1, xaxis = list(title = iname1()))
}
fig
})
# Violin plot v2
output$violin2 <- plotly::renderPlotly({
fig <- plotly::plot_ly(data = idata2(), y = ~get(isel2()), type = 'violin',
box = list(visible = T),
meanline = list(visible = T),
x0 = iname2(),
points = 'all',
pointpos = -1.5,
jitter = 0.1,
hoveron = "violins+points+kde"
)
# match axes with other variable if requested
if (input$axmatch==TRUE){
fig <- fig %>% plotly::layout( yaxis = list(title = "", zeroline = F, range = axlims()) )
} else {
fig <- fig %>% plotly::layout( yaxis = list(title = "", zeroline = F) )
}
fig
})
# Histogram v2
output$histo2 <- plotly::renderPlotly({
fig <- plot_ly(data = idata2(), x = ~get(isel2()), type = "histogram")
# match axes with other variable if requested
if (input$axmatch==TRUE){
fig <- fig %>% plotly::layout(bargap=0.1, xaxis = list(title = iname2(), range = axlims()))
} else {
fig <- fig %>% plotly::layout(bargap=0.1, xaxis = list(title = iname2()))
}
fig
})
# scatter plot between v1 and v2
output$scatter <- plotly::renderPlotly({
# build data frame first, since the variables may come from different dfs
df <- dplyr::inner_join(
idata1()[c("UnitCode", "UnitName", isel1())],
idata2()[c("UnitCode", "UnitName", isel2())],
by = "UnitCode"
)
colnames(df) <- c("UnitCode", "UnitName", "v1", "UnitName2", "v2")
# build data frame first, since the variables may come from different dfs
# df <- data.frame(v1 = dplyr::pull(idata1(),isel1()),
# v2 = dplyr::pull(idata2(),input$vr2))
sc <- plotly::plot_ly(data = df, type = 'scatter', mode = 'markers') %>%
plotly::add_trace(
x = ~v1,
y = ~v2,
text = ~UnitName,
hoverinfo = 'text',
marker = list(size = 15),
showlegend = F
) %>%
plotly::layout(xaxis = list(title = iname1()),
yaxis = list(title = iname2()))
sc
})
# Overlaid histogram of v1 and v2 together
output$histo12 <- plotly::renderPlotly({
# # build data frame first, since the variables may come from different dfs
# df <- data.frame(v1 = dplyr::pull(idata1(),isel1()),
# v2 = dplyr::pull(idata2(),input$vr2))
# build data frame first, since the variables may come from different dfs
df <- dplyr::inner_join(
idata1()[c("UnitCode", "UnitName", isel1())],
idata2()[c("UnitCode", "UnitName", isel2())],
by = "UnitCode"
)
colnames(df) <- c("UnitCode", "UnitName", "v1", "UnitName2", "v2")
fig <- plotly::plot_ly(df, alpha = 0.6)
fig <- fig %>% plotly::add_histogram(x = ~v1, name = iname1())
fig <- fig %>% plotly::add_histogram(x = ~v2, name = iname2())
fig <- fig %>% plotly::layout(barmode = "overlay",
xaxis = list(title = ""))
fig
})
## ---- Text ouputs ---- ##
# data treatment summary, if treated data is selected
output$treatinfo1 <- renderTable({
if(input$dset1=="Treated"){
tbl <- COIN$Analysis$Treated$TreatSummary
# get row of indicator, remove indcode
treatinfo <- tbl[tbl$IndCode == isel1(),-1]
# add stats
treatinfo <- cbind(treatinfo,
Skew = round( e1071::skewness(idata1()[[isel1()]], na.rm = T, type = 2), 3),
Kurtosis = round( e1071::kurtosis(idata1()[[isel1()]], na.rm = T, type = 2), 3))
} else {
# no treated data set selected, so just return skew and kurtosis
treatinfo <- data.frame(
Skew = round( e1071::skewness(idata1()[[isel1()]], na.rm = T, type = 2), 3),
Kurtosis = round( e1071::kurtosis(idata1()[[isel1()]], na.rm = T, type = 2), 3))
}
return(treatinfo)
})
# data treatment summary, if treated data is selected
output$treatinfo2 <- renderTable({
if(input$dset2=="Treated"){
tbl <- COIN$Analysis$Treated$TreatSummary
# get row of indicator, remove indcode
treatinfo <- tbl[tbl$IndCode == isel2(),-1]
# add stats
treatinfo <- cbind(treatinfo,
Skew = round( e1071::skewness(idata2()[[isel2()]], na.rm = T, type = 2), 3),
Kurtosis = round( e1071::kurtosis(idata2()[[isel2()]], na.rm = T, type = 2), 3))
} else {
# no treated data set selected, so just return skew and kurtosis
treatinfo <- data.frame(
Skew = round( e1071::skewness(idata2()[[isel2()]], na.rm = T, type = 2), 3),
Kurtosis = round( e1071::kurtosis(idata2()[[isel2()]], na.rm = T, type = 2), 3))
}
return(treatinfo)
})
# data treatment summary, if treated data is selected
output$treatinfoall <- reactable::renderReactable({
# show info of indicator 1 if a treated variable is selected, otherwise i2, otherwise message
if(input$dset1=="Treated" | input$dset2=="Treated"){
treatinfoall <- COIN$Analysis$Treated$TreatSummary
treatinfoall <- treatinfoall[treatinfoall$Treatment !="None",]
} else {
treatinfoall <- data.frame(IndCode = "Select a treated data set (if it exists) to view.")
}
return(treatinfoall %>%
reactable::reactable(defaultPageSize = 5, highlight = TRUE, wrap = FALSE,
rownames = FALSE, resizable = TRUE))
})
## ---- Update dropdown menus ---- ##
# Update dropdown menu of indicator selection based on data set 1
observeEvent(input$dset1,{
updateSelectInput(session = session, inputId = "vr1",
choices = getIn(COIN,input$dset1)$IndCodes)
})
# Update dropdown menu of indicator selection based on data set 2
observeEvent(input$dset2,{
updateSelectInput(session = session, inputId = "vr2",
choices = getIn(COIN,input$dset2)$IndCodes)
})
# Update selected indicator in dropdown 2
observeEvent(isel2(),{
updateSelectInput(session = session, inputId = "vr2", selected = isel2())
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
} else {
message("indDash() only works in interactive mode. Open an interactive R session to view the app.")
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.