bossanova 0.1.0.dev14__tar.gz → 0.1.0.dev16__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.
Files changed (242) hide show
  1. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/PKG-INFO +1 -1
  2. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/__init__.py +4 -0
  3. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/builders/state.py +6 -1
  4. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/structs/explore.py +88 -6
  5. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/structs/state.py +22 -4
  6. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/__init__.py +3 -0
  7. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/config.py +56 -0
  8. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/design/reference.py +93 -0
  9. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/contrasts.py +107 -7
  10. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/multiplicity.py +3 -2
  11. bossanova-0.1.0.dev16/bossanova/internal/maths/rounding.py +89 -0
  12. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/common/__init__.py +14 -0
  13. bossanova-0.1.0.dev16/bossanova/internal/operations/common/contrast_registry.py +61 -0
  14. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/compare.py +11 -7
  15. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/__init__.py +12 -0
  16. bossanova-0.1.0.dev16/bossanova/internal/operations/marginal/bracket_contrasts.py +565 -0
  17. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/compute.py +216 -5
  18. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/conditions.py +43 -1
  19. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/contrasts.py +146 -16
  20. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/emm.py +190 -31
  21. bossanova-0.1.0.dev16/bossanova/internal/operations/marginal/explore.py +173 -0
  22. bossanova-0.1.0.dev16/bossanova/internal/operations/marginal/explore_parser.py +754 -0
  23. bossanova-0.1.0.dev16/bossanova/internal/operations/marginal/explore_scanner.py +81 -0
  24. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/inference.py +11 -8
  25. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/slopes.py +23 -11
  26. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/mem.py +1 -4
  27. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/model/core.py +149 -29
  28. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/model/summary.py +30 -30
  29. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/pyproject.toml +4 -2
  30. bossanova-0.1.0.dev14/bossanova/internal/operations/marginal/explore.py +0 -494
  31. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/.gitignore +0 -0
  32. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/LICENSE +0 -0
  33. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/README.md +0 -0
  34. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/README.md +0 -0
  35. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/__init__.py +0 -0
  36. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/advertising.csv +0 -0
  37. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/cake.csv +0 -0
  38. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/chickweight.csv +0 -0
  39. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/credit.csv +0 -0
  40. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/gammas.csv +0 -0
  41. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/mtcars.csv +0 -0
  42. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/penguins.csv +0 -0
  43. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/poker.csv +0 -0
  44. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/sleep.csv +0 -0
  45. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/titanic.csv +0 -0
  46. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/titanic_test.csv +0 -0
  47. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/data/titanic_train.csv +0 -0
  48. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/distributions/__init__.py +0 -0
  49. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/distributions/continuous.py +0 -0
  50. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/distributions/discrete.py +0 -0
  51. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/distributions/varying.py +0 -0
  52. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/expressions.py +0 -0
  53. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/__init__.py +0 -0
  54. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/__init__.py +0 -0
  55. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/builders/__init__.py +0 -0
  56. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/builders/data.py +0 -0
  57. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/builders/dataframes.py +0 -0
  58. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/builders/resamples.py +0 -0
  59. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/builders/results.py +0 -0
  60. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/builders/specs.py +0 -0
  61. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/schemas.py +0 -0
  62. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/structs/__init__.py +0 -0
  63. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/structs/data.py +0 -0
  64. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/structs/display.py +0 -0
  65. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/structs/formula.py +0 -0
  66. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/structs/specs.py +0 -0
  67. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/containers/validators.py +0 -0
  68. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/backend/__init__.py +0 -0
  69. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/backend/dispatch.py +0 -0
  70. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/backend/jax.py +0 -0
  71. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/backend/numpy.py +0 -0
  72. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/backend/protocol.py +0 -0
  73. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/batching.py +0 -0
  74. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/convergence.py +0 -0
  75. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/design/__init__.py +0 -0
  76. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/design/coding.py +0 -0
  77. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/design/names.py +0 -0
  78. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/design/z_matrix.py +0 -0
  79. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/differentiation.py +0 -0
  80. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/__init__.py +0 -0
  81. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/algebra.py +0 -0
  82. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/base.py +0 -0
  83. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/core.py +0 -0
  84. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/derived.py +0 -0
  85. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/factories.py +0 -0
  86. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/plotting.py +0 -0
  87. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/distributions/probability.py +0 -0
  88. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/__init__.py +0 -0
  89. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/binomial.py +0 -0
  90. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/create.py +0 -0
  91. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/gamma.py +0 -0
  92. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/gaussian.py +0 -0
  93. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/links.py +0 -0
  94. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/poisson.py +0 -0
  95. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/response.py +0 -0
  96. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/schema.py +0 -0
  97. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/family/tdist.py +0 -0
  98. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/__init__.py +0 -0
  99. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/diagnostics.py +0 -0
  100. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/estimation.py +0 -0
  101. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/hypothesis.py +0 -0
  102. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/information_criteria.py +0 -0
  103. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/profile.py +0 -0
  104. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/sandwich.py +0 -0
  105. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/satterthwaite.py +0 -0
  106. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/wald_variance.py +0 -0
  107. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/inference/welch.py +0 -0
  108. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/linalg/__init__.py +0 -0
  109. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/linalg/qr.py +0 -0
  110. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/linalg/schur.py +0 -0
  111. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/linalg/sparse.py +0 -0
  112. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/linalg/svd.py +0 -0
  113. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/predict.py +0 -0
  114. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/rng.py +0 -0
  115. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/__init__.py +0 -0
  116. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/glm.py +0 -0
  117. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/glmer.py +0 -0
  118. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/heuristics.py +0 -0
  119. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/initialization.py +0 -0
  120. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/lambda_builder.py +0 -0
  121. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/lambda_sparse.py +0 -0
  122. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/lambda_template.py +0 -0
  123. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/lmer.py +0 -0
  124. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/optimize.py +0 -0
  125. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/pirls_sparse.py +0 -0
  126. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/solvers/quadrature.py +0 -0
  127. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/tolerances.py +0 -0
  128. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/transforms.py +0 -0
  129. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/variance.py +0 -0
  130. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/maths/weights.py +0 -0
  131. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/__init__.py +0 -0
  132. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/bundle.py +0 -0
  133. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/common/data_utils.py +0 -0
  134. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/common/factors.py +0 -0
  135. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/common/formula_utils.py +0 -0
  136. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/__init__.py +0 -0
  137. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/cv.py +0 -0
  138. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/deviance.py +0 -0
  139. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/f_test.py +0 -0
  140. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/helpers.py +0 -0
  141. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/lrt.py +0 -0
  142. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/lrt_compare.py +0 -0
  143. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/compare/refit.py +0 -0
  144. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/contrasts.py +0 -0
  145. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/convergence.py +0 -0
  146. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/diagnostics.py +0 -0
  147. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/fit/__init__.py +0 -0
  148. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/fit/dispatch.py +0 -0
  149. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/fit/glm.py +0 -0
  150. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/fit/glmer.py +0 -0
  151. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/fit/lmer.py +0 -0
  152. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/fit/ols.py +0 -0
  153. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/fit/rank.py +0 -0
  154. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/__init__.py +0 -0
  155. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/design.py +0 -0
  156. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/encoding.py +0 -0
  157. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/evaluate.py +0 -0
  158. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/evaluate_newdata.py +0 -0
  159. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/evaluate_transforms.py +0 -0
  160. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/helpers.py +0 -0
  161. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/parse.py +0 -0
  162. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/parser/__init__.py +0 -0
  163. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/parser/expr.py +0 -0
  164. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/parser/parser.py +0 -0
  165. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/parser/scanner.py +0 -0
  166. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/parser/token.py +0 -0
  167. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/formula/random_effects.py +0 -0
  168. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/__init__.py +0 -0
  169. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/asymptotic.py +0 -0
  170. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/bootstrap.py +0 -0
  171. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/cv.py +0 -0
  172. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/mee.py +0 -0
  173. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/params.py +0 -0
  174. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/permutation.py +0 -0
  175. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/prediction.py +0 -0
  176. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/profile.py +0 -0
  177. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/resample_bundle.py +0 -0
  178. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/satterthwaite_emm.py +0 -0
  179. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/infer/simulation.py +0 -0
  180. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/grid.py +0 -0
  181. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/joint_tests.py +0 -0
  182. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/marginal/validation.py +0 -0
  183. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/predict.py +0 -0
  184. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/profile.py +0 -0
  185. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/rendering/__init__.py +0 -0
  186. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/rendering/latex.py +0 -0
  187. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/__init__.py +0 -0
  188. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/common.py +0 -0
  189. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/core.py +0 -0
  190. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/glm.py +0 -0
  191. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/glmer.py +0 -0
  192. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/lm.py +0 -0
  193. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/lm_bca.py +0 -0
  194. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/lm_operators.py +0 -0
  195. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/lmer.py +0 -0
  196. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/mixed.py +0 -0
  197. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/results.py +0 -0
  198. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/resample/utils.py +0 -0
  199. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/__init__.py +0 -0
  200. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/dgp/__init__.py +0 -0
  201. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/dgp/generate.py +0 -0
  202. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/dgp/glm.py +0 -0
  203. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/dgp/glmer.py +0 -0
  204. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/dgp/lm.py +0 -0
  205. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/dgp/lmer.py +0 -0
  206. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/harness.py +0 -0
  207. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/metrics.py +0 -0
  208. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/model_sim.py +0 -0
  209. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/simulation/power.py +0 -0
  210. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/transforms.py +0 -0
  211. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/operations/varying.py +0 -0
  212. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/README.md +0 -0
  213. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/__init__.py +0 -0
  214. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/cognition.py +0 -0
  215. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/compare.py +0 -0
  216. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/core.py +0 -0
  217. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/core_data.py +0 -0
  218. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/core_protocols.py +0 -0
  219. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/core_sizing.py +0 -0
  220. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/core_viz.py +0 -0
  221. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/dag.py +0 -0
  222. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/design.py +0 -0
  223. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/fit.py +0 -0
  224. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/fit_builders.py +0 -0
  225. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/fit_layers.py +0 -0
  226. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/helpers.py +0 -0
  227. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/lattice.py +0 -0
  228. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/layout.py +0 -0
  229. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/params.py +0 -0
  230. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/predict.py +0 -0
  231. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/profile.py +0 -0
  232. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/ranef.py +0 -0
  233. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/relationships.py +0 -0
  234. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/resamples.py +0 -0
  235. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/resid.py +0 -0
  236. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/internal/viz/vif.py +0 -0
  237. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/model/__init__.py +0 -0
  238. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/model/guards.py +0 -0
  239. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/bossanova/py.typed +0 -0
  240. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/tests/bossanova_benchmarks/bootstrap/data/README.md +0 -0
  241. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/tests/bossanova_benchmarks/insteval/data/README.md +0 -0
  242. {bossanova-0.1.0.dev14 → bossanova-0.1.0.dev16}/tests/bossanova_tests/hypothesis/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bossanova
3
- Version: 0.1.0.dev14
3
+ Version: 0.1.0.dev16
4
4
  Summary: Bridging statistical cultures with some jazz
5
5
  Author: Eshin Jolly
6
6
  License-Expression: MIT
@@ -15,7 +15,9 @@ except (ImportError, AttributeError):
15
15
 
16
16
  # Configuration
17
17
  from bossanova.internal.maths.config import ( # noqa: E402
18
+ get_display_digits,
18
19
  get_singular_tolerance,
20
+ set_display_digits,
19
21
  set_singular_tolerance,
20
22
  )
21
23
 
@@ -42,11 +44,13 @@ __all__ = [
42
44
  "compare",
43
45
  "expressions",
44
46
  "get_backend",
47
+ "get_display_digits",
45
48
  "get_singular_tolerance",
46
49
  "load_dataset",
47
50
  "lrt",
48
51
  "model",
49
52
  "set_backend",
53
+ "set_display_digits",
50
54
  "set_singular_tolerance",
51
55
  "show_datasets",
52
56
  "viz",
@@ -197,6 +197,7 @@ def build_mee_state(
197
197
  mee_type: str,
198
198
  *,
199
199
  units: str = "link",
200
+ weights: str = "equal",
200
201
  L_matrix: np.ndarray | None = None,
201
202
  contrast_method: str | None = None,
202
203
  n_contrast_levels: int | None = None,
@@ -219,10 +220,13 @@ def build_mee_state(
219
220
  focal_var: The primary variable being explored.
220
221
  mee_type: Type of effect ("means", "slopes", "contrasts").
221
222
  units: Scale of the estimates: "link" (linear predictor) or "data" (response).
223
+ weights: Averaging philosophy: ``"equal"`` (balanced reference grid,
224
+ emmeans-style) or ``"observed"`` (g-computation over observed data).
222
225
  L_matrix: Design matrix for delta method inference (optional).
223
226
  Shape (n_estimates, n_coef). For EMMs this is X_ref.
224
227
  contrast_method: Original contrast type for multiplicity adjustment
225
- ("pairwise", "revpairwise", "sequential", "poly", or None).
228
+ ("pairwise", "sequential", "poly", "treatment", "sum",
229
+ "helmert", or None).
226
230
  n_contrast_levels: Number of EMM levels before contrasting (family size).
227
231
  link: Link function name for response-scale CI back-transformation.
228
232
  L_matrix_link: Link-scale L_matrix for CI back-transformation.
@@ -257,6 +261,7 @@ def build_mee_state(
257
261
  focal_var=focal_var,
258
262
  type=mee_type,
259
263
  units=units,
264
+ weights=weights,
260
265
  L_matrix=L_matrix,
261
266
  contrast_method=contrast_method,
262
267
  n_contrast_levels=n_contrast_levels,
@@ -4,29 +4,93 @@ from attrs import field, frozen, validators
4
4
 
5
5
  __all__ = [
6
6
  "Condition",
7
+ "ContrastExpr",
8
+ "ContrastItem",
9
+ "ContrastOperand",
7
10
  "ExploreFormula",
8
11
  "ExploreFormulaError",
9
12
  ]
10
13
 
11
14
 
15
+ # =====================================================================
16
+ # Bracket contrast expression containers
17
+ # =====================================================================
18
+
19
+
20
+ @frozen
21
+ class ContrastOperand:
22
+ """One side of a bracket contrast item.
23
+
24
+ Represents a single operand in a contrast like ``A - B`` or
25
+ ``(A + B) - C``.
26
+
27
+ Attributes:
28
+ levels: Level names for this operand. Empty tuple for wildcard.
29
+ Multiple levels indicate a grouped mean (e.g., ``(A + B)``
30
+ produces ``levels=("A", "B")``).
31
+ is_wildcard: True for the ``*`` operator, which expands to all
32
+ levels not mentioned on the other side.
33
+ """
34
+
35
+ levels: tuple[str, ...] = field(factory=tuple, converter=tuple)
36
+ is_wildcard: bool = field(default=False, validator=validators.instance_of(bool))
37
+
38
+
39
+ @frozen
40
+ class ContrastItem:
41
+ """A single contrast: left operand minus right operand.
42
+
43
+ Represents one ``left - right`` comparison in a bracket contrast
44
+ expression like ``Drug[Active - Placebo]``.
45
+
46
+ Attributes:
47
+ left: Left operand (positive side).
48
+ right: Right operand (negative side).
49
+ """
50
+
51
+ left: ContrastOperand = field(validator=validators.instance_of(ContrastOperand))
52
+ right: ContrastOperand = field(validator=validators.instance_of(ContrastOperand))
53
+
54
+
55
+ @frozen
56
+ class ContrastExpr:
57
+ """Bracket contrast expression: ``Drug[A - B, C - D]``.
58
+
59
+ Represents a complete bracket contrast specification parsed from
60
+ explore formula syntax.
61
+
62
+ Attributes:
63
+ var: Variable name (e.g., ``"Drug"``).
64
+ items: Tuple of contrast items to compute.
65
+ """
66
+
67
+ var: str = field(validator=validators.instance_of(str))
68
+ items: tuple[ContrastItem, ...] = field(converter=tuple)
69
+
70
+
12
71
  @frozen
13
72
  class Condition:
14
73
  """A conditioning specification in explore formula.
15
74
 
16
- Represents a variable to condition on, optionally with specific values
17
- or a method for generating values.
75
+ Represents a variable to condition on, optionally with specific values,
76
+ a method for generating values, or a bracket contrast expression.
18
77
 
19
78
  Attributes:
20
79
  var: Variable name to condition on.
21
80
  at_values: Specific values to evaluate at (e.g., (50.0,) or ("A", "B")).
22
81
  at_range: Number of evenly-spaced values across the variable's range.
23
82
  at_quantile: Number of quantile values to use.
83
+ contrast_expr: Bracket contrast expression on this condition variable
84
+ (e.g., from ``Dose[High - Low]`` on the RHS). When set, the
85
+ variable is treated as a grid categorical during condition
86
+ resolution, and the contrast is applied as a post-processing step.
24
87
  """
25
88
 
26
89
  var: str = field(validator=validators.instance_of(str))
27
90
  at_values: tuple | None = field(default=None)
28
91
  at_range: int | None = field(default=None)
29
92
  at_quantile: int | None = field(default=None)
93
+ contrast_expr: ContrastExpr | None = field(default=None)
30
94
 
31
95
 
32
96
  @frozen
@@ -38,10 +102,16 @@ class ExploreFormula:
38
102
 
39
103
  Attributes:
40
104
  focal_var: The variable to compute marginal effects for.
41
- contrast_type: Type of contrast (pairwise, revpairwise, sequential, poly)
42
- or None for simple EMMs.
105
+ contrast_type: Type of contrast (pairwise, sequential, poly, treatment,
106
+ sum, helmert, custom) or None for simple EMMs. Set to ``"custom"``
107
+ for bracket contrast expressions.
43
108
  contrast_degree: Degree parameter for polynomial contrasts (default None
44
109
  means use n_levels - 1, i.e., maximum degree).
110
+ contrast_ref: Reference level for treatment/dummy contrasts
111
+ (e.g., ``"Placebo"`` from ``treatment(Drug, ref=Placebo)``).
112
+ contrast_expr: Bracket contrast expression AST (e.g., from
113
+ ``Drug[Active - Placebo]`` syntax). None for named contrast
114
+ functions or simple EMMs.
45
115
  conditions: Tuple of Condition objects specifying conditioning variables.
46
116
  focal_at_values: Specific values to evaluate the focal variable at
47
117
  (e.g., from ``Days@[0, 3, 6, 9]`` syntax). None means use all levels.
@@ -54,6 +124,8 @@ class ExploreFormula:
54
124
  focal_var: str = field(validator=validators.instance_of(str))
55
125
  contrast_type: str | None = field(default=None)
56
126
  contrast_degree: int | None = field(default=None)
127
+ contrast_ref: str | None = field(default=None)
128
+ contrast_expr: ContrastExpr | None = field(default=None)
57
129
  conditions: tuple[Condition, ...] = field(factory=tuple, converter=tuple)
58
130
  focal_at_values: tuple[float | str, ...] | None = field(default=None)
59
131
  focal_at_range: int | None = field(default=None)
@@ -82,14 +154,24 @@ class ExploreFormula:
82
154
 
83
155
  @property
84
156
  def has_contrast(self) -> bool:
85
- """Return True if a contrast function is specified."""
86
- return self.contrast_type is not None
157
+ """Return True if any contrast is specified (named function or bracket expr)."""
158
+ return self.contrast_type is not None or self.contrast_expr is not None
159
+
160
+ @property
161
+ def has_contrast_expr(self) -> bool:
162
+ """Return True if a bracket contrast expression is specified."""
163
+ return self.contrast_expr is not None
87
164
 
88
165
  @property
89
166
  def has_conditions(self) -> bool:
90
167
  """Return True if conditioning variables are specified."""
91
168
  return len(self.conditions) > 0
92
169
 
170
+ @property
171
+ def has_rhs_contrasts(self) -> bool:
172
+ """Return True if any RHS condition has a bracket contrast expression."""
173
+ return any(c.contrast_expr is not None for c in self.conditions)
174
+
93
175
 
94
176
  class ExploreFormulaError(ValueError):
95
177
  """Error in explore formula syntax.
@@ -219,13 +219,18 @@ class MeeState:
219
219
  explore_formula: The explore formula string that generated this result.
220
220
  focal_var: The primary variable being explored.
221
221
  type: Type of marginal effect ("means", "slopes", "contrasts").
222
+ weights: Averaging philosophy used: ``"equal"`` (balanced reference grid,
223
+ emmeans-style) or ``"observed"`` (g-computation over observed data).
222
224
  L_matrix: Design matrix for delta method inference. Shape (n_estimates, n_coef).
223
225
  For EMMs, this is X_ref; for slopes, a coefficient selector row.
224
226
  contrast_method: Original contrast type for multiplicity adjustment
225
- ("pairwise", "revpairwise", "sequential", "poly", or None).
227
+ ("pairwise", "sequential", "poly", "treatment", "sum",
228
+ "helmert", or None).
226
229
  n_contrast_levels: Number of EMM levels before contrasting (family size for Tukey).
227
- link: Link function name for response-scale back-transformation (set when units="data").
228
- L_matrix_link: Link-scale L_matrix for CI back-transformation (set when units="data").
230
+ link: Link function name for response-scale back-transformation (set when units="data"
231
+ and weights="equal").
232
+ L_matrix_link: Link-scale L_matrix for CI back-transformation (set when units="data"
233
+ and weights="equal").
229
234
  inference_method: Inference method used (``"asymp"``, ``"boot"``,
230
235
  ``"perm"``, or ``None``). Controls which columns appear in
231
236
  the ``.effects`` DataFrame.
@@ -253,6 +258,9 @@ class MeeState:
253
258
  focal_var: str = field(validator=validators.instance_of(str))
254
259
  type: str = field(validator=validators.in_(("means", "slopes", "contrasts")))
255
260
  units: str = field(default="link", validator=validators.in_(("link", "data")))
261
+ weights: str = field(
262
+ default="equal", validator=validators.in_(("equal", "observed"))
263
+ )
256
264
 
257
265
  # Design matrix for delta method inference (optional)
258
266
  # Shape: (n_estimates, n_coef). For EMMs, this is X_ref; for slopes, a selector row.
@@ -264,7 +272,17 @@ class MeeState:
264
272
  contrast_method: str | None = field(
265
273
  default=None,
266
274
  validator=validators.optional(
267
- validators.in_(("pairwise", "revpairwise", "sequential", "poly"))
275
+ validators.in_(
276
+ (
277
+ "pairwise",
278
+ "sequential",
279
+ "poly",
280
+ "treatment",
281
+ "sum",
282
+ "helmert",
283
+ "custom",
284
+ )
285
+ )
268
286
  ),
269
287
  )
270
288
  n_contrast_levels: int | None = field(default=None)
@@ -28,6 +28,7 @@ from bossanova.internal.maths.batching import (
28
28
  get_available_memory_gb,
29
29
  )
30
30
  from bossanova.internal.maths.config import is_singular
31
+ from bossanova.internal.maths.rounding import round_float_columns, round_sigfigs
31
32
  from bossanova.internal.maths.inference import (
32
33
  Chi2TestResult,
33
34
  FTestResult,
@@ -240,6 +241,8 @@ __all__ = [
240
241
  "poly_numeric",
241
242
  "poly_numeric_labels",
242
243
  "qr_solve",
244
+ "round_float_columns",
245
+ "round_sigfigs",
243
246
  "sequential_contrast",
244
247
  "sequential_labels",
245
248
  "sum_contrast",
@@ -12,15 +12,71 @@ if TYPE_CHECKING:
12
12
  import numpy as np
13
13
 
14
14
  __all__ = [
15
+ "get_display_digits",
15
16
  "get_singular_tolerance",
16
17
  "is_singular",
18
+ "set_display_digits",
17
19
  "set_singular_tolerance",
18
20
  ]
19
21
 
22
+ # Default significant figures for DataFrame display output.
23
+ # Matches R's summary() convention of 4 significant digits.
24
+ _DISPLAY_DIGITS: int = 4
25
+
20
26
  # Default singular tolerance matches lme4's default (utilities.R:924-928)
21
27
  _SINGULAR_TOLERANCE: float = 1e-4
22
28
 
23
29
 
30
+ def get_display_digits() -> int:
31
+ """Get the number of significant figures for DataFrame display output.
32
+
33
+ Controls how many significant figures are shown in user-facing
34
+ DataFrames returned by model properties (``.params``, ``.effects``,
35
+ ``.diagnostics``, etc.) and ``compare()``. Matches R's ``signif()``
36
+ convention.
37
+
38
+ Returns:
39
+ Current display digits setting (default 4).
40
+
41
+ Examples:
42
+ ```python
43
+ from bossanova import get_display_digits
44
+ get_display_digits()
45
+ # 4
46
+ ```
47
+ """
48
+ return _DISPLAY_DIGITS
49
+
50
+
51
+ def set_display_digits(digits: int) -> None:
52
+ """Set the number of significant figures for DataFrame display output.
53
+
54
+ Controls how many significant figures are shown in user-facing
55
+ DataFrames returned by model properties (``.params``, ``.effects``,
56
+ ``.diagnostics``, etc.) and ``compare()``. Matches R's ``signif()``
57
+ convention.
58
+
59
+ The ``summary()`` method has its own ``digits`` parameter and is
60
+ unaffected by this setting.
61
+
62
+ Args:
63
+ digits: Number of significant figures. Must be >= 1.
64
+
65
+ Raises:
66
+ ValueError: If digits is less than 1.
67
+
68
+ Examples:
69
+ ```python
70
+ from bossanova import set_display_digits
71
+ set_display_digits(6) # Show 6 significant figures
72
+ ```
73
+ """
74
+ global _DISPLAY_DIGITS
75
+ if digits < 1:
76
+ raise ValueError(f"digits must be >= 1, got {digits}")
77
+ _DISPLAY_DIGITS = digits
78
+
79
+
24
80
  def get_singular_tolerance() -> float:
25
81
  """Get the current singular tolerance for mixed models.
26
82
 
@@ -9,6 +9,7 @@ from bossanova.internal.maths.design.names import (
9
9
 
10
10
  __all__ = [
11
11
  "build_continuous_reference_matrix",
12
+ "build_counterfactual_design_matrices",
12
13
  "build_reference_design_matrix",
13
14
  "build_reference_row",
14
15
  ]
@@ -248,6 +249,98 @@ def build_continuous_reference_matrix(
248
249
  return X_ref
249
250
 
250
251
 
252
+ def build_counterfactual_design_matrices(
253
+ X: np.ndarray,
254
+ X_names: tuple[str, ...] | list[str],
255
+ focal_var: str,
256
+ levels: list[str],
257
+ ) -> list[np.ndarray]:
258
+ """Build counterfactual design matrices for g-computation.
259
+
260
+ For each focal level, creates a modified copy of the full design matrix
261
+ ``X`` where the focal variable's indicator columns are set to match that
262
+ level and interaction columns involving the focal variable are recomputed
263
+ from component values. Non-focal columns are left unchanged, preserving
264
+ the observed covariate distribution.
265
+
266
+ This is the core building block for ``weights="observed"``
267
+ (g-computation / counterfactual prediction). Each returned matrix answers:
268
+ "What would the design matrix look like if every observation were assigned
269
+ to this focal level?"
270
+
271
+ Args:
272
+ X: Original design matrix, shape ``(N, p)``.
273
+ X_names: Column names from the design matrix, in order.
274
+ focal_var: Name of the categorical focal variable.
275
+ levels: List of levels to compute counterfactuals for.
276
+
277
+ Returns:
278
+ List of counterfactual design matrices (one per level), each shape
279
+ ``(N, p)``. Order matches ``levels``.
280
+
281
+ Examples:
282
+ For a model ``y ~ treatment + x + treatment:x`` with
283
+ ``X_names = ("Intercept", "x", "treatment[B]", "x:treatment[B]")``::
284
+
285
+ mats = build_counterfactual_design_matrices(X, X_names, "treatment", ["ref", "B"])
286
+ # mats[0]: treatment set to ref for all rows (treatment[B]=0, x:treatment[B]=0)
287
+ # mats[1]: treatment set to B for all rows (treatment[B]=1, x:treatment[B]=x_i)
288
+ """
289
+ X_names_list = list(X_names)
290
+ N, p = X.shape
291
+
292
+ # Pre-parse column metadata once
293
+ col_infos = [parse_design_column_name(name) for name in X_names_list]
294
+
295
+ # Identify focal dummy columns and their levels
296
+ focal_col_indices: list[int] = []
297
+ focal_col_levels: list[str | None] = []
298
+ for j, info in enumerate(col_infos):
299
+ if info.column_type == "categorical" and info.base_term == focal_var:
300
+ focal_col_indices.append(j)
301
+ focal_col_levels.append(info.level)
302
+
303
+ # Identify interaction columns involving the focal variable
304
+ interaction_cols: list[int] = []
305
+ for j, info in enumerate(col_infos):
306
+ if info.is_interaction:
307
+ parts = X_names_list[j].split(":")
308
+ if any(
309
+ parse_design_column_name(part).base_term == focal_var for part in parts
310
+ ):
311
+ interaction_cols.append(j)
312
+
313
+ result: list[np.ndarray] = []
314
+ for level in levels:
315
+ X_cf = X.copy()
316
+
317
+ # Set focal dummy columns
318
+ for j, lvl in zip(focal_col_indices, focal_col_levels):
319
+ X_cf[:, j] = 1.0 if lvl == level else 0.0
320
+
321
+ # Recompute interaction columns involving the focal variable
322
+ for j in interaction_cols:
323
+ parts = X_names_list[j].split(":")
324
+ col_product = np.ones(N, dtype=np.float64)
325
+ for part in parts:
326
+ pinfo = parse_design_column_name(part)
327
+ if pinfo.base_term == focal_var:
328
+ # Focal component: use counterfactual indicator
329
+ if pinfo.column_type == "categorical":
330
+ col_product *= 1.0 if pinfo.level == level else 0.0
331
+ elif part in X_names_list:
332
+ # Continuous focal in interaction (rare but possible)
333
+ col_product *= X[:, X_names_list.index(part)]
334
+ elif part in X_names_list:
335
+ # Non-focal component: use original observed values
336
+ col_product *= X[:, X_names_list.index(part)]
337
+ X_cf[:, j] = col_product
338
+
339
+ result.append(X_cf)
340
+
341
+ return result
342
+
343
+
251
344
  # ---------------------------------------------------------------------------
252
345
  # Internal helpers
253
346
  # ---------------------------------------------------------------------------
@@ -7,9 +7,11 @@ import numpy as np
7
7
  __all__ = [
8
8
  "build_all_pairwise_contrast",
9
9
  "build_contrast_matrix",
10
+ "build_helmert_contrast",
10
11
  "build_pairwise_contrast",
11
12
  "build_sequential_contrast",
12
13
  "build_sum_to_zero_contrast",
14
+ "build_treatment_contrast",
13
15
  "compose_contrast",
14
16
  "get_contrast_labels",
15
17
  ]
@@ -224,6 +226,98 @@ def build_sum_to_zero_contrast(n_levels: int) -> np.ndarray:
224
226
  return C
225
227
 
226
228
 
229
+ def build_treatment_contrast(n_levels: int, ref_idx: int = 0) -> np.ndarray:
230
+ """Build treatment (Dunnett-style) contrasts against a reference level.
231
+
232
+ Creates n_levels - 1 contrasts, each comparing one non-reference level
233
+ to the specified reference level.
234
+
235
+ Args:
236
+ n_levels: Number of EMM levels (factor levels).
237
+ ref_idx: Index of the reference level (0-based).
238
+
239
+ Returns:
240
+ Contrast matrix of shape (n_levels - 1, n_levels).
241
+ Row i compares non-reference level i to the reference.
242
+
243
+ Raises:
244
+ ValueError: If n_levels < 1 or ref_idx out of range.
245
+
246
+ Examples:
247
+ >>> C = build_treatment_contrast(3, ref_idx=0)
248
+ >>> C
249
+ array([[-1., 1., 0.],
250
+ [-1., 0., 1.]])
251
+
252
+ >>> C = build_treatment_contrast(3, ref_idx=2)
253
+ >>> C
254
+ array([[ 1., 0., -1.],
255
+ [ 0., 1., -1.]])
256
+ """
257
+ if n_levels < 1:
258
+ raise ValueError(f"n_levels must be >= 1, got {n_levels}")
259
+ if ref_idx < 0 or ref_idx >= n_levels:
260
+ raise ValueError(f"ref_idx must be in [0, {n_levels - 1}], got {ref_idx}")
261
+
262
+ if n_levels == 1:
263
+ return np.zeros((0, 1))
264
+
265
+ C = np.zeros((n_levels - 1, n_levels))
266
+ row = 0
267
+ for i in range(n_levels):
268
+ if i == ref_idx:
269
+ continue
270
+ C[row, ref_idx] = -1
271
+ C[row, i] = 1
272
+ row += 1
273
+
274
+ return C
275
+
276
+
277
+ def build_helmert_contrast(n_levels: int) -> np.ndarray:
278
+ """Build Helmert contrasts (each level vs mean of previous levels).
279
+
280
+ Creates n_levels - 1 contrasts where level k is compared to
281
+ the mean of levels 0, 1, ..., k-1. Useful for ordered factors
282
+ where you want to test if each successive level differs from
283
+ the average of all prior levels.
284
+
285
+ Args:
286
+ n_levels: Number of EMM levels (factor levels).
287
+
288
+ Returns:
289
+ Contrast matrix of shape (n_levels - 1, n_levels).
290
+
291
+ Raises:
292
+ ValueError: If n_levels < 1.
293
+
294
+ Examples:
295
+ >>> C = build_helmert_contrast(3)
296
+ >>> C
297
+ array([[-1. , 1. , 0. ],
298
+ [-0.5, -0.5, 1. ]])
299
+
300
+ >>> C = build_helmert_contrast(4)
301
+ >>> C
302
+ array([[-1. , 1. , 0. , 0. ],
303
+ [-0.5 , -0.5 , 1. , 0. ],
304
+ [-0.33333333, -0.33333333, -0.33333333, 1. ]])
305
+ """
306
+ if n_levels < 1:
307
+ raise ValueError(f"n_levels must be >= 1, got {n_levels}")
308
+
309
+ if n_levels == 1:
310
+ return np.zeros((0, 1))
311
+
312
+ C = np.zeros((n_levels - 1, n_levels))
313
+ for k in range(1, n_levels):
314
+ # Level k vs mean of levels 0..k-1
315
+ C[k - 1, :k] = -1.0 / k
316
+ C[k - 1, k] = 1.0
317
+
318
+ return C
319
+
320
+
227
321
  def build_contrast_matrix(
228
322
  contrast_type: str | dict,
229
323
  levels: list,
@@ -237,9 +331,10 @@ def build_contrast_matrix(
237
331
  Args:
238
332
  contrast_type: Type of contrast:
239
333
  - "pairwise": All pairwise comparisons (k(k-1)/2 contrasts)
240
- - "revpairwise": Reversed pairwise comparisons
241
334
  - "trt.vs.ctrl" or "treatment": Compare each level to first level
242
335
  - "sequential": Successive differences (level[i+1] - level[i])
336
+ - "sum": Each level vs grand mean (deviation coding)
337
+ - "helmert": Each level vs mean of previous levels
243
338
  - dict: Custom contrasts with names as keys and weights as values
244
339
  levels: List of factor levels.
245
340
  normalize: If True, normalize custom contrasts to sum to 1/-1.
@@ -281,10 +376,6 @@ def build_contrast_matrix(
281
376
  # All pairwise comparisons: B-A, C-A, C-B, ...
282
377
  return build_all_pairwise_contrast(n_levels)
283
378
 
284
- elif contrast_type == "revpairwise":
285
- # Reversed pairwise: A-B, A-C, B-C, ...
286
- return -build_all_pairwise_contrast(n_levels)
287
-
288
379
  elif contrast_type in ("trt.vs.ctrl", "treatment"):
289
380
  # Each level vs first level
290
381
  return build_pairwise_contrast(n_levels)
@@ -293,14 +384,23 @@ def build_contrast_matrix(
293
384
  # Successive differences: B-A, C-B, D-C, ...
294
385
  return build_sequential_contrast(n_levels)
295
386
 
387
+ elif contrast_type == "sum":
388
+ # Each level vs grand mean (deviation coding)
389
+ return build_sum_to_zero_contrast(n_levels)
390
+
391
+ elif contrast_type == "helmert":
392
+ # Each level vs mean of previous levels
393
+ return build_helmert_contrast(n_levels)
394
+
296
395
  else:
297
396
  raise ValueError(
298
397
  f"Unknown contrast type: {contrast_type!r}\n\n"
299
398
  "Supported types:\n"
300
399
  " - 'pairwise': All pairwise comparisons\n"
301
- " - 'revpairwise': Reversed pairwise\n"
302
400
  " - 'sequential': Successive differences (level[i+1] - level[i])\n"
303
- " - 'trt.vs.ctrl': Each level vs first level\n"
401
+ " - 'trt.vs.ctrl' / 'treatment': Each level vs first level\n"
402
+ " - 'sum': Each level vs grand mean\n"
403
+ " - 'helmert': Each level vs mean of previous levels\n"
304
404
  " - dict: Custom contrasts {name: weights}"
305
405
  )
306
406
 
@@ -3,8 +3,9 @@
3
3
  Provides critical value computation for multiplicity-adjusted CIs,
4
4
  matching R's emmeans defaults:
5
5
 
6
- - Tukey HSD for pairwise/revpairwise contrasts (studentized range).
7
- - Multivariate-t (MVT) for sequential contrasts (Genz-Bretz CDF + root-finding).
6
+ - Tukey HSD for pairwise contrasts (studentized range).
7
+ - Multivariate-t (MVT) for sequential, treatment, sum, helmert contrasts
8
+ (Genz-Bretz CDF + root-finding).
8
9
  - No adjustment for polynomial contrasts.
9
10
 
10
11
  Internal exports: