mescla.mixing.ml.mix_ml

mescla.mixing.ml.mix_ml(endmembers, samples, sd_endmembers=None, sd_samples=None, n_starts=10, max_iter=2000, screen_iter=30, tol=1e-09, ratio_tol=1e-07, seed=0)[source]

Estimate mixing ratios and end-member concentrations jointly (MIX).

Parameters:
  • endmembers (EndMembers, DataFrame or array, shape (ne, ns)) – Measured end-member compositions.

  • samples (WaterChemistry, DataFrame or array, shape (np, ns)) – Measured mixture compositions.

  • sd_endmembers (array_like, optional) – Standard deviations of the end-member analyses, shape (ne, ns) or one value per species. Falls back to the sigma carried by endmembers. These are usually the large ones: they must express spatial and temporal variability and conceptual error, not laboratory precision.

  • sd_samples (array_like, optional) – Standard deviations of the sample analyses, shape (np, ns) or one per species. Falls back to the sigma carried by samples.

  • n_starts (int, default 10) – Random restarts, in addition to the least-squares and uniform starts. The paper reports convergence to different minima from different starting points and recommends exactly this: try least squares, uniform, and about ten random perturbations, then keep the best.

  • max_iter (int, default 2000) – Iteration cap for the final, fully converged run.

  • screen_iter (int, default 30) – Cheap budget used to rank the starting points before committing to one.

  • tol (float, default 1e-9) – Relative change in the objective at which to stop.

  • ratio_tol (float, default 1e-7) – Largest change in any mixing ratio at which to stop. Both this and tol must be satisfied.

  • seed (int, optional)

Returns:

MixingResultresult.endmembers holds the revised compositions – compare them with what you measured. result.diagnostics["improvement_index"] quantifies how much they moved relative to their assigned uncertainty.

Return type:

MixingResult

Notes

The alternation has a long, very flat tail: the objective can keep creeping in the sixth decimal for thousands of iterations after the mixing ratios have settled to better than 1e-4, which is already far beyond analytical precision. converged therefore reports whether both movement criteria were met, and diagnostics["final_ratio_change"] tells you how much the ratios were still moving when it stopped – read that rather than the flag alone.

Assigning the standard deviations is a modelling decision, not a formality, and it is the step that decides your answer. The asymmetry matters more than the absolute values: make the end-member sigmas larger than the sample sigmas when you want the mixtures to reshape the end-members, and smaller when you have already established the end-members and want them preserved. Always sweep them (mescla.uncertainty.identifiability.sigma_sweep()) and report the sweep.

Examples

>>> result = mix_ml(endmembers, samples,
...                 sd_endmembers=0.30 * endmembers.data,
...                 sd_samples=0.05 * samples.data)
>>> result.endmembers.to_frame()                     # revised compositions