Introduction
I only discovered flexdashboard
after delving semi-deeply into shiny
and shinydashboard
.
For the uninitiated, flexdashboard
is an R package that utilizes shiny
to
create dashboards.
(If you have never heard of shiny
you can read up on it here)
I think what makes flexdashboard
different from building a regular shiny
app or shinydashboard
is that it is easier and faster to work with–at least in my experience. So
it is probably a better entry point for building dashboards in R than shinydashboard
.
In this post, I am going to walk you through an example of building your very own flexdashboard
from scratch. We will use the quandl
package to fetch current and historical data on
gold and silver prices, then display the value of a dynamic number of ounces of each inputed
by the user.
Before we get started, you might want to clone the Github repo for this project or view the finished app.
Getting Started
You will need to have R and RStudio installed on your machine. Also, make sure you
have shiny
, flexdashboard
, quandl
, tidyverse
, and scales
installed. And because
we use quandl
, you will need to sign up
for a free account and get an API access token.
The following code installs these packages:
install.packages(c('shiny', 'flexdashboard', 'quandl', 'tidyverse', 'scales'))
Then, whenever you want to create a new flexdashboard
file, just click the
icon in the top left and select the flexdashboard
template.
(You might need to restart RStudio if you just installed flexdashboard
)
YAML
The top of the newly opened .Rmd
file includes information on how to build the
app. You can set the title
to anything you like, of course. The runtime
option is set to shiny
but there are other options, such as
shiny_prerendered
, that I won’t go into. Lastly, the output
is where we tell
the machine we want a flex_dashboard
with rows (instead of columns) and we
want rows to fill the screen (instead of scroll).
As a bonus, you can add a social media link and source code in the menu.
After you’re finished, it should look like this:
---
title: "Gold & Silver Portfolio"
runtime: shiny
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
social: menu
source_code: embed
---
Setup
In the first code chunk we load the required packages, enable bookmarking (more
on that later), and set the Quandl API key. You can name this chunk setup
.
If you plan on sharing your code or tracking it on github
, then you shoudn’t
store the API token in your code. One workaround is to create a new file called
.Renviron
in your project directory and in the first line add
QUANDL_API_KEY=api_key
. Be sure to add .Renviron
to .gitignore
. Then, to
make sure that the environmental variable is available in the app use
readRenviron()
. This is important if you plan to publish to shinyapps.io.
Note also the code that enables bookmarking. This is a cool feature in shiny
that allows users to store the state of the dashboard in the URL which can be
restored at a later time.
library(flexdashboard)
library(shiny)
library(Quandl)
library(scales)
library(tidyverse)
readRenviron(".Renviron")
shiny::enableBookmarking(store = 'url')
Quandl.api_key(Sys.getenv('QUANDL_API_KEY'))
Load Data
Next, we load the data from Quandl. The first two lines query the Quandl servers, and the rest of
the code extracts and reshapes.
Because data only need to be loaded once, this step shouldn’t
be repeated each time a graph or other output is re-rendered. To ensure that this step is run
only once during app start-up, we name this chunk global
. In this tutorial, we focus on
gold and silver, but Quandl has data on stocks and other assests as well.
gold <- Quandl("LBMA/GOLD")
silver <- Quandl("LBMA/SILVER")
gold_current <- gold$`USD (AM)`[1]
silver_current <- silver$USD[1]
portfolio <- merge(
gold,
silver,
by = "Date",
all.x = TRUE,
sort = FALSE
)
Define Inputs
Now for the meat and potatoes!
To make things simple, all inputs are placed in the sidebar.
Each row in the dashboard is defined by a level 2 markdown header (——————) and
each column within a row is defined by the three hashtags at the top (###).
We add a sidebar with .sidebar
and set the width to 20% with
data-width=200
. (The total width is 1000, so 200 is 20%)
Inputs {.sidebar data-width=200}
-----------------------------------------------------------------------
###
numericInput(
inputId = "gold_oz",
label = "Gold (ounces)",
value = 0,
min = 0
)
numericInput(
inputId = "silver_oz",
label = "Silver (ounces)",
value = 0,
min = 0
)
radioButtons(
inputId = "combine",
label = "Combine Values in Plot",
choices = c("Yes" = TRUE, "No" = FALSE),
selected = FALSE
)
hr()
bookmarkButton(class = "btn-link")
There are three inputs here. The first two are used to define how many ounces of silver and gold the user has in his portfolio, and the final input is a yes/no radio button to combine lines in the plot created later.
In the last line, we add a bookmark button that triggers bookmarking. Once clicked, a modal will pop up with a URL that can be used to restore the current state of the dashboard.
Current Portfolio Value
Since we are done with the sidebar, we need to create another row.
Current Value {data-height=100}
-----------------------------------------------------------------------
The goal is to display the current value of gold and silver (and total value)
in the portfolio using the valueBox
function from
flexdashboard
. To ensure that each value box gets its own column, use the header
syntax again (###) above each new code chunk.
renderValueBox({
valueBox(
value = dollar(round(input$gold_oz * gold_current + input$silver_oz * silver_current)),
icon = "fas fa-coins",
caption = "Total Value"
)
})
renderValueBox({
valueBox(
value = dollar(round(input$gold_oz * gold_current)),
icon = "fas fa-coins",
caption = "Gold Value"
)
})
renderValueBox({
valueBox(
value = dollar(round(input$silver_oz * silver_current)),
icon = "fas fa-coins",
caption = "Silver Value"
)
})
The valueBox
function is wrapped inside the renderValueBox
function so that the value box
is properly scaled to the size of the browser window. The value
argument to valueBox
is the
main content displayed. In this case, the value argument is set to
the number of ounces of gold and silver
inputed by the user multiplied by current prices fetched from Quandl.
The dollar
function
comes from the scales
package and adds the dollar sign formatting. Also, the icon
argument
specifies an optional icon to be displayed in the value box. If you don’t like the one I
selected, you can find another one here.
Portfolio Value Over Time
The value boxes display the current value of your precious metals holdings, but what about the historical value?
The final row graphs the total value of gold and silver over time with ggplot
. When the app
first loads, both the silver_oz
and gold_oz
inputs are set to 0 and the plot should be
empty. The first line of code inside renderPlot
ensures nothing is plotted when
both inputs are 0. If either of the inputs are non-zero, a data frame is created with the date
and total value by metal. The data are reshaped long with gather
.
If the user opts to combine the gold and silver line graphs, we need to collapse sum
the value column by date. If the user does not opt to combine (the default), then the total gold
and silver value over time is plotted separately by setting color = source
. You can choose the
colors for gold and silver with scale_color_manual
.
The last bit of code adds a lower limit (no negative values)
and formatting to the y-axis to make it prettier. I’m a fan
of theme_minimal()
and use it for all my plots created with ggplot
. Finally,
the two arguments passed to
theme()
turn off the legend title and increase the text size. Make sure to call
renderPlot({
if (input$silver_oz == 0 & input$gold_oz == 0) return()
portfolio_value <- data.frame(
"gold" = input$gold_oz * portfolio$`USD (AM)`,
"silver" = input$silver_oz * portfolio$USD,
"date" = gold$Date
) %>%
gather(key = "source", value = "value", gold, silver)
if (input$combine) {
portfolio_value <- portfolio_value %>%
group_by(date) %>%
summarise(value = sum(value))
p <- ggplot(portfolio_value, aes(x = date, y = value))
} else {
p <- ggplot(portfolio_value, aes(x = date, y = value, color = source)) +
scale_color_manual(values = c("gold" = "#f5b342", "silver" = "#C0C0C0"))
}
p +
geom_line(size = 1) +
scale_y_continuous(
limits = c(0, NA),
name = "Total Value",
labels = dollar_format()
) +
scale_x_date(name = "Date") +
theme_minimal() +
theme(
legend.title = element_blank(),
text = element_text(size = 16)
)
})
Deploy
You can create a free shinyapps.io account to deploy
the dashboard to the wild wild web. After you create an account, install the rsconnect
package and click the
blue publish button (next to the run button) in RStudio and you’re done! The free account
does have limits that you can read about at the link above.
Conclusion
You should now have a fully functioning app to track your gold and silver portfolio. Enter in your own gold and silver holdings and restore the state of the dashboard at any time by creating a bookmark.
I hope you enjoyed this post! Please let me know what you think in the comments below.