Integrando en una aplicación
Introducción
Ya tenemos todos los elementos que necesitamos, en las secciones Serie de Tiempo, Diagrama de dispersión y Mapa desarrollamos cada elemento de la aplicación, ahora lo que tenemos que hacer es integrar todo en un flujo interactivo.
Como vimos antes, las aplicaciones de Shiny estan compuestas de dos piezas: la interfaz de usuario y el servidor. La interfaz de usuario en nuestro caso es relativamente sencilla, contiene cuatro elementos fundamentales: el selector de delito y las salidas de los tres componentes.
En una aplicación Web normal, la estructura de la interfaz de usuario se define utilizando una combinación de html y css. Shiny nos provee una seri de funciones de R que generan los componentes Web que necesitamos. Por ejemplo, digamos que queremos hacer un radio button utilizando Shiny:
radioButtons("est", "Estación:",
c("Radio Universal" = "ru",
"La pantera" = "590")
)
La función radioButtons
nos regresa el código de html para que en la página aparezcan nuestros botones.
Regresando a nuestra apliacación, generar un selector para los diferentes tipos de delitos de la base de datos es muy fácil:
selectInput("id_delito","Selecciona un delito",
c("Robo a casa habitación",
"Robo a negocio",
"Robo a transeúnte",
"Robo a transportista",
"Robo en transporte público colectivo",
"Robo en transporte público individual",
"Robo de vehículo automotor",
"Extorsión",
"Feminicidio",
"Homicidio doloso",
"Secuestro")
)
Servidor
Lo que nos falta ahora es la lógica del servidor, es decir, las funciones que se van a encargar de tomar el input del usuario y regresar las gráficas. En la Sección Mi primera aplicación vimos cómo hacer funciones que regresen elementos visuales para acomodarlos donde queremos. Ahora vamos a hacer lo mismo pero con nuestras gráficas, por ejemplo, para construir la serie de tiempo y ponerla en la columna que le toque:
$serie <- renderPlot({
outputbase_ts() %>%
filter(subtipo_de_delito==input$id_delito) %>%
ggplot(aes(fecha,Total)) +
geom_line() +
geom_point(data = . %>%
filter(Total==max(Total)),
size=3,col="red"
+
) geom_point(data = . %>%
filter(fecha==max(fecha)),
size=3
+
) theme_classic()
})
Aquí estamos suponiendo que en la columna que queremos tenemos un elemento plotOutput(serie)
. Lo único especial de este código es la llamada a la función base_ts()
en la gráfica original base_ts
son los datos con los que construimos la gráfica, en este caso base_ts()
es una expresión reactiva de Shini que transforma la base de datos en una función que sólo se consulta en el momento en el que se necesita, esto permite desarrollar la interactividad de forma más rápida y eficiente:
<- reactive({
base_ts
base_ts })
Entonces, la función completa del servidor, suponiendo que en las columnas correspondientes tenemos los elementos leafletOutput
(para el mapa) y plotOutput
para las gráficas, se vería así:
<- function(input, output) {
server
#Reactividad de los mapas
<- reactive({
b_mapa %>%
base_mapa filter(fecha==as.Date("2022-08-01"))
})# Reactividad de la serie
<- reactive({
b_ts
base_ts
})# Reactividad del scatter
<- reactive({
b_scatter
base_mapa
})# Mapa leaflet
$mapa <- renderLeaflet({
output<- b_mapa()[b_mapa()$subtipo_de_delito == input$id_delito,]
base <- colorNumeric(palette = "inferno",domain = b_mapa()[b_mapa()$subtipo_de_delito == input$id_delito,]$Total)
pal
leaflet() %>%
addProviderTiles(providers$OpenStreetMap) %>%
addPolygons(data = base$geometry,
fillColor = pal(base$Total),
color = "black",
weight = 1,
opacity = 1,
fillOpacity = .7,
label = base$NOMGEO,
group = "Entidades")%>%
addLegend(pal = pal,values = base$Total) %>%
addLayersControl(overlayGroups = "Entidades")
})# Mapa serie de tiempo
$serie <- renderPlot({
outputb_ts() %>%
filter(subtipo_de_delito == input$id_delito) %>%
ggplot(aes(fecha,Total))+
geom_line()+
geom_point(data = . %>%
filter(Total == max(Total)),
size = 3,col = "red"
+
)geom_point(data = . %>%
filter(fecha == max(fecha)),
size = 3
+
)theme_classic()
})
#Scatter PLot
$scatter <- renderPlot({
output<- b_scatter() %>%
base filter(subtipo_de_delito == input$id_delito)
<- base %>% filter(Total == max(Total))
ejes
%>%
base filter(subtipo_de_delito == input$id_delito) %>%
st_drop_geometry() %>%
pivot_wider(id_cols = c('clave_ent','entidad'),
names_from = 'fecha',
values_from = 'Total') %>%
ggplot(aes(`2022-07-01`,`2022-08-01`)) +
geom_point() +
scale_y_continuous(limits = c(0,ejes$Total)) +
scale_x_continuous(limits = c(0,ejes$Total)) +
geom_abline() +
theme_classic() +
::geom_text_repel(aes(label=entidad))
ggrepel
}) }
Como pueden ver, sólo se trata de empaquetar el flujo con el que hacemos cada una de las gráficas en la función de Shiny correspondiente y asignar el resultado al elemento de output
que queramos. Hacer una aplicación de Shiny es sólo un poco más copmplicado que hacer cada una de las gráficas y, justo esa, es la gran ventaja de Shiny!
Código final
Finalmente, ya sólo necesitamos integrar todo el código en una sola aplicación bajo el modelo de Shiny. Eso implica crear dos funciones: un shinyUI
y un server
, luego ponemos las dos funciones en un archivo único app.R
y llamamos a la función shinyApp(ui = ui, server = server)
para levantar la aplicación. El código completo lo puedes ver a continuación:
Sys.setlocale("LC_ALL", "Spanish")
# Librerías ----
library(shiny)
library(tidyverse)
library(leaflet)
library(sf)
library(ggrepel)
# Bases input ----
# Encoding de latin 1 para que reconozca los asentos
# Base_ ts es el histórico por delito desdel 2015 al 2022
# con el total a nivel nacional de Carpetas O víctimas
<- read_rds("01_input/ts_delitos_prioritarios.rds")
base_ts Encoding(base_ts$subtipo_de_delito) <- "latin1"
# BAse Mapa:
# Incluye los últimos dos meses de información del SESNSP
# con el total a nivel nacional de Carpetas O víctimas por entidad.
# y su geometría del Marco Geoestadístico.
<- read_rds("01_input/mapa_simplificado.rds")
base_mapa <- sf::st_as_sf(base_mapa)
base_mapa Encoding(base_mapa$subtipo_de_delito) <- "latin1"
Encoding(base_mapa$entidad) <- "latin1"
# Ui
<- shinyUI(
ui navbarPage(
title = "CentroGeo,Taller de shiny",
fluid = TRUE,
collapsible = TRUE,
tabPanel("General",#General
fluidRow(
tagList(
div(class = "container",
h1("Información General",class = "title fit-h1"),
p(" La información presentada corresponde a los datos públicados por Secretariado Ejecutivo del Sistema Nacional de Seguridad Pública.
Con información reportada por las Procuradurías Generales de Justicia o Fiscalías Generales de las 32 entidades federativas actualización.
La información está al més de agosto 2022 y fue actualizada el 21 de septiembre del 2022 con datos del SESNSP")))),
column(12,
h4("Tipo de delito"),
selectInput("id_delito",
"Selecciona un delito",
c("Robo a casa habitación",
"Robo a negocio",
"Robo a transeúnte",
"Robo a transportista",
"Robo en transporte público colectivo",
"Robo en transporte público individual",
"Robo de vehículo automotor",
"Extorsión",
"Feminicidio",
"Homicidio doloso",
"Secuestro"))),
br(),
column(7,
leafletOutput("mapa",
width = 700, height = 600)),
column(5,
plotOutput("serie",
height = 300)),
column(5,
plotOutput("scatter",
height = 300))
))
)
# Define server logic required to draw a histogram
<- function(input, output) {
server
#Reactividad de los mapas
<- reactive({
b_mapa %>%
base_mapa filter(fecha==as.Date("2022-08-01"))
})# Reactividad de la serie
<- reactive({
b_ts
base_ts
})# Reactividad del scatter
<- reactive({
b_scatter
base_mapa
})# Mapa leaflet
$mapa <- renderLeaflet({
output<- b_mapa()[b_mapa()$subtipo_de_delito==input$id_delito,]
base <- colorNumeric(palette = "inferno",domain =b_mapa()[b_mapa()$subtipo_de_delito==input$id_delito,]$Total)
pal
leaflet() %>%
addProviderTiles(providers$OpenStreetMap) %>%
addPolygons(data=base$geometry,
fillColor = pal(base$Total),
color="black",
weight = 1,
opacity = 1,
fillOpacity = .7,
label = base$NOMGEO,
group = "Entidades")%>%
addLegend(pal = pal,values = base$Total) %>%
addLayersControl(overlayGroups = "Entidades")
})# Mapa serie de tiempo
$serie <- renderPlot({
outputb_ts() %>%
filter(subtipo_de_delito==input$id_delito) %>%
ggplot(aes(fecha,Total))+
geom_line()+
geom_point(data=. %>%
filter(Total==max(Total)),
size=3,col="red"
+
)geom_point(data=. %>%
filter(fecha==max(fecha)),
size=3
+
)theme_classic()
})
#Scatter PLot
$scatter <- renderPlot({
output<- b_scatter() %>%
base filter(subtipo_de_delito==input$id_delito)
<- base %>% filter(Total==max(Total))
ejes
%>%
base filter(subtipo_de_delito==input$id_delito) %>%
st_drop_geometry() %>%
pivot_wider(id_cols = c('clave_ent','entidad'),
names_from = 'fecha',
values_from = 'Total') %>%
ggplot(aes(`2022-07-01`,`2022-08-01`))+
geom_point()+
scale_y_continuous(limits = c(0,ejes$Total))+
scale_x_continuous(limits = c(0,ejes$Total))+
geom_abline()+
theme_classic()+
::geom_text_repel(aes(label=entidad))
ggrepel
})
}
# Run the application
shinyApp(ui = ui, server = server)
Ahora, pega este código en un archivo nuevo, guárdalo como app.R
(conservando la estructura de archivos de la Introducción) y listo, ya puedes correr la aplicación utilizando el botón Run App de Rstudio.
Para ver la aplicación funcionando en vivo: