Introducción a la visualización mediante dashboards

Máster Universitario en Análisis de Datos Deportivos, Universidad Rey Juan Carlos

Data Science

Unión, no intersección

R for Data Science

 

CRISP-DM

  • Despliegue puede incluir muchas cosas distintas

  • El uso de los modelos

  • La Visualización de resultados

  • MLOps: como DevOps en informática

  • Explicabilidad de modelos

Dashboards

Informes estáticos

  • Cuadro de mando integral: ejemplo clásico en la empresa

  • Informes estadísticos: Normalmente resultados clave con gráficos, tablas e interpretación. Seleccionados por analista

  • Por diseño, pensados para ser impresos en papel, formato pdf

Dashboards estáticos

Más compactos, diseñado para pantalla

Dashboards interactivos

Informe estático con elementos interactivos (widgets)

Limitaciones y alternativas

  • Limitados a lo que el estadístico quiere mostrar

  • No solo lo van a usar personas expertas en estadística

  • Toma de decisiones a distintos niveles

  • Aplicaciones Reactivas guían a las partes interesadas en la obtención e interpretación de resultados

  • Los interfaces bien diseñados para modelos avanzados favorecen la explicabilidad

Aplicaciones reactivas

Reactive programming

The key idea of reactive programming is to specify a graph of dependencies so that when an input changes, all related outputs are automatically updated.

Hadley Wickham, Mastering Shiny



Los outputs, i.e., gráficos, tablas, etc., se generan con R cuando los inputs, i.e., selecciones del usuario, cambian.

Aplicaciones Shiny

  • Código “estándar” de R

  • No hace falta nada más, todo el código HTML, CSS y JS se genera automáticamente

  • Dos “componentes”

  • ui (interfaz de usuario, página web). Contiene inputs y outputs

  • server. Contiene objetos reactivos y funciones generadoras (rendering)

Una aplicación shiny simple

library(shiny)
library(palmerpenguins); library(plotly); library(ggplot2)
ui <- fluidPage(title = "Simple demo app",
                sidebarLayout(
                  sidebarPanel(  
                    varSelectInput("varx", "Explanatory variable", penguins |> select(where(is.numeric))),
                    varSelectInput("vary", "Response variable", penguins |> select(where(is.numeric))),
                    varSelectInput("varcol", "Colour variable", penguins |> select(where(is.factor)))),
                  mainPanel( 
                    plotlyOutput("plot"),
                    verbatimTextOutput("model")))
)
server <- function(input, output, session){
  output$plot <- renderPlotly({
    p <- penguins |> ggplot(aes_string(x = input$varx, y = input$vary, col = input$varcol)) +
      geom_point()
    ggplotly(p)})
  output$model <- renderPrint({
    m <- lm(get(input$vary) ~ get(input$varx) + get(input$varcol), penguins)
    summary(m)})
}
shinyApp(ui = ui, server = server)

🖥️ Live demo 🤞🏻

Añadiendo características interesantes

Dashboards

Configuración

Buenas prácticas de Ing.Soft.

Shiny modules

  • Componentes reutilizables

  • Con su propio espacio de nombres (namespace)

  • Facilitan pruebas y depuración

  • Complejo de entender al principio

👉Modularizing Shiny app code

Casos de éxito y ejemplos en producción

AfCIOT - TiVA indicators

  • Joint project with UNECA, WTO, and OECD

  • International Trade, Environmental and Employment Indicators

  • International and multidisciplinar team

  • Industry and product classification, gap estimation, policy simulation and visualisation

  • Key result: A shiny app for reporting, visualisation and policy simulation

  • Several important challenges for the multinational scope

AfCIOT - TiVA indicators

Acknowledgements:

🖥️ Live demo 🤞🏻

Turismo sostenible

EVASTUR

  • Proyecto con la empresa Dephimática

  • Especializada en estadística pública

  • Capacity building en Ciencia de Datos

  • Sistema de indicadores de turismo sostenible: exploración, visualización, análisis

EVASTUR - Dashboard

🖥️ Live demo 🤞🏻

Indicadores de pobreza energética en Brasil

EPE Brasil

  • Weaving Connections: A Comprehensive Diagnosis of Access to and Use of Electricity and Essential Services by Low-Income Population (MRC- SP 0366)

  • Proyecto de transferencia con MRC Consultants and Advisors (Grupo Veolia)

  • ETL, Modelo de datos, visualización

  • Indicadores de pobreza energética (UCM)

  • Presentado en reunión del G20

OBEPE

Acknowledgements:

🖥️ Live demo 🤞🏻

Deporte y ciberseguridad

Analítica deportiva

Proyectos de investigación: DICYME

  • Dynamic Industrial Cyber Risk Modelling based on Evidence (DICYME) research project (CPP2021-009025)

  • Proyecto de colaboración público-privada con DeNexus TECH SL.

  • Dashboard (no publicado aún)

  • Conexión con MongoDB

  • App relacionada: CRAS

Aplicación shiny CRAS

  • Análisis de riesgos

  • Primer paso: se simulan los eventos en n años

  • Segundo paso: se simula el gasto de cada evento

Resultados

Smart Cities

  • Casos de uso para turismo, sanidad e infraestructuras de la Comunidad de Madrid

  • Aplicaciones coherentes paso a paso para los casos de uso (cargar datos - visualización - modelización - obtención de resultados)

  • Algunos casos con Shiny, otros con Python o Power BI

  • Ejemplo: http://www.citizenlabhub.es/cu/55

Apps relacionadas con control de calidad

Síagro - Animal production SPC

https://www.siagro.es | 🖥️ Live demo 🤞🏻

placido: app para muestreo de aceptación

Slides: emilio.lcano.com/p/seio2023

Simulación de proceso

Aplicación shiny para SPC paso a paso

Caso de uso

  • Gráficos de control de la media, el rango y la desviación típica

  • Análisis de capacidad

  • Paquete qcc

  • Datos de ejemplo del paquete

Se extiende fácilmente para otros datos y paquetes

Configuración inicial

  1. Crear un proyecto en RStudio

  2. Instalar paquetes shiny, bslib, qcc

  3. Crear script vacío

  4. Insertar estructura ui + server (por ejemplo con el snippet)

  5. Ir añadiendo el código de los ejemplos de esta presentación (o copiándolo entero, cada ejemplo es autocontenido)

Estructura básica app shiny

library(shiny)

ui <- fluidPage(
  
)

server <- function(input, output, session) {
  
}

shinyApp(ui, server)

Disposición del dashboard (layout)

  • bslib tiene muchas opciones
library(shiny)
library(bslib)

ui <- page_navbar(
  title = "Control Estadístico de procesos ACME",
  sidebar = sidebar(title = "Selección"),
  nav_panel(title = "Gráfico de Control"),
  nav_panel(title = "Análisis de la capacidad del proceso")
)

server <- function(input, output, session) {
  
}

shinyApp(ui, server)

Añadiendo inputs y outputs al ui

library(shiny)
library(bslib)
library(qcc)

ui <- page_navbar(
  title = "Control Estadístico de procesos ACME",
  sidebar = sidebar(
    title = "Selección",
    selectInput("tipo", "Tipo de gráfico", 
                c("xbar", "R", "S" )),
    numericInput("fase1", "Fase 1 hasta muestra", value = 25),
    sliderInput("limites", "Límites de especificación",
                min = 73.90, max = 74.15,
                value = c(73.95, 74.05), 
                step = 0.01),
    selectInput("datos", "Datos (fake)", 
                data(package = "qcc")$results[,3])
  ),
  nav_panel(title = "Gráfico de Control",
            plotOutput("grafico_control"),
            verbatimTextOutput("salida_control")),
  nav_panel(title = "Análisis de la capacidad del proceso",
            plotOutput("grafico_capacidad"))
)

server <- function(input, output, session) {
  
}

shinyApp(ui, server)

Añadiendo objetos reactivos

library(shiny)
library(bslib)
library(qcc)

ui <- page_navbar(
  title = "Control Estadístico de procesos ACME",
  sidebar = sidebar(
    title = "Selección",
    selectInput("tipo", "Tipo de gráfico", 
                c("xbar", "R", "S" )),
    numericInput("fase1", "Fase 1 hasta muestra", value = 25),
    sliderInput("limites", "Límites de especificación",
                min = 73.90, max = 74.15,
                value = c(73.95, 74.05), 
                step = 0.01),
    selectInput("datos", "Datos", data(package = "qcc")$results[,3])),
  nav_panel(title = "Gráfico de Control",
            plotOutput("grafico_control"),
            verbatimTextOutput("salida_control")),
  nav_panel(title = "Análisis de la capacidad del proceso",
            plotOutput("grafico_capacidad")
  )
)

server <- function(input, output, session) {
  gc <- reactive({
    req(input$tipo, input$fase1)
    data("pistonrings")
    diameter <- qcc.groups(pistonrings$diameter, pistonrings$sample)
    qcc(diameter[1:as.numeric(input$fase1),], 
        type = input$tipo, plot = FALSE)
  }) 
}

shinyApp(ui, server)

Renderizando outputs

library(shiny)
library(bslib)
library(qcc)

ui <- page_navbar(
  title = "Control Estadístico de procesos ACME",
  sidebar = sidebar(
    title = "Selección",
    selectInput("tipo", "Tipo de gráfico", c("xbar", "R", "S" )),
    numericInput("fase1", "Fase 1 hasta muestra", value = 25),
    sliderInput("limites", "Límites de especificación",
                min = 73.90, max = 74.15,
                value = c(73.95, 74.05), 
                step = 0.01),
    selectInput("datos", "Datos", data(package = "qcc")$results[,3])),
  
  nav_panel(title = "Gráfico de Control",
            card(plotOutput("grafico_control"),
                 verbatimTextOutput("salida_control"),
                 fill = FALSE)),
  nav_panel(title = "Análisis de la capacidad del proceso",
            plotOutput("grafico_capacidad"))
)

server <- function(input, output, session) {
  gc <- reactive({
    req(input$fase1)
    data("pistonrings")
    diameter <- qcc.groups(pistonrings$diameter, pistonrings$sample)
    qcc(diameter[1:as.numeric(input$fase1),], 
        type = input$tipo, plot = FALSE)
  }) 
  
  output$grafico_control <- renderPlot({
    plot(gc())
  })
  output$salida_control <- renderText({
    paste(capture.output(summary(gc())), collapse = "\n")
  })
  output$grafico_capacidad <- renderPlot({
    req(input$tipo == "xbar")
    process.capability(gc(), input$limites)
  })
}

shinyApp(ui, server)

Dale color - ver cómo queda un tema

library(shiny)
library(bslib)
library(qcc)

ui <- page_navbar(
  title = "Control Estadístico de procesos ACME",
  sidebar = sidebar(
    title = "Selección",
    selectInput("tipo", "Tipo de gráfico", c("xbar", "R", "S" )),
    numericInput("fase1", "Fase 1 hasta muestra", value = 25),
    sliderInput("limites", "Límites de especificación",
                min = 73.90, max = 74.15,
                value = c(73.95, 74.05), 
                step = 0.01),
    selectInput("datos", "Datos", data(package = "qcc")$results[,3])),
  
  nav_panel(title = "Gráfico de Control",
            card(plotOutput("grafico_control"),
                 verbatimTextOutput("salida_control"),
                 fill = FALSE)),
  nav_panel(title = "Análisis de la capacidad del proceso",
            plotOutput("grafico_capacidad"))
)

server <- function(input, output, session) {
  bs_themer()
  gc <- reactive({
    req(input$fase1)
    data("pistonrings")
    diameter <- qcc.groups(pistonrings$diameter, pistonrings$sample)
    qcc(diameter[1:as.numeric(input$fase1),], 
        type = input$tipo, plot = FALSE)
  }) 
  
  output$grafico_control <- renderPlot({
    plot(gc())
  })
  output$salida_control <- renderText({
    paste(capture.output(summary(gc())), collapse = "\n")
  })
  output$grafico_capacidad <- renderPlot({
    req(input$tipo == "xbar")
    process.capability(gc(), input$limites)
  })
}

shinyApp(ui, server)

Dale color - configura el tema

library(shiny)
library(bslib)
library(qcc)

ui <- page_navbar(
  theme = bs_theme(bootswatch = "minty"),
  title = "Control Estadístico de procesos ACME",
  sidebar = sidebar(
    title = "Selección",
    selectInput("tipo", "Tipo de gráfico", c("xbar", "R", "S" )),
    numericInput("fase1", "Fase 1 hasta muestra", value = 25),
    sliderInput("limites", "Límites de especificación",
                min = 73.90, max = 74.15,
                value = c(73.95, 74.05), 
                step = 0.01),
    selectInput("datos", "Datos", data(package = "qcc")$results[,3])),
  
  nav_panel(title = "Gráfico de Control",
            card(plotOutput("grafico_control"),
                 verbatimTextOutput("salida_control"),
                 fill = FALSE)),
  nav_panel(title = "Análisis de la capacidad del proceso",
            plotOutput("grafico_capacidad"))
)

server <- function(input, output, session) {
  gc <- reactive({
    req(input$fase1)
    data("pistonrings")
    diameter <- qcc.groups(pistonrings$diameter, pistonrings$sample)
    qcc(diameter[1:as.numeric(input$fase1),], 
        type = input$tipo, plot = FALSE)
  }) 
  
  output$grafico_control <- renderPlot({
    plot(gc())
  })
  output$salida_control <- renderText({
    paste(capture.output(summary(gc())), collapse = "\n")
  })
  output$grafico_capacidad <- renderPlot({
    req(input$tipo == "xbar")
    process.capability(gc(), input$limites)
  })
}

shinyApp(ui, server)

Implentación real

  • Lectura de datos del proceso en tiempo real

  • Monitorizar proceso en fase II

  • Añadir análisis exploratorio

  • Añadir modelos

  • Mejorar interfaz

  • Complejidad de la app

Ejercicio

Modifica el código de la aplicación para hacer el análisis en fase 2 tomando el resto de datos como en el ejemplo de la ayuda de la función qcc()

Preguntas?

(emilopezcano?)

http://emilio.lcano.com

emilio.lopez@urjc.es

Slides: emilio.lcano.com/p/udc2025


¡Gracias!

🖥️ Live demo 🤞🏻