bossanova 0.1.0.dev15__tar.gz → 0.1.0.dev17__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 (243) hide show
  1. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/PKG-INFO +1 -1
  2. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/builders/state.py +9 -3
  3. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/structs/state.py +14 -9
  4. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/design/reference.py +93 -0
  5. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/bracket_contrasts.py +4 -4
  6. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/compute.py +81 -43
  7. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/emm.py +197 -38
  8. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/inference.py +2 -2
  9. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/slopes.py +87 -48
  10. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/core_protocols.py +27 -7
  11. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/fit_builders.py +3 -1
  12. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/helpers.py +6 -2
  13. bossanova-0.1.0.dev17/bossanova/model/__init__.py +6 -0
  14. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/model/core.py +26 -18
  15. bossanova-0.1.0.dev17/bossanova/model/result.py +109 -0
  16. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/pyproject.toml +4 -2
  17. bossanova-0.1.0.dev15/bossanova/model/__init__.py +0 -5
  18. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/.gitignore +0 -0
  19. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/LICENSE +0 -0
  20. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/README.md +0 -0
  21. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/__init__.py +0 -0
  22. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/README.md +0 -0
  23. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/__init__.py +0 -0
  24. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/advertising.csv +0 -0
  25. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/cake.csv +0 -0
  26. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/chickweight.csv +0 -0
  27. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/credit.csv +0 -0
  28. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/gammas.csv +0 -0
  29. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/mtcars.csv +0 -0
  30. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/penguins.csv +0 -0
  31. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/poker.csv +0 -0
  32. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/sleep.csv +0 -0
  33. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/titanic.csv +0 -0
  34. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/titanic_test.csv +0 -0
  35. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/data/titanic_train.csv +0 -0
  36. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/distributions/__init__.py +0 -0
  37. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/distributions/continuous.py +0 -0
  38. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/distributions/discrete.py +0 -0
  39. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/distributions/varying.py +0 -0
  40. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/expressions.py +0 -0
  41. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/__init__.py +0 -0
  42. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/__init__.py +0 -0
  43. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/builders/__init__.py +0 -0
  44. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/builders/data.py +0 -0
  45. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/builders/dataframes.py +0 -0
  46. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/builders/resamples.py +0 -0
  47. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/builders/results.py +0 -0
  48. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/builders/specs.py +0 -0
  49. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/schemas.py +0 -0
  50. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/structs/__init__.py +0 -0
  51. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/structs/data.py +0 -0
  52. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/structs/display.py +0 -0
  53. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/structs/explore.py +0 -0
  54. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/structs/formula.py +0 -0
  55. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/structs/specs.py +0 -0
  56. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/containers/validators.py +0 -0
  57. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/__init__.py +0 -0
  58. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/backend/__init__.py +0 -0
  59. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/backend/dispatch.py +0 -0
  60. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/backend/jax.py +0 -0
  61. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/backend/numpy.py +0 -0
  62. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/backend/protocol.py +0 -0
  63. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/batching.py +0 -0
  64. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/config.py +0 -0
  65. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/convergence.py +0 -0
  66. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/design/__init__.py +0 -0
  67. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/design/coding.py +0 -0
  68. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/design/names.py +0 -0
  69. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/design/z_matrix.py +0 -0
  70. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/differentiation.py +0 -0
  71. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/__init__.py +0 -0
  72. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/algebra.py +0 -0
  73. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/base.py +0 -0
  74. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/core.py +0 -0
  75. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/derived.py +0 -0
  76. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/factories.py +0 -0
  77. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/plotting.py +0 -0
  78. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/distributions/probability.py +0 -0
  79. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/__init__.py +0 -0
  80. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/binomial.py +0 -0
  81. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/create.py +0 -0
  82. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/gamma.py +0 -0
  83. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/gaussian.py +0 -0
  84. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/links.py +0 -0
  85. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/poisson.py +0 -0
  86. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/response.py +0 -0
  87. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/schema.py +0 -0
  88. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/family/tdist.py +0 -0
  89. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/__init__.py +0 -0
  90. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/contrasts.py +0 -0
  91. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/diagnostics.py +0 -0
  92. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/estimation.py +0 -0
  93. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/hypothesis.py +0 -0
  94. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/information_criteria.py +0 -0
  95. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/multiplicity.py +0 -0
  96. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/profile.py +0 -0
  97. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/sandwich.py +0 -0
  98. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/satterthwaite.py +0 -0
  99. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/wald_variance.py +0 -0
  100. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/inference/welch.py +0 -0
  101. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/linalg/__init__.py +0 -0
  102. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/linalg/qr.py +0 -0
  103. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/linalg/schur.py +0 -0
  104. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/linalg/sparse.py +0 -0
  105. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/linalg/svd.py +0 -0
  106. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/predict.py +0 -0
  107. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/rng.py +0 -0
  108. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/rounding.py +0 -0
  109. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/__init__.py +0 -0
  110. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/glm.py +0 -0
  111. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/glmer.py +0 -0
  112. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/heuristics.py +0 -0
  113. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/initialization.py +0 -0
  114. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/lambda_builder.py +0 -0
  115. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/lambda_sparse.py +0 -0
  116. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/lambda_template.py +0 -0
  117. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/lmer.py +0 -0
  118. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/optimize.py +0 -0
  119. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/pirls_sparse.py +0 -0
  120. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/solvers/quadrature.py +0 -0
  121. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/tolerances.py +0 -0
  122. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/transforms.py +0 -0
  123. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/variance.py +0 -0
  124. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/maths/weights.py +0 -0
  125. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/__init__.py +0 -0
  126. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/bundle.py +0 -0
  127. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/common/__init__.py +0 -0
  128. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/common/contrast_registry.py +0 -0
  129. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/common/data_utils.py +0 -0
  130. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/common/factors.py +0 -0
  131. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/common/formula_utils.py +0 -0
  132. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/__init__.py +0 -0
  133. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/compare.py +0 -0
  134. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/cv.py +0 -0
  135. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/deviance.py +0 -0
  136. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/f_test.py +0 -0
  137. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/helpers.py +0 -0
  138. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/lrt.py +0 -0
  139. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/lrt_compare.py +0 -0
  140. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/compare/refit.py +0 -0
  141. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/contrasts.py +0 -0
  142. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/convergence.py +0 -0
  143. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/diagnostics.py +0 -0
  144. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/fit/__init__.py +0 -0
  145. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/fit/dispatch.py +0 -0
  146. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/fit/glm.py +0 -0
  147. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/fit/glmer.py +0 -0
  148. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/fit/lmer.py +0 -0
  149. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/fit/ols.py +0 -0
  150. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/fit/rank.py +0 -0
  151. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/__init__.py +0 -0
  152. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/design.py +0 -0
  153. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/encoding.py +0 -0
  154. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/evaluate.py +0 -0
  155. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/evaluate_newdata.py +0 -0
  156. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/evaluate_transforms.py +0 -0
  157. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/helpers.py +0 -0
  158. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/parse.py +0 -0
  159. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/parser/__init__.py +0 -0
  160. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/parser/expr.py +0 -0
  161. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/parser/parser.py +0 -0
  162. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/parser/scanner.py +0 -0
  163. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/parser/token.py +0 -0
  164. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/formula/random_effects.py +0 -0
  165. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/__init__.py +0 -0
  166. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/asymptotic.py +0 -0
  167. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/bootstrap.py +0 -0
  168. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/cv.py +0 -0
  169. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/mee.py +0 -0
  170. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/params.py +0 -0
  171. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/permutation.py +0 -0
  172. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/prediction.py +0 -0
  173. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/profile.py +0 -0
  174. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/resample_bundle.py +0 -0
  175. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/satterthwaite_emm.py +0 -0
  176. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/infer/simulation.py +0 -0
  177. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/__init__.py +0 -0
  178. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/conditions.py +0 -0
  179. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/contrasts.py +0 -0
  180. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/explore.py +0 -0
  181. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/explore_parser.py +0 -0
  182. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/explore_scanner.py +0 -0
  183. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/grid.py +0 -0
  184. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/joint_tests.py +0 -0
  185. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/marginal/validation.py +0 -0
  186. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/predict.py +0 -0
  187. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/profile.py +0 -0
  188. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/rendering/__init__.py +0 -0
  189. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/rendering/latex.py +0 -0
  190. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/__init__.py +0 -0
  191. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/common.py +0 -0
  192. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/core.py +0 -0
  193. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/glm.py +0 -0
  194. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/glmer.py +0 -0
  195. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/lm.py +0 -0
  196. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/lm_bca.py +0 -0
  197. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/lm_operators.py +0 -0
  198. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/lmer.py +0 -0
  199. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/mixed.py +0 -0
  200. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/results.py +0 -0
  201. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/resample/utils.py +0 -0
  202. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/__init__.py +0 -0
  203. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/dgp/__init__.py +0 -0
  204. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/dgp/generate.py +0 -0
  205. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/dgp/glm.py +0 -0
  206. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/dgp/glmer.py +0 -0
  207. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/dgp/lm.py +0 -0
  208. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/dgp/lmer.py +0 -0
  209. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/harness.py +0 -0
  210. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/metrics.py +0 -0
  211. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/model_sim.py +0 -0
  212. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/simulation/power.py +0 -0
  213. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/transforms.py +0 -0
  214. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/operations/varying.py +0 -0
  215. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/README.md +0 -0
  216. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/__init__.py +0 -0
  217. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/cognition.py +0 -0
  218. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/compare.py +0 -0
  219. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/core.py +0 -0
  220. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/core_data.py +0 -0
  221. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/core_sizing.py +0 -0
  222. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/core_viz.py +0 -0
  223. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/dag.py +0 -0
  224. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/design.py +0 -0
  225. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/fit.py +0 -0
  226. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/fit_layers.py +0 -0
  227. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/lattice.py +0 -0
  228. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/layout.py +0 -0
  229. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/mem.py +0 -0
  230. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/params.py +0 -0
  231. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/predict.py +0 -0
  232. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/profile.py +0 -0
  233. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/ranef.py +0 -0
  234. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/relationships.py +0 -0
  235. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/resamples.py +0 -0
  236. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/resid.py +0 -0
  237. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/internal/viz/vif.py +0 -0
  238. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/model/guards.py +0 -0
  239. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/model/summary.py +0 -0
  240. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/bossanova/py.typed +0 -0
  241. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/tests/bossanova_benchmarks/bootstrap/data/README.md +0 -0
  242. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/tests/bossanova_benchmarks/insteval/data/README.md +0 -0
  243. {bossanova-0.1.0.dev15 → bossanova-0.1.0.dev17}/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.dev15
3
+ Version: 0.1.0.dev17
4
4
  Summary: Bridging statistical cultures with some jazz
5
5
  Author: Eshin Jolly
6
6
  License-Expression: MIT
@@ -196,7 +196,8 @@ def build_mee_state(
196
196
  focal_var: str,
197
197
  mee_type: str,
198
198
  *,
199
- units: str = "link",
199
+ how: str = "mem",
200
+ effect_scale: str = "link",
200
201
  L_matrix: np.ndarray | None = None,
201
202
  contrast_method: str | None = None,
202
203
  n_contrast_levels: int | None = None,
@@ -218,7 +219,11 @@ def build_mee_state(
218
219
  explore_formula: The explore formula string.
219
220
  focal_var: The primary variable being explored.
220
221
  mee_type: Type of effect ("means", "slopes", "contrasts").
221
- units: Scale of the estimates: "link" (linear predictor) or "data" (response).
222
+ how: Averaging method: ``"mem"`` (Marginal Estimated Mean,
223
+ balanced reference grid) or ``"ame"`` (Average Marginal
224
+ Effect, g-computation over observed data).
225
+ effect_scale: Scale of estimates: ``"link"`` (linear predictor)
226
+ or ``"response"`` (inverse-link / data scale).
222
227
  L_matrix: Design matrix for delta method inference (optional).
223
228
  Shape (n_estimates, n_coef). For EMMs this is X_ref.
224
229
  contrast_method: Original contrast type for multiplicity adjustment
@@ -257,7 +262,8 @@ def build_mee_state(
257
262
  explore_formula=explore_formula,
258
263
  focal_var=focal_var,
259
264
  type=mee_type,
260
- units=units,
265
+ how=how,
266
+ effect_scale=effect_scale,
261
267
  L_matrix=L_matrix,
262
268
  contrast_method=contrast_method,
263
269
  n_contrast_levels=n_contrast_levels,
@@ -219,18 +219,21 @@ 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
+ how: Averaging method: ``"mem"`` (Marginal Estimated Mean, balanced
223
+ reference grid, emmeans-style) or ``"ame"`` (Average Marginal
224
+ Effect, g-computation over observed data).
225
+ effect_scale: Scale of estimates: ``"link"`` (linear predictor) or
226
+ ``"response"`` (inverse-link / data scale).
224
227
  L_matrix: Design matrix for delta method inference. Shape (n_estimates, n_coef).
225
228
  For EMMs, this is X_ref; for slopes, a coefficient selector row.
226
229
  contrast_method: Original contrast type for multiplicity adjustment
227
230
  ("pairwise", "sequential", "poly", "treatment", "sum",
228
231
  "helmert", or None).
229
232
  n_contrast_levels: Number of EMM levels before contrasting (family size for Tukey).
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").
233
+ link: Link function name for response-scale back-transformation (set when
234
+ effect_scale="response" and how="mem").
235
+ L_matrix_link: Link-scale L_matrix for CI back-transformation (set when
236
+ effect_scale="response" and how="mem").
234
237
  inference_method: Inference method used (``"asymp"``, ``"boot"``,
235
238
  ``"perm"``, or ``None``). Controls which columns appear in
236
239
  the ``.effects`` DataFrame.
@@ -257,8 +260,10 @@ class MeeState:
257
260
  explore_formula: str = field(validator=validators.instance_of(str))
258
261
  focal_var: str = field(validator=validators.instance_of(str))
259
262
  type: str = field(validator=validators.in_(("means", "slopes", "contrasts")))
260
- units: str = field(default="link", validator=validators.in_(("link", "data")))
261
- weights: str = field(default="equal", validator=validators.in_(("equal", "observed")))
263
+ how: str = field(default="mem", validator=validators.in_(("mem", "ame")))
264
+ effect_scale: str = field(
265
+ default="link", validator=validators.in_(("link", "response"))
266
+ )
262
267
 
263
268
  # Design matrix for delta method inference (optional)
264
269
  # Shape: (n_estimates, n_coef). For EMMs, this is X_ref; for slopes, a selector row.
@@ -285,7 +290,7 @@ class MeeState:
285
290
  )
286
291
  n_contrast_levels: int | None = field(default=None)
287
292
 
288
- # Response-scale back-transformation (set when units="data" for GLM/GLMER)
293
+ # Response-scale back-transformation (set when effect_scale="response" for GLM/GLMER)
289
294
  link: str | None = field(default=None)
290
295
  L_matrix_link: np.ndarray | None = field(
291
296
  default=None, repr=False, validator=is_optional_ndarray
@@ -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
  # ---------------------------------------------------------------------------
@@ -304,7 +304,7 @@ def compute_compound_bracket_contrasts(
304
304
  *,
305
305
  data: pl.DataFrame,
306
306
  spec: object | None = None,
307
- units: str = "link",
307
+ effect_scale: str = "link",
308
308
  resolved: object | None = None,
309
309
  ) -> MeeState:
310
310
  """Compute bracket contrasts for a compound focal variable.
@@ -324,7 +324,7 @@ def compute_compound_bracket_contrasts(
324
324
  contrast_expr: ContrastExpr from the parser.
325
325
  data: Model data DataFrame.
326
326
  spec: ModelSpec with link/family info.
327
- units: Scale of estimates.
327
+ effect_scale: Scale of estimates: ``"link"`` or ``"response"``.
328
328
  resolved: ResolvedConditions for additional conditioning.
329
329
 
330
330
  Returns:
@@ -396,7 +396,7 @@ def compute_compound_bracket_contrasts(
396
396
  # Handle response-scale transformation
397
397
  L_matrix: np.ndarray = X_ref
398
398
  link_name: str | None = None
399
- if units == "data" and spec is not None:
399
+ if effect_scale == "response" and spec is not None:
400
400
  _link = getattr(spec, "link", "identity")
401
401
  if _link != "identity":
402
402
  from bossanova.internal.maths.family.links import (
@@ -421,7 +421,7 @@ def compute_compound_bracket_contrasts(
421
421
  focal_var=focal_var,
422
422
  mee_type="means",
423
423
  L_matrix=L_matrix,
424
- units=units,
424
+ effect_scale=effect_scale,
425
425
  link=link_name,
426
426
  )
427
427
 
@@ -2,7 +2,8 @@
2
2
 
3
3
  Routes parsed explore formulas to the appropriate computation: EMMs for
4
4
  categorical focal variables, slopes for continuous, or contrasts.
5
- Supports ``units=`` (link/data scale) and ``varying=`` (marginal/conditional).
5
+ Supports ``effect_scale=`` (link/response scale), ``how=`` (mem/ame),
6
+ and ``varying=`` (marginal/conditional).
6
7
  """
7
8
 
8
9
  from __future__ import annotations
@@ -70,7 +71,7 @@ def dispatch_marginal_computation(
70
71
  Validates the focal variable, determines whether it is categorical or
71
72
  continuous, and dispatches to the correct computation function.
72
73
 
73
- Supports ``units=`` and ``varying=`` kwargs for scale transforms and
74
+ Supports ``effect_scale=`` and ``varying=`` kwargs for scale transforms and
74
75
  conditional (group-specific) effects in mixed models. When RHS conditions
75
76
  contain bracket contrasts (e.g., ``Drug[A - B] ~ Dose[High - Low]``),
76
77
  applies them as a post-processing step after the main computation.
@@ -80,11 +81,14 @@ def dispatch_marginal_computation(
80
81
  bundle: DataBundle with model data and metadata.
81
82
  fit: FitState with fitted coefficients.
82
83
  data: The model's data DataFrame (for variable lookup and type detection).
83
- spec: ModelSpec with link/family info (needed for units="data").
84
+ spec: ModelSpec with link/family info (needed for effect_scale="response").
84
85
  formula_spec: FormulaSpec with learned encoding (needed for finite-diff slopes).
85
86
  varying_offsets: VaryingState with BLUPs (needed for conditional effects).
86
87
  **kwargs: Additional computation options. Recognized keys:
87
- - ``units``: ``"link"`` (default) or ``"data"`` (response scale).
88
+ - ``how``: ``"auto"`` (default), ``"mem"`` (emmeans-style), or
89
+ ``"ame"`` (g-computation / average marginal effect).
90
+ - ``effect_scale``: ``"link"`` (default) or ``"response"``
91
+ (inverse-link / data scale).
88
92
  - ``varying``: ``"exclude"`` (default) or ``"include"`` (conditional).
89
93
  - ``by``: Grouping variable for faceted effects.
90
94
  - ``inverse_transforms``: ``True`` (default) to auto-resolve raw
@@ -160,7 +164,7 @@ def _dispatch_marginal_core(
160
164
  compute_compound_bracket_contrasts,
161
165
  )
162
166
 
163
- units = str(kwargs.pop("units", "link"))
167
+ effect_scale = str(kwargs.pop("effect_scale", "link"))
164
168
  resolved = _resolve_all_conditions(parsed, bundle, data, kwargs, None, True)
165
169
  return compute_compound_bracket_contrasts(
166
170
  bundle,
@@ -169,7 +173,7 @@ def _dispatch_marginal_core(
169
173
  parsed.contrast_expr,
170
174
  data=data,
171
175
  spec=spec,
172
- units=units,
176
+ effect_scale=effect_scale,
173
177
  resolved=resolved,
174
178
  )
175
179
 
@@ -180,15 +184,31 @@ def _dispatch_marginal_core(
180
184
  # Validate focal variable is not the response
181
185
  validate_focal_var(bundle, focal_var)
182
186
 
183
- # Extract units= and varying= from kwargs
184
- units = str(kwargs.pop("units", "link"))
185
- if units not in ("link", "data"):
186
- raise ValueError(f"units must be 'link' or 'data', got {units!r}")
187
+ # Extract effect_scale= and varying= from kwargs
188
+ effect_scale = str(kwargs.pop("effect_scale", "link"))
189
+ if effect_scale not in ("link", "response"):
190
+ raise ValueError(
191
+ f"effect_scale must be 'link' or 'response', got {effect_scale!r}"
192
+ )
187
193
 
188
194
  varying = str(kwargs.pop("varying", "exclude"))
189
195
  if varying not in ("exclude", "include"):
190
196
  raise ValueError(f"varying must be 'exclude' or 'include', got {varying!r}")
191
197
 
198
+ # Extract how= kwarg with smart default
199
+ how_raw = kwargs.pop("how", "auto")
200
+ how_str = str(how_raw)
201
+ if how_str not in ("auto", "mem", "ame"):
202
+ raise ValueError(f"how must be 'auto', 'mem', or 'ame', got {how_str!r}")
203
+ if how_str == "auto":
204
+ # Smart default: "ame" for GLMs (non-identity link), "mem" otherwise
205
+ if spec is not None and spec.link != "identity":
206
+ how = "ame"
207
+ else:
208
+ how = "mem"
209
+ else:
210
+ how = how_str
211
+
192
212
  # Extract inverse_transforms= kwarg (default True)
193
213
  inverse_transforms = bool(kwargs.pop("inverse_transforms", True))
194
214
 
@@ -252,7 +272,8 @@ def _dispatch_marginal_core(
252
272
  parsed.contrast_expr,
253
273
  data=data,
254
274
  spec=spec,
255
- units=units,
275
+ effect_scale=effect_scale,
276
+ how=how,
256
277
  resolved=resolved,
257
278
  )
258
279
 
@@ -266,7 +287,8 @@ def _dispatch_marginal_core(
266
287
  parsed.contrast_degree,
267
288
  data,
268
289
  spec=spec,
269
- units=units,
290
+ effect_scale=effect_scale,
291
+ how=how,
270
292
  resolved=resolved,
271
293
  focal_at_values=parsed.focal_at_values,
272
294
  contrast_ref=parsed.contrast_ref,
@@ -289,7 +311,8 @@ def _dispatch_marginal_core(
289
311
  at_overrides=at_overrides,
290
312
  set_categoricals=resolved.set_categoricals or None,
291
313
  spec=spec,
292
- units=units,
314
+ effect_scale=effect_scale,
315
+ how=how,
293
316
  )
294
317
  # Continuous at-values (e.g. "Days@[0, 3, 6, 9]"): EMMs at
295
318
  # specific values. Forward-transform the at-values through the
@@ -318,7 +341,7 @@ def _dispatch_marginal_core(
318
341
  focal_var,
319
342
  resolved=resolved,
320
343
  spec=spec,
321
- units=units,
344
+ effect_scale=effect_scale,
322
345
  )
323
346
  if is_conditional and grouping_var is not None and varying_offsets is not None:
324
347
  return compute_conditional_emm(
@@ -329,7 +352,7 @@ def _dispatch_marginal_core(
329
352
  spec=spec,
330
353
  varying_offsets=varying_offsets,
331
354
  grouping_var=grouping_var,
332
- units=units,
355
+ effect_scale=effect_scale,
333
356
  at_overrides=at_overrides,
334
357
  set_categoricals=resolved.set_categoricals or None,
335
358
  )
@@ -340,7 +363,8 @@ def _dispatch_marginal_core(
340
363
  at_overrides=at_overrides,
341
364
  set_categoricals=resolved.set_categoricals or None,
342
365
  spec=spec,
343
- units=units,
366
+ effect_scale=effect_scale,
367
+ how=how,
344
368
  )
345
369
 
346
370
  # Continuous focal variable → slopes
@@ -353,7 +377,7 @@ def _dispatch_marginal_core(
353
377
  spec=spec,
354
378
  varying_offsets=varying_offsets,
355
379
  grouping_var=grouping_var,
356
- units=units,
380
+ effect_scale=effect_scale,
357
381
  )
358
382
 
359
383
  # Crossed / conditioned slopes: conditions on RHS
@@ -378,7 +402,7 @@ def _dispatch_marginal_core(
378
402
  data=data,
379
403
  spec=spec,
380
404
  formula_spec=fspec,
381
- units=units,
405
+ effect_scale=effect_scale,
382
406
  )
383
407
 
384
408
  return _compute_marginal_slope(
@@ -389,7 +413,8 @@ def _dispatch_marginal_core(
389
413
  data=data,
390
414
  spec=spec,
391
415
  formula_spec=fspec,
392
- units=units,
416
+ effect_scale=effect_scale,
417
+ how=how,
393
418
  )
394
419
 
395
420
 
@@ -548,7 +573,8 @@ def _compute_emm_categorical(
548
573
  at_overrides: dict[str, float] | None = None,
549
574
  set_categoricals: dict[str, str] | None = None,
550
575
  spec: ModelSpec | None = None,
551
- units: str = "link",
576
+ effect_scale: str = "link",
577
+ how: str = "mem",
552
578
  ) -> MeeState:
553
579
  """Compute EMMs for a categorical focal variable.
554
580
 
@@ -561,8 +587,9 @@ def _compute_emm_categorical(
561
587
  at_overrides: Optional covariate overrides for conditioning.
562
588
  set_categoricals: Optional dict pinning non-focal categoricals to
563
589
  specific levels (e.g. ``{"Ethnicity": "Asian"}``).
564
- spec: ModelSpec with link info (for units="data").
565
- units: Scale of estimates.
590
+ spec: ModelSpec with link info (for effect_scale="response").
591
+ effect_scale: Scale of estimates: ``"link"`` or ``"response"``.
592
+ how: Averaging method: ``"mem"`` or ``"ame"``.
566
593
 
567
594
  Returns:
568
595
  MeeState with grid of levels and their estimated means.
@@ -576,7 +603,8 @@ def _compute_emm_categorical(
576
603
  at_overrides=at_overrides,
577
604
  set_categoricals=set_categoricals,
578
605
  spec=spec,
579
- units=units,
606
+ effect_scale=effect_scale,
607
+ how=how,
580
608
  )
581
609
 
582
610
 
@@ -587,7 +615,7 @@ def _compute_emm_crossed(
587
615
  *,
588
616
  resolved: ResolvedConditions,
589
617
  spec: ModelSpec | None = None,
590
- units: str = "link",
618
+ effect_scale: str = "link",
591
619
  ) -> MeeState:
592
620
  """Compute crossed EMMs over focal levels x condition grid.
593
621
 
@@ -599,8 +627,8 @@ def _compute_emm_crossed(
599
627
  fit: FitState with fitted coefficients.
600
628
  focal_var: Name of the categorical focal variable.
601
629
  resolved: ResolvedConditions with grid and scalar conditions.
602
- spec: ModelSpec with link info (for units="data").
603
- units: Scale of estimates.
630
+ spec: ModelSpec with link info (for effect_scale="response").
631
+ effect_scale: Scale of estimates: ``"link"`` or ``"response"``.
604
632
 
605
633
  Returns:
606
634
  MeeState with ``n_focal x n_grid`` rows and condition columns.
@@ -681,7 +709,7 @@ def _compute_emm_crossed(
681
709
  estimates, L_matrix = estimates_link, X_ref
682
710
  link_name: str | None = None
683
711
  L_matrix_link: np.ndarray | None = None
684
- if units == "data" and spec is not None and spec.link != "identity":
712
+ if effect_scale == "response" and spec is not None and spec.link != "identity":
685
713
  from bossanova.internal.maths.family.links import (
686
714
  apply_link_inverse,
687
715
  apply_link_inverse_deriv,
@@ -705,7 +733,8 @@ def _compute_emm_crossed(
705
733
  explore_formula=f"{focal_var}{cond_str}",
706
734
  focal_var=focal_var,
707
735
  mee_type="means",
708
- units=units,
736
+ effect_scale=effect_scale,
737
+ how="mem",
709
738
  L_matrix=L_matrix,
710
739
  link=link_name,
711
740
  L_matrix_link=L_matrix_link,
@@ -778,7 +807,8 @@ def _compute_marginal_slope(
778
807
  data: pl.DataFrame,
779
808
  spec: ModelSpec | None = None,
780
809
  formula_spec: FormulaSpec | None = None,
781
- units: str = "link",
810
+ effect_scale: str = "link",
811
+ how: str = "mem",
782
812
  ) -> MeeState:
783
813
  """Compute marginal slope for a continuous focal variable.
784
814
 
@@ -796,7 +826,8 @@ def _compute_marginal_slope(
796
826
  data: Raw model data DataFrame.
797
827
  spec: ModelSpec with family/link info.
798
828
  formula_spec: FormulaSpec with learned encoding (for finite-diff).
799
- units: Scale of estimates: ``"link"`` or ``"data"``.
829
+ effect_scale: Scale of estimates: ``"link"`` or ``"response"``.
830
+ how: Averaging method: ``"mem"`` or ``"ame"``.
800
831
 
801
832
  Returns:
802
833
  MeeState with marginal slope estimate.
@@ -824,7 +855,8 @@ def _compute_marginal_slope(
824
855
  spec=spec,
825
856
  formula_spec=formula_spec,
826
857
  data=data,
827
- units=units,
858
+ effect_scale=effect_scale,
859
+ how=how,
828
860
  )
829
861
 
830
862
  # Fast path: coefficient extraction — use resolved name
@@ -834,7 +866,7 @@ def _compute_marginal_slope(
834
866
  focal_var=coef_var,
835
867
  explore_formula=focal_var,
836
868
  spec=spec,
837
- units=units,
869
+ effect_scale=effect_scale,
838
870
  )
839
871
 
840
872
 
@@ -1020,7 +1052,8 @@ def _compute_contrasts(
1020
1052
  data: pl.DataFrame,
1021
1053
  *,
1022
1054
  spec: ModelSpec | None = None,
1023
- units: str = "link",
1055
+ effect_scale: str = "link",
1056
+ how: str = "mem",
1024
1057
  resolved: ResolvedConditions | None = None,
1025
1058
  focal_at_values: tuple[float | str, ...] | None = None,
1026
1059
  contrast_ref: str | None = None,
@@ -1030,7 +1063,7 @@ def _compute_contrasts(
1030
1063
  First computes EMMs, then applies the requested contrast matrix.
1031
1064
  When ``resolved`` contains grid conditions, computes crossed EMMs and
1032
1065
  applies grouped contrasts. When ``focal_at_values`` is provided
1033
- (e.g. from ``pairwise(cyl[4, 8])``), contrasts are computed only
1066
+ (e.g. from ``pairwise(cyl@[4, 8])``), contrasts are computed only
1034
1067
  over the requested subset of levels.
1035
1068
 
1036
1069
  Args:
@@ -1041,8 +1074,9 @@ def _compute_contrasts(
1041
1074
  sum, helmert).
1042
1075
  contrast_degree: Degree for polynomial contrasts (None = max).
1043
1076
  data: Model data for level extraction.
1044
- spec: ModelSpec with link info (for units="data").
1045
- units: Scale of estimates.
1077
+ spec: ModelSpec with link info (for effect_scale="response").
1078
+ effect_scale: Scale of estimates: ``"link"`` or ``"response"``.
1079
+ how: Averaging method: ``"mem"`` or ``"ame"``.
1046
1080
  resolved: Resolved conditions for conditioning.
1047
1081
  focal_at_values: Optional subset of levels from at-spec syntax
1048
1082
  (e.g. ``pairwise(cyl@[4, 8])``).
@@ -1088,7 +1122,7 @@ def _compute_contrasts(
1088
1122
  focal_var,
1089
1123
  resolved=resolved,
1090
1124
  spec=spec,
1091
- units=units,
1125
+ effect_scale=effect_scale,
1092
1126
  )
1093
1127
  if contrast_ref is not None:
1094
1128
  ref_idx = _resolve_ref_idx(emm_state.grid)
@@ -1112,7 +1146,8 @@ def _compute_contrasts(
1112
1146
  at_overrides=at_overrides,
1113
1147
  set_categoricals=set_cats,
1114
1148
  spec=spec,
1115
- units=units,
1149
+ effect_scale=effect_scale,
1150
+ how=how,
1116
1151
  )
1117
1152
  if contrast_ref is not None:
1118
1153
  ref_idx = _resolve_ref_idx(emm_state.grid)
@@ -1129,7 +1164,8 @@ def _compute_bracket_contrasts(
1129
1164
  *,
1130
1165
  data: pl.DataFrame,
1131
1166
  spec: ModelSpec | None = None,
1132
- units: str = "link",
1167
+ effect_scale: str = "link",
1168
+ how: str = "mem",
1133
1169
  resolved: ResolvedConditions | None = None,
1134
1170
  ) -> MeeState:
1135
1171
  """Compute bracket contrast expression for a categorical focal variable.
@@ -1144,8 +1180,9 @@ def _compute_bracket_contrasts(
1144
1180
  focal_var: Name of the categorical variable.
1145
1181
  contrast_expr: ContrastExpr AST from the parser.
1146
1182
  data: Model data DataFrame.
1147
- spec: ModelSpec with link info (for units="data").
1148
- units: Scale of estimates.
1183
+ spec: ModelSpec with link info (for effect_scale="response").
1184
+ effect_scale: Scale of estimates: ``"link"`` or ``"response"``.
1185
+ how: Averaging method: ``"mem"`` or ``"ame"``.
1149
1186
  resolved: Resolved conditions for conditioning.
1150
1187
 
1151
1188
  Returns:
@@ -1163,7 +1200,7 @@ def _compute_bracket_contrasts(
1163
1200
  focal_var,
1164
1201
  resolved=resolved,
1165
1202
  spec=spec,
1166
- units=units,
1203
+ effect_scale=effect_scale,
1167
1204
  )
1168
1205
  return apply_bracket_contrasts_grouped(emm_state, contrast_expr)
1169
1206
 
@@ -1178,6 +1215,7 @@ def _compute_bracket_contrasts(
1178
1215
  at_overrides=at_overrides,
1179
1216
  set_categoricals=set_cats,
1180
1217
  spec=spec,
1181
- units=units,
1218
+ effect_scale=effect_scale,
1219
+ how=how,
1182
1220
  )
1183
1221
  return apply_bracket_contrasts(emm_state, contrast_expr)