bossanova 0.1.0.dev18__tar.gz → 0.1.0.dev20__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 (241) hide show
  1. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/.gitignore +2 -1
  2. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/PKG-INFO +1 -1
  3. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/__init__.py +4 -0
  4. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/__init__.py +18 -2
  5. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/expressions.py +11 -11
  6. bossanova-0.1.0.dev20/bossanova/internal/compare/__init__.py +12 -0
  7. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/structs/display.py +44 -0
  8. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/design/__init__.py +0 -6
  9. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/__init__.py +0 -23
  10. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/dispatch.py +4 -4
  11. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/glmer.py +18 -21
  12. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/__init__.py +0 -19
  13. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/__init__.py +0 -23
  14. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/dispatch.py +9 -0
  15. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/__init__.py +2 -13
  16. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/slopes.py +15 -0
  17. bossanova-0.1.0.dev20/bossanova/internal/rendering/__init__.py +22 -0
  18. bossanova-0.1.0.dev20/bossanova/internal/rendering/markdown.py +163 -0
  19. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/__init__.py +0 -12
  20. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/mem.py +4 -5
  21. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/predict.py +14 -0
  22. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/model/core.py +230 -183
  23. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/model/result.py +84 -0
  24. bossanova-0.1.0.dev20/bossanova/model/summary.py +1103 -0
  25. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/pyproject.toml +10 -2
  26. bossanova-0.1.0.dev18/bossanova/internal/compare/__init__.py +0 -27
  27. bossanova-0.1.0.dev18/bossanova/internal/rendering/__init__.py +0 -16
  28. bossanova-0.1.0.dev18/bossanova/model/summary.py +0 -467
  29. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/LICENSE +0 -0
  30. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/README.md +0 -0
  31. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/README.md +0 -0
  32. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/advertising.csv +0 -0
  33. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/cake.csv +0 -0
  34. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/chickweight.csv +0 -0
  35. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/credit.csv +0 -0
  36. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/gammas.csv +0 -0
  37. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/mtcars.csv +0 -0
  38. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/penguins.csv +0 -0
  39. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/poker.csv +0 -0
  40. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/sleep.csv +0 -0
  41. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/titanic.csv +0 -0
  42. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/titanic_test.csv +0 -0
  43. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/data/titanic_train.csv +0 -0
  44. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/distributions/__init__.py +0 -0
  45. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/distributions/continuous.py +0 -0
  46. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/distributions/discrete.py +0 -0
  47. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/distributions/varying.py +0 -0
  48. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/__init__.py +0 -0
  49. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/compare.py +0 -0
  50. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/cv.py +0 -0
  51. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/deviance.py +0 -0
  52. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/f_test.py +0 -0
  53. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/helpers.py +0 -0
  54. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/ic.py +0 -0
  55. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/lrt.py +0 -0
  56. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/lrt_compare.py +0 -0
  57. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/compare/refit.py +0 -0
  58. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/__init__.py +0 -0
  59. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/builders/__init__.py +0 -0
  60. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/builders/dataframes.py +0 -0
  61. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/builders/resamples.py +0 -0
  62. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/builders/results.py +0 -0
  63. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/builders/specs.py +0 -0
  64. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/builders/state.py +0 -0
  65. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/schemas.py +0 -0
  66. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/structs/__init__.py +0 -0
  67. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/structs/data.py +0 -0
  68. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/structs/explore.py +0 -0
  69. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/structs/formula.py +0 -0
  70. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/structs/specs.py +0 -0
  71. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/structs/state.py +0 -0
  72. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/containers/validators.py +0 -0
  73. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/design/coding.py +0 -0
  74. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/design/names.py +0 -0
  75. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/design/reference.py +0 -0
  76. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/design/z_matrix.py +0 -0
  77. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/convergence.py +0 -0
  78. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/diagnostics.py +0 -0
  79. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/glm.py +0 -0
  80. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/lmer.py +0 -0
  81. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/ols.py +0 -0
  82. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/predict.py +0 -0
  83. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/rank.py +0 -0
  84. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/fit/varying.py +0 -0
  85. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/bundle.py +0 -0
  86. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/contrast_registry.py +0 -0
  87. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/contrast_specs.py +0 -0
  88. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/design.py +0 -0
  89. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/encoding.py +0 -0
  90. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/evaluate.py +0 -0
  91. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/evaluate_contrast.py +0 -0
  92. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/evaluate_newdata.py +0 -0
  93. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/evaluate_transforms.py +0 -0
  94. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/helpers.py +0 -0
  95. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/parse.py +0 -0
  96. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/parser/__init__.py +0 -0
  97. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/parser/expr.py +0 -0
  98. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/parser/parser.py +0 -0
  99. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/parser/scanner.py +0 -0
  100. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/parser/token.py +0 -0
  101. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/formula/random_effects.py +0 -0
  102. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/asymptotic.py +0 -0
  103. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/bootstrap.py +0 -0
  104. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/cv.py +0 -0
  105. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/formula_utils.py +0 -0
  106. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/mee.py +0 -0
  107. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/params.py +0 -0
  108. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/permutation.py +0 -0
  109. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/prediction.py +0 -0
  110. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/profile.py +0 -0
  111. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/__init__.py +0 -0
  112. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/common.py +0 -0
  113. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/core.py +0 -0
  114. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/glmer.py +0 -0
  115. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/lm_operators.py +0 -0
  116. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/lmer.py +0 -0
  117. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/results.py +0 -0
  118. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample/simulate.py +0 -0
  119. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/resample_bundle.py +0 -0
  120. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/satterthwaite_emm.py +0 -0
  121. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/infer/simulation.py +0 -0
  122. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/bracket_contrasts.py +0 -0
  123. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/compute.py +0 -0
  124. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/conditions.py +0 -0
  125. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/contrasts.py +0 -0
  126. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/emm.py +0 -0
  127. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/explore.py +0 -0
  128. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/explore_parser.py +0 -0
  129. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/explore_scanner.py +0 -0
  130. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/factors.py +0 -0
  131. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/grid.py +0 -0
  132. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/inference.py +0 -0
  133. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/joint_tests.py +0 -0
  134. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/matrices.py +0 -0
  135. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/transforms.py +0 -0
  136. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/marginal/validation.py +0 -0
  137. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/__init__.py +0 -0
  138. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/backend/__init__.py +0 -0
  139. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/backend/dispatch.py +0 -0
  140. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/backend/jax.py +0 -0
  141. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/backend/numpy.py +0 -0
  142. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/backend/protocol.py +0 -0
  143. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/batching.py +0 -0
  144. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/config.py +0 -0
  145. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/convergence.py +0 -0
  146. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/differentiation.py +0 -0
  147. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/__init__.py +0 -0
  148. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/algebra.py +0 -0
  149. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/base.py +0 -0
  150. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/core.py +0 -0
  151. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/derived.py +0 -0
  152. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/factories.py +0 -0
  153. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/plotting.py +0 -0
  154. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/distributions/probability.py +0 -0
  155. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/__init__.py +0 -0
  156. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/binomial.py +0 -0
  157. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/create.py +0 -0
  158. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/gamma.py +0 -0
  159. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/gaussian.py +0 -0
  160. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/links.py +0 -0
  161. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/poisson.py +0 -0
  162. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/response.py +0 -0
  163. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/schema.py +0 -0
  164. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/family/tdist.py +0 -0
  165. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/__init__.py +0 -0
  166. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/diagnostics.py +0 -0
  167. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/estimation.py +0 -0
  168. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/hypothesis.py +0 -0
  169. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/information_criteria.py +0 -0
  170. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/multiplicity.py +0 -0
  171. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/profile.py +0 -0
  172. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/sandwich.py +0 -0
  173. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/satterthwaite.py +0 -0
  174. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/wald_variance.py +0 -0
  175. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/inference/welch.py +0 -0
  176. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/linalg/__init__.py +0 -0
  177. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/linalg/qr.py +0 -0
  178. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/linalg/schur.py +0 -0
  179. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/linalg/sparse.py +0 -0
  180. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/linalg/svd.py +0 -0
  181. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/predict.py +0 -0
  182. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/rng.py +0 -0
  183. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/rounding.py +0 -0
  184. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/__init__.py +0 -0
  185. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/glm.py +0 -0
  186. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/glmer.py +0 -0
  187. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/heuristics.py +0 -0
  188. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/initialization.py +0 -0
  189. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/lambda_builder.py +0 -0
  190. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/lambda_sparse.py +0 -0
  191. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/lambda_template.py +0 -0
  192. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/lmer.py +0 -0
  193. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/optimize.py +0 -0
  194. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/pirls_sparse.py +0 -0
  195. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/solvers/quadrature.py +0 -0
  196. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/tolerances.py +0 -0
  197. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/transforms.py +0 -0
  198. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/variance.py +0 -0
  199. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/maths/weights.py +0 -0
  200. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/rendering/latex.py +0 -0
  201. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/dgp/__init__.py +0 -0
  202. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/dgp/generate.py +0 -0
  203. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/dgp/glm.py +0 -0
  204. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/dgp/glmer.py +0 -0
  205. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/dgp/lm.py +0 -0
  206. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/dgp/lmer.py +0 -0
  207. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/harness.py +0 -0
  208. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/metrics.py +0 -0
  209. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/model_sim.py +0 -0
  210. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/simulation/power.py +0 -0
  211. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/README.md +0 -0
  212. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/__init__.py +0 -0
  213. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/cognition.py +0 -0
  214. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/compare.py +0 -0
  215. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/core.py +0 -0
  216. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/core_data.py +0 -0
  217. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/core_protocols.py +0 -0
  218. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/core_sizing.py +0 -0
  219. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/core_viz.py +0 -0
  220. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/dag.py +0 -0
  221. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/design.py +0 -0
  222. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/fit.py +0 -0
  223. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/fit_builders.py +0 -0
  224. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/fit_layers.py +0 -0
  225. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/helpers.py +0 -0
  226. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/lattice.py +0 -0
  227. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/layout.py +0 -0
  228. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/mem_forest.py +0 -0
  229. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/params.py +0 -0
  230. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/profile.py +0 -0
  231. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/ranef.py +0 -0
  232. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/relationships.py +0 -0
  233. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/resamples.py +0 -0
  234. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/resid.py +0 -0
  235. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/internal/viz/vif.py +0 -0
  236. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/model/__init__.py +0 -0
  237. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/model/guards.py +0 -0
  238. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/bossanova/py.typed +0 -0
  239. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/tests/bossanova_benchmarks/bootstrap/data/README.md +0 -0
  240. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/tests/bossanova_benchmarks/insteval/data/README.md +0 -0
  241. {bossanova-0.1.0.dev18 → bossanova-0.1.0.dev20}/tests/bossanova_tests/hypothesis/README.md +0 -0
@@ -17,7 +17,7 @@ docs/_build/
17
17
  docs/performance/
18
18
  profile_*.py
19
19
  benchmarks/
20
- !bossanova-docs/engineering/benchmarks/
20
+ !bossanova-docs/benchmarking/
21
21
  examples/
22
22
  papers/
23
23
  paper-summaries/
@@ -84,3 +84,4 @@ bossanova-docs/wip/
84
84
  research.md
85
85
  201b-ghct-05-models/
86
86
  emmeans/
87
+ walkthrough.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bossanova
3
- Version: 0.1.0.dev18
3
+ Version: 0.1.0.dev20
4
4
  Summary: Bridging statistical cultures with some jazz
5
5
  Author: Eshin Jolly
6
6
  License-Expression: MIT
@@ -36,6 +36,9 @@ from bossanova.internal.compare import compare, lrt # noqa: E402
36
36
  # Visualization
37
37
  from bossanova.internal import viz # noqa: E402
38
38
 
39
+ # Export utilities
40
+ from bossanova.internal.rendering.markdown import to_markdown # noqa: E402
41
+
39
42
  # Expressions
40
43
  from bossanova import expressions # noqa: E402
41
44
 
@@ -53,6 +56,7 @@ __all__ = [
53
56
  "set_display_digits",
54
57
  "set_singular_tolerance",
55
58
  "show_datasets",
59
+ "to_markdown",
56
60
  "viz",
57
61
  ]
58
62
 
@@ -1,4 +1,20 @@
1
- """Bundled sample datasets for regression and mixed-effects modeling."""
1
+ """Bundled sample datasets for regression and mixed-effects modeling.
2
+
3
+ | Dataset | Argument | Description |
4
+ |---------|----------|-------------|
5
+ | Advertising | `"advertising"` | TV, radio, newspaper ad spend vs sales (200 x 4) |
6
+ | Cake | `"cake"` | Baking angle by recipe and temperature (270 x 5) |
7
+ | ChickWeight | `"chickweight"` | Chick weight over time by diet (578 x 4) |
8
+ | Credit | `"credit"` | Credit card balance by income and demographics (400 x 11) |
9
+ | Gammas | `"gammas"` | Simulated BOLD signal for Gamma GLM (6000 x 4) |
10
+ | mtcars | `"mtcars"` | Motor Trend car road tests (32 x 12) |
11
+ | Penguins | `"penguins"` | Palmer Penguins body measurements (344 x 8) |
12
+ | Poker | `"poker"` | Poker hand outcomes by skill and limit (300 x 4) |
13
+ | Sleep | `"sleep"` | Reaction time vs sleep deprivation (180 x 3) |
14
+ | Titanic | `"titanic"` | Passenger survival with demographics (891 x 15) |
15
+ | Titanic Train | `"titanic_train"` | Titanic training set, Kaggle format (891 x 12) |
16
+ | Titanic Test | `"titanic_test"` | Titanic test set, Kaggle format (418 x 11) |
17
+ """
2
18
 
3
19
  from importlib.resources import files
4
20
  from typing import Literal
@@ -52,7 +68,7 @@ def show_datasets() -> pl.DataFrame:
52
68
  ```python
53
69
  from bossanova.data import show_datasets
54
70
  show_datasets()
55
- # shape: (11, 2)
71
+ # shape: (12, 2)
56
72
  # ┌───────────────┬─────────────────────────────────┐
57
73
  # │ name ┆ description │
58
74
  # │ --- ┆ --- │
@@ -4,17 +4,17 @@ This module provides stateless Polars expressions for data transformations
4
4
  commonly used in statistical modeling. These match the semantics of the
5
5
  formula-based transforms in `bossanova.internal.formula`.
6
6
 
7
- Core Transforms:
8
- center(col): x - mean(x) — subtract mean only
9
- norm(col): x / std(x) — divide by std only (normalize magnitude)
10
- zscore(col): (x - mean(x)) / std(x) — traditional z-score (1 SD)
11
- scale(col): (x - mean(x)) / (2 * std(x)) — Gelman scaling (2 SD)
12
-
13
- Additional Helpers:
14
- rank(col, method): Average-method rank transform (matches R's rank())
15
- winsorize(col, lower, upper): Percentile capping for outliers
16
- lag(col, n): Lagged (shifted backward) values
17
- lead(col, n): Lead (shifted forward) values
7
+ **Core Transforms:**
8
+ - `center(col)` — subtract mean only
9
+ - `norm(col)` — divide by std only
10
+ - `zscore(col)` — traditional z-score (1 SD)
11
+ - `scale(col)` — Gelman scaling (2 SD)
12
+
13
+ **Additional Helpers:**
14
+ - `rank(col, method)` — average-method rank transform
15
+ - `winsorize(col, lower, upper)` percentile capping
16
+ - `lag(col, n)` lagged values
17
+ - `lead(col, n)` lead values
18
18
 
19
19
  The scale() transform follows Gelman (2008) recommendation to divide by
20
20
  2 standard deviations, making continuous predictor coefficients directly
@@ -0,0 +1,12 @@
1
+ """Model comparison: information criteria, likelihood ratio tests, and composite tables.
2
+
3
+ Call chain:
4
+ bossanova.compare(m1, m2, ...) -> compare() (composite AIC/BIC/loglik table)
5
+ bossanova.lrt(m1, m2) -> lrt() (nested model likelihood ratio test)
6
+ """
7
+
8
+ from bossanova.internal.compare.compare import compare
9
+ from bossanova.internal.compare.ic import compare_aic, compare_bic
10
+ from bossanova.internal.compare.lrt import lrt
11
+
12
+ __all__ = ["compare", "compare_aic", "compare_bic", "lrt"]
@@ -101,3 +101,47 @@ class MathDisplay:
101
101
  LaTeX equation without $$ delimiters.
102
102
  """
103
103
  return self.equation
104
+
105
+ def to_markdown(
106
+ self,
107
+ path: str | None = None,
108
+ *,
109
+ include_explanations: bool = True,
110
+ ) -> str:
111
+ """Export equation as Quarto-compatible display math markdown.
112
+
113
+ Wraps the equation in ``$$...$$`` display math delimiters. When
114
+ *include_explanations* is True, appends the term explanations as
115
+ a bulleted list below the equation block.
116
+
117
+ Suitable for embedding in ``.qmd`` files via Quarto's
118
+ ``{{< include >}}`` shortcode.
119
+
120
+ Args:
121
+ path: Optional file path to write the markdown. Parent
122
+ directories are created automatically. If None, returns
123
+ the string without writing.
124
+ include_explanations: If True (default), append term
125
+ explanations below the equation.
126
+
127
+ Returns:
128
+ Markdown string with ``$$equation$$`` and optional
129
+ explanations.
130
+
131
+ Examples:
132
+ >>> m.show_math().to_markdown("equations/model_eq.md")
133
+ >>> md = m.show_math().to_markdown(include_explanations=False)
134
+ """
135
+ from bossanova.internal.rendering.markdown import (
136
+ equation_to_markdown,
137
+ write_text,
138
+ )
139
+
140
+ text = equation_to_markdown(
141
+ self.equation,
142
+ self.explanations,
143
+ include_explanations=include_explanations,
144
+ )
145
+ if path is not None:
146
+ write_text(text, path)
147
+ return text
@@ -4,12 +4,6 @@ Call chain:
4
4
  formula.build_design_matrices() -> treatment_coding() / sum_coding() / ... (categorical columns)
5
5
  marginal.build_reference_grid() -> build_reference_row() (EMM reference grids)
6
6
  formula.build_random_effects_from_spec() -> build_z_simple() / build_z_nested() / build_z_crossed()
7
-
8
- Submodules:
9
- coding: Contrast matrix builders for categorical encoding (treatment, sum, helmert, poly)
10
- names: Design matrix column name parsing and variable type detection
11
- reference: Reference design matrix (X_ref) for marginal effects
12
- z_matrix: Sparse Z matrix (random effects design matrix) construction
13
7
  """
14
8
 
15
9
  from .coding import (
@@ -2,29 +2,6 @@
2
2
 
3
3
  Call chain:
4
4
  model.fit() -> fit_model() -> resolve_solver() -> fit_ols_qr / fit_glm_irls / fit_lmer_pls / fit_glmer_pirls
5
-
6
- Container flow:
7
- Input: ModelSpec, DataBundle
8
- Output: FitState (coef, vcov, fitted, residuals, loglik, theta for mixed)
9
-
10
- Solver dispatch:
11
- gaussian + no RE -> fit_ols_qr (QR decomposition)
12
- gaussian + no RE -> fit_glm_irls (if method="ml")
13
- non-gauss + no RE -> fit_glm_irls (IRLS)
14
- gaussian + RE -> fit_lmer_pls (Penalized Least Squares)
15
- non-gauss + RE -> fit_glmer_pirls (Penalized IRLS)
16
-
17
- Submodules:
18
- dispatch: Solver selection and fit_model entry point
19
- ols: QR decomposition fitting (OLS/WLS)
20
- glm: Iteratively Reweighted Least Squares (IRLS)
21
- lmer: Penalized Least Squares (PLS) for LMM
22
- glmer: Penalized IRLS (PIRLS) for GLMM
23
- rank: Rank deficiency detection via pivoted QR
24
- diagnostics: AIC/BIC, R-squared, metadata computation
25
- convergence: Mixed model convergence checks (gradient, Hessian)
26
- varying: BLUP extraction and variance component decomposition
27
- predict: Prediction on training or new data
28
5
  """
29
6
 
30
7
  from bossanova.internal.fit.convergence import check_convergence
@@ -105,7 +105,7 @@ def dispatch_solver(
105
105
  tol: float | None = None,
106
106
  verbose: bool = False,
107
107
  nAGQ: int = 1,
108
- use_hessian: bool = True,
108
+ use_hessian: bool = False,
109
109
  ) -> FitState:
110
110
  """Dispatch to the concrete solver implementation.
111
111
 
@@ -121,7 +121,7 @@ def dispatch_solver(
121
121
  tol: Convergence tolerance (solver-specific defaults if None).
122
122
  verbose: Print optimization progress (default: False).
123
123
  nAGQ: Quadrature points for GLMER (default: 1).
124
- use_hessian: Use Hessian-based vcov for GLMER (default: True).
124
+ use_hessian: Use Hessian-based vcov for GLMER (default: False).
125
125
 
126
126
  Returns:
127
127
  FitState from the selected solver.
@@ -172,7 +172,7 @@ def fit_model(
172
172
  tol: float | None = None,
173
173
  verbose: bool = False,
174
174
  nAGQ: int = 1,
175
- use_hessian: bool = True,
175
+ use_hessian: bool = False,
176
176
  ) -> FitState:
177
177
  """Dispatch to appropriate fitter based on model specification.
178
178
 
@@ -205,7 +205,7 @@ def fit_model(
205
205
  tol: Convergence tolerance (solver-specific defaults if None).
206
206
  verbose: Print optimization progress (default: False).
207
207
  nAGQ: Quadrature points for GLMER (default: 1).
208
- use_hessian: Use Hessian-based vcov for GLMER (default: True).
208
+ use_hessian: Use Hessian-based vcov for GLMER (default: False).
209
209
 
210
210
  Returns:
211
211
  FitState containing all fitting results.
@@ -29,7 +29,7 @@ def fit_glmer_pirls(
29
29
  tol: float = 1e-7,
30
30
  verbose: bool = False,
31
31
  nAGQ: int = 1,
32
- use_hessian: bool = True,
32
+ use_hessian: bool = False,
33
33
  ) -> FitState:
34
34
  """Fit generalized linear mixed model using Penalized IRLS.
35
35
 
@@ -70,7 +70,10 @@ def fit_glmer_pirls(
70
70
  tol: PIRLS convergence tolerance (default: 1e-7).
71
71
  verbose: Print optimization progress (default: False).
72
72
  nAGQ: Quadrature points (0 or 1, default: 1).
73
- use_hessian: Use Hessian-based vcov (default: True).
73
+ use_hessian: Use Hessian-based vcov (default: False). The default
74
+ Schur complement approach matches lme4's ``vcov()`` with
75
+ ``use.hessian=FALSE`` and avoids expensive numerical
76
+ differentiation. Set to True for observed-information vcov.
74
77
 
75
78
  Returns:
76
79
  FitState containing:
@@ -91,7 +94,6 @@ def fit_glmer_pirls(
91
94
  - bossanova.internal.maths.solvers.glmer: Underlying PIRLS implementation
92
95
  """
93
96
  from bossanova.internal.maths.solvers.glmer import (
94
- compute_hessian_vcov,
95
97
  compute_irls_quantities,
96
98
  fit_glmm_pirls,
97
99
  )
@@ -177,9 +179,17 @@ def fit_glmer_pirls(
177
179
  converged = fit_result["converged"]
178
180
  n_iter = fit_result["n_func_evals"]
179
181
 
182
+ # Compute IRLS weights once (needed by both vcov paths and for XtWX_inv)
183
+ working_weights, _ = compute_irls_quantities(y, eta, family)
184
+ if weights is not None:
185
+ total_weights = weights * working_weights
186
+ else:
187
+ total_weights = working_weights
188
+
180
189
  # Compute variance-covariance matrix of beta
181
190
  if use_hessian:
182
- # Hessian-based vcov (matches lme4 default)
191
+ from bossanova.internal.maths.solvers.glmer import compute_hessian_vcov
192
+
183
193
  vcov, _ = compute_hessian_vcov(
184
194
  theta=theta,
185
195
  beta=beta,
@@ -196,15 +206,7 @@ def fit_glmer_pirls(
196
206
  eta_init=eta, # Use optimal eta as starting point
197
207
  )
198
208
  else:
199
- # Schur complement-based vcov (faster)
200
- working_weights, _ = compute_irls_quantities(y, eta, family)
201
-
202
- if weights is not None:
203
- total_weights = weights * working_weights
204
- else:
205
- total_weights = working_weights
206
-
207
- # Build Lambda for Schur complement
209
+ # Schur complement-based vcov (analytical, matches lme4 vcov(use.hessian=FALSE))
208
210
  Lambda = build_lambda_sparse(theta, n_groups_list, re_structure, metadata)
209
211
  vcov = compute_vcov_schur_sparse(X, Z, Lambda, weights=total_weights)
210
212
 
@@ -218,13 +220,8 @@ def fit_glmer_pirls(
218
220
  # Degrees of freedom
219
221
  df_resid = float(n - p)
220
222
 
221
- # Compute final IRLS weights and XtWX_inv for cluster-robust SEs
222
- final_working_weights, _ = compute_irls_quantities(y, eta, family)
223
- if weights is not None:
224
- final_irls_weights = weights * final_working_weights
225
- else:
226
- final_irls_weights = final_working_weights
227
- XtWX = X.T @ (X * final_irls_weights[:, np.newaxis])
223
+ # XtWX_inv for sandwich/cluster-robust SEs (reuse total_weights)
224
+ XtWX = X.T @ (X * total_weights[:, np.newaxis])
228
225
  XtWX_inv = np.linalg.inv(XtWX)
229
226
 
230
227
  return build_fit_state(
@@ -240,6 +237,6 @@ def fit_glmer_pirls(
240
237
  u=u,
241
238
  converged=converged,
242
239
  n_iter=n_iter,
243
- irls_weights=final_irls_weights,
240
+ irls_weights=total_weights,
244
241
  XtWX_inv=XtWX_inv,
245
242
  )
@@ -3,25 +3,6 @@
3
3
  Call chain:
4
4
  model(formula, data) -> parse_formula() -> build_bundle_from_data() -> build_design_matrices()
5
5
  model.predict(newdata=) -> evaluate_newdata() (applies learned encoding to new observations)
6
-
7
- Container flow:
8
- Input: formula string + Polars DataFrame
9
- Output: FormulaSpec (via parse_formula) + DataBundle (X, y, Z matrices via build_design_matrices)
10
-
11
- Submodules:
12
- parse: R-style formula string parsing into FormulaSpec
13
- parser/: Recursive descent parser (scanner, tokens, AST nodes)
14
- design: Design matrix construction from FormulaSpec
15
- evaluate: Term evaluation (AST nodes to design matrix columns)
16
- evaluate_transforms: Stateful, math, and polynomial transforms
17
- evaluate_contrast: Contrast function evaluation for categorical encoding
18
- evaluate_newdata: Apply learned encoding to new observations
19
- encoding: Categorical variable encoding using Polars Enum
20
- random_effects: Z matrix construction from FormulaSpec
21
- helpers: Shared AST utilities
22
- bundle: DataBundle construction from formula + DataFrame
23
- contrast_registry: Contrast function vocabulary and validation
24
- contrast_specs: User-facing contrast specification resolution
25
6
  """
26
7
 
27
8
  from bossanova.internal.formula.design import (
@@ -1,31 +1,8 @@
1
1
  """Inference operations: asymptotic, bootstrap, permutation, cross-validation, profile, and simulation.
2
2
 
3
- Provides asymptotic (Wald-based), bootstrap, permutation, cross-validation,
4
- profile likelihood, and simulation inference for parameters, marginal effects,
5
- predictions, and variance components.
6
-
7
3
  Call chain:
8
4
  model.infer() -> dispatch_infer() -> dispatch_{params,mee,prediction}_inference()
9
5
  Each dispatcher routes to the method-specific backend (asymptotic, bootstrap, etc.)
10
-
11
- Container flow:
12
- Input: ModelSpec, DataBundle, FitState (+ MeeState for effects, PredictionState for predictions)
13
- Output: InferenceState (SE, CI, p-values) augmented onto the target state
14
-
15
- Submodules:
16
- dispatch: Top-level infer routing
17
- params: Parameter inference dispatch
18
- mee: Marginal effects inference dispatch
19
- prediction: Prediction inference dispatch
20
- asymptotic: Wald-based SEs, CIs, p-values
21
- bootstrap: Parametric and nonparametric bootstrap
22
- permutation: Permutation tests for params and effects
23
- cv: K-fold and group-K-fold cross-validation
24
- profile: Profile likelihood for variance components
25
- simulation: Summary statistics across simulation replicates
26
- satterthwaite_emm: Satterthwaite df for EMM contrasts (mixed models)
27
- resample/: Resampling engine (bootstrap indices, CI methods, model-type hooks)
28
- formula_utils: Formula manipulation for term ablation (CV)
29
6
  """
30
7
 
31
8
  from bossanova.internal.infer.asymptotic import (
@@ -189,6 +189,15 @@ def dispatch_infer(
189
189
  threshold=threshold,
190
190
  )
191
191
 
192
+ # --- CV with stale predict state: reroute to fit branch ---
193
+ # When last_op is "predict" but predictions were made on newdata (different
194
+ # n than training data), CV can't attach training-data-sized arrays to the
195
+ # prediction state. Fall back to the fit branch which produces model-level
196
+ # CV metrics (PRE, RMSE, etc.) — almost always what the user intended.
197
+ if how == "cv" and last_op == "predict" and pred is not None:
198
+ if len(pred.fitted) != bundle.n:
199
+ last_op = "fit"
200
+
192
201
  # --- Dispatch based on last_op ---
193
202
  match last_op:
194
203
  case "fit":
@@ -1,18 +1,7 @@
1
1
  """Marginal effects and estimated marginal means computation.
2
2
 
3
- Pipeline:
4
- 1. Grid construction (build_reference_grid): Evaluation grid at which predictions are computed
5
- 2. EMM/Slopes (compute_emm, compute_slopes): Predictions at grid points
6
- 3. Contrasts (compute_contrasts): Apply contrast matrices to EMM results
7
-
8
- Provides:
9
- - parse_explore_formula: Parse explore formula syntax
10
- - ExploreFormula, ExploreFormulaError, Condition: Parsed explore formula types
11
- - build_reference_grid: Construct evaluation grid from DataBundle
12
- - compute_emm: Compute estimated marginal means at grid points
13
- - compute_slopes: Compute marginal effects for continuous predictors
14
- - compute_contrasts: Apply contrast matrices to EMM results
15
- - matrices: EMM contrast matrix builders (build_*_matrix)
3
+ Call chain:
4
+ model.explore() -> build_reference_grid() -> compute_emm() / compute_slopes() -> compute_contrasts()
16
5
  """
17
6
 
18
7
  from bossanova.internal.marginal.explore import (
@@ -312,6 +312,15 @@ def compute_slopes_finite_diff(
312
312
 
313
313
  validate_focal_var(bundle, focal_var)
314
314
 
315
+ # --- drop rows with missing values in model variables -----------------
316
+ # The caller may pass raw data that includes rows dropped during fitting.
317
+ # Null/NaN values propagate through matrix multiplication and corrupt the
318
+ # AME estimate, so filter to complete cases on the model's columns.
319
+ model_cols = [c for c in bundle.X_names if c in data.columns]
320
+ if bundle.y_name in data.columns:
321
+ model_cols.append(bundle.y_name)
322
+ data = data.drop_nulls(subset=model_cols)
323
+
315
324
  # --- step size --------------------------------------------------------
316
325
  focal_col = data[focal_var].cast(pl.Float64).to_numpy()
317
326
  var_range = float(focal_col.max() - focal_col.min())
@@ -443,6 +452,12 @@ def compute_slopes_crossed(
443
452
  "(needed by evaluate_newdata)."
444
453
  )
445
454
 
455
+ # --- drop rows with missing values in model variables -----------------
456
+ model_cols = [c for c in bundle.X_names if c in data.columns]
457
+ if bundle.y_name in data.columns:
458
+ model_cols.append(bundle.y_name)
459
+ data = data.drop_nulls(subset=model_cols)
460
+
446
461
  # --- step size --------------------------------------------------------
447
462
  focal_col = data[focal_var].cast(pl.Float64).to_numpy()
448
463
  var_range = float(focal_col.max() - focal_col.min())
@@ -0,0 +1,22 @@
1
+ """LaTeX equation and markdown rendering for model output.
2
+
3
+ Call chain:
4
+ model.equation() -> build_equation(spec) -> LaTeX string
5
+ model_result.to_markdown() -> dataframe_to_markdown(df) -> markdown string
6
+ """
7
+
8
+ from bossanova.internal.rendering.latex import build_equation
9
+ from bossanova.internal.rendering.markdown import (
10
+ dataframe_to_markdown,
11
+ equation_to_markdown,
12
+ to_markdown,
13
+ write_text,
14
+ )
15
+
16
+ __all__ = [
17
+ "build_equation",
18
+ "dataframe_to_markdown",
19
+ "equation_to_markdown",
20
+ "to_markdown",
21
+ "write_text",
22
+ ]
@@ -0,0 +1,163 @@
1
+ """Markdown table and equation rendering for report export.
2
+
3
+ Pure functions that convert Polars DataFrames and LaTeX equations into
4
+ pipe-delimited GitHub Flavored Markdown / Quarto-compatible text.
5
+ Used by ``ModelResult.to_markdown()``, ``MathDisplay.to_markdown()``,
6
+ and the top-level ``bossanova.to_markdown()`` utility.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+
13
+ import polars as pl
14
+
15
+ __all__ = [
16
+ "dataframe_to_markdown",
17
+ "equation_to_markdown",
18
+ "to_markdown",
19
+ "write_text",
20
+ ]
21
+
22
+
23
+ def dataframe_to_markdown(
24
+ df: pl.DataFrame,
25
+ caption: str | None = None,
26
+ ) -> str:
27
+ """Convert a Polars DataFrame to a pipe-delimited markdown table.
28
+
29
+ Produces GitHub Flavored Markdown (GFM) table syntax that renders
30
+ correctly in Quarto, GitHub, and most markdown environments. Columns
31
+ are padded to align visually. Null values render as empty cells.
32
+
33
+ No additional numeric formatting is applied — the DataFrame is assumed
34
+ to be pre-rounded (as all bossanova result DataFrames are).
35
+
36
+ Args:
37
+ df: Polars DataFrame to convert.
38
+ caption: Optional table caption. Rendered as ``Table: {caption}``
39
+ above the table (Quarto cross-reference syntax).
40
+
41
+ Returns:
42
+ Pipe-delimited markdown table as a string.
43
+
44
+ Examples:
45
+ >>> import polars as pl
46
+ >>> df = pl.DataFrame({"x": [1, 2], "y": [3.14, 2.72]})
47
+ >>> print(dataframe_to_markdown(df))
48
+ | x | y |
49
+ |---|------|
50
+ | 1 | 3.14 |
51
+ | 2 | 2.72 |
52
+ """
53
+ cols = df.columns
54
+
55
+ # Convert all values to strings, rendering nulls as empty
56
+ str_rows: list[list[str]] = []
57
+ for row in df.iter_rows():
58
+ str_rows.append([("" if v is None else str(v)) for v in row])
59
+
60
+ # Compute column widths (minimum 3 for the separator dashes)
61
+ widths = [max(3, len(c)) for c in cols]
62
+ for row in str_rows:
63
+ for i, cell in enumerate(row):
64
+ widths[i] = max(widths[i], len(cell))
65
+
66
+ # Build table lines
67
+ header = "| " + " | ".join(c.ljust(widths[i]) for i, c in enumerate(cols)) + " |"
68
+ separator = "|" + "|".join("-" * (w + 2) for w in widths) + "|"
69
+ data_lines = [
70
+ "| " + " | ".join(cell.ljust(widths[i]) for i, cell in enumerate(row)) + " |"
71
+ for row in str_rows
72
+ ]
73
+
74
+ parts: list[str] = []
75
+ if caption is not None:
76
+ parts.append(f"Table: {caption}")
77
+ parts.append("")
78
+ parts.append(header)
79
+ parts.append(separator)
80
+ parts.extend(data_lines)
81
+ return "\n".join(parts)
82
+
83
+
84
+ def equation_to_markdown(
85
+ equation: str,
86
+ explanations: tuple[str, ...] = (),
87
+ *,
88
+ include_explanations: bool = True,
89
+ ) -> str:
90
+ r"""Wrap a LaTeX equation in display math delimiters for Quarto.
91
+
92
+ Produces ``$$...$$`` display math that Quarto renders natively in
93
+ Typst, HTML, and PDF output. Optionally appends term explanations
94
+ as plain text below the equation block.
95
+
96
+ Args:
97
+ equation: LaTeX equation string (without ``$$`` delimiters).
98
+ explanations: Term explanation strings from ``MathDisplay``.
99
+ include_explanations: If True (default), append explanations
100
+ as plain text lines below the equation.
101
+
102
+ Returns:
103
+ Markdown string with display math block and optional explanations.
104
+
105
+ Examples:
106
+ >>> print(equation_to_markdown(r"y = \beta_0 + \beta_1 x"))
107
+ $$
108
+ y = \beta_0 + \beta_1 x
109
+ $$
110
+ """
111
+ parts = [f"$$\n{equation}\n$$"]
112
+ if include_explanations and explanations:
113
+ parts.append("")
114
+ for exp in explanations:
115
+ parts.append(f"- {exp}")
116
+ return "\n".join(parts)
117
+
118
+
119
+ def write_text(text: str, path: str | Path) -> None:
120
+ """Write text content to a file, creating parent directories.
121
+
122
+ Args:
123
+ path: Destination file path. Parent directories are created
124
+ automatically if they don't exist.
125
+ text: Text content to write.
126
+ """
127
+ dest = Path(path)
128
+ dest.parent.mkdir(parents=True, exist_ok=True)
129
+ dest.write_text(text, encoding="utf-8")
130
+
131
+
132
+ def to_markdown(
133
+ df: pl.DataFrame,
134
+ path: str | Path | None = None,
135
+ caption: str | None = None,
136
+ ) -> str:
137
+ """Convert a Polars DataFrame to a markdown table, optionally saving to file.
138
+
139
+ Standalone utility for DataFrames not wrapped in ``ModelResult`` —
140
+ such as results from ``compare()``, ``model.jointtest()``,
141
+ ``model.vif()``, ``model.to_odds_ratio()``, etc.
142
+
143
+ For ``ModelResult`` objects (returned by ``.fit()``, ``.infer()``,
144
+ etc.), use ``result.to_markdown()`` instead.
145
+
146
+ Args:
147
+ df: Polars DataFrame to convert.
148
+ path: Optional file path. If given, writes the markdown and
149
+ creates parent directories automatically.
150
+ caption: Optional table caption (``Table: ...`` Quarto syntax).
151
+
152
+ Returns:
153
+ Pipe-delimited markdown table as a string.
154
+
155
+ Examples:
156
+ >>> from bossanova import compare, to_markdown
157
+ >>> to_markdown(compare(m1, m2), "comparison.md", caption="Model Comparison")
158
+ >>> md = to_markdown(m.jointtest()) # string only
159
+ """
160
+ text = dataframe_to_markdown(df, caption=caption)
161
+ if path is not None:
162
+ write_text(text, path)
163
+ return text
@@ -4,18 +4,6 @@ Call chain:
4
4
  model.simulate(n=...) -> generate_{lm,glm,lmer,glmer}_data() (pre-fit DGP)
5
5
  model.simulate(nsim=...) -> simulate_responses_from_fit() (post-fit parametric bootstrap)
6
6
  run_power_study(formula, ...) -> run_power_analysis() -> expand_sweep_grid()
7
-
8
- Container flow:
9
- Pre-fit: SimulationSpec + VaryingSpec -> DataBundle (synthetic data)
10
- Post-fit: FitState -> simulated response arrays
11
- Power: sweep parameters -> Polars DataFrame (power results)
12
-
13
- Submodules:
14
- dgp/: Data generating process functions (lm, glm, lmer, glmer)
15
- model_sim: Response simulation from fitted model parameters
16
- harness: Monte Carlo iteration runner and MonteCarloResult container
17
- metrics: Recovery metrics (bias, RMSE, coverage, rejection rate)
18
- power: Power analysis and parameter sweep utilities
19
7
  """
20
8
 
21
9
  from bossanova.internal.simulation.dgp import (