Shiny interactivo y dinámico

En un artículo del corpus anterior desarrollamos una aplicación sencilla Shiny para mostrar una gráfica de precios de una lista de acciones del Mercado Americano, utilizando el paquete quantmod.

Vamos a partir de la aplicación anterior y vamos a realizar las actividades siguientes:

  • Permitir al usuario especificar las fechas del período de tiempo que se quiere dibujar en el gráfico.
  • Permitir que se pueda escoger el tema para dibujar el gráfico.
  • El usuario decide si quiere gráfico de velas japonesas o barras.
  • Realizar cambio en los gráficos sin cargar los datos cada vez que hay un cambio en las opciones. Sólo cargar los datos cuando cambia el instrumento.

Recapitulemos

El código del programa completo queda como sigue:

library(shiny)
library(quantmod)

ui<-fluidPage(
  titlePanel("Mercados con R"),
  sidebarLayout(
    sidebarPanel("Seleccione la acción que desea consultar",
                 selectInput('accion',
                             label = 'Acción',
                             choices = c("Apple"="AAPL", "Cisco"="CSCO",
                                         "IBM"="IBM", "Facebook"="FB",
                                         "Twitter"="TWTR", "Microsoft"="MSFT",
                                         "Google"="GOOG"))),
    mainPanel("Gráfico de Acciones del Mercado de Valores Americano",
              h1('Gráficos de Precios'),
              p('A continuación se muestra la gráfica del precio de la acción seleccionada.'),
              plotOutput('grafico'))
  )
)

server<-function(input, output) {
  output$grafico <- renderPlot({
    stockdata <- getSymbols(input$accion, src="google", from = "2017-01-01",
                            to = "2017-08-18", auto.assign = FALSE)
    candleChart(stockdata, name=input$accion)
  })
}

shinyApp(ui=ui, server=server)

Ahora tenemos un Aplicación Web en Shiny/R que muestra una lista de acciones del Mercado Americano y al seleccionar alguna de ellas, la aplicación muestra un gráfico de precios de dicha acción utilizando el paquete quantmod.

Ahora vamos a agregar las funcionalidades descritas previamente:

Permitir al usuario especificar las fechas del período de tiempo que se quiere dibujar en el gráfico

En nuestro código original podemos ver que la forma de especificar las fechas es mediante las opciones from y to de la función getSymbols(). Primero debemos agregar el Input que requerimos y en este caso es dateInput(). Esta función requiere un inputId que identifica el valor que ingresa el usuario o el valor especificado en la opción value que en nuestro caso es "2017-01-01". Se especifica la etiqueta que va a aparecer sobre la entrada, especificamos el idioma con la opción language y el ancho que va a tomar en relación al espacio disponible.

dateInput(inputId="fechadesde", label="Desde la fecha",
          language= "es", width = "40%", value = "2017-01-01")

Vamos a agregar dos dateInput para especificar el rango de fechas que se desean los datos:

dateInput(inputId="fechadesde", label="Desde la fecha",
         language= "es", width = "40%", value = "2017-01-01"),
dateInput(inputId="fechahasta", label="Hasta la fecha",
         language= "es", width = "40%", value = "2017-08-18"),

Este código debe ir en donde especificamos la interfaz de la aplicación y más específicamente en la barra lateral, es decir, dentro de sidebarPanel(). Tenga cuidado con las comas y paréntesis para evitar errores.

Ahora del lado del servidor sólo debemos cambiar las fechas por las variables de entrada input$fechadesde y input$fechahasta como sigue:

stockdata <- getSymbols(input$accion, src="google", from = input$fechadesde,
                        to = input$fechahasta, auto.assign = FALSE)

Ya tenemos una aplicación que toma el rango de fechas y dibuja el gráfico correspondiente.

Permitir que se pueda escoger el tema para dibujar el gráfico

Resulta que quantmod permite crear y especificar temas, es decir, colores para los diferentes componentes del gráfico. Todo esto se hace mediante la función chartTheme(theme = "black", ...). En particular vamos a utilizar dos que existen black y white. Esta opción la vamos a proveer mediante un selectInput()

selectInput('tema', label = 'Tema', choices = c("Negro"="black", "Blanco"="white"))

Del lado del servidor vamos a especificar la variable de entrada input$tema, pero también vamos a cambiar la función que genera el gráfico a chartSeries() ya que esta nos permite agregar más opciones de la forma siguiente:

chartSeries(stockdata, name = input$accion, theme = input$tema)

El usuario decide si quiere gráfico de velas japonesas o barras

En la función chartSeries() ya tenemos la opción para especificar el tipo de gráfico y contamos, además del gráfico de velas y barras, con el gráfico de línea.

Del lado de la interfaz agregamos un selectInput() con las opciones candlesticks, bars y line de la misma manera que lo hemos hecho previamente:

selectInput('tipo', label = 'Tipo de gráfico',
           choices = c("Velas"="candlesticks","Barras"="bars", "Línea"="line"))

Del lado del servidor especificamos la variable de entrada en la opción type de la función chartSeries()

chartSeries(stockdata, name = input$accion, type = input$tipo, theme = input$tema)

Hasta ahora nuestro código completo luce de la forma siguiente:

library(shiny)
library(quantmod)

ui<-fluidPage(
  titlePanel("Mercados con R"),
  sidebarLayout(
    sidebarPanel("Seleccione la acción que desea consultar",
                 selectInput('accion',
                             label = 'Acción',
                             choices = c("Apple"="AAPL", "Cisco"="CSCO",
                                         "IBM"="IBM", "Facebook"="FB",
                                         "Twitter"="TWTR", "Microsoft"="MSFT",
                                         "Google"="GOOG")),
                 dateInput(inputId="fechadesde", label="Desde la fecha",
                           language= "es", width = "40%", value = "2017-01-01"),
                 dateInput(inputId="fechahasta", label="Hasta la fecha",
                           language= "es", width = "40%", value = "2017-08-18"),
                 selectInput('tema', label = 'Tema', choices = c("Negro"="black", "Blanco"="white")),
                 selectInput('tipo', label = 'Tipo de gráfico',
                             choices = c("Velas"="candlesticks","Barras"="bars", "Línea"="line"))
                 ),
    mainPanel("Gráfico de Acciones del Mercado de Valores Americano",
              h1('Gráficos de Precios'),
              p('A continuación se muestra la gráfica del precio de la acción seleccionada.'),
              plotOutput('grafico'))
  )
)

server<-function(input, output) {  
  output$grafico <- renderPlot({
    stockdata <- getSymbols(input$accion, src="google", from = input$fechadesde,
                            to = input$fechahasta, auto.assign = FALSE)
    chartSeries(stockdata, name = input$accion, type = input$tipo, theme = input$tema)
  })
}

shinyApp(ui=ui, server=server)

El último cambio requiere un poco de conocimiento sobre cómo funciona una aplicación Shiny.

Sólo cargar los datos cuando cambia el instrumento

En este caso el objetivo es separar el cambio de los datos del cambio de los parámetros. Es decir, sólo cargar los datos cuando se cambia alguna variable relacionada con los datos y no volverlos a cargar cuando se cambia una variable relacionada con cómo se vé el gráfico.

Nota sobre la eficiencia de la ejecución

Dependiendo de dónde se especifica una función o código esta se ejecuta al cargar la aplicación, cada vez que la aplicación corre o cada vez que se cambia alguna entrada. Esto se puede ver de la manera siguiente:

# Código que corre cuando se carga la aplicación en el servidor
# Carga de librerías y de datos
library(ggplot2)

shinyServer(function(input, output) {
  # Código que corre cada vez que algún usuario se conecta a la aplicación
  # Aquí se cargan datos específicos por conexión
  output$grafico <- renderPlot({
    # Código que corre cada vez que cambia 
    # alguna entrada que sirve como parámetro al dibujo del gráfico
  })
})

Es importante colocar el código en la zona apropiada para evitar impactos en el desempeño de la aplicación.

Por ejemplo si cada vez que cambia cualquier entrada se cargan los datos, entonces estamos generando una carga innecesaria. Si los datos toman tiempo en cargar, el desempeño de la aplicación es malo.

Comportamiento interactivo y reactivo

El Shiny es posible crear expresiones reactivas, es decir, segmentos de código que se aislan y sólo se ejecutan si cambia alguna entrada de la cual depende. Por ejemplo, en nuestro caso la función que busca los datos es getSymbols() y esta depende de la acción y del rango de fechas:

stockdata <- getSymbols(input$accion, src="google", from = input$fechadesde,
                        to = input$fechahasta, auto.assign = FALSE)

Resulta que esta carga se está ejecutando cada vez que cambia cualquiera de las variables, incluyendo aquellas que no tienen que ver con la carga de los datos.

Para este tipo de situación se puede aislar este código en una expresión reactiva de la forma siguiente:

stockdata <- reactive({
  getSymbols(input$accion, src="google", from = input$fechadesde,
             to = input$fechahasta, auto.assign = FALSE)
})

La forma de usar los datos ahora es mediante la llamada a stockdata(). La expresión reactiva se invoca como una función. El código del servidor queda como sigue:

shinyServer(function(input, output) {
  stockdata <- reactive({
    stockdata <- getSymbols(input$accion, src="google", from = input$fechadesde,
                            to = input$fechahasta, auto.assign = FALSE)
  })
  output$grafico <- renderPlot({
    chartSeries(stockdata(), name = input$accion, type = input$tipo, theme = input$tema)
  })
})

De esta forma el código para cargar los datos se ejecuta sólamente cuando cambia la acción o el rango de fechas, no cuando cambia el tipo de gráfico o el tema.


El código completo queda como sigue:

library(shiny)
library(quantmod)

ui<-fluidPage(
  titlePanel("Mercados con R"),
  sidebarLayout(
    sidebarPanel("Seleccione la acción que desea consultar",
                 selectInput('accion',
                             label = 'Acción',
                             choices = c("Apple"="AAPL", "Cisco"="CSCO",
                                         "IBM"="IBM", "Facebook"="FB",
                                         "Twitter"="TWTR", "Microsoft"="MSFT",
                                         "Google"="GOOG")),
                 dateInput(inputId="fechadesde", label="Desde la fecha",
                           language= "es", width = "40%", value = "2017-01-01"),
                 dateInput(inputId="fechahasta", label="Hasta la fecha",
                           language= "es", width = "40%", value = "2017-08-18"),
                 selectInput('tema', label = 'Tema', choices = c("Negro"="black", "Blanco"="white")),
                 selectInput('tipo', label = 'Tipo de gráfico',
                             choices = c("Velas"="candlesticks","Barras"="bars", "Línea"="line"))
    ),
    mainPanel("Gráfico de Acciones del Mercado de Valores Americano",
              h1('Gráficos de Precios'),
              p('A continuación se muestra la gráfica del precio de la acción seleccionada.'),
              plotOutput('grafico'))
  )
)

server<-function(input, output) {
  stockdata <- reactive({
    getSymbols(input$accion, src="google", from = input$fechadesde,
               to = input$fechahasta, auto.assign = FALSE)
  })
  output$grafico <- renderPlot({
    chartSeries(stockdata(), name = input$accion, type = input$tipo, theme = input$tema)
  })
}

shinyApp(ui=ui, server=server)

La aplicación se ve como sigue:

Resultado de la aplicación Shiny


También puedes correr el código directamente desde la cónsola de R o RStudio utilizando el comando runGist().

runGist("https://gist.github.com/aaramirez/05d3493b6d1f9b536ecd8ce37f4dd114")

Desde Github puedes crear fragmentos de código que puedes compartir como Gist, hemos hecho uno para que puedas correr la aplicación.

Copyright © 2014-2018 Synergy Vision. Los artículos del Corpus se comparten bajo los términos de la licencia Creative Commons con Reconocimiento, Propósito no comercial, Compartir contenido similar, 4.0 Internacional (CC BY-NC-SA 4.0).

Citar el artículo.