bossanova 0.1.0.dev16__tar.gz → 0.1.0.dev18__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 (261) hide show
  1. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/.gitignore +1 -1
  2. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/PKG-INFO +16 -1
  3. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/README.md +15 -0
  4. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/__init__.py +1 -1
  5. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/expressions.py +1 -1
  6. bossanova-0.1.0.dev18/bossanova/internal/compare/__init__.py +27 -0
  7. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/compare.py +28 -11
  8. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/cv.py +5 -5
  9. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/deviance.py +1 -1
  10. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/f_test.py +1 -1
  11. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/helpers.py +3 -3
  12. bossanova-0.1.0.dev18/bossanova/internal/compare/ic.py +160 -0
  13. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/lrt.py +1 -1
  14. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/lrt_compare.py +1 -1
  15. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/compare/refit.py +1 -1
  16. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/builders/dataframes.py +2 -1
  17. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/builders/results.py +1 -2
  18. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/builders/specs.py +13 -4
  19. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/builders/state.py +9 -7
  20. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/schemas.py +3 -2
  21. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/structs/data.py +1 -1
  22. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/structs/display.py +6 -1
  23. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/structs/explore.py +4 -0
  24. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/structs/specs.py +3 -1
  25. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/structs/state.py +65 -29
  26. bossanova-0.1.0.dev18/bossanova/internal/design/__init__.py +77 -0
  27. {bossanova-0.1.0.dev16/bossanova/internal/maths → bossanova-0.1.0.dev18/bossanova/internal}/design/coding.py +44 -250
  28. {bossanova-0.1.0.dev16/bossanova/internal/maths → bossanova-0.1.0.dev18/bossanova/internal}/design/reference.py +1 -1
  29. bossanova-0.1.0.dev18/bossanova/internal/fit/__init__.py +80 -0
  30. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/fit}/convergence.py +1 -1
  31. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/fit}/diagnostics.py +29 -5
  32. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/fit/dispatch.py +88 -23
  33. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/fit/glm.py +5 -13
  34. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/fit/glmer.py +13 -20
  35. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/fit/lmer.py +7 -17
  36. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/fit/ols.py +0 -7
  37. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/fit}/predict.py +6 -6
  38. bossanova-0.1.0.dev18/bossanova/internal/formula/__init__.py +57 -0
  39. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/formula}/bundle.py +28 -4
  40. {bossanova-0.1.0.dev16/bossanova/internal/operations/common → bossanova-0.1.0.dev18/bossanova/internal/formula}/contrast_registry.py +11 -1
  41. bossanova-0.1.0.dev16/bossanova/internal/operations/contrasts.py → bossanova-0.1.0.dev18/bossanova/internal/formula/contrast_specs.py +13 -13
  42. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/design.py +2 -2
  43. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/encoding.py +45 -25
  44. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/evaluate.py +46 -73
  45. bossanova-0.1.0.dev18/bossanova/internal/formula/evaluate_contrast.py +399 -0
  46. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/evaluate_newdata.py +7 -39
  47. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/evaluate_transforms.py +6 -79
  48. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/helpers.py +1 -34
  49. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/parse.py +4 -4
  50. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/parser/__init__.py +12 -1
  51. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/parser/expr.py +19 -0
  52. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/parser/parser.py +26 -1
  53. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/random_effects.py +143 -135
  54. bossanova-0.1.0.dev18/bossanova/internal/infer/__init__.py +105 -0
  55. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/asymptotic.py +34 -9
  56. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/bootstrap.py +22 -18
  57. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/cv.py +17 -19
  58. bossanova-0.1.0.dev18/bossanova/internal/infer/dispatch.py +482 -0
  59. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/mee.py +22 -14
  60. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/params.py +31 -43
  61. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/permutation.py +3 -3
  62. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/prediction.py +14 -9
  63. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/profile.py +14 -8
  64. bossanova-0.1.0.dev18/bossanova/internal/infer/resample/__init__.py +49 -0
  65. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/infer}/resample/common.py +1 -1
  66. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/infer}/resample/glmer.py +50 -66
  67. bossanova-0.1.0.dev18/bossanova/internal/infer/resample/lm_operators.py +151 -0
  68. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/infer}/resample/lmer.py +15 -24
  69. bossanova-0.1.0.dev18/bossanova/internal/infer/resample/results.py +136 -0
  70. bossanova-0.1.0.dev18/bossanova/internal/infer/resample/simulate.py +124 -0
  71. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/satterthwaite_emm.py +3 -3
  72. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/__init__.py +33 -17
  73. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/bracket_contrasts.py +7 -7
  74. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/compute.py +122 -116
  75. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/contrasts.py +105 -261
  76. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/emm.py +34 -34
  77. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/explore.py +2 -2
  78. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/explore_parser.py +25 -2
  79. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/explore_scanner.py +2 -2
  80. {bossanova-0.1.0.dev16/bossanova/internal/operations/common → bossanova-0.1.0.dev18/bossanova/internal/marginal}/factors.py +4 -4
  81. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/grid.py +3 -3
  82. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/inference.py +4 -4
  83. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/joint_tests.py +3 -3
  84. bossanova-0.1.0.dev16/bossanova/internal/maths/inference/contrasts.py → bossanova-0.1.0.dev18/bossanova/internal/marginal/matrices.py +114 -111
  85. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/slopes.py +82 -53
  86. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/marginal}/transforms.py +8 -8
  87. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/__init__.py +22 -42
  88. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/__init__.py +1 -1
  89. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/__init__.py +0 -18
  90. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/welch.py +12 -9
  91. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/linalg/__init__.py +2 -4
  92. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/linalg/schur.py +3 -3
  93. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/linalg/sparse.py +3 -70
  94. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/glmer.py +1 -7
  95. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/lambda_template.py +3 -3
  96. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/lmer.py +2 -2
  97. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/pirls_sparse.py +3 -3
  98. bossanova-0.1.0.dev18/bossanova/internal/rendering/__init__.py +16 -0
  99. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/rendering/latex.py +7 -7
  100. bossanova-0.1.0.dev18/bossanova/internal/simulation/__init__.py +73 -0
  101. bossanova-0.1.0.dev18/bossanova/internal/simulation/dgp/__init__.py +13 -0
  102. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/dgp/glm.py +2 -2
  103. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/dgp/glmer.py +2 -2
  104. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/dgp/lm.py +2 -2
  105. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/dgp/lmer.py +2 -2
  106. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/harness.py +11 -11
  107. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/metrics.py +3 -23
  108. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/power.py +149 -8
  109. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/README.md +2 -2
  110. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/__init__.py +25 -3
  111. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/core.py +2 -0
  112. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/core_protocols.py +27 -7
  113. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/core_viz.py +66 -3
  114. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/design.py +50 -71
  115. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/fit_builders.py +3 -1
  116. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/fit_layers.py +19 -36
  117. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/helpers.py +91 -3
  118. bossanova-0.1.0.dev18/bossanova/internal/viz/mem.py +643 -0
  119. bossanova-0.1.0.dev18/bossanova/internal/viz/mem_forest.py +538 -0
  120. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/params.py +31 -0
  121. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/predict.py +34 -45
  122. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/profile.py +50 -12
  123. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/resamples.py +13 -13
  124. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/resid.py +3 -0
  125. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/vif.py +6 -13
  126. bossanova-0.1.0.dev18/bossanova/model/__init__.py +7 -0
  127. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/model/core.py +366 -360
  128. bossanova-0.1.0.dev18/bossanova/model/result.py +109 -0
  129. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/model/summary.py +1 -1
  130. bossanova-0.1.0.dev18/pyproject.toml +242 -0
  131. bossanova-0.1.0.dev16/bossanova/internal/containers/builders/data.py +0 -10
  132. bossanova-0.1.0.dev16/bossanova/internal/maths/design/__init__.py +0 -76
  133. bossanova-0.1.0.dev16/bossanova/internal/operations/__init__.py +0 -195
  134. bossanova-0.1.0.dev16/bossanova/internal/operations/common/__init__.py +0 -33
  135. bossanova-0.1.0.dev16/bossanova/internal/operations/common/data_utils.py +0 -36
  136. bossanova-0.1.0.dev16/bossanova/internal/operations/compare/__init__.py +0 -6
  137. bossanova-0.1.0.dev16/bossanova/internal/operations/fit/__init__.py +0 -31
  138. bossanova-0.1.0.dev16/bossanova/internal/operations/formula/__init__.py +0 -33
  139. bossanova-0.1.0.dev16/bossanova/internal/operations/infer/__init__.py +0 -76
  140. bossanova-0.1.0.dev16/bossanova/internal/operations/profile.py +0 -8
  141. bossanova-0.1.0.dev16/bossanova/internal/operations/rendering/__init__.py +0 -5
  142. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/__init__.py +0 -75
  143. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/glm.py +0 -786
  144. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/lm.py +0 -796
  145. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/lm_bca.py +0 -326
  146. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/lm_operators.py +0 -337
  147. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/mixed.py +0 -14
  148. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/results.py +0 -299
  149. bossanova-0.1.0.dev16/bossanova/internal/operations/resample/utils.py +0 -128
  150. bossanova-0.1.0.dev16/bossanova/internal/operations/simulation/__init__.py +0 -64
  151. bossanova-0.1.0.dev16/bossanova/internal/operations/simulation/dgp/__init__.py +0 -13
  152. bossanova-0.1.0.dev16/bossanova/internal/viz/mem.py +0 -478
  153. bossanova-0.1.0.dev16/bossanova/model/__init__.py +0 -5
  154. bossanova-0.1.0.dev16/pyproject.toml +0 -580
  155. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/LICENSE +0 -0
  156. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/README.md +0 -0
  157. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/__init__.py +0 -0
  158. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/advertising.csv +0 -0
  159. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/cake.csv +0 -0
  160. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/chickweight.csv +0 -0
  161. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/credit.csv +0 -0
  162. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/gammas.csv +0 -0
  163. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/mtcars.csv +0 -0
  164. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/penguins.csv +0 -0
  165. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/poker.csv +0 -0
  166. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/sleep.csv +0 -0
  167. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/titanic.csv +0 -0
  168. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/titanic_test.csv +0 -0
  169. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/data/titanic_train.csv +0 -0
  170. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/distributions/__init__.py +0 -0
  171. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/distributions/continuous.py +0 -0
  172. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/distributions/discrete.py +0 -0
  173. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/distributions/varying.py +0 -0
  174. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/__init__.py +0 -0
  175. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/__init__.py +0 -0
  176. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/builders/__init__.py +0 -0
  177. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/builders/resamples.py +0 -0
  178. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/structs/__init__.py +0 -0
  179. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/structs/formula.py +0 -0
  180. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/containers/validators.py +0 -0
  181. {bossanova-0.1.0.dev16/bossanova/internal/maths → bossanova-0.1.0.dev18/bossanova/internal}/design/names.py +0 -0
  182. {bossanova-0.1.0.dev16/bossanova/internal/maths → bossanova-0.1.0.dev18/bossanova/internal}/design/z_matrix.py +0 -0
  183. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/fit/rank.py +0 -0
  184. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/fit}/varying.py +0 -0
  185. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/parser/scanner.py +0 -0
  186. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/formula/parser/token.py +0 -0
  187. {bossanova-0.1.0.dev16/bossanova/internal/operations/common → bossanova-0.1.0.dev18/bossanova/internal/infer}/formula_utils.py +0 -0
  188. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal/infer}/resample/core.py +0 -0
  189. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/resample_bundle.py +0 -0
  190. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/infer/simulation.py +0 -0
  191. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/conditions.py +0 -0
  192. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/marginal/validation.py +0 -0
  193. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/backend/__init__.py +0 -0
  194. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/backend/dispatch.py +0 -0
  195. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/backend/jax.py +0 -0
  196. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/backend/numpy.py +0 -0
  197. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/backend/protocol.py +0 -0
  198. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/batching.py +0 -0
  199. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/config.py +0 -0
  200. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/convergence.py +0 -0
  201. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/differentiation.py +0 -0
  202. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/algebra.py +0 -0
  203. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/base.py +0 -0
  204. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/core.py +0 -0
  205. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/derived.py +0 -0
  206. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/factories.py +0 -0
  207. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/plotting.py +0 -0
  208. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/distributions/probability.py +0 -0
  209. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/__init__.py +0 -0
  210. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/binomial.py +0 -0
  211. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/create.py +0 -0
  212. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/gamma.py +0 -0
  213. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/gaussian.py +0 -0
  214. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/links.py +0 -0
  215. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/poisson.py +0 -0
  216. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/response.py +0 -0
  217. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/schema.py +0 -0
  218. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/family/tdist.py +0 -0
  219. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/diagnostics.py +0 -0
  220. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/estimation.py +0 -0
  221. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/hypothesis.py +0 -0
  222. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/information_criteria.py +0 -0
  223. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/multiplicity.py +0 -0
  224. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/profile.py +0 -0
  225. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/sandwich.py +0 -0
  226. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/satterthwaite.py +0 -0
  227. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/inference/wald_variance.py +0 -0
  228. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/linalg/qr.py +0 -0
  229. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/linalg/svd.py +0 -0
  230. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/predict.py +0 -0
  231. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/rng.py +0 -0
  232. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/rounding.py +0 -0
  233. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/__init__.py +0 -0
  234. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/glm.py +0 -0
  235. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/heuristics.py +0 -0
  236. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/initialization.py +0 -0
  237. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/lambda_builder.py +0 -0
  238. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/lambda_sparse.py +0 -0
  239. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/optimize.py +0 -0
  240. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/solvers/quadrature.py +0 -0
  241. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/tolerances.py +0 -0
  242. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/transforms.py +0 -0
  243. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/variance.py +0 -0
  244. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/maths/weights.py +0 -0
  245. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/dgp/generate.py +0 -0
  246. {bossanova-0.1.0.dev16/bossanova/internal/operations → bossanova-0.1.0.dev18/bossanova/internal}/simulation/model_sim.py +0 -0
  247. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/cognition.py +0 -0
  248. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/compare.py +0 -0
  249. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/core_data.py +0 -0
  250. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/core_sizing.py +0 -0
  251. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/dag.py +0 -0
  252. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/fit.py +0 -0
  253. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/lattice.py +0 -0
  254. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/layout.py +0 -0
  255. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/ranef.py +0 -0
  256. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/internal/viz/relationships.py +0 -0
  257. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/model/guards.py +0 -0
  258. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/bossanova/py.typed +0 -0
  259. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/tests/bossanova_benchmarks/bootstrap/data/README.md +0 -0
  260. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/tests/bossanova_benchmarks/insteval/data/README.md +0 -0
  261. {bossanova-0.1.0.dev16 → bossanova-0.1.0.dev18}/tests/bossanova_tests/hypothesis/README.md +0 -0
@@ -78,7 +78,7 @@ COMPARISON-REPORT.md
78
78
  ERROR_MESSAGE_IMPROVEMENTS.md
79
79
  *.qmd
80
80
  bossanova-docs/apidocs/
81
- bossanova-docs/help/
81
+ bossanova-docs/api/
82
82
  bossanova-docs/wip/
83
83
  .hypothesis/
84
84
  research.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bossanova
3
- Version: 0.1.0.dev16
3
+ Version: 0.1.0.dev18
4
4
  Summary: Bridging statistical cultures with some jazz
5
5
  Author: Eshin Jolly
6
6
  License-Expression: MIT
@@ -70,4 +70,19 @@ m.explore("Days")
70
70
  m.effects
71
71
  ```
72
72
 
73
+ ## Development
74
+
75
+ ```bash
76
+ # Install dependencies
77
+ pixi install
78
+
79
+ # Install the IPython kernel (required for notebooks)
80
+ pixi run setup-kernel
81
+
82
+ # Run the full lint + type-check chain
83
+ pixi run lint
84
+
85
+ # Run fast unit tests
86
+ pixi run test
87
+ ```
73
88
 
@@ -42,4 +42,19 @@ m.explore("Days")
42
42
  m.effects
43
43
  ```
44
44
 
45
+ ## Development
46
+
47
+ ```bash
48
+ # Install dependencies
49
+ pixi install
50
+
51
+ # Install the IPython kernel (required for notebooks)
52
+ pixi run setup-kernel
53
+
54
+ # Run the full lint + type-check chain
55
+ pixi run lint
56
+
57
+ # Run fast unit tests
58
+ pixi run test
59
+ ```
45
60
 
@@ -31,7 +31,7 @@ from bossanova.data import ( # noqa: E402
31
31
  from bossanova.model import model # noqa: E402
32
32
 
33
33
  # Statistics
34
- from bossanova.internal.operations.compare import compare, lrt # noqa: E402
34
+ from bossanova.internal.compare import compare, lrt # noqa: E402
35
35
 
36
36
  # Visualization
37
37
  from bossanova.internal import viz # noqa: E402
@@ -2,7 +2,7 @@
2
2
 
3
3
  This module provides stateless Polars expressions for data transformations
4
4
  commonly used in statistical modeling. These match the semantics of the
5
- formula-based transforms in `bossanova.internal.operations.formula`.
5
+ formula-based transforms in `bossanova.internal.formula`.
6
6
 
7
7
  Core Transforms:
8
8
  center(col): x - mean(x) — subtract mean only
@@ -0,0 +1,27 @@
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
+ Container flow:
8
+ Input: Sequence of fitted model instances
9
+ Output: Polars DataFrame with comparison metrics
10
+
11
+ Submodules:
12
+ compare: Composite comparison table (AIC, BIC, loglik, df)
13
+ ic: Information criterion comparison (delta-AIC/BIC, Akaike/Schwarz weights)
14
+ lrt: Likelihood ratio test for nested mixed models
15
+ lrt_compare: LRT implementation details
16
+ f_test: F-test for nested linear models
17
+ deviance: Deviance test for nested GLMs
18
+ cv: Cross-validation comparison (Nadeau-Bengio corrected t-test)
19
+ refit: REML-to-ML refit helper for mixed model comparison
20
+ helpers: Shared formatting utilities
21
+ """
22
+
23
+ from bossanova.internal.compare.compare import compare
24
+ from bossanova.internal.compare.ic import compare_aic, compare_bic
25
+ from bossanova.internal.compare.lrt import lrt
26
+
27
+ __all__ = ["compare", "compare_aic", "compare_bic", "lrt"]
@@ -35,16 +35,17 @@ import polars as pl
35
35
 
36
36
  from bossanova.internal.maths.config import get_display_digits
37
37
  from bossanova.internal.maths.rounding import round_float_columns
38
- from bossanova.internal.operations.compare.cv import compare_cv
39
- from bossanova.internal.operations.compare.deviance import compare_deviance
40
- from bossanova.internal.operations.compare.f_test import compare_f_test
41
- from bossanova.internal.operations.compare.helpers import (
42
- infer_method,
38
+ from bossanova.internal.compare.cv import compare_cv
39
+ from bossanova.internal.compare.deviance import compare_deviance
40
+ from bossanova.internal.compare.f_test import compare_f_test
41
+ from bossanova.internal.compare.helpers import (
42
+ resolve_method,
43
43
  sort_models_by_complexity,
44
44
  validate_models,
45
45
  )
46
- from bossanova.internal.operations.compare.lrt_compare import compare_lrt
47
- from bossanova.internal.operations.compare.refit import refit_with_ml
46
+ from bossanova.internal.compare.ic import compare_aic, compare_bic
47
+ from bossanova.internal.compare.lrt_compare import compare_lrt
48
+ from bossanova.internal.compare.refit import refit_with_ml
48
49
 
49
50
  __all__ = ["compare"]
50
51
 
@@ -78,6 +79,8 @@ def compare(
78
79
  - "lrt": Likelihood ratio test (for mixed models)
79
80
  - "deviance": Deviance test (for glm)
80
81
  - "cv": Cross-validation comparison (for lm/glm)
82
+ - "aic": AIC comparison with delta-AIC and Akaike weights
83
+ - "bic": BIC comparison with delta-BIC and Schwarz weights
81
84
  sort: If True, sort models by complexity before comparing.
82
85
  This ensures proper nesting order.
83
86
  refit: If True and models are lmer/glmer with REML estimation,
@@ -140,6 +143,15 @@ def compare(
140
143
  - diff_se: Nadeau-Bengio corrected standard error
141
144
  - p_value: Two-sided p-value
142
145
 
146
+ For AIC/BIC comparison:
147
+ - model: Formula string
148
+ - npar: Number of estimated parameters
149
+ - loglik: Log-likelihood
150
+ - deviance: Deviance (-2 * loglik)
151
+ - AIC/BIC: Information criteria
152
+ - delta_AIC/delta_BIC: Difference from best model
153
+ - weight: Akaike/Schwarz weight (model probability)
154
+
143
155
  Raises:
144
156
  ValueError: If models are not valid for comparison.
145
157
 
@@ -211,15 +223,15 @@ def compare(
211
223
  # Validate and auto-fit models (pass method and refit for REML check)
212
224
  models = validate_models(models, method=method, refit=refit)
213
225
 
214
- # Sort if requested (not applicable for CV)
215
- if sort and method != "cv":
226
+ # Sort if requested (not applicable for CV or IC methods which sort internally)
227
+ if sort and method not in ("cv", "aic", "bic"):
216
228
  model_list = sort_models_by_complexity(models)
217
229
  else:
218
230
  model_list = list(models)
219
231
 
220
232
  # Infer method if auto
221
233
  if method == "auto":
222
- method = infer_method(model_list[0])
234
+ method = resolve_method(model_list[0])
223
235
 
224
236
  # For LRT with refit=True, refit REML models with ML
225
237
  if method == "lrt" and refit:
@@ -234,9 +246,14 @@ def compare(
234
246
  result = compare_deviance(model_list, test=test)
235
247
  elif method == "cv":
236
248
  result = compare_cv(model_list, cv=cv, seed=seed, metric=metric)
249
+ elif method == "aic":
250
+ result = compare_aic(model_list)
251
+ elif method == "bic":
252
+ result = compare_bic(model_list)
237
253
  else:
238
254
  raise ValueError(
239
- f"Unknown method: {method}. Use 'auto', 'f', 'lrt', 'deviance', or 'cv'."
255
+ f"Unknown method: {method}. "
256
+ "Use 'auto', 'f', 'lrt', 'deviance', 'cv', 'aic', or 'bic'."
240
257
  )
241
258
 
242
259
  # Round float columns to significant figures (digits=0 disables rounding)
@@ -13,19 +13,19 @@ from scipy import stats
13
13
 
14
14
  from bossanova.internal.containers.schemas import Col, ComparisonCv
15
15
  from bossanova.internal.maths.rng import RNG
16
- from bossanova.internal.operations.compare.helpers import get_model_type
17
- from bossanova.internal.operations.resample.core import (
16
+ from bossanova.internal.compare.helpers import get_model_type
17
+ from bossanova.internal.infer.resample.core import (
18
18
  generate_kfold_indices,
19
19
  generate_loo_indices,
20
20
  )
21
21
 
22
22
  __all__ = [
23
23
  "compare_cv",
24
- "cv_fold_score",
24
+ "compute_cv_fold_score",
25
25
  ]
26
26
 
27
27
 
28
- def cv_fold_score(
28
+ def compute_cv_fold_score(
29
29
  model: Any,
30
30
  train_idx: np.ndarray,
31
31
  test_idx: np.ndarray,
@@ -142,7 +142,7 @@ def compare_cv(
142
142
 
143
143
  for train_idx, test_idx in splits:
144
144
  for i, model in enumerate(models):
145
- score = cv_fold_score(model, train_idx, test_idx, metric)
145
+ score = compute_cv_fold_score(model, train_idx, test_idx, metric)
146
146
  fold_scores[i].append(score)
147
147
 
148
148
  # Convert to arrays
@@ -14,7 +14,7 @@ from bossanova.internal.containers.schemas import (
14
14
  ComparisonDevianceChi2,
15
15
  ComparisonDevianceF,
16
16
  )
17
- from bossanova.internal.operations.compare.helpers import check_nested
17
+ from bossanova.internal.compare.helpers import check_nested
18
18
 
19
19
  __all__ = [
20
20
  "compare_deviance",
@@ -10,7 +10,7 @@ import polars as pl
10
10
  from scipy import stats
11
11
 
12
12
  from bossanova.internal.containers.schemas import Col, ComparisonFTest
13
- from bossanova.internal.operations.compare.helpers import check_nested
13
+ from bossanova.internal.compare.helpers import check_nested
14
14
 
15
15
  __all__ = [
16
16
  "compare_f_test",
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
15
15
  __all__ = [
16
16
  "check_nested",
17
17
  "get_model_type",
18
- "infer_method",
18
+ "resolve_method",
19
19
  "sort_models_by_complexity",
20
20
  "validate_models",
21
21
  ]
@@ -100,7 +100,7 @@ def validate_models(
100
100
 
101
101
  # For LRT, check that all models use ML estimation (unless refit=True)
102
102
  model_type = model_types[0]
103
- inferred_method = method if method != "auto" else infer_method(models[0])
103
+ inferred_method = method if method != "auto" else resolve_method(models[0])
104
104
 
105
105
  if inferred_method == "lrt" and model_type in ("lmer", "glmer") and not refit:
106
106
  methods = [m._spec.method for m in models]
@@ -151,7 +151,7 @@ def sort_models_by_complexity(models: tuple[Any, ...]) -> list[Any]:
151
151
  return sorted(models, key=get_total_params)
152
152
 
153
153
 
154
- def infer_method(model: Any) -> str:
154
+ def resolve_method(model: Any) -> str:
155
155
  """Infer the appropriate comparison method for a model type.
156
156
 
157
157
  Uses :func:`classify_model_type` to canonically determine the model type
@@ -0,0 +1,160 @@
1
+ """Information criterion comparison for model selection.
2
+
3
+ Computes AIC/BIC tables with delta-IC and Akaike/Schwarz weights,
4
+ following Burnham & Anderson (2002). Unlike hypothesis-test comparisons
5
+ (F-test, LRT, deviance), IC comparison does not require model nesting —
6
+ any set of models fitted to the same data can be compared.
7
+
8
+ Functions:
9
+ compare_aic: Rank models by AIC with delta-AIC and Akaike weights.
10
+ compare_bic: Rank models by BIC with delta-BIC and Schwarz weights.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import math
16
+ from typing import Any
17
+
18
+ import polars as pl
19
+
20
+ from bossanova.internal.containers.schemas import (
21
+ Col,
22
+ ComparisonAic,
23
+ ComparisonBic,
24
+ )
25
+ from bossanova.internal.maths.family import ESTIMATED_DISPERSION_FAMILIES
26
+ from bossanova.internal.maths.inference import compute_aic, compute_bic
27
+
28
+ __all__ = ["compare_aic", "compare_bic"]
29
+
30
+
31
+ def _get_n_params_ic(model: Any) -> int:
32
+ """Get total number of estimated parameters for IC computation.
33
+
34
+ Follows the same counting as ``compute_diagnostics()``:
35
+
36
+ - ``rank`` (estimable fixed effects)
37
+ - ``+ len(theta)`` for mixed models (variance parameters)
38
+ - ``+ 1`` for families with estimated dispersion (e.g. Gaussian sigma)
39
+
40
+ Args:
41
+ model: A fitted model instance.
42
+
43
+ Returns:
44
+ Total parameter count for AIC/BIC computation.
45
+ """
46
+ k: int = model._bundle.rank
47
+ if model._fit.theta is not None:
48
+ k += len(model._fit.theta)
49
+ if model._spec.family in ESTIMATED_DISPERSION_FAMILIES:
50
+ k += 1
51
+ return k
52
+
53
+
54
+ def compare_aic(models: list[Any]) -> pl.DataFrame:
55
+ """Compare models by AIC with delta-AIC and Akaike weights.
56
+
57
+ Models are sorted by AIC (best first). Delta-AIC is the difference
58
+ from the best model. Akaike weights represent the relative likelihood
59
+ of each model being the best, following Burnham & Anderson (2002):
60
+
61
+ w_i = exp(-0.5 * delta_i) / sum(exp(-0.5 * delta_j))
62
+
63
+ Args:
64
+ models: List of fitted model objects (at least 2).
65
+
66
+ Returns:
67
+ DataFrame with columns: model, npar, loglik, deviance, AIC, BIC,
68
+ delta_AIC, weight. Sorted by AIC ascending (best model first).
69
+ """
70
+ return _compare_ic(models, criterion="aic")
71
+
72
+
73
+ def compare_bic(models: list[Any]) -> pl.DataFrame:
74
+ """Compare models by BIC with delta-BIC and Schwarz weights.
75
+
76
+ Models are sorted by BIC (best first). Delta-BIC is the difference
77
+ from the best model. Schwarz weights use the same formula as Akaike
78
+ weights but applied to BIC differences.
79
+
80
+ Args:
81
+ models: List of fitted model objects (at least 2).
82
+
83
+ Returns:
84
+ DataFrame with columns: model, npar, loglik, deviance, AIC, BIC,
85
+ delta_BIC, weight. Sorted by BIC ascending (best model first).
86
+ """
87
+ return _compare_ic(models, criterion="bic")
88
+
89
+
90
+ def _compare_ic(models: list[Any], *, criterion: str) -> pl.DataFrame:
91
+ """Shared implementation for AIC/BIC comparison.
92
+
93
+ Args:
94
+ models: List of fitted models.
95
+ criterion: ``"aic"`` or ``"bic"``.
96
+
97
+ Returns:
98
+ DataFrame sorted by the chosen criterion with delta-IC and weights.
99
+ """
100
+ formulas: list[str] = []
101
+ npars: list[int] = []
102
+ logliks: list[float] = []
103
+ deviances: list[float] = []
104
+ aics: list[float] = []
105
+ bics: list[float] = []
106
+
107
+ for m in models:
108
+ k = _get_n_params_ic(m)
109
+ loglik = float(m._fit.loglik)
110
+ n = m._bundle.n
111
+
112
+ formulas.append(m.formula)
113
+ npars.append(k)
114
+ logliks.append(loglik)
115
+ deviances.append(-2.0 * loglik)
116
+ aics.append(compute_aic(loglik, k))
117
+ bics.append(compute_bic(loglik, k, n))
118
+
119
+ # Select criterion and schema
120
+ if criterion == "aic":
121
+ ic_values = aics
122
+ delta_col = Col.DELTA_AIC
123
+ schema = ComparisonAic
124
+ else:
125
+ ic_values = bics
126
+ delta_col = Col.DELTA_BIC
127
+ schema = ComparisonBic
128
+
129
+ # Sort all lists by IC value (ascending = best first)
130
+ order = sorted(range(len(models)), key=lambda i: ic_values[i])
131
+ formulas = [formulas[i] for i in order]
132
+ npars = [npars[i] for i in order]
133
+ logliks = [logliks[i] for i in order]
134
+ deviances = [deviances[i] for i in order]
135
+ aics = [aics[i] for i in order]
136
+ bics = [bics[i] for i in order]
137
+ ic_values = [ic_values[i] for i in order]
138
+
139
+ # Delta-IC: difference from best model
140
+ ic_min = ic_values[0]
141
+ deltas = [v - ic_min for v in ic_values]
142
+
143
+ # Akaike/Schwarz weights: exp(-0.5 * delta) / sum(exp(-0.5 * delta))
144
+ raw_weights = [math.exp(-0.5 * d) for d in deltas]
145
+ weight_sum = sum(raw_weights)
146
+ weights = [w / weight_sum for w in raw_weights]
147
+
148
+ return pl.DataFrame(
149
+ {
150
+ Col.MODEL: formulas,
151
+ Col.NPAR: npars,
152
+ Col.LOGLIK: logliks,
153
+ Col.DEVIANCE: deviances,
154
+ Col.AIC_R: aics,
155
+ Col.BIC_R: bics,
156
+ delta_col: deltas,
157
+ Col.WEIGHT: weights,
158
+ },
159
+ schema=schema,
160
+ )
@@ -4,7 +4,7 @@ from typing import Any
4
4
 
5
5
  import polars as pl
6
6
 
7
- from bossanova.internal.operations.compare.compare import compare
7
+ from bossanova.internal.compare.compare import compare
8
8
 
9
9
  __all__ = ["lrt"]
10
10
 
@@ -16,7 +16,7 @@ from bossanova.internal.containers.schemas import (
16
16
  from bossanova.internal.maths.config import is_singular
17
17
  from bossanova.internal.maths.inference import compute_aic as compute_aic
18
18
  from bossanova.internal.maths.inference import compute_bic as _compute_bic
19
- from bossanova.internal.operations.compare.helpers import check_nested, get_model_type
19
+ from bossanova.internal.compare.helpers import check_nested, get_model_type
20
20
 
21
21
  __all__ = [
22
22
  "compare_lrt",
@@ -6,7 +6,7 @@ estimation when the original uses REML, enabling valid likelihood ratio tests.
6
6
 
7
7
  from typing import Any
8
8
 
9
- from bossanova.internal.operations.compare.helpers import get_model_type
9
+ from bossanova.internal.compare.helpers import get_model_type
10
10
 
11
11
  __all__ = [
12
12
  "refit_with_ml",
@@ -61,7 +61,7 @@ def build_params_dataframe(
61
61
  Column set varies by inference method:
62
62
 
63
63
  - **asymp**: all inference columns (p_value last).
64
- - **boot**: no p_value; df = n_resamples.
64
+ - **boot**: all inference columns; df = n_resamples.
65
65
  - **perm**: no CIs; df = n_valid_resamples.
66
66
  - **cv**: PRE columns only.
67
67
  - **None**: term + estimate only.
@@ -96,6 +96,7 @@ def build_params_dataframe(
96
96
  data[Col.CI_UPPER] = params_inference.ci_upper.tolist()
97
97
  data[Col.STATISTIC] = params_inference.statistic.tolist()
98
98
  data[Col.DF] = params_inference.df.tolist()
99
+ data[Col.P_VALUE] = params_inference.p_value.tolist()
99
100
  return pl.DataFrame(data, schema=ParamsBoot)
100
101
 
101
102
  if method == "perm":
@@ -28,7 +28,7 @@ _INFERENCE_COLS: tuple[str, ...] = (
28
28
  )
29
29
 
30
30
  # Columns excluded per inference method.
31
- _BOOT_EXCLUDE: frozenset[str] = frozenset({Col.P_VALUE})
31
+ _BOOT_EXCLUDE: frozenset[str] = frozenset()
32
32
  _PERM_EXCLUDE: frozenset[str] = frozenset({Col.CI_LOWER, Col.CI_UPPER})
33
33
 
34
34
 
@@ -43,7 +43,6 @@ def append_inference_columns(
43
43
  column that is not None on *state*. Columns are added in canonical
44
44
  order: se, ci_lower, ci_upper, statistic, df, p_value.
45
45
 
46
- When *method* is ``"boot"``, the ``p_value`` column is excluded.
47
46
  When *method* is ``"perm"``, the ``ci_lower`` and ``ci_upper``
48
47
  columns are excluded.
49
48
 
@@ -173,7 +173,7 @@ def build_model_spec_from_formula(
173
173
  if "~" not in formula:
174
174
  raise ValueError(f"Invalid formula: {formula!r}. Formula must contain '~'.")
175
175
 
176
- from bossanova.internal.operations.formula.parse import extract_formula_structure
176
+ from bossanova.internal.formula.parse import extract_formula_structure
177
177
 
178
178
  try:
179
179
  structure = extract_formula_structure(formula)
@@ -269,13 +269,20 @@ def build_simulation_spec(
269
269
  # =============================================================================
270
270
 
271
271
 
272
+ _CONTRAST_FUNC_RE = (
273
+ r"(?:treatment|dummy|sum|deviation|helmert|sequential|poly)\((\w+)(?:,\s*[^)]+)?\)"
274
+ )
275
+
276
+
272
277
  def parse_formula_simple(formula: str) -> dict[str, Any]:
273
278
  """Simple formula parser for simulation defaults.
274
279
 
275
280
  Parses a model formula to extract variable types for default distributions.
281
+ Recognizes contrast encoding functions (``treatment(x)``, ``sum(x)``, etc.)
282
+ as categorical markers.
276
283
 
277
284
  Args:
278
- formula: Model formula (e.g., "y ~ x + factor(group) + (1|subject)").
285
+ formula: Model formula (e.g., ``"y ~ x + treatment(group) + (1|subject)"``).
279
286
 
280
287
  Returns:
281
288
  Dictionary with keys: response, continuous, factors, re_groups.
@@ -283,6 +290,8 @@ def parse_formula_simple(formula: str) -> dict[str, Any]:
283
290
  Examples:
284
291
  >>> parse_formula_simple("y ~ x + z")
285
292
  {'response': 'y', 'continuous': ['x', 'z'], 'factors': [], 're_groups': []}
293
+ >>> parse_formula_simple("y ~ treatment(group) + x")
294
+ {'response': 'y', 'continuous': ['x'], 'factors': ['group'], 're_groups': []}
286
295
  """
287
296
  if "~" not in formula:
288
297
  msg = f"Invalid formula: missing '~'. Got: {formula}"
@@ -297,10 +306,10 @@ def parse_formula_simple(formula: str) -> dict[str, Any]:
297
306
  msg = f"Invalid formula: empty response variable. Got: {formula}"
298
307
  raise ValueError(msg)
299
308
 
300
- factors = re.findall(r"factor\((\w+)\)", rhs)
309
+ factors = re.findall(_CONTRAST_FUNC_RE, rhs)
301
310
  re_groups = re.findall(r"\([^)]+\|(\w+)\)", rhs)
302
311
 
303
- cleaned = re.sub(r"factor\(\w+\)", "", rhs)
312
+ cleaned = re.sub(_CONTRAST_FUNC_RE, "", rhs)
304
313
  cleaned = re.sub(r"\([^)]+\|\w+\)", "", cleaned)
305
314
 
306
315
  all_terms = re.findall(r"\b([a-zA-Z_]\w*)\b", cleaned)
@@ -196,8 +196,8 @@ def build_mee_state(
196
196
  focal_var: str,
197
197
  mee_type: str,
198
198
  *,
199
- units: str = "link",
200
- weights: str = "equal",
199
+ how: str = "mem",
200
+ effect_scale: str = "link",
201
201
  L_matrix: np.ndarray | None = None,
202
202
  contrast_method: str | None = None,
203
203
  n_contrast_levels: int | None = None,
@@ -219,9 +219,11 @@ def build_mee_state(
219
219
  explore_formula: The explore formula string.
220
220
  focal_var: The primary variable being explored.
221
221
  mee_type: Type of effect ("means", "slopes", "contrasts").
222
- units: Scale of the estimates: "link" (linear predictor) or "data" (response).
223
- weights: Averaging philosophy: ``"equal"`` (balanced reference grid,
224
- emmeans-style) or ``"observed"`` (g-computation over observed data).
222
+ 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).
225
227
  L_matrix: Design matrix for delta method inference (optional).
226
228
  Shape (n_estimates, n_coef). For EMMs this is X_ref.
227
229
  contrast_method: Original contrast type for multiplicity adjustment
@@ -260,8 +262,8 @@ def build_mee_state(
260
262
  explore_formula=explore_formula,
261
263
  focal_var=focal_var,
262
264
  type=mee_type,
263
- units=units,
264
- weights=weights,
265
+ how=how,
266
+ effect_scale=effect_scale,
265
267
  L_matrix=L_matrix,
266
268
  contrast_method=contrast_method,
267
269
  n_contrast_levels=n_contrast_levels,
@@ -231,6 +231,7 @@ ParamsBoot = Schema(
231
231
  Col.CI_UPPER: Float64,
232
232
  Col.STATISTIC: Float64,
233
233
  Col.DF: Float64,
234
+ Col.P_VALUE: Float64,
234
235
  }
235
236
  )
236
237
 
@@ -528,7 +529,7 @@ VaryingCorrSchema = Schema(
528
529
  # compare() Schemas (rows = models)
529
530
  # =============================================================================
530
531
 
531
- # Currently unused retained as reference schema for AIC-based comparison.
532
+ # AIC-based model comparison (used by compare(method='aic')).
532
533
  ComparisonAic = Schema(
533
534
  {
534
535
  Col.MODEL: String,
@@ -542,7 +543,7 @@ ComparisonAic = Schema(
542
543
  }
543
544
  )
544
545
 
545
- # Currently unused retained as reference schema for BIC-based comparison.
546
+ # BIC-based model comparison (used by compare(method='bic')).
546
547
  ComparisonBic = Schema(
547
548
  {
548
549
  Col.MODEL: String,
@@ -171,7 +171,7 @@ class DataBundle:
171
171
  X: NDArray[np.floating] = field(validator=is_ndarray)
172
172
  y: NDArray[np.floating] = field(validator=is_ndarray)
173
173
  X_names: tuple[str, ...] = field(converter=to_tuple)
174
- y_name: str
174
+ y_name: str = field(validator=validators.instance_of(str))
175
175
 
176
176
  valid_mask: NDArray[np.bool_] = field(validator=is_ndarray)
177
177
  n_total: int = field(validator=validators.instance_of(int))
@@ -51,7 +51,12 @@ class MathDisplay:
51
51
  """
52
52
 
53
53
  equation: str = field(validator=validators.instance_of(str))
54
- explanations: tuple[str, ...] = field(factory=tuple)
54
+ explanations: tuple[str, ...] = field(
55
+ factory=tuple,
56
+ validator=validators.deep_iterable(
57
+ member_validator=validators.instance_of(str)
58
+ ),
59
+ )
55
60
  model_info: str = field(default="", validator=validators.instance_of(str))
56
61
 
57
62
  def _repr_latex_(self) -> str:
@@ -109,6 +109,9 @@ class ExploreFormula:
109
109
  means use n_levels - 1, i.e., maximum degree).
110
110
  contrast_ref: Reference level for treatment/dummy contrasts
111
111
  (e.g., ``"Placebo"`` from ``treatment(Drug, ref=Placebo)``).
112
+ contrast_level_ordering: Explicit level ordering for order-dependent
113
+ contrasts (helmert, sequential, poly). Parsed from bracket list
114
+ syntax, e.g. ``poly(dose, [low, med, high])``.
112
115
  contrast_expr: Bracket contrast expression AST (e.g., from
113
116
  ``Drug[Active - Placebo]`` syntax). None for named contrast
114
117
  functions or simple EMMs.
@@ -125,6 +128,7 @@ class ExploreFormula:
125
128
  contrast_type: str | None = field(default=None)
126
129
  contrast_degree: int | None = field(default=None)
127
130
  contrast_ref: str | None = field(default=None)
131
+ contrast_level_ordering: tuple[str, ...] | None = field(default=None)
128
132
  contrast_expr: ContrastExpr | None = field(default=None)
129
133
  conditions: tuple[Condition, ...] = field(factory=tuple, converter=tuple)
130
134
  focal_at_values: tuple[float | str, ...] | None = field(default=None)