knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.asp = 0.618, out.widht = "80%" ) # This is necessary to direct knitr to find the # 'data' directory, thanks to # https://stackoverflow.com/a/24585750/1036500 knitr::opts_knit$set(root.dir = normalizePath('../'))
library(treenetproc) data("dendro_data_L1") data("dendro_data_L2") example_L1 <- dendro_data_L1 %>% dplyr::filter(series == "site-1_dendro-1") %>% dplyr::filter(ts <= "2013-06-01") example_L2 <- dendro_data_L2 %>% dplyr::filter(series == "site-1_dendro-1") %>% dplyr::filter(ts <= "2013-06-01") layout(matrix(c(1, 2), nrow = 2), heights = c(1, 1), widths = 1) par(mar = c(0, 2.1, 0, 1)) plot(data = example_L1, value ~ ts, type = "l", xaxt = "n", yaxt = "n", ylab = "", las = 1, col = "grey70") title(ylab = "raw", mgp = c(0.8, 1, 0)) par(mar = c(0, 2.1, 0, 1)) plot(data = example_L2, value ~ ts, type = "n", xaxt = "n", yaxt = "n", ylab = "", xlab = "") title(ylab = "processed", mgp = c(0.8, 1, 0)) lines(data = example_L2, value ~ ts, col = "#08519c")
library(treenetproc) library(dplyr)
The package treenetproc
cleans, processes and visualises highly resolved
time series of dendrometer data. In two steps, raw dendrometer data is aligned
to regular time intervals (level 1: L1
) and cleaned (level 2: L2
). In an
optional third step several commonly used characteristics such as the start and
end of stem growth can be calculated.
This vignette demonstrates the workflow of the package with sample code.
For more information on the functionality of the package, the user is referred
to Knüsel et al. (2021).
Raw dendrometer and temperature data can be provided as input. Providing temperature data is optional. However, temperature data increases the quality of error detection and processing (cf. Step 2: Providing temperature data).
The raw dendrometer or climate data can be provided in long (Table 1) or wide
format (Table 2). Data of multiple sensors can either be specified in a column
named series
to separate the sensors (long format, Table 1) or as separate
columns (wide format, Table 2). Dendrometer data always has to be provided in
µm
. In addition, a column named ts
with timestamps in any standard date
format (default is date_format = "%Y-%m-%d %H:%M:%S"
) is required.
Dendrometer and temperature data can either be provided in two separate
data frames or in the same data.frame
. If temperature data is provided
along with dendrometer data, the name of the temperature data series has to
contain the string 'temp
' to be identified as temperature data (cf. Table 2).
data("dendro_data_L0") knitr::kable(dendro_data_L0[1:5, ], caption = "**Table 1**: Raw dendrometer data in `long` format. Dendrometer measurements have to be provided in `µm`.")
data("dendro_data_L0_wide") knitr::kable(dendro_data_L0_wide[1:5, ], caption = "**Table 2**: Raw dendrometer and temperature data in `wide` format. Dendrometer measurements have to be provided in `µm`.")
After converting the data to the correct format (long
or wide
), it has
to be time-aligned to regular time intervals with the function proc_L1
.
The resulting time resolution can be specified with the argument reso
(in
minutes, i.e. reso = 10
for a 10-minute resolution). Temperature data can be
time-aligned in the same way as dendrometer data.
In case the data is provided in wide
format, this has to be specified
with the argument input = "wide"
.
# reduce size of dendro_data_L0 to make computing of proc_L1 faster data("dendro_data_L0") dendro_data_L0 <- dendro_data_L0[1:50, ] data("dendro_data_L0_wide") dendro_data_L0_wide <- dendro_data_L0_wide[1:50, ]
# Time-align dendrometer data in long format (cf. Table 1) proc_L1(data_L0 = dendro_data_L0, reso = 10) # Time-align dendrometer and temperature data in wide format (cf. Table 2) proc_L1(data_L0 = dendro_data_L0_wide, reso = 10, input = "wide")
Time-aligned dendrometer data can be cleaned and processed with the function
proc_dendro_L2
. To increase the quality of error detection,
time-aligned temperature data can optionally be provided to temp_L1
.
# reduce size of dendro_data_L1 to make computing of proc_dendro_L2 faster data("dendro_data_L1") dendro_data_L1 <- dendro_data_L1 %>% filter(series == "site-1_dendro-1") %>% filter(ts <= "2013-05-02")
# Clean time-aligned (L1) dendrometer data proc_dendro_L2(dendro_L1 = dendro_data_L1, temp_L1 = temp_data_L1, plot = FALSE)
The function returns a data.frame
containing the cleaned dendrometer data
(Table 3).
data("dendro_data_L2") knitr::kable(dendro_data_L2[1:5, ], caption = "**Table 3**: Cleaned dendrometer data.")
The output data.frame
contains the following columns:
%Y-%m-%d %H:%M:%S
value
up to this timestampmax
and value
(calculated according
to Zweifel et al. 2016)lowtemp
)The column flags
documents all changes that occurred during the error
detection and processing. The numbers after the name of the flag specify
in which iteration of the cleaning process the changes occurred:
"out"
: outlier point removed (e.g. "out1"
for an outlier removed in
iteration 1 of the cleaning process)"jump"
: jump corrected"fill"
: value was linearly interpolated (length of gaps that are linearly
interpolated can be specified with the argument interpol
)
The visual checking of the results remains an essential step in dendrometer
data cleaning. By default all changes are plotted (plot = TRUE
) and saved to
a PDF file in the current working directory (plot_export = TRUE
).
# Load dataset data("dendro_data_L1") # Subset dendrometer dataset for example dendro_L1 <- dendro_data_L1 %>% filter(series == "site-1_dendro-1") %>% filter(ts < "2014-12-31") # Clean time-aligned (L1) dendrometer data and plot changes proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, plot_period = "monthly", plot_export = FALSE)
The plot contains...
L1
dendrometer data in
the first panel, L2
dendrometer data (with L1
data
in the background in grey) in the second panel. Interpolated points are
circled and frost periods are indicated with a horizontal, cyan lineL1
and L2
data (red) as well as the deleted values
(pink) in the third panelWhen plotted monthly (plot_period = "monthly"
), each change to the data
gets an ID. The ID numbers facilitate the reversal of
wrong or unwanted corrections. Alternatively, the plots can be drawn for
the full period (plot_period = "full"
) or for each year separately
(plot_period = "yearly"
). In these cases, the ID's are not displayed.
As a summary for each dendrometer series, an additional plot shows the yearly
growth curves aligned by the day of year. The first year is shown in grey since
the measurements may start before or after the beginning of the growing season
and therefore the start of growth may not be comparable to other years.
Beneath the plot all input values, some growth statistics as well as the
package version used are reported.
proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, plot_period = "monthly", plot_export = FALSE)
proc_dendro_L2()
The rigidity of the error detection can be adjusted with the variables
tol_out
and tol_jump
. Both variables control the thresholds used for
the classification of outliers and jumps/shifts in the data.
If values are erroneously deleted (i.e. classified as outliers), the
value of tol_out
should be increased (high values of tol_out
decrease the
rigidity of the thresholds). A decrease in the value of tol_out
, in contrast,
is appropriate if outliers are not detected as such (low values of tol_out
increase the rigidity of the thresholds).
Similarly, an increase in the value of tol_jump
makes sense if a dendrometer
series is flattened during processing (see example below). A decrease, in
contrast, is appropriate if obvious jumps are not corrected.
# Load dataset data("dendro_data_L1") # Subset dendrometer dataset for example dendro_L1 <- dendro_data_L1 %>% filter(series == "site-1_dendro-2") # Clean dendrometer data (tol_jump = 1, tol_out = 10) proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, tol_jump = 1, tol_out = 10, plot_export = FALSE)
data_shrink_L2 <- proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, tol_jump = 1, tol_out = 10, plot = FALSE) par(mar = c(2.1, 4, 1, 2)) plot(data = data_shrink_L2, value ~ ts, type = "n", ylab = paste0("L2 (", "\u00b5", "m)"), las = 1) lines(data = dendro_L1, value ~ ts, col = "grey70") lines(data = data_shrink_L2, value ~ ts, col = "#08519c")
Such problems can be fixed by increasing the value of tol_jump
.
# Clean dendrometer data (use default values of 'tol_jump' and 'tol_out') proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, tol_jump = 50, tol_out = 10, plot_export = FALSE)
data("dendro_data_L2") dendro_L2 <- dendro_data_L2 %>% filter(series == "site-1_dendro-2") par(mar = c(2.1, 4, 1, 2)) plot(data = dendro_L2, value ~ ts, type = "n", ylab = paste0("L2 (", "\u00b5", "m)"), las = 1) lines(data = dendro_L1, value ~ ts, col = "grey70") lines(data = dendro_L2, value ~ ts, col = "#08519c")
Providing temperature data along with dendrometer data ensures that frost
events are not treated as outliers during data cleaning. In periods of probable
frost (i.e. when the temperature < lowtemp
) the threshold for outlier
detection is multiplied by frost_thr
.
Temperature data can either be provided along with the dendrometer data
(series name has to contain the string temp
), or it can be provided
separately to the argument temp_L1
in the function proc_dendro_L2()
.
If no temperature data is provided, a sample temperature dataset is used. The sample temperature dataset assumes permanent frost in the months December, January and February.
# Load dataset data("dendro_data_L1") # Subset dataset for example dendro_L1 <- dendro_data_L1 %>% filter(series == "site-1_dendro-4") # Clean dendrometer data (without temperature data) proc_dendro_L2(dendro_L1 = dendro_L1, plot_export = FALSE)
# Process to L2 without temperature data dendro_L2 <- proc_dendro_L2(dendro_L1 = dendro_L1, plot = FALSE) par(mar = c(2.1, 4, 1, 2)) plot(data = dendro_L2, value ~ ts, type = "n", ylab = paste0("L2 (", "\u00b5", "m)"), las = 1) lines(data = dendro_L1, value ~ ts, col = "grey70") lines(data = dendro_L2, value ~ ts, col = "#08519c")
The sample temperature dataset does not assume frost after the end of
February, therefore many values during the frost shrinkage are classified as
outliers and deleted.
# Load time-aligned temperature data data("temp_data_L1") # Clean dendrometer data (with temperature data) proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, plot_export = FALSE)
dendro_L2 <- proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, plot = FALSE) par(mar = c(2.1, 4, 1, 2)) plot(data = dendro_L2, value ~ ts, type = "n", ylab = paste0("L2 (", "\u00b5", "m)"), las = 1) lines(data = dendro_L1, value ~ ts, col = "grey70") lines(data = dendro_L2, value ~ ts, col = "#08519c") # add abline to indicate frost data_L2_frost <- dendro_L2 %>% dplyr::rename(value_L2 = value) plot_frost_period(df = data_L2_frost)
Including the temperature dataset increases the thresholds for jump and outlier
detection in periods of probable frost (i.e. when temperature < lowtemp
),
which are indicated with a horizontal cyan line in the plot.
The increase of the thresholds for outlier detection prevented the
classification of the values during the frost shrinkage as outliers.
Remaining errors that cannot be removed by adjusting the default values
of tol_jump
and/or tol_out
can be corrected using the function
corr_dendro_L2()
. This function can be used to reverse erroneous changes
or force changes that were not automatically made. There are three
possibilities to manually correct remaining errors:
L1
data with the function
corr_dendro_L1()
.
# Load dataset data("dendro_data_L1") # Subset dataset for example dendro_L1 <- dendro_data_L1 %>% filter(series == "site-1_dendro-3") # Clean dendrometer data dendro_L2 <- proc_dendro_L2(dendro_L1 = dendro_L1, temp_L1 = temp_data_L1, plot_period = "monthly", plot_export = FALSE)
The following errors remained after data cleaning:
59-61
are not necessary since the raw data seems to be correctCorrection of the above-described errors:
# Correct described errors corr_dendro_L2(dendro_L1 = dendro_L1, dendro_L2 = dendro_L2, reverse = 59:61, force = "2013-08-12", delete = c("2013-08-01", "2013-08-04"), series = "site-1_dendro-3", plot_export = FALSE)
All three errors have been corrected successfully. The corrections are also
reflected in the returned data.frame
and all changes are documented in the
column flags
as:
"rev"
: for reversed corrections with the argument reverse
"fjump"
: for forced jumps with the argument force
"del"
: for deleted values with the argument delete
After error detection and processing and the manual removal of remaining
errors, the package offers two functions to calculate additional physiological
parameters that may be of use for later analyses. The function grow_seas
returns the day of year of growth onset and growth cessation. The function
phase_stats
calculates the timing, duration, amplitude and the rate of change
of shrinkage and expansion phases.
The day of year of the start and end of the growing season can be calculated
with the function grow_seas
.
# Load dataset data("dendro_data_L2") # Subset dataset for example dendro_L2 <- dendro_data_L2 %>% filter(series == "site-1_dendro-1") # Calculate growing season start and end grow_seas(dendro_L2 = dendro_L2)
The function returns a data.frame
containing the day of year (doy
) of the
start and end of the growing season (Table 4). Values are
returned starting from the second year only, since gro_start
and gro_end
depend on the values from the previous year.
# calculate grow_seas grow_seas <- grow_seas(dendro_L2 = dendro_L2) knitr::kable(grow_seas, caption = "**Table 4**: Sample output data of the function `grow_seas`.")
Several characteristics of shrinkage and expansion phases can be calculated
with the function phase_stats
.
# Load dataset data("dendro_data_L2") # Subset dataset for example dendro_L2 <- dendro_data_L2 %>% filter(series == "site-1_dendro-2") # Calculate phase statistics phase_stats(dendro_L2 = dendro_L2, plot_phase = TRUE, plot_export = FALSE)
The function returns a data.frame
containing the timing, duration, amplitude
and slope of the shrinkage (shrink
) and expansion (exp
) phases (Table 5).
In addition, the parameter phase_class
identifies days on which radial change
is likely driven by transpiration (phase_class = 1
) or temperature
(phase_class = -1
).
# calculate phase_stats phase_stats <- phase_stats(dendro_L2 = dendro_L2) knitr::kable(phase_stats[1:5, ], caption = "**Table 5**: Sample output data of the function `phase_stats`.")
To evaluate the identification of shrinkage and expansion phases, all phases
can be plotted by setting plot_phase = TRUE
. The plot shows the
maximum (filled circle) and minimum (filled triangle) of the respective phase
and reports its statistics. Empty circles and triangles show maxima or minima
of previous or subsequent phases.
phase_stats(dendro_L2 = dendro_L2, plot_phase = TRUE, plot_export = FALSE)
The often reported parameter maximum daily shrinkage (mds
) is equivalent to the
parameter shrink_amp
in case shrink_dur
is shorter than 24 hours. The
following example demonstrates how mds
can be extracted from the phase
statistics:
# Calculate phase statistics phase_stats <- phase_stats(dendro_L2 = dendro_L2) # Calculate maximum daily shrinkage (mds) mds <- phase_stats %>% mutate(mds = ifelse(shrink_dur < 1440, shrink_amp, NA))
To cite treenetproc
in a publication use:
citation("treenetproc")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.