class: center, middle, inverse, title-slide .title[ # Shiny
Visualizacion de datos con R
Slides 2 ] .subtitle[ ## Diplomado en Data Science, MatPUC ] .author[ ### Joshua Kunst Fuentes
jbkunst@gmail.com
] --- # Shiny: Visualizacion de datos con R ## Version 2024 ### Clases * Lunes 4 de noviembre * Miércoles 6 de Noviembre --- # Programa - [Slides 1](https://jkunst.com/shiny-visualizacion-de-datos-con-R/slides-01.html) - Aplicación (web), ejemplos. - Introducción a shiny: Interfaz usuario y servidor - [Slides 2](https://jkunst.com/shiny-visualizacion-de-datos-con-R/slides-02.html) - Layouts - Integración HTMLWidgets - [Slides 3](https://jkunst.com/shiny-visualizacion-de-datos-con-R/slides-03.html) - Temas, templates y diseño - Compartir una app - Shiny en Python - [Slides 4](https://jkunst.com/shiny-visualizacion-de-datos-con-R/slides-04.html) - Expresiones reactivas - Orden de ejecución - Extensiones shiny --- class: center, middle, inverse # Layouts --- # Layouts <br/> Laytous se refiere a la disposición de elementos -como inputs, textos, outputs- en nuestra app. Dependiendo de las necesidades puede ser convenientes algunos tipos de layuts sobre otros. -- <br/> Como mencionamos anteriormente {shiny} utiliza boostrap 3, y seguirá usando esta versión para evitar problemas de compatibilidad con aplicaciones existentes y mantener la estabilidad en diseños desarrollados bajo esa versión. -- <br/> Para usar versiones recientes de boostrap se debe utilizar el paquete {bslib} que viene con otro contenedores (funciones) y nuevas funcionalidades, como _valueBox_es. --- # {shiny} Tipos de Layouts <br/> .center[ <img src="imgs/shiny-layouts.png" width="800px" /> ] --- # {shiny} sidebarLayout <br/>  --- # {shiny} sidebarLayout - Estructura Es una distribución simple donde lo primero en aparecer es los controles o inputs. Luego viene el panel principal el cual posee mayor espacio. .center[ <img src="imgs/sidebar2.png" width="800px" /> ] --- # {shiny} sidebarLayout - Ejemplo código .code70[ ```r library(shiny) ui <- fluidPage( titlePanel("Hello Shiny!"), sidebarLayout( sidebarPanel( sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30) ), mainPanel( plotOutput("distPlot") ) ) ) server <- function(input, output) { output$distPlot <- renderPlot({ x <- faithful[, 2] bins <- seq(min(x), max(x), length.out = input$bins + 1) hist(x, breaks = bins, col = 'skyblue', border = 'white', xlab = 'Waiting time to next eruption (in mins)', main = 'Histogram of waiting times') }) } shinyApp(ui = ui, server = server) ``` ] --- # {shiny} Tipos de Paneles de Navegación Es posible que nuestra aplicación vaya creciendo, en dicho caso, Los paneles nos permiten organizar distintas secciones de la aplicación. .center[ <img src="imgs/navlayouts.png" width="800px" /> ] --- # {shiny} tabsetPanel Los tabs (`tabsetPanel`) son útiles para separar secciones _similares_ en nuestra app. A diferencia de por ejemplo `navbarPage`. .code70[ ``` r library(shiny) ui <- fluidPage( titlePanel("Hello Shiny!"), sidebarLayout( sidebarPanel( sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500) ), mainPanel( tabsetPanel( tabPanel("Plot", plotOutput("plot")), tabPanel("Summary", verbatimTextOutput("summary")), tabPanel("Table", tableOutput("tabla")) ) ) ) ) server <- function(input, output) { output$plot <- renderPlot({ hist(rnorm(input$obs)) }) output$summary <- renderText({ input$obs }) output$tabla <- renderTable({ data.frame(input$obs) }) } shinyApp(ui, server) ``` ] --- # {shiny} tabsetPanel Los tabs (`tabsetPanel`) son útiles para separar secciones _similares_ en nuestra app. A diferencia de por ejemplo `navbarPage`.  --- # Ejercicio: Modificar la estructura de la aplicación <br/> Nivel _fácil, para calentar las manos_: Para el ejemplo de `sidebarLayout`: Modifique la aplicación para que posea un layout de tipo `flowLayout`. <br/> Nivel _entretenido_: Para el ejemplo de `tabsetPanel` agregue: - Un tab más con un gráfico de líneas. - Un tab que utilice la función `summary` para mostra resúmen de los datos. - Modifique la applicación para utilizar la función `navbarPage`. --- # Solución 1 ```r library(shiny) ui <- fluidPage( titlePanel("Hello Shiny!"), flowLayout( sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500), plotOutput("distPlot") ) ) server <- function(input, output) { output$distPlot <- renderPlot({ hist(rnorm(input$obs)) }) } shinyApp(ui, server) ``` --- # Solución 2 .pull-left[ ```r library(shiny) library(ggplot2) ui <- navbarPage( title = "Hello Shiny!", tabPanel("Plot", sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500), plotOutput("plot") ), tabPanel("Línea", plotOutput("linea")), tabPanel("Summary", verbatimTextOutput("summary")), tabPanel("Table", tableOutput("tabla")) ) ``` ] .pull-right[ ```r server <- function(input, output) { output$plot <- renderPlot({ hist(rnorm(input$obs)) }) output$summary <- renderPrint({ summary(rnorm(input$obs)) }) output$tabla <- renderTable({ data.frame(rnorm(input$obs)) }) output$linea <- renderPlot({ x <- rnorm(input$obs) qplot(x = 1:input$obs, x, geom = "line") }) } shinyApp(ui, server) ``` ] --- # {bslib} page_sidebar  --- # {bslib} page_sidebar .pull-left[ ```r library(shiny) library(bslib) ui <- page_sidebar( title = "Hello Shiny!", sidebar = sidebar( sliderInput( "bins", label = "Number of bins:", min = 1, value = 30, max = 50 ) ), plotOutput("distPlot") ) ``` <small>https://shiny.posit.co/r/articles/build/layout-guide/</small> ] .pull-right[  ] --- # {bslib} page_navbar  .code70[ ```r ui <- page_navbar( title = "My App", bg = "#2D89C8", inverse = TRUE, nav_panel(title = "One", p("First page content.")), nav_panel(title = "Two", p("Second page content.")), nav_panel(title = "Three", p("Third page content.")), nav_spacer(), nav_menu( title = "Links", align = "right", nav_item(tags$a("Posit", href = "https://posit.co")), nav_item(tags$a("Shiny", href = "https://shiny.posit.co")) ) ) ``` ] --- class: center, middle, inverse # HTMLWidgets --- # HTMLWidgets HTMLWidgets son un tipo de paquetes que nos permiten realizar visualizaciones en HTML las cuales se pueden usar en (1) consola, (integrar) integrar con shiny y también (3) rmarkdown. Existen una gran cantida de paquetes https://gallery.htmlwidgets.org/, y nos sirven para complementar nuestra aplicación. Cada paquete HTMLWidget tiene su propio set de funciones, el código utilizado para hacer un gráfico en plotly no es el mismo (pero generalmente muy similar) al utilizado en highcharter, echarts4r: - https://plotly.com/r/ - https://jkunst.com/highcharter/ - https://echarts4r.john-coene.com/ - https://rstudio.github.io/leaflet/ - https://rstudio.github.io/DT/ Ejemplo de uso de script https://github.com/jbkunst/shiny-visualizacion-de-datos-con-R/blob/master/R/script-htmlwidgets.R --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r *library(ggplot2) ``` ] .panel2-ggplot2-auto[ ] --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r library(ggplot2) *data(iris) ``` ] .panel2-ggplot2-auto[ ] --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r library(ggplot2) data(iris) *ggplot(iris, aes(Sepal.Length, Sepal.Width)) ``` ] .panel2-ggplot2-auto[ <!-- --> ] --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r library(ggplot2) data(iris) ggplot(iris, aes(Sepal.Length, Sepal.Width)) + * geom_point(aes(color = Species), size = 2.5) ``` ] .panel2-ggplot2-auto[ <!-- --> ] --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r library(ggplot2) data(iris) ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point(aes(color = Species), size = 2.5) + * scale_color_viridis_d(end = .9) ``` ] .panel2-ggplot2-auto[ <!-- --> ] --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r library(ggplot2) data(iris) ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point(aes(color = Species), size = 2.5) + scale_color_viridis_d(end = .9) + * geom_smooth(method = "lm") ``` ] .panel2-ggplot2-auto[ <!-- --> ] --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r library(ggplot2) data(iris) ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point(aes(color = Species), size = 2.5) + scale_color_viridis_d(end = .9) + geom_smooth(method = "lm") + * facet_wrap(vars(Species)) ``` ] .panel2-ggplot2-auto[ <!-- --> ] --- count: false # Antes, un poco de {ggplot2} .panel1-ggplot2-auto[ ``` r library(ggplot2) data(iris) ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point(aes(color = Species), size = 2.5) + scale_color_viridis_d(end = .9) + geom_smooth(method = "lm") + facet_wrap(vars(Species)) + * theme_minimal() ``` ] .panel2-ggplot2-auto[ <!-- --> ] <style> .panel1-ggplot2-auto { color: black; width: 38.6060606060606%; hight: 32%; float: left; padding-left: 1%; font-size: 80% } .panel2-ggplot2-auto { color: black; width: 59.3939393939394%; hight: 32%; float: left; padding-left: 1%; font-size: 80% } .panel3-ggplot2-auto { color: black; width: NA%; hight: 33%; float: left; padding-left: 1%; font-size: 80% } </style> --- # {plotly} .pull-left[ ``` r library(ggplot2) library(plotly) data(iris) p <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point(aes(color = Species), size = 2.5) + scale_color_viridis_d(end = .9) + geom_smooth(method = "lm") + facet_wrap(vars(Species)) + theme_minimal() ggplotly(p) ``` ] .pull-right[
] --- # {highcharter} .pull-left[ ``` r library(highcharter) library(forecast) data("AirPassengers") modelo <- forecast(auto.arima(AirPassengers)) hchart(modelo) |> hc_add_theme(hc_theme_hcrt()) |> hc_navigator(enabled = TRUE) |> hc_rangeSelector(enabled = TRUE) |> hc_title(text = "Proyección") ``` ] .pull-right[
] --- # Unos datos .pull-left[ ``` r library(rvest) # descargar datos de paginas web url <- "https://www.sismologia.cl/sismicidad/catalogo/2024/11/20241103.html" datos <- read_html(url) |> html_table() |> dplyr::nth(2) |> janitor::clean_names() |> tidyr::separate( latitud_longitud, into = c("latitud", "longitud"), sep = " ", convert = TRUE ) datos ``` ] .pull-right[ ``` ## # A tibble: 12 × 6 ## fecha_local_lugar fecha_utc latitud longitud profundidad magnitud_2 ## <chr> <chr> <dbl> <dbl> <chr> <chr> ## 1 2024-11-03 19:35:1352 km a… 2024-11-… -30.0 -71.9 41 km 3.0 Ml ## 2 2024-11-03 18:31:5392 km a… 2024-11-… -18.5 -71.2 37 km 2.5 Ml ## 3 2024-11-03 16:41:1365 km a… 2024-11-… -21.4 -68.9 100 km 3.2 Ml ## 4 2024-11-03 10:47:39133 km … 2024-11-… -28.3 -69.2 117 km 4.3 Ml ## 5 2024-11-03 04:48:0478 km a… 2024-11-… -24.0 -67.1 228 km 3.1 Ml ## 6 2024-11-03 02:39:11102 km … 2024-11-… -22.7 -67.2 226 km 3.2 Ml ## 7 2024-11-03 02:06:3645 km a… 2024-11-… -21.1 -69.0 118 km 2.6 Ml ## 8 2024-11-03 01:16:3823 km a… 2024-11-… -29.3 -71.2 53 km 4.3 Ml ## 9 2024-11-03 01:04:04103 km … 2024-11-… -35.7 -69.7 199 km 5.3 Mw ## 10 2024-11-03 00:12:1536 km a… 2024-11-… -21.3 -67.9 209 km 3.0 Ml ## 11 2024-11-02 22:46:3449 km a… 2024-11-… -28.4 -70.3 99 km 2.5 Ml ## 12 2024-11-02 21:01:3967 km a… 2024-11-… -23.9 -67.4 204 km 3.2 Ml ``` ] --- # {DT} .pull-left[ ``` r library(DT) datatable(datos) ``` ] .pull-right[
] --- # {leaflet} .pull-left[ ``` r library(leaflet) leaflet(datos) |> addTiles() |> addMarkers( lng = ~longitud, lat = ~latitud, popup = ~as.character(magnitud_2), label = ~as.character(`fecha_local_lugar`) ) |> addProviderTiles("Esri.WorldImagery") ``` ] .pull-right[
] --- # Pero como usar HTMLWidgets en nuestra App? <br> Cada uno de los HTMLWidgets presentados, en su documentación detallan como usarlos en una aplicación shiny. De forma general en cada paquete existirá una función tipo: - `*Output` para colocarla en el `ui`. - `render*` para definirla en el `server` A modo de ejemplo, el paquete `leaflet` tiene `leafletOutput()` y `renderLeaflet()`. Los anterior está documentado en https://rstudio.github.io/leaflet/shiny.html. --- # Ejercicio: Agregar un HTMLWidget a la app A la aplicación de ejemplo del sidebar que se muestra a continuación, modificar para que sea un gráfico _interactivo_ usando el plaque {plotly}. .pull-left[ ``` r library(shiny) library(bslib) ui <- page_sidebar( title = "Hello Shiny!", sidebar = sidebar( sliderInput( "bins", label = "Number of bins:", min = 1, value = 30, max = 50 ) ), plotOutput("distPlot") ) ``` ] .pull-right[ ``` r server <- function(input, output) { output$distPlot <- renderPlot({ x <- faithful[, 2] bins <- seq(min(x), max(x), length.out = input$bins + 1) hist( x, breaks = bins, col = 'darkgray', border = 'white', xlab = 'Waiting time to next eruption (in mins)', main = 'Histogram of waiting times' ) }) } shinyApp(ui, server) ``` ] --- # Ejercicio: Agregar un HTMLWidget a la app **Solución** A la aplicación de ejemplo del sidebar que se muestra a continuación, modificar para que sea un gráfico _interactivo_ usando el plaque {plotly}. .pull-left[ ``` r library(shiny) library(bslib) *library(plotly) ui <- page_sidebar( title = "Hello Shiny!", sidebar = sidebar( sliderInput( "bins", label = "Number of bins:", min = 1, value = 30, max = 50 ) ), * plotlyOutput("distPlot") ) ``` ] .pull-right[ ``` r server <- function(input, output) { * output$distPlot <- renderPlotly({ x <- faithful[, 2] bins <- list(start = min(x), end = max(x), size = (max(x) - min(x))/input$bins) plot_ly( x = ~ x, type = "histogram", nbinsx = bins, marker = list(color = 'darkgray', line = list(color = 'white', width = 1) ) ) |> layout( xaxis = list(title = 'Waiting time to next eruption (in mins)'), title = 'Histogram of waiting times' ) } shinyApp(ui, server) ``` ] --- # Ejercicio: Agregar + HTMLWidgets a la app <br/> A la aplicación obtenida de modificar la de `tabsetPanel` (Nivel entretenido), modificar gráficos y tablas para incluir algunos HTMLWidgets. --- # Ejercicio: Agregar + HTMLWidgets a la app **Solución** .pull-left[ ```r library(shiny) library(ggplot2) library(plotly) library(DT) ui <- navbarPage( title = "Hello Shiny!", tabPanel("Plot", sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500), plotOutput("plot") ), tabPanel("Línea", plotlyOutput("linea")), tabPanel("Summary", verbatimTextOutput("summary")), tabPanel("Table", DTOutput("tabla")) ) ``` ] .pull-right[ ```r server <- function(input, output) { output$plot <- renderPlot({ hist(rnorm(input$obs)) }) output$summary <- renderPrint({ summary(rnorm(input$obs)) }) output$tabla <- renderDT({ d <- data.frame(rnorm(input$obs)) datatable(d) }) output$linea <- renderPlotly({ y <- rnorm(input$obs) p <- qplot(x = 1:input$obs, y = y, geom = "line") ggplotly(p) }) } shinyApp(ui, server) ``` ] --- # Ejercicio: Agregar + HTMLWidgets a la app **Solución v2** .pull-left[ ```r library(shiny) library(ggplot2) library(plotly) library(DT) ui <- navbarPage( title = "Hello Shiny!", tabPanel("Plot", sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500), plotlyOutput("plot") ), tabPanel('Linea', plotOutput('linea')), tabPanel("Summary", textOutput("summary")), tabPanel("Table", dataTableOutput("tabla")), tabPanel('Resumen', verbatimTextOutput('resumen')) ) ``` ] .pull-right[ ```r server <- function(input, output) { output$plot <- renderPlotly({ n <- input$obs x <- rnorm(n) p <- qplot(x, geom = "histogram") ggplotly(p) }) output$summary <- renderText({ input$obs }) output$tabla <- renderDataTable({ df <- data.frame(1:input$obs) datatable(df) }) output$resumen <- renderPrint({ summary(rnorm(input$obs)) } ) output$linea <- renderPlot({ plot(rnorm(input$obs), type = 'l') }) } shinyApp(ui, server) ``` ]