Module 10: Capstone Project: End-to-End Environmental Case Study & Reproducible Reporting
Module 10: Capstone Project: End-to-End Environmental Case Study & Reproducible Reporting
Complete real-world investigation: analyzing river water quality, nutrient runoff, and heavy metal concentrations along an urban-agricultural gradient, generating reproducible R Markdown / Quarto reports and high-res figures.
10. Capstone Project: End-to-End Environmental Case Study & Reproducible Reporting
In this capstone module, you synthesize all foundational statistical concepts into an end-to-end scientific research project. You will analyze a real-world multi-site watershed investigation assessing the impact of urbanization and agricultural runoff on river ecosystem health.
The 6-Step Publication-Grade Workflow:
- Data Ingestion & Tidy Architecture: Load field monitoring matrices and inspect variable types.
- Exploratory Data Analysis: Compute central tendency, spread, and standard error across ecological strata.
- Assumption Verification: Test residual normality (Shapiro-Wilk) and homoscedasticity (Levene's test).
- Inferential Modeling: Execute ANOVA, post-hoc Tukey HSD, and linear regression models.
- Publication Graphics: Build multi-panel figures (
ggplot2+patchwork) with statistical annotation brackets. - Reproducible Synthesis: Compile findings into dynamic R Markdown / Quarto documents with complete session info metadata.
Complete Production R Script: The Watershed Case Study
# Complete Capstone Watershed Analysis Script
library(tidyverse)
# 1. Generate Synthetic Multi-Site Watershed Dataset (n = 80 samples)
set.seed(2026)
watershed <- tibble(
sample_id = paste0("SAMP_", sprintf("%03d", 1:80)),
zone = factor(rep(c("Pristine_Headwater", "Agricultural_Midstream", "Urban_Downstream", "Restored_Wetland"), each = 20)),
turbidity = c(rnorm(20, 2.1, 0.4), rnorm(20, 8.4, 1.2), rnorm(20, 19.5, 3.1), rnorm(20, 4.2, 0.8)), # NTU
nitrate = c(rnorm(20, 0.35, 0.08), rnorm(20, 6.2, 1.1), rnorm(20, 8.9, 1.4), rnorm(20, 1.8, 0.4)), # mg/L
bio_index = c(rnorm(20, 88, 6), rnorm(20, 52, 8), rnorm(20, 24, 7), rnorm(20, 71, 5)) # EPT Biotic Index
)
# 2. Descriptive Statistics Summary Table
stats_summary <- watershed %>%
group_by(zone) %>%
summarise(
n = n(),
mean_bio = mean(bio_index),
sd_bio = sd(bio_index),
sem_bio = sd(bio_index) / sqrt(n()),
mean_nitrate = mean(nitrate),
sd_nitrate = sd(nitrate)
)
print(stats_summary)
# 3. Hypothesis Testing: Does Macroinvertebrate Health Differ Significantly by Zone?
anova_bio <- aov(bio_index ~ zone, data = watershed)
summary(anova_bio)
# Check Normality of Model Residuals
shapiro_res <- shapiro.test(residuals(anova_bio))
cat("Shapiro-Wilk p-value:", shapiro_res$p.value, "
")
# Post-Hoc Comparisons
tukey_bio <- TukeyHSD(anova_bio)
print(tukey_bio)
# 4. Regression Analysis: Predicting Biotic Health from Nutrient Pollution
model_nitrate_bio <- lm(bio_index ~ nitrate, data = watershed)
summary(model_nitrate_bio)
# 5. Publication Visualization with ggplot2
fig_a <- ggplot(watershed, aes(x = zone, y = bio_index, fill = zone)) +
geom_boxplot(alpha = 0.7, width = 0.5, outlier.shape = NA) +
geom_jitter(width = 0.15, alpha = 0.8, size = 2, shape = 21, color = "black") +
theme_bw(base_size = 11) +
theme(legend.position = "none", axis.text.x = element_text(angle = 20, hjust = 1)) +
labs(title = "A: Biotic Integrity Across River Zones", y = "Macroinvertebrate Biotic Index", x = "")
fig_b <- ggplot(watershed, aes(x = nitrate, y = bio_index)) +
geom_point(aes(color = zone), size = 2.5, alpha = 0.85) +
geom_smooth(method = "lm", color = "black", se = TRUE) +
theme_bw(base_size = 11) +
labs(title = "B: Biotic Health vs. Nitrate Pollution", x = "Nitrate Concentration (mg/L)", y = "Biotic Index")
# Print figures
print(fig_a)
print(fig_b)How to Report Statistical Results in Scientific Journals
Scientific Paper Reporting Template:
"One-way ANOVA revealed a statistically significant difference in macroinvertebrate biotic integrity across the four river zones ($F_{3, 76} = 284.1, p < 0.001, \eta^2 = 0.91$). Post-hoc pairwise comparisons using Tukey's HSD indicated that pristine headwaters exhibited significantly higher biotic health ($ ext{Mean} = 88.0, ext{SD} = 6.0$) compared to midstream agricultural reaches ($ ext{Mean} = 52.0, ext{SD} = 8.0, p < 0.001$) and urban downstream sites ($ ext{Mean} = 24.0, ext{SD} = 7.0, p < 0.001$). Linear regression analysis demonstrated that nitrate pollution explained $86\%$ of the variation in biotic degradation ($\beta = -7.21 ext{ mg}^{-1} ext{L}, R^2 = 0.86, p < 0.001$)."
Common Pitfalls & Troubleshooting Checklist
- Java Heap Exhaustion (GATK / Picard): If processes terminate with
java.lang.OutOfMemoryError, set your environment variable explicitly:export _JAVA_OPTIONS="-Xmx16g -Djava.io.tmpdir=/tmp". - Index Timestamp Mismatch: If tools complain that a BAM index is missing or out of date, regenerate with
samtools index -@ 4 sample.bam. - Conda Channel Conflicts: Avoid ABI incompatibilities between bioconda and conda-forge by setting strict priority:
conda config --set channel_priority strict. - Chromosome Naming Contamination: Always verify whether reference FASTA headers use
chr1or1; divergent naming causes silent alignment drops.