Shiny es el paquete de R que permite crear aplicaciones web interactivas escribiendo solo R, sin tocar JavaScript. Un analista que sabe hacer gráficos y modelos en R puede convertir ese análisis en una app con controles deslizantes, selectores, tablas actualizables y gráficos reactivos sin aprender frontend. Eso es lo que lo hace tan popular en entornos científicos y empresariales donde el equipo de datos no tiene perfil web.
Shiny 1.8, publicado en 2024, introdujo mejoras en la gestión de sesiones y en el sistema de módulos. La estructura básica de una aplicación Shiny no ha cambiado desde los inicios: hay una parte de interfaz (ui) y una parte de lógica (server).
Estructura básica de una app Shiny
library(shiny)
# UI: define lo que ve el usuario
ui <- fluidPage(
titlePanel("Explorador de datos de iris"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "especie",
label = "Selecciona especie:",
choices = c("Todas", unique(iris$Species)),
selected = "Todas"
),
selectInput(
inputId = "variable_x",
label = "Eje X:",
choices = names(iris)[1:4],
selected = "Sepal.Length"
),
selectInput(
inputId = "variable_y",
label = "Eje Y:",
choices = names(iris)[1:4],
selected = "Petal.Length"
),
sliderInput(
inputId = "tamanyo_punto",
label = "Tamaño de punto:",
min = 1, max = 5, value = 2, step = 0.5
)
),
mainPanel(
plotOutput("grafico"),
tableOutput("resumen")
)
)
)
# Server: define la lógica reactiva
server <- function(input, output, session) {
# Datos filtrados reactivos
datos_filtrados <- reactive({
if (input$especie == "Todas") {
iris
} else {
iris[iris$Species == input$especie, ]
}
})
# Gráfico reactivo
output$grafico <- renderPlot({
df <- datos_filtrados()
plot(
df[[input$variable_x]],
df[[input$variable_y]],
col = as.integer(df$Species) + 1,
pch = 16,
cex = input$tamanyo_punto,
xlab = input$variable_x,
ylab = input$variable_y,
main = paste("Iris:", input$variable_x, "vs", input$variable_y)
)
legend("topright", legend = levels(df$Species),
col = 2:4, pch = 16)
})
# Tabla reactiva
output$resumen <- renderTable({
df <- datos_filtrados()
aggregate(. ~ Species, data = df[, 1:5], FUN = mean)
})
}
# Lanzar la app
shinyApp(ui = ui, server = server)
El modelo reactivo de Shiny
Lo que hace especial a Shiny es su sistema de reactividad. Cuando el usuario mueve un control, Shiny sabe qué cálculos dependen de ese control y los recalcula solo a ellos. No hay que gestionar callbacks manualmente ni conectar eventos: el grafo de dependencias se construye automáticamente.
server <- function(input, output, session) {
# reactive(): un valor que se recalcula cuando cambian sus dependencias
datos <- reactive({
df <- read.csv(input$archivo$datapath)
df[df$valor > input$umbral, ]
})
# reactiveVal(): una variable mutable que dispara reactividad al cambiar
contador <- reactiveVal(0)
observeEvent(input$boton_click, {
contador(contador() + 1)
})
# observe(): corre un bloque sin producir output cuando cambian sus inputs
observe({
cat("El umbral cambió a:", input$umbral, "n")
})
# eventReactive(): como reactive() pero solo se dispara con un evento concreto
resultado_modelo <- eventReactive(input$boton_ajustar, {
lm(y ~ x1 + x2, data = datos())
})
output$coeficientes <- renderPrint({
summary(resultado_modelo())
})
}
Módulos: reutilización en apps grandes
Cuando una app crece, los módulos permiten encapsular partes de la UI y el server como unidades reutilizables. Un módulo es simplemente un par de funciones con un namespace aislado:
# Definir un módulo de gráfico
grafico_UI <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("variable"), "Variable:", choices = names(iris)[1:4]),
plotOutput(ns("hist"))
)
}
grafico_server <- function(id, datos) {
moduleServer(id, function(input, output, session) {
output$hist <- renderPlot({
hist(datos()[[input$variable]], main = input$variable, col = "steelblue")
})
})
}
# Usar el módulo en la app principal
ui <- fluidPage(
grafico_UI("modulo1"),
grafico_UI("modulo2")
)
server <- function(input, output, session) {
datos_reactivos <- reactive(iris)
grafico_server("modulo1", datos_reactivos)
grafico_server("modulo2", datos_reactivos)
}
Despliegue: shinyapps.io y Shiny Server
Posit ofrece shinyapps.io para desplegar apps en la nube con un solo comando desde RStudio:
library(rsconnect) deployApp()
Para servidores propios existe Shiny Server (versión open source gratuita) y Posit Connect (versión enterprise). La versión open source permite alojar cualquier número de apps sin límites de tiempo de sesión, aunque sin las características de autenticación y escalado de la versión de pago.
Una alternativa más ligera que Shiny para dashboards estáticos o semestáticos es Quarto con Observable JS. Para análisis de datos complejos que alimentan la app, vale la pena ver el artículo sobre tidyverse con dplyr y ggplot2. Si te preguntas cómo combinar R con Python en el mismo flujo de trabajo, el artículo sobre reticulate para usar Python desde R cubre esa integración.
Imagen: Pexels / weCare Media
