photobiology 0.9.13
User Guide: 2 Radiation

Pedro J. Aphalo

2016-12-18

Spectra

Package ‘photobiology’ defines a family of classes based on the tibble class, compatible with R’s data frames. The present package by imposing some restrictions on the naming of the member vectors, allows methods to find the data when passed one of these objects as argument. In addition, as the data are checked when the object is built or modified, there is no need to test for their validity each time a calculation is carried out. Other advantage of using spectrum objects, is that specialized versions of generic functions like print and operators like + are defined for spectra. ___spct objects are data.frame objects, as a result of how classes have been derived. In this package we define a generic or base spectrum class, derived from data.frame from which specialized types of spectra are in turn derived. This parenthood hierarchy means that spectra objects can be used almost anywhere where a data.frame is expected. Specializations of many methods including extraction (indexing) methods and partial assignment methods are defined to ensure that the expectations on the variables contained in objects of these classes is guaranteed in most situations. Other specializations of methods and functions are related to achieving a convenient and concise syntax tailored for spectral data as in the case of mathematical operators and functions.

Another important aspect is that spectral data as stored in objects of these classes is always of known physical quantities expressed using known units. Furthermore, attributes are used to keep track of both metadata related to the origin of the data and of later transformations that affect their interpretation, such as normalization or re-scaling. Although sanity tests are applied at the time of object creation, to a large extent the responsibility of ensuring that the numbers provided as argument to object constructors comply with expectations remains with the users of the packages.

In addition to the classes for storing individual spectra, classes for storing collections of spectra are defined. These classes are derived from class list and can contain member spectra of different lengths and measured at different wavelength values.

We give in this vignette brief descriptions and examples of the use of different classes, methods, functions and operators. We start with the simplest and most frequently used methods.

Getting started

We load two packages, our ‘photobiology’ and ‘lubridate’, as they will be used in the examples.

library(photobiology)
library(lubridate)

Classes

The package defines several classes intended to be used to store different types of spectral data. They are all derived from generic_spct, which in turn is derived from data.frame and internally created using tibble::tibble. Table 1 lists them. Attributes are used in objects of these classes to keep metadata such as information about units of expression.


Table 1. Classes for spectral data. In addition to the required variables listed in the table, additional arbitrary variables are partly supported—some operations will not include them in returned values to avoid ambiguity and other possible conflicts. In addition to the attributes listed, all spectral objects have attributes multiple.wl, normalized, scaled, when.measured, where.measured, what.measured plus the normal attributes of tibble (and data.frame) objects including comment.

Class name Required variables Attributes
generic_spct w.length
raw_spct w.length, counts instr.desc, instr.settings, linearized
cps_spct w.length, cps instr.desc, instr.settings, linearized
source_spct w.length, s.e.irrad time.unit, bswf
w.length, s.q.irrad time.unit, bswf
filter_spct w.length, Tfr Tfr.type
w.length, A Tfr.type
reflector_spct w.length, Rfr Rfr.type
object_spct w.length, Tfr, Rfr Tfr.type, Rfr.type
response_spct w.length, s.e.response time.unit
w.length, s.q.response time.unit
chroma_spct w.length, x, y, z

The design imposes that data from different observations are never present as different data columns, if present, additional data columns represent different properties from the same observation event. In other words, the storage format is tidy as defined by Hadley Wickham. In most cases, one spectral object should correspond to one spectral observation, but some functions are compatible or can be used to create spectral objects where the spectral data from different observations are stored “longitudinally” and “tagged” with a factor with a level for each observation event. These observations must use consistent units of expression and attribute values. This long format is useful, for example, when producing plots with package ‘ggplot2’.

Data assumptions

An assumption of the package is that wavelengths are always expressed in nanometres (\(1~\mathrm{nm} = 1 \cdot 10^{-9}\,\mathrm{m}\)). If the data to be analysed uses different units for wavelengths, e.g. Ångstrom (\(1~\textrm{Å} = 1 \cdot 10^{-10}\,\mathrm{m}\)), the values need to be re-scaled before creating objects of the spectral classes. The same applies to all spectral quantities, as there is an expectation in every case, of using base SI units for expression. Table 2 lists the units of expression for the different variables and the metadata attributes that may determine variations in the expression of the quantities.


Table 2. Variables used for spectral data and their units of expression. A: as stored in objects of the spectral classes, B: also recognized by the set family of functions for spectra and automatically converted. time.unit accepts in addition to the character strings listed in the table, objects of classes lubridate::duration and period, in addition numeric values are interpreted as seconds. exposure.time accepts these same values, but not the character strings.

Variables Unit of expression Attribute value
A: stored
w.length nm
counts \(n\)
cps \(n\,s^{-1}\)
s.e.irrad \(W\,m^{-2}\,nm^{-1}\) time.unit = “second”
s.e.irrad \(J\,m^{-2}\,d^{-1}\,nm^{-1}\) time.unit = “day”
s.e.irrad varies time.unit = duration
s.q.irrad \(mol\,m^{-2}\,s^{-1}\,nm^{-1}\) time.unit = “second”
s.q.irrad \(mol\,m^{-2}\,d^{-1}\,nm^{-1}\) time.unit = “day”
s.q.irrad \(mol\,m^{-2}\,nm^{-1}\) time.unit = “exposure”
s.q.irrad varies time.unit = duration
Tfr [0,1] Tfr.type = “total”
Tfr [0,1] Tfr.type = “internal”
A a.u. Tfr.type = “internal”
Rfr [0,1] Rfr.type = “total”
Rfr [0,1] Rfr.type = “specular”
s.e.response \(\mathit{x}\,J^{-1}\,s^{-1}\,nm^{-1}\) time.unit = “second”
s.e.response \(\mathit{x}\,mol^{-1}\,d^{-1}\,nm^{-1}\) time.unit = “day”
s.e.response \(\mathit{x}\,J^{-1}\,nm^{-1}\) time.unit = “exposure”
s.e.response varies time.unit = duration
s.q.response \(\mathit{x}\,mol^{-1}\,s^{-1}\,nm^{-1}\) time.unit = “second”
s.q.response \(\mathit{x}\,mol^{-1}\,d^{-1}\,nm^{-1}\) time.unit = “day”
s.q.response \(\mathit{x}\,mol^{-1}\,nm^{-1}\) time.unit = “exposure”
s.q.response varies time.unit = duration
x, y, z [0,1]
B: converted
wl \(\to\) w.length nm
wavelength \(\to\) w.length nm
Tpc \(\to\) Tfr [0,100] Tfr.type = “total”
Tpc \(\to\) Tfr [0,100] Tfr.type = “internal”
Rpc \(\to\) Rfr [0,100] Rfr.type = “total”
Rpc \(\to\) Rfr [0,100] Rfr.type = “specular”
counts.per.second \(\to\) cps

Energy irradiance is assumed to be expressed in \(W\,m^{-2}\) and photon irradiances in , that is to say using second as unit for time. This is the default, but it is possible to set the unit for time to day in the case of source_spct objects.

The default time unit used is second, but day and exposure can be used by supplying the arguments "day" or "exposure"—The meaning of "exposure" is the total exposure time, in other words, fluence instead of irradiance—to a parameter of the constructor of source_spct objects. In addition to these character constants objects of class lubridate:duration are also accepted.

The attributes are normally set when a spectral object is created, either using default values or with values supplied as arguments to the constructor. However, methods for querying and setting most of these attributes are also available.


Not respecting the expectations about data inputs or setting erroneous values in the metadata attributes will yield completely wrong results if calculations are attempted! It is extremely important to make sure that the wavelengths are in nanometres as this is what all functions expect. If wavelength values are in the wrong units, the action-spectra weights and quantum to/from energy units conversions will be wrongly calculated, and the values returned by most functions wrong, without warning. Errors in some cases will be triggered at the time of object creation as the data input to constructors is tested to be within the expected range of values, which in the case of some quantities frequently allows detection of mistakes in the use unit scaling factors._

If spectral irradiance data is in \(W\,m^{-2}\,nm^{-1}\), and the wavelength in nm, as is the case for many Macam spectroradiometers, the data can be used directly and functions in the package will return irradiances in \(W\,m^{-2}\).

If, for example, the spectral irradiance data output by a spectroradiometer is expressed in \(mW\,m^{-2}\,nm^{-1}\), and the wavelengths are in Ångstrom then to obtain correct results when using any of the packages in the suite, we need to rescale the data when creating a new object.

# not run
my.spct <- source_spct(w.length = wavelength/10, s.e.irrad = irrad/1000)

In the example above, we take advantage of the behaviour of the S language: an operation between a scalar and vector, is equivalent to applying this operation to each element of the vector. Consequently, in the code above, each value from the vector of wavelengths is divided by 10, and each value in the vector of spectral irradiances is divided by 1000.

Querying the class

Before giving examples of how to construct objects to store spectral data we show how to query the class of an object, and how to query the class of a spectrum. Consistently with R, the package provides is functions for querying the type of spectra objects. The only unusual function provided as another name for is.generic_spct is is.any_spct().

is.any_spct(sun.spct)
## [1] TRUE
is.generic_spct(sun.spct)
## [1] TRUE
is.source_spct(sun.spct)
## [1] TRUE

In addition function class_spc() returns directly the spectrum-related class attributes. It filters out from the output of class() the underlying classes inherited.

class_spct(sun.spct)
## [1] "source_spct"  "generic_spct"
class(sun.spct)
## [1] "source_spct"  "generic_spct" "tbl_df"       "tbl"         
## [5] "data.frame"

Construction

There are basically two different approaches to the creation of spectra by users, a constructor similar to data.frame constructor that takes vectors as arguments, and a constructor that converts list objects into spectral objects, which works similarly to as.data.frame from base R. In contrast to the data frame constructors spectral constructor require the variables or the vector arguments to be suitably named so that they can be recognized. As data frames and spectral objects are also lists, they are acceptable arguments.

Here we briefly describe the as constructor functions for spectra. In the first example we create an object to store spectral irradiance data for a ficticious light source, by first creating a data frame, and creating the spectral object as a copy of it. In the example below we supply a single value, 1, for the spectral irradiance. This value gets recycled as is normal in R, but of course in real use it is more usual to supply a vector of the same length as the w.length vector.

my.df <- data.frame(w.length = 400:410, s.e.irrad = 1)
my.spct <- as.source_spct(my.df)
class(my.spct)
## [1] "source_spct"  "generic_spct" "tbl_df"       "tbl"         
## [5] "data.frame"
class(my.df)
## [1] "data.frame"
my.spct
## Object: source_spct [11 x 2]
## Wavelength range 400 to 410 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 11 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      400         1
## 2      401         1
## 3      402         1
## 4      403         1
## # ... with 7 more rows

We can make a `generic_spct’ copy of any spectrum object.

my.g.spct <- as.generic_spct(my.spct)
class(my.g.spct)
## [1] "generic_spct" "tbl_df"       "tbl"          "data.frame"

When constructing spectral objects from numeric vectors the names of the arguments are meaningful and convey information on the nature of the spectral data and basis of expression.

source_spct(w.length = 300:305, s.e.irrad = 1)
## Object: source_spct [6 x 2]
## Wavelength range 300 to 305 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 6 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      300         1
## 2      301         1
## 3      302         1
## 4      303         1
## 5      304         1
## 6      305         1
z <- 300:305
y <- 2
source_spct(w.length = z, s.e.irrad = y)
## Object: source_spct [6 x 2]
## Wavelength range 300 to 305 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 6 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      300         2
## 2      301         2
## 3      302         2
## 4      303         2
## 5      304         2
## 6      305         2
w.length <- 300:305
s.e.irrad <- 1
source_spct(w.length, s.e.irrad)
## Object: source_spct [6 x 2]
## Wavelength range 300 to 305 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 6 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      300         1
## 2      301         1
## 3      302         1
## 4      303         1
## 5      304         1
## 6      305         1

The different constructors have additional arguments to be used in setting non-default values for the attributes. These arguments have the same name as the attributes. Here we used the data frame created in the first chunk of the section.

my.d.spct <- as.source_spct(my.df, time.unit = "day")

Argument strict.range can be used to override or make more strict the validation of the data values.

source_spct(w.length = 300:305, s.e.irrad = -1)
## Warning in range_check(x, strict.range = strict.range): Negative spectral
## energy irradiance values; minimun s.e.irrad = -1
## Object: source_spct [6 x 2]
## Wavelength range 300 to 305 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 6 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      300        -1
## 2      301        -1
## 3      302        -1
## 4      303        -1
## 5      304        -1
## 6      305        -1
source_spct(w.length = 300:305, s.e.irrad = -1, strict.range = NULL)
## Object: source_spct [6 x 2]
## Wavelength range 300 to 305 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 6 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      300        -1
## 2      301        -1
## 3      302        -1
## 4      303        -1
## 5      304        -1
## 6      305        -1

Finally argument comment can be used to add a comment to the data at the time of construction.

my.cm.spct <- source_spct(w.length = 300:305, s.e.irrad = 1,
                          comment = "This is a comment")
comment(my.cm.spct)
## [1] "This is a comment"

Special attributes

Spectral objects have several attributes used to store metadata, such as the time unit used or type of spectral quantity. Some attributes are meaningful for all the classes of spectra defined in the package. These are time of measurement using attribute "when.measured", place of measurement using attribute "where.measured", a user supplied label using attribute "what-measured", and free-text comments. One can set and get comments stored in spectra by means of base R’s comment() and comment() <- functions. Some of the functions in this package append additional information to comments or merge comments.

Functions setWhenMeasured() and getWhenMeasured() are used for setting or getting a date as a POSIXct value. This format is compatible with many functions from package lubridate.

my.spct <- sun.spct
setWhenMeasured(my.spct, NULL)
getWhenMeasured(my.spct)
## [1] NA
setWhenMeasured(my.spct, ymd_hms("2015-10-31 22:55:00", tz = "EET"))
getWhenMeasured(my.spct)
## [1] "2015-10-31 20:55:00 UTC"

Functions setWhereMeasured() and getWhereMeasured() are used for setting or getting a geocode as a data.frame value. This format is compatible with function geocode() from package ggmap. It is also possible, to simply pass latitude and longitude coordinates, as shown below. The returned value is always a data frame with columns "lon" and "lat".

setWhereMeasured(my.spct, NULL)
getWhereMeasured(my.spct)
##   lon lat
## 1  NA  NA
setWhereMeasured(my.spct, lat = 60, lon = -10)
getWhereMeasured(my.spct)
##   lon lat
## 1 -10  60
getWhereMeasured(my.spct)$lon
## [1] -10
my.spct
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2015-10-31 20:55:00 UTC
## Measured at 60 N, -10 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows

Functions setWhatMeasured() and getWhatMeasured() are used for setting or getting a text value.

setWhatMeasured(my.spct, "something")
getWhatMeasured(my.spct)
## [1] "something"
my.spct
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Label: something
## Measured on 2015-10-31 20:55:00 UTC
## Measured at 60 N, -10 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows

Similar functions exist for other attributes which are not shared by all spectral classes. Spectral objects may have several other attributes used to store metadata, such as the time unit used. There functions available for querying and setting the state if these attributes. is_ functions return a logical value, and get functions return the values of the attributes themselves. In addition set functions can be used to set the value attributes, but many of the set functions are very rarely needed in user code.

The attributes described below are set automatically, and consequently function setBSWFUsed() and other set functions for these attributes are mainly of use to programmers extending the package. One exception is when a wrong value has been assigned by mistake and needs to be overwritten.

For example function is_effective() returns TRUE if the spectral data has been weighted with a BSWF. The corresponding getBSWFUsed() function can be used, in this case to retrieve the name of the BSWF that was used when generating the data. Here we demonstrate with one example, where we use two different waveband objects—constructed on-the-fly with constructor function—, defining a range of wavelengths.

is_effective(sun.spct)
## [1] FALSE
is_effective(sun.spct * waveband(c(400, 700)))
## [1] FALSE

Sometimes it may be desired to change the time unit used for expressing spectral irradiance or spectral response, and this can be achieved with the conversion function convertTimeUnit. This function both converts spectral data to the new unit of expression and sets the time.unit attribute, preserving the validity of the data object.

ten.minutes.spct <-
  convertTimeUnit(sun.spct, time.unit = duration(10, "minutes"))
ten.minutes.spct
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 600s (~10 minutes)
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
getTimeUnit(ten.minutes.spct)
## [1] "600s (~10 minutes)"

Spectral objects created with earlier (pre-release) versions of this package are missing some attributes. For this reason summary and plot functions may not work as expected with them. These old objects can be updated by adding the missing attribute using functions setTimeUnit, setBSWFUsed, setTfrType and setRfrType. However, in many cases function update_spct can be used to set the missing attributes to default values, or the scripts re-run to rebuild the data objects from raw data.

Collections of spectra

Classes

The package defines several classes intended to be used to store collections of different types of spectral data. They are all derived from generic_mspct, which in turn is derived from list. Table 3 lists them.


Table 3. Classes for collections of spectral objects. Objects of class generic_mspct can have member objects of any class derived from generic_spct and can be heterogeneous. Attributes can be queried and set with the normal R methods attr and attributes as well as with functions defined in this package. See table 1 for the attributes used in individual member spectra of collections.

Class name Class of member objects Attributes
generic_mspct generic_spct names, dim, comment
raw_mspct raw_spct names, dim, comment
cps_mspct cps_spct names, dim, comment
source_mspct source_spct names, dim, comment
filter_mspct filter_spct names, dim, comment
reflector_mspct reflector_spct names, dim, comment
object_mspct object_spct names, dim, comment
response_mspct response_spct names, dim, comment
chroma_mspct chroma_spct names, dim, comment

Objects of these classes, except for those of class generic_mspct, can contain members belonging to one of the classes. Being all other spectral object classes derived from generic_spct, generic_mspct objects can contain heterogeneous collections of spectra. In all cases, there are no restrictions on the lengths, wavelength range and/or wavelength step size, or attributes other than class of the contained spectra. Mimicking R’s arrays and matrices, a dim attribute is always present and dim methods are provided. These allows the storage of time series of spectral data, or (hyper)spectral image data, or even higher dimensional spectral data. The handling of 1D and 2D spectral collections is already implemented in the summary methods. Handling of 3D and higher dimensional data can be implemented in the future without changing the class definition. By having implemented dim, also methods ncol and nrow are available as they use dim internally. Array-like subscripting is not implemented.

Construction

Constructors

We can construct a collection using a list of spectral objects as a starting point, in this case the spectral transmittance for two glass filters.

two_suns.mspct <- source_mspct(list(sun1 = sun.spct, sun2 = sun.spct))
two_suns.mspct
## Object: source_mspct [2 x 1]
## --- Member: sun1 ---
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
## --- Member: sun2 ---
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
## 
## --- END ---

We can also create heterogeneous collections, but this reduces the number of methods that can be used on the resulting collection.

mixed.mspct <- generic_mspct(list(filter = clear.spct, source = sun.spct))
class(mixed.mspct)
## [1] "generic_mspct" "list"
lapply(mixed.mspct, class_spct)
## $filter
## [1] "filter_spct"  "generic_spct"
## 
## $source
## [1] "source_spct"  "generic_spct"

Using as functions

The as functions for collections of spectra, not only change the class of the collection object, but also apply the corresponding {as} functions to the member objects. They copy the original objects and then convert the copy, which is returned.

two_gen.mscpt <- as.generic_mspct(two_suns.mspct)
class(two_gen.mscpt)
## [1] "generic_mspct" "list"
lapply(two_gen.mscpt, class_spct)
## $sun1
## [1] "source_spct"  "generic_spct"
## 
## $sun2
## [1] "source_spct"  "generic_spct"

Converting tidy data

Spectral objects containing multiple spectra identified by a factor (class of the argument is replicated to collection members).

two_suns.spct <- rbindspct(list(a = sun.spct, b = sun.spct / 2))
subset2mspct(two_suns.spct)
## Object: source_mspct [2 x 1]
## --- Member: a ---
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
## *    <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
## --- Member: b ---
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
## *    <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
## 
## --- END ---

Data frame containing tidy spectral data (target class and index variable need to be supplied as arguments).

test1.df <- data.frame(w.length = rep(200:210, 2),
                       s.e.irrad = rep(c(1, 2), c(11, 11)),
                       spectrum = factor(rep(c("A", "B"), c(11,11))))
subset2mspct(test1.df, member.class = "source_spct", idx.var = "spectrum")
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [11 x 2]
## Wavelength range 200 to 210 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 11 × 2
##   w.length s.e.irrad
## *    <int>     <dbl>
## 1      200         1
## 2      201         1
## 3      202         1
## 4      203         1
## # ... with 7 more rows
## --- Member: B ---
## Object: source_spct [11 x 2]
## Wavelength range 200 to 210 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 11 × 2
##   w.length s.e.irrad
## *    <int>     <dbl>
## 1      200         2
## 2      201         2
## 3      202         2
## 4      203         2
## # ... with 7 more rows
## 
## --- END ---

To convert a tidy data frame into a long form spectral object we need to pass the number of spectra through parameter multiple.wl to override the usual check for unique wavelength values.

setSourceSpct(test1.df, multiple.wl = 2L)
test1.df
## Object: source_spct [22 x 3]
## containing 2 spectra in long form
## Wavelength range 200 to 210 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 22 × 3
##   w.length s.e.irrad spectrum
##      <int>     <dbl>   <fctr>
## 1      200         1        A
## 2      201         1        A
## 3      202         1        A
## 4      203         1        A
## # ... with 18 more rows

Converting untidy data frames

Data frame containing untidy or wide spectral data (class is determined by the function used, columns which are not numeric are skipped.

test2.df <- data.frame(w.length = 200:210, A = 1, B = 2, z = "A")
split2source_mspct(test2.df)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [11 x 2]
## Wavelength range 200 to 210 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 11 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      200         1
## 2      201         1
## 3      202         1
## 4      203         1
## # ... with 7 more rows
## --- Member: B ---
## Object: source_spct [11 x 2]
## Wavelength range 200 to 210 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 11 × 2
##   w.length s.e.irrad
##      <int>     <dbl>
## 1      200         2
## 2      201         2
## 3      202         2
## 4      203         2
## # ... with 7 more rows
## 
## --- END ---
split2source_mspct(test2.df, spct.data.var = "s.q.irrad")
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [11 x 2]
## Wavelength range 200 to 210 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 11 × 2
##   w.length s.q.irrad
##      <int>     <dbl>
## 1      200         1
## 2      201         1
## 3      202         1
## 4      203         1
## # ... with 7 more rows
## --- Member: B ---
## Object: source_spct [11 x 2]
## Wavelength range 200 to 210 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 11 × 2
##   w.length s.q.irrad
##      <int>     <dbl>
## 1      200         2
## 2      201         2
## 3      202         2
## 4      203         2
## # ... with 7 more rows
## 
## --- END ---

Querying the class

is. functions are defined for these classes. R’s class method can also be used.

is.source_mspct(two_suns.mspct)
## [1] TRUE
class(two_suns.mspct)
## [1] "source_mspct"  "generic_mspct" "list"

In addition to using class to query the class of the collection, we can use base R’s lapply together with class or class_spct to query the class of each of the members of the collection.

is.filter_mspct(mixed.mspct)
## [1] FALSE
is.any_mspct(mixed.mspct)
## [1] TRUE
class(mixed.mspct)
## [1] "generic_mspct" "list"
lapply(mixed.mspct, class_spct)
## $filter
## [1] "filter_spct"  "generic_spct"
## 
## $source
## [1] "source_spct"  "generic_spct"
lapply(mixed.mspct, class)
## $filter
## [1] "filter_spct"  "generic_spct" "tbl_df"       "tbl"         
## [5] "data.frame"  
## 
## $source
## [1] "source_spct"  "generic_spct" "tbl_df"       "tbl"         
## [5] "data.frame"

Extract, replace and combine

R’s extraction and replacement methods have specializations for collections of spectra and can be used with the same syntax and functionality as for R lists. However they test the class and validity of the returned objects and replacement members.


Methods [, and [<-, extract and replace parts of the collection, respectively. Even when only one member is extracted, the returned value is a collection of spectra. The expected replacement value is also, always a collection of spectra.
# not run as this triggers an error when building the vignette with 'devtools'
two_suns.mspct[1]
two_suns.mspct[1:2]

Avoid code like that in the chunck bellow. Collections of spectra are named lists, consequently assigning by position as shown here swapts the values without swapping the names of the slots!

# not run as this triggers an error when building the vignette with 'devtools'
two_suns.mspct[1:2] <- two_suns.mspct[2:1]

Methods [[, $ and [[<-, extract and replace individual members of the collection, respectively. They always return or expect objects of one of the spectral classes.
two_suns.mspct[[1]]
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
two_suns.mspct$sun1
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
two_suns.mspct[["sun1"]]
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
two_suns.mspct[["sun1"]] <- sun.spct * 2
two_suns.mspct[["sun2"]] <- NULL
two_suns.mspct
## Object: source_mspct [2 x 1]
## --- Member: sun1 ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---

We can use the combine method c() with collections of spectra (but not to create new collections from individual spectra).

c(two_suns.mspct, mixed.mspct)
## Object: generic_mspct [3 x 1]
## --- Member: sun1 ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: filter ---
## Object: filter_spct [4 x 2]
## Wavelength range 100 to 5000 nm, step 1 to 4898 nm 
## 
## # A tibble: 4 × 2
##   w.length   Tfr
## *    <dbl> <dbl>
## 1      100     1
## 2      101     1
## 3     4999     1
## 4     5000     1
## --- Member: source ---
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
## 
## --- END ---

Transform or apply functions

For our apply functions we follow the naming convention used in package plyr, but using ms as prefix for _mspct objects. The apply functions implemented in the ‘photobiology’ package are msmsply, msdply, mslply and msaply which both accepts a collections of spectra as first argument and return a collection of spectra, a data frame, a list, or an array respectively (see Table 4).


Table 4. Apply functions for collections of spectra. Key: v., value returned by apply function; f.v., value returned by the applied function (argument .fun). In the table generic_mspct and generic_spct indicate objects of these classes or any class derived from them. The exact class of the collection of spectra object returned will be determined by the class(es) of the values returned by the applied function.

apply function first arg. class v. class f.v. class f.v. length f.v. dims
msmsply generic_mspct generic_mspct generic_spct 1 any
msdply generic_mspct data.frame numeric \(1\ldots n\) 1
mslply generic_mspct list any any any
msaply generic_mspct vector any simple 1 0
msaply generic_mspct matrix any simple \(2\ldots n\) \(2\ldots n\)
concolve_each generic_mspct generic_mspct generic_spct 1 any

Functions msmsply(), msdply and mslply can be used to apply a function to each member spectrum in a collection. The apply function to use depends on the return value of the applied function.

In the case of msmsply() the applied function is expected to return a transformed spectrum as another object of class generic_spct or a class derived from it. The value returned by msmsply is a collection of spectra, of a type determined by the class(es) of the member spectra in the new collection.

We start with a simple example in which we add a constant to each spectrum in the collection

two.mspct <- source_mspct(list(A = sun.spct * 1, B = sun.spct * 2))
msmsply(two.mspct, `+`, 0.1)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000       0.1
## 2 280.9231       0.1
## 3 281.8462       0.1
## 4 282.7692       0.1
## # ... with 518 more rows
## --- Member: B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000       0.1
## 2 280.9231       0.1
## 3 281.8462       0.1
## 4 282.7692       0.1
## # ... with 518 more rows
## 
## --- END ---

and continue with a more complex example in which we trim each spectrum

msmsply(two.mspct, trim_wl, range = c(281, 500), fill = NA)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [525 x 2]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 525 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000        NA
## 2 280.9231        NA
## 3 281.0000        NA
## 4 281.0000         0
## # ... with 521 more rows
## --- Member: B ---
## Object: source_spct [525 x 2]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 525 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000        NA
## 2 280.9231        NA
## 3 281.0000        NA
## 4 281.0000         0
## # ... with 521 more rows
## 
## --- END ---

In the second example we pass two arguments by name to the applied function. The number of arguments is not fixed, but the spectrum will be always passed as the first argument to the function.

In the case of msdply() the applied function is expected to return an R object of the same length for each of the member spectra.

msdply(two.mspct, max)
## # A tibble: 2 × 2
##   spct.idx max.wl
##     <fctr>  <dbl>
## 1        A    800
## 2        B    800
ranges.df <- msdply(two.mspct, range)
ranges.df
## # A tibble: 2 × 3
##   spct.idx min.wl max.wl
##     <fctr>  <dbl>  <dbl>
## 1        A    280    800
## 2        B    280    800
cat(comment(ranges.df))
## Applied function: 'range'.
msdply(two.mspct, range, na.rm = TRUE)
## # A tibble: 2 × 3
##   spct.idx min.wl max.wl
##     <fctr>  <dbl>  <dbl>
## 1        A    280    800
## 2        B    280    800

In the case of mslply() the applied function is expected to return an R object of any length, possibly variable among members.

str(mslply(two.mspct, names))
## List of 2
##  $ A: chr [1:2] "w.length" "s.e.irrad"
##  $ B: chr [1:2] "w.length" "s.e.irrad"
##  - attr(*, "comment")= chr "Applied function: 'names'.\n"

In the case of msaply() the applied function is expected to return an R object of length 1, although a list with dimensions will be returned for longer return values.

str(msaply(two.mspct, max))
##  atomic [1:2] 800 800
##  - attr(*, "comment")= chr "Applied function: 'max'.\n"
str(msaply(two.mspct, range))
##  num [1:2, 1:2] 280 280 800 800
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:2] "1" "2"
##  - attr(*, "comment")= chr "Applied function: 'range'.\n"

For the most common cases for which one would use the apply functions described in the previous section methods and functions operation on collections of spectra are defined in the package. For example the following statement is equivalent to the second example in the previous section. These are described in a later section and listed in Table 9.

Convolution

By convolution we normally mean the multiplication value by value at matching wavelengths of two spectra. The function described in this section facilitates this and similar operations among collections of spectra. An example use case could be the convolution of spectral irradiance by spectral transmittance for all combinations of light sources and filters in a collection of source spectra and a collection of filter spectra.

Default operator (or function) is that for multiplication, either one or both of the two first arguments must be a collection of spectra. When only one argument is a collection of spectra, the other one can be a spectrum, or even a numeric vector. For multiplication the order of the operands does not affect the returned value. With operators or functions for non-transitive operations the order does matter.

convolve_each(two.mspct, sun.spct)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---
convolve_each(sun.spct, two.mspct)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---
another_two.mspct <- two.mspct
names(another_two.mspct) <- c("a", "b")
convolve_each(another_two.mspct, two.mspct)
## Object: source_mspct [2 x 2]
## --- Member: a_A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: a_B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: b_A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: b_B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---

The function convolve_each will use other operators or functions and even pass additional named arguments when these are supplied as arguments.

convolve_each(two.mspct, sun.spct, oper = `+`)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---

There are cases where functions convolve_each() and msmsply() can be both used, but there are also cases where their differences matter. An example is convolving two collections of spectra, a case where only convolve_each() can be used. In contrast, when one of the arguments is not a spectrum or a collection of spectra, msmsply() should be used instead.

Attributes

Some of the set and get functions used with attributes have method definitions for collections of spectra. Some examples follow.

getWhenMeasured(two.mspct)
## # A tibble: 2 × 2
##   spct.idx       when.measured
##     <fctr>              <dttm>
## 1        A 2010-06-22 09:51:00
## 2        B 2010-06-22 09:51:00
setWhenMeasured(two.mspct, ymd("2015-10-31", tz = "EET"))
getWhenMeasured(two.mspct)
## # A tibble: 2 × 2
##   spct.idx       when.measured
##     <fctr>              <dttm>
## 1        A 2015-10-30 22:00:00
## 2        B 2015-10-30 22:00:00
setWhenMeasured(two.mspct,
                list(ymd_hm("2015-10-31 10:00", tz = "EET"),
                     ymd_hm("2015-10-31 11:00", tz = "EET")))
getWhenMeasured(two.mspct)
## # A tibble: 2 × 2
##   spct.idx       when.measured
##     <fctr>              <dttm>
## 1        A 2015-10-31 08:00:00
## 2        B 2015-10-31 09:00:00
two.mspct
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2015-10-31 08:00:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2015-10-31 09:00:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---

Other methods available are getWhereMeasured and setWhereMeasured.

Wavebands

When a range of wavelengths or a range of wavelengths plus a spectral weighting function (SWF) is needed for radiation summaries or transformations, methods, operators and functions defined in package ‘photobiology’ use waveband objects to store these data. A few other bits of information can be included to fine-tune calculations. The waveband definitions do NOT describe whether input spectral irradiances are photon or energy based, nor whether the output irradiance will be based on photon or energy units. All waveband objects belong to the S3 class waveband.

Construction

When defining a waveband which uses a SWF, a function can be supplied either based on energy effectiveness, on photon effectiveness, or one function for each one. If only one function is supplied the other one is built automatically, but if performance is a concern it is better to provide two separate functions. Another case when you might want to enter the same function twice, is if you are using an absorptance spectrum as SWF, as the percent of radiation absorbed will be independent of whether photon or energy units are used for the spectral irradiance.

To create a waveband object we use constructor function waveband, and optionally giving a name to it. We will use these objects in many of the examples below, so you will need to run the code chunk bellow to be able to reproduce those examples. It should be noted that waveband constructors for the most frequently used wavelength-range definitions are provided by package ‘photobiologyWavebands’.

PAR.wb <- waveband(c(400, 700), wb.name = "PAR")
UVA.wb <- waveband(c(315, 400), wb.name = "UVA")
UVB.wb <- waveband(c(280, 315), wb.name = "UVB")
UVC.wb <- waveband(c(100, 280), wb.name = "UVC")
UV.wb  <- waveband(c(100, 400), wb.name =  "UV")
UV_bands.lst <- list(UVC.wb, UVB.wb, UVA.wb)

When including a BSWF, we can supply, one or two versions of functions returning the weights as a function of wavelength. Several such functions are defined in package ‘photobiologyWavebands’ as well as waveband constructors using them. Here we show how a waveband can be defined based on a SWF, using the CIE definition for the erythemal spectral weighting function. Although the constructor is smart enough to derive the missing function when only one function is supplied, performance may suffer unless two performance-optimized function are provided, one for energy-based effect and a second one for photon-based effect.

CIE_e_fun <-
function(w.length){
    CIE.energy <- numeric(length(w.length))
    CIE.energy[w.length <= 298] <- 1
    CIE.energy[(w.length > 298) & (w.length <= 328)] <-
      10^(0.094*(298-w.length[(w.length > 298) & (w.length <= 328)]))
    CIE.energy[(w.length > 328) & (w.length <= 400)] <-
      10^(0.015*(139-w.length[(w.length > 328) & (w.length <= 400)]))
    CIE.energy[w.length > 400] <- 0
    return(CIE.energy)
}
CIE.wb <- waveband(c(250, 400), weight = "SWF",
                   SWF.e.fun = CIE_e_fun, SWF.norm = 298)

The first argument to waveband() does not need to be a numeric vector of length two. Any R object of a class that supplies a range() method definition that can be interpreted as a range of wavelengths in nanometres can be used. As a consequence, when wanting to construct a waveband covering the whole range of a spectrum one can simply supply the spectrum as argument, or to construct an unweighted waveband which covers exactly the same range of wavelengths as an existing effective (weighted) waveband, one can supply a waveband object as an argument.

waveband(sun.spct)
## Total 
## low (nm) 280 
## high (nm) 800 
## weighted none

Querying the class

The function is.waveband can the used to query any R object. This function returns a logical value.

is.waveband(PAR.wb)
## [1] TRUE

Above, we demonstrate that PAR.wb is a waveband object, the function photobiologyWavebands::PAR() is a waveband constructor returning a waveband object. See package ‘photobiologyWavebands’ for details on pre-defined waveband constructors for frequently used wavelength ranges and biological spectral weighting functions (BSWFs).

Retrieving properties

The function is_effective can the used to query any R object.

is_effective(waveband(c(400,500)))
## [1] FALSE

Collections of wavebands

In the current implementation there is no special class used for storing collections of waveband objects. We simply use base R’s list class.

Construction

List constructor

Just base R’s functions used to create a list object.

wavebands <- list(waveband(c(300,400)), waveband(c(400,500)))
wavebands
## [[1]]
## range.300.400 
## low (nm) 300 
## high (nm) 400 
## weighted none 
## 
## [[2]]
## range.400.500 
## low (nm) 400 
## high (nm) 500 
## weighted none

Special constructor

The function split_bands can be used to generate lists of unweighted wavebands in two different ways: a) it can be used to split a range of wavelengths given by an R object into a series of adjacent wavebands, or b) with a list of objects returning ranges, it can be used to create non-adjacent and even overlapping wavebands.

The code chunk bellow shows an example of two variations of case a). With the default value for length.out of NULL each numerical value in the input is taken as a wavelength (nm) at the boundary between adjacent wavebands. If a numerical value is supplied to length.out, then the whole wavelength range of the input is split into this number of equally spaced adjacent wavebands.

split_bands(c(200, 225, 300))
## $wb1
## range.200.225 
## low (nm) 200 
## high (nm) 225 
## weighted none 
## 
## $wb2
## range.225.300 
## low (nm) 225 
## high (nm) 300 
## weighted none
split_bands(c(200, 225, 300), length.out = 2)
## $wb1
## range.200.250 
## low (nm) 200 
## high (nm) 250 
## weighted none 
## 
## $wb2
## range.250.300 
## low (nm) 250 
## high (nm) 300 
## weighted none

In both examples above, the output is a list of two wavebands, but the split boundaries are at a different wavelength. The chunk bellow gives a few more examples of the use of case a).

split_bands(sun.spct, length.out = 2)
## $wb1
## range.280.540 
## low (nm) 280 
## high (nm) 540 
## weighted none 
## 
## $wb2
## range.540.800 
## low (nm) 540 
## high (nm) 800 
## weighted none
split_bands(PAR.wb, length.out = 2)
## $wb1
## range.400.550 
## low (nm) 400 
## high (nm) 550 
## weighted none 
## 
## $wb2
## range.550.700 
## low (nm) 550 
## high (nm) 700 
## weighted none
split_bands(c(200, 800), length.out = 3)
## $wb1
## range.200.400 
## low (nm) 200 
## high (nm) 400 
## weighted none 
## 
## $wb2
## range.400.600 
## low (nm) 400 
## high (nm) 600 
## weighted none 
## 
## $wb3
## range.600.800 
## low (nm) 600 
## high (nm) 800 
## weighted none

Now we demonstrate case b). This case is handled by recursion, so each list element can be anything that is a valid input to the function, including a nested list. However, the returned value is always a flat list of wavebands.

split_bands(list(A = c(200, 300), B = c(400, 500), C = c(250, 350)))
## $A
## range.200.300 
## low (nm) 200 
## high (nm) 300 
## weighted none 
## 
## $B
## range.400.500 
## low (nm) 400 
## high (nm) 500 
## weighted none 
## 
## $C
## range.250.350 
## low (nm) 250 
## high (nm) 350 
## weighted none
split_bands(list(c(100, 150, 200), c(800, 825)))
## $wb.a
## range.100.150 
## low (nm) 100 
## high (nm) 150 
## weighted none 
## 
## $<NA>
## range.150.200 
## low (nm) 150 
## high (nm) 200 
## weighted none 
## 
## $wb.b
## range.800.825 
## low (nm) 800 
## high (nm) 825 
## weighted none

In case b) if we supply a numeric value to length.out, this value is used recursively for each element of the list.

split_bands(UV_bands.lst, length.out  =  2)
## $wb.a
## range.100.190 
## low (nm) 100 
## high (nm) 190 
## weighted none 
## 
## $<NA>
## range.190.280 
## low (nm) 190 
## high (nm) 280 
## weighted none 
## 
## $wb.b
## range.280.297.5 
## low (nm) 280 
## high (nm) 298 
## weighted none 
## 
## $<NA>
## range.297.5.315 
## low (nm) 298 
## high (nm) 315 
## weighted none 
## 
## $wb.c
## range.315.357.5 
## low (nm) 315 
## high (nm) 358 
## weighted none 
## 
## $<NA>
## range.357.5.400 
## low (nm) 358 
## high (nm) 400 
## weighted none
split_bands(list(c(100, 150, 200), c(800, 825)), length.out = 1)
## $wb.a
## range.100.200 
## low (nm) 100 
## high (nm) 200 
## weighted none 
## 
## $wb.b
## range.800.825 
## low (nm) 800 
## high (nm) 825 
## weighted none

Object inspection methods

print()

The print() method for spectra is based on the method defined in package ‘tibble’, consequently, it is possible to use the options from this package to control printing. In the code chunk below, tibble.print_max, the number of rows in the spectral object above which only tibble.print_min rows are printed, are both set to 5, instead of the default 20 and 10, to avoid excessive clutter in our examples.

options(tibble.print_max = 4)
options(tibble.print_min = 4)

For explicit calls to print() its argument n can be used to control the number of lines printed. If n is set to Inf the whole spectrum is always printed. The output differs from that of the print() method from package ‘dplyr’ in that additional metadata specific to spectra are shown.

print(sun.spct, n = 3)

Specialized print() methods for collections of spectra and for waveband objects are also defined.

summary()

The summary() method for spectra is based on base R’s summary() method for data frames, and accepts the same arguments. The main difference is that the attributes containing metadata and dimensions of the original spectrum object are copied to the summary object.

summary(sun.spct)

Specialized print() methods for summaries of spectra are defined. The output differs from that of the print() method from base R in that additional metadata specific to spectra are shown.

Transformations: using operators

Binary operators

All of R’s built-in maths operators have definitions for spectra. It is possible to sum, subtract, multiply and divide spectra. These operators can be used even if the spectral data is on different arbitrary sets of wavelengths. Operators by default return values expressed in energy units. Only certain operations are meaningful for a given combination of objects belonging to different classes, and meaningless combinations return NA also issuing a warning (see Table 5). By default operations are carried out on spectral energy irradiance for source_spct objects and transmittance for filter_spct objects.


Table 5. Binary operators and their operands. Validity and class of result. All operations marked \Y' are allowed, those marked’ are forbidden and return NA and issue a warning. Operators %/% and %% follow /.

e1 + - * / ^ e2 value
raw_spct Y Y Y Y Y raw_spct raw_spct
cps_spct Y Y Y Y Y cps_spct cps_spct
source_spct Y Y Y Y Y source_spct source_spct
filter_spct (T) N N Y Y N filter_spct filter_spct
filter_spct (A) Y Y N N N filter_spct filter_spct
reflector_spct N N Y Y N reflector_spct reflector_spct
object_spct N N N N N object_spct
response_spct Y Y Y Y N response_spct response_spct
chroma_spct Y Y Y Y Y chroma_spct chroma_spct
raw_spct Y Y Y Y Y numeric raw_spct
cps_spct Y Y Y Y Y numeric cps_spct
source_spct Y Y Y Y Y numeric source_spct
filter_spct Y Y Y Y Y numeric filter_spct
reflector_spct Y Y Y Y Y numeric reflector_spct
object_spct N N N N N numeric
response_spct Y Y Y Y Y numeric response_spct
chroma_spct Y Y Y Y Y numeric chroma_spct
source_spct N N Y Y N response_spct response_spct
source_spct N N Y Y N filter_spct (T) source_spct
source_spct N N Y Y N filter_spct (A) source_spct
source_spct N N Y Y N reflector_spct source_spct
source_spct N N N N N object_spct
source_spct N N Y N N waveband (no BSWF) source_spct
source_spct N N Y N N waveband (BSWF) source_spct (effective)

sun.spct * sun.spct
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

When meaningful, operations between different spectra are also allowed. For example, it is possible to simulate the effect of a filter on a light source by multiplying (or convolving) the two spectra.

sun.spct * polyester.spct
## Object: source_spct [533 x 2]
## Wavelength range 280 to 800 nm, step 0.07692308 to 1 nm 
## Time unit 1s
## 
## # A tibble: 533 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.0000         0
## 4 281.8462         0
## # ... with 529 more rows

If we have two layers of the filter, this can be approximated using either of these two statements.

sun.spct * polyester.spct * polyester.spct
## Object: source_spct [533 x 2]
## Wavelength range 280 to 800 nm, step 0.07692308 to 1 nm 
## Time unit 1s
## 
## # A tibble: 533 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.0000         0
## 4 281.8462         0
## # ... with 529 more rows
sun.spct * polyester.spct^2
## Object: source_spct [533 x 2]
## Wavelength range 280 to 800 nm, step 0.07692308 to 1 nm 
## Time unit 1s
## 
## # A tibble: 533 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.0000         0
## 4 281.8462         0
## # ... with 529 more rows

Operators are also defined for operations between a spectrum and a numeric vector (with normal recycling).

sun.spct * 2
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
2 * sun.spct
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
sun.spct * c(0,1)
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

There is one special case, for chroma_spct: if the numeric operand has length three, containing three named values x, y and z, the corresponding value is used for each of the chromaticity columns in the chroma_spct. Un-named values or differently named values are not treated specially.

Operators are also defined for operations between an spectrum and a waveband object. The next to code chunks demonstrate how the class of the result depends on whether the waveband object describes a range of wavelengths or a range of wavelengths plus a BSWF.

sun.spct * UVB.wb
## Object: source_spct [37 x 2]
## Wavelength range 280 to 315 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 37 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 33 more rows
sun.spct * CIE.wb

And of course these operations can be combined into more complex statements, including parentheses, when needed. The example below estimates the difference in effective spectral irradiance according to the CIE98 definition, between sunlight and sunlight filtered with a polyester film. Of course, the result is valid only for the solar spectral data used, which corresponds to Southern Finland.

sun.spct * CIE.wb - sun.spct * polyester.spct * CIE.wb
## Object: source_spct [133 x 2]
## Wavelength range 280 to 400 nm, step 0.07692308 to 1 nm 
## Time unit 1s
## Data weighted using 'range.250.400.wtd' BSWF
## 
## # A tibble: 133 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.0000         0
## 4 281.8462         0
## # ... with 129 more rows

Unary operators and maths functions

Many common maths functions, as well as unary minus and plus, are implemented for spectral objects (see Table 6).


Table 6. Unary operators and maths functions for spectra. Classes for which they are implemented and class of the result. All operations marked Y are allowed, those marked N are not implemented and return NA and issue a warning. Additional supported functions: log2, log10, sin, cos, tan, asin, acos, atan, sinpi, cospi, tanpi, signif, floor, ceiling, trunc, sign, abs.

e1 +, - log, exp trig. round sqrt value
raw_spct Y Y Y Y Y raw_spct
cps_spct Y Y Y Y Y cps_spct
source_spct Y Y Y Y Y source_spct
filter_spct Y Y Y Y Y filter_spct
reflector_spct Y Y Y Y Y reflector_spct
object_spct N N N N N
response_spct Y Y Y Y Y response_spct
chroma_spct Y Y Y Y Y chroma_spct

-sun.spct
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
sqrt(sun.spct)
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

Options

Table 7 lists all the recognized options affecting maths operators and functions, and their default values. Within the suite all functions have a default value which is used when the options are undefined. Options are set using base R’s function options, and queried with functions options and getOption.


Table 7. Options used in the ‘r4photobiology suite’ and recognized by methods, operators and functions in the ‘photobiology’ package. Option names, accepted and default values, and the purpose of the options are given.

Option values, default function
Base R
digits 7 \(d - 3\) used by summary
tibble
tibble.print_max 20 Maximum number of rows printed
tibble.print_min 10 Number of rows printed if row number threshold is exceeded
tibble.width NULL Output width
r4photobiology
photobiology.radiation.unit “energy” output (\(W\,m^{-2}\,nm^{-1}\))
"photon" output ()
photobiology.filter.qty “transmittance” output (\(/1\))
"absorptance" output (\(/1\))
"absorbance" output (a.u. \(\log_{10}\) base)
photobiology.strict.range NA skip range test
TRUE trigger an error
FALSE trigger a warning
photobiology.waveband.trim FALSE exclude
TRUE trim or exclude
photobiology.use.cached.mult FALSE do not cache intermediate results
TRUE cache intermediate results
photobiology.verbose FALSE minimal warnings and messages
TRUE all warnings and messages

The behaviour of the operators defined in this package depends on the value of two global options. For example, if we would like the operators to operate on spectral photon irradiance and return spectral photon irradiance instead of spectral energy irradiance, this behaviour can be set, and will remain active until unset or reset.

options(photobiology.radiation.unit = "photon")
sun.spct * UVB.wb
## Object: source_spct [37 x 2]
## Wavelength range 280 to 315 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 37 × 2
##   w.length s.q.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 33 more rows
options(photobiology.radiation.unit = "energy")
sun.spct * UVB.wb
## Object: source_spct [37 x 2]
## Wavelength range 280 to 315 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 37 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 33 more rows

The other options listed in the Table above can be set similarly, to unset any option, they can be given a NULL value.

Transformations: methods and functions

In this section we describe methods and functions that take one or more spectral objects, and in some cases also waveband objects, as arguments and return another spectral object (see Tables 8 and 9) or that take a collection of spectral objects, and in some cases also waveband objects, as arguments and return a collection of spectral objects.


Table 8. Transformation methods for spectra. Key: + available, – not available, f available in the future.

methods raw/cps source response filter reflector object chroma
merge + + + + + + +
rbindspct + + + + + + +
e2q, q2e + +
A2T, T2A +
subset + + + + + + +
clip_wl + + + + + + +
trim_wl + + + + + + +
(trim_spct) + + + + + + +
interpolate_wl + + + + + +
(interpolate_spct) + + + + + +
fscale + + + + +
fshift + + + + +
normalize + + + + +
clean + + + +
math operators + + + + + + +
math functions + + + + + + +
tag + + + + + +
untag + + + + + +


Table 9. Transformation methods for collections of spectra. Key: + available, – not available, ms use msmsply() or convolve_each() to apply function or operator to collection members.

methods raw/cps source response filter reflector object chroma
convolve_each + + + + + +
msmsply + + + + + + +
msdply + + + + + + +
mslply + + + + + + +
msaply + + + + + + +
rbindspct + + + + + + +
c + + + + + + +
math operators ms ms ms ms ms ms ms
math functions ms ms ms ms ms ms ms
e2q, q2e + +
A2T, T2A +
clip_wl + + + + + + +
trim_wl + + + + + + +
trim2overlap + + + + + + +
extend2extremes + + + + + + +
(trim_mspct) + + + + + + +
interpolate_wl + + + + + +
(interpolate_mspct) + + + + + +
fscale + + + + +
fshift + + + + +
normalize + + + + +
clean + + + +
tag + + + + + +
untag + + + + + +

Manipulating spectra

Sometimes, especially for plotting, we may want to row-bind spectra. When the aim is that the returned object retains its class and other attributes like the time unit. Package ‘photobiology’ provides function rbinspct for row-binding spectra, with the necessary checks for consistency of the bound spectra.

# STOPGAP
shade.spct <- sun.spct

By default an ID factor named spct.idx is added allow to identify the source of the observations after the binding. If the supplied list has named members, then these names are used as factor levels. If a character value is supplied to as idfactor argument, this is used as name for the factor.

rbindspct(list(sun.spct, shade.spct))
## Object: source_spct [1,044 x 4]
## containing 2 spectra in long form
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 1,044 × 4
##   w.length s.e.irrad spct.idx s.q.irrad
##      <dbl>     <dbl>   <fctr>     <dbl>
## 1 280.0000         0   spct_1         0
## 2 280.9231         0   spct_1         0
## 3 281.8462         0   spct_1         0
## 4 282.7692         0   spct_1         0
## # ... with 1,040 more rows
rbindspct(list(A = sun.spct, B = shade.spct), idfactor = "site")
## Object: source_spct [1,044 x 4]
## containing 2 spectra in long form
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## 
## # A tibble: 1,044 × 4
##   w.length s.e.irrad   site s.q.irrad
##      <dbl>     <dbl> <fctr>     <dbl>
## 1 280.0000         0      A         0
## 2 280.9231         0      A         0
## 3 281.8462         0      A         0
## 4 282.7692         0      A         0
## # ... with 1,040 more rows

Special Extract methods for spectral objects have been implemented. These are used by default and preserve the attributes used by this package, except when the returned value is a single column from the spectral object.

sun.spct[1:10, ]
## Object: source_spct [10 x 3]
## Wavelength range 280 to 288.30769 nm, step 0.9230769 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 10 × 3
##   w.length s.e.irrad s.q.irrad
## *    <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 6 more rows
sun.spct[1:10, 1]
##  [1] 280.0000 280.9231 281.8462 282.7692 283.6923 284.6154 285.5385
##  [8] 286.4615 287.3846 288.3077
sun.spct[1:10, 1, drop = TRUE]
##  [1] 280.0000 280.9231 281.8462 282.7692 283.6923 284.6154 285.5385
##  [8] 286.4615 287.3846 288.3077
sun.spct[1:10, "w.length", drop = TRUE]
##  [1] 280.0000 280.9231 281.8462 282.7692 283.6923 284.6154 285.5385
##  [8] 286.4615 287.3846 288.3077

In contrast to trim_spct, subset never interpolates or inserts hinges. On the other hand, the subset argument accepts any logical expression and can be consequently used to do subsetting, for example, based on factors. Both subset() and trim() methods preserve attributes.

subset(sun.spct, s.e.irrad > 0.2)
## Object: source_spct [475 x 3]
## Wavelength range 324 to 800 nm, step 1 to 3 nm 
## Time unit 1s
## 
## # A tibble: 475 × 3
##   w.length s.e.irrad    s.q.irrad
## *    <dbl>     <dbl>        <dbl>
## 1      324 0.2075508 5.621282e-07
## 2      325 0.2168055 5.890059e-07
## 3      326 0.2774416 7.560580e-07
## 4      327 0.2851096 7.793375e-07
## # ... with 471 more rows
subset(sun.spct, w.length > 600)
## Object: source_spct [200 x 3]
## Wavelength range 601 to 800 nm, step 1 nm 
## Time unit 1s
## 
## # A tibble: 200 × 3
##   w.length s.e.irrad    s.q.irrad
## *    <dbl>     <dbl>        <dbl>
## 1      601 0.6295837 3.162962e-06
## 2      602 0.6305890 3.173284e-06
## 3      603 0.6360329 3.205995e-06
## 4      604 0.6578140 3.321284e-06
## # ... with 196 more rows
subset(sun.spct, c(TRUE, rep(FALSE, 99)))
## Object: source_spct [6 x 3]
## Wavelength range 280 to 779 nm, step 99 to 100 nm 
## Time unit 1s
## 
## # A tibble: 6 × 3
##   w.length s.e.irrad    s.q.irrad
## *    <dbl>     <dbl>        <dbl>
## 1      280 0.0000000 0.000000e+00
## 2      379 0.4131498 1.308919e-06
## 3      479 0.7536975 3.017857e-06
## 4      579 0.6474340 3.133575e-06
## 5      679 0.5798542 3.291202e-06
## 6      779 0.4713172 3.069140e-06

R’s Extract methods $ and [[ can be used to extract whole columns. Replace methods $<- and [<- have definitions for spectral objects, which allow their safe use. They work identically to those for data frames but check the validity of the spectra after the replacement.

Conversions between radiation units

The functions e2q and q2e can be used on source spectra to convert spectral energy irradiance into spectral photon irradiance and vice versa. A second optional argument sets the action with "add" and "replace" as possible values. By default these functions use normal reference semantics.

e2q(sun.spct, "add")
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows
e2q(sun.spct, "replace")
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.q.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

For filter_spct objects functions T2A and A2T allow conversion between spectral transmittance and spectral absorbance and vice versa.

Normalizing a spectrum

Function normalize permits normalizing a spectrum to a value of one at an arbitrary wavelength (nm) or to the wavelength of either the maximum or the minimum spectral value. It supports the different spectral classes, we use a source_spct object as an example.

normalize(sun.spct)
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## Spectral data normalized to 1 at 451 nm 
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

Which is equivalent to supplying "max" as argument to norm, it is also possible to give a range within which the maximum should be searched.

normalize(sun.spct, range = PAR.wb, norm = "max")
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## Spectral data normalized to 1 at 451 nm 
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

It is also possible to normalize to an arbitrary wavelength within the range of the data, even if it is not one of the wavelength values present in the spectral object, as interpolation is used when needed.

normalize(sun.spct, norm = 600.3)
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## Spectral data normalized to 1 at 600.3 nm 
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

The normalization status of a spectral object can be tested with method is_normalized() and the normalization used can be recalled with method getNormalized().

my.spct <- normalize(sun.spct)
is_normalized(my.spct)
## [1] TRUE
getNormalized(my.spct)
## [1] 451

Once a spectrum is normalized, summary methods that return values in absolute units such as irrad(), will trigger an error if applied. Ratios and similar summaries that are invariant with respect to normalization can be calculated.

Applying method fscale() removes the normalization and clears the corresponding attribute where the normalization information is stored. This attribute also can be cleared with method setNormalized(), but this is rarely valid or of any use.

Rescaling a spectrum

Function fscale() rescales a spectrum by dividing each spectral data value by a value calculated with a function (f) selected by a character string (“total” or “mean”), or an actual R function which can accept the spectrum object supplied as its first argument. Additional named arguments can be also passed.

fscale(sun.spct)
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## Rescaled to 'mean' = 1
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
fscale(sun.spct, f = "total")
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## Rescaled to 'total' = 1
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
fscale(sun.spct, range = PAR.wb, f = irrad)
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## Rescaled to 'a user supplied R function' = 1
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
fscale(sun.spct, range = PAR.wb, f = q_irrad, target = 800e-6)
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## Rescaled to 'a user supplied R function' = 8e-04
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

In the third example, the spectral data is rescaled so that the corresponding photosynthetically-active irradiance is equal to one.

The normalization status of a spectral object can be tested with method is_normalized() and the normalization used can be recalled with method getNormalized().

my.spct <- fscale(sun.spct)
is_scaled(my.spct)
## [1] TRUE
getScaled(my.spct)
## $multiplier
## [1] 1.932188
## 
## $f
## [1] "mean"
## 
## $range
## [1] 280 800
## 
## $target
## [1] 1

Once a spectrum is scaled, summary methods that return values in absolute units such as irrad(), will trigger a warning if applied. Ratios and similar summaries that are invariant with respect to normalization do not trigger warnings.

Applying method normalize() removes the scaling and clears the corresponding attribute where the scaling information is stored. This attribute also can be cleared with method setScaled(), and this can be useful is some cases such as when irradiance has been measured separately from the emission spectrum.

Shifting the zero of the spectral data scale

Function fshift() shifts the zero of the scale of a spectrum by subtracting from each spectral data value a value calculated with a function (f) selected by a character string (“mean”, “min” or “max”), or an actual R function which can accept the spectrum object supplied as its first argument. The range argument selects a region of the spectrum to be used as reference in the calculation of the summary.

fshift(sun.spct, range = UVB.wb, f = "mean")
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length   s.e.irrad
##      <dbl>       <dbl>
## 1 280.0000 -0.01841458
## 2 280.9231 -0.01841458
## 3 281.8462 -0.01841458
## 4 282.7692 -0.01841458
## # ... with 518 more rows
fshift(sun.spct, range = c(280,290), f = "min")
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows

In the first example, the spectral data shifted so that the mean spectral irradiance becomes zero for the UV-B region. In the second example the minimum value in the range of wavelengths between 280~nm and 290~nm is used as zero reference for the scale.

Replacing off-range spectral data values

Method clean() should be used with care as off-range values stem almost always from calibration errors or measuring noise. This function allows one to replace such values, but in many cases a zero shift or rescaling could be the option to be preferred. Even when the off-range values are the result of random noise, replacing them with the boundary values can cause bias, by censoring the data. Here we create artificial off-range values by subtracting a constant from each spectrum.

clean(sun.spct - 0.01, range = c(280.5, 282))
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000     -0.01
## 2 280.9231      0.00
## 3 281.8462      0.00
## 4 282.7692     -0.01
## # ... with 518 more rows
clean(polyester.spct - 0.053)
## Warning in range_check(x, strict.range = strict.range): Off-range
## transmittance values [-0.05...0.87] instead of [0..1]
## Object: filter_spct [611 x 2]
## Wavelength range 190 to 800 nm, step 1 nm 
## 
## # A tibble: 611 × 2
##   w.length   Tfr
##      <int> <dbl>
## 1      190     0
## 2      191     0
## 3      192     0
## 4      193     0
## # ... with 607 more rows

Wavelength interpolation

Converting spectra available at a given set of wavelengths values to a different one, is frequently needed when operating with several spectra of different origin. One can increase the apparent resolution by interpolation, and reduce it by local averaging or smoothing and resampling. The same function works on all spct objects, interpolating every column except w.length and replacing in this last column the old wavelength values with the new ones supplied as argument. The optional argument fill.value control what value is assigned to wavelengths in the new data that are outside the range of the old wavelengths.

interpolate_wl(sun.spct, seq(400, 500, by = 0.1))
## Object: source_spct [1,001 x 3]
## Wavelength range 400 to 500 nm, step 0.1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 1,001 × 3
##   w.length s.e.irrad    s.q.irrad
##      <dbl>     <dbl>        <dbl>
## 1    400.0 0.6081049 2.033314e-06
## 2    400.1 0.6099118 2.039879e-06
## 3    400.2 0.6117187 2.046445e-06
## 4    400.3 0.6135257 2.053010e-06
## # ... with 997 more rows

Trimming and clipping

Method clip_wl()

Sometimes it is desirable to change the range of wavelengths included in a spectrum. If we are interested in a given part of the spectrum, there is no need to do calculations or plotting the whole spectrum. To select part a spectrum based on a range of wavelengths we may use the clip_wl method.

The range of wavelengths expressed in nanometres can be given as numeric vector of length two.

clip_wl(sun.spct, range = c(400, 402))
## Object: source_spct [3 x 3]
## Wavelength range 400 to 402 nm, step 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 3 × 3
##   w.length s.e.irrad    s.q.irrad
## *    <dbl>     <dbl>        <dbl>
## 1      400 0.6081049 2.033314e-06
## 2      401 0.6261742 2.098967e-06
## 3      402 0.6497388 2.183388e-06
clip_wl(sun.spct, range = c(400, NA))
## Object: source_spct [401 x 3]
## Wavelength range 400 to 800 nm, step 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 401 × 3
##   w.length s.e.irrad    s.q.irrad
## *    <dbl>     <dbl>        <dbl>
## 1      400 0.6081049 2.033314e-06
## 2      401 0.6261742 2.098967e-06
## 3      402 0.6497388 2.183388e-06
## 4      403 0.6207287 2.091091e-06
## # ... with 397 more rows

As for trim_wl() the range can be also supplied as a waveband object, or any other object for which range() returns a numeric range. Even a different spectrum object is acceptable.

clip_wl(sun.spct, range = UVA.wb)
## Object: source_spct [86 x 3]
## Wavelength range 315 to 400 nm, step 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 86 × 3
##   w.length s.e.irrad    s.q.irrad
## *    <dbl>     <dbl>        <dbl>
## 1      315 0.1127901 2.969940e-07
## 2      316 0.1020587 2.695897e-07
## 3      317 0.1487690 3.942191e-07
## 4      318 0.1413919 3.758526e-07
## # ... with 82 more rows

The result can be a spectrum of length zero.

clip_wl(sun.spct, range = c(100, 200))
## Object: source_spct [0 x 3]
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 0 × 3
## # ... with 3 variables: w.length <dbl>, s.e.irrad <dbl>, s.q.irrad <dbl>

Method trim_wl()

Sometimes, we need more flexibility. We may want to replace the observed values outside a certain range or expand the range of wavelengths, filling the expansion of all other variables with a certain value (i.e. a number, or NA.). In contrast to clipping (or functionally equivalent, indexing, or subsetting), trimming ensures that there will be spectral data returned at the boundaries of the trimmed region. These values are obtained by interpolation when they are not already present in the data.

More flexibility is available in method trim_wl(), to which we can supply arguments range, use.hinges, and fill. By default interpolation is used at the boundaries of the range.

trim_wl(sun.spct, c(282.5, NA))
## Object: source_spct [520 x 3]
## Wavelength range 282.5 to 800 nm, step 0.2692308 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 520 × 3
##   w.length s.e.irrad s.q.irrad
## *    <dbl>     <dbl>     <dbl>
## 1 282.5000         0         0
## 2 282.7692         0         0
## 3 283.6923         0         0
## 4 284.6154         0         0
## # ... with 516 more rows
clip_wl(sun.spct, c(282.5, NA))
## Object: source_spct [519 x 3]
## Wavelength range 282.76923 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 519 × 3
##   w.length s.e.irrad s.q.irrad
## *    <dbl>     <dbl>     <dbl>
## 1 282.7692         0         0
## 2 283.6923         0         0
## 3 284.6154         0         0
## 4 285.5385         0         0
## # ... with 515 more rows

As for clip_wl() the range can be also supplied as a waveband object, or any other object for which range() returns a numeric range. Even a different spectrum object is acceptable.

trim_wl(sun.spct, PAR.wb)
## Object: source_spct [301 x 3]
## Wavelength range 400 to 700 nm, step 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 301 × 3
##   w.length s.e.irrad    s.q.irrad
## *    <dbl>     <dbl>        <dbl>
## 1      400 0.6081049 2.033314e-06
## 2      401 0.6261742 2.098967e-06
## 3      402 0.6497388 2.183388e-06
## 4      403 0.6207287 2.091091e-06
## # ... with 297 more rows

The default for fill is NULL which results in deletion values outside the trimmed region. However, it is possible to supply a different argument, to be used to replace the off-range data values.

trim_wl(sun.spct, c(281.5, NA), fill = NA)
## Object: source_spct [524 x 3]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 524 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000        NA        NA
## 2 280.9231        NA        NA
## 3 281.5000        NA        NA
## 4 281.5000         0         0
## # ... with 520 more rows

Furthermore, when fill is not NULL, expansion is possible.

trim_wl(sun.spct, c(275, NA), fill = 0)
## Object: source_spct [529 x 3]
## Wavelength range 275 to 800 nm, step 1.023182e-12 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 529 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 275.0000         0         0
## 2 275.8333         0         0
## 3 276.6667         0         0
## 4 277.5000         0         0
## # ... with 525 more rows

By default interpolation at the boundaries is used, but setting use.hinges to FALSE results in clipping, a behaviour similar to that of clip_wl only if fill == NULL.

trim_wl(sun.spct, c(281.5, NA), fill = NA)
## Object: source_spct [524 x 3]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 524 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000        NA        NA
## 2 280.9231        NA        NA
## 3 281.5000        NA        NA
## 4 281.5000         0         0
## # ... with 520 more rows
trim_wl(sun.spct, c(281.5, NA), fill = NA, use.hinges = FALSE)
## Object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000        NA        NA
## 2 280.9231        NA        NA
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 518 more rows

When use.hinges == TRUE and expansion or replacement is done, two observations are inserted at each boundary, differing in wavelength by \(1 \times 10^{-12}\),nm to prevent rounding errors in later calculations.

Functions trim2ovelap() and extend2extremes()

Functions trim2ovelap() and extend2extremes() are defined only for collections of spectra. They are convenience functions, as they handle special use cases of trim_wl() with a simplified syntax. They can be used to make the wavelength range of the different members of a collection of spectra consistent, either by trimming all spectra to the range of overlapping wavelengths, or by extending the wavelength ranges as needed and filling the added spectral values with a constant value.

trim2overlap(two.mspct)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2015-10-31 08:00:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
## *    <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2015-10-31 09:00:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
## *    <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---
extend2extremes(two.mspct, fill = 0)
## Object: source_mspct [2 x 1]
## --- Member: A ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2015-10-31 08:00:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## --- Member: B ---
## Object: source_spct [522 x 2]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm 
## Measured on 2015-10-31 09:00:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 522 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 518 more rows
## 
## --- END ---

Functions trim_spct and trim_spct

Functions trim_spct and trim_spct are not generic, and add even more flexibility, but trim_wl should be preferred in user scripts.

Convolving weights

It is very instructive to look at weighted spectral data to understand how effective irradiances are calculated. Plotting effective spectral irradiance data can be very instructive when analysing the interaction of photoreceptors and ambient radiation. It can also illustrate what a large effect that small measuring errors can have on the estimated effective irradiances or exposures when SWFs have a steep slope.

Individual spectra

The multiplication operator is defined for operations between a source_spct and a waveband, so this is the easiest way of doing the calculations.

sun.spct * CIE.wb
## Object: source_spct [122 x 2]
## Wavelength range 280 to 400 nm, step 0.9230769 to 1 nm 
## Time unit 1s
## Data weighted using 'range.250.400.wtd' BSWF
## 
## # A tibble: 122 × 2
##   w.length s.e.irrad
##      <dbl>     <dbl>
## 1 280.0000         0
## 2 280.9231         0
## 3 281.8462         0
## 4 282.7692         0
## # ... with 118 more rows

Tagging with bands and colours

We call tagging, to the process of adding reference information to spectral data. For example we can add a factor indicating regions or bands in the spectrum. We can add also information on the colour, as seen by humans, for each observed value, or for individual regions or bands of the spectrum. In most cases this additional information is used for annotations when plotting the spectral data.

Individual spectra

The function tag can be used to tag different parts of a spectrum according to wavebands.

tag(sun.spct, PAR.wb)
## Object: source_spct [524 x 6]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Time unit 1s
## 
## # A tibble: 524 × 6
##   w.length s.e.irrad s.q.irrad wl.color wb.color   wb.f
##      <dbl>     <dbl>     <dbl>    <chr>    <chr> <fctr>
## 1 280.0000         0         0  #000000     <NA>     NA
## 2 280.9231         0         0  #000000     <NA>     NA
## 3 281.8462         0         0  #000000     <NA>     NA
## 4 282.7692         0         0  #000000     <NA>     NA
## # ... with 520 more rows
tag(sun.spct, UV_bands.lst)
## Object: source_spct [524 x 6]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Time unit 1s
## 
## # A tibble: 524 × 6
##   w.length s.e.irrad s.q.irrad wl.color wb.color   wb.f
##      <dbl>     <dbl>     <dbl>    <chr>    <chr> <fctr>
## 1 280.0000         0         0  #000000    black    UVB
## 2 280.9231         0         0  #000000    black    UVB
## 3 281.8462         0         0  #000000    black    UVB
## 4 282.7692         0         0  #000000    black    UVB
## # ... with 520 more rows

The added factor and colour data can be used for further processing or for plotting. Information about the tagging and wavebands is stored in an attribute tag.attr in every tagged spectrum, this yields a more compact output and keeps a trace of the tagging.

tg.sun.spct <- tag(sun.spct, PAR.wb)
attr(tg.sun.spct, "spct.tags")
## $time.unit
## [1] "second"
## 
## $wb.key.name
## [1] "Bands"
## 
## $wl.color
## [1] TRUE
## 
## $wb.color
## [1] TRUE
## 
## $wb.num
## [1] 1
## 
## $wb.colors
## [1] "#735B57"
## 
## $wb.names
## [1] "PAR"
## 
## $wb.list
## $wb.list[[1]]
## PAR 
## low (nm) 400 
## high (nm) 700 
## weighted none

Additional functions are available which return a tagged spectrum and take as input a list of wavebands, but no spectral data. They build a spectrum from the data in the wavebands, and are useful for plotting the boundaries of wavebands.

wb2tagged_spct(UV_bands.lst)
## Object: generic_spct [8 x 12]
## Wavelength range 100 to 400 nm, step 9.947598e-13 to 180 nm 
## 
## # A tibble: 8 × 12
##   w.length counts   cps s.e.irrad s.q.irrad   Tfr   Rfl s.e.response
##      <dbl>  <dbl> <dbl>     <dbl>     <dbl> <dbl> <dbl>        <dbl>
## 1      100      0     0         0         0     0     0            0
## 2      100      0     0         0         0     0     0            0
## 3      280      0     0         0         0     0     0            0
## 4      280      0     0         0         0     0     0            0
## # ... with 4 more rows, and 4 more variables: wl.color <chr>,
## #   wb.color <chr>, wb.f <fctr>, y <dbl>
wb2rect_spct(UV_bands.lst)
## Object: generic_spct [3 x 15]
## Wavelength range 190 to 357.5 nm, step 60 to 107.5 nm 
## 
## # A tibble: 3 × 15
##   w.length counts   cps s.e.irrad s.q.irrad   Tfr   Rfl s.e.response
##      <dbl>  <dbl> <dbl>     <dbl>     <dbl> <dbl> <dbl>        <dbl>
## 1    190.0      0     0         0         0     0     0            0
## 2    297.5      0     0         0         0     0     0            0
## 3    357.5      0     0         0         0     0     0            0
## # ... with 7 more variables: wl.color <chr>, wb.color <chr>,
## #   wb.name <chr>, wb.f <fctr>, wl.high <dbl>, wl.low <dbl>, y <dbl>

Function wb2tagged_spct returns a tagged spectrum, with two rows for each waveband, corresponding to the low and high wavelength boundaries, while function wb2rect_spct returns a spectrum with only one row per waveband, with w.length set to its midpoint but with additional columns xmin and xmax corresponding to the low and high wavelength boundaries of the wavebands.

Function is_tagged can be used to query if an spectrum is tagged or not, and function untag removes the tags.

tg.sun.spct
## Object: source_spct [524 x 6]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Time unit 1s
## 
## # A tibble: 524 × 6
##   w.length s.e.irrad s.q.irrad wl.color wb.color   wb.f
##      <dbl>     <dbl>     <dbl>    <chr>    <chr> <fctr>
## 1 280.0000         0         0  #000000     <NA>     NA
## 2 280.9231         0         0  #000000     <NA>     NA
## 3 281.8462         0         0  #000000     <NA>     NA
## 4 282.7692         0         0  #000000     <NA>     NA
## # ... with 520 more rows
is_tagged(tg.sun.spct)
## [1] TRUE
untg.sun.spct <- untag(tg.sun.spct)
is_tagged(untg.sun.spct)
## [1] FALSE

In the chuck above, we can see how this works, using in this case the default byref = FALSE which adds the tags to a copy of the spectrum object. In contrast, setting byref = TRUE adds the tags in place, or “by reference”“, to the spct object supplied as argument. Passing arguments by reference is unusual for R and is best avoided.

is_tagged(untg.sun.spct)
## [1] FALSE
untag(tg.sun.spct, byref = TRUE)
## Object: source_spct [524 x 3]
## Wavelength range 280 to 800 nm, step 1.023182e-12 to 1 nm 
## Time unit 1s
## 
## # A tibble: 524 × 3
##   w.length s.e.irrad s.q.irrad
##      <dbl>     <dbl>     <dbl>
## 1 280.0000         0         0
## 2 280.9231         0         0
## 3 281.8462         0         0
## 4 282.7692         0         0
## # ... with 520 more rows
is_tagged(untg.sun.spct)
## [1] FALSE

Summaries

Summaries can be calculated both from individual spectral objects (Table 10) and from collections of spectral objects (Table 11). They return a simpler object than the spectral data in their arguments. For example a vector of numeric values, possibly of length one, in the case of individual spectra, or a data frame containing one row of summary data for each spectrum the collection of multiple spectra supplied as argument.


Table 10. Summary methods for spectra. Key: + available, - not available.

methods raw/cps source response filter reflector object chroma
irrad +
e_irrad +
q_irrad +
fluence +
e_fluence +
q_fluence +
ratio +
e_ratio +
q_ratio +
qe_ratio +
eq_ratio +
response +
e_response +
q_response +
transmittance + +
absorptance + +
absorbance + +
reflectance + +
range + + + + + + +
min + + + + + + +
max + + + + + + +
stepsize + + + + + + +
spread + + + + + + +
midpoint + + + + + + +
labels + + + + + + +
summary + + + + + + +
peaks + + + + (+) (+)
valleys + + + + (+) (+)
integrate_spct + + + + + + +
average_spct + + + + + + +
color +


Table 11. Summary methods for collections of spectra. Key: + available, – not available, ms use msmsply() to apply function to collection members, d use msdply(), l use mslply to apply function to collection members, a use msaply to apply function to collection members.

methods raw/cps source response filter reflector object chroma
f_mspct + + + + + + +
irrad +
e_irrad +
q_irrad +
fluence +
e_fluence +
q_fluence +
ratio +
e_ratio +
q_ratio +
qe_ratio +
eq_ratio +
response +
e_response +
q_response +
transmittance + +
absorptance + +
absorbance + +
reflectance + +
range + + + + + + +
min + + + + + + +
max + + + + + + +
stepsize + + + + + + +
spread + + + + + + +
midpoint + + + + + + +
labels l l l l l l l
summary l l l l l l l
peaks + + + + (+) (+)
valleys + + + + (+) (+)
integrate_spct a, d, l a, d, l a, d, l a, d, l a, d, l a, d, l a, d, l
average_spct a, d, l a, d, l a, d, l a, d, l a, d, l a, d, l a, d, l
color +

Summary

Specialized definitions of summary and the corresponding print methods are available for spectral objects. Attributes "what.measured", "when.measured" and "where.measured" are included in the summary print out only if set in the spectral object summarized.

summary(sun.spct)
## Summary of object: source_spct [522 x 3]
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit: 1s
## 
##     w.length       s.e.irrad        s.q.irrad        
##  Min.   :280.0   Min.   :0.0000   Min.   :0.000e+00  
##  1st Qu.:409.2   1st Qu.:0.4115   1st Qu.:1.980e-06  
##  Median :539.5   Median :0.5799   Median :2.929e-06  
##  Mean   :539.5   Mean   :0.5160   Mean   :2.407e-06  
##  3rd Qu.:669.8   3rd Qu.:0.6664   3rd Qu.:3.154e-06  
##  Max.   :800.0   Max.   :0.8205   Max.   :3.375e-06
summary(two_suns.spct)
## Summary of object: source_spct [1,044 x 4]
## containg  2  spectra in long form
## Wavelength range 280 to 800 nm, step 0.9230769 to 1 nm
## Time unit: 1s
## 
##     w.length       s.e.irrad      spct.idx   s.q.irrad        
##  Min.   :280.0   Min.   :0.0000   a:522    Min.   :0.000e+00  
##  1st Qu.:409.0   1st Qu.:0.2471   b:522    1st Qu.:1.159e-06  
##  Median :539.5   Median :0.3494            Median :1.580e-06  
##  Mean   :539.5   Mean   :0.3870            Mean   :1.806e-06  
##  3rd Qu.:670.0   3rd Qu.:0.5799            3rd Qu.:2.928e-06  
##  Max.   :800.0   Max.   :0.8205            Max.   :3.375e-06

Wavelength

Individual spectra

The usual and a couple of new summary functions are available for spectra, but redefined to return wavelength based summaries in nanometres (nm).

range(sun.spct)
## [1] 280 800
min(sun.spct)
## [1] 280
max(sun.spct)
## [1] 800
midpoint(sun.spct)
## [1] 540
spread(sun.spct)
## [1] 520
stepsize(sun.spct)
## [1] 0.9230769 1.0000000

Collections of spectra


Most frequently used summary methods are implemented for collections of spectra. See the Table for those methods that need to be applied with functions msaply, msdply or mslply to members in a collection returning the results in an array (vector, or matrix), a data frame or a list object. In many cases depending of the class desired for the result, one can chose a suitable apply function, and sometimes it is best to use such a function, even when the corresponding method is implemented for collections of spectra.

Collections of spectra can be useful not only for time-series of spectra or spectral images, but also when dealing with a small group of related spectra. In the example below we show how to use a collection of spectra for calculating summaries. The spectra in a collection do not need to have been measured at the same wavelength values, or have the same number of rows or even of columns. Consequently, in many cases applying the wavelength summary functions described above to collections of spectra can be useful. The value returned is a data frame, with a number of data columns equal to the length of the returned value by the corresponding method for individual spectra.

filters.mspct <- filter_mspct(list(none = clear.spct,
                                   pet = polyester.spct,
                                   yellow = yellow_gel.spct))
range(filters.mspct)
## # A tibble: 3 × 3
##   spct.idx min.wl max.wl
##     <fctr>  <dbl>  <dbl>
## 1     none    100   5000
## 2      pet    190    800
## 3   yellow    190    800

Peaks and valleys

Individual spectra

Functions peaks and valleys take spectra as first argument and return a subset of the spectral object data corresponding to local maxima and local minima of the measured variable. span defines the width of the window used as a number of observations.

peaks(sun.spct, span = 51)
## Object: source_spct [3 x 2]
## Wavelength range 451 to 747 nm, step 44 to 252 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 3 × 2
##   w.length s.e.irrad
## *    <dbl>     <dbl>
## 1      451 0.8204633
## 2      495 0.7899872
## 3      747 0.5025733
valleys(sun.spct, span = 51)
## Object: source_spct [9 x 2]
## Wavelength range 358 to 761 nm, step 30 to 72 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 9 × 2
##   w.length s.e.irrad
## *    <dbl>     <dbl>
## 1      358 0.2544907
## 2      393 0.2422023
## 3      431 0.4136900
## 4      487 0.6511654
## # ... with 5 more rows

In the case of source_spct and response_spct methods unit.out can be used to force peaks to be searched using either energy or photon based spectral irradiance. The default is energy, or the option "photobiology.radiation.unit" if set.

peaks(sun.spct, span = 51, unit.out = "photon")
## Object: source_spct [7 x 2]
## Wavelength range 451 to 754 nm, step 36 to 90 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 7 × 2
##   w.length    s.q.irrad
## *    <dbl>        <dbl>
## 1      451 3.093155e-06
## 2      495 3.268822e-06
## 3      531 3.374912e-06
## 4      621 3.355564e-06
## # ... with 3 more rows

It is possible to approximately set the width of the windows in nanometres by using function step_size. However, here we simply use an odd number of wavelengths steps.

peaks(sun.spct, span = 21)
## Object: source_spct [18 x 2]
## Wavelength range 354 to 774 nm, step 11 to 51 nm 
## Measured on 2010-06-22 09:51:00 UTC
## Measured at 60.20942 N, 24.96424 E
## Time unit 1s
## 
## # A tibble: 18 × 2
##   w.length s.e.irrad
## *    <dbl>     <dbl>
## 1      354 0.3758625
## 2      366 0.4491898
## 3      378 0.4969714
## 4      416 0.6761818
## # ... with 14 more rows

Low level functions find_peaks, get_peaks and get_valleys take numeric vectors as arguments.

Collections of spectra

We can use msmsply() to extract the peaks of a collection of spectra.

msmsply(filters.mspct, peaks, span = 11)
## Object: filter_mspct [3 x 1]
## --- Member: none ---
## Object: filter_spct [0 x 2]
## 
## # A tibble: 0 × 2
## # ... with 2 variables: w.length <dbl>, Tfr <dbl>
## --- Member: pet ---
## Object: filter_spct [8 x 2]
## Wavelength range 453 to 648 nm, step 6 to 76 nm 
## 
## # A tibble: 8 × 2
##   w.length   Tfr
## *    <int> <dbl>
## 1      453 0.926
## 2      503 0.926
## 3      579 0.921
## 4      609 0.919
## # ... with 4 more rows
## --- Member: yellow ---
## Object: filter_spct [5 x 2]
## Wavelength range 642 to 755 nm, step 8 to 64 nm 
## 
## # A tibble: 5 × 2
##   w.length    Tfr
## *    <int>  <dbl>
## 1      642 0.8980
## 2      706 0.9005
## 3      714 0.9005
## 4      744 0.9015
## 5      755 0.9020
## 
## --- END ---

Two of the filters in the collection do not have peaks, and a spectrum object of length zero is returned for them.

Irradiance

Individual spectra

The code using spct objects is simple, to integrate the whole spectrum we can use

irrad(sun.spct)
##    Total 
## 269.1249 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"

and, to integrate a range of wavelengths, in the example, photosynthetically active radiation, we use the waveband PAR.wb we earlier defined.

irrad(sun.spct, PAR.wb)
##      PAR 
## 196.6343 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"

It is also valid to pass as argument for w.band a numeric range representing wavelengths in nanometres.

irrad(sun.spct, c(400, 700))
##  range.400.700 
##       196.6343 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"

The default for irrad, when no argument unit.out is supplied, is to return the irradiance value in energy irradiance units, unless the R option photobiology.radiation.unit is set.

Functions e_irrad and q_irrad save some typing, and always return the same type of spectral irradiance quantity, independently of global option photobiology.radiation.unit.

e_irrad(sun.spct, PAR.wb) # W m-2
##      PAR 
## 196.6343 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"
q_irrad(sun.spct, PAR.wb) * 1e6 # umol s-1 m-2
##      PAR 
## 894.1352 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "photon irradiance total"

It is also possible to supply a time unit to use as basis of expression for the returned value, but be aware that conversion into a logger time unit is only valid for sources like lamps, which have an output the remains constant in time.

irrad(sun.spct, PAR.wb, time.unit = "hour")
##      PAR 
## 707883.4 
## attr(,"time.unit")
## [1] "hour"
## attr(,"radiation.unit")
## [1] "energy irradiance total"
irrad(sun.spct, PAR.wb, time.unit = duration(8, "hours"))
##     PAR 
## 5663067 
## attr(,"time.unit")
## [1] "28800s (~8 hours)"
## attr(,"radiation.unit")
## [1] "energy irradiance total"

Using a shorter time unit than the original, yields an average value re-expressed on a new time unit base.

irrad(sun.daily.spct, PAR.wb, time.unit = "second")
##      PAR 
## 92.16251 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"

Lists of wavebands are also accepted as argument.

e_irrad(sun.spct, UV_bands.lst) # W m-2
##        UVB        UVA 
##  0.6445105 27.9842061 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"

These functions have an additional argument quantity, with default "total", which can take values controlling the output. The value “total” yields irradiance in \(W\,m^{-2}\), integrated over wavelengths for each waveband, while “average” yields the mean spectral irradiance within each waveband in \(W\,m^{-2}\,nm^{-1}\). The value “contribution” is relative to the irradiance for the whole spectrum, expressed as a fraction of one, while the value “relative” is relative to the sum of the irradiances for the different wavebands given as argument, also expressed as a fraction of one.

irrad(sun.spct, UV_bands.lst, quantity = "total")
##        UVB        UVA 
##  0.6445105 27.9842061 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"
irrad(sun.spct, UV_bands.lst, quantity = "contribution")
##         UVB         UVA 
## 0.002394838 0.103982226 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance contribution"
irrad(sun.spct, UV_bands.lst, quantity = "relative")
##        UVB        UVA 
## 0.02251273 0.97748727 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance relative"
irrad(sun.spct, UV_bands.lst, quantity = "average")
##        UVB        UVA 
## 0.01841458 0.32922595 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance average"

Collections of spectra

Collections of spectra can be useful not only for time-series of spectra or spectral images, but also when dealing with a small group of related spectra. In the example below we show how to use a collection of spectra for estimating irradiances under different filters set up in sunlight.

We reuse collection of filter spectra filters.mspct from an earlier section.

names(filters.mspct)
## [1] "none"   "pet"    "yellow"

We then convolve each filter’s spectral transmittance by the spectral irradiance of the light source.

filtered_sun <- convolve_each(filters.mspct, sun.spct)
irrad(filtered_sun, list(UVA.wb, PAR.wb))
## # A tibble: 3 × 3
##   spct.idx   irrad_UVA irrad_PAR
##     <fctr>       <dbl>     <dbl>
## 1     none 27.98420611  196.6343
## 2      pet 22.98282851  181.0213
## 3   yellow  0.03726539  106.9680

The code above can also be written as a single statement.

irrad(convolve_each(filters.mspct, sun.spct), list(UVA.wb, PAR.wb))
## # A tibble: 3 × 3
##   spct.idx   irrad_UVA irrad_PAR
##     <fctr>       <dbl>     <dbl>
## 1     none 27.98420611  196.6343
## 2      pet 22.98282851  181.0213
## 3   yellow  0.03726539  106.9680

It is also possible to use an apply function. Syntax parallels that of base R’s and package plyr’s. See sections apply functions and convolve for more details.

One thing to remember, is that operators in R are just normal functions with special names and call syntax. They can also be called with the usual function call syntax by enclosing their name in back quotes. We use this to pass as argument the multiplication operator * in a call to msmsply which returns, in this case, a source_multi_spct object. After this we just call the irrad method on the collection of spectra and obtain the result as a data frame with one row per spectrum and one column by waveband.

filtered_sun <- msmsply(filters.mspct, `*`, sun.spct)
irrad(filtered_sun, list(UVA.wb, PAR.wb))
## # A tibble: 3 × 3
##   spct.idx   irrad_UVA irrad_PAR
##     <fctr>       <dbl>     <dbl>
## 1     none 27.98420611  196.6343
## 2      pet 22.98282851  181.0213
## 3   yellow  0.03726539  106.9680

Fluence

Individual spectra

The calculation of fluence values (time-integrated irradiance) is identical to that for irradiance, except that a exposure.time argument needs to be supplied. The exposure time must be a lubridate::duration, but any argument accepted by as.duration can also be used. Functions fluence, e_fluence and q_fluence correspond to irrad, e_irrad and q_irrad,

fluence(sun.spct, exposure.time = duration(1, "hours"))
##    Total 
## 968849.6 
## attr(,"radiation.unit")
## [1] "energy fluence (J m-2)"
## attr(,"exposure.duration")
## [1] "3600s (~1 hours)"
fluence(sun.spct, exposure.time = 3600) # seconds
## converting 'time.unit' 3600 into a lubridate::duration
##    Total 
## 968849.6 
## attr(,"radiation.unit")
## [1] "energy fluence (J m-2)"
## attr(,"exposure.duration")
## [1] 3600

and, to obtain the photon fluence for a range of wavelengths, in the example, photosynthetically active radiation, we use the PAR.wb waveband object earlier defined, for 25 minutes of exposure.

q_fluence(sun.spct, PAR.wb, exposure.time = duration(25, "minutes"))
##      PAR 
## 1.341203 
## attr(,"radiation.unit")
## [1] "photon fluence (mol m-2)"
## attr(,"exposure.duration")
## [1] "1500s (~25 minutes)"

Photon and energy ratios

Individual spectra

The functions described here, in there simplest use, calculate a ratio between two wavebands. The function q_ratio returning photon ratios. However both waveband parameters can take lists of wavebands as arguments, with normal recycling rules in effect. The corresponding function e_ratio returns energy ratios.

q_ratio(sun.spct, UVB.wb, PAR.wb)
##  UVB: PAR(q:q) 
##    0.001873724 
## attr(,"radiation.unit")
## [1] "q:q ratio"
q_ratio(sun.spct, list(UVC.wb, UVB.wb, UVA.wb, UV.wb))
##       UVB: Total(q:q)       UVA: Total(q:q)  UV.tr.lo: Total(q:q) 
##           0.001334593           0.067567343           0.068901936 
## attr(,"radiation.unit")
## [1] "q:q ratio"
q_ratio(sun.spct, UVB.wb, list(UV.wb, PAR.wb))
##  UVB: UV.tr.lo(q:q)       UVB: PAR(q:q) 
##         0.019369458         0.001873724 
## attr(,"radiation.unit")
## [1] "q:q ratio"

Function qe_ratio, has only one waveband parameter, and returns the photon to energy ratio, while its complement eq_ratio returns the energy to photon ratio.

qe_ratio(sun.spct, list(UVB.wb, PAR.wb))
##    q:e( UVB)    q:e( PAR) 
## 2.599434e-06 4.547199e-06 
## attr(,"radiation.unit")
## [1] "q:e ratio"

Collections of spectra

q_ratio(filtered_sun, list(UVB.wb, UVA.wb, PAR.wb))
## # A tibble: 3 × 4
##   spct.idx `q_ratio_UVB:Total(q:q)` `q_ratio_UVA:Total(q:q)`
##     <fctr>                    <dbl>                    <dbl>
## 1     none             1.334593e-03             0.0675673430
## 2      pet             3.700698e-05             0.0614239749
## 3   yellow             2.540715e-06             0.0001339225
## # ... with 1 more variables: `q_ratio_PAR:Total(q:q)` <dbl>

Normalized difference indexes

Individual spectra

These indexes are frequently used to summarize reflectance data, for example in remote sensing the NDVI (normalized difference vegetation index). Here we give an unusual example to demonstrate that function normalized_diff_ind() can be used to calculate, or define any similar index.

normalized_diff_ind(sun.spct,
                    waveband(c(400, 700)), waveband(c(700, 1100)),
                    irrad)
## NDI  irrad  [ 400.700 ] - [ 700.1100 ] 
##                              0.6352382

Transmittance, reflectance, absorptance and absorbance

Individual spectra

The functions transmittance, absorptance and absorbance take filter_spct as argument, while function reflectance takes reflector_spct objects as argument. Functions transmittance, reflectance and absorptance are also implemented for object_spct. These functions return as default an average value for these quantities assuming a light source with a flat spectral energy output, but this can be changed as described above for irrad().

transmittance(polyester.spct, list(UVB.wb, UVA.wb, PAR.wb))
##         UVB         UVA         PAR 
## 0.007671429 0.782682353 0.920245000 
## attr(,"Tfr.type")
## [1] "total"
## attr(,"radiation.unit")
## [1] "transmittance average"

It is more likely that we would like to calculate these values with reference to light of a certain spectral quality. This needs to be calculated by hand, which is not difficult.

irrad(sun.spct * polyester.spct, list(UVB.wb, UVA.wb, PAR.wb, wb.trim = TRUE)) /
  irrad(sun.spct, list(UVB.wb, UVA.wb, PAR.wb, wb.trim = TRUE))
##        UVB        UVA        PAR 
## 0.02506541 0.82127856 0.92059898 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy irradiance total"

Collections of spectra

Here we construct a collection of filter spectra, and then we calculate the transmittance of these filters for two wavebands, obtaining the results as a data frame, with one row per filter, and one column per waveband. We reuse once more filters.mspct from an earlier section.

transmittance(filters.mspct, list(UVA.wb, PAR.wb))
## # A tibble: 3 × 3
##   spct.idx transmittance_UVA transmittance_PAR
##     <fctr>             <dbl>             <dbl>
## 1     none       1.000000000         1.0000000
## 2      pet       0.782682353         0.9202450
## 3   yellow       0.001601353         0.5655132

Integrated response

Individual spectra

The functions response, e_response and q_response take response_spct objects as arguments, and return the integrated value for each waveband (integrated over wavelength) assuming a light source with a flat spectral energy or photon output respectively. If no waveband is supplied as argument, the whole spectrum is integrated.

response(photodiode.spct)
##    Total 
## 25.37446 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy response total"

When a waveband, or list of wavebands, is supplied the response is calculated for the wavebands.

e_response(photodiode.spct, list(UVB.wb, UVA.wb))
##       UVB       UVA 
## 0.7002548 5.9818181 
## attr(,"time.unit")
## [1] "second"
## attr(,"radiation.unit")
## [1] "energy response total"

This function has an additional argument quantity, with default "total", as described for irrad().

Collections of spectra

sensors <- response_mspct(list(GaAsP = photodiode.spct,
                               CCD = ccd.spct))
response(sensors, list(UVB.wb, UVA.wb, PAR.wb), quantity = "contribution")
## # A tibble: 2 × 4
##   spct.idx response_UVB response_UVA response_PAR.tr.hi
##     <fctr>        <dbl>        <dbl>              <dbl>
## 1    GaAsP   0.02759684   0.23574173          0.7152528
## 2      CCD   0.02146115   0.06323201          0.4098033

Integration over wavelengths

When we need to integrate some numeric variable stored in a spectral object we can use functions integrate_spct or average_spct.

Calculation from individual spectra

We can integrate the values of arbitrary numeric columns other than w.length in an spectral object. All spectral classes are derived from generic_spct, so the examples in this section apply to objects of any of the derived spectral classes as well.

integrate_spct(sun.spct)
##      e.irrad      q.irrad 
## 2.691249e+02 1.255336e-03

The function average_spct integrates every column holding numeric values from a spectrum object, except for w.length, and divides the result by the spread or width of the wavelength range integrated, returning a value expressed in the same units as the spectral data.

average_spct(sun.spct)
##      e.irrad      q.irrad 
## 5.175479e-01 2.414107e-06

RGB colours

Two functions allow calculation of simulated colour of light sources as R colour definitions. Three different functions are available, one for monochromatic light taking as argument wavelength values, and one for polychromatic light taking as argument spectral energy irradiances and the corresponding wave length values. The third function can be used to calculate a representative RGB colour for a band of the spectrum represented as a range of wavelength, based on the assumption of a flat energy irradiance across the range. By default CIE coordinates for typical human vision are used, but the functions have a parameter that can be used for supplying a different chromaticity definition.

Examples for monochromatic light:

w_length2rgb(550) # green
## wl.550.nm 
## "#00FF00"
w_length2rgb(630) # red
## wl.630.nm 
## "#FF0000"
w_length2rgb(c(550, 630, 380, 750)) # vectorized
## wl.550.nm wl.630.nm wl.380.nm wl.750.nm 
## "#00FF00" "#FF0000" "#000000" "#000000"

Examples for wavelength ranges:

w_length_range2rgb(c(400,700))
## 400-700 nm 
##  "#735B57"

Examples for spectra as vectors, in this case for the solar spectrum:

with(sun.spct, s_e_irrad2rgb(w.length, s.e.irrad))
## [1] "#544F4B"
with(sun.spct, s_e_irrad2rgb(w.length, s.e.irrad, sens = ciexyzCMF2.spct))
## [1] "#544F4B"

Examples with source_spct objects.

rgb_spct(sun.spct)
## [1] "#544F4B"
rgb_spct(sun.spct, sens = ciexyzCMF2.spct)
## [1] "#544F4B"

And also a color method for source_spct.

color(sun.spct)
## source CMF 
##  "#544F4B"
color(sun.spct * yellow_gel.spct)
## source CMF 
##  "#946000"