Integration of EPR Spectrum/Data for Quantitative Analysis
Source:R/eval_integ_EPR_Spec.R
      eval_integ_EPR_Spec.RdEvaluates integrals of EPR spectra (based on the pracma::cumtrapz function)
 depending on input data => either corresponding to derivative
 or single integrated EPR signal form, with the option to correct the single integral baseline
 by the polynomial fit of the poly.degree level. For EPR time/temperature/...etc spectral series,
 (data frame must be available in tidy/long table format),
 there is an option to integrate all EPR spectra literally in one step (see also Examples),
 similarly to function available in acquisition/processing software at EPR spectrometers.
Usage
eval_integ_EPR_Spec(
  data.spectr,
  B = "B_G",
  B.unit = "G",
  Intensity = "dIepr_over_dB",
  lineSpecs.form = "derivative",
  Blim = NULL,
  correct.integ = FALSE,
  BpeaKlim = NULL,
  poly.degree = NULL,
  sigmoid.integ = FALSE,
  output.vecs = FALSE
)Arguments
- data.spectr
- Spectrum data frame/table object with magnetic flux density (in - mTor- Gor- T) and that of the derivative or already single integrated intensity.- indexcolumn may be already present as well.
- B
- Character string, pointing to magnetic flux density column header (in the original - data.spectr) either in- millitesla/- teslaor in- Gauss, such as- B = "B_mT"or- B = "B_G"(default) or- B = "Field"...etc.
- B.unit
- Character string pointing to unit of magnetic flux density, which is to be presented on \(x(B)\)-axis of the EPR spectrum, like - "G"("Gauss"),- "mT"("millitesla") or- "T"("Tesla"). Default:- B.unit = "G".
- Intensity
- Character string, pointing to column name of either derivative (e.g. - Intensity = "dIepr_over_dB", default) or single integrated EPR spectrum (e.g.- Intensity = "single_Integrated") within the actual- data.spectr.
- lineSpecs.form
- Character string, describing either - "derivative"(default) or- "integrated"(i.e.- "absorption", which can be used as well) line form of the analyzed EPR spectrum/data.
- Blim
- Numeric vector, magnetic flux density in - mT/- G/- Tcorresponding to lower and upper limit of the selected \(B\)-region, e.g.- Blim = c(3495.4,3595.4). Default:- Blim = NULL(corresponding to the entire spectral \(B\)-range).
- correct.integ
- Logical, whether to correct the integral by baseline polynomial model fit. Default: - correct.integ = FALSE.
- BpeaKlim
- Numeric vector, magnetic flux density in - mT/- G/- Tcorresponding to lower and upper limit of the SELECTED \(B\)-PEAK REGION, e.g.- BpeaKlim = c(3535.4,3555.4). This is the region (without the peak), which is actually not considered for the baseline fit.
- poly.degree
- Numeric, degree of the polynomial function used to fit baseline under the single integrated curve of the original EPR spectrum (see also - BpeaKlim).
- sigmoid.integ
- Logical, whether to involve (column in data frame) double integral or single integral (if the - data.spectrand- Intesityare already in single integrated form), in sigmoid shape, which is required for the quantitative analysis, default:- sigmoid.integ = FALSE.
- output.vecs
- Logical, whether the "integral" columns are presented within the original - data.spectrdata frame (- output.vecs = FALSE, default) or called as a vectors or list for additional processing of spectral data series by dplyr (see- Valueand- Examples).
Value
The integration results may be divided into following types, depending on the above-described
  arguments. Generally, they are either data frames, including the original data and the integrals
  (output.vecs = FALSE) or vectors/vectors list, corresponding to individual baseline
  corrected/uncorrected integrals (output.vecs = TRUE). This is especially useful
  for spectral (time) series EPR data, which can be handily processed
  by the group_by using the
  pipe operators (%>%).
- Data frame/table, including EPR spectral data (general - Intensity(integrated or derivative) vs \(B\)) as well as its corresponding- single(column- single_Integ) integral. This is the case if only a single uncorrected integral is required.
- Data frame/table with single integral/intensity already corrected by a certain degree of polynomial baseline (fitted to experimental baseline without peak). Single integrals are related either to derivative or already integrated EPR spectra where corrected integral column header is denoted as - single_Integ_correct. This is the case if- correct.integ = TRUEand- sigmoid.integ = FALSE+- output.vecs = FALSE.
- Data frame with - singleand- double/sigmoidintegral column/variable (- sigmoid_Integ), essential for the quantitative analysis. For such case it applies:- output.vecs = FALSEand- correct.integ = FALSE.
- Data frame in case of - correct.integ = TRUE,- sigmoid.integ = TRUEand- output.vecs = FALSE. It contains the original data frame columns + corrected single integral (- single_Integ_correct) and double/sigmoid integral (- sigmoid_Integ) which is evaluated from the baseline corrected single one. Therefore, such double/sigmoid integral is suitable for the accurate determination of radical (paramagnetic centers) amount.
- Numeric vector, corresponding to baseline uncorrected/corrected single integral in case of - sigmoid.integ = FALSE+- output.vecs = TRUE.
- List of numeric vectors (if - output.vecs = TRUE) corresponding to:- single
- Corrected or uncorrected single integral (in case of derivative form), depending on the - correct.integargument.
- sigmoid
- Double integral (in case of derivative form) or single integral (in case of integrated spectral form) for quantitative analysis. 
 
Details
The relative error of the cumulative trapezoidal (cumtrapz) function is minimal,
 usually falling into the range of \(\langle 1,5\rangle\,\%\) or even lower, depending on the spectral
 data resolution (see Epperson JF (2013) and Seeburger P (2023) in the References).
 Therefore, the better the resolution, the more accurate the integral. If the initial EPR spectrum displays low
 signal-to-noise ratio, the integral often looses its sigmoid-shape
 and thus, the EPR spectrum has to be either simulated (see also vignette("functionality"))
 or smoothed by the smooth_EPR_Spec_by_npreg, prior to integration. Afterwards,
 integrals are evaluated from the simulated or smoothed EPR spectra.
 For the purpose of quantitative analysis, the integrals are evaluated using the B.units = "G"
 (see Arguments). Hence, depending on \(B\)-unit (G or mT or T) each resulting integral
 column have to be optionally (in case of mT or T) multiplied by the factor of 10 or 10000,
 respectively, because \(1\,\text{mT}\equiv 10\,\text{G}\) and \(1\,\text{T}\equiv 10^4\,\text{G}\).
 Such corrections are already included in the function/script.
 Instead of "double integral/integ." the term "sigmoid integral/integ." is used. "Double integral"
 in the case of originally single integrated EPR spectrum (see data.spectr
 and Intensity) is confusing. In such case, the EPR spectrum is integrated just once.
References
Weber RT (2011). Xenon User's Guide. Bruker BioSpin Manual Version 1.3, Software Version 1.1b50.
Hans W. Borchers (2023). pracma: Practical Numerical Math Functions. R package version 2.4.4, https://cran.r-project.org/web/packages/pracma/index.html.
Seeburger P (2023). “Numerical Integration - Midpoint, Trapezoid, Simpson's rule”, http://tinyurl.com/trapz-integral.
Svirin A (2023). “Calculus, Integration of Functions-Trapezoidal Rule”, https://math24.net/trapezoidal-rule.html.
Epperson JF (2013). An Introduction to Numerical Methods and Analysis. Wiley and Sons, ISBN 978-1-118-36759-9, https://books.google.cz/books?id=310lAgAAQBAJ.
See also
Other Evaluations and Quantification:
eval_kinR_EPR_modelFit(),
eval_kinR_ODE_model(),
quantify_EPR_Abs(),
quantify_EPR_Norm_const()
Examples
## loading the built-in package example
## time series EPR spectra:
triarylamine.decay.series.dsc.path <-
load_data_example(file =
        "Triarylamine_radCat_decay_series.DSC")
triarylamine.decay.series.asc.path <-
load_data_example(file =
        "Triarylamine_radCat_decay_series.zip")
unzip(triarylamine.decay.series.asc.path,
      exdir = tempdir()
      )
## loading the kinetics:
triarylamine.decay.series.data <-
  readEPR_Exp_Specs_kin(name.root =
    "Triarylamine_radCat_decay_series",
    dir_ASC = tempdir(),
    dir_dsc_par =
      system.file("extdata",
                  package = "eprscope")
   )
#
## select the first spectrum
triarylamine.decay.series.data1st <-
   triarylamine.decay.series.data$df %>%
     dplyr::filter(time_s ==
       triarylamine.decay.series.data$time[1])
#
## integrate the first spectrum with default arguments
triarylamine.decay.data1st.integ01 <-
  eval_integ_EPR_Spec(triarylamine.decay.series.data1st)
#
## data frame preview
head(triarylamine.decay.data1st.integ01)
#>          B_G time_s  dIepr_over_dB      B_mT  single_Integ
#>        <num>  <num>          <num>     <num>         <num>
#> 1: 3390.0000      6  1.3629316e-05 339.00000 1.0353596e-04
#> 2: 3390.0833      6 -1.0134169e-06 339.00833 1.0406162e-04
#> 3: 3390.1667      6 -1.9794802e-05 339.01667 1.0319461e-04
#> 4: 3390.2500      6 -2.9826537e-05 339.02500 1.0112706e-04
#> 5: 3390.3333      6 -1.6870754e-05 339.03333 9.9181337e-05
#> 6: 3390.4167      6  2.5629187e-06 339.04167 9.8585177e-05
#
## integration (including baseline correction)
## of the 1st spectrum from the series
triarylamine.decay.data1st.integ02 <-
  eval_integ_EPR_Spec(triarylamine.decay.series.data1st,
    ## limits obtained from interactive spectrum:
    BpeaKlim = c(3471.5,3512.5),
    Blim = c(3425,3550),
    correct.integ = TRUE,
    poly.degree = 3,
    sigmoid.integ = TRUE
    )
#
## data frame preview
head(triarylamine.decay.data1st.integ02)
#> # A tibble: 6 × 8
#>     B_G time_s dIepr_over_dB  B_mT single_Integ baseline_Integ_fit
#>   <dbl>  <dbl>         <dbl> <dbl>        <dbl>              <dbl>
#> 1 3425       6   -0.0000160   342.    0.0000326          0.0000227
#> 2 3425.      6   -0.00000338  343.    0.0000317          0.0000227
#> 3 3425.      6    0.0000173   343.    0.0000323          0.0000226
#> 4 3425.      6    0.0000157   343.    0.0000337          0.0000226
#> 5 3425.      6   -0.0000271   343.    0.0000332          0.0000226
#> 6 3425.      6    0.00000128  343.    0.0000322          0.0000225
#> # ℹ 2 more variables: single_Integ_correct <dbl>, sigmoid_Integ <dbl>
#
## plot the single integrated EPR spectrum,
## including baseline correction
plot_EPR_Specs(triarylamine.decay.data1st.integ02,
               x = "B_G",
               x.unit = "G",
               Intensity = "single_Integ_correct",
               lineSpecs.form = "integrated"
             )
 #
## plot, corresponding to double/sigmoid integral,
## which is related to corrected single integral
plot_EPR_Specs(triarylamine.decay.data1st.integ02,
               x = "B_G",
               x.unit = "G",
               Intensity = "sigmoid_Integ",
               lineSpecs.form = "integrated"
             )
#
## plot, corresponding to double/sigmoid integral,
## which is related to corrected single integral
plot_EPR_Specs(triarylamine.decay.data1st.integ02,
               x = "B_G",
               x.unit = "G",
               Intensity = "sigmoid_Integ",
               lineSpecs.form = "integrated"
             )
 #
## vectorized output of the uncorrected `sigmoid_integral`
triarylamine.decay.data1st.integ03 <-
  eval_integ_EPR_Spec(triarylamine.decay.series.data1st,
                      sigmoid.integ = TRUE,
                      output.vecs = TRUE)[["sigmoid"]]
#
## preview of the first 6 values
triarylamine.decay.data1st.integ03[1:6]
#> [1] 0.0000000e+00 8.6498993e-06 1.7285576e-05 2.5798979e-05 3.4145162e-05
#> [6] 4.2385433e-05
#
## incorporation of vectorized integration into
## data "pipe" ("%>%") `dplyr` processing of EPR spectral
## time series, creating column with `sigmoid` integral
## where its corresponding single integral (intensity)
## has undergone a baseline correction, finally the max. value
## of all sigmoid integrals along with the time is
## summarized in data frame for quantitative kinetic analysis
triarylamine.decay.data.integs <-
  triarylamine.decay.series.data$df %>%
  dplyr::group_by(time_s) %>%
  dplyr::filter(dplyr::between(B_G,3425,3550)) %>%
  dplyr::mutate(sigmoid_Integ =
    eval_integ_EPR_Spec(dplyr::pick(B_G,dIepr_over_dB),
                        correct.integ = TRUE,
                        BpeaKlim = c(3471.5,3512.5),
                        poly.degree = 3,
                        sigmoid.integ = TRUE,
                        output.vecs = TRUE)$sigmoid
                       ) %>%
  dplyr::summarize(Area = max(sigmoid_Integ))
## in such case `Blim` range is not defined by `eval_integ_EPR_Spec`,
## the `Blim = NULL` and `dplyr::between()` must be set !!!
#
## preview of the final data frame
head(triarylamine.decay.data.integs)
#> # A tibble: 6 × 2
#>   time_s   Area
#>    <dbl>  <dbl>
#> 1      6 0.0169
#> 2     21 0.0179
#> 3     36 0.0169
#> 4     51 0.0172
#> 5     66 0.0184
#> 6     81 0.0158
#
## preview of the simple plot
ggplot2::ggplot(triarylamine.decay.data.integs) +
  ggplot2::geom_point(ggplot2::aes(x = time_s,y = Area))
#
## vectorized output of the uncorrected `sigmoid_integral`
triarylamine.decay.data1st.integ03 <-
  eval_integ_EPR_Spec(triarylamine.decay.series.data1st,
                      sigmoid.integ = TRUE,
                      output.vecs = TRUE)[["sigmoid"]]
#
## preview of the first 6 values
triarylamine.decay.data1st.integ03[1:6]
#> [1] 0.0000000e+00 8.6498993e-06 1.7285576e-05 2.5798979e-05 3.4145162e-05
#> [6] 4.2385433e-05
#
## incorporation of vectorized integration into
## data "pipe" ("%>%") `dplyr` processing of EPR spectral
## time series, creating column with `sigmoid` integral
## where its corresponding single integral (intensity)
## has undergone a baseline correction, finally the max. value
## of all sigmoid integrals along with the time is
## summarized in data frame for quantitative kinetic analysis
triarylamine.decay.data.integs <-
  triarylamine.decay.series.data$df %>%
  dplyr::group_by(time_s) %>%
  dplyr::filter(dplyr::between(B_G,3425,3550)) %>%
  dplyr::mutate(sigmoid_Integ =
    eval_integ_EPR_Spec(dplyr::pick(B_G,dIepr_over_dB),
                        correct.integ = TRUE,
                        BpeaKlim = c(3471.5,3512.5),
                        poly.degree = 3,
                        sigmoid.integ = TRUE,
                        output.vecs = TRUE)$sigmoid
                       ) %>%
  dplyr::summarize(Area = max(sigmoid_Integ))
## in such case `Blim` range is not defined by `eval_integ_EPR_Spec`,
## the `Blim = NULL` and `dplyr::between()` must be set !!!
#
## preview of the final data frame
head(triarylamine.decay.data.integs)
#> # A tibble: 6 × 2
#>   time_s   Area
#>    <dbl>  <dbl>
#> 1      6 0.0169
#> 2     21 0.0179
#> 3     36 0.0169
#> 4     51 0.0172
#> 5     66 0.0184
#> 6     81 0.0158
#
## preview of the simple plot
ggplot2::ggplot(triarylamine.decay.data.integs) +
  ggplot2::geom_point(ggplot2::aes(x = time_s,y = Area))
 #
## this does not correspond to example
## in `eval_kinR_EPR_modelFit`, `eval_kin_EPR_ODE_model`
## or in `plot_theme_NoY_ticks` based on the same input data,
## as those `Area` vs `time` relations were evaluated using
## the simulated EPR spectra (see also `vignette("datasets")`)
#
if (FALSE) { # \dontrun{
## Similar to previous data processing, creating both: corrected
## single integral + sigmoid integral for each time within the spectral
## series. Sigmoid integral was evalutated from the single one by
## `cumtrapz()` function from `pracma` package and finally re-scaled.
triarylamine.decay.data.integs <-
  triarylamine.decay.series.data$df %>%
  dplyr::group_by(time_s) %>%
  eval_integ_EPR_Spec(correct.integ = TRUE,
                      Blim = c(3425,3550),
                      BpeaKlim = c(3472.417,3505.5),
                      poly.degree = 3) %>%
 dplyr::group_by(time_s) %>%
 dplyr::mutate(sigmoid_Integ =
   pracma::cumtrapz(B_G,single_Integ_correct)[,1]) %>%
 dplyr::mutate(sigmoid_Integ_correct =
   abs(min(sigmoid_Integ) - sigmoid_Integ))
} # }
#
## this does not correspond to example
## in `eval_kinR_EPR_modelFit`, `eval_kin_EPR_ODE_model`
## or in `plot_theme_NoY_ticks` based on the same input data,
## as those `Area` vs `time` relations were evaluated using
## the simulated EPR spectra (see also `vignette("datasets")`)
#
if (FALSE) { # \dontrun{
## Similar to previous data processing, creating both: corrected
## single integral + sigmoid integral for each time within the spectral
## series. Sigmoid integral was evalutated from the single one by
## `cumtrapz()` function from `pracma` package and finally re-scaled.
triarylamine.decay.data.integs <-
  triarylamine.decay.series.data$df %>%
  dplyr::group_by(time_s) %>%
  eval_integ_EPR_Spec(correct.integ = TRUE,
                      Blim = c(3425,3550),
                      BpeaKlim = c(3472.417,3505.5),
                      poly.degree = 3) %>%
 dplyr::group_by(time_s) %>%
 dplyr::mutate(sigmoid_Integ =
   pracma::cumtrapz(B_G,single_Integ_correct)[,1]) %>%
 dplyr::mutate(sigmoid_Integ_correct =
   abs(min(sigmoid_Integ) - sigmoid_Integ))
} # }