bossanova 0.1.0.dev19__tar.gz → 0.1.0.dev21__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.dev19 → bossanova-0.1.0.dev21}/.gitignore +3 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/PKG-INFO +1 -1
- bossanova-0.1.0.dev21/bossanova/distributions/__init__.py +63 -0
- bossanova-0.1.0.dev21/bossanova/distributions/continuous.py +45 -0
- bossanova-0.1.0.dev21/bossanova/distributions/discrete.py +217 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/distributions/varying.py +39 -58
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/__init__.py +2 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/compare.py +16 -13
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/cv.py +3 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/deviance.py +13 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/helpers.py +5 -10
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/lrt.py +15 -12
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/__init__.py +9 -5
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/dataframes.py +0 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/resamples.py +2 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/specs.py +16 -16
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/state.py +10 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/schemas.py +33 -19
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/__init__.py +4 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/data.py +7 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/display.py +6 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/explore.py +2 -11
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/state.py +16 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/validators.py +1 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/design/reference.py +23 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/design/z_matrix.py +1 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/__init__.py +6 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/convergence.py +3 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/dispatch.py +83 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/glm.py +2 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/glmer.py +20 -23
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/predict.py +3 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/varying.py +76 -8
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/bundle.py +87 -16
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/contrast_specs.py +40 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/design.py +2 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/encoding.py +113 -73
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate_transforms.py +2 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/__init__.py +6 -6
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/asymptotic.py +5 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/bootstrap.py +92 -45
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/cv.py +213 -185
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/dispatch.py +54 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/mee.py +2 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/permutation.py +29 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/profile.py +8 -9
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/__init__.py +6 -6
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/common.py +6 -6
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/core.py +11 -11
- bossanova-0.1.0.dev21/bossanova/internal/infer/resample/results.py +53 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample_bundle.py +10 -10
- bossanova-0.1.0.dev21/bossanova/internal/infer/satterthwaite_emm.py +326 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/__init__.py +2 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/bracket_contrasts.py +77 -7
- bossanova-0.1.0.dev21/bossanova/internal/marginal/compute.py +1025 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/conditions.py +22 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/contrasts.py +131 -5
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/emm.py +335 -40
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/explore.py +8 -11
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/explore_parser.py +37 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/grid.py +1 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/joint_tests.py +2 -2
- bossanova-0.1.0.dev21/bossanova/internal/marginal/resolve.py +365 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/slopes.py +65 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/transforms.py +37 -33
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/__init__.py +6 -62
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/dispatch.py +2 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/__init__.py +5 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/algebra.py +2 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/base.py +41 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/derived.py +4 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/factories.py +75 -12
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/plotting.py +2 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/__init__.py +3 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/binomial.py +0 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/create.py +5 -5
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/gamma.py +0 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/gaussian.py +0 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/links.py +0 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/poisson.py +0 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/schema.py +0 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/tdist.py +9 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/__init__.py +6 -6
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/diagnostics.py +15 -9
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/sandwich.py +3 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/satterthwaite.py +12 -16
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/wald_variance.py +0 -5
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/welch.py +13 -13
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/__init__.py +4 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/qr.py +2 -6
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/sparse.py +0 -10
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/rng.py +3 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/glm.py +0 -37
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/glmer.py +40 -15
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lambda_sparse.py +2 -11
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lambda_template.py +0 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/optimize.py +1 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/pirls_sparse.py +27 -15
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/transforms.py +7 -6
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/weights.py +0 -3
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/generate.py +1 -14
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/compare.py +27 -22
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_data.py +53 -52
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_protocols.py +6 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/fit.py +2 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/fit_builders.py +8 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/fit_layers.py +8 -7
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/helpers.py +4 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/layout.py +0 -2
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/mem.py +52 -28
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/mem_forest.py +49 -20
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/params.py +7 -6
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/predict.py +9 -8
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/ranef.py +16 -13
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/resamples.py +5 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/resid.py +14 -13
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/vif.py +2 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/model/core.py +128 -153
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/model/summary.py +24 -23
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/pyproject.toml +74 -5
- bossanova-0.1.0.dev19/bossanova/distributions/__init__.py +0 -53
- bossanova-0.1.0.dev19/bossanova/distributions/continuous.py +0 -273
- bossanova-0.1.0.dev19/bossanova/distributions/discrete.py +0 -274
- bossanova-0.1.0.dev19/bossanova/internal/infer/resample/results.py +0 -136
- bossanova-0.1.0.dev19/bossanova/internal/infer/satterthwaite_emm.py +0 -218
- bossanova-0.1.0.dev19/bossanova/internal/marginal/compute.py +0 -1219
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/LICENSE +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/README.md +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/README.md +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/advertising.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/cake.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/chickweight.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/credit.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/gammas.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/mtcars.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/penguins.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/poker.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/sleep.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/titanic.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/titanic_test.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/data/titanic_train.csv +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/expressions.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/f_test.py +4 -4
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/ic.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/lrt_compare.py +1 -1
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/compare/refit.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/results.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/formula.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/specs.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/design/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/design/coding.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/design/names.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/diagnostics.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/lmer.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/fit/ols.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/contrast_registry.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate_contrast.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate_newdata.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/helpers.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parse.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/expr.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/parser.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/scanner.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/token.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/formula/random_effects.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/formula_utils.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/params.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/prediction.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/glmer.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/lm_operators.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/lmer.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/simulate.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/infer/simulation.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/explore_scanner.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/factors.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/inference.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/matrices.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/validation.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/jax.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/numpy.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/protocol.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/batching.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/config.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/convergence.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/differentiation.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/core.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/probability.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/response.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/estimation.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/hypothesis.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/information_criteria.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/multiplicity.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/profile.py +0 -0
- {bossanova-0.1.0.dev19/bossanova/internal/fit → bossanova-0.1.0.dev21/bossanova/internal/maths/linalg}/rank.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/schur.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/svd.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/predict.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/rounding.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/heuristics.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/initialization.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lambda_builder.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lmer.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/quadrature.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/tolerances.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/maths/variance.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/rendering/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/rendering/latex.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/rendering/markdown.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/glm.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/glmer.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/lm.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/lmer.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/harness.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/metrics.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/model_sim.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/power.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/README.md +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/cognition.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_sizing.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_viz.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/dag.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/design.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/lattice.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/profile.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/internal/viz/relationships.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/model/__init__.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/model/guards.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/model/result.py +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/bossanova/py.typed +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/tests/bossanova_benchmarks/bootstrap/data/README.md +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/tests/bossanova_benchmarks/insteval/data/README.md +0 -0
- {bossanova-0.1.0.dev19 → bossanova-0.1.0.dev21}/tests/bossanova_tests/hypothesis/README.md +0 -0
|
@@ -17,7 +17,7 @@ docs/_build/
|
|
|
17
17
|
docs/performance/
|
|
18
18
|
profile_*.py
|
|
19
19
|
benchmarks/
|
|
20
|
-
!bossanova-docs/
|
|
20
|
+
!bossanova-docs/benchmarking/
|
|
21
21
|
examples/
|
|
22
22
|
papers/
|
|
23
23
|
paper-summaries/
|
|
@@ -42,6 +42,7 @@ tests/parity/traces/
|
|
|
42
42
|
# Coverage reports
|
|
43
43
|
coverage_reports/
|
|
44
44
|
.coverage
|
|
45
|
+
.coverage.*
|
|
45
46
|
htmlcov/
|
|
46
47
|
# pixi environments
|
|
47
48
|
.pixi/*
|
|
@@ -84,3 +85,4 @@ bossanova-docs/wip/
|
|
|
84
85
|
research.md
|
|
85
86
|
201b-ghct-05-models/
|
|
86
87
|
emmeans/
|
|
88
|
+
walkthrough.md
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Distribution factories for simulation and probability queries.
|
|
2
|
+
|
|
3
|
+
Users get rich Distribution objects with plotting, algebra, and probability::
|
|
4
|
+
|
|
5
|
+
d = normal(0, 1)
|
|
6
|
+
d # In notebooks: renders PDF plot
|
|
7
|
+
d + normal(3, 2) # Convolution -> Normal(3, sqrt(5))
|
|
8
|
+
d > 1.96 # P(X > 1.96) = 0.025
|
|
9
|
+
d.pdf(0) # 0.3989
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from bossanova.distributions.continuous import (
|
|
13
|
+
Distribution,
|
|
14
|
+
exponential,
|
|
15
|
+
gamma,
|
|
16
|
+
normal,
|
|
17
|
+
t,
|
|
18
|
+
t_dist,
|
|
19
|
+
uniform,
|
|
20
|
+
)
|
|
21
|
+
from bossanova.distributions.discrete import (
|
|
22
|
+
Categorical,
|
|
23
|
+
binomial,
|
|
24
|
+
categorical,
|
|
25
|
+
poisson,
|
|
26
|
+
)
|
|
27
|
+
from bossanova.distributions.varying import Varying, varying
|
|
28
|
+
|
|
29
|
+
# Rich internal types also available
|
|
30
|
+
from bossanova.internal.maths.distributions import (
|
|
31
|
+
ConvolvedDistribution,
|
|
32
|
+
f_dist,
|
|
33
|
+
FoldedDistribution,
|
|
34
|
+
Probability,
|
|
35
|
+
TransformedDistribution,
|
|
36
|
+
TruncatedDistribution,
|
|
37
|
+
beta,
|
|
38
|
+
chi2,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"Categorical",
|
|
43
|
+
"ConvolvedDistribution",
|
|
44
|
+
"Distribution",
|
|
45
|
+
"FoldedDistribution",
|
|
46
|
+
"Probability",
|
|
47
|
+
"TransformedDistribution",
|
|
48
|
+
"TruncatedDistribution",
|
|
49
|
+
"Varying",
|
|
50
|
+
"beta",
|
|
51
|
+
"binomial",
|
|
52
|
+
"categorical",
|
|
53
|
+
"chi2",
|
|
54
|
+
"exponential",
|
|
55
|
+
"f_dist",
|
|
56
|
+
"gamma",
|
|
57
|
+
"normal",
|
|
58
|
+
"poisson",
|
|
59
|
+
"t",
|
|
60
|
+
"t_dist",
|
|
61
|
+
"uniform",
|
|
62
|
+
"varying",
|
|
63
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Continuous distribution factories — thin facade over internal/maths/distributions/.
|
|
2
|
+
|
|
3
|
+
Re-exports internal Distribution factories with user-facing parameterization.
|
|
4
|
+
Users get rich Distribution objects with plotting, algebra, and probability queries.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from bossanova.internal.maths.distributions.base import Distribution
|
|
8
|
+
from bossanova.internal.maths.distributions.factories import (
|
|
9
|
+
exponential,
|
|
10
|
+
gamma as _internal_gamma,
|
|
11
|
+
normal,
|
|
12
|
+
t,
|
|
13
|
+
t_dist,
|
|
14
|
+
uniform,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def gamma(shape: float, scale: float = 1.0) -> Distribution:
|
|
19
|
+
"""Create a gamma distribution (scale parameterization only).
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
shape: Shape parameter (must be positive).
|
|
23
|
+
scale: Scale parameter (must be positive). Default is 1.0.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Distribution object.
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
```python
|
|
30
|
+
g = gamma(shape=2.0) # Gamma(2, 1), mean = 2
|
|
31
|
+
g = gamma(shape=2.0, scale=0.5) # Gamma(2, 0.5), mean = 1
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
return _internal_gamma(shape=shape, scale=scale)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"Distribution",
|
|
39
|
+
"exponential",
|
|
40
|
+
"gamma",
|
|
41
|
+
"normal",
|
|
42
|
+
"t",
|
|
43
|
+
"t_dist",
|
|
44
|
+
"uniform",
|
|
45
|
+
]
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""Discrete distribution factories — thin facade.
|
|
2
|
+
|
|
3
|
+
Categorical stays as a native attrs struct (no internal counterpart).
|
|
4
|
+
Poisson and Binomial delegate to internal Distribution objects.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from attrs import frozen, field
|
|
11
|
+
from scipy import stats
|
|
12
|
+
|
|
13
|
+
from bossanova.internal.maths.distributions.base import Distribution
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@frozen
|
|
17
|
+
class Categorical:
|
|
18
|
+
"""Categorical distribution for simulation.
|
|
19
|
+
|
|
20
|
+
Generates categorical values from specified levels with optional probabilities.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
levels: Category names. Either provide explicit levels or use k= for auto-naming.
|
|
24
|
+
p: Probabilities for each level. Must sum to 1. If None, uniform distribution.
|
|
25
|
+
k: Number of levels for auto-naming (level_1, level_2, ...).
|
|
26
|
+
Ignored if levels is provided.
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: If neither levels nor k is provided.
|
|
30
|
+
ValueError: If both levels and k are provided.
|
|
31
|
+
ValueError: If p is provided without levels.
|
|
32
|
+
ValueError: If the length of p does not match the length of levels.
|
|
33
|
+
ValueError: If p does not sum to 1.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
```python
|
|
37
|
+
from bossanova.distributions import categorical
|
|
38
|
+
|
|
39
|
+
cat = categorical(levels=["A", "B", "C"])
|
|
40
|
+
rng = np.random.default_rng(42)
|
|
41
|
+
samples = cat.sample(5, rng) # shape (5,)
|
|
42
|
+
|
|
43
|
+
cat = categorical(levels=["A", "B"], p=[0.8, 0.2]) # mostly "A"
|
|
44
|
+
|
|
45
|
+
cat = categorical(k=4) # level_1, level_2, level_3, level_4
|
|
46
|
+
```
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
levels: tuple[str, ...] | None = field(
|
|
50
|
+
default=None, converter=lambda x: tuple(x) if x is not None else None
|
|
51
|
+
)
|
|
52
|
+
p: tuple[float, ...] | None = field(
|
|
53
|
+
default=None, converter=lambda x: tuple(x) if x is not None else None
|
|
54
|
+
)
|
|
55
|
+
k: int | None = field(default=None)
|
|
56
|
+
|
|
57
|
+
def __attrs_post_init__(self) -> None:
|
|
58
|
+
"""Validate that levels/k and p are consistent."""
|
|
59
|
+
# Must have either levels or k
|
|
60
|
+
if self.levels is None and self.k is None:
|
|
61
|
+
msg = "Must specify either levels or k"
|
|
62
|
+
raise ValueError(msg)
|
|
63
|
+
if self.levels is not None and self.k is not None:
|
|
64
|
+
msg = "Specify either levels or k, not both"
|
|
65
|
+
raise ValueError(msg)
|
|
66
|
+
|
|
67
|
+
# Validate p if provided
|
|
68
|
+
if self.p is not None:
|
|
69
|
+
if self.levels is None:
|
|
70
|
+
msg = "p requires levels to be specified"
|
|
71
|
+
raise ValueError(msg)
|
|
72
|
+
if len(self.p) != len(self.levels):
|
|
73
|
+
msg = f"p length ({len(self.p)}) must match levels length ({len(self.levels)})"
|
|
74
|
+
raise ValueError(msg)
|
|
75
|
+
if not np.isclose(sum(self.p), 1.0):
|
|
76
|
+
msg = f"p must sum to 1.0, got {sum(self.p)}"
|
|
77
|
+
raise ValueError(msg)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def actual_levels(self) -> tuple[str, ...]:
|
|
81
|
+
"""Return the actual level names (resolving k if needed)."""
|
|
82
|
+
if self.levels is not None:
|
|
83
|
+
return self.levels
|
|
84
|
+
return tuple(f"level_{i + 1}" for i in range(self.k)) # type: ignore[arg-type]
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def n_levels(self) -> int:
|
|
88
|
+
"""Return the number of levels."""
|
|
89
|
+
return len(self.actual_levels)
|
|
90
|
+
|
|
91
|
+
def sample(self, n: int, rng: np.random.Generator) -> np.ndarray:
|
|
92
|
+
"""Sample n categorical values.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
n: Number of samples to draw.
|
|
96
|
+
rng: NumPy random number generator.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Array of n categorical values as strings.
|
|
100
|
+
"""
|
|
101
|
+
levels = self.actual_levels
|
|
102
|
+
probs = self.p if self.p is not None else None
|
|
103
|
+
indices = rng.choice(len(levels), size=n, p=probs)
|
|
104
|
+
return np.array([levels[i] for i in indices])
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# =============================================================================
|
|
108
|
+
# Factory functions
|
|
109
|
+
# =============================================================================
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def categorical(
|
|
113
|
+
levels: list[str] | None = None,
|
|
114
|
+
*,
|
|
115
|
+
p: list[float] | None = None,
|
|
116
|
+
k: int | None = None,
|
|
117
|
+
) -> Categorical:
|
|
118
|
+
"""Create a categorical distribution.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
levels: Category names. Either provide explicit levels or use k= for
|
|
122
|
+
auto-naming (level_1, level_2, ...). Mutually exclusive with k.
|
|
123
|
+
p: Probabilities for each level. Must sum to 1. Length must match
|
|
124
|
+
levels. If None, a uniform distribution over levels is used.
|
|
125
|
+
Requires levels to be specified.
|
|
126
|
+
k: Number of levels for auto-naming (level_1, level_2, ...).
|
|
127
|
+
Mutually exclusive with levels.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Categorical distribution object.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
ValueError: If neither levels nor k is provided.
|
|
134
|
+
ValueError: If both levels and k are provided.
|
|
135
|
+
ValueError: If p is provided without levels.
|
|
136
|
+
ValueError: If the length of p does not match the length of levels.
|
|
137
|
+
ValueError: If p does not sum to 1.
|
|
138
|
+
|
|
139
|
+
Examples:
|
|
140
|
+
```python
|
|
141
|
+
categorical(["A", "B", "C"])
|
|
142
|
+
# Categorical(levels=('A', 'B', 'C'), p=None, k=None)
|
|
143
|
+
|
|
144
|
+
categorical(["A", "B"], p=[0.7, 0.3])
|
|
145
|
+
# Categorical(levels=('A', 'B'), p=(0.7, 0.3), k=None)
|
|
146
|
+
|
|
147
|
+
categorical(k=4) # level_1, level_2, level_3, level_4
|
|
148
|
+
# Categorical(levels=None, p=None, k=4)
|
|
149
|
+
```
|
|
150
|
+
"""
|
|
151
|
+
return Categorical(levels=levels, p=p, k=k)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def poisson(rate: float = 1.0) -> Distribution:
|
|
155
|
+
"""Create a Poisson distribution.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
rate: Rate parameter (lambda). Must be > 0. Default is 1.0.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Distribution object.
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
ValueError: If rate is not positive.
|
|
165
|
+
|
|
166
|
+
Examples:
|
|
167
|
+
```python
|
|
168
|
+
dist = poisson() # Poisson(1)
|
|
169
|
+
dist = poisson(rate=5.0) # Poisson(5), mean = 5
|
|
170
|
+
```
|
|
171
|
+
"""
|
|
172
|
+
if rate <= 0:
|
|
173
|
+
raise ValueError("rate must be > 0")
|
|
174
|
+
return Distribution(
|
|
175
|
+
dist=stats.poisson(mu=rate),
|
|
176
|
+
name="Poisson",
|
|
177
|
+
params={"rate": rate},
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def binomial(n: int = 1, p: float = 0.5) -> Distribution:
|
|
182
|
+
"""Create a binomial distribution.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
n: Number of trials. Must be >= 1. Default is 1 (Bernoulli).
|
|
186
|
+
p: Probability of success. Must be in [0, 1]. Default is 0.5.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Distribution object.
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
ValueError: If n is not an integer >= 1 or p is not in [0, 1].
|
|
193
|
+
|
|
194
|
+
Examples:
|
|
195
|
+
```python
|
|
196
|
+
dist = binomial() # Bernoulli(0.5)
|
|
197
|
+
dist = binomial(n=10, p=0.3) # Binomial(10, 0.3), mean = 3
|
|
198
|
+
```
|
|
199
|
+
"""
|
|
200
|
+
if not isinstance(n, int) or n < 1:
|
|
201
|
+
raise ValueError("n must be an integer >= 1")
|
|
202
|
+
if not 0 <= p <= 1:
|
|
203
|
+
raise ValueError(f"p ({p}) must be in [0, 1]")
|
|
204
|
+
return Distribution(
|
|
205
|
+
dist=stats.binom(n=n, p=p),
|
|
206
|
+
name="Binomial",
|
|
207
|
+
params={"n": n, "p": p},
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
__all__ = [
|
|
212
|
+
"Categorical",
|
|
213
|
+
"Distribution",
|
|
214
|
+
"binomial",
|
|
215
|
+
"categorical",
|
|
216
|
+
"poisson",
|
|
217
|
+
]
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from attrs import frozen, field, validators
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
|
+
__all__ = ["Varying", "varying"]
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
@frozen
|
|
8
10
|
class Varying:
|
|
@@ -15,27 +17,22 @@ class Varying:
|
|
|
15
17
|
sd (float): Standard deviation for random intercept. Default 1.0.
|
|
16
18
|
intercept_sd (float | None): Alias for sd (for clarity with random slopes).
|
|
17
19
|
slope_sds (dict[str, float]): Dict mapping term names to their random slope SDs.
|
|
18
|
-
corr (float
|
|
19
|
-
|
|
20
|
+
corr (float): Correlation between intercept and slope(s). Applied
|
|
21
|
+
uniformly across all term pairs. Default 0.0.
|
|
20
22
|
n_per (int | None): For nested effects, number of sub-groups per parent group.
|
|
21
23
|
|
|
22
24
|
Examples:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Varying(n=50, sd=0.3, ...)
|
|
27
|
-
|
|
28
|
-
Random intercept + slope: (1 + time|subject)::
|
|
29
|
-
|
|
30
|
-
>>> varying(n=50, intercept_sd=0.3, slope_sds={"time": 0.1}, corr=0.2)
|
|
31
|
-
Varying(n=50, intercept_sd=0.3, slope_sds={'time': 0.1}, corr=0.2, ...)
|
|
25
|
+
```python
|
|
26
|
+
# Random intercept only: (1|subject)
|
|
27
|
+
varying(n=50, sd=0.3)
|
|
32
28
|
|
|
33
|
-
|
|
29
|
+
# Random intercept + slope: (1 + time|subject)
|
|
30
|
+
varying(n=50, intercept_sd=0.3, slope_sds={"time": 0.1}, corr=0.2)
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
# Nested effects: (1|school/class) - 3 classes per school
|
|
33
|
+
varying(n=10, sd=0.4) # schools
|
|
34
|
+
varying(n_per=3, sd=0.2) # classes nested in schools
|
|
35
|
+
```
|
|
39
36
|
"""
|
|
40
37
|
|
|
41
38
|
n: int | None = field(default=None)
|
|
@@ -44,7 +41,7 @@ class Varying:
|
|
|
44
41
|
# For random slopes
|
|
45
42
|
intercept_sd: float | None = field(default=None)
|
|
46
43
|
slope_sds: dict[str, float] = field(factory=dict)
|
|
47
|
-
corr: float
|
|
44
|
+
corr: float = field(default=0.0)
|
|
48
45
|
|
|
49
46
|
# For nested effects
|
|
50
47
|
n_per: int | None = field(default=None)
|
|
@@ -72,15 +69,15 @@ class Varying:
|
|
|
72
69
|
raise ValueError(msg)
|
|
73
70
|
|
|
74
71
|
# Validate correlation
|
|
75
|
-
if isinstance(self.corr,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
if isinstance(self.corr, dict):
|
|
73
|
+
msg = (
|
|
74
|
+
"Dict-form corr is not yet supported. "
|
|
75
|
+
"Use a single scalar value for uniform correlation across all pairs."
|
|
76
|
+
)
|
|
77
|
+
raise NotImplementedError(msg)
|
|
78
|
+
if not -1 <= self.corr <= 1:
|
|
79
|
+
msg = f"corr must be in [-1, 1], got {self.corr}"
|
|
80
|
+
raise ValueError(msg)
|
|
84
81
|
|
|
85
82
|
@property
|
|
86
83
|
def effective_intercept_sd(self) -> float:
|
|
@@ -160,15 +157,8 @@ class Varying:
|
|
|
160
157
|
self.slope_sds[t] for t in sorted(self.slope_sds)
|
|
161
158
|
]
|
|
162
159
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
corr_matrix = np.full((n_terms, n_terms), self.corr)
|
|
166
|
-
np.fill_diagonal(corr_matrix, 1.0)
|
|
167
|
-
else:
|
|
168
|
-
# Build from dict - default to 0 for unspecified pairs
|
|
169
|
-
corr_matrix = np.eye(n_terms)
|
|
170
|
-
# Note: dict-based correlation not fully implemented for simplicity
|
|
171
|
-
# Users should use single corr value for now
|
|
160
|
+
corr_matrix = np.full((n_terms, n_terms), self.corr)
|
|
161
|
+
np.fill_diagonal(corr_matrix, 1.0)
|
|
172
162
|
|
|
173
163
|
# Convert correlation to covariance: Cov = D @ Corr @ D
|
|
174
164
|
D = np.diag(sds)
|
|
@@ -183,7 +173,7 @@ def varying(
|
|
|
183
173
|
sd: float = 1.0,
|
|
184
174
|
intercept_sd: float | None = None,
|
|
185
175
|
slope_sds: dict[str, float] | None = None,
|
|
186
|
-
corr: float
|
|
176
|
+
corr: float = 0.0,
|
|
187
177
|
n_per: int | None = None,
|
|
188
178
|
) -> Varying:
|
|
189
179
|
"""Create a varying (random effect) distribution specification.
|
|
@@ -197,37 +187,28 @@ def varying(
|
|
|
197
187
|
intercept_sd (float | None): Alias for sd (for clarity with random slopes).
|
|
198
188
|
slope_sds (dict[str, float] | None): Dict mapping term names to their
|
|
199
189
|
random slope SDs.
|
|
200
|
-
corr (float
|
|
201
|
-
|
|
190
|
+
corr (float): Correlation between intercept and slope(s). Applied
|
|
191
|
+
uniformly across all term pairs. Default 0.0.
|
|
202
192
|
n_per (int | None): For nested effects, number of sub-groups per parent group.
|
|
203
193
|
|
|
204
194
|
Returns:
|
|
205
195
|
Varying specification for use in model.simulate().
|
|
206
196
|
|
|
207
197
|
Examples:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
>>> from bossanova.distributions import varying
|
|
211
|
-
>>> v = varying(n=50, sd=0.3)
|
|
212
|
-
>>> v.n
|
|
213
|
-
50
|
|
214
|
-
>>> v.sd
|
|
215
|
-
0.3
|
|
216
|
-
|
|
217
|
-
Random intercept + slope: (1 + time|subject)::
|
|
198
|
+
```python
|
|
199
|
+
from bossanova.distributions import varying
|
|
218
200
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
True
|
|
222
|
-
>>> v.n_terms
|
|
223
|
-
2
|
|
201
|
+
# Random intercept only: (1|subject)
|
|
202
|
+
v = varying(n=50, sd=0.3) # v.n == 50, v.sd == 0.3
|
|
224
203
|
|
|
225
|
-
|
|
204
|
+
# Random intercept + slope: (1 + time|subject)
|
|
205
|
+
v = varying(n=50, intercept_sd=0.3, slope_sds={"time": 0.1}, corr=0.2)
|
|
206
|
+
# v.has_random_slopes == True, v.n_terms == 2
|
|
226
207
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
208
|
+
# Nested effects: (1|school/class) - 3 classes per school
|
|
209
|
+
schools = varying(n=10, sd=0.4)
|
|
210
|
+
classes = varying(n_per=3, sd=0.2) # classes.is_nested == True
|
|
211
|
+
```
|
|
231
212
|
"""
|
|
232
213
|
return Varying(
|
|
233
214
|
n=n,
|
|
@@ -82,7 +82,7 @@ def compare(
|
|
|
82
82
|
- "aic": AIC comparison with delta-AIC and Akaike weights
|
|
83
83
|
- "bic": BIC comparison with delta-BIC and Schwarz weights
|
|
84
84
|
sort: If True, sort models by complexity before comparing.
|
|
85
|
-
This ensures proper nesting order.
|
|
85
|
+
This ensures proper nesting order. Default True.
|
|
86
86
|
refit: If True and models are lmer/glmer with REML estimation,
|
|
87
87
|
automatically refit with ML for valid LRT comparison.
|
|
88
88
|
Original models are not mutated. Default False.
|
|
@@ -157,34 +157,38 @@ def compare(
|
|
|
157
157
|
|
|
158
158
|
Examples:
|
|
159
159
|
```python
|
|
160
|
+
from bossanova import model, compare, load_dataset
|
|
161
|
+
mtcars = load_dataset("mtcars")
|
|
162
|
+
sleepstudy = load_dataset("sleepstudy")
|
|
163
|
+
|
|
160
164
|
# Models are auto-fitted if needed (calls .fit() with defaults)
|
|
161
|
-
compare(
|
|
165
|
+
compare(model("mpg ~ 1", mtcars), model("mpg ~ wt", mtcars))
|
|
162
166
|
|
|
163
167
|
# Equivalent to explicit .fit() calls:
|
|
164
|
-
compare(
|
|
168
|
+
compare(model("mpg ~ 1", mtcars).fit(), model("mpg ~ wt", mtcars).fit())
|
|
165
169
|
|
|
166
170
|
# Model objects are fitted in-place, so they're usable after compare()
|
|
167
|
-
compact =
|
|
168
|
-
full =
|
|
171
|
+
compact = model("mpg ~ 1", mtcars)
|
|
172
|
+
full = model("mpg ~ wt", mtcars)
|
|
169
173
|
compare(compact, full)
|
|
170
|
-
full.
|
|
174
|
+
full.params # Works - model was auto-fitted
|
|
171
175
|
|
|
172
176
|
# glm example (deviance test)
|
|
173
177
|
compare(
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
model("am ~ 1", mtcars, family="binomial"),
|
|
179
|
+
model("am ~ wt", mtcars, family="binomial"),
|
|
176
180
|
)
|
|
177
181
|
|
|
178
182
|
# lmer example (likelihood ratio test)
|
|
179
183
|
compare(
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
model("Reaction ~ Days + (1|Subject)", sleepstudy).fit(method="ML"),
|
|
185
|
+
model("Reaction ~ Days + (Days|Subject)", sleepstudy).fit(method="ML"),
|
|
182
186
|
)
|
|
183
187
|
|
|
184
188
|
# CV example (Nadeau-Bengio corrected)
|
|
185
189
|
compare(
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
model("mpg ~ 1", mtcars),
|
|
191
|
+
model("mpg ~ wt", mtcars),
|
|
188
192
|
method="cv", cv=5, seed=42,
|
|
189
193
|
)
|
|
190
194
|
```
|
|
@@ -218,7 +222,6 @@ def compare(
|
|
|
218
222
|
|
|
219
223
|
See Also:
|
|
220
224
|
- lrt: Likelihood ratio test for mixed models
|
|
221
|
-
- anova: Type I/II/III ANOVA tables
|
|
222
225
|
"""
|
|
223
226
|
# Validate and auto-fit models (pass method and refit for REML check)
|
|
224
227
|
models = validate_models(models, method=method, refit=refit)
|
|
@@ -66,7 +66,7 @@ def compute_cv_fold_score(
|
|
|
66
66
|
|
|
67
67
|
# Predict on test data
|
|
68
68
|
new_model.predict(newdata=test_data)
|
|
69
|
-
y_pred = new_model.predictions[
|
|
69
|
+
y_pred = new_model.predictions[Col.FITTED].to_numpy()
|
|
70
70
|
|
|
71
71
|
# Get actual test values
|
|
72
72
|
y_true = test_data[model._spec.response_var].to_numpy()
|
|
@@ -104,13 +104,13 @@ def compare_cv(
|
|
|
104
104
|
Returns:
|
|
105
105
|
DataFrame with columns:
|
|
106
106
|
- model: Formula string
|
|
107
|
+
- PRE: Proportional reduction in error
|
|
108
|
+
- t_stat: Nadeau-Bengio corrected t-statistic
|
|
107
109
|
- cv_score: Mean CV score (error)
|
|
108
110
|
- cv_se: Standard error of CV score
|
|
109
111
|
- diff: Difference from reference model (first model)
|
|
110
112
|
- diff_se: Corrected standard error of difference
|
|
111
|
-
- t_stat: Nadeau-Bengio corrected t-statistic
|
|
112
113
|
- p_value: Two-sided p-value
|
|
113
|
-
- PRE: Proportional reduction in error
|
|
114
114
|
|
|
115
115
|
Notes:
|
|
116
116
|
The Nadeau-Bengio correction accounts for the fact that k-fold CV
|
|
@@ -51,14 +51,23 @@ def compare_deviance(models: list[Any], test: str = "chisq") -> pl.DataFrame:
|
|
|
51
51
|
test: Test type: "chisq" for chi-squared test (default), "f" for F-test.
|
|
52
52
|
|
|
53
53
|
Returns:
|
|
54
|
-
DataFrame with columns:
|
|
54
|
+
DataFrame with columns (test="chisq"):
|
|
55
55
|
- model: Formula string
|
|
56
|
-
-
|
|
56
|
+
- chi2: Chi-squared statistic (deviance difference)
|
|
57
|
+
- dev_diff: Deviance difference (reduction)
|
|
57
58
|
- deviance: Residual deviance
|
|
58
59
|
- df: Degrees of freedom for this comparison
|
|
60
|
+
- df_resid: Residual degrees of freedom
|
|
61
|
+
- p_value: p-value from chi-squared distribution
|
|
62
|
+
|
|
63
|
+
DataFrame with columns (test="f"):
|
|
64
|
+
- model: Formula string
|
|
65
|
+
- F: F-statistic
|
|
59
66
|
- dev_diff: Deviance difference (reduction)
|
|
60
|
-
-
|
|
61
|
-
-
|
|
67
|
+
- deviance: Residual deviance
|
|
68
|
+
- df: Degrees of freedom for this comparison
|
|
69
|
+
- df_resid: Residual degrees of freedom
|
|
70
|
+
- p_value: p-value from F distribution
|
|
62
71
|
|
|
63
72
|
Notes:
|
|
64
73
|
The deviance difference follows a chi-squared distribution with df degrees
|
|
@@ -6,11 +6,7 @@ used across all comparison strategies (F-test, deviance, LRT, CV).
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from typing import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
pass
|
|
9
|
+
from typing import Any
|
|
14
10
|
|
|
15
11
|
__all__ = [
|
|
16
12
|
"check_nested",
|
|
@@ -154,13 +150,12 @@ def sort_models_by_complexity(models: tuple[Any, ...]) -> list[Any]:
|
|
|
154
150
|
def resolve_method(model: Any) -> str:
|
|
155
151
|
"""Infer the appropriate comparison method for a model type.
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
corresponding comparison method.
|
|
153
|
+
Reads ``model._model_type`` to determine the model type and maps it
|
|
154
|
+
to the corresponding comparison method.
|
|
160
155
|
|
|
161
156
|
Args:
|
|
162
|
-
model: A fitted model object with a ``
|
|
163
|
-
``
|
|
157
|
+
model: A fitted model object with a ``_model_type`` attribute
|
|
158
|
+
(one of ``"lm"``, ``"glm"``, ``"lmer"``, ``"glmer"``).
|
|
164
159
|
|
|
165
160
|
Returns:
|
|
166
161
|
Comparison method: ``"f"`` for lm, ``"lrt"`` for lmer/glmer,
|