O que é shiny?

O Shiny é um sistema para desenvolvimento de aplicações web usando o R, um pacote do R (shiny) e um servidor web (shiny server). O Shiny é exatamente isso e nada mais, portanto Shiny não é uma página web.

Como criar um exemplo que utilize o shiny?

  • Apresentação ou documento: File > New file > R Markdown

Escolha a opção Shiny, depois documento ou apresentação.

Um ponto positivo é que em um arquivo definido assim podemos utilizar os gráficos interativos dos pacotes ggvis, ggiraph, plotly, etc.  

  • Aplicativo: File > New file > Shiny web app

Exemplo inicial

Este exemplo aparece ao criar um arquivo.

inputPanel(
  # configurando o objeto n_breaks
  selectInput("n_breaks", label = "Number of bins:",
              choices = c(10, 20, 35, 50), selected = 20),
  
  # configurando o objeto bw_adjust
  sliderInput("bw_adjust", label = "Bandwidth adjustment:",
              min = 0.2, max = 2, value = 1, step = 0.2)
)

renderPlot({
  hist(faithful$eruptions, probability = TRUE, breaks = as.numeric(input$n_breaks),
       xlab = "Duration (minutes)", main = "Geyser eruption duration")
  
  dens <- density(faithful$eruptions, adjust = input$bw_adjust)
  lines(dens, col = "blue")
})

inputPanel

inputPanel

Cria o painel para que o usuário possa interagir. Para isso, precisamos adicionar os inputs.

inputs

Na prática, inputs são widgets que possibilitam a interação do usuário com o app. Eles recebem um valor escolhido pelo usuário e o envia para o server side.


Segue uma lista das principais funções para inputs utilizados em um Shiny app:

  • actionButton: botão para executar uma ação.
  • checkboxGroupInput: um grupo de check boxes.
  • checkboxInput: um único check box.
  • dateInput: um calendário para seleção de data.
  • dateRangeInput: um par de calendários para escolher um intervalo de datas.
  • fileInput: uma ferramenta para auxiliar o upload de arquivos.
  • numericInput: Um campo para enviar números.
  • radioButtons: Um conjunto de botões para seleção.
  • selectInput: Um select box com um conjunto de opções.
  • sliderInput: Um slider.
  • textInput: Um campo para enviar texto.

renders

renders

Os outputs devem ser construídos com funções render_(). Existe uma função render_() para cada tipo de objeto.

As principais são:

  • renderDataTable: data frames.
  • renderImage: imagens.
  • renderPlot: gráficos.
  • renderPrint: qualquer printed output.
  • renderTable: data frames, matrizes, e outras estruturas em forma de tabela.
  • renderText: strings.
  • renderUI: um elemento do UI ou HTML.
  • Existem algumas funções exclusivas para gráficos de alguns pacotes, por exemplo, renderPlotly, renderGirafe, renderLeaflet, etc. Ao utilizar algum pacote com gráficos interativos procure a função render adequada.

O argumento dessas funções será sempre um bloco de código, usado para gerar o output desejado.

Outro exemplo

Outro exemplo

Exemplo ao criar Rmarkdown document.

shinyAppDir(
  system.file("examples/06_tabsets", package = "shiny"),
  options = list(
    width = "100%", height = 550
  )
)

O aplicativo que está sendo carregado esta em {C://Program Files//R//R-3.5.1//library//shiny//examples//06_tabsets}.



É possível fazer a mesma coisa em um documento ou apresentação sem ter que fazer um aplicativo.

sidebarLayout(

    # Sidebar panel for inputs ----
    sidebarPanel(

      # Input: Select the random distribution type ----
      radioButtons("dist", "Distribution type:",
                   c("Normal" = "norm",
                     "Uniform" = "unif",
                     "Log-normal" = "lnorm",
                     "Exponential" = "exp")),

      # br() element to introduce extra vertical spacing ----
      br(),

      # Input: Slider for the number of observations to generate ----
      sliderInput("n",
                  "Number of observations:",
                  value = 500,
                  min = 1,
                  max = 1000)

    ),

    # Main panel for displaying outputs ----
    mainPanel(

      # Output: Tabset w/ plot, summary, and table ----
      tabsetPanel(type = "tabs",
                  tabPanel("Plot", plotOutput("plot")),
                  tabPanel("Summary", verbatimTextOutput("summary")),
                  tabPanel("Table", tableOutput("table"))
      )

    )
)



d <- reactive({
    dist <- switch(input$dist,
                   norm = rnorm,
                   unif = runif,
                   lnorm = rlnorm,
                   exp = rexp,
                   rnorm)

    dist(input$n)
  })


  output$plot <- renderPlot({
    dist <- input$dist
    n <- input$n

    hist(d(),
         main = paste("r", dist, "(", n, ")", sep = ""),
         col = "#75AADB", border = "white")
  })



  # Generate an HTML table view of the data ----
  output$table <- renderTable({
    d()
  })
  
  # Generate a summary of the data ----
  output$summary <- renderPrint({
    summary(d())
  })

Mais sobre aplicativos

Rodando os exemplos internos

Note que cada exemplo explora ferramentas diferentes!

runExample("01_hello")      # a histogram
runExample("02_text")       # tables and data frames
runExample("03_reactivity") # a reactive expression
runExample("04_mpg")        # global variables
runExample("05_sliders")    # slider bars
runExample("06_tabsets")    # tabbed panels
runExample("07_widgets")    # help text and submit buttons
runExample("08_html")       # Shiny app built from HTML
runExample("09_upload")     # file upload wizard
runExample("10_download")   # file download wizard
runExample("11_timer")      # an automated timer

Estrutura de um aplicativo

Estrutura de um aplicativo

Veja no exemplo abaixo que o aplicativo pode ser separado em duas partes:

  • ui: user side
  • server: Server side
library(shiny)

# Define a interface do usuário para o app que gera um histograma.
ui <- fluidPage(

  # Título do app.
  titlePanel("Meu primeiro shiny app!"),

  # Barra lateral com as definições do input e do output.
  sidebarLayout(

    # Barra lateral para os inputs.
    sidebarPanel(

      # Input: número de classes do histograma.
      sliderInput(inputId = "classes",
                  label = "Número de classes:",
                  min = 1,
                  max = 30,
                  value = 10)

    ),

    # Painel principal para mostrar os outputs.
    mainPanel(

      # Output: Histograma
      plotOutput(outputId = "distPlot")

    )
  )
)


# Define o código necessário para a construção de um histograma.
server <- function(input, output) {

  # Função que gera o histograma e devolve para o user side.
  # Essa função é reativa. Isso significa que o histograma
  # vai mudar sempre que o valor do número de classes mudar.
  output$distPlot <- renderPlot({

    x    <- mtcars$mpg
    bins <- seq(min(x), max(x), length.out = input$classes + 1)

    hist(x, breaks = bins, col = "#75AADB", border = "white",
         xlab = "Milhas por galão",
         main = "Histograma do número de milhas rodadas por galão de combustível.")

  })

}

shinyApp(ui = ui, server = server)

User side

Tudo o que será apresentado ao usuário está guardado no objeto ui, que nada mais é do que um código HTML.

ui <- fluidPage(
  titlePanel("Título"),

  sidebarLayout(
    sidebarPanel("Painel lateral"),
    mainPanel("Painel principal")
  )
)

A função fluidPage() utilizada como exemplo acima é utilizada pelo Shiny para criar um display que automaticamente ajusta as dimensões da janela do navegador do usuário. Os elementos da interface do usuário são então colocados dentro dessa função.


As funções titlePanel() e sidebarLayout() são utilizadas dentro da função fluidPage(). A primeira gera o título “Título”, enquanto a segunda estrutura um layout com barra lateral para o app.

A função sidebarLayout() recebe dois argumentos:

  • sidebarPanel() - estrutura o painel na barra lateral; e
  • mainPanel() - estrutura o painel principal.

O uso da função sidebarLayout() não é obrigatório!

Existem outras formas de organizar os inputs e outputs em um aplicativo!

Podemos utilizar:

  • column: Divide o ambiente em colunas,
  • box: Cria caixas,
  • tabsetPanel: Cria abas,
  • navbarPage: Cria abas similares a de um navegador,
  • inputPanel: Cria um painel,
  • conditionalPanel: Cria um paneil condicional.

Veja alguns exemplos de uso nos aplicativos disponibilidados na página e na internet! Por exemplo, em https://shiny.rstudio.com/gallery/

Vamos observar com mais cuidado.

ui <- fluidPage(

    titlePanel("Meu primeiro shiny app!"),

  sidebarLayout(
    sidebarPanel(
      sliderInput(inputId = "classes",
                  label = "Número de classes:",
                  min = 1,
                  max = 30,
                  value = 10)
    ),

    mainPanel(
      plotOutput(outputId = "distPlot")
    )
  )
)

Repare que a função sliderInput() recebe alguns argumentos.

  • O mais importante é o inputId=. Esse argumento definirá o nome que usaremos para chamar esse input dentro do server.
  • O argumento label= recebe o texto que aparecerá no widget, ajudando o usuário a entender o que o input controla.
  • No caso da sliderInput(), podemos controlar o valor mínimo do slider (min = 1), o valor máximo (max = 30) e o valor padrão (value = 10).

Cada input terá argumentos específicos da própria função. Assim, se você nunca usou um determinado input, procure no help() da função quais são os argumentos que ela recebe.

outputs

No exemplo do histograma, o input do nosso app era o número de classes e o output era o próprio histograma. Veja que no objeto ui temos o seguinte código:

mainPanel(
      plotOutput(outputId = "distPlot")
    )

Isso quer dizer que vamos receber um output do tipo “plot” (gráfico) do servidor e colocá-lo dentro do mainPanel().

Da mesma forma que há uma função para cada tipo de input, há uma função para cada tipo de output:

  • dataTableOutput: para data frames.
  • htmlOutput ou uiOutput(): para código HTML.
  • imageOutput: para imagens.
  • plotOutput: para gráficos.
  • tableOutput: para tabelas.
  • textOutput: para textos.
  • verbatimTextOutput: para textos não-formatados.
  • Existem funções específicas para gráficos interativos de alguns pacotes, por exemplo, plotlyOutput, lefleatOutput, etc.

Assim como as funções de input, funções de output recebem um argumento de identificação, o outputId=. Esse argumento recebe uma string que representa o nome utilizado no server side para se referir a esse output.

Consulte o help() de cada função para saber mais sobre os argumentos adicionais.


Criados os inputs e outputs do app, agora precisamos manipulá-los no server side.

Server side

Com a interface do usuário estruturada, precisamos agora implementar a função server(). Nela, colocaremos as instruções para gerar os outputs que nós vemos no user side a partir dos valores dos inputs que o usuário escolher.

A primeira coisa que precisamos fazer é defini-la. A função server() será sempre uma função que recebe dois argumentos: input e output.

server <- function(input, output) {
  
  # Código
  
}



A partir daí, precisamos seguir três regras:

  1. Todos os outputs estão numa lista chamada output. Assim, como no exemplo do histograma nós chamamos o gráfico de hist, para nos referirmos a ele no server side utilizaremos output$hist.
  2. Os outputs devem ser construídos com funções render_(). Existe uma função render_() para cada tipo de objeto.
  3. Da mesma forma que os outputs, todos os inputs estão numa lista chamada input. Assim, para acessar o valor escolhido para o número de classes no exemplo do histograma, utilizaremos input$classes.
server <- function(input, output) {

  output$distPlot <- renderPlot({

    x    <- mtcars$mpg
    bins <- seq(min(x), max(x), length.out = input$classes + 1)

    hist(x, breaks = bins, col = "#75AADB", border = "white",
         xlab = "Milhas por galão",
         main = "Histograma do número de milhas rodadas por galão de combustível.")

  })

}



Repare nas {} dentro da função renderPlot().

Sempre que você usar um input dentro de uma função render_(), o seu output se tornará reativo ao valor do input. Isso significa que, sempre que o usuário mudar o valor do input, o Shiny atualizará automaticamente o valor dentro da lista e também todas as funções render_() que dependam dele.

library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      numericInput(inputId = "num",
                   label = "Número  de observações",
                   value = 100)
    ),
    mainPanel(plotOutput(outputId = "hist"))
  )
)

server <- function(input, output) {
  
  output$hist <- renderPlot({hist(rnorm(input$num))})
  
}

shinyApp(ui = ui, server = server)

Reatividade

O fluxo de reatividade será sempre conduzido por valores e funções reativas. Os objetos dentro da lista input são os principais objetos reativos e as funções render_() são as principais funções reativas.

Um fluxo básico seria o seguinte:

  1. O usuário altera o valor do input x.
  2. O valor reativo input$x é invalidado.
  3. Toda função reativa que depender de input$x é notificada.
  4. Essas funções verificam qual é o novo valor de input$x e atualizam suas saídas.


O Shiny disponibiliza funções para manipular a reatividade, alterando o fluxo básico apresentado acima.

Em http://material.curso-r.com/shiny/ encontramos:

  • Funções para manipular reatividade;
  • Exemplos de como customizar o aplicativo;
  • Formas de compartilhar (publicar) o aplicativo construído.

Como melhorar um aplicativo?

Como melhorar um aplicativo?

Existem vários pacotes que permitem personalizar o seu aplicativo, permitindo, entre outras coisas, mudar tema e cores, inserir caixas informativas e botões com diferentes estilos. Alguns são:

  • shinythemes
  • shinydashboard
  • shinyWidgets
  • shinyjs


Consulte, por exemplo: