# Data file from the TEMPEST repository
message("Reading data...")
## Reading data...
dat <- read_parquet("tempest_tree_ghg_fluxes.parquet")
message(nrow(dat), " rows and ", ncol(dat), " columns")
## 3361 rows and 12 columns
# Read soil temperature and moisture data
files <- list.files(path = "L2_data/", pattern = "parquet$", full.names = TRUE)
lapply(files, read_parquet) %>%
bind_rows() %>%
# compute a 'Date' column...
mutate(Date = as.Date(TIMESTAMP)) %>%
# ...and average variables for each date
group_by(Site, Plot, Date, research_name) %>%
summarise(Value = mean(Value), .groups = "drop") %>%
# reshape
pivot_wider(names_from = "research_name", values_from = "Value", names_repair = "universal") ->
soildat
## New names:
## • `soil-temp-5cm` -> `soil.temp.5cm`
## • `soil-vwc-5cm` -> `soil.vwc.5cm`
# TODO: plot soil data
glimpse(dat)
## Rows: 3,361
## Columns: 12
## Groups: Year, Date, Plot, Timepoint, Species, ID [3,361]
## $ Year <dbl> 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, …
## $ Date <date> 2021-10-08, 2021-10-08, 2021-10-08, 2021-10-08,…
## $ Plot <chr> "Control", "Control", "Control", "Control", "Con…
## $ Timepoint <chr> "(none)", "(none)", "(none)", "(none)", "(none)"…
## $ Species <chr> "Tulip Poplar", "Red Maple", "Red Maple", "Beech…
## $ ID <chr> "C1", "C10", "C12", "C14", "C16", "C17", "C2", "…
## $ CO2_lin_flux.estimate <dbl> 0.42493599, 0.22701875, 0.08174378, 0.29282769, …
## $ CO2_lin_r.squared <dbl> 0.9980241, 0.9926840, 0.9514107, 0.9952041, 0.84…
## $ CO2_rob_flux.estimate <dbl> 0.42512176, 0.22678729, 0.08119638, 0.29299455, …
## $ CH4_lin_flux.estimate <dbl> 0.1516792079, -0.0052212805, 0.1701407219, 0.000…
## $ CH4_lin_r.squared <dbl> 0.999189922, 0.602961702, 0.999108777, 0.0170388…
## $ CH4_rob_flux.estimate <dbl> 0.1517037352, -0.0052248314, 0.1698198573, 0.000…
# 1. Print summary
summary(dat)
## Year Date Plot Timepoint
## Min. :2021 Min. :2021-10-08 Length:3361 Length:3361
## 1st Qu.:2022 1st Qu.:2022-07-07 Class :character Class :character
## Median :2023 Median :2023-06-06 Mode :character Mode :character
## Mean :2023 Mean :2023-06-28
## 3rd Qu.:2024 3rd Qu.:2024-06-12
## Max. :2025 Max. :2025-09-12
## Species ID CO2_lin_flux.estimate
## Length:3361 Length:3361 Min. :-0.08419
## Class :character Class :character 1st Qu.: 0.11837
## Mode :character Mode :character Median : 0.30497
## Mean : 0.38523
## 3rd Qu.: 0.58891
## Max. : 3.49284
## CO2_lin_r.squared CO2_rob_flux.estimate CH4_lin_flux.estimate
## Min. :5.367e-05 Min. :-0.09171 Min. :-0.0970870
## 1st Qu.:9.682e-01 1st Qu.: 0.11806 1st Qu.: 0.0007698
## Median :9.937e-01 Median : 0.30576 Median : 0.0049256
## Mean :9.508e-01 Mean : 0.38510 Mean : 0.0259362
## 3rd Qu.:9.985e-01 3rd Qu.: 0.58729 3rd Qu.: 0.0168636
## Max. :1.000e+00 Max. : 3.48976 Max. : 0.9723704
## CH4_lin_r.squared CH4_rob_flux.estimate
## Min. :1.300e-07 Min. :-0.0892519
## 1st Qu.:1.048e-01 1st Qu.: 0.0007614
## Median :5.729e-01 Median : 0.0049023
## Mean :5.274e-01 Mean : 0.0258854
## 3rd Qu.:9.364e-01 3rd Qu.: 0.0167526
## Max. :1.000e+00 Max. : 0.9740040
# 2. ggplot(dat, ...)
# 3. Drop existing species column from dat
# 4. Read in TEMPEST_Trees_Instruments.csv
tree_inventory <- read.csv("TEMPEST_Trees_Instruments.csv") %>%
select(Sapflux_ID, spp) # don't need these
#
dat %>%
left_join(tree_inventory,
# in dat the column is "ID", while in the tree inventory it's
# "Sapflux_ID", so we tell left_join that these two columns
# correspond to each other
by = c("ID" = "Sapflux_ID"),
# this is optional but good 'defensive programming': we specify
# that we expect a many-to-one relationship (i.e., there shouldn't
# be multiple matches for a given sapflux tree!)
relationship = "many-to-one") ->
tree_dat
# To help readability, generate the common name from the species code
tree_dat %>%
mutate(Species = case_when(
spp == "ACRU" ~ "Red maple",
spp == "LITU" ~ "Tulip poplar",
spp == "FAGR" ~ "American beech"
)) ->
tree_dat
# Make a nice summary table to check things
tree_dat %>%
# for each plot and species...
group_by(Plot, Species) %>%
# ...count the number of distinct IDs...
summarise(value = n_distinct(ID), .groups = "drop") %>%
# ...and then reshape so that plot names run across the top
pivot_wider(names_from = "Plot") %>%
knitr::kable()
| Species | Control | Freshwater | Seawater |
|---|---|---|---|
| American beech | 6 | 6 | 6 |
| Red maple | 8 | 6 | 6 |
| Tulip poplar | 6 | 6 | 6 |
# Filter out the TEMPEST flood measurements
tree_dat %>%
filter(Timepoint %in% c("(none)", "Pre-Treatment")) ->
tree_dat_ac
message("Annual cycle dataset has ", nrow(tree_dat_ac), " rows and ",
ncol(tree_dat_ac), " columns")
## Annual cycle dataset has 2184 rows and 13 columns
# EVAN:
# 1. What data frame do we want to use to plot?
# 2. This plots each tree as a separate color. How useful is this? What are alternatives?
ggplot(data = tree_dat,
mapping = aes(x = Date, y = CO2_lin_flux.estimate, color = Species))+
geom_point() + # Add a layer of points
labs(title = "CO2 flux by species by date") # Add labels and a title
ggplot(data = tree_dat,
mapping = aes(x = Date, y = CH4_lin_flux.estimate, color = Species))+
geom_point() + # Add a layer of points
labs(title = "CH4 flux by species by date") # Add labels and a title
# Summarize by …?
The trees are experimental units but what we’re actually interested in is…
# EVAN: how do we want to summarize and then plot the annual cycle data?
library(dplyr)
library(lubridate)
##
## Attaching package: 'lubridate'
## The following object is masked from 'package:arrow':
##
## duration
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
#calculate average CO2 flux per year per species
tree_dat %>%
mutate(Year = year(Date)) %>% # Extract the year from the Date column
group_by(Year, Species) %>% # Group by Year and Species
summarise(avg_CO2_flux = mean(CO2_lin_flux.estimate, na.rm = TRUE), .groups = "drop") ->
avg_CO2flux_per_year_species
# View the resulting table
print(avg_CO2flux_per_year_species)
## # A tibble: 15 × 3
## Year Species avg_CO2_flux
## <dbl> <chr> <dbl>
## 1 2021 American beech 0.0893
## 2 2021 Red maple 0.142
## 3 2021 Tulip poplar 0.148
## 4 2022 American beech 0.383
## 5 2022 Red maple 0.310
## 6 2022 Tulip poplar 0.545
## 7 2023 American beech 0.417
## 8 2023 Red maple 0.303
## 9 2023 Tulip poplar 0.505
## 10 2024 American beech 0.386
## 11 2024 Red maple 0.269
## 12 2024 Tulip poplar 0.524
## 13 2025 American beech 0.281
## 14 2025 Red maple 0.294
## 15 2025 Tulip poplar 0.419
# Plotting the average CO2 flux by year and species
ggplot(avg_CO2flux_per_year_species, aes(x = Year, y = avg_CO2_flux, color = Species)) +
geom_line(size = 1) + # Add lines to connect points
geom_point(size = 3) + # Add points at each data point
labs(
title = "Average Annual CO2 Flux by Species",
x = "Year",
y = "Average CO2 Flux (g CO2 / m² / day)",
color = "Species"
) +
theme_minimal() + # Clean minimal theme
theme(
legend.position = "top", # Position the legend at the top
axis.text.x = element_text(angle = 45, hjust = 1) # Angle x-axis labels for better readability
)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
#calculate average CH4 flux per year per species
tree_dat %>%
mutate(Year = year(Date)) %>% # Extract the year from the Date column
group_by(Year, Species) %>% # Group by Year and Species
summarise(avg_CH4_flux = mean(CH4_lin_flux.estimate, na.rm = TRUE), .groups = "drop") ->
avg_CH4flux_per_year_species
# View the resulting table
print(avg_CH4flux_per_year_species)
## # A tibble: 15 × 3
## Year Species avg_CH4_flux
## <dbl> <chr> <dbl>
## 1 2021 American beech 0.00259
## 2 2021 Red maple 0.0465
## 3 2021 Tulip poplar 0.0119
## 4 2022 American beech 0.0173
## 5 2022 Red maple 0.0554
## 6 2022 Tulip poplar 0.0141
## 7 2023 American beech 0.0147
## 8 2023 Red maple 0.0469
## 9 2023 Tulip poplar 0.0165
## 10 2024 American beech 0.0148
## 11 2024 Red maple 0.0412
## 12 2024 Tulip poplar 0.0133
## 13 2025 American beech 0.0148
## 14 2025 Red maple 0.0471
## 15 2025 Tulip poplar 0.0130
# Plotting the average CH4 flux by year and species
ggplot(avg_CH4flux_per_year_species, aes(x = Year, y = avg_CH4_flux, color = Species)) +
geom_line(size = 1) + # Add lines to connect points
geom_point(size = 3) + # Add points at each data point
labs(
title = "Average Annual CH4 Flux by Species",
x = "Year",
y = "Average CO2 Flux (g CH4 / m² / day)",
color = "Species"
) +
theme_minimal() + # Clean minimal theme
theme(
legend.position = "top", # Position the legend at the top
axis.text.x = element_text(angle = 45, hjust = 1) # Angle x-axis labels for better readability
)
# Change “Seawater” to “Saltwater”
tree_dat <- tree_dat %>%
mutate(Plot = recode(Plot, "Seawater" = "Saltwater"))
#calculate average CO2 flux per date per species
# Summarizing by specific Date and Species
avg_CO2flux_per_day_species <- tree_dat %>%
group_by(Date, Species) %>% # Group by full Date and Species
summarise(avg_CO2_flux = mean(CO2_lin_flux.estimate, na.rm = TRUE), .groups = "drop")
# Ensure the Date column is in Date format (if needed)
avg_CO2flux_per_day_species$Date <- as.Date(avg_CO2flux_per_day_species$Date)
# Plotting the average flux by Date and Species
ggplot(avg_CO2flux_per_day_species, aes(x = Date, y = avg_CO2_flux, color = Species)) +
geom_line(size = 1) + # Add lines to connect points
geom_point(size = 3) + # Add points at each data point
labs(
title = "Average CO2 Flux by Date and Species",
x = "Date",
y = "Average CO2 Flux (g CO2 / m² / day)",
color = "Species"
) +
theme_minimal() + # Clean minimal theme
theme(
legend.position = "top", # Position the legend at the top
axis.text.x = element_text(angle = 45, hjust = 1) # Rotate x-axis labels for readability
)
#calculate average CH4 flux per date per species
# Summarizing by specific Date and Species
avg_CH4flux_per_day_species <- tree_dat %>%
group_by(Date, Species) %>% # Group by full Date and Species
summarise(avg_CH4_flux = mean(CH4_lin_flux.estimate, na.rm = TRUE), .groups = "drop")
# Ensure the Date column is in Date format (if needed)
avg_CH4flux_per_day_species$Date <- as.Date(avg_CH4flux_per_day_species$Date)
# Plotting the average CH4 flux by Date and Species
ggplot(avg_CH4flux_per_day_species, aes(x = Date, y = avg_CH4_flux, color = Species)) +
geom_line(size = 1) + # Add lines to connect points
geom_point(size = 3) + # Add points at each data point
labs(
title = "Average CH4 Flux by Date and Species",
x = "Date",
y = "Average CH4 Flux (g CO2 / m² / day)",
color = "Species"
) +
theme_minimal() + # Clean minimal theme
theme(
legend.position = "top", # Position the legend at the top
axis.text.x = element_text(angle = 45, hjust = 1) # Rotate x-axis labels for readability
)
# Specify the tree ID you want to remove (S7)
tree_id_to_remove <- "S7"
# Create a new dataset excluding the specified tree ID (S7)
tree_dat_no_S7 <- tree_dat %>%
filter(ID != tree_id_to_remove) # Exclude the tree with ID = "S7"
# Check column names to ensure 'ID' is correct
colnames(tree_dat)
## [1] "Year" "Date" "Plot"
## [4] "Timepoint" "Species" "ID"
## [7] "CO2_lin_flux.estimate" "CO2_lin_r.squared" "CO2_rob_flux.estimate"
## [10] "CH4_lin_flux.estimate" "CH4_lin_r.squared" "CH4_rob_flux.estimate"
## [13] "spp"
# Specify the tree ID you want to remove (S7)
tree_id_to_remove <- "S7"
# Calculate the average CH4 flux per date and species excluding tree "S7"
avg_CH4flux_no_S7 <- tree_dat_no_S7 %>%
group_by(Date, Species) %>% # Group by Date and Species
summarise(avg_CH4_flux = mean(CH4_lin_flux.estimate, na.rm = TRUE), .groups = "drop")
# Ensure the Date column is in Date format (if needed)
avg_CH4flux_no_S7$Date <- as.Date(avg_CH4flux_no_S7$Date)
# Plotting the average CH4 flux by Date and Species (excluding tree "S7")
ggplot(avg_CH4flux_no_S7, aes(x = Date, y = avg_CH4_flux, color = Species)) +
geom_line(size = 1) + # Add lines to connect points
geom_point(size = 3) + # Add points at each data point
labs(
title = "Average CH4 Flux by Year and Species (Excluding Tree S7)",
x = "Date",
y = "Average CH4 Flux (g CH4 / m² / day)",
color = "Species"
) +
theme_minimal() + # Clean minimal theme
theme(
legend.position = "top", # Position the legend at the top
axis.text.x = element_text(angle = 45, hjust = 1) # Rotate x-axis labels for readability
)
avg_CH4flux_stats_filtered <- tree_dat %>%
filter(ID != "S7") %>% # remove tree S7
mutate(Year = year(Date)) %>% # extract year
filter(Year != 2021) %>% # exclude 2021
group_by(Year, Species) %>% # group by Year for annual average
summarise(
avg_CH4_flux = mean(CH4_lin_flux.estimate, na.rm = TRUE),
sd_CH4_flux = sd(CH4_lin_flux.estimate, na.rm = TRUE),
.groups = "drop"
)
# Plot using the filtered dataset
ggplot(avg_CH4flux_stats_filtered, aes(x = Year, y = avg_CH4_flux, color = Species)) +
geom_line(size = 1) +
geom_point(size = 3) +
geom_errorbar(aes(
ymin = avg_CH4_flux - sd_CH4_flux,
ymax = avg_CH4_flux + sd_CH4_flux
), width = 0.2) +
labs(
title = "Average Annual CH4 Flux by Species (Excluding Tree S7 and 2021)",
x = "Year",
y = "Average CH4 Flux (g CH4 / m² / day)",
color = "Species"
) +
theme_minimal() +
theme(
legend.position = "top",
axis.text.x = element_text(angle = 45, hjust = 1)
)
avg_CH4flux_stats_filtered <- tree_dat %>%
filter(ID != "S7") %>%
mutate(Year = lubridate::year(Date)) %>%
filter(Year != 2021) %>%
group_by(Year, Species, Plot) %>% # <- include Plot here
summarise(
avg_CH4_flux = mean(CH4_lin_flux.estimate, na.rm = TRUE),
sd_CH4_flux = sd(CH4_lin_flux.estimate, na.rm = TRUE),
.groups = "drop"
)
ggplot(avg_CH4flux_stats_filtered,
aes(x = Year, y = avg_CH4_flux, color = Species)) +
geom_line() +
geom_point() +
geom_errorbar(aes(
ymin = avg_CH4_flux - sd_CH4_flux,
ymax = avg_CH4_flux + sd_CH4_flux
), width = 0.2) +
facet_wrap(~ Plot) + # <- split panels by plot
theme_minimal()
tree_soil_dat <- tree_dat %>%
mutate(
Date = as.Date(Date),
Plot = recode(Plot, "C" = "Control", "F" = "Freshwater", "S" = "Seawater")
) %>%
left_join(
soildat %>%
mutate(Date = as.Date(Date)) %>%
group_by(Plot, Date) %>%
summarise(soil.temp.5cm = mean(soil.temp.5cm, na.rm = TRUE), .groups = "drop"),
by = c("Plot", "Date"),
relationship = "many-to-one"
)
# Ensure Date class matches
tree_dat$Date <- as.Date(tree_dat$Date)
soildat$Date <- as.Date(soildat$Date)
# Optional: recode plots to full names
tree_dat <- tree_dat %>%
mutate(Plot = recode(Plot, "C" = "Control", "F" = "Freshwater", "S" = "Seawater"))
soildat <- soildat %>%
mutate(Plot = recode(Plot, "C" = "Control", "F" = "Freshwater", "S" = "Seawater"))
# Join soil temperature
tree_soil_dat <- tree_dat %>%
left_join(
soildat %>%
group_by(Plot, Date) %>%
summarise(soil.temp.5cm = mean(soil.temp.5cm, na.rm = TRUE), .groups = "drop"),
by = c("Plot", "Date")
)
# Check for unmatched rows
summary(tree_soil_dat$soil.temp.5cm)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 1.578 12.309 18.594 16.279 20.800 24.753 679
# Prepare data for plotting
tree_soil_plot <- tree_soil_dat %>%
filter(
!is.na(soil.temp.5cm),
!is.nan(soil.temp.5cm),
!is.na(CH4_lin_flux.estimate),
!is.nan(CH4_lin_flux.estimate)
)
ggplot(tree_soil_plot,
aes(x = soil.temp.5cm, y = CH4_lin_flux.estimate, color = Species)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE) +
facet_wrap(~ Plot) +
theme_minimal() +
labs(
title = "CH4 Flux vs Soil Temperature by Plot",
x = "Soil Temperature (5 cm, °C)",
y = "CH4 Flux (g CH4 / m² / day)",
color = "Species"
)
## `geom_smooth()` using formula = 'y ~ x'
# Prepare the dataset excluding tree S7
tree_soil_plot_no_S7 <- tree_dat %>%
filter(ID != "S7") %>% # remove tree S7
mutate(
Date = as.Date(Date),
Plot = recode(Plot, "C" = "Control", "F" = "Freshwater", "S" = "Seawater")
) %>%
left_join(
soildat %>%
mutate(Date = as.Date(Date)) %>%
group_by(Plot, Date) %>%
summarise(soil.temp.5cm = mean(soil.temp.5cm, na.rm = TRUE), .groups = "drop"),
by = c("Plot", "Date"),
relationship = "many-to-one"
) %>%
filter(
!is.na(soil.temp.5cm),
!is.nan(soil.temp.5cm),
!is.na(CH4_lin_flux.estimate),
!is.nan(CH4_lin_flux.estimate)
)
# Plot using the new dataset
ggplot(tree_soil_plot_no_S7,
aes(x = soil.temp.5cm, y = CH4_lin_flux.estimate, color = Species)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE) +
facet_wrap(~ Plot) +
theme_minimal() +
labs(
x = "Soil Temperature (5 cm, °C)",
y = "CH4 Flux (g CH4 / m² / day)",
title = "CH4 Flux vs Soil Temperature by Plot (Excluding Tree S7)"
)
## `geom_smooth()` using formula = 'y ~ x'
# Prepare the dataset including S7 for CO2
tree_soil_plot_CO2 <- tree_dat %>%
mutate(
Date = as.Date(Date),
Plot = recode(Plot, "C" = "Control", "F" = "Freshwater", "S" = "Seawater")
) %>%
left_join(
soildat %>%
mutate(Date = as.Date(Date)) %>%
group_by(Plot, Date) %>%
summarise(soil.temp.5cm = mean(soil.temp.5cm, na.rm = TRUE), .groups = "drop"),
by = c("Plot", "Date"),
relationship = "many-to-one"
) %>%
filter(
!is.na(soil.temp.5cm),
!is.nan(soil.temp.5cm),
!is.na(CO2_lin_flux.estimate),
!is.nan(CO2_lin_flux.estimate)
)
# Plot using the new dataset
ggplot(tree_soil_plot_CO2,
aes(x = soil.temp.5cm, y = CO2_lin_flux.estimate, color = Species)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE) +
facet_wrap(~ Plot) +
theme_minimal() +
labs(
x = "Soil Temperature (5 cm, °C)",
y = "CO2 Flux (g CO2 / m² / day)",
title = "CO2 Flux vs Soil Temperature by Plot (Including Tree S7)"
)
## `geom_smooth()` using formula = 'y ~ x'