Estos días, como parte de un proyecto personal, he estado profundizando un poco en el mundo de cómo hacer Shiny apps en R. He terminado creando una app para generar gráficos con indicadores socioeconómicos en países europeos, en la que el usuario puede decidir qué países y años quiere ver representados, y para qué indicador, edad y sexo. Si tenéis curiosidad, podéis ver la app aquí. Tiene este aspecto:
A lo largo de este post, voy a intentar enseñaros cómo se hace esto en R. Tenéis todo el código de la app en mi GitHub por si queréis descargarlo para jugar con él o adaptarlo a vuestros proyectos. Antes de poneros a desarrollar vuestras aplicaciones, os recomendaría que hicieseis algún tutorial para entender bien la sintaxis. Este curso de Datacamp no me gustó mucho pero sirve para dar una idea general, y en la propia web de Shiny tenéis un montón de ejemplos con código para replicar. La galería de Show me Shiny es muy muy completa también.
Anatomía básica de una aplicación de Shiny
Para crear una shiny app vamos a tener dos archivos de R: uno con la user interface y otro con el server. Podéis tener todo junto si os gusta más, pero creo que es más ordenado dividirlo en dos partes. Cuando hacéis «new file» en RStudio, una de las opciones que sale es la de crear una shiny app, que os va a dar las dos posibilidades.
Muy resumidamente, la user interface es la parte del código en la que vamos a explicar a R cuáles son los inputs que puede dar el usuario, así como la disposición que va a tener la app final (qué outputs vamos a tener y dónde y cómo van a estar colocados). Por su parte, el server es donde vamos a definir esos outputs y cómo van a ser construidos.
En el caso de la app que vamos a empezar a construir, veréis que en la ui se definirán las opciones de los paneles laterales (qué se puede seleccionar y cómo), así como el hecho de que va a haber tres pestañas y cómo es la distribución entre barra lateral y gráfico en ellas. En el archivo del server es donde relacionaremos los inputs de la app con nuestros datos y donde definiremos qué queremos ver en los gráficos.
But first… datos y librerías
Todos los datos que he usado para la app son indicadores sobre renta y condiciones de vida de Eurostat. Podéis encontrar aquí la base de datos completa. Yo he descargado cinco indicadores representativos usando el paquete eurostat, que os recomiendo muchísimo.
Para la app, los paquetes que vamos a cargar son shiny, tidyverse, rCharts (para los gráficos) y rsconnect (esto es opcional y sólo si queréis publicar vuestra app).
library(shiny) library(tidyverse) library(rCharts) library(rsconnect) load("eurostat.RData")
La user interface
A continuación os dejo el código de la user interface. Tened en cuenta que si queréis tener la app entera en el mismo archivo, deberíais guardar todo esto en un objeto «ui» para después poder pasárselo a la función shinyApp().
El código está comentado, pero como podría resultar algo confuso, algunas notas útiles (por algún motivo que me encantaría conocer el código de R en wordpress sale espantoso, pero os recuerdo que podéis ir a github a verlo bien):
- fluidPage() es la función que vamos a tener siempre para crear nuestra ui. Está creando nuestra página.
- Como quiero tener tres pestañas en mi app, uso tabsetPanel con type = «tabs». Dentro de esto, cada llamada a tabPanel() especifica la disposición dentro de cada pestaña. Veréis que en esta app la disposición de la barra lateral cambia en función de la pestaña, y por eso está definida también dentro de tabPanel. Tenéis muchos ejemplos online si queréis tener una sidebar estática.
- sidebarPanel() genera la barra lateral donde vamos a introducir los inputs. Aquí, cada llamada a selectInput() señala un elemento distinto de los que vamos a escoger. Dentro de esta función, tenemos que especificar siempre el inputId, que es cómo nos referiremos al elemento a lo largo del código, el label, que es el nombre con el que aparecerá en la app, los valores que puede tomar el input bajo choices y el seleccionado por defecto con selected. Se pueden elegir otros elementos como multiple si se quiere poder seleccionar varias opciones de la lista.
- Además de varios selectInput(), veréis que la barra lateral también incluye una llamada a sliderInput(), que permitirá elegir el rango de años que queremos ver representados. La lógica de la función es exactamente la misma.
- Tras definir la barra lateral, toca contar a R qué vamos a enseñar en el cuerpo de la app. Esto es lo que se hace con mainPanel(showOutput(«lines», «highcharts»)), donde mainPanel() nos indica que estamos en el cuerpo de la app y showOutput que vamos a mostrar un output que definiremos luego en el server. «lines» es el nombre con el que vamos a referirnos a este output en el resto de la app, y «highcharts» el tipo de output.
- El resto del código de la ui sigue exactamente la misma lógica para crear el código de la pestaña con los gráficos de barras. Como la última pestaña incluye solo texto con la información de la app, lo definimos directamente aquí.
#Creating the user interface fluidPage( # Application title headerPanel("Plotting Eurostat statistics on income and living conditions"), #Giving a tabset appearance to the app tabsetPanel(type = "tabs", #Each tabPanel call specifies input for contents of tab tabPanel("Line plots", #Tab title sidebarLayout( #To have a personalized sidebar per tab sidebarPanel( #creating the select lists for countries, indicators, sex, age: selectInput(inputId = "geo", label = "Select countries:", choices = levels(eurostat$Country), selected = levels(eurostat$Country)[1], multiple = TRUE), #allowing multiple country selection selectInput(inputId = "ind", label = "Select indicator", choices = levels(eurostat$ind), selected = levels(eurostat$ind)[1]), selectInput(inputId = "age", label = "Age groups:", choices = levels(eurostat$age_groups), selected = "Total"), selectInput(inputId = "sex", label = "Sex:", choices = levels(eurostat$sex), selected = "Total"), #Slider bar to allow custom x axis sliderInput("years", "Year range", min(eurostat$Year), max(eurostat$Year), value = c(1995, 2018), step = 5)), #The main panel of the tab will show the lines plot mainPanel(showOutput("lines", "highcharts")))), #Same process for the next tab: bar plots #(some changes made to the options in the side panel) tabPanel("Bar plots", sidebarLayout( sidebarPanel( selectInput(inputId ="years_b", label = "Year", choices = c(1995:2018), selected = 2017), selectInput(inputId = "ind_b", label = "Indicator", choices = levels(eurostat$ind), selected = levels(eurostat$ind)[1]), selectInput(inputId = "age_b", label = "Age groups", choices = levels(eurostat$age_groups), selected = "Total"), selectInput(inputId = "sex_b", label = "Sex", choices = levels(eurostat$sex), selected = "Total")), mainPanel(showOutput("bars", "highcharts")))), #Panel with information about the app: tabPanel("About", p(HTML("")), p(HTML("This is a Shiny Application built to plot statistics on income and living conditions from Eurostat.")), p(HTML("It allows to either compare countries across time by using line charts, or to take more specific snapshots of a moment in time by comparing the 34 countries available.")), p(HTML("You can browse through different indicators and look at their values while specifying sex ang age groups.")), p(HTML("Passing the mouse over the chart gives the exact values of the indicators by country and year.")), p(HTML("Code for the app is available on <a href='https://github.com/aaumaitre/eurostat'>Github</a>.")), p(HTML("Data comes from Eurostat and has been retrieved using the eurostat package in R")), p(HTML("Plots are generated using RCharts, but you can expect a ggplot version coming soon")) ) ))
El server
¿Recordáis los nombres que estábamos dando a los inputs y outputs? Pues ahora es cuando vamos a usarlos. Dentro del server, vamos a crear una función definiendo qué vamos a mostrar donde hemos indicado que habrá un output, y cómo se relaciona con los inputs. (si veis mal el código, click).
- Empezamos con el output$lines <- renderChart2({ , que indica que vamos a estar trabajando con nuestro output llamado «lines» y que vamos a crear un gráfico (renderChart2 sirve para crear objetos de rCharts).
- Dentro de renderChart2, lo primero que hago es dar nombre a los inputs seleccionados, de modo que ahora tendremos un objeto con cada una de las selecciones hechas en la barra lateral. Con este objeto hecho, creo un dataframe (siempre dentro de la función) que va a filtrar el mío en función de todos estos valores, y que será el finalmente usado por el gráfico (lines_data)
- Una vez tengo los datos, creo el gráfico h1 dándole los valores que quiero.
- El proceso para el gráfico de barras es exactamente el mismo.
function(input, output) { #First, the lines chart output$lines = renderChart2({ #The "Selected" variables will serve to subset out data in function of # the input: they are a way of storing the input selected GEOSelected = input$geo INDSelected = input$ind AGESelected = input$age SEXSelected = input$sex #To link the input selected with our dataframe, we subset our data #frame ("eurostat") with the values selected by the user and create #a data frame lines_data = subset(eurostat, Country == GEOSelected & ind == INDSelected & Year >= input$years[1] & Year <= input$years[2] & age_groups == AGESelected & sex == SEXSelected ) #And with this the plot is created h1 = hPlot(x = "Year", y = "Value", group = "Country", data = lines_data, type = 'line') h1$title(text = INDSelected) #Changes title in function of the indicator return(h1) } ) #Same process for the bar chart: output$bars = renderChart2(({ IBSelected = input$ind_b YBSelected = input$years_b AGEBSelected = input$age_b SEXBSelected = input$sex_b bars_data = subset(eurostat, ind == IBSelected & Year == YBSelected & age_groups == AGEBSelected & sex == SEXBSelected) h2 = hPlot(Value ~ Country, data = bars_data, type = 'column') h2$title(text = IBSelected) return(h2) })) }
¿Y ahora?
¡Pues ya estaría! Si estáis usando RStudio, no tenéis más que darle a ‘Run App’ y veréis cómo se hace la magia. Si queréis publicar vuestra app online, lo podéis hacer directamente desde Shiny – recordad simplemente que tenéis que haber creado un archivo Shiny App y no un script normal para ello, y que vuestro archivo de datos debe estar en la misma carpeta que vuestro código. Esto os ahorrara algún dolor de cabeza.
Y como de costumbre, si queréis saber algo específico, siempre podéis mandarme un tuit o escribirme por aquí.