library("tidyverse"); theme_set(theme_bw())
library("shiny")
Shiny applications are interactive websites dashdboards built using R and produced using an R Server backend. They are much more flexible in what you can do compared to using packages that allow for interactive tables and figures. Due to the backend requirement, they are not as easy to deploy as the approaches we discussed as the interactive tables and figures. My guess is that shiny 2.0 will utilize javascript and avoid the R server backend, but we’ll have to wait and see.
The backend requirement can be avoided by utilizing Posit’s shinyapps.io. The downside to using this deployment platform is that you are limited in the amount of monthly use on your app.
Take a look at some shiny apps on the Shiny Gallery to get an idea of what shiny can do.
Let’s explore this Radiant app by Vincent Nijs.
The user can control the app through a set of control widgets. Here is a demonstration of many of those widgets.
ui <- fluidPage(
titlePanel("User input options"),
column(width = 3,
actionButton("action",
label = "actionButton"),
fileInput("file",
label = "File input"),
textInput("text",
label = "Text")
),
column(width = 3,
checkboxInput("checkbox",
label = "Checkbox"),
checkboxGroupInput("checkboxGroup",
label = "Checkbox Group",
choices = c("A","B","C")),
radioButtons("radioButtons",
label = "Radio Buttons",
choices = c("A","B","C")),
selectInput("select",
label = "Select",
choices = c("A","B","C")),
selectInput("select",
label = "Select",
choices = c("A","B","C"),
multiple = TRUE)
),
column(width = 3,
numericInput("numeric",
label = "Numeric", value = 0),
sliderInput("slider",
label = "Slider",
min = 0, max = 10, value = 5, step = 1),
sliderInput("slider",
label = "Slider",
min = 0, max = 10, value = c(3,7), step = 1)
),
column(width = 3,
dateInput("date",
label = "Date"),
dateRangeInput("daterange",
label = "Date range"),
helpText(h3("Help text"),
"Here is some help text.")
)
)
shinyApp(ui = ui, server = function(input, output) {} )
ui <- fluidPage(
titlePanel("Here is text output!"),
textOutput("text")
)
server <- function(input, output) {
output$text <- renderText({
"Here's some text"
})
}
shinyApp(ui = ui, server = server)
ui <- fluidPage(
titlePanel("Here is table output!"),
tableOutput('table')
)
server <- function(input, output) {
output$table <- renderTable(warpbreaks)
}
shinyApp(ui = ui, server = server)
ui <- fluidPage(
titlePanel("Here is datatable output!"),
dataTableOutput('table')
)
server <- function(input, output) {
output$table <- renderDataTable(warpbreaks)
}
shinyApp(ui = ui, server = server )
ui <- fluidPage(
titlePanel("Here is plot output!"),
plotOutput("plot")
)
server <- function(input, output) {
output$plot <- renderPlot({
hist(rnorm(100))
})
}
shinyApp(ui = ui, server = server)
ui <- fluidPage(
titlePanel("Here is print output!"),
verbatimTextOutput("print")
)
server <- function(input, output) {
output$print <- renderPrint({
m <- lm(breaks ~ tension + wool, data = warpbreaks)
summary(m)
})
}
shinyApp(ui = ui, server = server)
In the output examples above, you have seen that the output table,
figure, text, etc. are constructed in the server function,
render
ed, and then shown to the user using an
*Output
function in the ui
. In that
*Output
function, we used a text string that corresponded
to the list element of the output
object. Thus, we had a
connection from the server
function to the ui
through the output
object.
Here we will see how to create a connection from the ui
to the server
using the input
object and the
inputId
s in control widgets.
ui <- fluidPage(
titlePanel("2-D Histogram with Hexes!"),
sidebarLayout(
sidebarPanel(
checkboxInput(inputId = "logx", label = "Log Carat"),
checkboxInput(inputId = "logy", label = "Log Price"),
textInput(inputId = "low", label = "Low color", value = "gray"),
textInput(inputId = "high", label = "High color", value = "blue"),
),
mainPanel(
plotOutput(outputId = "hexbinPlot")
)
)
)
server <- function(input, output) {
output$hexbinPlot <- renderPlot({
g <- ggplot(diamonds, aes(x = carat, y = price)) +
geom_hex() +
theme_bw() +
labs(
title = "Diamonds: Price vs Carat",
x = "Carat",
y = "Price ($)"
) +
scale_fill_gradient(
low = input$low,
high = input$high
)
if (input$logx)
g <- g + scale_x_log10()
if (input$logy)
g <- g + scale_y_log10()
g
})
}
shinyApp(ui = ui, server = server)
Here is an app I created to try to understand the frequency interpretation of a particular p-value. The idea is to understand how often the null hypothesis is true when we observe a particular p-value, say 0.05.
To construct the app, we consider the simple example of \[ Y \sim N(\mu,1). \] with null hypothesis \[ H_0: \mu = 0. \]
The frequency of the null hypothesis being true will depend on
To address the first question, the user will choose the proportion of times the null hypothesis is true among experiments run by a scientist. Various possibilities are reasonable and will depend on the scientific field the user is trying to emulate. On one hand, scientists are typically running experiments because they think the null is not true and thus, probably, the null is true in only a small fraction of experiments. On the other hand, scientists who are running high-throughput experiments, e.g. microarray or remote sensing, which are like looking for a needle in a haystack. In these situations, the null is true in a high fraction of experiments.
Once the user determines the proportion of null hypotheses that are true among the scientist’s experiments, the user needs to determine what \(\mu\) is when the null hypothesis is not true. Since we will end up having many experiments where the null is not true, we generally sample \(\mu\) from some distribution. A distribution where values for \(\mu\) are far from zero (relative to the data variance of 1) indicates large effects. In contrast, if the distribution is near zero, then this indicates small effects.
The app the simulates scientists experiments by randomly determining whether the null or alternative are true. When the null is true, then \(\mu\) is set to 0 while when the alternative is true, it samples a new value for \(\mu\). Then it samples an observation \(Y\) according to the value for \(\mu\). Using this value for \(Y\), the app will compute a p-value. If the p-value is within a small window around the chosen p-value, the app will record whether or not the null hypothesis is true.
The app will repeat this process until the desired number of p-values within the small window are observed. It will then report how many times the null (and alternative) were true.
shiny::runGitHub('pvalue','jarad')
The code for this app can be seen at https://github.com/jarad/pvalue.