This notebook presents simple codes to load, process and clean time-series data about leaf water potential ( \(LWP\) ) , soil water potential (\(SWP\)) and weight (\(W\)) for transpiration rate (\(T_r\)).
The repository contains the following folders :
data/ :
raw/ contains one folder per variable. Inside each subfolder are stored raw files directly collected from sensors. Typically, each file corresponds to one plant.
clean/ contains cleaned tables (in .csv format) for metadata and for each variable.
plots/ : contains plots and figures generated by this notebook.
src/ contains custom functions used to process, compile and clean the raw data.
Loading libraries and defining paths
Code
# rm(list=ls()) # clean workspace [comment when Rendering]# setwd(dirname(rstudioapi::getActiveDocumentContext()$path)) # Set working directory to the current file [comment when Rendering]# LOAD LIBRARIESlibrary(SciViews)library(ggplot2)library(pander)library(dplyr)library(tidyverse)library(data.table)library(anytime)library(ggthemes)# LOAD UTILITARY FUNCTIONSsource("src/Data_Preparation.R") # Contains functions to extract data from raw filessource("src/LWP_algorithm.R") # Contains the code of the LWP cleaning algorithm# SET DIRECTORIESdata_path_clean <-paste0("data/clean/") # Set path to Clean folderdata_path_raw <-paste0("data/raw/") # Set path to row folderplot_path <-paste0("plots/") # Set path to plot folderpath_LWP <-paste0(data_path_raw, "LWP/")path_SWP <-paste0(data_path_raw, "SWP/")path_weight <-paste0(data_path_raw, "weight/")
\(LWP\) data from psychrometers are stored in separated .csv files.
We use the custom function load_LWP() to load and process each csv file in the provided path.
The result is a single table containing Date, Time, LWP value and the ID of plants.
The single file is written as LWP_raw.csv in the raw folder.
Code
# Compile all PSY files into one dataframeLWP_raw <-load_LWP(path = path_LWP)# Change the PSY id by Plant_IDLWP_raw <-left_join(LWP_raw, Plants_ID[c("Plant_ID", "PSY")], by ="PSY") %>%select(-c("PSY"))pander(head(LWP_raw))# Write to raw folderwrite.csv(x = LWP_raw, file =paste0(data_path_raw, "LWP_raw.csv"), row.names = F)
Load compiled raw file
Once the raw files are compiled, we can simply load the processed csv file.
NB : each time we load the raw.csv files, we need to format correctly the DateTime column.
Code
# Load from Raw folderLWP_raw <-read.csv(paste0(data_path_raw, "LWP_raw.csv")) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))
Simple plot :
Code
p1 <-ggplot(left_join(LWP_raw, Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = LWP, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Leaf Water Potantial [MPa]") +ylim(-1.5, 0.5) +# Add custom theme my_theme# ========================= # # Show plot and save to filep1
We see that data is messy, so we implemented an algorithm to clean LWP data. It consists of creating buffers per day, and for each buffer we run a bunch of tests to check if we want to keep the buffer of not.
check.na evaluates how many NAN are in the buffer. If there is more NAN than the nan.th proportion, the buffer is discarded.
check.zero evaluates how many zeros are in the buffer. If there is more zeros than the zero.th proportion, the buffer is discarded.
check.cycles evaluates if LWP at night is lesser than LWP at day. If the mean LWP at day is higher that the mean LWP at night multiplied by cycle.alpha, the buffer is discarded.
check.slope evaluates the mean slope of the buffer via a linear model. If the absolute value of the slope is higher than slope.th, the buffer is discarded.
check.level evaluates the statistical mean of the buffer compared to the mean of the other buffers. If \(\mu_{LWP, i} < \mu_{LWP} - \alpha \cdot \sigma_{LWP}\) or \(\mu_{LWP, i} > \mu_{LWP} + \alpha \cdot \sigma_{LWP}\) (with \(\mu_{LWP}\) is the mean LWP for all buffers, \(\mu_{LWP,i}\) is the mean LWP of the ith buffer, \(\alpha\) is the unput multiplicator and \(\sigma_{LWP}\) is the standard deviation of LWP), the buffer is discarded.
Finally, the clean dataframe is written to storage.
LWP_clean <-read.csv(paste0(data_path_clean, "LWP_clean.csv")) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))# Add NA so that there is gaps in geom_linetemp <-expand.grid(DateTime =unique(LWP_clean$DateTime),Plant_ID =unique(LWP_clean$Plant_ID) )LWP_clean <-left_join(temp, LWP_clean, by =c("Plant_ID", "DateTime"))
Simple plot :
Code
LWP_plot <-ggplot(left_join(LWP_clean, Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = LWP, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Leaf Water Potantial [MPa]") +ylim(-1.5, 0.5) +# Add custom theme my_theme# ========================= # # Show plot and save to fileLWP_plot
We can make a quick graph of the comparison between Raw and Clean LWP
Code
p <-ggplot() +geom_line(data = LWP_raw, aes(x = DateTime, y = LWP), color ="red") +geom_line(data = LWP_clean, aes(x = DateTime, y = LWP), color ="green") +# ylim(-3, 0.5) +facet_wrap(~Plant_ID, scale ="free") + my_theme# ========================= # # Show plot and save to filep
Extract from sensor files and compile into a single raw file
SWP data is stored as a unique raw .DAT file.
The function load_SWP loads the file and convert it to a tibble.
The pre-processed file can then be stored in data/raw/ folder.
Depending on the format of the .DAT file, you may need to change the inputs :
- path_SWP : Path to the folder containing the .DAT files. - sep : Separator of column. By defaut is space, other used formats are ",", ";".
- skip : Number of lines to skip. By default is zero.
- filename : By default is null and the function scan every file of the folder. If filename is specified, the function only loads the corresponding file.
Code
# Extract from DAT filesSWP_raw <-load_SWP(path_SWP = path_SWP,sep ="\t",skip =0, file =NULL)# Replace Teros_ID by Plant_ID and set DateTime as POSIXctSWP_raw <-left_join(SWP_raw, Plants_ID[c("Plant_ID", "Teros")], by =c("Teros_ID"="Teros")) %>%select(c("DateTime", "SWP", "Plant_ID")) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%d/%m/%Y %H:%M"))# Write to diskwrite.csv(x = SWP_raw, file =paste0(data_path_raw, "SWP_raw.csv"), row.names = F)
Load raw file, clean it a bit and write to a clean file
Here we load the pre-processed SWP dataframe. Statistical or manual corrections can be done at will.
The final version of SWP data can then be written to data/clean/ folder.
Code
# Load SWP_rawSWP_raw <-na.omit(read.csv(paste0(data_path_raw, "SWP_raw.csv"))) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))# Filter data so DateTime match with LWPSWP_clean <- SWP_raw %>%filter(DateTime >=min(LWP_clean$DateTime) & DateTime <=max(LWP_clean$DateTime)) %>%filter(DateTime >as.POSIXct("2024-10-25"))# Write to diskwrite.csv(x = SWP_clean, file =paste0(data_path_clean, "SWP_clean.csv"), row.names = F)
Simple plot :
Code
SWP_plot <-ggplot(left_join(SWP_clean, Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = SWP, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Soil Water Potantial [MPa]") +ylim(-850, 0.5) +# Add custom theme my_theme# =========================================== #SWP_plot
Extract from sensor output files and compile into one single raw file
Weight data is stored as individual .csv files in data/raw/weight/ folder.
Similarly to LWP and SWP, the function load_W function extracts individual files and creates a single table.
The table is store in data/raw/folder.
Code
# Load raw dataW_raw <-load_W(path = path_weight, skip =1) %>%inner_join(Plants_ID[c("Plant_ID", "Scale")], by ="Scale") %>%mutate(Date =as.Date(Date, format ="%d/%m/%Y")) %>%select(-c("Scale"))# Write to diskwrite.csv(x = W_raw, file =paste0(data_path_raw, "W_raw.csv"), row.names = F)
Load raw file, clean a bit and write clean file
Here we load the pre-processed Weight table, apply corrections and filtering at will, then write cleaned data to data/clean/ folder.
Code
# Load raw fileW_raw <-unique(read.csv(paste0(data_path_raw, "W_raw.csv"))) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))# CleaningW_clean <- W_raw %>%filter(DateTime >=as.POSIXct("2024-10-24 11:00:00"))# Write to diskwrite.csv(x = W_clean, file =paste0(data_path_clean, "W_clean.csv"), row.names = F)
Simple plot :
Code
W_plot <-ggplot(W_clean %>%left_join(Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = Weight, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Weight [g]") +# ylim(-1.5, 0.5) +# Add custom theme my_themeW_plot
full_plot <-ggplot(TS_full) +# Display the data as a linegeom_line(aes(x = DateTime, y = Value, color = Measure)) +# Choose manual colorsscale_color_manual(values =c("darkgreen", "orange", "blue")) +# Set x limits and define the format of breaksscale_x_datetime(# date_breaks = "1 day",date_labels ="%d/%m",name ="Date") +# Set y limits and add a secondary axisscale_y_continuous(limits =c(-2, 1.5),bquote(expr = psi[Soil/Leaf]~"[MPa]"), sec.axis =sec_axis(~.*10, name =bquote(T[act]~"["*gh^-1*"]")) ) +# Split screen by Plant_namefacet_wrap(~Plant_name, scales ="free",ncol =2) +# Premade theme my_theme +# Change some specificities of the themetheme(legend.position ="bottom") full_plot
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).
Code
dev.off()
png
2
Source Code
---title: "DATA PROCESSING"authors: "Marco D'Agostino, Théo Degand, Lei Ding"toc: trueformat: html: output-file: "index.html" html-math-method: mathjax code-fold: true # code-summary: "Show code" code-tools: true code-overflow: wrap self-contained: false theme: zephyreditor: source---## INTRODUCTION & SET UPThis notebook presents simple codes to load, process and clean time-series data about leaf water potential ( $LWP$ ) , soil water potential ($SWP$) and weight ($W$) for transpiration rate ($T_r$).The repository contains the following folders :- **data/** : - **raw/** contains one folder per variable. Inside each subfolder are stored raw files directly collected from sensors. Typically, each file corresponds to one plant. - **clean/** contains cleaned tables (in .csv format) for metadata and for each variable.- **plots/** : contains plots and figures generated by this notebook.- **src/** contains custom functions used to process, compile and clean the raw data.### Loading libraries and defining paths```{r, results='hide', message=FALSE, warning=FALSE}# rm(list=ls()) # clean workspace [comment when Rendering]# setwd(dirname(rstudioapi::getActiveDocumentContext()$path)) # Set working directory to the current file [comment when Rendering]# LOAD LIBRARIESlibrary(SciViews)library(ggplot2)library(pander)library(dplyr)library(tidyverse)library(data.table)library(anytime)library(ggthemes)# LOAD UTILITARY FUNCTIONSsource("src/Data_Preparation.R") # Contains functions to extract data from raw filessource("src/LWP_algorithm.R") # Contains the code of the LWP cleaning algorithm# SET DIRECTORIESdata_path_clean <-paste0("data/clean/") # Set path to Clean folderdata_path_raw <-paste0("data/raw/") # Set path to row folderplot_path <-paste0("plots/") # Set path to plot folderpath_LWP <-paste0(data_path_raw, "LWP/")path_SWP <-paste0(data_path_raw, "SWP/")path_weight <-paste0(data_path_raw, "weight/")``````{r,}my_theme <-theme_classic() +theme(text =element_text(size =11),axis.title =element_text(size =12),axis.text =element_text(size =11),axis.text.x =element_text(size =8, angle =90, hjust =1, vjust =0.5),plot.title =element_text(size =12, hjust =0.5),strip.background =element_rect(colour ="white", fill ="white"),strip.text =element_text(size =12), # Increase size and make bold )```### Load MetadataLoad metadata about the experiment.Here we designed a simple experiment as example :- 4 plants (`Plant_ID` column)- 2 different treatments (`Treatment` column) : - well watered (*WW*) - water deficit (*WD* )- Leaf water potential ($LWP$) was recorded with psychrometers (`PSY` column). Raw files are stored in `data/raw/LWP/`.- Soil water potential ($SWP$) was recorded with TEROS 21 (`Teros` column). Raw files are stored in `data/raw/SWP/`.- Weight ($W$) was recorded with scales (`Scale` column). Raw files are stored in `data/raw/weight/`.```{r}Plants_ID <-read.csv(paste0(data_path_clean, "Plants_ID.csv"))pander(Plants_ID)```## 1) LOAD LEAF WATER POTENTIAL DATA### Extract from raw files$LWP$ data from psychrometers are stored in separated `.csv` files.We use the custom function `load_LWP()` to load and process each csv file in the provided path.The result is a single table containing Date, Time, LWP value and the ID of plants.The single file is written as `LWP_raw.csv` in the raw folder.```{r, eval=FALSE}# Compile all PSY files into one dataframeLWP_raw <-load_LWP(path = path_LWP)# Change the PSY id by Plant_IDLWP_raw <-left_join(LWP_raw, Plants_ID[c("Plant_ID", "PSY")], by ="PSY") %>%select(-c("PSY"))pander(head(LWP_raw))# Write to raw folderwrite.csv(x = LWP_raw, file =paste0(data_path_raw, "LWP_raw.csv"), row.names = F)```### Load compiled raw fileOnce the raw files are compiled, we can simply load the processed csv file.NB : each time we load the raw.csv files, we need to format correctly the DateTime column.```{r, message=FALSE, warning=FALSE}# Load from Raw folderLWP_raw <-read.csv(paste0(data_path_raw, "LWP_raw.csv")) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))```Simple plot :```{r, message=FALSE, warning=FALSE}p1 <-ggplot(left_join(LWP_raw, Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = LWP, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Leaf Water Potantial [MPa]") +ylim(-1.5, 0.5) +# Add custom theme my_theme# ========================= # # Show plot and save to filep1ggsave(filename ="LWP_raw.png", plot = p1, device ="png", path = plot_path, width =9, height =4)```### Apply cleaning algorithm and store to folder.We see that data is messy, so we implemented an algorithm to clean LWP data. It consists of creating buffers per day, and for each buffer we run a bunch of tests to check if we want to keep the buffer of not.- `check.na` evaluates how many NAN are in the buffer. If there is more NAN than the `nan.th` proportion, the buffer is discarded.- `check.zero` evaluates how many zeros are in the buffer. If there is more zeros than the `zero.th` proportion, the buffer is discarded.- `check.cycles` evaluates if LWP at night is lesser than LWP at day. If the mean LWP at day is higher that the mean LWP at night multiplied by `cycle.alpha`, the buffer is discarded.- `check.slope` evaluates the mean slope of the buffer via a linear model. If the absolute value of the slope is higher than `slope.th`, the buffer is discarded.- `check.level` evaluates the statistical mean of the buffer compared to the mean of the other buffers. If $\mu_{LWP, i} < \mu_{LWP} - \alpha \cdot \sigma_{LWP}$ or $\mu_{LWP, i} > \mu_{LWP} + \alpha \cdot \sigma_{LWP}$ (with $\mu_{LWP}$ is the mean LWP for all buffers, $\mu_{LWP,i}$ is the mean LWP of the ith buffer, $\alpha$ is the unput multiplicator and $\sigma_{LWP}$ is the standard deviation of LWP), the buffer is discarded.Finally, the clean dataframe is written to storage.```{r, message=FALSE, warning=FALSE}LWP_clean <-clean.LWP(LWP_data = LWP_raw,check.na = T,check.zero = T, check.cycles = T, check.slope = F, check.level = F, nan.th =0.5, zero.th =0.5, cycle.alpha =0.5, slope.th =0.000002, level.alpha =2)LWP_clean <- LWP_clean[c("Date", "Time", "DateTime", "LWP", "Plant_ID")]# Additionnal cleaningLWP_clean <- LWP_clean %>%filter(LWP >-3)# Write to storagewrite.csv(x = LWP_clean, file =paste0(data_path_clean, "LWP_clean.csv"), row.names = F)```### Load clean file```{r, message=FALSE, warning=FALSE}LWP_clean <-read.csv(paste0(data_path_clean, "LWP_clean.csv")) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))# Add NA so that there is gaps in geom_linetemp <-expand.grid(DateTime =unique(LWP_clean$DateTime),Plant_ID =unique(LWP_clean$Plant_ID) )LWP_clean <-left_join(temp, LWP_clean, by =c("Plant_ID", "DateTime"))```Simple plot :```{r, message=FALSE, warning=FALSE}LWP_plot <-ggplot(left_join(LWP_clean, Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = LWP, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Leaf Water Potantial [MPa]") +ylim(-1.5, 0.5) +# Add custom theme my_theme# ========================= # # Show plot and save to fileLWP_plotggsave(filename ="LWP_clean.png", plot = LWP_plot, device ="png", path = plot_path, width =9, height =4)```We can make a quick graph of the comparison between Raw and Clean LWP```{r, message=FALSE, warning=FALSE}p <-ggplot() +geom_line(data = LWP_raw, aes(x = DateTime, y = LWP), color ="red") +geom_line(data = LWP_clean, aes(x = DateTime, y = LWP), color ="green") +# ylim(-3, 0.5) +facet_wrap(~Plant_ID, scale ="free") + my_theme# ========================= # # Show plot and save to filepggsave(filename ="LWP_comparison.png", plot = p, device ="png", path = plot_path, width =9, height =4)```## 2) LOAD SOIL WATER POTENTIAL DATA### Extract from sensor files and compile into a single raw fileSWP data is stored as a unique raw `.DAT` file.\The function `load_SWP` loads the file and convert it to a tibble.\The pre-processed file can then be stored in `data/raw/` folder.\Depending on the format of the `.DAT` file, you may need to change the inputs :\- `path_SWP` : Path to the folder containing the `.DAT` files. \ - `sep` : Separator of column. By defaut is space, other used formats are `","`, `";"`.\- `skip` : Number of lines to skip. By default is zero.\- `filename` : By default is null and the function scan every file of the folder. If `filename` is specified, the function only loads the corresponding file.```{r, eval=FALSE, message=FALSE, warning=FALSE}# Extract from DAT filesSWP_raw <-load_SWP(path_SWP = path_SWP,sep ="\t",skip =0, file =NULL)# Replace Teros_ID by Plant_ID and set DateTime as POSIXctSWP_raw <-left_join(SWP_raw, Plants_ID[c("Plant_ID", "Teros")], by =c("Teros_ID"="Teros")) %>%select(c("DateTime", "SWP", "Plant_ID")) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%d/%m/%Y %H:%M"))# Write to diskwrite.csv(x = SWP_raw, file =paste0(data_path_raw, "SWP_raw.csv"), row.names = F)```### Load raw file, clean it a bit and write to a clean fileHere we load the pre-processed SWP dataframe. Statistical or manual corrections can be done at will.\The final version of SWP data can then be written to `data/clean/` folder.```{r, message=FALSE, warning=FALSE}# Load SWP_rawSWP_raw <-na.omit(read.csv(paste0(data_path_raw, "SWP_raw.csv"))) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))# Filter data so DateTime match with LWPSWP_clean <- SWP_raw %>%filter(DateTime >=min(LWP_clean$DateTime) & DateTime <=max(LWP_clean$DateTime)) %>%filter(DateTime >as.POSIXct("2024-10-25"))# Write to diskwrite.csv(x = SWP_clean, file =paste0(data_path_clean, "SWP_clean.csv"), row.names = F)```Simple plot :```{r, message=FALSE, warning=FALSE}SWP_plot <-ggplot(left_join(SWP_clean, Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = SWP, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Soil Water Potantial [MPa]") +ylim(-850, 0.5) +# Add custom theme my_theme# =========================================== #SWP_plotggsave(filename ="SWP_clean.png", plot = SWP_plot, device ="png", path = plot_path, width =9, height =4)```## 3) EXTRACT WEIGHT DATA### Extract from sensor output files and compile into one single raw fileWeight data is stored as individual `.csv` files in `data/raw/weight/` folder.\Similarly to LWP and SWP, the function `load_W` function extracts individual files and creates a single table.\The table is store in `data/raw/`folder.```{r, eval=FALSE, message=FALSE, warning=FALSE}# Load raw dataW_raw <-load_W(path = path_weight, skip =1) %>%inner_join(Plants_ID[c("Plant_ID", "Scale")], by ="Scale") %>%mutate(Date =as.Date(Date, format ="%d/%m/%Y")) %>%select(-c("Scale"))# Write to diskwrite.csv(x = W_raw, file =paste0(data_path_raw, "W_raw.csv"), row.names = F)```### Load raw file, clean a bit and write clean fileHere we load the pre-processed Weight table, apply corrections and filtering at will, then write cleaned data to `data/clean/` folder.```{r, message=FALSE, warning=FALSE}# Load raw fileW_raw <-unique(read.csv(paste0(data_path_raw, "W_raw.csv"))) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))# CleaningW_clean <- W_raw %>%filter(DateTime >=as.POSIXct("2024-10-24 11:00:00"))# Write to diskwrite.csv(x = W_clean, file =paste0(data_path_clean, "W_clean.csv"), row.names = F)```Simple plot :```{r, message=FALSE, warning=FALSE}W_plot <-ggplot(W_clean %>%left_join(Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = Weight, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Weight [g]") +# ylim(-1.5, 0.5) +# Add custom theme my_themeW_plotggsave(filename ="W_clean.png", plot = W_plot, device ="png", path = plot_path, width =9, height =4)```## 4) Compute Transpiration rateThe function `compute_Tr` computes transpiration rate $T_r$ from Weight time series using the formula$$ T_{r, t} [g \cdot h^{-1}] = (W_{t-1} - W_{t}) / \Delta t $$Pre-processed transpiration rate data is stored in `data/raw/` folder.```{r, message=FALSE, warning=FALSE}# Load clean Weight fileW_clean <-unique(read.csv(paste0(data_path_clean, "W_clean.csv"))) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))# Compute Tr using custom functionTr_raw <-compute_Tr(W = W_clean, th =20, DT.format ="%Y-%m-%d %H:%M:%S")# Write to diskwrite.csv(x = Tr_raw, file =paste0(data_path_raw, "Tr_raw.csv"), row.names = F)```### Load Tr raw from file```{r, message=FALSE, warning=FALSE}# Read Tr raw fil from folderTr_raw <-unique(read.csv(paste0(data_path_raw, "Tr_raw.csv"))) %>%mutate(DateTime =anytime(DateTime)) %>%mutate(DateTime =as.POSIXct(DateTime, format ="%Y-%m-%d %H:%M:%S"))```Simple plot```{r}Tr_plot <-ggplot(left_join(Tr_raw, Plants_ID, by ="Plant_ID")) +# Display data as linegeom_line(aes(x = DateTime, y = Tr, color = Treatment,group = Plant_ID)) +# Split into facets per Plant_IDfacet_wrap(~Plant_ID, scales ="free",ncol =2) +# Setup x axisscale_x_datetime(date_breaks ="1 day",date_labels ="%d/%m",name ="Date") +# Setup y axisylab("Transpiration rate [g/h]") +ylim(-1, 15) +# Add custom theme my_themeTr_plotggsave(filename ="Tr_raw.png", plot = Tr_plot, device ="png", path = plot_path, width =9, height =4)```## Make one plot with the 3 variablesCreate a gigantic file with every time series measurement.For that, we create temporary table by gathering variables into one key column (`Measure`) and one value column (`Value`).Then we bind them by row. For better visualization, we create a new attribute `Plant_name` that combine `Plant_ID` and `Treatment`.```{r}LWP_temp <- LWP_clean[c("Plant_ID", "DateTime", "LWP")] %>%gather(LWP, key ="Measure", value ="Value") SWP_temp <- SWP_clean[c("Plant_ID", "DateTime", "SWP")] %>%gather(SWP, key ="Measure", value ="Value") %>%mutate(Value = Value/1000)W_temp <- W_clean[c("Plant_ID", "DateTime", "Weight")] %>%gather(Weight, key ="Measure", value ="Value") %>%mutate(Value = Value/1500)T_temp <- Tr_raw[c("Plant_ID", "DateTime", "Tr")] %>%gather(Tr, key ="Measure", value ="Value") %>%mutate(Value = Value/10)TS_full <-rbind(LWP_temp, SWP_temp) %>%rbind(T_temp) %>%left_join(Plants_ID, by ="Plant_ID") %>%mutate(Plant_name =paste0("Plant ", Plant_ID, " | ", Treatment))```Generate the plot```{r}full_plot <-ggplot(TS_full) +# Display the data as a linegeom_line(aes(x = DateTime, y = Value, color = Measure)) +# Choose manual colorsscale_color_manual(values =c("darkgreen", "orange", "blue")) +# Set x limits and define the format of breaksscale_x_datetime(# date_breaks = "1 day",date_labels ="%d/%m",name ="Date") +# Set y limits and add a secondary axisscale_y_continuous(limits =c(-2, 1.5),bquote(expr = psi[Soil/Leaf]~"[MPa]"), sec.axis =sec_axis(~.*10, name =bquote(T[act]~"["*gh^-1*"]")) ) +# Split screen by Plant_namefacet_wrap(~Plant_name, scales ="free",ncol =2) +# Premade theme my_theme +# Change some specificities of the themetheme(legend.position ="bottom") full_plotggsave(filename ="Full_plot.png", plot = full_plot, device ="png", path = plot_path, width =7, height =5)svg(filename =paste0(plot_path, "Full_plot.svg"), width =7, height =5)full_plotdev.off()```