R/inspect.ft.R
inspect.ft.Rd
inspect.ft
is a data exploration and preparation function that visualises
flowthrough respirometry data, checks it for common issues, and prepares it
for use in later functions in respR
, such as calc_rate.ft()
.
inspect.ft(
x,
time = NULL,
out.oxy = NULL,
in.oxy = NULL,
in.oxy.value = NULL,
delta.oxy = NULL,
plot = TRUE,
add.data = NULL,
...
)
data.frame
containing columns of time
and out.oxy
or
delta.oxy
concentrations, and optionally in.oxy
.
integer or string. Defaults to 1
. Specifies the column of the
Time data as either a column number or the name.
integer(s) or string(s). Defaults to NULL
. Specifies the
column(s) of the outflow oxygen data as either column numbers or names.
integer(s) or string(s). Defaults to NULL
. Specifies the
column(s) of the inflow oxygen data as either column numbers or names.
numeric value. Defaults to NULL
. If there is no
continuous in.oxy
data, this specifies a fixed value of oxygen
concentration for inflowing water in same units as out.oxy
, and is used
with out.oxy
to calculate a delta.oxy
.
integer(s) or string(s). Defaults to all non-time columns if
no other inputs given. Specifies the column number(s) or name(s) of delta
oxygen data, for when the user has already calculated the difference
between outflow and inflow oxygen (should be negative values for oxygen
uptake). If this is used, out.oxy
and in.oxy
should be NULL.
logical. Defaults to TRUE. Plots the data. See Details.
integer or string. Defaults to NULL
. Specifies the column
number or name of an optional additional data source that will be plotted
in blue alongside the full oxygen timeseries.
Allows additional plotting controls to be passed, such as legend = FALSE
, quiet = TRUE
, rate.rev = FALSE
and pos
.
Output is a list
object of class inspect.ft
containing input
parameters and data, data check summaries, and metadata, which can be
passed to calc_rate.ft()
to determine rates. If there are failed checks
or warnings, the row locations of the potentially problematic data can be
found in $locs
.
inspect.ft
is intended to be specific to flowthrough respirometry data.
In flowthrough respirometry (also known as 'open flow' or 'continuous flow'
respirometry) rather than calculating a rate from a changing oxygen
concentration recording in a sealed chamber, instead the difference (i.e.
'oxygen delta') between the inflowing and outflowing oxygen concentrations of
a respirometer receiving water at a constant flow rate is used to calculate
an oxygen consumption or production rate, typically after it has reached a
steady state. Therefore, in general, regions of stable oxygen delta values
(difference between outflow and inflow oxygen) are of interest. inspect.ft
visualises and prepares the data for use in calc_rate.ft()
. By specifying
data types in this function and saving the output, they do not need to be
specified in later functions.
Given an input data frame x
, the function scans the columns specified via
the time
, out.oxy
, in.oxy
or delta.oxy
inputs. Columns are specified
by using the column number (e.g. time = 1
), or by name (e.g. time = "Time.Hrs"
). If no columns are specified, by default the function assumes
the first column is time
, and all others are delta.oxy
oxygen data.
However, best practice is to use the inputs to specify particular columns.
The x
input must contain at least two data types; a single column of
numeric time
data, with either a column of paired out.oxy
concentrations (i.e. the exhalent or 'downstream' concentrations), or a
column of already calculated delta.oxy
values, that is the difference
between outflow and inflow concentrations, or the outflow concentration
corrected by a background recording from a 'blank' or empty chamber.
out.oxy input option: If an out.oxy
column has been specified, in order
to calculate the oxygen delta (and therefore a rate in calc_rate.ft()
)
there must also be an inflow oxygen concentration input (i.e. the inhalent or
'upstream' concentration). This will generally be a column of paired in.oxy
concentrations, in which case the paired values of out.oxy
and in.oxy
are
used to calculate the oxygen delta.oxy
, which is saved in the output and
used to determine a rate in calc_rate.ft()
. Alternatively, if the inflow
oxygen concentration is a known, generally unvarying value (such as fully
air-saturated water from a header tank) this can be entered as a single value
via in.oxy.value
and this is used to calculate the delta.oxy
.
delta.oxy input option: If delta oxygen values have already been
calculated, these can be entered via the delta.oxy
input, and these are
prepared and saved for rate calculations in calc_rate.ft
.
respR
requires data be in the form of paired values of numeric time and
oxygen. All columns are checked that they contain numeric data before any
other checks are performed. If any of the inspected columns do not contain
numeric data the remaining checks for that column are skipped, and the
function exits returning NULL
, printing the summary of the checks. No plot
is produced. Only when all inspected columns pass this numeric check can the
resulting output object be saved and passed to other respR
functions.
The time
column is checked for missing (NA/NaN
) values, infinite values
both positive and negative (Inf/-Inf
), that values are sequential, that
there are no duplicate times, and that it is numerically evenly-spaced.
Oxygen columns are checked for missing (NA/NaN
) and infinite values
(Inf/-Inf
). See Failed Checks section for what it means for analyses if
these checks result in warnings. If the output is assigned, the specified
columns are saved to a list
object for use in later functions such as
calc_rate.ft()
. A plot is also produced.
If plot = TRUE
, entered data is plotted against both time (bottom, blue
axis) and row index (top, red axis), depending on the inputs:
a single out.oxy
column with either a paired in.oxy
column or
in.oxy.value
: a two panel plot. The top plot is both outflow (green points)
and inflow (turquoise points) oxygen. The bottom plot is the oxygen delta
(black points) between outflow and inflow oxygen, essentially a unitless
oxygen uptake or production rate.
a single delta.oxy
column: a one panel plot of oxygen delta values.
multiple out.oxy
or delta.oxy
columns: a grid plot of all delta.oxy
data (either as entered or calculated from out.oxy
and in.oxy
). Specific
delta plots can be examined individually by using the pos
input (e.g.
plot(x, pos = 2)
). Y-axes are not equal.
unspecified columns: all columns are plotted assuming time
is in column
1, and all others are oxygen delta.oxy
data. Y-axes are not equal.
In delta plots, that is those plotting delta.oxy
values, either directly
entered or calculated, consistent oxygen uptake or production rates will be
represented by flat or level regions. The width
input may help with
selecting regions from which to extract rates, and can be passed in the main
function call or using plot()
on the output object. This smooths delta
oxygen values by calculating a rolling mean across the data. See Additional
plotting options below.
Note: Since respR
is primarily used to examine oxygen consumption,
the delta oxygen and rate plots are by default plotted on a reverse y-axis.
In respR
oxygen uptake rates are negative since they represent a negative
slope of oxygen against time. In these plots the axis is reversed so that
higher uptake rates (i.e. more negative) will be higher on these plots. If
you are interested instead in oxygen production rates, which are positive,
the rate.rev = FALSE
input can be passed in either the inspect.ft
call,
or when using plot()
on the output object. In this case, the delta and rate
values will be plotted numerically, and higher oxygen production rates will
be higher on the plot.
Using the add.data
input an additional data source, for example
temperature, can be plotted alongside the oxygen timeseries. This should be
either a column number (e.g. add.data = 3
) or name (e.g. add.data = "Temperature"
) indicating a column in the input x
data frame sharing the
same time data. None of the data checks are performed on this column; it is
simply to give a basic visual aid in the plot to, for example, help decide if
regions of the data should be used or not used because this parameter was
variable. Values are saved in the output as a vector under $add.data
. It is
plotted in blue on a separate y-axis on the main timeseries plot. It is not
plotted if multiple oxygen columns are inspected. See examples.
The width
input may help with selecting regions from which to extract
rates. This smooths delta oxygen values by calculating a rolling mean across
the data, and should be a value between 0 and 1 representing a proportion of
the total data width. If left as the default NULL
no smoothing is
performed. This is a visual aid which only affects plotted values and does
not alter output delta oxygen values.
If the legend or labels obscure part of the plot, they can be suppressed via
legend = FALSE
in either the inspect.ft
call, or when using plot()
on
the output object. Suppress console output messages with quiet = TRUE
. If
multiple columns have been inspected, the pos
input can be used to examine
each out.oxy
~in.oxy
~del.oxy
dataset. If axis labels (particularly
y-axis) are difficult to read, las = 2
can be passed to make axis labels
horizontal. In addition, 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)
)
can be used to adjust plot margins.
For a quick overview of larger experiments, multiple columns of out.oxy
,
in.oxy
and delta.oxy
can be inspected, but must share the same numeric
time data column specified by the time
input. Note, multiple column
inspection is chiefly intended to be exploratory functionality to provide a
quick overview of larger datasets. While the output will contain all data
columns in $dataframe
and $data
, subsequent functions such as
calc_rate.ft()
will use only the first delta.oxy
column for calculating
rates. Best practice is to inspect and assign each individual experiment or
column pair as separate inspect.ft
objects. See Examples.
If multiple out.oxy
columns are specified, in.oxy
can be a single column
(if for example all chambers are supplied from the same header tank), in
which case it is used to calculate an oxygen delta for all out.oxy
columns.
A single in.oxy.value
in the same units as out.oxy
can also be specified.
There can also be multiple in.oxy
columns, in which case it is assumed each
out.oxy
column is paired with each in.oxy
at the same position, and used
to calculate the oxygen delta.oxy
. In this case, out.oxy
and in.oxy
must have equal numbers of columns.
The most important data check in inspect.ft
is that all data columns are
numeric. If any column fails this check, the function skips the remaining
checks for that column, the function exits returning NULL
, and no output
object or plot is produced.
The other failed check that requires action is the check for infinite values
(Inf/-Inf
). Some oxygen sensing systems add these in error when
interference or data dropouts occur. Infinite values will cause problems when
it comes to calculating rates, so need to be removed. If found, locations of
these are printed and can be found in the output object under $locs
. Note,
these values are not plotted, so special note should be taken of the warnings
and console printout.
The remaining data checks in inspect.ft
are mainly exploratory and help
diagnose and flag potential issues with the data that might affect rate
calculations. For instance, long experiments may have had sensor dropouts the
user is unaware of. Some might not be major issues. For instance, an uneven
time warning can result from using decimalised minutes, which is a completely
valid time metric, but happens to be numerically unevenly spaced. As an
additional check, if uneven time is found, the minimum and maximum intervals
in the time data are in the console output, so a user can see immediately if
there are large gaps in the data.
If some of these checks produce warnings, it should generally not hinder
analysis of the data. respR
has been coded to rely on linear regressions on
exact data values, and not make assumptions about data spacing or order.
Therefore issues such as missing or NA/NaN values, duplicate or
non-sequential time values, or uneven time spacing should not cause any
erroneous results, as long as they do not occur over large regions of the
data. inspect.ft
however outputs locations (row numbers) of where these
issues occur (located in the $locs
element of the output), allowing users
to amend them before analysis. We would recommend that to be completely
confident in any results from analysis of such data, and avoid obscure
errors, these issues be addressed before proceeding.
For experiments in which the specimen data is to be background corrected by a
concurrently-run control experiment, inspect.ft
can be used by specifying
the specimen experiment as out.oxy
, and the "blank" as the in.oxy
input.
In this way, any variations in oxygen in the specimen data due to background
microbial activity, or for any other reason such as fluctuations in inflow
oxygen, are accounted for in the delta oxygen calculations, and therefore in
the rate calculated in calc_rate.ft()
. See the vignettes on the website
for examples.
If the background recordings are experiments with their own outflow and
inflow recordings, which show a generally consistent oxygen delta due to
microbial activity, this can be saved as a separate inspect.ft
object, a
background rate calculated in calc_rate.ft()
, and this used in
adjust_rate.ft()
as the by
input to perform background adjustments to
specimen rates.
Note: All background calculations should be from experiments done at the same flow rate as the specimen experiments to be corrected.
Saved output objects can be used in the generic S3 functions plot()
,
print()
and summary()
.
For additional help, documentation, vignettes, and more visit the respR
website at https://januarharianto.github.io/respR/
# Inspect outflow and inflow oxygen data
x <- inspect.ft(flowthrough.rd, time = 1, out.oxy = 2,
in.oxy = 3)
#> inspect.ft: No issues detected while inspecting data frame.
#>
#> # print.inspect.ft # --------------------
#> time oxy.out oxy.in
#> numeric pass pass pass
#> Inf/-Inf pass pass pass
#> NA/NaN pass pass pass
#> sequential pass - -
#> duplicated pass - -
#> evenly-spaced pass - -
#>
#> -----------------------------------------
print(x)
#>
#> # print.inspect.ft # --------------------
#> time oxy.out oxy.in
#> numeric pass pass pass
#> Inf/-Inf pass pass pass
#> NA/NaN pass pass pass
#> sequential pass - -
#> duplicated pass - -
#> evenly-spaced pass - -
#>
#> -----------------------------------------
plot(x)
#>
#> # plot.inspect.ft # ---------------------
#> plot.inspect.ft: Plotting inspect.ft dataset from position 1 of 1 ...
#> -----------------------------------------
# Inspect outflow oxygen data with inflow oxygen as a known value in
# the same units
x <- inspect.ft(flowthrough.rd, time = 1, out.oxy = 2,
in.oxy.value = 8.90)
#> inspect.ft: No issues detected while inspecting data frame.
#>
#> # print.inspect.ft # --------------------
#> time oxy.out
#> numeric pass pass
#> Inf/-Inf pass pass
#> NA/NaN pass pass
#> sequential pass -
#> duplicated pass -
#> evenly-spaced pass -
#>
#> -----------------------------------------
# Inspect already calculated delta oxygen data
inspect.ft(flowthrough.rd, time = 1, delta.oxy = 4)
#> inspect.ft: No issues detected while inspecting data frame.
#>
#> # print.inspect.ft # --------------------
#> time oxy.delta
#> numeric pass pass
#> Inf/-Inf pass pass
#> NA/NaN pass pass
#> sequential pass -
#> duplicated pass -
#> evenly-spaced pass -
#>
#> -----------------------------------------
# inspect multiple columns for a quick overview
inspect.ft(flowthrough_mult.rd, time = 1, delta.oxy = 10:12)
#> Warning: inspect.ft: Time values are not evenly-spaced (numerically).
#> inspect.ft: Data issues detected. For more information use print().
#>
#> # print.inspect.ft # --------------------
#> num.time oxy.delta.1 oxy.delta.2 oxy.delta.3
#> numeric pass pass pass pass
#> Inf/-Inf pass pass pass pass
#> NA/NaN pass pass pass pass
#> sequential pass - - -
#> duplicated pass - - -
#> evenly-spaced WARN - - -
#>
#> Uneven Time data locations (first 20 shown) in column: num.time
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#> Minimum and Maximum intervals in uneven Time data:
#> [1] 0.01 0.02
#> -----------------------------------------
# Inspect outflow and use a blank control chamber as background
# correction
#
# This experiment has increasing background respiration over time.
# Inspecting outflow oxygen with inflow header tank concentrations
# suggests specimen rates (bottom delta.oxy plot) are increasing.
inspect.ft(flowthrough_sim.rd, time = 1,
out.oxy = 2, in.oxy = 4)
#> inspect.ft: No issues detected while inspecting data frame.
#>
#> # print.inspect.ft # --------------------
#> num_time oxy.out.spec oxy.header
#> numeric pass pass pass
#> Inf/-Inf pass pass pass
#> NA/NaN pass pass pass
#> sequential pass - -
#> duplicated pass - -
#> evenly-spaced pass - -
#>
#> -----------------------------------------
# However, inspecting with recordings from a concurrent blank
# control accounts for this and shows specimen rates are level
# when background is taken into account.
inspect.ft(flowthrough_sim.rd, time = 1,
out.oxy = 2, in.oxy = 3)
#> inspect.ft: No issues detected while inspecting data frame.
#>
#> # print.inspect.ft # --------------------
#> num_time oxy.out.spec oxy.out.blank
#> numeric pass pass pass
#> Inf/-Inf pass pass pass
#> NA/NaN pass pass pass
#> sequential pass - -
#> duplicated pass - -
#> evenly-spaced pass - -
#>
#> -----------------------------------------