Author: Kang Yu (GitHub Profile)
Date: 2025-03-19

# Introduction to visa

#### Kang Yu (GitHub Profile)

#### 2025-03-19

Imaging Spectroscopy (also known as Hyperspectral Remote Sensing, HRS) technology and data are increasingly used in environmental sciences, and nowadays much more beyond that, thus requiring accessible data and analytical tools (especially open source) for students and scientists with a diverse background. Therefore, I come up with such a idea since i was a PhD student at the University of Cologne, inspired by the growing community of R and R users. I have been mainly working with spectral data of plants, and that is reason i use the name VISA for this package, with the aim to facilitate the use of imaging spectroscopy techniques and data for the extraction of vegetation signatures for plant stresses and biodiversity.

Future development of this tool has a long-term goal to include: i) implement the state-of-the-art applications of vegetation spectral indicators, ii) provide a platform to share vegetation spectral data to address certain questions of interest or applications in a broad context, and iii) make it compatible with more data formats and tools, such as the r package hsdar1.

Currently, `visa` can be installed:

- from CRAN, either from command line ‘install.packages(“visa”)’ or from the Rstudio Packages installation tab.

- from my GitHub repository visa, using `devtools::install_github("kang-yu/visa")`.

This vignette will introduces the design and features of `visa` from the following aspects:

- Data
- Functions
- Compatibility

## New features in the updates

- Compute three-band Ratio of Band Difference (RBD) \(RBD = (\lambda_k - \lambda_j)/(\lambda_j - \lambda_i)\), and plotting of the three dimensional output array.

## Data

The `visa` package intends to simplify the use and reduce the limit of data format and structure. `visa` uses two data formats, and a hacking use of R’s `data.frame` and, a S42 class specifically for `visa`.

### Built on `data.frame`

Why i call it a hacking use is because a data.frame is a table organized by variables, and it is ideal to store every spectral band in as a variable. Imaging that you have thousands of columns and you have to refer to thousands of bands when using data.frame. Then, why not just store all the spectral bands, ie. the spectral matrix in a single variable, like the example data `NSpec.DF`.

This will be ease your coding for analysis, and you write your argument as ‘y ~ spectra’ instead of ‘y ~ band1 + band2 + band3 + … ’

    # check the data type of `NSpec.DF`
    library(visa)
    #> Registered S3 methods overwritten by 'ggpp':
    #>   method                  from
    #>   heightDetails.titleGrob ggplot2
    #>   widthDetails.titleGrob  ggplot2
    data(NSpec.DF)
    class(NSpec.DF)
    #> [1] "data.frame"
    class(NSpec.DF$spectra)
    #> [1] "matrix" "array"
    str(NSpec.DF$spectra)
    #>  num [1:19, 1:651] 0.01078 0.01059 0.00942 0.0104 0.01047 ...
    #>  - attr(*, "dimnames")=List of 2
    #>   ..$ : NULL
    #>   ..$ : chr [1:651] "350 nm" "351 nm" "352 nm" "353 nm" ...

Storing spectra matrix as a variable in data.frame

### S4 class `Spectra` and `SpectraDatabase`

There are already a lot of r packages for spectral data analysis, and some of them use the S4 class, e.g. the `hsdar` package. `visa` also supports the S4 format but in a simplified version, using only five slots currently.

    # check the data type of `NSpec.Lib`
    class(NSpec.Lib)
    #> [1] "Spectra"
    #> attr(,"package")
    #> [1] "visa"
    class(NSpec.Lib@spectra)
    #> [1] "matrix" "array"
    str(NSpec.Lib@spectra)
    #>  num [1:19, 1:651] 0.01078 0.01059 0.00942 0.0104 0.01047 ...
    #>  - attr(*, "dimnames")=List of 2
    #>   ..$ : NULL
    #>   ..$ : chr [1:651] "350 nm" "351 nm" "352 nm" "353 nm" ...

Notice that the small difference of accessing data in two types of data, i.e., using `$` and `@`, respectively.

## Functions

### Computing correlation matrix between 2-band NSR and another variable

The first idea of writing this package was to compute the correlation matrix for the thorough analysis of correlations between, on one hand, the combinations of spectral bands, and on the other hand, the vegetation variables of interest.

Here gives the example using the `cm.nsr` function, which can be used for non-spectra data as well.

    library(visa)
    data(NSpec.DF)
    x <- NSpec.DF$N # nitrogen
    S <- NSpec.DF$spectra[, seq(1, ncol(NSpec.DF$spectra), 10)] # resampled to 10 nm steps
    cm <- cm.nsr(S, x, cm.plot = TRUE)
    #> [1] "The max value of R^2 is 0.5333"
    #> [1] "i_460" "j_450"

### Plotting correlation matrix (2D array)

The correlation matrix plot is the plot of correlation coefficients (r/r2) by bands in x- and y-axis.

    # use the output from last example
    # cm <- cm.nsr(S, x)
    # Plotting the correlation matrix
    #ggplot.cm(cm) # deprecated

    plt.2dcm(cm) # new function replacing ggplot.cm
    #> [1] "The max value of R^2 is 0.5333"
    #> [1] "i_460" "j_450"

Plot of correlation matrix

#### More Examples and Details

The computation of SR and NSR follow the equations, e.g.:

##### 2-band combinations

\(SR = \lambda_i / \lambda_j\) \(NSR = (\lambda_i - \lambda_j)/(\lambda_i + \lambda_j)\)

To know more about the NDVI, please also check on Wikipedia3.

##### 3-band combinations

\(RBD = (\lambda_k - \lambda_j)/(\lambda_j - \lambda_i)\)

#### Example data `NSpec.Lib`

The first type is the ‘NSpec.Lib’ in the default S4 class ‘Spectra’.

    library(visa)
    # check the data type
    class(NSpec.Lib)

[1] “Spectra” attr(,“package”) [1] “visa”

    # data structure
    # str(NSpec.Lib)
    # print the first 10 columns
    knitr::kable(head(NSpec.Lib@spectra[,1:10]))

350 nm | 351 nm | 352 nm | 353 nm | 354 nm | 355 nm | 356 nm | 357 nm | 358 nm | 359 nm
--- | --- | --- | --- | --- | --- | --- | --- | --- | ---
0.0107850 | 0.0103620 | 0.0105130 | 0.0107780 | 0.0105690 | 0.0104170 | 0.0104270 | 0.0105240 | 0.0104590 | 0.0103860
0.0105900 | 0.0101850 | 0.0101270 | 0.0102320 | 0.0101690 | 0.0101270 | 0.0101720 | 0.0102500 | 0.0101840 | 0.0102270
0.0094211 | 0.0088921 | 0.0091092 | 0.0095035 | 0.0093468 | 0.0090576 | 0.0089967 | 0.0091336 | 0.0090165 | 0.0089895
0.0104040 | 0.0098426 | 0.0098587 | 0.0101030 | 0.0100700 | 0.0100920 | 0.0100790 | 0.0099628 | 0.0097052 | 0.0096571
0.0104650 | 0.0102510 | 0.0101960 | 0.0102150 | 0.0101590 | 0.0098056 | 0.0097447 | 0.0099987 | 0.0099463 | 0.0097695
0.0123460 | 0.0122110 | 0.0121620 | 0.0121480 | 0.0120900 | 0.0121430 | 0.0120990 | 0.0119900 | 0.0121290 | 0.0121080

#### Example data `NSpec.DF`

The second type is a data.frame format, i.e., `NSpec.DF`.

    # check the data type
    class(NSpec.DF)

[1] “data.frame”

    # check whether it contains the same data as 'NSpec.Lib'
    knitr::kable(head(NSpec.DF$spectra[,1:10]))

350 nm | 351 nm | 352 nm | 353 nm | 354 nm | 355 nm | 356 nm | 357 nm | 358 nm | 359 nm
--- | --- | --- | --- | --- | --- | --- | --- | --- | ---
0.0107850 | 0.0103620 | 0.0105130 | 0.0107780 | 0.0105690 | 0.0104170 | 0.0104270 | 0.0105240 | 0.0104590 | 0.0103860
0.0105900 | 0.0101850 | 0.0101270 | 0.0102320 | 0.0101690 | 0.0101270 | 0.0101720 | 0.0102500 | 0.0101840 | 0.0102270
0.0094211 | 0.0088921 | 0.0091092 | 0.0095035 | 0.0093468 | 0.0090576 | 0.0089967 | 0.0091336 | 0.0090165 | 0.0089895
0.0104040 | 0.0098426 | 0.0098587 | 0.0101030 | 0.0100700 | 0.0100920 | 0.0100790 | 0.0099628 | 0.0097052 | 0.0096571
0.0104650 | 0.0102510 | 0.0101960 | 0.0102150 | 0.0101590 | 0.0098056 | 0.0097447 | 0.0099987 | 0.0099463 | 0.0097695
0.0123460 | 0.0122110 | 0.0121620 | 0.0121480 | 0.0120900 | 0.0121430 | 0.0120990 | 0.0119900 | 0.0121290 | 0.0121080

#### Accessing data

The `spectra` function access the spectral data stored in the Spectra.Dataframe or Spectra.Library formats.

    #library(visa)
    #data(NSpec.DF)
    spec <- spectra(NSpec.DF)
    # check whether it contains the same data as 'NSpec.Lib'
    knitr::kable(head(spec[,1:10]))

350 nm | 351 nm | 352 nm | 353 nm | 354 nm | 355 nm | 356 nm | 357 nm | 358 nm | 359 nm
--- | --- | --- | --- | --- | --- | --- | --- | --- | ---
0.0107850 | 0.0103620 | 0.0105130 | 0.0107780 | 0.0105690 | 0.0104170 | 0.0104270 | 0.0105240 | 0.0104590 | 0.0103860
0.0105900 | 0.0101850 | 0.0101270 | 0.0102320 | 0.0101690 | 0.0101270 | 0.0101720 | 0.0102500 | 0.0101840 | 0.0102270
0.0094211 | 0.0088921 | 0.0091092 | 0.0095035 | 0.0093468 | 0.0090576 | 0.0089967 | 0.0091336 | 0.0090165 | 0.0089895
0.0104040 | 0.0098426 | 0.0098587 | 0.0101030 | 0.0100700 | 0.0100920 | 0.0100790 | 0.0099628 | 0.0097052 | 0.0096571
0.0104650 | 0.0102510 | 0.0101960 | 0.0102150 | 0.0101590 | 0.0098056 | 0.0097447 | 0.0099987 | 0.0099463 | 0.0097695
0.0123460 | 0.0122110 | 0.0121620 | 0.0121480 | 0.0120900 | 0.0121430 | 0.0120990 | 0.0119900 | 0.0121290 | 0.0121080

The `wavelength` function access the wavelengths of bands stored in the Spectra.Dataframe or Spectra.Library formats.

    #library(visa)
    #data(NSpec.DF)
    spec <- wavelength(NSpec.DF)
    # check whether it contains the same data as 'NSpec.Lib'
    str(spec)

num [1:651] 350 351 352 353 354 355 356 357 358 359 …

## Compatibility

### Data format conversion

The `as.spectra` function save spectral data in the format of Spectra.Library.

    slib <- as.spectra(spectra = matrix(rnorm(100), nrow = 10),
                       wavelength = 1:10,
                       w.unit = "nm",
                       data = data.frame(y=rnorm(10))
                       )
    str(slib)

Formal class ‘Spectra’ [package “visa”] with 4 slots ..@ spectra : num [1:10, 1:10] 1.6184 -1.1682 -0.4217 0.0904 1.9674 … ..@ wavelength: int [1:10] 1 2 3 4 5 6 7 8 9 10 ..@ w.unit : chr “nm” ..@ data :‘data.frame’: 10 obs. of 1 variable: .. ..$ y: num [1:10] -0.0685 -0.2184 0.3457 -0.1863 1.1664 …

The `as.spectra.dataframe` function save spectral data in the format of Spectra.Dataframe. The Spectra.Dataframe is actually a data.frame.

    s.df <- as.spectra.dataframe(spectra = matrix(rnorm(100), nrow = 10),
                                 wavelength = 1:10,
                                 w.unit = "nm",
                                 data = data.frame(y=rnorm(10))
                                 )
    str(s.df)

‘data.frame’: 10 obs. of 2 variables: $ y : num 0.8399 -0.7426 0.9641 0.6418 0.0274 … $ spectra: num [1:10, 1:10] -0.572 -1.097 1.83 -0.445 -0.717 … ..- attr(*, “dimnames”)=List of 2 .. ..$ : NULL .. ..$ : chr [1:10] “1 nm” “2 nm” “3 nm” “4 nm” …

### Future development

Regarding compatibility for future development, special focuses will be put on:

- spectral image analysis
- spatial data integration
- Functions for simplifying integrated analysis such as using radiative transfer models and machine learning models

1. Lukas W. Lehnert, Hanna Meyer, Joerg Bendix (2018). hsdar: Manage, analyse and simulate hyperspectral data in R↩︎

2. The S4 object system http://adv-r.had.co.nz/S4.html↩︎

3. Normalized difference vegetation index (https://en.wikipedia.org/wiki/Normalized_difference_vegetation_index)↩︎