bossanova 0.1.0.dev22__tar.gz → 0.1.0.dev24__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/PKG-INFO +1 -1
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/emm.py +33 -4
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/model_sim.py +22 -6
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/README.md +0 -1
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/__init__.py +0 -3
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/helpers.py +1 -43
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/predict.py +198 -11
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/model/core.py +9 -15
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/pyproject.toml +1 -1
- bossanova-0.1.0.dev22/bossanova/internal/viz/fit.py +0 -571
- bossanova-0.1.0.dev22/bossanova/internal/viz/fit_builders.py +0 -324
- bossanova-0.1.0.dev22/bossanova/internal/viz/fit_layers.py +0 -453
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/.gitignore +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/LICENSE +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/README.md +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/README.md +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/advertising.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/cake.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/chickweight.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/credit.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/gammas.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/mtcars.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/penguins.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/poker.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/sleep.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/titanic.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/titanic_test.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/data/titanic_train.csv +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/distributions/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/distributions/continuous.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/distributions/discrete.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/distributions/varying.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/expressions.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/compare.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/cv.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/deviance.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/f_test.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/helpers.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/ic.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/lrt.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/lrt_compare.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/compare/refit.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/builders/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/builders/dataframes.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/builders/resamples.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/builders/results.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/builders/specs.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/builders/state.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/schemas.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/structs/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/structs/data.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/structs/display.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/structs/explore.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/structs/formula.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/structs/specs.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/structs/state.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/containers/validators.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/design/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/design/coding.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/design/names.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/design/reference.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/design/z_matrix.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/convergence.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/diagnostics.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/dispatch.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/glm.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/glmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/lmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/ols.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/predict.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/fit/varying.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/bundle.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/contrast_registry.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/contrast_specs.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/design.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/encoding.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/evaluate.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/evaluate_contrast.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/evaluate_newdata.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/evaluate_transforms.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/helpers.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/parse.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/parser/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/parser/expr.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/parser/parser.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/parser/scanner.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/parser/token.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/formula/random_effects.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/asymptotic.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/bootstrap.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/cv.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/dispatch.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/formula_utils.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/mee.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/params.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/permutation.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/prediction.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/profile.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/common.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/core.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/glmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/lm_operators.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/lmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/results.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample/simulate.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/resample_bundle.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/satterthwaite_emm.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/infer/simulation.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/bracket_contrasts.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/compute.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/conditions.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/contrasts.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/explore.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/explore_parser.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/explore_scanner.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/factors.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/grid.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/inference.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/joint_tests.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/matrices.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/resolve.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/slopes.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/transforms.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/marginal/validation.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/backend/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/backend/dispatch.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/backend/jax.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/backend/numpy.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/backend/protocol.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/batching.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/config.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/convergence.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/differentiation.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/algebra.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/base.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/core.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/derived.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/factories.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/plotting.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/distributions/probability.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/binomial.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/create.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/gamma.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/gaussian.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/links.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/poisson.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/response.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/schema.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/family/tdist.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/diagnostics.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/estimation.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/hypothesis.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/information_criteria.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/multiplicity.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/profile.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/sandwich.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/satterthwaite.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/wald_variance.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/inference/welch.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/linalg/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/linalg/qr.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/linalg/rank.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/linalg/schur.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/linalg/sparse.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/linalg/svd.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/predict.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/rng.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/rounding.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/glm.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/glmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/heuristics.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/initialization.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/lambda_builder.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/lambda_sparse.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/lambda_template.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/lmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/optimize.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/pirls_sparse.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/solvers/quadrature.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/tolerances.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/transforms.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/variance.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/maths/weights.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/rendering/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/rendering/latex.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/rendering/markdown.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/dgp/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/dgp/generate.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/dgp/glm.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/dgp/glmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/dgp/lm.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/dgp/lmer.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/harness.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/metrics.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/simulation/power.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/cognition.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/compare.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/core.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/core_data.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/core_protocols.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/core_sizing.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/core_viz.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/dag.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/design.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/lattice.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/layout.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/mem.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/mem_forest.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/params.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/profile.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/ranef.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/relationships.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/resamples.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/resid.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/internal/viz/vif.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/model/__init__.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/model/guards.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/model/result.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/model/summary.py +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/bossanova/py.typed +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/tests/bossanova_benchmarks/bootstrap/data/README.md +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/tests/bossanova_benchmarks/insteval/data/README.md +0 -0
- {bossanova-0.1.0.dev22 → bossanova-0.1.0.dev24}/tests/bossanova_tests/hypothesis/README.md +0 -0
|
@@ -653,8 +653,24 @@ def _compute_emm_crossed_mem(
|
|
|
653
653
|
L_matrix = d[:, np.newaxis] * X_ref
|
|
654
654
|
|
|
655
655
|
grid = pl.DataFrame(grid_data)
|
|
656
|
-
|
|
657
|
-
|
|
656
|
+
|
|
657
|
+
# Add scalar-pinned conditions as constant columns (set_categoricals
|
|
658
|
+
# and at_overrides are applied to the design matrix but must also
|
|
659
|
+
# appear in the output grid so users can see the pinned values).
|
|
660
|
+
if resolved.set_categoricals:
|
|
661
|
+
for cond_var, cond_level in resolved.set_categoricals.items():
|
|
662
|
+
if cond_var not in grid.columns:
|
|
663
|
+
grid = grid.with_columns(pl.lit(cond_level).alias(cond_var))
|
|
664
|
+
if resolved.at_overrides:
|
|
665
|
+
for cond_var, cond_val in resolved.at_overrides.items():
|
|
666
|
+
if cond_var not in grid.columns:
|
|
667
|
+
grid = grid.with_columns(pl.lit(cond_val).alias(cond_var))
|
|
668
|
+
|
|
669
|
+
# Reorder: grid_vars + pinned conditions first, then focal
|
|
670
|
+
pin_cols = list(resolved.set_categoricals) + list(resolved.at_overrides)
|
|
671
|
+
leading = grid_vars + [c for c in pin_cols if c not in grid_vars]
|
|
672
|
+
if leading:
|
|
673
|
+
grid = grid.select(leading + [c for c in grid.columns if c not in leading])
|
|
658
674
|
|
|
659
675
|
cond_str = " ~ " + ", ".join(grid_vars) if grid_vars else ""
|
|
660
676
|
return build_mee_state(
|
|
@@ -770,8 +786,21 @@ def _compute_emm_crossed_gcomp(
|
|
|
770
786
|
L_matrix = np.einsum("mnp,mn->mp", X_all, d_all) / X_all.shape[1] # (C*K, p)
|
|
771
787
|
|
|
772
788
|
grid = pl.DataFrame(grid_data)
|
|
773
|
-
|
|
774
|
-
|
|
789
|
+
|
|
790
|
+
# Add scalar-pinned conditions as constant columns (mirrors MEM path)
|
|
791
|
+
if resolved.set_categoricals:
|
|
792
|
+
for cond_var, cond_level in resolved.set_categoricals.items():
|
|
793
|
+
if cond_var not in grid.columns:
|
|
794
|
+
grid = grid.with_columns(pl.lit(cond_level).alias(cond_var))
|
|
795
|
+
if resolved.at_overrides:
|
|
796
|
+
for cond_var, cond_val in resolved.at_overrides.items():
|
|
797
|
+
if cond_var not in grid.columns:
|
|
798
|
+
grid = grid.with_columns(pl.lit(cond_val).alias(cond_var))
|
|
799
|
+
|
|
800
|
+
pin_cols = list(resolved.set_categoricals) + list(resolved.at_overrides)
|
|
801
|
+
leading = grid_vars + [c for c in pin_cols if c not in grid_vars]
|
|
802
|
+
if leading:
|
|
803
|
+
grid = grid.select(leading + [c for c in grid.columns if c not in leading])
|
|
775
804
|
|
|
776
805
|
cond_str = " ~ " + ", ".join(grid_vars) if grid_vars else ""
|
|
777
806
|
return build_mee_state(
|
|
@@ -96,13 +96,29 @@ def generate_data_from_spec(
|
|
|
96
96
|
if isinstance(v, np.ndarray) and v.dtype.kind == "f"
|
|
97
97
|
]
|
|
98
98
|
|
|
99
|
+
# Build interaction columns from coef keys (e.g. "x1:x2" → x1 * x2)
|
|
99
100
|
obs_n = sim_spec.n
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
interaction_cols: dict[str, np.ndarray] = {}
|
|
102
|
+
for name in sim_spec.coef:
|
|
103
|
+
if ":" not in name:
|
|
104
|
+
continue
|
|
105
|
+
parts = name.split(":")
|
|
106
|
+
# All parts must be sampled continuous variables
|
|
107
|
+
if not all(p in data_dict and data_dict[p].dtype.kind == "f" for p in parts):
|
|
108
|
+
continue
|
|
109
|
+
col = np.ones(obs_n)
|
|
110
|
+
for p in parts:
|
|
111
|
+
col = col * data_dict[p]
|
|
112
|
+
interaction_cols[name] = col
|
|
113
|
+
|
|
114
|
+
# Build X: intercept + main effects + interactions
|
|
115
|
+
interaction_names = list(interaction_cols.keys())
|
|
116
|
+
columns = [np.ones(obs_n)]
|
|
117
|
+
columns.extend(data_dict[v] for v in continuous_vars)
|
|
118
|
+
columns.extend(interaction_cols[name] for name in interaction_names)
|
|
119
|
+
X = np.column_stack(columns) if columns else np.ones((obs_n, 1))
|
|
120
|
+
|
|
121
|
+
coef_names = ["Intercept"] + continuous_vars + interaction_names
|
|
106
122
|
coef_values = np.array([sim_spec.coef.get(name, 0.0) for name in coef_names])
|
|
107
123
|
|
|
108
124
|
# Linear predictor
|
|
@@ -35,7 +35,6 @@ m.plot_resid()
|
|
|
35
35
|
| `plot_resid` | Residual diagnostic grid (4 panels) |
|
|
36
36
|
| `plot_predict` | Marginal predictions across predictor range |
|
|
37
37
|
| `plot_explore` | Marginal effects/means visualization |
|
|
38
|
-
| `plot_fit` | Composite diagnostic panel |
|
|
39
38
|
| `plot_compare` | Multi-model coefficient comparison |
|
|
40
39
|
| `plot_dag` | Causal DAG visualization |
|
|
41
40
|
| `plot_lattice` | Model lattice (Hasse diagram) |
|
|
@@ -6,7 +6,6 @@ Post-estimation plots (require .fit()):
|
|
|
6
6
|
plot_params: Coefficient forest plot with CIs
|
|
7
7
|
plot_resid: Residual diagnostic panels (QQ, fitted vs residuals)
|
|
8
8
|
plot_predict: Prediction plots with confidence bands
|
|
9
|
-
plot_fit: Observed vs fitted scatter
|
|
10
9
|
plot_explore: Marginal effects visualization
|
|
11
10
|
plot_ranef: Random effects caterpillar plot (mixed models)
|
|
12
11
|
plot_resamples: Bootstrap/permutation distribution plots
|
|
@@ -44,7 +43,6 @@ from bossanova.internal.viz.params import plot_params
|
|
|
44
43
|
from bossanova.internal.viz.ranef import plot_ranef
|
|
45
44
|
from bossanova.internal.viz.resid import plot_resid
|
|
46
45
|
from bossanova.internal.viz.predict import plot_predict
|
|
47
|
-
from bossanova.internal.viz.fit import plot_fit
|
|
48
46
|
from bossanova.internal.viz.mem import plot_explore
|
|
49
47
|
from bossanova.internal.viz.compare import plot_compare
|
|
50
48
|
from bossanova.internal.viz.resamples import plot_resamples
|
|
@@ -75,7 +73,6 @@ __all__ = [
|
|
|
75
73
|
"plot_dag",
|
|
76
74
|
"plot_design",
|
|
77
75
|
"plot_explore",
|
|
78
|
-
"plot_fit",
|
|
79
76
|
"plot_lattice",
|
|
80
77
|
"plot_params",
|
|
81
78
|
"plot_predict",
|
|
@@ -18,7 +18,6 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from matplotlib.axes import Axes
|
|
19
19
|
|
|
20
20
|
__all__ = [
|
|
21
|
-
"detect_blup_mode",
|
|
22
21
|
"detect_ci_columns",
|
|
23
22
|
"fill_ci_band",
|
|
24
23
|
"get_grouping_factors",
|
|
@@ -59,8 +58,7 @@ def fill_ci_band(
|
|
|
59
58
|
) -> None:
|
|
60
59
|
"""Render sorted CI bands on a matplotlib axes.
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
and ``predict.add_ci_band``. It sorts data along x, then calls
|
|
61
|
+
Shared rendering core for CI bands. Sorts data along x, then calls
|
|
64
62
|
``ax.fill_between()`` once per hue level (or once if no hue).
|
|
65
63
|
|
|
66
64
|
Args:
|
|
@@ -170,43 +168,3 @@ def get_grouping_factors(model: "Any") -> list[str]:
|
|
|
170
168
|
return groups
|
|
171
169
|
|
|
172
170
|
return []
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def detect_blup_mode(
|
|
176
|
-
model: "Any",
|
|
177
|
-
hue: str | None,
|
|
178
|
-
col: str | None,
|
|
179
|
-
row: str | None,
|
|
180
|
-
blups: bool | None,
|
|
181
|
-
) -> bool:
|
|
182
|
-
"""Determine if BLUP mode should be enabled for a plot.
|
|
183
|
-
|
|
184
|
-
Returns True if:
|
|
185
|
-
- ``blups=True`` explicitly, OR
|
|
186
|
-
- ``blups=None`` AND model is mixed AND any of ``hue``/``col``/``row``
|
|
187
|
-
is a grouping factor (auto-detection).
|
|
188
|
-
|
|
189
|
-
Args:
|
|
190
|
-
model: A fitted bossanova model.
|
|
191
|
-
hue: Hue variable name (or None).
|
|
192
|
-
col: Column facet variable name (or None).
|
|
193
|
-
row: Row facet variable name (or None).
|
|
194
|
-
blups: Explicit BLUP mode setting. None triggers auto-detection.
|
|
195
|
-
|
|
196
|
-
Returns:
|
|
197
|
-
True if BLUP mode should be enabled.
|
|
198
|
-
"""
|
|
199
|
-
if blups is not None:
|
|
200
|
-
return blups
|
|
201
|
-
|
|
202
|
-
# Only mixed models have grouping factors
|
|
203
|
-
grouping_factors = get_grouping_factors(model)
|
|
204
|
-
if not grouping_factors:
|
|
205
|
-
return False
|
|
206
|
-
|
|
207
|
-
# Check if any faceting/hue variable is a grouping factor
|
|
208
|
-
for var in (hue, col, row):
|
|
209
|
-
if var and var in grouping_factors:
|
|
210
|
-
return True
|
|
211
|
-
|
|
212
|
-
return False
|
|
@@ -36,6 +36,142 @@ if TYPE_CHECKING:
|
|
|
36
36
|
__all__ = ["plot_predict"]
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Formula parsing helpers
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
_FORMULA_CHARS = {"~", "@", "(", "["}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _is_formula(term: str) -> bool:
|
|
47
|
+
"""Return True if *term* looks like formula syntax rather than a bare column name.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
term: The user-supplied term string.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
True when the string contains ``~``, ``@``, or ``(``.
|
|
54
|
+
"""
|
|
55
|
+
return bool(_FORMULA_CHARS & set(term))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _parse_predict_formula(
|
|
59
|
+
formula: str,
|
|
60
|
+
model: "Any",
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
"""Translate an explore-style formula into :func:`plot_predict` kwargs.
|
|
63
|
+
|
|
64
|
+
Parses ``formula`` using the existing :func:`parse_explore_formula`
|
|
65
|
+
machinery, then maps the resulting :class:`ExploreFormulaSpec` fields
|
|
66
|
+
onto the keyword arguments that :func:`plot_predict` understands.
|
|
67
|
+
|
|
68
|
+
Contrast formulas (``pairwise(treatment)``, ``Drug[A - B]``) are
|
|
69
|
+
rejected with a ``ValueError`` — use :func:`plot_explore` instead.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
formula: An explore-style formula string (e.g. ``"age ~ sex"``).
|
|
73
|
+
model: A fitted bossanova model (used for data access and term
|
|
74
|
+
validation).
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Dict with keys: ``term``, and optionally ``hue``, ``col``, ``row``,
|
|
78
|
+
``at``, ``n_points``, ``focal_values``.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
ValueError: If the formula contains contrasts.
|
|
82
|
+
"""
|
|
83
|
+
from bossanova.internal.marginal import parse_explore_formula
|
|
84
|
+
|
|
85
|
+
spec = parse_explore_formula(formula)
|
|
86
|
+
|
|
87
|
+
# Reject contrast formulas — they belong in plot_explore
|
|
88
|
+
if spec.has_contrast:
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"Contrast formula {formula!r} is not supported by plot_predict(). "
|
|
91
|
+
"Use plot_explore() for contrasts."
|
|
92
|
+
)
|
|
93
|
+
# Also reject RHS bracket contrasts
|
|
94
|
+
if spec.has_rhs_contrasts:
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f"Contrast formula {formula!r} is not supported by plot_predict(). "
|
|
97
|
+
"Use plot_explore() for contrasts."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
result: dict[str, Any] = {"term": spec.focal_var}
|
|
101
|
+
data = model.data
|
|
102
|
+
|
|
103
|
+
# Handle focal variable modifiers (@range, @[values], @quantile)
|
|
104
|
+
if spec.focal_at_range is not None:
|
|
105
|
+
result["n_points"] = spec.focal_at_range
|
|
106
|
+
elif spec.focal_at_values is not None:
|
|
107
|
+
result["focal_values"] = list(spec.focal_at_values)
|
|
108
|
+
elif spec.focal_at_quantile is not None:
|
|
109
|
+
col_data = data[spec.focal_var].drop_nulls().to_numpy()
|
|
110
|
+
quantiles = np.linspace(0, 1, spec.focal_at_quantile + 2)[1:-1]
|
|
111
|
+
result["focal_values"] = list(np.quantile(col_data, quantiles))
|
|
112
|
+
|
|
113
|
+
# Classify conditions into faceting roles (hue/col/row) or at-pins
|
|
114
|
+
at: dict[str, Any] = {}
|
|
115
|
+
facet_slots: list[str] = [] # hue, col, row — assigned in order
|
|
116
|
+
|
|
117
|
+
for cond in spec.conditions:
|
|
118
|
+
resolved = _resolve_condition_values(cond, data)
|
|
119
|
+
if resolved is not None and len(resolved) == 1:
|
|
120
|
+
# Single pinned value → goes into at dict
|
|
121
|
+
at[cond.var] = resolved[0]
|
|
122
|
+
else:
|
|
123
|
+
# Bare or multi-value condition → assigned to facet slot
|
|
124
|
+
if len(facet_slots) >= 3:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"Too many unpinned conditions in formula {formula!r}. "
|
|
127
|
+
"plot_predict supports at most 3 faceting variables "
|
|
128
|
+
"(hue, col, row). Pin additional conditions with @value."
|
|
129
|
+
)
|
|
130
|
+
facet_slots.append(cond.var)
|
|
131
|
+
# If condition has explicit multi-values, inject via at as list
|
|
132
|
+
if resolved is not None:
|
|
133
|
+
at[cond.var] = resolved
|
|
134
|
+
|
|
135
|
+
# Map facet slots to hue → col → row
|
|
136
|
+
facet_keys = ["hue", "col", "row"]
|
|
137
|
+
for key, var in zip(facet_keys, facet_slots):
|
|
138
|
+
result[key] = var
|
|
139
|
+
|
|
140
|
+
if at:
|
|
141
|
+
result["at"] = at
|
|
142
|
+
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _resolve_condition_values(
|
|
147
|
+
cond: Any,
|
|
148
|
+
data: pl.DataFrame,
|
|
149
|
+
) -> list | None:
|
|
150
|
+
"""Resolve a :class:`Condition` to concrete values or ``None``.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
cond: A ``Condition`` from :func:`parse_explore_formula`.
|
|
154
|
+
data: The model's training data.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
A list of concrete values if the condition specifies explicit
|
|
158
|
+
values (``at_values``, ``at_range``, ``at_quantile``), or
|
|
159
|
+
``None`` for bare conditions (use all unique levels).
|
|
160
|
+
"""
|
|
161
|
+
if cond.at_values is not None:
|
|
162
|
+
return list(cond.at_values)
|
|
163
|
+
if cond.at_range is not None:
|
|
164
|
+
col_data = data[cond.var].drop_nulls().to_numpy()
|
|
165
|
+
return list(
|
|
166
|
+
np.linspace(np.nanmin(col_data), np.nanmax(col_data), cond.at_range)
|
|
167
|
+
)
|
|
168
|
+
if cond.at_quantile is not None:
|
|
169
|
+
col_data = data[cond.var].drop_nulls().to_numpy()
|
|
170
|
+
quantiles = np.linspace(0, 1, cond.at_quantile + 2)[1:-1]
|
|
171
|
+
return list(np.quantile(col_data, quantiles))
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
|
|
39
175
|
def plot_predict(
|
|
40
176
|
model: "Any",
|
|
41
177
|
term: str,
|
|
@@ -65,9 +201,21 @@ def plot_predict(
|
|
|
65
201
|
holding others at reference values (means for continuous, reference level
|
|
66
202
|
for categorical).
|
|
67
203
|
|
|
204
|
+
Accepts either a bare column name or an explore-style formula::
|
|
205
|
+
|
|
206
|
+
plot_predict(m, "age") # bare column
|
|
207
|
+
plot_predict(m, "age ~ sex") # hue by sex
|
|
208
|
+
plot_predict(m, "age ~ sex@Female") # pin sex=Female
|
|
209
|
+
plot_predict(m, "age@range(5)") # 5-point grid
|
|
210
|
+
plot_predict(m, "age@[25,50,75]") # explicit grid values
|
|
211
|
+
|
|
212
|
+
Contrast formulas (``pairwise()``, ``Drug[A - B]``) are not supported —
|
|
213
|
+
use :func:`plot_explore` for those.
|
|
214
|
+
|
|
68
215
|
Args:
|
|
69
216
|
model: A fitted bossanova model.
|
|
70
|
-
term: The predictor variable to vary
|
|
217
|
+
term: The predictor variable to vary, or an explore-style formula
|
|
218
|
+
string. Can be continuous or categorical.
|
|
71
219
|
hue: Column name for color encoding (creates separate lines per level).
|
|
72
220
|
col: Column name for faceting into columns.
|
|
73
221
|
row: Column name for faceting into rows.
|
|
@@ -121,7 +269,6 @@ def plot_predict(
|
|
|
121
269
|
viz.plot_predict(m, "Days", show_blups=True)
|
|
122
270
|
|
|
123
271
|
See Also:
|
|
124
|
-
plot_fit: Observed vs Predicted scatter plot.
|
|
125
272
|
plot_explore: Marginal effects/means visualization.
|
|
126
273
|
"""
|
|
127
274
|
import seaborn as sns
|
|
@@ -129,6 +276,22 @@ def plot_predict(
|
|
|
129
276
|
if not model._is_fitted:
|
|
130
277
|
raise RuntimeError("Model must be fitted before plotting predictions")
|
|
131
278
|
|
|
279
|
+
# Formula detection: if term contains ~, @, or ( → parse as formula
|
|
280
|
+
focal_values: list[float | str] | None = None
|
|
281
|
+
if _is_formula(term):
|
|
282
|
+
parsed = _parse_predict_formula(term, model)
|
|
283
|
+
term = parsed["term"]
|
|
284
|
+
# Override kwargs only if formula provided them and user didn't
|
|
285
|
+
hue = hue or parsed.get("hue")
|
|
286
|
+
col = col or parsed.get("col")
|
|
287
|
+
row = row or parsed.get("row")
|
|
288
|
+
if parsed.get("n_points") is not None:
|
|
289
|
+
n_points = parsed["n_points"]
|
|
290
|
+
focal_values = parsed.get("focal_values")
|
|
291
|
+
# Merge at dicts: user-supplied at takes precedence
|
|
292
|
+
if parsed.get("at"):
|
|
293
|
+
at = {**parsed["at"], **(at or {})}
|
|
294
|
+
|
|
132
295
|
style = BOSSANOVA_STYLE
|
|
133
296
|
if palette is None:
|
|
134
297
|
palette = style["palette"]
|
|
@@ -159,6 +322,7 @@ def plot_predict(
|
|
|
159
322
|
conf_int=conf_int,
|
|
160
323
|
show_blups=show_blups,
|
|
161
324
|
groups=groups,
|
|
325
|
+
focal_values=focal_values,
|
|
162
326
|
)
|
|
163
327
|
|
|
164
328
|
# Create the plot
|
|
@@ -209,8 +373,32 @@ def build_prediction_data(
|
|
|
209
373
|
conf_int: float,
|
|
210
374
|
show_blups: bool,
|
|
211
375
|
groups: list[str] | None,
|
|
376
|
+
focal_values: list[float | str] | None = None,
|
|
212
377
|
) -> pl.DataFrame:
|
|
213
|
-
"""Build prediction DataFrame with fitted values and intervals.
|
|
378
|
+
"""Build prediction DataFrame with fitted values and intervals.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
model: A fitted bossanova model.
|
|
382
|
+
data: The model's training data.
|
|
383
|
+
term: The focal predictor variable to vary.
|
|
384
|
+
hue: Column for color encoding.
|
|
385
|
+
col: Column for column faceting.
|
|
386
|
+
row: Column for row faceting.
|
|
387
|
+
at: Dictionary of predictor values to hold fixed. Values can be
|
|
388
|
+
scalars (pinned to one value) or lists (expanded into the grid).
|
|
389
|
+
n_points: Number of points for continuous predictors.
|
|
390
|
+
is_categorical: Whether term is categorical.
|
|
391
|
+
pred_type: Prediction type ("data" or "link").
|
|
392
|
+
interval: Interval type ("confidence", "prediction", or None).
|
|
393
|
+
conf_int: Confidence level.
|
|
394
|
+
show_blups: Whether to show group-specific BLUP lines.
|
|
395
|
+
groups: Which group levels to show for BLUPs.
|
|
396
|
+
focal_values: Explicit values for the focal variable's grid.
|
|
397
|
+
When set, overrides the default linspace grid.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Polars DataFrame with prediction grid and fitted values.
|
|
401
|
+
"""
|
|
214
402
|
response_col = get_model_formula(model).split("~")[0].strip()
|
|
215
403
|
grouping_factors = get_grouping_factors(model)
|
|
216
404
|
is_mixed = is_mixed_model(model)
|
|
@@ -239,8 +427,10 @@ def build_prediction_data(
|
|
|
239
427
|
continue # Skip if not showing BLUPs
|
|
240
428
|
|
|
241
429
|
if col_name == term:
|
|
242
|
-
# Vary this term
|
|
243
|
-
if
|
|
430
|
+
# Vary this term — use focal_values if provided
|
|
431
|
+
if focal_values is not None:
|
|
432
|
+
grid_data[col_name] = list(focal_values)
|
|
433
|
+
elif is_categorical:
|
|
244
434
|
grid_data[col_name] = data[col_name].unique().sort().to_list()
|
|
245
435
|
else:
|
|
246
436
|
col_data = data[col_name].to_numpy()
|
|
@@ -249,7 +439,9 @@ def build_prediction_data(
|
|
|
249
439
|
np.linspace(np.nanmin(col_data), np.nanmax(col_data), n_points)
|
|
250
440
|
)
|
|
251
441
|
elif at and col_name in at:
|
|
252
|
-
|
|
442
|
+
# Support list-valued at entries (expand into grid)
|
|
443
|
+
val = at[col_name]
|
|
444
|
+
grid_data[col_name] = list(val) if isinstance(val, list) else [val]
|
|
253
445
|
elif col_name in [hue, col, row] and col_name is not None:
|
|
254
446
|
# Vary grouping variables
|
|
255
447
|
grid_data[col_name] = data[col_name].unique().sort().to_list()
|
|
@@ -472,11 +664,6 @@ def add_ci_band(
|
|
|
472
664
|
confidence bounds. Supports both ``ci_lower``/``ci_upper``
|
|
473
665
|
(preferred) and ``lwr``/``upr`` (legacy) column naming.
|
|
474
666
|
|
|
475
|
-
Unlike ``fit_layers.add_ci_band``, this version receives
|
|
476
|
-
pre-filtered data from ``FacetGrid.map_dataframe()`` and generates
|
|
477
|
-
hue colors dynamically via ``plt.cm.tab10`` rather than using a
|
|
478
|
-
pre-computed color map.
|
|
479
|
-
|
|
480
667
|
Delegates rendering to :func:`~bossanova.internal.viz.helpers.fill_ci_band`.
|
|
481
668
|
|
|
482
669
|
Args:
|
|
@@ -1968,8 +1968,16 @@ class model:
|
|
|
1968
1968
|
def plot_predict(self, term: str, **kwargs: object) -> object:
|
|
1969
1969
|
"""Plot marginal predictions across a predictor range.
|
|
1970
1970
|
|
|
1971
|
+
Accepts either a bare column name or an explore-style formula::
|
|
1972
|
+
|
|
1973
|
+
m.plot_predict("age") # bare column
|
|
1974
|
+
m.plot_predict("age ~ sex") # hue by sex
|
|
1975
|
+
m.plot_predict("age ~ sex@Female") # pin sex=Female
|
|
1976
|
+
m.plot_predict("age@range(5)") # 5-point grid
|
|
1977
|
+
m.plot_predict("age@[25,50,75]") # explicit grid values
|
|
1978
|
+
|
|
1971
1979
|
Args:
|
|
1972
|
-
term: Predictor variable
|
|
1980
|
+
term: Predictor variable or explore-style formula string.
|
|
1973
1981
|
**kwargs: Forwarded to ``bossanova.viz.plot_predict``. Key
|
|
1974
1982
|
options: ``hue``, ``col``, ``at``, ``interval``,
|
|
1975
1983
|
``show_data``, ``show_rug``, ``height``, ``aspect``.
|
|
@@ -1979,20 +1987,6 @@ class model:
|
|
|
1979
1987
|
"""
|
|
1980
1988
|
return self._plot("plot_predict", term, **kwargs)
|
|
1981
1989
|
|
|
1982
|
-
def plot_fit(self, x: str, **kwargs: object) -> object:
|
|
1983
|
-
"""Plot fitted model against observed data.
|
|
1984
|
-
|
|
1985
|
-
Args:
|
|
1986
|
-
x: Predictor variable for the x-axis.
|
|
1987
|
-
**kwargs: Forwarded to ``bossanova.viz.plot_fit``. Key
|
|
1988
|
-
options: ``hue``, ``col``, ``scatter``, ``fit_line``,
|
|
1989
|
-
``ci``, ``blups``, ``groups``, ``height``.
|
|
1990
|
-
|
|
1991
|
-
Returns:
|
|
1992
|
-
object: Matplotlib figure.
|
|
1993
|
-
"""
|
|
1994
|
-
return self._plot("plot_fit", x, **kwargs)
|
|
1995
|
-
|
|
1996
1990
|
def plot_explore(self, specs: str, **kwargs: object) -> object:
|
|
1997
1991
|
"""Plot marginal effects or estimated marginal means.
|
|
1998
1992
|
|