Eutrophication is a switch? A test of the posted phosphorus-threshold claim
Supplied working report; original verdicts not validated.
Review status: Needs review. This is an archival source note, not a passed proof.
README.md
# Eutrophication is a switch? A test of the posted phosphorus-threshold claim **Hypothesis:** "Eutrophication Is a Switch" — Original Theories, Volume XVI #10 **Posted:** <https://incoprea.com/research-library/original-theories-vol16-20261030/> **Test:** `tests/05-lake-switch`, run 30 October 2026 **Data:** EPA National Lakes Assessment 2007 and 2017 (1,146 and 993 target lakes) **Status:** working result, not peer reviewed, AI-assisted ## The result A sharp change point is statistically demonstrable on the full 2017 sample — a continuous two-segment fit beats a straight line by ΔBIC 63.6 and a quadratic by ΔBIC 14.5, with a break at 145 µg/L — but it is carried by the top 5% of the phosphorus distribution: removing the 50 lakes above 448 µg/L removes the break by the pre-specified BIC rule (ΔBIC −0.79 against a line, 0.18 against a quadratic), and where it appears the break is a ceiling (slope 1.03 below, 0.11 above), not an accelerating switch. The proposal's threshold is therefore not established as a general property of the gradient; the two-state signature is weak (residual valley 0.616 against a null 0.117) while the persistence signature is strong (2007→2017 chlorophyll coefficient 0.502 across 216 lakes). The proposal's directional picture — a lake that is clear below the threshold and then crosses a boundary into algal dominance above it — is the opposite of what the full sample shows. Chlorophyll rises in proportion to phosphorus across most of the gradient (slope 1.03 on log–log axes) and then stops responding (slope 0.11). That is a saturation curve, not an accelerating switch, and the flat upper segment is a property of a few dozen hypereutrophic lakes. ## What was tested The claim has three testable implications, and all were fixed in `hypothesis.md` before any model was fitted: - **P1 — a sharp break.** log10 chlorophyll-a against log10 total phosphorus should be better described by a continuous two-segment change point (M1) than by a straight line (M0) or a quadratic (M2). - **P2 — two states.** Residuals from a smooth fit should be bimodal, not merely skewed. - **P3 — path dependence.** Among lakes sampled in both 2007 and 2017, 2017 chlorophyll should depend on 2007 chlorophyll after conditioning on 2017 phosphorus. The pre-specified support rule for P1 was: ΔBIC(M1 vs M0) > 6 **and** bootstrap likelihood-ratio p < 0.05 **and** ΔBIC(M1 vs M2) > 0. All three were required; none was added after the fact. ## Data and sample Two EPA National Lakes Assessment surveys, one index visit per lake (`VISIT_NO == 1`), target-sampled lakes only (2007 `LAKE_SAMP == "Target_Sampled"`; 2017 `SITETYPE == "PROB"`). Total phosphorus and chlorophyll-a are in µg/L and both are logged. Full provenance and row counts are in `SOURCE.md`; the working CSVs are byte-identical members of the official EPA archives. | | 2007 | 2017 | |---|---|---| | Target lakes with TP > 0 and CHLA > 0 | 1,146 | 993 | | TP range (µg/L) | 1–4,679 | 1.36–11,238.8 | | CHLA range (µg/L) | 0.067–936 | 0.1–3,299.18 | | TP values flagged below reporting limit | 116 | 25 | | Lakes with maximum depth | 1,145 | 988 | Below-limit values are kept at their reported numeric value in the primary analysis, because both surveys report a number rather than only a "less-than" token and dropping them would preferentially delete the low-phosphorus end. A sensitivity analysis drops them instead (S4 below). The 2017 sample is the primary sample; 2007 is a replication in a different survey year, though not an independent one — the 2017 revisit panel re-sampled 2007 sites, and 216 lakes match one-to-one in the path-dependence sample. The two surveys are not pooled in the decisive test. ## Result 1 — a tail-driven ceiling All models are ordinary least squares on log10 scales, n = 993. | Model | Parameters | BIC | RSS | |---|---|---|---| | M0 — linear | 2 | −1609.05 | 193.72 | | M1 — continuous two-segment, break at τ | 4 | −1672.62 | 179.20 | | M2 — quadratic | 3 | −1658.15 | 183.10 | ΔBIC(M1 vs M0) = 63.56 and ΔBIC(M1 vs M2) = 14.47, both favouring the break. The bootstrap likelihood-ratio statistic is 77.36 with p = 0.0005 (2,000 parametric draws; 0.0005 is the smallest value 2,000 draws can resolve). The break is at **τ = 144.8 µg/L**, with a 95% percentile bootstrap interval of **69.8–274.2 µg/L** from 10,000 paired resamples; 10.1% of resamples put τ on a grid edge, so the location is bounded rather than pinned down. By the pre-specified rule P1 is supported on the full sample: the relation is not a single gradual curve. The support is for the existence of a break, not for the direction the proposal predicts — and, as the next subsection shows, not for the break as a general property of the gradient. The shape is the important part. Below the break the slope is **1.026**; above it the slope is **0.105**. The slope change is **−0.921** (95% CI −1.674 to −0.561). A lake at 50 µg/L has predicted chlorophyll of about 14 µg/L; at 145 µg/L it is about 41 µg/L; by 1,000 µg/L it is only about 50 µg/L. The break marks where chlorophyll stops tracking phosphorus, not where it starts to accelerate. The 2007 survey gives the same qualitative answer: n = 1,146, ΔBIC(M1 vs M0) = 89.10, ΔBIC(M1 vs M2) = 53.39, LRT 103.19, p = 0.0005, τ = 237.9 µg/L (95% CI 95.9–255.0), slopes 0.826 → −0.392 (change −1.218). Two qualifications: 30.5% of the 2007 bootstrap resamples put τ at a grid edge, because the 2007 break sits at the top of the search window, so only the existence of a break — not its location — replicates; and the 2007 upper-segment slope is actually negative, i.e. chlorophyll is flat or declining at the highest phosphorus concentrations. Spearman correlations on log–log axes are 0.749 in 2017 and 0.762 in 2007; M0 explains R² = 0.512 of the 2017 variance with a slope of 0.808. The break is a departure from a single power law on the full sample, but the departure is confined to the hypereutrophic tail. ### The break disappears when the top 5% of TP is removed The 95th percentile of 2017 TP is 447.9 µg/L. Removing the 50 lakes above it leaves 943. On that sample ΔBIC(M1 vs M0) = −0.79 and ΔBIC(M1 vs M2) = 0.18: the break no longer earns its two extra parameters. The likelihood-ratio statistic is still nominally significant (p = 0.001), which is why the two criteria can disagree — the break improves the fit slightly but not enough to pay the BIC penalty. The same trim on 2007 leaves 1,088 lakes and a marginal break (ΔBIC 6.45 and 13.37, τ = 101.4 µg/L, slopes 0.869 → 0.316); the 2007 break survives the BIC bar, but it is a slope reduction, not a ceiling. On the 2017 deep stratum, trimming removes the break (n = 613, ΔBIC 3.58 and 6.26). These trims were added after the full-sample fit and are labelled post-hoc; the pre-specified full-sample result is unchanged, but its interpretation is not: the break is a property of the hypereutrophic tail, not of the main gradient. ## Result 2 — conditional bimodality, but a small second mode Residuals from M2 have a Gaussian-KDE valley depth of **0.616** on the `dip_like` statistic. Against 2,000 Gaussian residual resamples the null median is 0.0 and the 95th percentile is 0.117; the raw p is 0.001 and the Benjamini–Hochberg adjusted p is 0.002 across the two bimodality tests. That passes the pre-specified rule (adjusted p < 0.05 and depth ≥ 0.10). The second mode is small. Twenty-seven lakes (2.7%) have M2 residuals below −1 log10 unit, and the unconditional log-chlorophyll distribution is not bimodal: its valley depth is 0.026 with p = 0.106. So the evidence is for a long left tail of unusually clear lakes conditional on phosphorus, not for two comparable algal states. Two post-hoc checks, not part of the pre-specified decision, probe this: residuals from a 20-bin flexible smoother still give a valley depth of 0.373 against a null 95th percentile of 0.091 (p = 0.0055), and dropping the two most extreme M2 residuals reduces the depth from 0.616 to 0.322. The statistical valley survives, but at roughly half its original depth, and its substantive interpretation as a distinct clear state is weak. `dip_like` is a descriptive Gaussian-KDE valley-depth statistic, **not Hartigan's dip test**, and should be read as such. ## Result 3 — path dependence is present, but it is not a test of reversal The surveys do resample the same lakes: matching on the NHD COMID with one site per survey per waterbody gives **216 matched lakes** (median relative area difference 10⁻⁶, confirming the same waterbody). This is above the 50-lake threshold, so the clause is testable rather than marked UNTESTED. The pre-specified model is log10 CHLA₂₀₁₇ = b₀ + b₁ log10 TP₂₀₁₇ + b₂ log10 CHLA₂₀₀₇ + ε. The estimate is **b₂ = 0.502** (se 0.054; t = 9.23; 95% paired-bootstrap CI 0.358–0.640; one-sided log10 p = −16.87; 10,000-label permutation p = 0.0001). The model R² is 0.592, b₁ = 0.332, the partial correlation of 2007 and 2017 chlorophyll given 2017 phosphorus is 0.535, and the simple Spearman correlation between the two chlorophyll measurements is 0.745. Adding 2007 phosphorus leaves the result essentially unchanged (b₂ = 0.539, b₃ = −0.122). This is persistence conditional on current phosphorus. It is consistent with the proposal's "not reversed" clause, but it is equally consistent with fixed lake-level differences — colour, morphometry, nitrogen, grazing — that make a lake consistently clearer or greener than its phosphorus alone would predict. No lake in the data is observed returning to its earlier phosphorus concentration, so the strong form of the clause is not directly tested. ## Depth and alternative definitions The depth split was fixed at 3 m before fitting. The proposal's internal-loading mechanism implies the break should matter more in shallow, polymictic lakes; the data do not follow that pattern. | Survey | Stratum | n | ΔBIC(M1 vs M0) | ΔBIC(M1 vs M2) | τ (µg/L) | P1 verdict | |---|---|---|---|---|---|---| | 2017 | shallow (< 3 m) | 342 | 34.99 | −3.00 | 176 | not supported | | 2017 | deep (≥ 3 m) | 646 | 34.13 | 14.65 | 71.6 | supported | | 2007 | shallow (< 3 m) | 288 | 10.92 | −0.44 | 246 | not supported | | 2007 | deep (≥ 3 m) | 857 | 100.21 | 77.49 | 109 | supported | In shallow lakes the smooth quadratic is at least as good as the break (it wins by 3.00 BIC in 2017 and by 0.44 in 2007), so the pre-specified rule does not support a threshold there. On the full samples the break is a deep-lake feature in both surveys, but it is itself tail-dependent: after trimming the top 5% of TP within the 2017 deep stratum, ΔBIC(M1 vs M0) falls to 3.58 (n = 613), below the pre-specified bar. The verdict does not move under S1–S6, but it does move under the post-hoc tail trims S7–S9: | Variant | n | ΔBIC(M1 vs M0) | ΔBIC(M1 vs M2) | τ (µg/L) | Same P1 verdict? | |---|---|---|---|---|---| | S1 — 2007 replication | 1,146 | 89.10 | 53.39 | 237.9 | yes | | S2 — pooled 2007 + 2017 | 2,139 | 139.59 | 87.78 | 200.3 | yes | | S3 — 2017 including hand-picked sites | 1,097 | 68.49 | 18.45 | 146.1 | yes | | S4 — drop below-reporting-limit values | 968 | 63.62 | 8.06 | 143.7 | yes | | S5 — τ window central 60% | 993 | 58.15 | 9.05 | 75.3 | yes | | S6 — 2007 excluding reference lakes | 1,023 | 81.70 | 52.35 | 241.4 | yes | | S7 — 2017, trim top 5% TP (post-hoc) | 943 | −0.79 | 0.18 | 76.1 | **no** | | S8 — 2007, trim top 5% TP (post-hoc) | 1,088 | 6.45 | 13.37 | 101.4 | yes, marginal | | S9 — 2017 deep, trim top 5% TP (post-hoc) | 613 | 3.58 | 6.26 | 77.8 | **no** | S2 double-counts the 216 matched lakes and is reported only as a stress test. S5 shows that narrowing the search window pulls τ down to 75 µg/L without changing the verdict, which is a reminder that the break location is model-dependent even where its existence is not. S7–S9 were added after the full-sample fit, when the upper segment was seen to be dominated by hypereutrophic lakes; each removes the lakes above its own 95th percentile of TP (50 lakes above 447.9 µg/L in S7, 58 above 449.25 µg/L in S8, 33 above 242.2 µg/L in the 2017 deep stratum in S9). They are labelled post-hoc because they were not in `hypothesis.md`, and they are reported because they change the verdict. ## What this test cannot see A single visit per lake measures a snapshot, not a trajectory. P1 and P2 test a cross-sectional signature that a threshold mechanism would produce, but the same signature — a kink and a long left tail — can arise without bistability: from a smooth but concave relation, from a change in the limiting nutrient along the gradient, from light limitation at high phosphorus, or from the way lakes are distributed across the landscape. The fitted break is continuous by construction; a genuinely discontinuous jump would be smoothed into a steep segment, so this design cannot separate a stability boundary from a steep but continuous response. P3 is the closest available approach to the irreversibility clause, and it is weaker than the clause. Two snapshots ten years apart can show that history matters after conditioning on current phosphorus; they cannot show a lake crossing a boundary and returning, and they cannot separate hysteresis from fixed lake-level typology. The 216 matched lakes are the 2017 revisit panel, a selected subset of the 2007 sample, not a fresh random draw from it. Other limits, specific to this design: - **Unweighted fits.** The NLA is a probability sample, but the models are unweighted OLS. The estimand is the relation among sampled lakes, not a population total; design weights would change the target, not just the precision. - **Chlorophyll is a proxy for algal dominance.** Non-algal turbidity and light limitation can both flatten the chlorophyll–phosphorus relation, and this test does not use turbidity or light measurements, so it cannot separate them. - **Index-season snapshot.** Within-lake seasonal variation, which is large for chlorophyll, is invisible. - **Revisit-panel composition.** The 2017 sample mixes new probability sites with a revisit panel drawn from the 2007 sample. Unweighted fits give the revisit lakes their sample share rather than their population weight, so the 2017 cross-section is not a clean probability sample of the national population. - **Survey population.** Very small and non-probability water bodies are outside the target population, and the 2007 and 2017 laboratory programmes are not identical. - **No manipulation.** Every statement here is an association; the data cannot identify the mechanism the proposal invokes, and nothing in this file should be read as a causal claim or as management advice. ## What would have changed the answer - A positive slope change would have matched the proposal's directional prediction. The observed change is negative and its 95% CI excludes zero (−1.674 to −0.561), so the data are not merely underpowered on this point. - If M2 had beaten M1 by more than 0 BIC in 2017, P1 would have failed by the pre-specified rule. It did not (M1 wins by 14.47); in shallow lakes it does. - If the matched-lake count had been below 50, P3 would have been marked UNTESTED. At 216 it is testable, but the estimate is persistence, not an observed reversal. - Removing the top 5% of TP changes the P1 verdict from supported to not supported under the BIC rule (ΔBIC −0.79 and 0.18). That is the most consequential result in this file: the threshold is a property of the hypereutrophic tail, not of the main gradient. - If the bimodality rule had been applied to raw log chlorophyll rather than M2 residuals, P2 would have failed (p = 0.106). The pre-specified residual version passes, but the post-hoc binned-smoother check gives a valley depth of 0.373 rather than 0.616, so the two-state reading is the least secure of the three results. ## Every number quoted, with its `results.json` key | Where | Numbers as quoted | `results.json` keys, in the same order | |---|---|---| | Primary sample | 993; 1.36–11,238.8; 0.1–3,299.18; 25; 988 | `primary_2017.n`; `samples.ranges_2017.tp_ugl`; `samples.ranges_2017.chla_ugl`; `samples.n_2017_below_rl_tp`; `samples.n_2017_depth_available` | | 2007 replication sample | 1,146; 1–4,679; 0.067–936; 116; 1,145 | `replication_2007.n`; `samples.ranges_2007.tp_ugl`; `samples.ranges_2007.chla_ugl`; `samples.n_2007_below_rl_tp`; `samples.n_2007_depth_available` | | Primary association | 0.749; 0.762; 0.512; 0.808 | `primary_2017.spearman_loglog`; `replication_2007.spearman_loglog`; `primary_2017.M0.r2`; `primary_2017.M0.slope` | | Primary BIC | −1609.05; −1672.62; −1658.15; 193.72; 179.20; 183.10; 63.56; 14.47 | `primary_2017.M0.bic`; `primary_2017.M1.bic`; `primary_2017.M2.bic`; `primary_2017.M0.rss`; `primary_2017.M1.rss`; `primary_2017.M2.rss`; `primary_2017.delta_bic_M1_M0`; `primary_2017.delta_bic_M1_M2` | | Primary LRT | 77.36; 0.0005 | `primary_2017.lrt.stat`; `primary_2017.lrt.p_boot` | | Primary break | 144.8; 69.8–274.2; 10.1% | `primary_2017.M1.tau_ugl`; `primary_2017.M1.tau_ci95_ugl`; `primary_2017.M1.frac_tau_at_grid_edge` | | Primary slopes | 1.026; 0.105; −0.921; −1.674 to −0.561 | `primary_2017.M1.slope_left`; `primary_2017.M1.slope_right`; `primary_2017.M1.slope_change`; `primary_2017.M1.slope_change_ci95`; `primary_2017.M1.predicted_chla_ugl.tp_50` = 13.7; `.tp_145` = 40.8; `.tp_1000` = 50.0 | | 2007 replication | 89.10; 53.39; 103.19; 0.0005; 237.9; 95.9–255.0; 30.5%; 0.826; −0.392; −1.218 | `replication_2007.delta_bic_M1_M0`; `replication_2007.delta_bic_M1_M2`; `replication_2007.lrt.stat`; `replication_2007.lrt.p_boot`; `replication_2007.M1.tau_ugl`; `replication_2007.M1.tau_ci95_ugl`; `replication_2007.M1.frac_tau_at_grid_edge`; `replication_2007.M1.slope_left`; `replication_2007.M1.slope_right`; `replication_2007.M1.slope_change` | | Bimodality (pre-specified) | 0.616; 0.117; 0.001; 0.002; 0.026; 0.106 | `bimodality.residual.observed_depth`; `bimodality.residual.null_q95`; `bimodality.residual.p`; `bimodality.residual.p_bh`; `bimodality.raw_chla.observed_depth`; `bimodality.raw_chla.p` | | Bimodality (post-hoc) | 0.373; 0.091; 0.0055; 0.322; 27; 2.7% | `bimodality.posthoc.binned_smoother_20.bins` = 20; `bimodality.posthoc.binned_smoother_20.observed_depth`; `bimodality.posthoc.binned_smoother_20.null_q95`; `bimodality.posthoc.binned_smoother_20.p`; `bimodality.posthoc.m2_depth_drop_two_most_negative`; `bimodality.posthoc.n_residual_below_minus1`; `bimodality.posthoc.share_residual_below_minus1` | | Path dependence | 216; 10⁻⁶; 0.502; 0.054; 9.23; 0.358–0.640; −16.87; 0.0001; 0.592; 0.332; 0.535; 0.745; 0.539; −0.122 | `path_dependence.n_matched`; `path_dependence.area_median_rel_diff`; `path_dependence.model.b2`; `path_dependence.model.se_b2`; `path_dependence.model.t_b2`; `path_dependence.model.b2_ci95`; `path_dependence.model.p_one_sided_log10`; `path_dependence.permutation.p_two_sided`; `path_dependence.model.r2`; `path_dependence.model.b1`; `path_dependence.partial_corr_chla_given_tp17`; `path_dependence.spearman_chla_07_17`; `path_dependence.extended_model.b2`; `path_dependence.extended_model.b3` | | Depth table | 342; 34.99; −3.00; 176; 646; 34.13; 14.65; 71.6; 288; 10.92; −0.44; 246; 857; 100.21; 77.49; 109 | `depth.2017.shallow.n`, `.delta_bic_M1_M0`, `.delta_bic_M1_M2`, `.M1.tau_ugl`; `depth.2017.deep.*`; `depth.2007.shallow.*`; `depth.2007.deep.*` (same four fields in each block) | | Tail trims (post-hoc) | 50; 448; 943; −0.79; 0.18; 76.1; 58; 449; 1,088; 6.45; 13.37; 101.4; 33; 242; 613; 3.58; 6.26; 77.8 | `sensitivity.S7_2017_trim_top5pct_TP.n_removed`; `.trim_threshold_ugl`; `.n`; `.delta_bic_M1_M0`; `.delta_bic_M1_M2`; `.M1.tau_ugl`; `sensitivity.S8_2007_trim_top5pct_TP.n_removed`; `.trim_threshold_ugl`; `.n`; `.delta_bic_M1_M0`; `.delta_bic_M1_M2`; `.M1.tau_ugl`; `sensitivity.S9_2017_deep_trim_top5pct_TP.n_removed`; `.trim_threshold_ugl`; `.n`; `.delta_bic_M1_M0`; `.delta_bic_M1_M2`; `.M1.tau_ugl` | | Pre-specified thresholds | 6; 0.05; 0; 0.05; 0.10; 0.025; 50; 3 | `settings.criteria.delta_bic_m1_m0_min`; `settings.criteria.lrt_alpha`; `settings.criteria.delta_bic_m1_m2_min`; `settings.criteria.p2_alpha`; `settings.criteria.dip_depth_min`; `settings.criteria.p3_alpha`; `settings.criteria.p3_min_matched_lakes`; `settings.depth_cut_m` | | Sensitivity table | 1,146; 2,139; 1,097; 968; 993; 1,023; 89.10; 139.59; 68.49; 63.62; 58.15; 81.70; 53.39; 87.78; 18.45; 8.06; 9.05; 52.35; 237.9; 200.3; 146.1; 143.7; 75.3; 241.4 | `sensitivity.S1_2007_replication.*`; `S2_pooled`; `S3_2017_including_HAND`; `S4_drop_below_rl`; `S5_tau_window_60pct`; `S6_2007_excluding_reference` — fields `.n`, `.delta_bic_M1_M0`, `.delta_bic_M1_M2`, `.M1.tau_ugl` in each block | The overall coding of the verdict is in `results.json` under `verdict` (`P1_supported`, `P1_robust_to_tail_trim`, `P1_direction`, `claim_direction_met`, `P2_supported`, `P3_supported`, `overall`). ## Reproduction ``` cd tests/05-lake-switch export MPLCONFIGDIR=[local provenance path] [local provenance path] run.py ``` `run.py` reads only `data/raw/`, writes `data/lake_table.csv`, `results.json`, `figures/fig1_change_point.png` and `figures/fig2_bootstrap_and_null.png`, and takes about 2–4 minutes. Every random draw uses seed **20261030**. The two figures show the 2017 scatter with the M0 and M1 fits (Figure 1) and the 10,000-draw bootstrap distribution of τ beside the 2,000-draw null distribution of the bimodality statistic (Figure 2). ## Disclosure Designed and executed with AI assistance (DeepSeek via its coding agent), not peer reviewed. The pre-specified criteria are in `hypothesis.md`; the analysis code is `run.py`; every quoted number is in `results.json`; source URLs and checksums are in `SOURCE.md`.
hypothesis.md
# Eutrophication is a switch? — pre-specified test
**Proposal tested (verbatim, Incoprea Research, Original Theories Volume XVI #10,
posted 30 October 2026):**
> "A lake switches to algal dominance above a phosphorus threshold rather than
> degrading gradually, and the switch is not reversed by returning to the same
> concentration."
**Mechanism (verbatim):**
> "Nutrient loading crosses a stability boundary between clear and turbid states;
> internal loading from sediment maintains the turbid state after external
> loading falls."
**Prediction (verbatim):**
> "Recovery should require lower phosphorus than the level that caused the
> switch."
**Refute criterion (verbatim):**
> "Reversible, gradual response would remove the bistability."
Posted at: <https://incoprea.com/research-library/original-theories-vol16-20261030/>
This file was written before any model was fitted. It fixes the samples, the
models, the decisive comparison, the support/refute rules and the sensitivity
re-runs. Results are in `README.md` and `results.json`; nothing below was
changed after the first fit.
---
## 1. What the claim implies, operationally
The claim contains two separable assertions:
- **P1 — a switch.** Along a phosphorus gradient, chlorophyll-a should change
slope sharply at some threshold, not smoothly.
- **P2 — two states.** At a given phosphorus concentration, lakes should cluster
into a clear and a turbid state, so the scatter around a smooth fit should be
bimodal rather than unimodal.
- **P3 — hysteresis.** A lake's current chlorophyll should carry information
about its past chlorophyll beyond what its current phosphorus implies:
lakes that were turbid at a given phosphorus should remain turbid when
phosphorus returns to that same value.
P1 and P2 are cross-sectional signatures. P3 needs repeated measurement of the
same lakes.
## 2. Data inspected before this file was written
Only structure, never a model fit. Specifically: column names, units and flag
semantics in the five metadata files; the number of rows, sites and repeat
visits per survey; counts of missing and below-reporting-limit values; the
observed ranges of total phosphorus, chlorophyll-a, area and maximum depth;
the number of 2007 sites that also appear in the 2017 survey, matched on the
National Hydrography Dataset COMID; and the availability of a 2017 maximum
depth field. No correlation, change-point fit, ΔBIC or bimodality statistic had
been computed when the criteria in §5–§8 were fixed.
## 3. Data sources and lake-level table
Two EPA National Lakes Assessment probability surveys, treated as two
cross-sections of the same national population:
- **NLA 2007** — `NLA2007_WaterQuality_20091123.csv` (one row per site-visit;
`PTL` total phosphorus µg/L, `CHLA` chlorophyll-a µg/L) joined to
`NLA2007_SampledLakeInformation_20091113.csv` on `SITE_ID` and `VISIT_NO`
(`DEPTHMAX` m, `AREA_HA`, `WSA_ECO9`, `COM_ID`). Both files are byte-identical
members of the EPA "NLA 2007 All Data" archive (sha256 in `SOURCE.md`).
- **NLA 2017** — `nla2017_all_sites-visits_data_for_population_estimates_220705_0.csv`
(`PTL_RESULT` µg/L, `CHLA_RESULT` µg/L, `SITETYPE`, `WGT_TP`), joined to
`nla_2017_site_information-data.csv` (`COMID`, `AREA_HA`, `US_L3NAME`,
`AG_ECO9`). Maximum depth is not in those two files; it is taken from
`nla_2017_profile-data.csv` inside `NLA2017_Report_Data_Files.zip` as the
maximum `INDEX_SITE_DEPTH` for the visit. All three are byte-identical
members of the EPA 2017 report-data archive.
`data/lake_table.csv` is one row per lake per survey: survey year, site id,
COMID, state, ecoregion, area, maximum depth, TP, CHLA, the raw quality flags,
and a below-reporting-limit indicator.
**Inclusion rules (fixed in advance).**
- One row per lake per survey: `VISIT_NO == 1`. Repeat visits are excluded so
a lake cannot enter twice within a survey.
- 2007: `LAKE_SAMP == "Target_Sampled"`. 2017: `SITETYPE == "PROB"`
(probability sample). Hand-picked 2017 sites (`HAND`) are excluded from the
primary sample and used only in sensitivity S3.
- Both TP and CHLA present and strictly positive (a log is undefined at zero).
**Below-reporting-limit rule (fixed in advance).** Both surveys report a
*numeric* value alongside the below-limit flag (`PTL_FLAG` / `PTL_NARS_FLAG` /
`CHLA_NARS_FLAG`), not merely a "less-than" token. The primary analysis keeps
those reported values, because dropping them would preferentially delete the
low-phosphorus end of the gradient where a threshold would sit. Sensitivity S4
drops every flagged value instead. 2017 `ND` (non-detect) records have no
numeric TP result and are therefore absent through the positivity rule, not
through a separate decision.
## 4. Primary sample
The **2017 probability sample** is the primary sample: it is the most recent
survey, it was analysed in one laboratory cycle, and it avoids counting the
same lake twice in the pooled table. The **2007 target sample** is a
replication in a different survey year, reported with the same models. It is
not independent: 216 lakes match one-to-one in the path-dependence sample
(§7), because the 2017 revisit panel re-sampled 2007 sites. A pooled
2017+2007 analysis is sensitivity S2.
## 5. Primary test A — is there a sharp change point?
All models use x = log10(TP in µg/L) and y = log10(CHLA in µg/L), fitted by
ordinary least squares:
| model | form | parameters (BIC) |
|---|---|---|
| **M0** | y = a + b·x | 2 |
| **M1** | y = a + b₁·x + d·(x − τ)₊ (continuous two-segment; slope b₁ below τ, b₁+d above) | 4 (3 coefficients + τ) |
| **M2** | y = a + b·x + c·x² (smooth alternative) | 3 |
τ is found by a grid search over 240 equally spaced values between the 10th and
90th percentiles of x (the central 80% of the observed phosphorus range), with
M1 refitted by least squares at every τ. M1 is continuous at τ by construction.
Reported: τ in log10(µg/L) and µg/L, the two slopes, the slope change d, AIC-free
BIC = n·ln(RSS/n) + k·ln(n), and ΔBIC defined so that **positive favours the
two-segment model**:
- ΔBIC(M1 vs M0) = BIC(M0) − BIC(M1)
- ΔBIC(M1 vs M2) = BIC(M2) − BIC(M1)
**Bootstrap likelihood-ratio test (M1 vs M0).** Parametric residual bootstrap
under M0: 2,000 draws of y* = M0 fit + N(0, σ̂²), M0 and the full M1 grid
refitted on each draw, LR* = n·(ln RSS₀* − ln RSS₁*). The p-value is
(1 + #{LR* ≥ LR_obs}) / (2,001). The naive χ²₁ and χ²₂ p-values are reported as
a check but are not decisive, because τ is not identified under M0.
**τ confidence interval.** 10,000 paired (x, y) bootstrap resamples, M1
refitted on each, percentile 2.5–97.5% interval for τ and for d. The fraction of
resamples landing on either grid edge is reported; if it is material, the
threshold is not located, only bounded.
**Pre-specified decision rule.**
- **SUPPORTED** only if all three hold:
1. ΔBIC(M1 vs M0) > 6;
2. bootstrap LRT p < 0.05;
3. ΔBIC(M1 vs M2) > 0 (M1 beats the smooth quadratic).
- **NOT SUPPORTED** if any of the three fails. This is a failure to confirm the
threshold signature, not proof that the response is gradual.
- **EVIDENCE AGAINST a sharp switch** if additionally ΔBIC(M1 vs M0) ≤ 0 and
ΔBIC(M1 vs M2) ≤ 0, i.e. the two-segment model is worse than *both* smooth
alternatives.
The 2007 replication is judged by exactly the same rule. There is no pooling of
the two surveys in the decisive test.
## 6. Primary test B — conditional bimodality
Residuals are taken from M2 fitted to the primary sample. The statistic is
`lib/stats.dip_like`, the depth of the deepest interior valley of a Gaussian
kernel density, expressed as a fraction of the lower adjacent peak. **It is a
descriptive valley-depth statistic, not Hartigan's dip test** and is reported as
such.
Null calibration: 2,000 parametric draws y* = M2 fit + N(0, σ̂²), M2 refitted,
`dip_like` recomputed. p = (1 + #{depth* ≥ depth_obs}) / 2,001. The null median
and 95th percentile are reported.
**Pre-specified decision rule.** P2 is SUPPORTED if the Benjamini–Hochberg
adjusted p < 0.05 **and** the observed valley depth is at least 0.10 (a 10%
density fall between peaks, fixed as the smallest depth worth calling a valley).
Otherwise P2 is not supported.
A secondary descriptive repeats the statistic on log CHLA itself against a
Gaussian null with the observed mean and SD (equivalent to a lognormal null in
raw units). The two p-values form the **only multiplicity family** in this
test and are BH-adjusted together; every other p-value in this file is a
single pre-specified test or a robustness check and is not corrected again.
## 7. Test C — the "not reversed" clause
The 2007 and 2017 surveys overlap: the 2017 revisit panel re-sampled 2007
probability lakes. Matching is on the NHD `COMID` (2007 `COM_ID`), requiring
exactly one site in each survey per COMID (1:1). Area agreement is reported as
a matching check. If fewer than 50 lakes match, P3 is marked **UNTESTED** and
nothing is substituted for it.
For matched lakes the pre-specified model is
log10 CHLA₂₀₁₇ = b₀ + b₁·log10 TP₂₀₁₇ + b₂·log10 CHLA₂₀₀₇ + ε
and the path-dependence prediction is **b₂ > 0** with current phosphorus held
fixed. A model adding log10 TP₂₀₀₇ is reported as a check. Inference: OLS
one-sided t-test, a 10,000-draw paired bootstrap 95% CI for b₂, and a
10,000-label permutation p (labels of 2007 CHLA shuffled across lakes). All
three seeds are fixed.
**Pre-specified decision rule.** Path dependence is SUPPORTED if the one-sided
t p < 0.025 and the bootstrap 95% CI for b₂ excludes zero. Otherwise it is not
supported. The clause is UNTESTED if the match count is below 50. A positive b₂
is consistent with hysteresis but does not establish it: persistent lake-level
differences (morphometry, colour, nitrogen) also produce it, and two snapshots
cannot show a lake crossing a boundary twice.
## 8. Depth stratification and alternative definitions
**Depth.** Internal loading is expected to matter more in shallow, polymictic
lakes. M1 vs M0 vs M2 are refitted separately for 2017 lakes with maximum depth
< 3 m (shallow) and ≥ 3 m (deep), with the same criteria. The 3 m cut is fixed
here, before fitting. 2007 is repeated with `DEPTHMAX` for replication. Lakes
missing depth are excluded from this stratification only.
**Alternatives (each reported, verdict compared with the primary):**
- **S1** 2007 target sample instead of 2017 (the replication itself);
- **S2** pooled 2007 + 2017 rows (the 254 matched lakes then contribute twice);
- **S3** 2017 including hand-picked (`HAND`) sites;
- **S4** drop every below-reporting-limit value in both surveys;
- **S5** τ searched over the central 60% rather than 80% of x;
- **S6** 2007 excluding `REF_Lake` reference sites.
The verdict "moves" if the three-part rule in §5 changes state in any of these.
## 9. What this design cannot see
A single visit per lake measures a snapshot, not a trajectory. P1 and P2 test a
cross-sectional signature that a threshold mechanism implies, but the same
signature can be produced without bistability — for example by a smooth but
convex relation, by a change in the limiting nutrient along the gradient, or by
the landscape distribution of lakes. P3 uses two snapshots ten years apart; it
can detect persistence of lake identity but cannot observe a lake returning to
a previous phosphorus concentration, and it cannot separate hysteresis from
fixed lake-level differences. The survey's target population excludes very
small and non-probability water bodies, and each lake is sampled once in the
index season, so within-lake seasonal variation is invisible. No design here
manipulates phosphorus; all statements are associations.
## 10. Reporting conventions
- Two-sided tests unless a direction is stated (P3 is one-sided positive).
- Percentile bootstrap CIs; the number of resamples is stated with each.
- Seed 20261030 for every random draw; Python `numpy` default generator.
- Every number in `README.md` appears in `results.json` under a named key.
- Observational data: "associated with" is the strongest causal verb used.