1 - Importing the Raw LMD Data

library(readxl)
LMD = read_excel("C:/Users/Samsung/OneDrive/Documentos/2 - Material de trabalho/7 - Projetos/7.1 Pesquisa/LMD/LMD Raw Data 06-24 apr 2026.xlsx", sheet = "R table")
head(LMD)

This chunk imports the raw LMD dataset from the Excel file into R for further cleaning and processing.

2 - Summarizing Calibration Data

# Separate calibration data
Calibration = subset(LMD, treatment == "Calibration")

# Calibration summary
Calibration_summary = data.frame(
  Mean = mean(Calibration$ppm.m, na.rm = TRUE),
  SD   = sd(Calibration$ppm.m, na.rm = TRUE),
  Min  = min(Calibration$ppm.m, na.rm = TRUE),
  Max  = max(Calibration$ppm.m, na.rm = TRUE))
head(Calibration_summary)
writexl::write_xlsx(Calibration, "1 - Calibration Data.xlsx")

This chunk separates the calibration records and calculates the mean, standard deviation, minimum, and maximum ppm values.

3 - Removing Calibration Records

LMD = subset(LMD, treatment != "Calibration" | is.na(treatment))
print(LMD)
## # A tibble: 621,871 × 9
##    SerialNo SessionName treatment DateTime            ppm.m `Distance(m)`
##       <dbl>       <dbl> <chr>     <dttm>              <dbl>         <dbl>
##  1     4647         865 G2        2026-04-06 11:00:35     0             1
##  2     4647         865 G2        2026-04-06 11:00:35     0             1
##  3     4647         865 G2        2026-04-06 11:00:35     0             1
##  4     4647         865 G2        2026-04-06 11:00:35     0             1
##  5     4647         865 G2        2026-04-06 11:00:35     0             1
##  6     4647         865 G2        2026-04-06 11:00:35     0             1
##  7     4647         865 G2        2026-04-06 11:00:35     0             1
##  8     4647         865 G2        2026-04-06 11:00:35     0             1
##  9     4647         865 G2        2026-04-06 11:00:35     0             1
## 10     4647         865 G2        2026-04-06 11:00:34     0             1
## # ℹ 621,861 more rows
## # ℹ 3 more variables: Latitude <dbl>, Longitude <dbl>, Media <chr>
writexl::write_xlsx(LMD, "2 - Main data without Calibration.xlsx")

This chunk removes all rows identified as Calibration, retaining only the measurement data.

4.1 - Background: extracting Data

Background = subset(LMD, `Distance(m)` > 5)
head(Background)
writexl::write_xlsx(Background, "3 - Background.xlsx")

This chunk identifies measurements taken more than 5 m away as background.

4.2 - Background: Identifying Values Outside the Central 95% Range

# Define the central 95% range
Lower_limit = quantile(Background$ppm.m, 0.025, na.rm = TRUE)
Upper_limit = quantile(Background$ppm.m, 0.975, na.rm = TRUE)
Lower_limit
## 2.5% 
##    0
Upper_limit
## 97.5% 
## 34.65
Outliers = subset(Background, ppm.m < Lower_limit | ppm.m > Upper_limit)
nrow(Outliers)
## [1] 151
head(Outliers)
Background_clean = subset(Background,ppm.m >= Lower_limit & ppm.m <= Upper_limit)
print(Background_clean)
## # A tibble: 5,864 × 9
##    SerialNo SessionName treatment DateTime            ppm.m `Distance(m)`
##       <dbl>       <dbl> <chr>     <dttm>              <dbl>         <dbl>
##  1     4647         865 G2        2026-04-06 10:58:43     0            20
##  2     4647         865 G2        2026-04-06 10:58:43     0            20
##  3     4647         865 G2        2026-04-06 10:58:43     0            20
##  4     4647         865 G2        2026-04-06 10:58:43     0            20
##  5     4647         865 G2        2026-04-06 10:58:42     0            20
##  6     4647         865 G2        2026-04-06 10:58:42     0            20
##  7     4647         865 G2        2026-04-06 10:58:42     0            20
##  8     4647         865 G2        2026-04-06 10:58:42     0            20
##  9     4647         865 G2        2026-04-06 10:58:42     0            20
## 10     4647         865 G2        2026-04-06 10:58:42     0            20
## # ℹ 5,854 more rows
## # ℹ 3 more variables: Latitude <dbl>, Longitude <dbl>, Media <chr>
writexl::write_xlsx(Background_clean, "4 - Background_clean.xlsx")

This chunk identifies ppm measurements below the 2.5th percentile or above the 97.5th percentile as potential outliers.

4.3 - Background: Averaging ppm Measurements per Second

# Background summary
Background_summary = data.frame(
  Mean = mean(Background_clean$ppm.m, na.rm = TRUE),
  SD   = sd(Background_clean$ppm.m, na.rm = TRUE),
  Min  = min(Background_clean$ppm.m, na.rm = TRUE),
  Max  = max(Background_clean$ppm.m, na.rm = TRUE))
Background_summary

This chunk summarizes their ppm values.

5.1 - Main data - Identifying Values Outside the Central 95% Range

# Define the central 95% range
Lower_limit = quantile(LMD$ppm.m, 0.025, na.rm = TRUE)
Upper_limit = quantile(LMD$ppm.m, 0.975, na.rm = TRUE)
Lower_limit
## 2.5% 
##    0
Upper_limit
## 97.5% 
##    24
Outliers = subset(LMD, ppm.m < Lower_limit | ppm.m > Upper_limit)
nrow(Outliers)
## [1] 15173
head(Outliers)
LMD_clean = subset(LMD,ppm.m >= Lower_limit & ppm.m <= Upper_limit)
head(LMD_clean)
writexl::write_xlsx(LMD_clean, "5 - LMD_clean.xlsx")

This chunk identifies ppm measurements below the 2.5th percentile or above the 97.5th percentile as potential outliers.

5.2 - Main data - Measurement Frequency per Second

# Count the number of measurements per second
Measurements_per_second = as.data.frame(table(LMD$DateTime))
Measurements_per_second
names(Measurements_per_second) = c("DateTime", "Measurements")

# Summary of measurements per second
Measurement_rate_summary = data.frame(
  Mean = mean(Measurements_per_second$Measurements),
  SD   = sd(Measurements_per_second$Measurements),
  Min  = min(Measurements_per_second$Measurements),
  Max  = max(Measurements_per_second$Measurements))

Measurements_per_second[Measurements_per_second$Measurements == max(Measurements_per_second$Measurements),]
Measurement_rate_summary

This chunk counts the number of LMD measurements recorded each second and calculates the mean, standard deviation, minimum, and maximum measurement frequency.The obtained average can also be interpreted approximately as the sampling frequency in Hz.

5.3 - Main data - Averaging ppm Measurements per Second

names(LMD)
## [1] "SerialNo"    "SessionName" "treatment"   "DateTime"    "ppm.m"      
## [6] "Distance(m)" "Latitude"    "Longitude"   "Media"
LMD_second = aggregate(ppm.m ~ SerialNo + SessionName + treatment + DateTime + `Distance(m)`,
  data = LMD,FUN = mean,na.rm = TRUE)
head(LMD_second)
writexl::write_xlsx(LMD_second, "6 - LMD_clean_second.xlsx")

This chunk averages all ppm measurements recorded within each second, resulting in one ppm value per second.

6 - Background correction: Subtracting mean background from ppm values

# Subtract the mean background value
LMD_second$ppm.m = LMD_second$ppm.m - Background_summary$Mean
# Remove negative values
LMD_second_corr = subset(LMD_second, ppm.m >= 0)
LMD_second_corr$ppm.m = round(LMD_second_corr$ppm.m, 2)
head(LMD_second_corr)
writexl::write_xlsx(LMD_second_corr, "7 - LMD_clean_second_background_correction.xlsx")

This chunk corrects the ppm measurements by subtracting the mean background concentration from each observation. Negative corrected values are removed, and the remaining ppm values are rounded to two decimal places.