Pneumatron curves - Workshop

Author

Marina Scalon

Working directory and packages

setwd("C:/Marina/PDS/Curso Ecofisiologia/R")
library(tidyverse)
Warning: package 'tidyverse' was built under R version 4.4.3
Warning: package 'ggplot2' was built under R version 4.4.3
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.2     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Warning: package 'fitplc' was built under R version 4.4.2
Loading required package: nlme

Attaching package: 'nlme'

The following object is masked from 'package:dplyr':

    collapse

Loading required package: car
Loading required package: carData

Attaching package: 'car'

The following object is masked from 'package:dplyr':

    recode

The following object is masked from 'package:purrr':

    some

Data organization

data_all <- read.csv2("Dados_pneumatron.csv")
str(data_all)
'data.frame':   7760 obs. of  7 variables:
 $ DATETIME   : chr  "2025/10/21 11:49:21" "2025/10/21 11:49:24" "2025/10/21 11:49:25" "2025/10/21 11:49:25" ...
 $ MILLISECOND: int  41 539 1041 1540 2040 2540 3042 3541 4041 4541 ...
 $ SAMPLE     : int  1 1 1 1 1 1 1 1 1 1 ...
 $ NLOG       : int  0 1 2 3 4 5 6 7 8 9 ...
 $ INT_TEMP   : chr  "23.8" "23.6" "23.6" "23.3" ...
 $ SENSOR     : int  15947 15942 15945 15941 15941 15942 15943 15943 15943 15943 ...
 $ PRESSURE   : chr  "91.9978" "91.9615" "91.9833" "91.9543" ...
data_all$DATETIME = as.POSIXct(data_all$DATETIME,
                               format = "%Y/%m/%d %H:%M:%S", tz = "GMT")
str(data_all)
'data.frame':   7760 obs. of  7 variables:
 $ DATETIME   : POSIXct, format: "2025-10-21 11:49:21" "2025-10-21 11:49:24" ...
 $ MILLISECOND: int  41 539 1041 1540 2040 2540 3042 3541 4041 4541 ...
 $ SAMPLE     : int  1 1 1 1 1 1 1 1 1 1 ...
 $ NLOG       : int  0 1 2 3 4 5 6 7 8 9 ...
 $ INT_TEMP   : chr  "23.8" "23.6" "23.6" "23.3" ...
 $ SENSOR     : int  15947 15942 15945 15941 15941 15942 15943 15943 15943 15943 ...
 $ PRESSURE   : chr  "91.9978" "91.9615" "91.9833" "91.9543" ...
data_all <- data_all %>% mutate_if(is.character, as.numeric)
str(data_all)
'data.frame':   7760 obs. of  7 variables:
 $ DATETIME   : POSIXct, format: "2025-10-21 11:49:21" "2025-10-21 11:49:24" ...
 $ MILLISECOND: int  41 539 1041 1540 2040 2540 3042 3541 4041 4541 ...
 $ SAMPLE     : int  1 1 1 1 1 1 1 1 1 1 ...
 $ NLOG       : int  0 1 2 3 4 5 6 7 8 9 ...
 $ INT_TEMP   : num  23.8 23.6 23.6 23.3 23.8 23.9 23.7 23.5 23.8 23.5 ...
 $ SENSOR     : int  15947 15942 15945 15941 15941 15942 15943 15943 15943 15943 ...
 $ PRESSURE   : num  92 92 92 92 92 ...

Following Pneumatron manual script to transform Pressure in Pdiff(difference from initial to final pressure)

results = NULL
for(psamp in unique(data_all$SAMPLE))
{
  data = data_all[data_all$SAMPLE == psamp, ]
  # Identify individual measurements by checking continuity in the "n" column
  data$MEASUREMENT = c(1, diff(data$NLOG))
  n = 1
  for(row in 1:nrow(data)){
    if(data$MEASUREMENT[row] != 1) n = n + 1
    data$MEASUREMENT[row] = n
  }

  # Get initial and final pressures for each measurement
  # This assumes that the desired, final pressure, is pressure at the end of
  # the measurement
  data.summ_ini <- data[0, ]
  data.summ_end <- data[0, ]
  for(i in unique(data$MEASUREMENT))
  {
    data.summ_ini = rbind(data.summ_ini,
                          data[data$MEASUREMENT == i & data$NLOG == 1, ])
    data.summ_end = rbind(data.summ_end,
                          data[data$MEASUREMENT == i & data$NLOG ==
                                 max(data$NLOG[data$MEASUREMENT == i]), ])
  }
  data.summ = merge(data.summ_ini, data.summ_end,
                    by = c("SAMPLE", "MEASUREMENT"), all = TRUE,
                    suffixes = c("_INI","_END"))
  data.summ$P_DIFF = data.summ$PRESSURE_END - data.summ$PRESSURE_INI
  # Store file
  results = rbind(results, data.summ)
}

Write csv file with results

write.csv(results, "processed_data.csv", 
                                row.names = FALSE)

Calculate Air Discharge (mol) load file with Psi and log number

results <- read.csv("processed_data.csv")
data_psi <- read.csv2("Potencial.csv") # data with log and psi value
str(data_psi)
'data.frame':   94 obs. of  3 variables:
 $ ID : chr  "TR-1" "TR-1" "TR-1" "TR-1" ...
 $ Log: int  7 63 87 106 129 8 64 82 105 130 ...
 $ Psi: chr  "0.97" "2.4" "2.8" "2.6" ...
data_psi$Psi <- as.numeric(data_psi$Psi) #ter certeza que esta numérico
data_psi <- data_psi %>% rename(SAMPLE = Log) #colocar o mesmo nome da planilha do pneumatron (SAMPLE)
data <- inner_join(results, data_psi, by = "SAMPLE")

atm <- 90 
str(data)
'data.frame':   94 obs. of  17 variables:
 $ SAMPLE         : int  7 8 9 13 14 16 17 18 19 20 ...
 $ MEASUREMENT    : int  1 1 1 1 1 1 1 1 1 1 ...
 $ DATETIME_INI   : chr  "2022-01-18 11:45:47" "2022-01-18 11:47:49" "2022-01-18 11:51:40" "2022-01-18 11:57:55" ...
 $ MILLISECOND_INI: int  540 540 541 541 539 539 540 542 539 541 ...
 $ NLOG_INI       : int  1 1 1 1 1 1 1 1 1 1 ...
 $ INT_TEMP_INI   : num  23.4 23.4 24.3 24.9 24.8 25 25.2 24.9 25.4 25.5 ...
 $ SENSOR_INI     : int  8639 8854 8803 8604 8649 8931 8706 9039 8999 8957 ...
 $ PRESSURE_INI   : num  39 40.6 40.2 38.7 39.1 ...
 $ DATETIME_END   : chr  "2022-01-18 11:46:16" "2022-01-18 11:48:18" "2022-01-18 11:52:09" "2022-01-18 11:58:25" ...
 $ MILLISECOND_END: int  29616 29622 29610 29741 29643 29748 29618 29616 29624 29635 ...
 $ NLOG_END       : int  59 59 59 59 59 59 59 59 59 59 ...
 $ INT_TEMP_END   : num  23.8 23.8 24.1 24.6 25.3 25.4 25 25 25 25.4 ...
 $ SENSOR_END     : int  15937 15940 15929 15931 15932 15938 15927 15936 15938 15937 ...
 $ PRESSURE_END   : num  91.9 91.9 91.9 91.9 91.9 ...
 $ P_DIFF         : num  52.9 51.4 51.7 53.1 52.8 ...
 $ ID             : chr  "TR-1" "AM-1" "TR-2" "PL-1" ...
 $ Psi            : num  0.97 0.86 2.5 0.32 0.82 1.2 0.25 0.82 0.7 1.2 ...
data$ad <- ((data$P_DIFF * 8.3144621 * (data$INT_TEMP_INI+273.15))/(atm * 1000)) * 1000 * 1000 * 1000

Calculate Percentual air discharge and save file

data <- data %>% group_by(ID) %>%
  mutate(pad = 100 * ((ad - ad[which.min(Psi)]) / (ad[which.max(Psi)] - ad[which.min(Psi)]))) #se valores de Psi estiverem negativos, inverte min e max

data_filtered <- data %>% filter(pad <= 100 & pad >=0)

write.csv(data_filtered,"curvas.csv")

Pltoting the curves using fitplc package

From here after data processed

curvas <- read.csv("curvas.csv")

curvas <- data_filtered %>% 
  separate(ID, into=c("sp", "ind"), sep="-")

Tropidanthus

data.tr <- curvas %>% filter(sp == "TR")
pfit.tr <- fitplc(data.tr, varnames= c(PLC= "pad", WP="Psi"), nboot=1000)
pfit.50.tr <- fitplc(data.tr, varnames= c(PLC= "pad", WP="Psi"), x=50)

plot(pfit.tr, what="PLC", main= "Parasita Tripodanthus", pxcex= 1.5, cex=1.5, cex.main=1.5, cex.lab=1.5, cex.axis=1.5)

coef(pfit.50.tr)
    Estimate Norm - 2.5% Norm - 97.5% Boot - 2.5% Boot - 97.5%
SX 44.663065   20.900011    87.732846   20.409872    202.77408
PX  2.462193    1.876474     2.832927    1.664162      3.00433

Plátano

data.pl <- curvas %>% filter(sp == "PL")
pfit.pl <- fitplc(data.pl, varnames= c(PLC= "pad", WP="Psi"), nboot=1000)

pfit.50.pl <- fitplc(data.pl, varnames= c(PLC= "pad", WP="Psi"), x=50)
summary(pfit.pl)
Class of object 'plcfit' as returned by 'fitplc'.
-------------------------------------------------

Parameters and %s%% confidence interval:

 95%    Estimate Norm - 2.5% Norm - 97.5% Boot - 2.5% Boot - 97.5%
SX 27.096751   17.648900    42.250738   19.191757    50.517801
PX  2.038539    1.613575     2.500243    1.635331     2.628422
plot(pfit.pl, what="PLC", main= "Plátano", pxcex= 1.5, cex=1.5, cex.main=1.5, cex.lab=1.5, cex.axis=1.5)

coef(pfit.50.pl)
    Estimate Norm - 2.5% Norm - 97.5% Boot - 2.5% Boot - 97.5%
SX 27.096751   17.648900    42.250738   19.244902    53.539267
PX  2.038539    1.613575     2.500243    1.642085     2.654864

Ligustrum

data.li <- curvas %>% filter(sp == "LI")
pfit.li <- fitplc(data.li, varnames= c(PLC= "pad", WP="Psi"), nboot=1000)

pfit.50.li <- fitplc(data.li, varnames= c(PLC= "pad", WP="Psi"),x=50)

plot(pfit.li, what="PLC", main= "Ligustrum", pxcex= 1.5, cex=1.5, cex.main=1.5, cex.lab=1.5, cex.axis=1.5)

coef(pfit.50.li)
     Estimate Norm - 2.5% Norm - 97.5% Boot - 2.5% Boot - 97.5%
SX 146.625488  130.003767   164.221886   65.315201   162.042517
PX   3.137706    3.120835     3.154297    3.120357     3.231741

Amora

data.am <- curvas %>% filter(sp == "AM")
# pfit.am <- fitplc(data.am, varnames= c(PLC= "pad", WP="Psi"), nboot=1000)

#não teve ajuste