Identifies critical oxygen values, the oxygen tension or concentration below which an uptake rate transitions from independent to dependent on the oxygen supply, typically known as PCrit.
oxy_crit(
x,
method = "bsr",
time = NULL,
oxygen = NULL,
rate = NULL,
width = 0.1,
parallel = FALSE,
thin = 5000,
plot = TRUE,
...
)
object of class inspect
or a data.frame
containing either paired
oxygen~time values, or paired rate~oxygen values. See Details.
string. Defaults to "bsr"
. Critical oxygen value analysis
method. Either "bsr"
or "segmented"
. See Details.
integer or string. Defaults to 1. Specifies column number or column name of the time data.
integer or string. Defaults to 2. Specifies column number or column name of the oxygen data.
integer or string. Defaults to NULL. Specifies column number or column name of the rate data.
numeric value between 0 and 1 representing proportion of the total data length. Determines the width of the rolling regression used to determine the rolling rate and the rolling mean of oxygen values the rate is paired with. Defaults to 0.1, representing 10% of total rows.
logical. Defaults to FALSE. Enables parallel processing for computationally intensive analyses of large datasets.
integer. Defaults to 5000. Number of rows to subsample x
data
to before running "bsr"
analysis. No effect on datasets smaller than this
value or with "segmented"
method. To perform no subsampling enter as
NULL
. See Details.
logical. Defaults to TRUE.
Allows additional plotting controls to be passed, such as legend = FALSE
, quiet = TRUE
, rate.rev = FALSE
, and panel
. See Plotting
section.
Output is a list
object of class oxy_crit
containing input
parameters and data, various summary data, metadata, and the primary output
of interest $crit
, which is the critical oxygen value in the units of the
oxygen data as entered. This can be converted to additional units using
convert_DO()
. Note, if the Broken-Stick analysis (method == "bsr"
)
has been used, $crit
will contain two results; $crit.intercept
and
$crit.midpoint
. For full explanation of the difference between these see
Yeager & Ultsch (1989), however they are generally very close in value.
In earlier versions of respR
, this function was known as pcrit
or
calc_pcrit
. It was renamed to avoid conflicts with functions of the same
name in another package, and also because technically the P in PCrit
stands for the partial pressure of oxygen. Since the function returns the
value in the units of the data as entered, whether they are concentration or
pressure units, this terminology can be technically in error. Instead, for
the purposes of the documentation we refer to this as the Critical Oxygen
Value, or "COV". If the units of oxygen are partial pressure units (e.g.
kPa), this is equivalent to PCrit, otherwise they should be reported with
this in mind.
The oxy_crit()
function provides two methods (one of which outputs two
results) to calculate the COV. These are selected using the method
input.
method = "bsr"
This is the default method, adapted from the “Broken-Stick” regression (BSR) approach, of Yeager & Ultsch (1989), in which two segments of the data are iteratively fitted and the intersection with the smallest sum of the residual sum of squares between the two linear models is the estimated COV. Two slightly different ways of reporting this breakpoint are detailed by Yeager & Ultsch (1989); the intercept and midpoint. These are usually very close in value, and the function returns both.
The thin
input influences the BSR analysis. The method is very
computationally intensive, so to speed up analyses the thin
input will
subsample datasets longer than this input to this number or rows before
analysis. The default value of 5000 has in testing provided a good balance
between speed and results accuracy and repeatability. However, results may
vary with different datasets, so users should experiment with varying the
value. To perform no subsampling and use the entire dataset enter thin = NULL
. It has no effect on datasets shorter than the thin
input.
method = "segmented"
The second method is a wrapper for the "Segmented" regression approach,
available as part of the segmented
R package (Muggeo 2008), which estimates
the COV by iteratively fitting two intersecting models and selecting the
value that minimises the “gap” between the fitted lines.
The data input x
should be an inspect
object or data.frame
containing
oxygen~time data, or a data.frame
containing rate~oxygen data.
This is the typical input, where a timeseries of oxygen concentrations or
partial pressures against time has been recorded, generally down to a very
low value of oxygen. A column of time
and a column of oxygen
should be
specified. The function defaults to time = 1
and oxygen = 2
if no other
inputs are entered. These can also be specified using the column names.
If an inspect
object is entered as the x
input, the data frame is
extracted automatically and column identifiers are not required since these
were already entered in inspect
. Note, if multiple oxygen
columns were
entered in inspect
only the first entered one will be used in oxy_crit
.
To calculate the COV, the function requires data in the form of oxygen
uptake rate against oxygen value. Therefore, the function performs a rolling
regression on the oxygen~time data to determine rates, and pairs these
against a rolling mean of the oxygen data. The function then performs the
selected analysis method
on these data. The width of the rolling regression
and rolling mean is determined by the width
input. The default is 0.1,
representing 10% of the length of the data. This performs well in testing,
however performance may vary with data that has abrupt changes in rate, or is
particularly noisy. Users should experiment with different width
values to
see how it affects results, and report this with their results and analysis
parameters.
Alternatively, if existing rolling oxygen uptake rates have been calculated,
and have appropriate paired oxygen concentration or partial pressure values,
these can be entered with the rate
and oxygen
inputs specifying the
respective columns as either numbers or the column names. In this case the
function performs the selected analysis method
on these data directly
without any processing. The width
input in this case is not relevant and is
ignored.
This option can only be used with data frame x
inputs. Note, other columns
such as time data may be present in the input, but are not required so need
not be specified.
A plot is produced (provided plot = TRUE
) of the input data and results.
The top panel is the input data, either the oxygen~time timeseries, or the
rate~oxygen series, depending on what was entered in x
. If the former, the
critical oxygen value is indicated by a horizontal line, or two lines in the
case of the Broken-Stick analysis. Note, since the two BSR results are
usually close in value these may overlay each other.
The bottom plot is the rate~oxygen series upon which the analysis was conducted, either as input or as calculated. Critical oxygen values are indicated by vertical lines, and regression fits upon which the analysis was based by black dashed lines.
Note, that in respR
oxygen uptake rates are negative since they represent a
negative slope of oxygen against time, therefore by default rates are plotted
on a reverse y-axis so higher rates appear higher on the plot. If analysing
already calculated rates which are positive values this behaviour can be
reversed by passing rate.rev = FALSE
in either the main function call or
when calling plot()
on the output object. There is no issue with using
positive rate values; they will give identical critical value results in the
analysis.
If the legend obscures parts of the plot they can be suppressed using legend = FALSE
. Suppress console output messages with quiet = TRUE
. Each panel
can be plotted on its own using panel = 1
or panel = 2
. If using
already-calculated, positive rate values to identify critical oxygen values,
the y-axis of the rolling rate plot can be plotted not reversed by passing
rate.rev = FALSE
These inputs can be passed in either the main oxy_crit
call or when calling plot()
on the output object. If axis labels
(particularly y-axis) are difficult to read, las = 2
can be passed to make
axis labels horizontal, and oma
(outer margins, default oma = c(0.4, 1, 1.5, 0.4)
), and mai
(inner margins, default mai = c(0.3, 0.15, 0.35, 0.15)
) used to adjust plot margins.
Saved output objects can be used in the generic S3 functions print()
and
summary()
.
print()
: prints the critical oxygen value for the particular method
used.
summary()
: prints critical oxygen value, plus additional coefficients and
metadata for the particular method
used. See Yeager & Ultsch (1989) and
Muggeo (2008) for what these represent. The summary can be exported as a
separate data frame by passing export = TRUE
.
For additional help, documentation, vignettes, and more visit the respR
website at https://januarharianto.github.io/respR/
Yeager DP, Ultsch GR (1989) Physiological regulation and conformation: A BASIC program for the determination of critical points. Physiological Zoology 62:888–907. doi: 10.1086/physzool.62.4.30157935
Muggeo V (2008) Segmented: an R package to fit regression models with broken-line relationships. R News 8:20–25.
# \donttest{
## Run on oxygen~time data.frame with default inputs
oxy_crit(squid.rd)
#> oxy_crit: Applying column defaults of 'time = 1' and 'oxygen = 2'.
#> oxy_crit: Performing Broken-Stick analysis (Yeager and Ultsch 1989)...
#> oxy_crit: Broken-Stick analysis completed in 4.8 seconds.
#> plot.oxy_crit: Plotting Oxygen ~ Time derived critical oxygen results.
#>
#> # print.oxy_crit # ----------------------
#>
#> Broken-Stick (Yeager & Ultsch 1989):
#>
#> Sum RSS 0.0000006404122
#> Intercept 2.610095
#> Midpoint 2.609737
#>
#> -----------------------------------------
## Try a lower 'thin' input to speed up analysis
oxy_crit(squid.rd, thin = 1000)
#> oxy_crit: Applying column defaults of 'time = 1' and 'oxygen = 2'.
#> oxy_crit: Performing Broken-Stick analysis (Yeager and Ultsch 1989)...
#> oxy_crit: Broken-Stick analysis completed in 0.8 seconds.
#> plot.oxy_crit: Plotting Oxygen ~ Time derived critical oxygen results.
#>
#> # print.oxy_crit # ----------------------
#>
#> Broken-Stick (Yeager & Ultsch 1989):
#>
#> Sum RSS 0.0000001280328
#> Intercept 2.612272
#> Midpoint 2.614072
#>
#> -----------------------------------------
## Use the Segmented method instead
oxy_crit(squid.rd, method = "segmented")
#> oxy_crit: Applying column defaults of 'time = 1' and 'oxygen = 2'.
#> oxy_crit: Performing Segmented breakpoint analysis (Muggeo 2003)...
#> oxy_crit: Segmented analysis convergence attained in 1 iterations.
#> plot.oxy_crit: Plotting Oxygen ~ Time derived critical oxygen results.
#>
#> # print.oxy_crit # ----------------------
#>
#> Segmented (Muggeo 2003):
#>
#> Std. Err. 0.001785221
#> Breakpoint 2.609903
#>
#> -----------------------------------------
## Experiment with different 'width' input
# Higher widths tend to oversmooth data
oxy_crit(squid.rd, method = "segmented", width = 0.2)
#> oxy_crit: Applying column defaults of 'time = 1' and 'oxygen = 2'.
#> oxy_crit: Performing Segmented breakpoint analysis (Muggeo 2003)...
#> oxy_crit: Segmented analysis convergence attained in 1 iterations.
#> plot.oxy_crit: Plotting Oxygen ~ Time derived critical oxygen results.
#>
#> # print.oxy_crit # ----------------------
#>
#> Segmented (Muggeo 2003):
#>
#> Std. Err. 0.00182839
#> Breakpoint 2.701036
#>
#> -----------------------------------------
# Lower width in this case gives very similar result to default 0.1
oxy_crit(squid.rd, method = "segmented", width = 0.05)
#> oxy_crit: Applying column defaults of 'time = 1' and 'oxygen = 2'.
#> oxy_crit: Performing Segmented breakpoint analysis (Muggeo 2003)...
#> oxy_crit: Segmented analysis convergence attained in 1 iterations.
#> plot.oxy_crit: Plotting Oxygen ~ Time derived critical oxygen results.
#>
#> # print.oxy_crit # ----------------------
#>
#> Segmented (Muggeo 2003):
#>
#> Std. Err. 0.001905877
#> Breakpoint 2.611025
#>
#> -----------------------------------------
## Run on oxygen~time data in 'inspect' object
insp <- inspect(squid.rd, time = 1, oxygen = 2)
#> inspect: No issues detected while inspecting data frame.
#>
#> # print.inspect # -----------------------
#> Time Oxygen
#> numeric pass pass
#> Inf/-Inf pass pass
#> NA/NaN pass pass
#> sequential pass -
#> duplicated pass -
#> evenly-spaced pass -
#>
#> -----------------------------------------
oxy_crit(insp)
#> oxy_crit: Applying column defaults of 'time = 1' and 'oxygen = 2'.
#> oxy_crit: Performing Broken-Stick analysis (Yeager and Ultsch 1989)...
#> oxy_crit: Broken-Stick analysis completed in 4.7 seconds.
#> plot.oxy_crit: Plotting Oxygen ~ Time derived critical oxygen results.
#>
#> # print.oxy_crit # ----------------------
#>
#> Broken-Stick (Yeager & Ultsch 1989):
#>
#> Sum RSS 0.0000006404122
#> Intercept 2.610095
#> Midpoint 2.609737
#>
#> -----------------------------------------
## Run on already calculated rate~oxygen data
# Calculate a rolling rate
rate <- auto_rate(squid.rd,
method = "rolling",
width = 0.1,
plot = FALSE)$rate
## Calculate a rolling mean oxygen
oxy <- na.omit(roll::roll_mean(squid.rd[[2]],
width = 0.1 * nrow(squid.rd)))
## Combine to data.frame
squid_rate_oxy <- data.frame(oxy, rate)
## Perform COV analysis
oxy_crit(squid_rate_oxy, oxygen = 1, rate = 2)
#> oxy_crit: Performing analysis using Rate ~ Oxygen data.
#> oxy_crit: Performing Broken-Stick analysis (Yeager and Ultsch 1989)...
#> oxy_crit: Broken-Stick analysis completed in 5.1 seconds.
#> plot.oxy_crit: Plotting Rate ~ Oxygen derived critical oxygen results.
#>
#> # print.oxy_crit # ----------------------
#>
#> Broken-Stick (Yeager & Ultsch 1989):
#>
#> Sum RSS 0.0000006404122
#> Intercept 2.610095
#> Midpoint 2.609737
#>
#> -----------------------------------------
# }