Assigning standard deviations¶
This is the step that decides your answer, and it is a modelling decision rather than a
formality. mix_ml() needs a standard deviation for every
species in every end-member and every sample.
The asymmetry matters more than the values¶
stage |
which sigma is larger |
why |
|---|---|---|
estimating end-members from the mixtures |
end-member σ > sample σ |
you want the mixtures to reshape the end-members |
end-members already established, fitting new samples |
sample σ > end-member σ |
you do not want new data to undo the previous fit |
Tubau et al. do exactly this: loose end-members in the river step, tight ones in the groundwater step.
What σ should express¶
Not the laboratory’s repeatability. It should carry
analytical error,
sampling error, and
conceptual error — the share of the mixture that may belong to a source you did not include, to a reaction, or to an incomplete dataset.
For end-members, (3) usually dominates. Using the lab’s precision instead typically understates the uncertainty by an order of magnitude and produces intervals you cannot defend.
The device worth stealing¶
Give a species you do not trust a σ hundreds or thousands of times larger, rather than dropping it. It then contributes nothing to the fit but still receives a predicted end-member composition. Tubau et al. obtained expected compositions for PO₄, F, B, TOC, Br, As, Fe and O₂ this way — species they had barely measured.
sigma = 0.05 * samples.data
sigma[:, samples.species.index("NO3")] *= 1000 # carried, not trusted
The bundled load_tubau_besos() ships their published scheme, factors and all.
Always sweep it¶
from mescla.uncertainty.identifiability import sigma_sweep
sigma_sweep(endmembers, samples, factors=(0.1, 0.5, 1.0, 2.0, 10.0))
If the mean contributions move by more than a few percent across the sweep, the variance assumptions are doing the work rather than your data. Report the sweep either way.