layout: true <div class="my-footer"><span> douglasrm.azevedo@gmail.com </span></div> --- class: title-page <div> <h2 style="text-align: justify; padding-top:2px">Shiny: Criando aplicativos e dashboards em R</h2> <div style="color:#858585"> <div style="float:right"> <img src="img/logo_r.png" alt="r" height="100"/> <img src="img/logo_shiny.png" alt="shiny" height="100"/> </div> </div> <div> <div style="text-align:center; padding-top:150px; color:#858585"> <img src="img/capa_produto.png" alt="capa" height="200px"/> <h5 style="text-align:center"> 28/04/2020 <br> Belo Horizonte, MG </h5> </div> --- class: slide-page <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> ##
Motivação Como reportar resultados de um modelo/projeto? -- + Slides (power point) -- + Relatórios (pdf, word) -- + Planilhas (excel) -- + Gráficos e tabelas (estáticos/interativos) -- <div style="text-align:center; padding-top:20px"> <img src="img/meeting.gif" alt="meeting" height="220px"/> </div> --- class: slide-page ##
Motivação + E se mostrássemos os resultados de maneira dinâmica? -- + E se disponibilizássemos um aplicativo? -- + E se a solução do problema já estivesse prototipada? -- + E se os clientes puderem interagir/testar os modelos/soluções? -- + E se tudo isso for .enfase[muito simples] e você consiga gerar um app em algumas poucas horas? -- <div style="text-align:center; padding-top:50px"> <img src="img/logo_shiny.png" alt="shiny" height="200"/> </div> --- class: slide-page ##
Motivação + https://geom.shinyapps.io/careerpathfinder-beta + https://vac-lshtm.shinyapps.io/ncov_tracker + https://ladco.shinyapps.io/NetAssessApp/ + https://connect.thinkr.fr/hexmake/ + https://datalab.review.fao.org/tweets-analysis.html# + https://require-r.shinyapps.io/shinyverse/ + https://voronoys.shinyapps.io/voronoys/ + https://voronoys.shinyapps.io/barchartraceR2D3/ + https://shiny.rstudio.com/gallery/ + https://community.rstudio.com/tag/shiny-contest-2020 --- class: slide-page ##
Motivação https://require-r.shinyapps.io/shinyverse/ <div style="text-align:center;"> <img src="img/apps/app_shinyverse.png" alt="shiny" height="330"/> </div> <h4 style="color: #000; text-align: justify;">Mostrando a eficiência de um método MCMC para o problema de detecção de .enfase[ponto de mudança em uma série temporal!].</h4> --- class: slide-page ##
Motivação https://vac-lshtm.shinyapps.io/ncov_tracker <div style="text-align:center;"> <img src="img/apps/app_covid.png" alt="shiny" height="350"/> </div> <h4 style="color: #000; text-align: justify;">Visualize a .enfase[evolução geográfica do COVID-19] (e outros vírus).</h4> --- class: slide-page ##
Motivação https://voronoys.shinyapps.io/voronoys/ <div style="text-align:center;"> <img src="img/apps/app_voronoys.png" alt="shiny" height="350"/> </div> <h4 style="color: #000; text-align: justify;">Visualização dos .enfase[resultados das eleições em nível de setor censitário!]</h4> --- class: slide-page ##
Motivação https://connect.thinkr.fr/hexmake/ <div style="text-align:center;"> <img src="img/apps/app_stickers.png" alt="shiny" height="350"/> </div> <h4 style="color: #000; text-align: justify;">.enfase[Crie seu sticker] online!</h4> --- class: slide-page ##
Motivação https://voronoys.shinyapps.io/barchartraceR2D3/ <div style="text-align:center"> <img src="img/apps/app_barchartrace.png" alt="shiny" height="350"/> </div> <h4 style="color: #000; text-align: justify;">.enfase[Crie seu barchart race!]</h4> --- class: slide-page ##
Motivação https://datalab.review.fao.org/tweets-analysis.html# <div style="text-align:center;"> <img src="img/apps/app_onu.png" alt="shiny" height="330"/> </div> <h4 style="color: #000; text-align: justify;">Meu obrigado ao <a href = "https://www.linkedin.com/in/lgsilvaesilva/">Luís Gustavo</a> por emprestar o material usado como base para esta apresentação!</h4> --- class: slide-page <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> ##
Introdução + R Shiny = .enfase[R + web framework] + Shiny é pacote desenvolvido pela empresa RStudio de forma gratuita que facilita a .enfase[criação aplicações web], tais como análises, e visualizações diretamente do R. + .enfase[Não é necessário] conhecimento em HTML, css, JavaScript. + Entretanto... para uma customização mais avançada, o pacote shiny permite a .enfase[interação com outras linguagens]. <div class="box" style="margin-top: 50px"> <h4>Um Shiny app é uma .enfase[página web (UI)] conectada a um computador/server que esteja rodando uma .enfase[sessão R (Server)]</h4> </div> --- class: slide-page ##
Introdução <div class="box" style="margin-top: 50px"> <h4>Os usuários interagem com a .enfase[interface web (UI)]. A UI envia comandos em R para o .enfase[servidor (server)] que retorna resultados para o usuário novamente na UI.</h4> </div> <div style="text-align:center; margin:50px"> <img src="img/ui_server.png" alt="shiny" height="200"/> </div> --- class: slide-page ##
Introdução .pull-left[ <div class = "box" style = "height: 120px"> <p style="text-align:justify">.enfase[ui]: funções escritas em R para construir a interface em HTML que será exibida para o usuário (wrappers).</span> </div> ```r fluidPage() ``` ] .pull-right[ <div class = "box" style = "height: 120px"> <p style="text-align:justify">.enfase[server]: função que recebe os inputs de ui, trabalha as informações e retorna outputs para a ui.</span> </div> ```r function(input, output) {} ``` ] -- <div id="bottom_shiny"> ```r library(shiny) ui <- fluidPage(h1("Hello World!", style = "text-align: center")) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` <div> --- class: slide-page ##
Introdução Em geral os objetos .enfase[ui] e .enfase[server] são criados em arquivos separados: .enfase[ui.R] e .enfase[server.R]. A maneira mais comum de trabalhar com um aplicativo shiny é criando um diretório da seguinte forma: .enfase[app-name:] + .enfase[global.R]: funções e objetos globais e imutáveis. + .enfase[ui.R]: arquivo com as funções de interface. + .enfase[server.R]: arquivos com as funções de operações da interface. + .enfase[www] + styles.css + img/ + html/ + ... --- class: slide-page ##
Introdução Vamos criar 3 arquivos: .box[ .enfase[ui.R]: ```r fluidPage(h1("Evolução do COVID-19", style = "text-align: center")) ``` .enfase[server.R]: ```r function(input, output) {} ``` .enfase[global.R]: <a href = "R/global.R" target = "_blank">\>\>baixe este arquivo e salve como global.R<<</a> ] --- class: slide-page <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> ##
UI (user interface) <div style="text-align:center; padding-top:20px"> <img src="img/inputs_outputs.png" alt="ui" height="400px"/> </div> --- class: slide-page ##
ui.R: adicionando inputs ```r fluidPage( h1("Evolução do COVID-19", style = "text-align: center"), * selectInput(inputId = "country", * label = "Selecione um país", * choices = countries, * selected = "Brazil", * multiple = FALSE * ) ) ``` + .enfase[inputId]: Nome ao qual iremos obter os valores inputados pelo usuário. + .enfase[label]: Texto a ser exibido. + .enfase[choices]: Possíveis escolhas. .enfase[countries] está em .enfase[global.R]! + .enfase[selected]: Input default. + .enfase[multiple]: Podemos selecionar mais de uma entrada? --- class: slide-page ##
ui.R: botão em HTML ```r <div class="form-group shiny-input-container"> <label class="control-label" for="country"> Selecione um país </label> <div> <select id="country"> <option value="Afghanistan"> Afghanistan </option> ... <option value="Brazil"> Brazil </option> ... <option value="Zimbabwe"> Zimbabwe </option> </select> <script type="application/json" data-for="country" data-nonempty="">{}</script> </div> </div> ``` --- class: slide-page ##
ui.R: tipos de input <div style="text-align:center"> <img src="img/widgets_basic.png" alt="widgets" height="400px"/> </div> [https://shiny.rstudio.com/tutorial/written-tutorial/lesson3/](https://shiny.rstudio.com/tutorial/written-tutorial/lesson3/) --- class: slide-page ##
ui.R: shinyWidgets <div style="text-align:center;"> <img src="img/widgets_shinyWidgets.png" alt="widgets" height="400px"/> </div> [https://dreamrs.github.io/shinyWidgets/index.html](https://dreamrs.github.io/shinyWidgets/index.html) --- class: slide-page ##
ui.R: *Output + Para apresentar um objeto do tipo output, basta adicioná-lo ao .enfase[fluidPage()] com uma função do tipo .enfase[*Output()]. <div style="text-align:center; padding-top:60px"> <img src="img/outputs_example.png" alt="widgets" height="200px"/> </div> --- class: slide-page ##
ui.R: *Output <div align = 'center'>
</div> --- class: slide-page ##
ui.R: adicionando outputs <br> .enfase[**]Não esquecer da vírgula entre os elementos. ```r fluidPage( h1("Evolução do COVID-19", style = "text-align: center"), selectInput(inputId = "country", label = "Selecione um país", choices = countries, selected = "Brazil", multiple = FALSE ), * plotOutput(outputId = "ts_plot") ) ``` + .enfase[outputId]: Nome do output que vamos criar em server.R. --- class: slide-page <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> ##
Server Sempre devemos lembrar de 3 passos: + Atribuir .enfase[output$nome_do_output] para fazer um link entre UI e server. + Utilizar a família de funções .enfase[render*]. Exemplo: renderPlot. + Acessar valores provenientes de UI com .enfase[input$nome_do_input]. <div style="text-align:center; padding-top:10px"> <img src="img/steps_server.png" alt="widgets" height="225px"/> </div> --- class: slide-page ##
Server: render* <div align = 'center'>
</div> --- class: slide-page ##
server.R: outputs <br> Adicionando o output em .enfase[output$ts_plot]. <br> ```r function(input, output) { * output$ts_plot <- renderPlot({ dados_sub <- covid19 %>% filter(country == input$country) plot(x = dados_sub$date, y = dados_sub$count, xlab = "Data", ylab = "Contagem", * main = input$country, type = "l", col = "steelblue", lwd = 2, bty = "l", col.axis = grey(0.5), col.lab = grey(0.5)) * }) } ``` --- class: slide-page <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> ##
Layout <div style="text-align:center;"> <img src="img/layout_basic.png" alt="layout" height="300px"/> </div> <div style="display: flex"> <div> <h4 style="color:#454545; margin: 0 0 10px 0;">Já acabou, Jéssica?</h4> <img src="img/jessica.jpeg" alt="meme" height="100px"/> </div> <h3 style="color:#454545; padding-left: 40px;">Vamos entender um pouco dos <span class="enfase">layouts</span>!</h3> </div> --- class: slide-page ##
Layouts básicos <div style="text-align:center; padding-top: 20px"> <img src="img/layouts_shiny.png" alt="layout" height="400px"/> </div> --- class: slide-page ##
Navegação <div style = "display: flex" id = "teste"> .box[ .enfase[tabsetPanel] ```r ui <- fluidPage( * tabsetPanel( * tabPanel( * title = "título 1", * "Tab 1" * ), tabPanel( title = "título 2", "Tab 2" ) * ) ) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` <div style="text-align:center; padding-top: 20px"> <img src="img/navbar/tabsetPanel.png" alt="layout" height="75px"/> </div> ] .box[ .enfase[navlistPanel] ```r ui <- fluidPage( * navlistPanel( * tabPanel( * title = "título 1", * "Tab 1" * ), tabPanel( title = "título 2", "Tab 2" ) * ) ) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` <div style="text-align:center; padding-top: 20px"> <img src="img/navbar/navlistPanel.png" alt="layout" height="75px"/> </div> ] .box[ .enfase[navbarPage] ```r *ui <- navbarPage( * title = "Página", * tabPanel( * title = "título 1", * "Tab 1" * ), tabPanel( title = "título 2", "Tab 2" ) *) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` <div style="text-align:center; padding-top: 30px"> <img src="img/navbar/navbarPage.png" alt="layout" height="75px"/> </div> ] </div> --- class: slide-page ##
shinydashboard <br> Exemplo utilizando o pacote .enfase[shinydashboard] ```r library(shinydashboard) ui <- dashboardPage( title = "EU.A3", skin = "blue", header = dashboardHeader(), sidebar = dashboardSidebar(), body = dashboardBody() ) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` Exemplos: https://rstudio.github.io/shinydashboard/examples.html --- class: slide-page ##
shinymaterial Exemplo utilizando o pacote .enfase[shinymaterial] ```r library(shinymaterial) ui <- material_page( title = "EU.A3", material_side_nav( material_side_nav_tabs( side_nav_tabs = c("Tab " = "tab"), ), material_side_nav_tab_content( side_nav_tab_id = "tab" ) ) ) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` Exemplos: https://ericrayanderson.github.io/shinymaterial/ --- class: slide-page ##
column() .enfase[**]Parâmetro .enfase[width] é obrigatório. Números inteiros entre 0 e 12. ```r fluidPage( * navbarPage( * title = "Evolução do COVID-19", * selected = "ts", * tabPanel( * value = "ts", * title = "Séries temporais", * column( * width = 2, selectInput( ... # Mesmo conteúdo de antes ) * ), * column( * width = 4, plotOutput(outputId = "ts_plot") * ) * ) * ) ) ``` --- class: slide-page ##
Adicionando css <br> Para personalizar o layout utilizando css <a href = "R/styles.css" target = "_blank">baixe este arquivo e salve em www/styles.css</a>. <br> ```r fluidPage( navbarPage( title = "Evolução do COVID-19", * theme = "styles.css" selected = "ts", tabPanel( ... # Mesmo conteúdo de antes ) ) ) ``` --- class: slide-page ##
Algunas cositas más .enfase[**]Criando um painel ao redor dos elementos na UI. ```r tabPanel( value = "ts", title = "Séries temporais", column( width = 2, * wellPanel( selectInput( ... ) * ) ), column( width = 4, * wellPanel( * h3("Um título bem bacana!"), plotOutput(outputId = "ts_plot") * ) ) ``` --- class: slide-page ##
Layout <h2 style = "color: #454545">Dá pra fazer <span class = "enfase">muito mais...</span></h2> <div style="text-align:center; padding-top: 20px"> <img src="img/html_css.png" alt="html_css" height="300px"/> </div> --- class: slide-page <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> ##
Reatividade + Muitas vezes um processo precisa ser .enfase[compartilhado entre diversos outputs]. + Em alguns casos o output precisa ser refeito apenas quando .enfase[determinados parâmetros são alterados]. + Em diversas aplicações precisamos .enfase[atualizar as opções de input ou salvar outputs] em disco ou banco de dados. .pull-left[ .box[ + .enfase[reactive()]: Reativo a todos os inputs + .enfase[eventReactive()]: Reativo a inputs selecionados ] ] .pull-left[ .box[ + .enfase[observe()]: Observa todos os inputs + .enfase[observeEvent()]: Observa inputs selecionados ] ] <div style = "padding-top:180px; text-align: justify"> .enfase[reactive()] e .enfase[eventReactive] retornam objetos, .enfase[observe()] e .enfase[observeEvent()] não possuem retornos. .enfase[isolate()] pode ser utilizado para ignorar mudanças em um determinado input. </div> --- class: slide-page ##
Reatividade + .enfase[reactive]({}): Reativo a qualquer intervenção do usuário. ```r function(input, output) { * dados_sub <- reactive({ * dados_sub <- covid19 %>% filter(country == input$country) * * return(dados_sub) * }) output$ts_plot <- renderPlot({ * dados_sub <- dados_sub() plot(x = dados_sub$date, y = dados_sub$count, xlab = "Data", ylab = "Contagem", * main = input$country, type = "l", col = "steelblue", lwd = 2, bty = "l", col.axis = grey(0.5), col.lab = grey(0.5)) }) } ``` --- class: slide-page ##
Botões de ação ```r tabPanel( value = "ts", title = "Séries temporais", column( width = 2, wellPanel( selectInput( ... ), * actionButton( * inputId = "refazer", * icon = icon("retweet"), * label = "Refazer o gráfico" * ) ) ), column( width = 4, wellPanel( plotOutput(outputId = "ts_plot") ) ) ``` --- class: slide-page ##
Reatividade + .enfase[eventReactive]({}): Reativo a intervenções especificadas do usuário. ```r function(input, output) { * dados_sub <- eventReactive(input$refazer, { dados_sub <- covid19 %>% filter(country == input$country) return(dados_sub) * }, ignoreNULL = FALSE) output$ts_plot <- renderPlot({ out_reactive <- dados_sub() plot(x = out_reactive$dados_sub$date, y = out_reactive$dados_sub$count, xlab = "Data", ylab = "Contagem", main = input$country, type = "l", col = "steelblue", lwd = 2, bty = "l", col.axis = grey(0.5), col.lab = grey(0.5)) }) } ``` --- class: slide-page ##
isolate + .enfase[isolate](): Isolar/Ignorar reatividade de um determinado input. ```r function(input, output) { dados_sub <- eventReactive(input$refazer, { dados_sub <- covid19 %>% filter(country == input$country) return(dados_sub) }) output$ts_plot <- renderPlot({ out_reactive <- dados_sub() plot(x = out_reactive$dados_sub$date, y = out_reactive$dados_sub$count, xlab = "Data", ylab = "Contagem", * main = isolate(input$country), type = "l", col = "steelblue", lwd = 2, bty = "l", col.axis = grey(0.5), col.lab = grey(0.5)) }) } ``` --- class: slide-page <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> ##
Coisas interessantes... Aqui tem um lista .enfase[gigante] de [extensões para o Shiny](https://github.com/nanxstats/awesome-shiny-extensions). As que eu mais utilizo: + [shinymaterial](https://github.com/ericrayanderson/shinymaterial) + [shinycssloaders](https://github.com/daattali/shinycssloaders) + [shinyBS](https://github.com/ebailey78/shinyBS) + [shinyjs](https://github.com/daattali/shinyjs) + [shinyWidgets](https://github.com/dreamRs/shinyWidgets) + [rintrojs](https://github.com/carlganz/rintrojs) Alguns sites que eu costumo consultar ao criar um shiny: + [Mastering shiny](https://mastering-shiny.org/) + [Shiny gallery](https://shiny.rstudio.com/gallery/) --- class: title-page <div> <h1 style="text-align: justify; padding-top:2px">Obrigado pela atenção!</h1> <div style="color:#858585"> <div style="float:right"> <h3 style="text-align: justify; padding-top:2px; color: #fff">Dúvidas? Perguntas? Sugestões?</h3> </div> </div> <div> <div style="text-align:center; padding-top:250px; color:#858585"> <div style="float:center"> <img src="img/logo_r.png" alt="r" height="100"/> <img src="img/logo_shiny.png" alt="shiny" height="100"/> </div> </div>