13 Shiny na prática II
13.1 Caixas de diálogo
13.2 Tooltips
Tooltips são uma ótima maneira de se comunicar com a pessoa utilizando o seu app. Elas permitem passar todo tipo de informação extra e não ocupam espaço da UI.
Essencialmente, elas são textos que só aparecem quando passamos o ponteiro do mouse em algum elemento da tela. Por exemplo, passe o mouse em cima da frase a seguir:
Existem várias soluções disponíveis para incluirmos tooltips em um aplicativo Shiny, entre elas o pacote tippy
.
Esse pacote está no CRAN, então basta rodar o código abaixo para instalá-lo:
install.packages("tippy")
Para colocar uma tooltip em um elemento do seu app, basta usar a função tippy::with_tippy()
.
::with_tippy(
tippynumericInput(
"tamanho",
label = "Selecione o tamanho da amostra",
value = 1000,
step = 1000
),tooltip = "Amostra de uma distribuição Normal com média 0 e variância 100."
)
Para aplicar a mesma tooltip a vários elementos, utilize a função tippy::tippy_class()
.
fluidRow(
column(
width = 4,
div(
class = "valuebox-tip",
::valueBoxOutput("valor_1", width = 12)
shinydashboard
)
),column(
width = 4,
div(
class = "valuebox-tip",
::valueBoxOutput("valor_2", width = 12)
shinydashboard
)
),column(
width = 4,
div(
class = "valuebox-tip",
::valueBoxOutput("valor_3", width = 12)
shinydashboard
)
),column(
width = 12,
plotOutput("grafico")
),::tippy_class(
tippy"valuebox-tip",
content = "Você precisa importar o css do pacote shinydashboard se quiser usar valueBoxes fora do shinydashboard."
) )
Você pode customizar a tooltip seguindo os parâmetros da documentação oficial da biblioteca tippy.js
.
::tippy_class(
tippy"valuebox-tip",
content = "Você precisa importar o css do pacote shinydashboard se quiser usar valueBoxes fora do shinydashboard.",
arrow = TRUE,
placement = "left"
)
Os fragmentos de código acima pertencem ao app a seguir. Rode o app para ver as tooltips em funcionamento.
library(shiny)
<- fluidPage(
ui ::useShinydashboard(),
shinyWidgetstitlePanel("Usando tooltips"),
sidebarLayout(
sidebarPanel(
::with_tippy(
tippynumericInput(
"tamanho",
label = "Selecione o tamanho da amostra",
value = 1000,
step = 1000
),tooltip = "Amostra de uma distribuição Normal com média 0 e variância 100."
)
),mainPanel(
fluidRow(
column(
width = 4,
div(
class = "valuebox-tip",
::valueBoxOutput("valor_1", width = 12)
shinydashboard
)
),column(
width = 4,
div(
class = "valuebox-tip",
::valueBoxOutput("valor_2", width = 12)
shinydashboard
)
),column(
width = 4,
div(
class = "valuebox-tip",
::valueBoxOutput("valor_3", width = 12)
shinydashboard
)
),column(
width = 12,
plotOutput("grafico")
),::tippy_class(
tippy"valuebox-tip",
content = "Você precisa importar o css do pacote shinydashboard se quiser usar valueBoxes fora do shinydashboard.",
arrow = TRUE,
placement = "left"
)
)
)
)
)
<- function(input, output, session) {
server
<- reactive(rnorm(input$tamanho, sd = 10))
amostra
$valor_1 <- shinydashboard::renderValueBox({
output::valueBox(
shinydashboardvalue = round(mean(amostra()), 1),
subtitle = "Média dos valores",
icon = icon("info-circle")
)
})
$valor_2 <- shinydashboard::renderValueBox({
output::valueBox(
shinydashboardvalue = round(var(amostra()), 1),
subtitle = "Variância dos valores",
icon = icon("info-circle")
)
})
$valor_3 <- shinydashboard::renderValueBox({
output::valueBox(
shinydashboardvalue = round(sd(amostra()), 1),
subtitle = "Desvio-padrão dos valores",
icon = icon("info-circle")
)
})
$grafico <- renderPlot(hist(amostra()))
output
}
shinyApp(ui, server)
13.3 Seletor de visualizações
Nesta seção, vamos mostrar como usar o widget radioGroupButtons
do pacote shinyWidgets
para construir um app com seletor de visualizações, isto é, botões que mudam o tipo de visualização apresentada na tela.
Como exemplo, vamos construir um shiny app que mostra um gráfico de barras, um gráfico de linhas ou uma tabela (a depender da escolha da pessoa que estiver usando) do número de gols do Brasileirão1 por temporada.
O primeiro passo é configurar o widget corretamente. Veja abaixo que o segredo é utilizar os argumentos choiceValues
e choiceNames
. Para o primeiro, passamos os valores que serão acessados dentro do server
a depender da escolha na UI (equivalente ao argumento choices
). Ao segundo, podemos passar tanto textos (que seriam escritos dentro do botão) quanto ícones. Nesse caso, utilizamos ícones da biblioteca Font Awesome, com ajuda da função shiny::icon()
. Repare também que deixamos o tamanho dos botões um pouco maiores com o argumento size = "lg"
.
::radioGroupButtons(
shinyWidgetsinputId = "vis_escolhida",
label = "",
choiceValues = c("barras", "linhas", "tabela"),
choiceNames = list(
icon("bar-chart"),
icon("line-chart"),
icon("table")
),size = "lg",
selected = "barras"
)
Agora, precisamos construir a lógica do Output, tanto na UI como no server. Como a nossa visualização pode gerar gráficos ou uma tabela, precisaremos de funções *Output()
e render*()
diferentes. Dessa forma, vamos utilizar na nossa UI um OutputUI()
.
# A nossa UI ficará assim
<- fluidPage(
ui fluidRow(
column(
width = 12,
h1("App com seletor de visualizações")
)
),br(),
fluidRow(
column(
offset = 1,
width = 11,
::radioGroupButtons(
shinyWidgetsinputId = "vis_escolhida",
label = "",
choiceValues = c("barras", "linhas", "tabela"),
choiceNames = list(
icon("bar-chart"),
icon("line-chart"),
icon("table")
),size = "lg",
selected = "barras"
)
)
),br(),
fluidRow(
column(
width = 12,
uiOutput("vis")
)
) )
Agora, no server, podemos criar funções *Output()
diferentes a depender da visualização escolhida. Usamos plotOutput()
para os gráficos e reactable::reactableOutput()
para a tabela.
$vis <- renderUI({
outputif (input$vis_escolhida %in% c("barras", "linhas")) {
plotOutput("grafico")
else if (input$vis_escolhida == "tabela") {
} ::reactableOutput("tabela")
reactable
} })
Agora vamos construir nossas visualizações. Primeiro, vamos montar a base que precisamos para gerar tanto os gráficos quanto a tabela.
Os dados vêm do pacote brasileirao
. Se você não possui esse pacote instalado, basta rodar o código abaixo:
install.packages("remotes")
::install_github("williamorim/brasileirao") remotes
Como a tabela que gera os gráficos não depende de nenhum valor ou expressão reativa, podemos colocar o código que a gera diretamente no server.
<- brasileirao::matches |>
tab ::filter(
dplyr!= "x",
score %in% 2006:2020
season |>
) ::separate(
tidyr
score,c("gols_casa", "gols_visitante"),
sep = "x",
convert = TRUE
|>
) ::mutate(
dplyrgols = gols_casa + gols_visitante
|>
) ::group_by(season) |>
dplyr::summarise(gols = sum(gols)) dplyr
Para gerar os gráficos, só precisamos de um renderPlot()
e um if/else
para devolver o gráfico certo.
$grafico <- renderPlot({
output
<- tab |>
grafico_base ggplot(aes(x = season, y = gols)) +
labs(x = "Temporada", y = "Número de gols") +
theme_minimal() +
ggtitle("Número de gols do Brasileirão por temporada")
if (input$vis_escolhida == "linhas") {
+
grafico_base geom_line(color = "dark green")
else if (input$vis_escolhida == "barras") {
} +
grafico_base geom_col(width = 0.5, fill = "dark green")
} })
Para gerar a tabela, precisamos apenas de um renderReactable()
.
$tabela <- reactable::renderReactable({
output|>
tab ::reactable(
reactablefullWidth = FALSE,
columns = list(
season = reactable::colDef(
name = "Temporada"
),gols = reactable::colDef(
name = "Número de gols"
)
)
) })
Juntando tudo, temos o app a seguir:
library(shiny)
library(ggplot2)
<- fluidPage(
ui fluidRow(
column(
width = 12,
h1("App com seletor de visualizações")
)
),br(),
fluidRow(
column(
offset = 1,
width = 11,
::radioGroupButtons(
shinyWidgetsinputId = "vis_escolhida",
label = "",
choiceValues = c("barras", "linhas", "tabela"),
choiceNames = list(
icon("bar-chart"),
icon("line-chart"),
icon("table")
),size = "lg",
selected = "barras"
)
)
),br(),
fluidRow(
column(
width = 12,
uiOutput("vis")
)
)
)
<- function(input, output, session) {
server
$vis <- renderUI({
outputif (input$vis_escolhida %in% c("barras", "linhas")) {
plotOutput("grafico")
else if (input$vis_escolhida == "tabela") {
} ::reactableOutput("tabela")
reactable
}
})
<- brasileirao::matches |>
tab ::filter(
dplyr!= "x",
score %in% 2006:2020
season |>
) ::separate(
tidyr
score,c("gols_casa", "gols_visitante"),
sep = "x",
convert = TRUE
|>
) ::mutate(
dplyrgols = gols_casa + gols_visitante
|>
) ::group_by(season) |>
dplyr::summarise(gols = sum(gols))
dplyr
$grafico <- renderPlot({
output
<- tab |>
grafico_base ggplot(aes(x = season, y = gols)) +
labs(x = "Temporada", y = "Número de gols") +
theme_minimal() +
ggtitle("Número de gols do Brasileirão por temporada")
if (input$vis_escolhida == "linhas") {
+
grafico_base geom_line(color = "dark green")
else if (input$vis_escolhida == "barras") {
} +
grafico_base geom_col(width = 0.5, fill = "dark green")
}
})
$tabela <- reactable::renderReactable({
output|>
tab ::reactable(
reactablefullWidth = FALSE,
columns = list(
season = reactable::colDef(
name = "Temporada"
),gols = reactable::colDef(
name = "Número de gols"
)
)
)
})
}
shinyApp(ui, server)
Campeonato Brasileiro de futebol da Série A.↩︎