Repository navigation
Add mu and sigma to LogNormal and raise AttributeError for unknown names - #433
Merged
Merged
Conversation
LogNormal.scale was sigma, the standard deviation of log(Y), but params['scale'] is exp(mu), as in scipy.stats.lognorm, so users read the wrong number (#168). LogNormal now has the attributes mu and sigma. The attributes loc and scale still return mu and sigma, but they give a FutureWarning. params does not change, so code that reads params['s'] and params['scale'], such as the skpro adapters, keeps working. The distributions built on scipy.stats returned None for any name they did not have, so a typo such as dist.s failed silently. One __getattr__ in Distn now forwards names to self.dist and raises AttributeError for the rest. It reads dist from __dict__, so predicted distributions can now be pickled and deep-copied. Before, both raised RecursionError. Models saved with earlier versions load and predict the same, because a model stores the distribution class and the internal parameters, not distribution objects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pylint on tests flagged the two bare expressions as expression-not-assigned (W0106), which failed the lint job. The test now checks that params has the keys s and scale, and it imports the score classes at the top of the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 27, 2026
ryan-wolbeck
self-requested a review
November 7, 2026 23:01
ryan-wolbeck
approved these changes
Nov 7, 2026
ryan-wolbeck
left a comment
Collaborator
There was a problem hiding this comment.
will need to fix some merge conflicts with 431 but looks good
Resolve the conflict in RELEASE_NOTES.md. Master rewrote the 0.6.0 notes into sections. Keep that version and add the two notes for issue #168: the LogNormal mu and sigma attributes under New features, and the AttributeError change under Bug fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pre-commit splits the test files over parallel pylint runs, one per CPU. duplicate-code only compares files in the same run, so its result depends on the CPU count and on which files exist. With the new test files of #433 and #434, the copy of the old-tree pickle helper in test_helpers.py and test_pickling.py lands in one run on a 4-CPU GitHub runner, and the hook fails. master already fails the same way when pylint gets all test files at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Thanks! The conflicts with #431 are fixed. f78949e merges master (with #431) into this branch, and the docs build, which runs the notebooks, passes. I added two small commits so that this PR and #434 merge cleanly together:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #168.
Problem
For
LogNormal,dist.paramsuses the names fromscipy.stats.lognorm:params['s']is σ andparams['scale']is exp(μ), where μ and σ are the mean and standard deviation of log(Y). The attributesdist.locanddist.scaleheld μ and σ. Sodist.scaleanddist.params['scale']had the same name but different values. Also,dist.sreturnedNonewithout an error, because the__getattr__of the distributions built onscipy.statsreturnedNonefor every name that the scipy object does not have.Changes
LogNormalhas new attributesmuandsigma.locandscaleare now properties. They still return μ and σ, but they give aFutureWarningthat names the replacement. The LogNormal scores usemuandsigma, so fitting gives no warning.paramsdoes not change.Distnhas one__getattr__. It forwards a name toself.distand raisesAttributeErrorfor all other names. It replaces the copies in Normal, LogNormal, Exponential, Gamma, HalfNormal, Laplace, Poisson, the three T classes and Weibull. It readsdistfrom__dict__, sopickle.loadsandcopy.deepcopyof a predicted distribution now work. Before, both raisedRecursionError.paramskeys and the new attributes. The developer guide no longer tells authors to write a__getattr__that returnsNone.tests/test_distn_attributes.pytests these changes. 24 of its 26 tests fail on master.Why not the 2020 plan
The issue thread proposed a leading underscore on the attributes of all distributions. This PR does not do that:
paramsagree, and a lot of code reads.locand.scaleonNormal. Examples are NREL's routee-powertrain and this repo'sfigures/.sample_fnandmean_fn. The example notebooks also read the attributes.This PR also does not change LogNormal
params. skpro'sNGBoostRegressorandNGBoostSurvivalcompute μ aslog(params['scale'])and σ asparams['s']. A change in meaning would give their users wrong numbers without an error.Compatibility
params, and they give no warning. I checked this for LogNormal regression, LogNormal survival, Exponential survival and Normal.NGBoostRegressor(dist="LogNormal")gives the same means as ngboost on this branch.hasattr(dist, name)is nowFalsefor a name that the distribution does not have. Before, it was alwaysTrue.locandscale. The warning says "a future release".Tests
pytest tests --slow -o "filterwarnings=error::FutureWarning:ngboost"on 6282515: 190 passed, 1 skipped, 2 xfailed, 22 xpassed. No FutureWarning comes from ngboost code, so nothing in ngboost readsLogNormal.locorLogNormal.scale.tests/test_pickling.py, because skops was not installed. With skops installed, that file gives 4 passed.test_dists_runs_on_examples_logscore. The 22 xpasses are the other cases of that test. They come from the existingxfailattests/test_distns.py:91. Its condition is a function, which is always true.-oand not-W: an earlier version of this description gavepytest tests -W error::FutureWarning:ngboost. That filter does not do this check. On the command line, pytest escapes the module name and matches it exactly. So it catches only warnings from the modulengboost, not fromngboost.distns.lognormal. In the ini optionfilterwarnings, the module is a regex that matches at the start of the name, so it also catches the submodules. To test the filter, I put one read ofself.scalein the LogNormal LogScore metric in a scratch copy. With-W,test_survival_runs_on_examplespassed with 500 warnings. With-o, it failed..pre-commit-config.yaml.Overlap
#431 also edits
docs/content/1-useage.ipynbanddocs/content/5-dev.ipynb. This PR changes source lines in two cells of each notebook, so a conflict, if any, will be small.🤖 Generated with Claude Code