Campus Water Quality: E. coli and Total Coliform

Marc Los Huertos

EA030 · Compiled 2026-04-24

Background Colilert Method Data Pipeline Import & Clean Dilution Correction Visualise ANOVA Interpret Summary

🎯 Learning Outcomes

  1. Explain why E. coli and total coliform are used as indicator organisms for water quality, and describe the health risks associated with each.
  2. Describe how the Colilert Quanti-Tray system produces MPN (Most Probable Number) estimates and why dilution correction is necessary.
  3. Import the class Colilert data from a CSV export, standardise column names, and apply dilution correction factors to compute true MPN/100 mL.
  4. Visualise MPN distributions across sampling sites and compare them to California Basin Plan recreational standards.
  5. Run a one-way ANOVA and Kruskal-Wallis test to determine whether bacterial concentrations differ significantly among campus sites.
  6. Interpret results in the context of campus hydrology and propose monitoring or management recommendations.

What this activity covers. Each semester, EA030 students collect water samples from three campus sites — pHake Lake, Seal Court, and the Pomona Bioswale — and analyse them for E. coli and total coliform using the Colilert Quanti-Tray method. This module explains the biology behind the test, walks through the data pipeline from raw Colilert readings to corrected MPN/100 mL, and guides you through the statistical analysis of class results. The goal is not just to run a t-test on your own group’s two samples, but to analyse the entire class dataset — which gives enough replication to make meaningful statistical inferences about site differences.


1. Background: Why E. coli?

Escherichia coli (E. coli) is a bacterium found in the intestines of humans and warm-blooded animals, where most strains are harmless and aid digestion. However, its presence in water is a primary public health concern for two reasons.

As an indicator organism. E. coli itself is not always dangerous in water, but its presence signals fecal contamination — meaning the water has been in contact with human or animal waste. Where fecal matter goes, pathogenic organisms follow: Cryptosporidium, Giardia, Salmonella, Hepatitis A, and noroviruses all co-occur with fecal contamination and are far harder and more expensive to detect directly. Testing for E. coli is a fast, inexpensive proxy for the presence of the entire suite of fecal pathogens.

As a direct pathogen. Certain strains — especially O157:H7 (Shiga-toxin-producing E. coli, STEC) — cause severe illness including haemorrhagic diarrhoea, urinary tract infections, and haemolytic uraemic syndrome (HUS), which can cause kidney failure and death. These strains enter water through agricultural runoff from cattle operations, sewage spills, and stormwater overflows.

Sources of fecal contamination on and near the Pomona campus

The three sampling sites capture distinct potential contamination pathways:

Site Potential sources
pHake Lake Waterfowl (geese, ducks) — major fecal contributors; irrigation return flow; adjacent lawn runoff
Seal Court Urban stormwater from hardscaped surfaces; pet waste; adjacent building drain connections
Pomona Bioswale Designed to filter runoff — acts as a treatment system; captures street and parking lot runoff before it enters groundwater

📌 Total coliform vs. E. coli — what’s the difference? Total coliform includes a broader group of bacteria in the family Enterobacteriaceae (including E. coli, Klebsiella, and Enterobacter). Some are environmental organisms that do not indicate fecal contamination — they naturally inhabit soil and plant matter. E. coli is a subset of total coliform that is almost exclusively fecal in origin. This is why regulatory standards for drinking water use E. coli as the indicator, while total coliform is used as a more sensitive screening test: if total coliform is absent, E. coli cannot be present, and no further testing is needed.

Regulatory standards

Use Standard Regulation
Contact recreation (swimming) 320 MPN/100 mL (single sample) CA Basin Plan REC-1
Contact recreation (geometric mean) 100 MPN/100 mL CA Basin Plan REC-1
Non-contact recreation 1,000 MPN/100 mL CA Basin Plan REC-2
Drinking water 0 (any detection) EPA SDWA / SWTR

2. The Colilert Method

2.1 How Colilert works

Colilert (IDEXX Laboratories) is an enzyme-substrate method that simultaneously detects total coliform and E. coli in a single test. The reagent contains two chromogenic substrates:

The sample is sealed in an IDEXX Quanti-Tray — a plastic tray with 97 large wells and 48 small wells — and incubated at 35°C for 24 hours. After incubation, you count the number of yellow (total coliform) and fluorescent (E. coli) wells and use the IDEXX MPN table to convert well counts to MPN/100 mL.

Step 1
Collect sample
100 mL in sterile bottle. Keep cold, analyse within 6 hours. If turbid, dilute 1:10 with sterile water.
Step 2
Add Colilert
Add one Colilert reagent sachet to the 100 mL sample. Mix until dissolved.
Step 3
Seal tray
Pour reagent mixture into a Quanti-Tray. Heat-seal with the Quanti-Tray Sealer. Label with site and time.
Step 4
Incubate & read
35°C ± 0.5°C for 24–28 hours. Count yellow wells (TC) and fluorescent wells (EC) under UV lamp.

2.2 The MPN concept

MPN (Most Probable Number) is a statistical estimate of microbial density based on the pattern of positive (enzyme-active) and negative wells. It is not a direct count — it is the bacterial concentration most likely to produce the observed pattern of positives. The Quanti-Tray system uses a maximum-likelihood algorithm to convert the (large positive wells, small positive wells) pair to an MPN/100 mL value.

⚙️ MPN and dilution correction. If a sample was too turbid to analyse undiluted, it is diluted 1:10 before adding Colilert. The Quanti-Tray then sees only 1/10 of the original bacteria. To recover the true concentration:

MPNcorrected = MPNraw × dilution factor

A raw reading of 450 MPN/100 mL from a 1:10 diluted sample represents a true concentration of 4,500 MPN/100 mL in the original water. Failing to apply this correction systematically underestimates bacterial concentrations from turbid samples — which are typically the most contaminated.

2.3 Reading results — what you look for in the tray

Observation Indicator Count
Yellow large well Total coliform positive Count as TC+
Yellow small well Total coliform positive Count as TC+
Blue fluorescence (UV) large well E. coli positive Count as EC+
Blue fluorescence (UV) small well E. coli positive Count as EC+
Colourless, no fluorescence Negative Not counted

All E. coli-positive wells are also total coliform positive (yellow), because E. coli produces both enzymes. But not all yellow wells fluoresce — environmental coliforms produce β-galactosidase but not β-glucuronidase.


3. The Data Pipeline

Before writing any R code, it helps to map the full workflow from Colilert tray to statistical result.

Field         Collect 100 mL sample → dilute if turbid → note dilution factor
↓
Lab          Add Colilert → seal Quanti-Tray → incubate 24 hr → count yellow/UV wells
↓
Google Sheet Each group enters: Site_ID, Dilution, TC_wells, EC_wells, raw MPN
↓
R: Import    read_csv() → standardise column names → remove controls
↓
R: Clean     Apply dilution correction → handle NAs → check ranges
↓
R: Analyse   Visualise → compare to standards → ANOVA / Kruskal-Wallis
↓
Interpret    Which sites exceed Basin Plan limits? Do sites differ? Why?
                 (This is the question you answer in your lab write-up)


4. Import and Clean the Class Data

4.1 Packages

library(tidyverse)
library(knitr)
library(kableExtra)

4.2 Import

Export the class Google Sheet as CSV and place it in your project’s data/ folder. The expected column structure from the Colilert data entry sheet is shown below.

# ── Use this chunk with your real class data ─────────────────────────────────
# MPN_data <- read_csv("data/Ecoli_Total_Coliform_Data_Entry.csv")
# ── Simulated class dataset ───────────────────────────────────────────────────
# Three sites, three sampling rounds, ~4 student groups per site per round
set.seed(2025)

n_groups <- 4   # groups per site per round
rounds   <- 3   # sampling rounds across the semester
sites    <- c("pHake Lake", "Seal Court", "Pomona Bioswale")

MPN_data <- expand.grid(
  Site_ID       = sites,
  Round         = paste0("Round_", 1:rounds),
  Group         = paste0("Group_", 1:n_groups),
  stringsAsFactors = FALSE
) %>%
  as_tibble() %>%
  mutate(
    # Dilution: pHake Lake often turbid; Bioswale usually undiluted
    Dilution = case_when(
      Site_ID == "pHake Lake"     ~ sample(c("Undiluted", "Diluted, 1:10"),
                                            n(), replace = TRUE,
                                            prob = c(0.4, 0.6)),
      Site_ID == "Seal Court"     ~ sample(c("Undiluted", "Diluted, 1:10"),
                                            n(), replace = TRUE,
                                            prob = c(0.7, 0.3)),
      Site_ID == "Pomona Bioswale" ~ "Undiluted"
    ),
    # Raw MPN from Quanti-Tray (log-normal; pHake Lake highest due to waterfowl)
    raw_TC = round(case_when(
      Site_ID == "pHake Lake"      ~ rlnorm(n(), log(280),  0.9),
      Site_ID == "Seal Court"      ~ rlnorm(n(), log(150),  1.0),
      Site_ID == "Pomona Bioswale" ~ rlnorm(n(), log(40),   0.8)
    )),
    raw_EC = round(case_when(
      Site_ID == "pHake Lake"      ~ rlnorm(n(), log(180),  1.0),
      Site_ID == "Seal Court"      ~ rlnorm(n(), log(90),   1.0),
      Site_ID == "Pomona Bioswale" ~ rlnorm(n(), log(18),   0.9)
    ))
  )

head(MPN_data)
## # A tibble: 6 × 6
##   Site_ID         Round   Group   Dilution      raw_TC raw_EC
##   <chr>           <chr>   <chr>   <chr>          <dbl>  <dbl>
## 1 pHake Lake      Round_1 Group_1 Undiluted        361    131
## 2 Seal Court      Round_1 Group_1 Undiluted         15    218
## 3 Pomona Bioswale Round_1 Group_1 Undiluted         15     17
## 4 pHake Lake      Round_2 Group_1 Diluted, 1:10    671    500
## 5 Seal Court      Round_2 Group_1 Undiluted         87     20
## 6 Pomona Bioswale Round_2 Group_1 Undiluted          9     13

4.3 Remove controls

The data entry sheet includes positive and negative controls run by the instructor. These are not environmental samples and must be excluded before any analysis.

MPN_data <- MPN_data %>%
  filter(!grepl("Control", Site_ID, ignore.case = TRUE))

cat("Rows after removing controls:", nrow(MPN_data), "\n")
## Rows after removing controls: 36

4.4 Column names and type coercion

Real Colilert CSV exports often have spaces in column headers and store MPN values as character (especially when the IDEXX table shows “>2419.6” for fully positive trays). This chunk standardises everything.

# With real data, uncomment this line to standardise column names:
# library(janitor)
# MPN_data <- clean_names(MPN_data)

# Coerce MPN columns to numeric; ">" prefix produces NA — handle explicitly
MPN_data <- MPN_data %>%
  mutate(
    # Replace ">2419.6" (fully positive tray) with the IDEXX maximum value
    raw_TC = case_when(
      is.character(raw_TC) & grepl("^>", raw_TC) ~ 2419.6,
      TRUE ~ as.numeric(as.character(raw_TC))
    ),
    raw_EC = case_when(
      is.character(raw_EC) & grepl("^>", raw_EC) ~ 2419.6,
      TRUE ~ as.numeric(as.character(raw_EC))
    ),
    # Treat remaining NAs as 0 (no positive wells observed)
    raw_TC = replace_na(raw_TC, 0),
    raw_EC = replace_na(raw_EC, 0)
  )

⚠️ “>2419.6” is the Quanti-Tray ceiling, not a missing value. When every well in the tray is positive, the IDEXX MPN table reports “>2419.6 MPN/100 mL” — meaning the true concentration is at least that high but cannot be precisely estimated. These samples need dilution and re-analysis. Do NOT replace this value with NA. For statistical analysis, use the lower bound 2419.6 (conservative) and note this as a censored observation in your methods.


5. Dilution Correction

This is the most common source of error in student Colilert analyses. Apply the correction before any visualisation or statistics.

MPN_data <- MPN_data %>%
  mutate(
    # Extract dilution factor as a number
    dilution_factor = case_when(
      Dilution == "Diluted, 1:10"  ~ 10,
      Dilution == "Diluted, 1:100" ~ 100,
      TRUE                         ~ 1      # Undiluted or any other value
    ),
    # Apply correction: corrected MPN = raw MPN × dilution factor
    TC_MPN = raw_TC * dilution_factor,
    EC_MPN = raw_EC * dilution_factor
  )

# Sanity check: corrected values should be ≥ raw values
MPN_data %>%
  group_by(Site_ID, Dilution) %>%
  summarise(
    n        = n(),
    mean_raw_EC  = round(mean(raw_EC), 1),
    mean_corr_EC = round(mean(EC_MPN), 1),
    .groups = "drop"
  ) %>%
  kbl(caption = "Effect of dilution correction on mean E. coli MPN") %>%
  kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE)
Effect of dilution correction on mean E. coli MPN
Site_ID Dilution n mean_raw_EC mean_corr_EC
Pomona Bioswale Undiluted 12 21.7 21.7
Seal Court Diluted, 1:10 4 88.0 880.0
Seal Court Undiluted 8 124.8 124.8
pHake Lake Diluted, 1:10 6 538.0 5380.0
pHake Lake Undiluted 6 272.2 272.2

💡 Always verify your dilution correction by inspection. After applying the correction, sort by EC_MPN descending and scan the top values. Any sample with a 1:10 dilution should have a corrected MPN 10× its raw value. If you see corrected values that are lower than raw values, or the same as raw values for diluted samples, your correction code has a bug.


6. Visualise Results

6.1 MPN distributions by site

site_colors <- c(
  "pHake Lake"      = "#2e7d94",
  "Seal Court"      = "#c8832a",
  "Pomona Bioswale" = "#4a7c5e"
)

ggplot(MPN_data, aes(x = Site_ID, y = EC_MPN + 1, fill = Site_ID)) +
  geom_boxplot(alpha = 0.8, outlier.shape = 21, outlier.size = 2.5,
               outlier.fill = "white") +
  geom_jitter(width = 0.12, size = 2.2, alpha = 0.65,
              aes(color = Site_ID)) +
  # California Basin Plan REC-1 single-sample standard
  geom_hline(yintercept = 320, linetype = "dashed",
             color = "#c0392b", linewidth = 0.9) +
  annotate("text", x = 0.6, y = 360,
           label = "CA REC-1: 320 MPN/100 mL",
           color = "#c0392b", hjust = 0, size = 3.2, fontface = "italic") +
  geom_hline(yintercept = 1000, linetype = "dotted",
             color = "#7a3a00", linewidth = 0.7) +
  annotate("text", x = 0.6, y = 1100,
           label = "CA REC-2: 1,000 MPN/100 mL",
           color = "#7a3a00", hjust = 0, size = 3.2, fontface = "italic") +
  scale_y_log10(
    labels = scales::comma,
    breaks = c(1, 10, 100, 320, 1000, 10000)
  ) +
  scale_fill_manual(values = site_colors,  guide = "none") +
  scale_color_manual(values = site_colors, guide = "none") +
  labs(
    title    = "E. coli MPN/100 mL — Pomona College campus sampling sites",
    subtitle = "log\u2081\u2080 scale  \u00b7  dashed = contact recreation  \u00b7  dotted = non-contact standard",
    x = NULL, y = "E. coli (MPN/100 mL + 1, log scale)"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title    = element_text(family = "serif", face = "bold",
                                 color = "#1e3a2f", size = 13),
    plot.subtitle = element_text(color = "#7a7a72", size = 10),
    panel.grid.major.x = element_blank(),
    panel.grid.minor   = element_blank()
  )

6.2 Total coliform vs. E. coli scatter

The ratio of TC to EC reveals the proportion of contamination that is fecal vs. environmental in origin. Points near the 1:1 line indicate predominantly fecal contamination; points well above it indicate a mix of environmental and fecal coliforms.

ggplot(MPN_data, aes(x = TC_MPN + 1, y = EC_MPN + 1, color = Site_ID)) +
  geom_point(size = 2.8, alpha = 0.75) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed",
              color = "#7a7a72", linewidth = 0.8) +
  annotate("text", x = 20, y = 28, label = "TC = EC (1:1 line)",
           color = "#7a7a72", size = 3, angle = 42, fontface = "italic") +
  scale_x_log10(labels = scales::comma) +
  scale_y_log10(labels = scales::comma) +
  scale_color_manual(values = site_colors, name = NULL) +
  labs(
    title    = "Total coliform vs. E. coli (MPN/100 mL + 1)",
    subtitle = "Points above the 1:1 line have TC > EC — environmental coliforms present",
    x = "Total coliform MPN/100 mL (log scale)",
    y = "E. coli MPN/100 mL (log scale)"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title    = element_text(family = "serif", face = "bold",
                                 color = "#1e3a2f", size = 13),
    plot.subtitle = element_text(color = "#7a7a72", size = 9.5),
    legend.position = "bottom"
  )

6.3 Exceedance summary table

MPN_data %>%
  group_by(Site_ID) %>%
  summarise(
    n           = n(),
    gm_EC       = round(exp(mean(log(EC_MPN + 1))), 1),
    median_EC   = round(median(EC_MPN), 1),
    pct_rec1    = round(100 * mean(EC_MPN > 320),  1),
    pct_rec2    = round(100 * mean(EC_MPN > 1000), 1),
    n_diluted   = sum(dilution_factor > 1),
    .groups     = "drop"
  ) %>%
  kbl(
    caption   = "E. coli summary by site (corrected MPN/100 mL)",
    col.names = c("Site", "N", "Geometric mean", "Median",
                  "% > REC-1 (320)", "% > REC-2 (1000)", "Diluted samples")
  ) %>%
  kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
  row_spec(which(MPN_data %>%
                   group_by(Site_ID) %>%
                   summarise(pct = mean(EC_MPN > 320), .groups="drop") %>%
                   pull(pct) > 0.25),
           bold = TRUE, background = "#fff5f5")
E. coli summary by site (corrected MPN/100 mL)
Site N Geometric mean Median % > REC-1 (320) % > REC-2 (1000) Diluted samples
Pomona Bioswale 12 17.7 15.0 0.0 0.0 0
Seal Court 12 182.3 172.0 33.3 16.7 4
pHake Lake 12 669.0 450.5 58.3 33.3 6

7. Statistical Tests: Do Sites Differ?

With the full class dataset (all groups, all rounds), we have enough replication to test whether bacterial concentrations differ significantly among sites. We use both ANOVA on log-transformed values and Kruskal-Wallis as a non-parametric check.

7.1 Log-transform and assumption check

MPN_data <- MPN_data %>%
  mutate(log_EC = log(EC_MPN + 1))

aov_ec <- aov(log_EC ~ Site_ID, data = MPN_data)

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
qqnorm(residuals(aov_ec), main = "Q-Q: log(EC_MPN + 1) residuals",
       col = "#2e7d94", pch = 19, cex = 0.8)
qqline(residuals(aov_ec), col = "#c8832a", lwd = 2)
plot(fitted(aov_ec), residuals(aov_ec),
     xlab = "Fitted values", ylab = "Residuals",
     main = "Residuals vs. fitted",
     col = "#2e7d94", pch = 19, cex = 0.8)
abline(h = 0, col = "#c8832a", lwd = 2, lty = 2)

par(mfrow = c(1, 1))

📌 Why log-transform MPN data? Raw MPN values are highly right-skewed — a few very high readings from storm events or turbid samples dominate the arithmetic mean. Taking log(MPN + 1) pulls outliers toward the bulk of the data, making the residuals more normally distributed and ANOVA assumptions more defensible. The “+1” prevents log(0) errors when a sample had no positive wells. This is the same reason the geometric mean is used instead of the arithmetic mean for regulatory assessments.

7.2 One-way ANOVA

summary(aov_ec)
##             Df Sum Sq Mean Sq F value   Pr(>F)    
## Site_ID      2  81.38   40.69   20.67 1.51e-06 ***
## Residuals   33  64.96    1.97                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

7.3 Tukey HSD post-hoc

tukey_ec <- TukeyHSD(aov_ec)
print(tukey_ec)
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = log_EC ~ Site_ID, data = MPN_data)
## 
## $Site_ID
##                                 diff        lwr        upr     p adj
## Pomona Bioswale-pHake Lake -3.634194 -5.0396383 -2.2287492 0.0000010
## Seal Court-pHake Lake      -1.300248 -2.7056927  0.1051964 0.0743409
## Seal Court-Pomona Bioswale  2.333946  0.9285011  3.7393901 0.0007757
tukey_tbl <- broom::tidy(tukey_ec) %>%
  select(contrast, estimate, conf.low, conf.high, adj.p.value) %>%
  mutate(across(where(is.numeric), ~ round(.x, 3)))

tukey_tbl %>%
  kbl(
    caption   = "Tukey HSD pairwise comparisons — log(E. coli MPN + 1)",
    col.names = c("Comparison", "Difference (log scale)",
                  "Lower 95% CI", "Upper 95% CI", "Adj. p-value")
  ) %>%
  kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
  row_spec(which(tukey_tbl$adj.p.value < 0.05),
           bold = TRUE, background = "#f1f8f1")
Tukey HSD pairwise comparisons — log(E. coli MPN + 1)
Comparison Difference (log scale) Lower 95% CI Upper 95% CI Adj. p-value
Pomona Bioswale-pHake Lake -3.634 -5.040 -2.229 0.000
Seal Court-pHake Lake -1.300 -2.706 0.105 0.074
Seal Court-Pomona Bioswale 2.334 0.929 3.739 0.001

7.4 Kruskal-Wallis (non-parametric check)

kruskal.test(EC_MPN ~ Site_ID, data = MPN_data)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  EC_MPN by Site_ID
## Kruskal-Wallis chi-squared = 22.066, df = 2, p-value = 1.616e-05

💡 Do ANOVA and Kruskal-Wallis agree? If both are significant (or both non-significant), you can be confident in the conclusion. If they disagree, examine the Q-Q plot carefully — a few extreme outliers can make ANOVA non-significant while Kruskal-Wallis detects a real difference. With MPN data from a class field exercise, Kruskal-Wallis is often the more appropriate primary test because the sample sizes per site are small and log-normality is hard to verify.

7.5 Total coliform: same tests

aov_tc <- aov(log(TC_MPN + 1) ~ Site_ID, data = MPN_data)
summary(aov_tc)
##             Df Sum Sq Mean Sq F value   Pr(>F)    
## Site_ID      2  83.54   41.77   24.21 3.38e-07 ***
## Residuals   33  56.94    1.73                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
kruskal.test(TC_MPN ~ Site_ID, data = MPN_data)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  TC_MPN by Site_ID
## Kruskal-Wallis chi-squared = 23.197, df = 2, p-value = 9.18e-06

8. Interpreting Your Results

8.1 Connecting statistics to site characteristics

Once you know which sites differ significantly, the next question is why. Use this framework:

MPN_data %>%
  group_by(Site_ID) %>%
  summarise(
    n           = n(),
    gm_EC       = round(exp(mean(log(EC_MPN + 1))), 1),
    gm_TC       = round(exp(mean(log(TC_MPN + 1))), 1),
    EC_TC_ratio = round(gm_EC / gm_TC, 2),
    pct_diluted = round(100 * mean(dilution_factor > 1), 1),
    .groups     = "drop"
  ) %>%
  kbl(
    caption   = "Summary metrics for interpreting site contamination patterns",
    col.names = c("Site", "N", "Geom. mean EC", "Geom. mean TC",
                  "EC:TC ratio", "% samples diluted")
  ) %>%
  kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE)
Summary metrics for interpreting site contamination patterns
Site N Geom. mean EC Geom. mean TC EC:TC ratio % samples diluted
Pomona Bioswale 12 17.7 24.0 0.74 0.0
Seal Court 12 182.3 174.7 1.04 33.3
pHake Lake 12 669.0 1000.8 0.67 50.0

How to use the EC:TC ratio:

8.2 Comparing your group’s results to the class

Your group’s samples are two of the data points that contributed to the analysis above. To identify your group’s samples:

# Replace "Group_1" and "Round_1" with your actual values
your_data <- MPN_data %>%
  filter(Group == "Group_1", Round == "Round_1")

your_data %>%
  select(Site_ID, Dilution, raw_TC, raw_EC, TC_MPN, EC_MPN) %>%
  kbl(caption = "Your group's corrected MPN values") %>%
  kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE)

9. Summary

💡 Key takeaways

💬 Discussion questions

  1. Your group found that pHake Lake had the highest E. coli concentrations, but the ANOVA was not significant (p = 0.12). Does this mean the sites have the same bacterial contamination? What factors related to your class dataset might prevent the test from detecting a real difference?
  2. Two groups sampling the same site on the same day got very different MPN values. One group counted 12 yellow large wells and 8 yellow small wells; the other counted 85 large and 42 small. Neither sample was diluted. List three field or lab procedural differences that could produce this result.
  3. The Pomona Bioswale is designed to filter runoff. Based on your results, is it effective at reducing bacterial contamination? What additional sampling design would you need to definitively answer this question?
  4. Canada geese are protected under the Migratory Bird Treaty Act. Given that their waste is likely the primary source of elevated E. coli in pHake Lake, what management options are available to Pomona College, and what are the regulatory and ethical constraints on each?
  5. Your EC:TC ratio at Seal Court was 0.35, meaning only 35% of the coliforms were E. coli. What does the remaining 65% indicate about the source of contamination, and does it change your public health risk assessment for that site?

References

IDEXX Laboratories. 2013. Colilert and Quanti-Tray/2000 Procedure. Westbrook, ME.

US EPA. 2012. Recreational Water Quality Criteria. EPA 820-F-12-058. Washington, DC.

California State Water Resources Control Board. 2022. Water Quality Control Plan for the Inland Valleys Basin (Basin Plan). Region 8.

Rompré, A., et al. 2002. Detection and enumeration of coliforms in drinking water: current methods and emerging approaches. Journal of Microbiological Methods 49:31–54.