library(roboplotr)
#> Error in get(paste0(generic, ".", class), envir = get_method_env()) :
#> object 'type_sum.accel' not found
knitr::opts_chunk$set(message = FALSE, warning = FALSE)
Basics
Plotly is especially challenging to use when the resulting plots are
dynamically sized, as elements external to the plot area, like titles,
captions, legends and logos will not be located where they are expected
to be after a resize. The purpose of the roboplotr package is to reduce
the workload of making dynamic plotly plots, with many layout elements
handled with parameters to the core roboplotr function,
roboplot()
. Some more complicated things are handled with
helper functions. Roboplotr has internal scripts to handle plot
relayouts on a resize, including dynamic title wraps and y-axis font
resizing.
This vignette will show an example how to use roboplotr package to create an html report with several interactive plots in one go with minimum hassle for the plots themselves, and we will also capture all the created plots as separate files in png format. For the purposes of this report we will use set widths for all the plots, but roboplot truly shines when you need your plots to fit different device widths, in which case you would not give set widths to the plots.
Every function parameter we use here (and the ones we don’t) have an example of use in the function documentation.
Setting the global options
First off, we use set_roboplot_options()
to set some
global options to define some visual and other specifications. We create
the directory “plots” in tempdir() to store any files created in.
dir.create(file.path(tempdir(), "plots"))
set_roboplot_options(
artefacts = set_artefacts(
filepath = file.path(tempdir(), "plots"),
auto = T,
artefacts = "png"
),
caption_template = "Source: {text}.",
height = 400,
imgdl_wide = set_imgdl_layout(
width = 650,
height = 400,
suffix = "",
format = "png"
),
imgdl_narrow = set_imgdl_layout(
width = 550,
height = 440,
suffix = "",
format = "png"
),
modebar = c(
"home",
"closest",
"compare",
"zoom",
"img_n",
"img_w",
"data_dl"
),
logo_file = system.file("images", "robonomist.png", package = "roboplotr"),
trace_colors = grDevices::palette.colors(6, "Set2"),
width = 650,
xaxis_ceiling = "guess",
verbose = "Warning"
)
Above we defined the logo used in the plots, defined how captions are constructed (avoiding repetedly writing the same information). Modebar elements were also defined, with further specifications for image downloads for the external png files. We also defined the colors used by all plot traces in the order we want them to be used, as well as the plot dimensions, and the x-axis ceiling handling.
When xaxis_ceiling
is provided, any scatter plots with
date format are given some breathing room to the upper end of the range
by rounding the last date unit to the next unit of the denomination
given, ie. if the last date value is "2022-10-07"
, the
x-axis upper bound will be "2022-11-01"
.
Captions, height, width, xaxis_ceiling, trace colors and artefacts
can be controlled by-plot, too, but giving them here avoids writing
repeatedly. As roboplot()
is written with dynamic plot
widhts in mind, it’s usually unnecessary to define it, but for
demonstration purposes we provided a width, as we want our rendered
plots to be the same width as the corresponding png files we are going
to create.
The specifications for image downloads are defined with
imgdl_wide
and imgdl_narrow
. These parameters
define the desired image dimensions for static image download buttons in
the modebar, and provide a suffix for the created files, and define the
file format. We set these to match our intended programmatically created
.png files.
We also want to automate the artefact file creation. This can be
controlled by-plot with the parameter artefacts
of
roboplot()
, but we’ll use set_artefacts()
here
to set some global options. We set auto
to true (to make
roboplot()
create the files automatically when creating a
plot), and give filepath
where the files will be located.
As all our plots have height and width set, these will be used in the
resulting static plots, too, unless overridden by
artefacts
.
We also set verbose
to “Warning” that quiets most of the
messages roboplotr gives.
Next we call some libraries we need for the data we are going to plot, and create our first plot.
Basic use
library(tidyverse)
library(robonomistClient)
library(lubridate)
d <- data_get("oecd/DSD_EO@DF_EO") |>
filter(
REF_AREA %in% c("Finland", "Euro area (17 countries)", "United States",
"United Kingdom", "Japan"),
FREQ == "Quarterly",
MEASURE == "Gross domestic product, volume, market prices",
time >= "2017-01-01"
) |>
collect() |>
with_groups(
REF_AREA, mutate,
value = 100 * value / mean(value[year(time) == 2019])
) |>
mutate(Country = recode(REF_AREA, "Euro area (17 countries)" = "Euro area"))
attr(d, "frequency") <- "Quarterly"
roboplot(d,
Country,
title = "Real GDP forecasts",
subtitle = "Index, 2019=100",
caption = "OECD"
)
At the minimum, roboplot()
needs some data to be
plotted, a title, and a caption. Subtitle is optional, but most likely
needed - it is the best place to show the y-axis unit. Caption is parsed
from the given string and elements defined in
set_roboplot_options()
.
Data transformation also stripped the data of the frequency
attribute, which roboplot()
uses to parse date formats for
several purposes, so we inserted that back. We could have skipped this,
too, leaving it up to roboplot()
to guess the
frequency.
Caption control
Next we will create another plot, with a caption that does not respect the globally set format.
inflation_eu <-
data_get("eurostat/prc_hicp_manr") |>
filter(
unit == "Annual rate of change",
coicop == "All-items HICP",
geo %in% c("Germany (until 1990 former territory of the FRG)",
"Euro area - 19 countries (from 2015)","Finland","Sweden"),
time >= "2015-01-01"
) |>
mutate(
geo = recode(geo,
"Euro area - 19 countries (from 2015)" = "Euro area",
"Germany (until 1990 former territory of the FRG)" = "Germany"
)
) |>
select(geo, time, value)
inflation_us <-
data_get("fred/CPIAUCSL", units = "pc1") |>
drop_na() |>
filter(time >= "2015-01-01") |>
transmute(geo = "USA", time, value)
d <- bind_rows(inflation_us, inflation_eu)
d |> roboplot(geo, "Consumer price inflation","Annual change of HICP, %",
caption = set_caption(template = "Sources: Eurostat & US BLS."))
The previous examples show how plotting with roboplotr is a
lightweight task. As the source notation is a bit different from the
previous plot and we have predefined caption formatting previously with
set_roboplot_options()
, we must use
set_caption()
to override the prefix instead of just giving
a single string.
Horizontal bar plots and x-axis string width, and omitted legends
Next we make a horizontal plot on producer prices, where we need to be be a bit more verbose with plot axes.
d <-
data_get(
"StatFin/thi/statfin_thi_pxt_118g.px", lang = "en", tidy_time = TRUE) |>
filter(str_detect(
`Products by activity (CPA 2015, MIG)`,
"^(01|02|03|07|08|10|16|17|19|20|21|22|23|24|25|26|27|28|29|30|35|36|42) "),
`Index series` %in% c("Producer price index for manufactured products"),
Information %in% c("Annual change, %")
) |>
filter(time == max(time)) |>
rename(CPA = `Products by activity (CPA 2015, MIG)`) |>
drop_na(value) |>
mutate(CPA = fct_reorder(str_replace(CPA, "[0-9]{2} ", ""), value))
d_title <- str_c("Producer prices in Finland ",format(unique(d$time), "%m/%Y"))
d |> roboplot(
title = d_title,
subtitle = "%, annual change",
caption = "Statistics Finland",
plot_type = "bar",
plot_mode = "horizontal",
plot_axes = set_axes(y = "CPA"),
legend = set_legend(maxwidth = 45)
)
As we wanted to present a horizontal bar plot, we used the parameters
plot_type
and plot_mode
from
roboplot()
to define the plot as such.
Before plotting, we reordered the column that would be y-axis as
factor based on value, as roboplot()
only does this
independently for the column that defines color
.
We also used plot_axes
parameter of
roboplot()
and the function set_axes()
parameter y
to set the column “CPA” from the data to be the
yaxis of the plot. set_axes() can take further parameters (described in
detail in function documentation), but for a horizontal bar plot where
you have a numeric column named "value"
,
set_axes()
can handle the ticktypes etc. for the plot by
just providing y-axis data column name as parameter y
. For
most cases this is enough.
As we didn’t want to color-code the bars, we skipped
providing color
for roboplot()
. As there is
now only a single trace color, roboplot()
omitted the
legend. We could also have provided another column for
color
that only has a single observation, and the result
would have been the same. We could have also included the legend with
setting legend
with set_legend()
position
to "bottom"
(currently the only
option), or forced legend to be omitted by using position
NA
.
As many of the tick labels would have been very long and would have
made the plot area are very narrow, we also provided
maxwidth
with set_legend()
. This would
normally cut legend items shorter, but with character strings in y-axis,
it shortens those too with an ellipsis to a maximum character length we
provided. Full texts can still be seen on hover.
Next we will create another plot with only a single observation and different dimensions, this time a line plot.
data_get("tidy/thi") |>
filter(
Indeksisarja == "Teollisuuden tuottajahintaindeksi",
Tiedot == "Pisteluku (2015=100)",
Toimiala == "Yhteensä"
) |>
mutate(value = 100 * (value / lag(value, 12) - 1),
Indeksisarja = "Producer Price Index for Manufactured Products") |>
drop_na() |>
roboplot(Indeksisarja,
"Producer Price Index for Manufactured Products",
"Annual change, %",
"Statistics Finland",
height = 440,
width = 550
)
We needed to drop values of NA that appeared in data transformation,
as otherwise the plot x-axis would have shown an unnecessary gap on the
left side of the plot. roboplot()
will omit rows from data
where all values are NA, but keeps them otherwise.
We decided to provide a column name for the parameter
color
, but as the plot takes the hovertemplate text from
data, we had to translate the column text manually into English to match
the plot. We could have just not have provided color
just
as well as there is only a single trace.
We also wanted a plot with different dimensions, and we give the the same dimensions that we decided the narrow png images will have, so the output and png files match.
As you can see from the next few examples, all of this makes the workflow for createing dynamic plots very short and simple.
Controlling x-axis format
The next two plots are weekly and daily data, and
roboplot()
tries to guess the correct date format from data
frequency and zoom level. Sometimes you have a specific format in mind,
and we’ll cover these next.
data_get("tidy/ec_oil_bulletin") |>
filter(
Country %in% c("Finland","Sweden", "Denmark"),
Variable == "Euro-super 95",
Taxes == T,
time >= "2018-01-01") |>
mutate(
value = value / 1000,
Country = recode(Country, FI = "Finland", DE = "Germany", SE = "Sweden")) |>
roboplot(
Country,
"Petrol price, 95E10",
"Euros per litre",
"European Commission",
height = 440,
width = 550,
plot_axes = set_axes(xformat = "%Y")
)
d <-
str_c("entsoe/dap_", c("FI","DE_LU","SE3","FR")) |>
map(data_get, start_time = "2020-01-01") |>
bind_rows() |>
arrange(time) |>
group_by(Area) |>
mutate(
ma28 = slider::slide_index_dbl(value, time, mean, .before = days(28))
) |>
group_by(Area, time = floor_date(time, "days") |> as_date()) |>
summarise(value = mean(ma28), .groups = "drop") |>
arrange(Area, time)
d |>
roboplot(Area,"Electricity prices, 28-day moving average","€/MWh","Entso-E")
We wanted to to ensure these x-axes are always displayed in years for
the first two plot and month / year for the plot with daily data. Using
years only for the last We provided xformat
in
plot_axes
. The formatting used is d3
time format. You can control y-axis with yformat
d3
number format, or use the appropriate format if you use something
other than date data for x-axis or numeric data for y-axis. Character
formatting is not currenlty an option.
Control trace order
roboplot()
uses the mean of column value
if
it exists to factorize the column used for color
if it’s
not factorized beforehand, and displays the trace and legend in that
order. If you want to specify another order you have to transform the
data itself as in the examples that follow.
d <- data_get("ec/esi_nace2") |>
filter(
Country %in%
c("European Union (current composition)", "Finland", "Germany", "Sweden"),
Indicator ==
"The Economic sentiment indicator is a composite measure (average = 100)",
time >= "2017-01-01"
) |>
mutate(
Country =
recode(Country, "European Union (current composition)" = "EU") |>
fct_relevel("EU","Finland"),
Indicator = "Economic sentiment indicator"
)
d |> roboplot(
Country,
"Economic sentiment indicator",
"Score, 100 = long-run average",
"European Commission"
)
data_get("ec/esi_nace2") |>
filter(Country %in% c("European Union (current composition)", "Finland"),
Indicator == "Consumer confidence indicator (20%)",
time >= "1996-01-01"
) |>
mutate(
Country = recode(Country, "European Union (current composition)" = "EU") |>
fct_relevel("EU","Finland")
) |>
roboplot(
Country, "Consumer confidence indicator","Score","European Commission"
)
Note that forcats::fct_relevel2()
from forcats behaves
differently than forcats::fct_relevel()
.
d <-
data_get("oecd/DSD_KEI@DF_KEI",
dl_filter = "FIN+CHN+USA+EA19+JPN.M.CCICP....") |>
filter(FREQ == "Monthly")
attr(d, "frequency") <- "Monthly"
d |>
select(REF_AREA, time, value) |>
filter(time >= "2000-01-01") |>
mutate(REF_AREA = recode(REF_AREA,
"United States" = "USA",
"China (People's Republic of)" = "China",
"Euro area (19 countries)" = "Euro area"
)) |>
with_groups(REF_AREA, mutate,
value = 10 * scale(value)[, 1] + 100,
value = 100 * value / mean(value[year(time) == 2019])
) |>
filter(time >= "2018-01-01") |>
mutate(REF_AREA = fct_reorder2(REF_AREA, value, time,.desc = F)) |>
roboplot(
REF_AREA,"Consumer Confidence Indicator","Index, 2019 = 100","OECD"
)
Zeroline control
For the next few plots we want to use a stronger zeroline than what
the default plotly plots have, so we will use the zeroline
parameter. Zeroline can also take a numeric value which locates the
zeroline at the given y-axis location.
data_get("tidy/euribor") |>
filter(str_detect(Korko, "(1|6|12) kk.*360")) |>
mutate(Korko = str_remove(Korko,"\\(.*") |> fct_inorder()) |>
filter(time >= "2014-01-01") |>
roboplot(Korko,"Short-term interest rates, Euribor","%","Bank of Finland",
zeroline = TRUE
)
data_get("tidy/10yield") |>
drop_na() |>
filter(time >= "2012-01-01") |>
select(Maa, time, value) |>
mutate(Maa = recode(Maa, "Suomi" = "Finland", "Saksa" = "Germany")) |>
roboplot(
Maa,
"Long-term government bond rates",
"%, 10-year bond",
caption = set_caption(template = "Sources: Bank of Finland, Deutche Bundesbank & Fed."),
zeroline = 2)
More axis controls
For the next plot we need to plot data other than dates as x-axis values.
data_get("ecb/YC",
dl_filter = "B.U2.EUR.4F.G_N_A.SV_C_YM.",
query_args = list(startPeriod = "2022-01-01"),
labels = FALSE
) |>
filter(`Financial market data type` |> str_starts("SR_")) |>
filter(
time %in% c(max(time), min(time), as.Date(c("2022-02-28","2022-06-01")))
) |>
mutate(
Year = str_match(`Financial market data type`, "([[:digit:]]*)Y")[,2],
Month = str_match(`Financial market data type`, "([[:digit:]]*)M")[, 2]
) |>
mutate(across(c(Year, Month), as.integer)) |>
replace_na(list(Year = 0, Month = 0)) |>
mutate(Maturity = Year + Month / 12) |>
select(Maturity, time, value) |>
mutate(Time = as.character(time)) |>
arrange(Maturity, value) |>
roboplot(
Time,
"Yield curves for AAA euro area government bonds",
"% interest rate vs. maturity",
caption = set_caption(
template = "The curves show the yield curve at various points in time.<br>Source: ECB."
),
plot_axes = set_axes(x = "Maturity", xticktype = "numeric"),
height = 440,
width = 550
)
We used plot_axes
in a similar way we used it with the
previous horizontal bar plot, but this time we defined the data column
used for x-axis, and provided the type roboplot()
will use
for tick formatting. Note that to ensure the traces are drawn correctly,
you have to order the data yourself before using it it in
roboplot()
.
For this plot we also wanted to show the numeric y-axis with logarithmic scale.
# China housing investment, from National bureau of statistics of China
# Total Value of Exports, Current Period(1,000 US dollars)
# https://data.stats.gov.cn/english/easyquery.htm?cn=A01
d <- tribble(
~time, ~value,
"Oct 2021", 300221418,
"Nov 2021", 325525287,
"Dec 2021", 340498780,
"Jan 2022", 327285953,
"Feb 2022", 217416504,
"Mar 2022", 276084613,
"Apr 2022", 273619664,
"May 2022", 308244886,
"Jun 2022", 331264197,
"Jul 2022", 332964256,
"Aug 2022", 314920505,
"Sep 2022", 322755333,
"Oct 2022", 298371747
) |> mutate(value = value / 1000000,
)
d |>
roboplot(
title = "Value of monthly exports from China",
subtitle = "Billion US dollars",
caption = "National Bureau of Statistics, China",
plot_axes = set_axes(xticktype = "character")
)
Finally, for the last plot we had a time
column which
was not in date format and we did not want to transform it into such, so
we defined the ticktype for x-axis as "character"
.
Now we only have to check if the png files are in the directory we wanted them to be in.
list.files(str_c(tempdir(),"/plots")) |>
str_subset("png$") |>
str_c(collapse = "<br>") |>
# roboplotr:::roboplotr_combine_words() |>
htmltools::HTML()
consumer_price_inflation.png
economic_sentiment_indicator.png
electricity_prices_28_day_moving_average.png
fao_monthly_real_food_price_index.png
long_term_government_bond_rates.png
petrol_price_95e10.png
producer_price_index_for_manufactured_products.png
producer_prices_in_finland_112024.png
real_gdp_forecasts.png
short_term_interest_rates_euribor.png
value_of_monthly_exports_from_china.png
yield_curves_for_aaa_euro_area_government_bonds.png
Everything seems to be in order! Now you have an html-document with all the plots needed for your report, and .png files of every plot stored, ready to be used in another document. You can do a lot more fine-tuning with roboplotr, and every function of the package has its own documentation that should give you a head start.