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).

Data input

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`.")



Step 1: Time-alignment (L1)

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")



Step 2: Error detection and processing (L2)

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)


Output data

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:

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:

Evaluation of error detection

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...

When 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)

Adjustments to parameters of proc_dendro_L2()

Rigidity of error detection

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.

Example of too rigid error detection

# 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

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().

Example without temperature data

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.

Example with temperature data

# 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.

Manual corrections

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:

Example of manual corrections

# 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:

  1. Corrections 59-61 are not necessary since the raw data seems to be correct
  2. A jump occurring on August 13 is not corrected
  3. Not all erroneous data is removed at the beginning (August 1-4)

Correction 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:

Step 3: Data aggregation (optional)

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.

Growing season

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`.")



Phase statistics

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))



References

Knüsel S, Peters RL, Haeni M, Wilhelm M, Zweifel R (2021) Processing and extraction of seasonal tree physiological parameters from stem radius time series. Forests: 12(6):765.

Zweifel R, Haeni M, Buchmann N, Eugster W (2016) Are trees able to grow in periods of stem shrinkage? New Phytol. 211:839-49.

Citation

To cite treenetproc in a publication use:

citation("treenetproc")


treenet/treenetproc documentation built on June 16, 2021, 4:39 p.m.