Visitar URL original
Add retained resamples to Morris analysis by DresdenGman · Pull Request #694 · SALib/SALib · GitHub
Skip to content

Add retained resamples to Morris analysis - #694

Merged
ConnectedSystems merged 3 commits into
SALib:mainfrom
DresdenGman:feature/morris-keep-resamples
Oct 29, 2026
Merged

ConnectedSystems merged 3 commits into
SALib:mainfrom
DresdenGman:feature/morris-keep-resamples

Conversation

@DresdenGman

Copy link
Copy Markdown
Contributor

Summary

  • add an opt-in keep_resamples argument to Morris analysis, returning the bootstrap mu_star estimates as mu_star_conf_all
  • use one trajectory-resample index matrix across parameters so each retained row is a coherent bootstrap replicate for ranking and robustness analysis
  • preserve DataFrame, plotting, and print_to_console behavior by excluding the two-dimensional resample matrix from tabular conversion

Details

The retained matrix has shape (num_resamples, number_of_groups). Without groups, its columns follow the problem parameter order. With groups, each bootstrap replicate is aggregated using the same group mean as the other Morris mu_star outputs.

The storage flag does not change confidence results: both paths use the same shared trajectory draws for a given seed. Morris previously generated independent trajectory indices inside the parameter loop. That was sufficient for marginal confidence widths, but it could not produce a coherent per-resample ranking across parameters. Moving the draw outside the loop preserves the estimator while aligning each bootstrap replicate. As a result, exact seeded confidence values can change from earlier versions; the CLI regression fixtures are updated to record that intentional numerical change.

For grouped analyses, the existing mu_star_conf definition remains unchanged: it is the mean of the parameter-level confidence widths. It therefore is not generally identical to a confidence width recomputed from an already-grouped mu_star_conf_all column.

Closes #579.

Validation

245 passed, 1 xfailed
pytest -q
ruff check src/SALib/analyze/morris.py tests/test_analyze_morris.py
black --check src/SALib/analyze/morris.py tests/test_analyze_morris.py
git diff --check

Regression coverage verifies the opt-in/default contract, confidence invariance, shared trajectory alignment, reconstruction from retained estimates, grouped aggregation, default key compatibility, and DataFrame/console conversion.

@willu47

willu47 commented Sep 21, 2026

Copy link
Copy Markdown
Member

@kerained we have received a submission addressing your suggestion for SALib. Would you be interested in assessing if it meets your needs?

@ConnectedSystems ConnectedSystems left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small comments for your consideration.

An interesting finding picked up by Claude is that the ResultDict would no longer be picklable (via pickle), but SALib in general doesn't really support pickling so this is a very minor point in my view (dill yes, pickle no).

If you wanted to recreate the issue, I got Claude to generate the below example:

import pickle
from concurrent.futures import ProcessPoolExecutor

from SALib import ProblemSpec
from SALib.analyze import morris
from SALib.test_functions import Ishigami


def run_analysis(seed):
    sp = ProblemSpec(
        {
            "names": ["x1", "x2", "x3"],
            "bounds": [[-3.14159, 3.14159]] * 3,
        }
    )
    sp.sample_morris(100, seed=seed).evaluate(Ishigami.evaluate)
    return morris.analyze(sp, sp.samples, sp.results, seed=seed, keep_resamples=True)


if __name__ == "__main__":
    Si = run_analysis(101)
    Si.to_df()  # works

    # Case 1: save results and load them later
    with open("morris_results.pkl", "wb") as f:
        pickle.dump(Si, f)
    with open("morris_results.pkl", "rb") as f:
        loaded = pickle.load(f)
    loaded.to_df()  # ValueError
    # loaded.plot()  # same ValueError (plot calls to_df)

    # Case 2: several analyses in worker processes
    with ProcessPoolExecutor(2) as pool:
        results = list(pool.map(run_analysis, [1, 2]))
    results[0].to_df()  # ValueError

and ``mu_star`` is high, then the effects are of different signs.

``sigma`` is used as an indicator of interactions between parameters, or
groups of parameters.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add a note here to say that bootstrapping behavior has changed (joint bootstrap over trajectories).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also make sure this is added in the changelog as a notification to users who upgrade.

Comment thread src/SALib/analyze/morris.py Outdated
Comment on lines +630 to +631
if mu_star_resamples is not None:
return mu_star_conf, mu_star_resamples

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think cleaner to always return (mu_star_conf, mu_star_resamples).

This would simplify the return type info as well.

_compute_statistical_outputs can drop the resamples when it is not needed.

Comment thread src/SALib/analyze/morris.py Outdated
Si["mu_star_conf"] = _compute_grouped_metric(mu_star_conf, groups)
if keep_resamples:
Si["mu_star_conf_all"] = np.asarray(
[_compute_grouped_metric(row, groups) for row in mu_star_resamples]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor performance note: The collation of results is done as a list comprehension but could be a single op (resamples @ groups / groups.sum(axis=0))

Document joint trajectory bootstrapping, consistently return confidence
widths with bootstrap estimates, and vectorize finite group aggregation.
Preserve Morris DataFrame conversion across pickle and process-pool
roundtrips using a dedicated ResultDict subclass.

Add regression coverage for serialization, plotting, default-result
invariance, and nonfinite group isolation.
Add an Unreleased notice for upgrading users about joint trajectory
bootstrapping and changed seeded confidence values, including when
keep_resamples is left at its default.
@ConnectedSystems
ConnectedSystems merged commit 820ffdb into SALib:main Oct 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhancing Morris Method: Returning Sensitivity Indices for Resampling Analysis

3 participants