Skip to contents

Introduction

Package ggspectra extends ggplot2 with stats, geoms, scales and annotations suitable for light- or radiation-related spectra. It also defines ggplot() and autoplot() methods specialized for the classes defined in package photobiology for storing different types of spectral data. The autoplot() methods are described separately in vignette User Guide: 2 Autoplot Methods and the ggplot() methods, statistics, and scales in User Guide: 1 Grammar of Graphics.

The new elements can be freely combined with methods and functions defined in packages ‘ggplot2’, scales, ggrepel, gganimate and other extensions to ‘ggplot2’.

This article, focuses on the animation of plots of time-series of spectra, which can be acquired easily using Ocean Optics spectrometers toether with R package ‘ooacquire’. Such data can also be obtained with radiation transfer models. Simlarly, kinetics of chemical reactions involving light absorbing reactants and/or products as well as any other temporal change in colour and or intensity can be characterised through spectral data. In addition to time, as exemplified below using time-series, other series of measurements in response to changes in other continuous explanatory variables are well suited to animations.

Currently, plots of spectra created with method autoplot() cannot be animated with ‘gganimate’.

Set up

## News at https://www.r4photobiology.info/
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
# if suggested packages are available
gganimate_installed <- requireNamespace("gganimate", quietly = TRUE)
eval_chunks <- gganimate_installed
if (eval_chunks) {
  library(gganimate)
} else {
  message("Please, install package 'gganimate'.")
}

We change the default theme.

A short time series of spectra

We change the labels in the factor so that the time of measurement can be shown.

my_sun.spct <- sun_evening.spct
time <- when_measured(my_sun.spct, as.df = TRUE)
my_sun.spct$spct.idx <- factor(my_sun.spct$spct.idx, 
                               levels = time[["spct.idx"]], 
                               labels = as.character(round_date(time[["when.measured"]], unit = "minute")))
my_sun.spct
## Object: source_spct [7,965 x 3]
## containing 5 spectra in long form
## Wavelength range 290-1000 nm, step 0.34-0.47 nm 
## Label: cosine.hour.9 
## time.01 measured on 2023-06-12 18:38:00.379657 UTC
## time.02 measured on 2023-06-12 18:39:00.797266 UTC
## time.03 measured on 2023-06-12 18:40:00.714554 UTC
## time.04 measured on 2023-06-12 18:41:00.768459 UTC
## time.05 measured on 2023-06-12 18:42:00.769065 UTC 
## Measured at 60.227 N, 24.018 E; Viikki, Helsinki, FI 
## Variables:
##  w.length: Wavelength [nm]
##  s.e.irrad: Spectral energy irradiance [W m-2 nm-1] 
## --
## # A tibble: 7,965 × 3
##    w.length s.e.irrad spct.idx           
##       <dbl>     <dbl> <fct>              
##  1     290          0 2023-06-12 18:38:00
##  2     290.         0 2023-06-12 18:38:00
##  3     291.         0 2023-06-12 18:38:00
##  4     291.         0 2023-06-12 18:38:00
##  5     292.         0 2023-06-12 18:38:00
##  6     292.         0 2023-06-12 18:38:00
##  7     293.         0 2023-06-12 18:38:00
##  8     293.         0 2023-06-12 18:38:00
##  9     294.         0 2023-06-12 18:38:00
## 10     294.         0 2023-06-12 18:38:00
## # ℹ 7,955 more rows

We create a base plot to play with:

ggplot(data = my_sun.spct) +
  aes(linetype = spct.idx) +
  geom_line() +
  scale_x_wl_continuous() +
  scale_y_s.e.irrad_continuous()

We can animate a plot in different ways. Here, using long transitions between spectra. During the transitions interpolated data values are used to create an artificial smooth transition.

ggplot(data = my_sun.spct) +
  geom_line() +
  scale_x_wl_continuous() +
  scale_y_s.e.irrad_continuous() +
  transition_states(spct.idx,
                    transition_length = 2,
                    state_length = 1)

We add an indication of which spectrum is being displayed. Here we define the title and subtitle of the plot to be dynamic, showing information about the currently displayed frame of the animation.

ggplot(data = my_sun.spct) +
  geom_line() +
  scale_x_wl_continuous() +
  scale_y_s.e.irrad_continuous() +
  transition_states(spct.idx,
                    transition_length = 2,
                    state_length = 1) +
  ggtitle('Now showing {closest_state} UTC',
          subtitle = 'Frame {frame} of {nframes}')

It is possible to add some annotations. Here energy irradiances for red and far-red light.

ggplot(data = sun_evening.mspct |> smooth_spct(method = "supsmu", strength = 0.5)) +
  geom_line() +
  stat_wb_e_irrad(aes(colour = after_stat(wb.color)),
                  w.band = list(Red("Sellaro"), Far_red("Sellaro")),
                  ypos.fixed = 0.078) +
  stat_wb_hbar(aes(colour = after_stat(wb.color)),
                  w.band = list(Red("Sellaro"), Far_red("Sellaro")),
                  ypos.fixed = 0.082) +
  scale_x_wl_continuous() +
  scale_y_s.e.irrad_continuous() +
  scale_colour_identity() +
  transition_states(spct.idx,
                    transition_length = 2,
                    state_length = 1,
                    ) +
  ggtitle('Now showing {closest_state} UTC',
          subtitle = 'Frame {frame} of {nframes}')

A longer time series of spectra

my_sun.spct <- sun_hourly_august.spct
my_sun.spct
## Object: source_spct [15,841 x 4]
## containing 31 spectra in long form
## Wavelength range 290-800 nm, step 1 nm 
## Label: Radiation transfer model (LibRadtran) simulation of solar spectrum
## by Anders K. Lindfors 
## spct.01 measured on 2014-08-21 03:30:00 UTC
## spct.02 measured on 2014-08-21 04:30:00 UTC
## spct.03 measured on 2014-08-21 05:30:00 UTC
## spct.04 measured on 2014-08-21 06:30:00 UTC
## spct.05 measured on 2014-08-21 07:30:00 UTC
## spct.06 measured on 2014-08-21 08:30:00 UTC
## spct.07 measured on 2014-08-21 09:30:00 UTC
## spct.08 measured on 2014-08-21 10:30:00 UTC
## spct.09 measured on 2014-08-21 11:30:00 UTC
## spct.10 measured on 2014-08-21 12:30:00 UTC
## spct.11 measured on 2014-08-21 13:30:00 UTC
## spct.12 measured on 2014-08-21 14:30:00 UTC
## spct.13 measured on 2014-08-21 15:30:00 UTC
## spct.14 measured on 2014-08-21 16:30:00 UTC
## spct.15 measured on 2014-08-21 17:30:00 UTC
## spct.16 measured on 2014-08-22 03:30:00 UTC
## spct.17 measured on 2014-08-22 04:30:00 UTC
## spct.18 measured on 2014-08-22 05:30:00 UTC
## spct.19 measured on 2014-08-22 06:30:00 UTC
## spct.20 measured on 2014-08-22 07:30:00 UTC
## spct.21 measured on 2014-08-22 08:30:00 UTC
## spct.22 measured on 2014-08-22 09:30:00 UTC
## spct.23 measured on 2014-08-22 10:30:00 UTC
## spct.24 measured on 2014-08-22 11:30:00 UTC
## spct.25 measured on 2014-08-22 12:30:00 UTC
## spct.26 measured on 2014-08-22 13:30:00 UTC
## spct.27 measured on 2014-08-22 14:30:00 UTC
## spct.28 measured on 2014-08-22 15:30:00 UTC
## spct.29 measured on 2014-08-22 16:30:00 UTC
## spct.30 measured on 2014-08-21 19:22:00 UTC
## spct.31 measured on 2014-08-22 01:22:00 UTC 
## Measured at 60.20388 N, 24.96082 E; Kumpula, Helsinki, Finland 
## Variables:
##  w.length: Wavelength [nm]
##  s.e.irrad: Spectral energy irradiance [W m-2 nm-1] 
## --
## # A tibble: 15,841 × 4
##    w.length s.e.irrad UTC                 spct.idx
##       <dbl>     <dbl> <dttm>              <fct>   
##  1      290  0        2014-08-21 03:30:00 spct.01 
##  2      291  2.85e-11 2014-08-21 03:30:00 spct.01 
##  3      292  6.49e-10 2014-08-21 03:30:00 spct.01 
##  4      293  2.84e- 9 2014-08-21 03:30:00 spct.01 
##  5      294  9.05e- 9 2014-08-21 03:30:00 spct.01 
##  6      295  2.94e- 8 2014-08-21 03:30:00 spct.01 
##  7      296  8.70e- 8 2014-08-21 03:30:00 spct.01 
##  8      297  1.91e- 7 2014-08-21 03:30:00 spct.01 
##  9      298  4.52e- 7 2014-08-21 03:30:00 spct.01 
## 10      299  9.78e- 7 2014-08-21 03:30:00 spct.01 
## # ℹ 15,831 more rows

Object my_sun.spct contains multiple spectra in long form. A variable identifies which rows belong to each spectrum, based on date and time. The data cover most of the daytime of two successive days. We select data for one day.

my_sun.spct <- subset(my_sun.spct, day(UTC) == 21)
idfactor <- getIdFactor(my_sun.spct)
idfactor
## [1] "spct.idx"
class(my_sun.spct[[idfactor]])
## [1] "factor"
unique(my_sun.spct[[idfactor]])
##  [1] spct.01 spct.02 spct.03 spct.04 spct.05 spct.06 spct.07 spct.08 spct.09
## [10] spct.10 spct.11 spct.12 spct.13 spct.14 spct.15 spct.30
## 16 Levels: spct.01 spct.02 spct.03 spct.04 spct.05 spct.06 spct.07 ... spct.30

For building an animation, we need the indexing variable to be a factor.

my_sun.spct[["time"]] <- my_sun.spct[[idfactor]]
my_sun.spct[[idfactor]] <- factor(my_sun.spct[[idfactor]])
length(levels(my_sun.spct[[idfactor]]))
## [1] 16

We can display all 16 spectra in a single static plot.

ggplot(data = my_sun.spct) +
  aes(group = get(idfactor)) +
  geom_line(alpha = 1/3) +
  scale_x_wl_continuous() +
  scale_y_s.e.irrad_continuous()

The same data as above, but displayed as an animated plot.

anim <- ggplot(data = my_sun.spct) +
  geom_line() +
  scale_x_wl_continuous() +
  scale_y_s.e.irrad_continuous() +
  transition_states(get(idfactor),
                    transition_length = 2,
                    state_length = 1) +
  ggtitle('Now showing {closest_state} UTC',
          subtitle = 'Frame {frame} of {nframes}')
animate(anim, duration = 20, fps = 10)

To more easily see the differences in the shape of the spectra we can scale them to an equal total irradiance, i.e., equal area under the curves. To emphasize this, we can use geom_spct() instead of geom_line() that was used in the examples above

anim <- ggplot(data = fscale(my_sun.spct, f = e_irrad, target = 100)) +
  geom_spct() +
  scale_x_wl_continuous() +
  scale_y_s.e.irrad_continuous(name = s.e.irrad_label(scaled = TRUE)) +
  transition_states(get(idfactor),
                    transition_length = 2,
                    state_length = 1) +
  ggtitle('Now showing {closest_state} UTC',
          subtitle = 'Frame {frame} of {nframes}')
animate(anim, duration = 20, fps = 10)