processbehavior 0.5.0__tar.gz → 0.6.0__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 (239) hide show
  1. {processbehavior-0.5.0 → processbehavior-0.6.0}/CHANGELOG.md +110 -1
  2. {processbehavior-0.5.0 → processbehavior-0.6.0}/CITATION.cff +1 -1
  3. {processbehavior-0.5.0 → processbehavior-0.6.0}/CONTRIBUTING.md +16 -8
  4. {processbehavior-0.5.0 → processbehavior-0.6.0}/PKG-INFO +9 -4
  5. {processbehavior-0.5.0 → processbehavior-0.6.0}/README.md +1 -1
  6. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/__init__.py +1 -1
  7. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/analysis.py +17 -17
  8. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/analysis_dataset.py +7 -6
  9. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/analysis_result.py +5 -4
  10. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/capability.py +42 -14
  11. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/exporters/excel_exporter.py +32 -0
  12. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/loss_function.py +96 -23
  13. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/maximum_information.py +5 -5
  14. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/effects_charts.py +3 -2
  15. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/renderers.py +1 -1
  16. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/process_behavior.py +98 -19
  17. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/residual_calculator.py +21 -21
  18. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/sds_detector.py +24 -11
  19. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/signals/config.py +8 -5
  20. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/signals/detector.py +1 -1
  21. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/signals/detectors.py +17 -14
  22. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/spc_constants.py +23 -23
  23. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/study.py +58 -7
  24. {processbehavior-0.5.0 → processbehavior-0.6.0}/pyproject.toml +7 -5
  25. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_ads3_charts.py +1 -1
  26. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_capability.py +2 -2
  27. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_capability_window.py +23 -0
  28. processbehavior-0.6.0/tests/test_design_report_diagnostics.py +87 -0
  29. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_documented_rule_coverage.py +19 -2
  30. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_effects_plotting.py +12 -0
  31. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_excel_export.py +38 -0
  32. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_garbage_character_handling.py +57 -0
  33. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_loss_function.py +8 -8
  34. processbehavior-0.6.0/tests/test_loss_reference.py +79 -0
  35. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_maximum_information.py +1 -1
  36. processbehavior-0.6.0/tests/test_nested_in_time.py +94 -0
  37. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_phased_xbar_s.py +3 -3
  38. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_r2_unavailable.py +1 -1
  39. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_r6_effect.py +2 -2
  40. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_recentering.py +1 -1
  41. processbehavior-0.6.0/tests/test_refresh_notebooks.py +104 -0
  42. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_residual_calculator.py +3 -3
  43. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_sds.py +3 -3
  44. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_sds_detector.py +2 -2
  45. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_signal_detectors.py +40 -0
  46. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_signals.py +1 -1
  47. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_spc_constants.py +1 -1
  48. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_unequal_subgroups.py +5 -5
  49. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_vas_chart_constants.py +3 -3
  50. processbehavior-0.5.0/examples/README.md +0 -10
  51. processbehavior-0.5.0/tests/test_bishop_reference.py +0 -82
  52. {processbehavior-0.5.0 → processbehavior-0.6.0}/.gitignore +0 -0
  53. {processbehavior-0.5.0 → processbehavior-0.6.0}/CODE_OF_CONDUCT.md +0 -0
  54. {processbehavior-0.5.0 → processbehavior-0.6.0}/LICENSE +0 -0
  55. {processbehavior-0.5.0 → processbehavior-0.6.0}/SECURITY.md +0 -0
  56. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/calibration.py +0 -0
  57. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/data_preparation.py +0 -0
  58. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/datasets/__init__.py +0 -0
  59. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/datasets/data/coffee_shop_demo_long.csv +0 -0
  60. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/datasets/loaders.py +0 -0
  61. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/datasets/synthetic.py +0 -0
  62. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/derivations.py +0 -0
  63. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/effects_calculator.py +0 -0
  64. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/exceptions.py +0 -0
  65. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/exporters/__init__.py +0 -0
  66. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/focused_result.py +0 -0
  67. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/formulation_spec.py +0 -0
  68. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/__init__.py +0 -0
  69. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/_math.py +0 -0
  70. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/capability_chart.py +0 -0
  71. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/contracts.py +0 -0
  72. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/control_chart.py +0 -0
  73. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/lane_boundaries.py +0 -0
  74. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/limits.py +0 -0
  75. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/maximum_information_chart.py +0 -0
  76. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/plotter.py +0 -0
  77. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/report.py +0 -0
  78. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/residuals.py +0 -0
  79. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/run_rules_viz.py +0 -0
  80. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/stats_box.py +0 -0
  81. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/themes.py +0 -0
  82. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/x_axis_layout.py +0 -0
  83. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/plotting/zones.py +0 -0
  84. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/py.typed +0 -0
  85. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/result_signals.py +0 -0
  86. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/result_tabular.py +0 -0
  87. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/series_length.py +0 -0
  88. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/signals/__init__.py +0 -0
  89. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/signals/result.py +0 -0
  90. {processbehavior-0.5.0 → processbehavior-0.6.0}/processbehavior/types.py +0 -0
  91. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/__init__.py +0 -0
  92. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/conftest.py +0 -0
  93. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/aco_medicare.json +0 -0
  94. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/known_effects_sds_1.json +0 -0
  95. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_inert_sds_1.json +0 -0
  96. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_inert_sds_2.json +0 -0
  97. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_inert_sds_3.json +0 -0
  98. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_inert_sds_4.json +0 -0
  99. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_inert_sds_5.json +0 -0
  100. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_inert_sds_6.json +0 -0
  101. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_sds_1.json +0 -0
  102. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_sds_2.json +0 -0
  103. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_sds_3.json +0 -0
  104. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_sds_4.json +0 -0
  105. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_sds_5.json +0 -0
  106. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/bishop_analyses/pm_sds_6.json +0 -0
  107. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/coffee_capability_full_baseline.json +0 -0
  108. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/data/FILLWEIGHTDATA_800.csv +0 -0
  109. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/data/coffee_shop_demo_long.csv +0 -0
  110. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/data/wheeler_cavity_thickness.csv +0 -0
  111. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/missing_values/X_data.parquet +0 -0
  112. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/missing_values/X_metadata.json +0 -0
  113. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/missing_values/X_statistics.json +0 -0
  114. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/missing_values/mR_data.parquet +0 -0
  115. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/missing_values/mR_metadata.json +0 -0
  116. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/missing_values/mR_statistics.json +0 -0
  117. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xbar_s/S_data.parquet +0 -0
  118. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xbar_s/S_metadata.json +0 -0
  119. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xbar_s/S_statistics.json +0 -0
  120. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xbar_s/Xbar_data.parquet +0 -0
  121. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xbar_s/Xbar_metadata.json +0 -0
  122. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xbar_s/Xbar_statistics.json +0 -0
  123. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xmr_r/X_data.parquet +0 -0
  124. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xmr_r/X_metadata.json +0 -0
  125. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xmr_r/X_statistics.json +0 -0
  126. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xmr_r/mR_data.parquet +0 -0
  127. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xmr_r/mR_metadata.json +0 -0
  128. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/paired_xmr_r/mR_statistics.json +0 -0
  129. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/pathological_ordering/X_data.parquet +0 -0
  130. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/pathological_ordering/X_metadata.json +0 -0
  131. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/pathological_ordering/X_statistics.json +0 -0
  132. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/pathological_ordering/mR_data.parquet +0 -0
  133. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/pathological_ordering/mR_metadata.json +0 -0
  134. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/pathological_ordering/mR_statistics.json +0 -0
  135. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/phased_xbar_s/S_data.parquet +0 -0
  136. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/phased_xbar_s/S_metadata.json +0 -0
  137. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/phased_xbar_s/S_statistics.json +0 -0
  138. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/phased_xbar_s/Xbar_data.parquet +0 -0
  139. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/phased_xbar_s/Xbar_metadata.json +0 -0
  140. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/phased_xbar_s/Xbar_statistics.json +0 -0
  141. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/residual_chart/S_data.parquet +0 -0
  142. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/residual_chart/S_metadata.json +0 -0
  143. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/residual_chart/S_statistics.json +0 -0
  144. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/single_obs_strata/X_data.parquet +0 -0
  145. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/single_obs_strata/X_metadata.json +0 -0
  146. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/single_obs_strata/X_statistics.json +0 -0
  147. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/single_obs_strata/mR_data.parquet +0 -0
  148. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/single_obs_strata/mR_metadata.json +0 -0
  149. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/single_obs_strata/mR_statistics.json +0 -0
  150. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_balanced/S_data.parquet +0 -0
  151. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_balanced/S_metadata.json +0 -0
  152. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_balanced/S_statistics.json +0 -0
  153. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_balanced/Xbar_data.parquet +0 -0
  154. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_balanced/Xbar_metadata.json +0 -0
  155. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_balanced/Xbar_statistics.json +0 -0
  156. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_uneven/S_data.parquet +0 -0
  157. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_uneven/S_metadata.json +0 -0
  158. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_uneven/S_statistics.json +0 -0
  159. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_uneven/Xbar_data.parquet +0 -0
  160. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_uneven/Xbar_metadata.json +0 -0
  161. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/stratified_uneven/Xbar_statistics.json +0 -0
  162. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/unstratified_small/X_data.parquet +0 -0
  163. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/unstratified_small/X_metadata.json +0 -0
  164. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/unstratified_small/X_statistics.json +0 -0
  165. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/unstratified_small/mR_data.parquet +0 -0
  166. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/unstratified_small/mR_metadata.json +0 -0
  167. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/fixtures/golden_masters/unstratified_small/mR_statistics.json +0 -0
  168. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_analysis_bugs.py +0 -0
  169. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_analysis_dataset.py +0 -0
  170. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_analysis_golden_master.py +0 -0
  171. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_analysis_result_contract.py +0 -0
  172. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_analysis_specifications.py +0 -0
  173. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_api_reference.py +0 -0
  174. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_beyond_limits_vectorized.py +0 -0
  175. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_bin_extremes.py +0 -0
  176. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_by_parameter.py +0 -0
  177. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_by_permutations.py +0 -0
  178. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_calibration.py +0 -0
  179. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_capability_chart.py +0 -0
  180. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_chart_availability.py +0 -0
  181. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_chart_column_contract.py +0 -0
  182. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_chart_parsing.py +0 -0
  183. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_control_chart_figure.py +0 -0
  184. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_data_preparation.py +0 -0
  185. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_datasets.py +0 -0
  186. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_derivations.py +0 -0
  187. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_derivations_robustness.py +0 -0
  188. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_design_state_lineage.py +0 -0
  189. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_edge_cases.py +0 -0
  190. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_effects_calculator.py +0 -0
  191. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_encode_rsg_vectorized.py +0 -0
  192. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_execute_validation.py +0 -0
  193. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_focus.py +0 -0
  194. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_init_cleaning.py +0 -0
  195. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_lane_boundaries.py +0 -0
  196. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_lane_connectors.py +0 -0
  197. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_limits_vectorized.py +0 -0
  198. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_manual_citations.py +0 -0
  199. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_na_in_factor_columns.py +0 -0
  200. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_notebook_ergonomics.py +0 -0
  201. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_ordered_categorical_lane_order.py +0 -0
  202. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_p0_p2_regressions.py +0 -0
  203. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_performance.py +0 -0
  204. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_phased_limits.py +0 -0
  205. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_plotting.py +0 -0
  206. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_plotting_decorations.py +0 -0
  207. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_plotting_hardening.py +0 -0
  208. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_plotting_xaxis.py +0 -0
  209. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_process_behavior.py +0 -0
  210. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_recentered_mr_fix.py +0 -0
  211. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_reference_integrity.py +0 -0
  212. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_release_gate_0_1_0_checklist.py +0 -0
  213. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_residual_aliases.py +0 -0
  214. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_residual_labels.py +0 -0
  215. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_residual_scope.py +0 -0
  216. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_resolve_by_grouping.py +0 -0
  217. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_result_signals.py +0 -0
  218. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_result_tabular.py +0 -0
  219. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_sampling_plan.py +0 -0
  220. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_series_length.py +0 -0
  221. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_signal_result_surface.py +0 -0
  222. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_single_detection.py +0 -0
  223. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_solo_mr_residuals.py +0 -0
  224. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_srp_refactor.py +0 -0
  225. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_strata_companion.py +0 -0
  226. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_strata_sufficiency.py +0 -0
  227. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_stratified_insufficiency.py +0 -0
  228. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_supports_calibration.py +0 -0
  229. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_synthetic.py +0 -0
  230. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_types.py +0 -0
  231. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_validate_by_parameter.py +0 -0
  232. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_weco_integration.py +0 -0
  233. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_why_not.py +0 -0
  234. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_x_axis_layout.py +0 -0
  235. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_xbar_residual_center.py +0 -0
  236. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_xbar_s_helpers.py +0 -0
  237. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_xbar_s_options.py +0 -0
  238. {processbehavior-0.5.0 → processbehavior-0.6.0}/tests/test_xbar_stratified.py +0 -0
  239. {processbehavior-0.5.0 → processbehavior-0.6.0}/validation/PBTESTDATABASE_T100.csv +0 -0
@@ -7,6 +7,114 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-11-07
11
+
12
+ ### Added
13
+ - **`validation/truth_report.py`.** It reports how closely the methods, as implemented, recover
14
+ answers known by construction: the PM INERT pure-noise data under all six missing-data
15
+ patterns, and Dr. Bishop's known-effects data. It is a report, not a gate.
16
+ - **Two design-report lines.** In ADS 3 the report says how many cells hold a single reading,
17
+ since one is enough to switch R2 for the whole study to the differencing method. When
18
+ conditions were observed in different periods, it says how far apart their average periods
19
+ are, since condition effects then average over different times.
20
+ - **`LossResult.reference`: what each loss part shows from noise alone.** The PDC, Time and
21
+ Interaction parts average squared deviations of estimated means, so with no effects each is
22
+ still above zero (about 14% interaction on the PM INERT pure-noise data). `reference` gives
23
+ the expected value of each under no effects for the study's own layout, exact for any
24
+ pattern of occupied cells and cell sizes, and the printed result shows it as a share of the
25
+ total. It is in `as_dict()` too. In ADS 2/3 the interaction is a remainder, so its reference
26
+ is `None`. Loss values are unchanged.
27
+ - **Windowed capability shows the window's own noise.** `CapabilityResult.window_sigma_r2` is
28
+ σ̂ from the window's R2 values, printed beside the full-study `sigma_hat_r2` that the
29
+ potential indices use. It appears only on windowed results; the default result is unchanged.
30
+ - **A Provenance sheet in Excel exports.** Written with the Summary sheet: library version,
31
+ time written, variables, input rows and rows analysed, design states, and a SHA-256
32
+ fingerprint of the analysed values. `AnalysisDataSet.n_input_rows` holds the input row count.
33
+ - **`validation/chance_signals.py`.** It reports how often charts signal on pure noise: X/mR
34
+ charts under the default rules (and mR with all eight), and the R3-R5 effect charts, whose
35
+ limits sit between about 2.2 and 4.4 standard deviations of the plotted points depending on
36
+ the chart and layout. A report, not a gate; its tables are in the WECO rules reference and
37
+ the residuals guide.
38
+
39
+ ### Changed
40
+ - **Manual citations follow the 11-7 edition.** Dr. Bishop's manual of 2026-11-07 has 20 chapters
41
+ (chapters 7 and up are one lower than before) and dot-numbered captions; every equation and
42
+ chapter citation in the code, docs and tests now uses its numbering, in the hyphen form the
43
+ manual repository checks ("11-7 manual Eq 13-8"). Seven equations it removed are cited by
44
+ their replacements. No behaviour changes.
45
+ - **mR charts check Rule 1 only by default.** Wheeler reads the moving range chart for points
46
+ above its upper limit. The zone and run rules assume symmetric, independent points, and
47
+ successive moving ranges share a reading, so with all eight rules on, 95% of 100-point
48
+ pure-noise mR charts showed a signal (59% with Rule 1 alone). X charts still check all eight.
49
+ `rules='all'` restores the old behaviour. Signal counts on mR charts drop.
50
+ - **The validation page says what is compared with VAS.** It claimed "every analytical
51
+ output"; the gate compares chart centres and limits, capability indices and loss shares. The
52
+ page now lists what is not compared (residual values one by one, signals, staged charts,
53
+ effects tables, calibrations, capability windows, the design report) and points to the truth
54
+ report. The `detect_signals()` docstring no longer says Rules 1-4 are the default.
55
+
56
+ ### Fixed
57
+ - **Tutorial outputs match the library again.** The docs site shows each notebook's saved outputs,
58
+ and eight tutorials had drifted: limits from before 0.4.0 in the Design-State-1 analysis, flat
59
+ limits where they now vary by subgroup size in the reference validation, older process means and
60
+ signal counts. All were re-executed. The Derived Variables tutorial, saved without outputs, now
61
+ shows them. Prose that the new outputs contradicted was corrected; the Bishop reference values in
62
+ the reference validation now quote his VAS rerun of 13 October 2026.
63
+ - **The factor x time interaction chart draws its lines in factor order on every machine.** It
64
+ sorted by time with an unstable sort, so the legend order, and the colour each line got, depended
65
+ on the numpy version.
66
+ - **Three signal rules now match their definitions.** Rule 5 (trend) fired only on 7 rising or
67
+ falling points, not 6; Rule 6 (oscillation) only on 15 alternating points, not 14; and Rule 2
68
+ counted only points in Zone A, leaving out points beyond 3σ, which Western Electric and Wheeler
69
+ count as "beyond 2σ". The docs, the rule descriptions and the minimum-observation table already
70
+ said 6, 14 and "Zone A or beyond". X charts may show slightly more signals; on 100 points of
71
+ pure noise, the share with a signal under the eight default rules rises from 70% to 79%.
72
+ - **The WECO rules page gives correct chance rates.** Six of its eight per-rule false-alarm rates
73
+ were wrong (for example 0.003% for 15 points hugging the centre, where it is 0.33%), and its
74
+ rule-set table had no source. The rates are now exact per-window figures, the table is measured
75
+ by `validation/chance_signals.py`, and the wording no longer says a signal is "almost certainly"
76
+ a special cause.
77
+ - **Commas in numbers stored as text are no longer misread.** A comma was removed wherever it
78
+ appeared, so a decimal comma such as `10,4` became 104; and a lone `10,4` in a column of plain
79
+ numbers silently became missing. Commas are now read one way per column: as decimal points
80
+ when the column has no `.` and some comma is not a thousands group, otherwise only as
81
+ separators between groups of three digits. Any other comma value becomes missing and is named.
82
+ - **The numeric-cleaning warning says what happened.** It counts the values actually reformatted
83
+ (it used to count every value in the column), gives examples, says when commas were read as
84
+ decimal points, and names the values that could not be read. A column of plain numeric strings
85
+ no longer warns.
86
+ - **A factor nested in time is named.** When every time period holds one level of a factor (a
87
+ stage, a before/after split), the design report says so beside the empty-cell count, and
88
+ `loss_function()` warns that the PDC and Time parts both contain that factor's effect, so the
89
+ decomposition counts it more than once. It points to staged charts instead. Loss values are
90
+ unchanged.
91
+ - **The design report no longer says "Complete structure" when there is nothing to compare.**
92
+ Without a sampling plan the structure line reads "Not checked (no sampling plan supplied)". It
93
+ used to say "Complete structure", even beside hundreds of empty cells.
94
+ - **Potential capability says why it is unavailable.** A study with no factors (such as one
95
+ stream of subgroups) or no time variable got "R2 residuals not available (ADS 1)". It now
96
+ says that Cp/Cpk use the VAS R2 residuals, which need at least one factor (or a time
97
+ variable), and that the study has none.
98
+
99
+ ### Removed
100
+ - **`examples/` and `validation/effects_visualization.ipynb`.** These demos predated the 0.2.0
101
+ API (`ProcessDataFrame`, `study.sds`, moved data files) and no longer ran; nothing linked to
102
+ them. The maintained, CI-executed tutorials are in `docs/tutorials/`.
103
+ - **`tests/test_bishop_reference.py`.** Its one test compared residual values with a workbook
104
+ of Dr. Bishop's that is not in the repository, so it skipped on every run. Residual charts are
105
+ still compared with VAS through their limits.
106
+
107
+ ### CI / Infrastructure
108
+ - **CI checks that docs notebooks match their saved outputs.** `scripts/refresh_notebooks.py`
109
+ re-executes the docs notebooks; with `--check` (in CI, replacing the nbmake run) it fails when a
110
+ notebook no longer runs or what a reader would see has changed, comparing printed text, tables
111
+ and each figure's data and labels. The `[test]` extra now needs nbclient, nbformat and ipykernel
112
+ instead of nbmake.
113
+ - **Python 3.14 is tested** on Ubuntu and Windows, in CI and in the release gate, and listed in
114
+ the package classifiers.
115
+ - **The truth report runs in CI.** `validation/truth_report.py` runs on every push (Ubuntu,
116
+ Python 3.13) and its output is kept as the `truth-report` artifact.
117
+
10
118
  ## [0.5.0] - 2026-10-29
11
119
 
12
120
  This release adds staged (phased) Xbar and S charts, computed per stage as Dr. Bishop
@@ -1181,7 +1289,8 @@ and may change between minor versions until 1.0.
1181
1289
  - `natsort >= 8.0`
1182
1290
  - `plotly >= 5.18, < 7`
1183
1291
 
1184
- [Unreleased]: https://github.com/cnicholas/processbehavior/compare/v0.5.0...HEAD
1292
+ [Unreleased]: https://github.com/cnicholas/processbehavior/compare/v0.6.0...HEAD
1293
+ [0.6.0]: https://github.com/cnicholas/processbehavior/compare/v0.5.0...v0.6.0
1185
1294
  [0.5.0]: https://github.com/cnicholas/processbehavior/compare/v0.4.0...v0.5.0
1186
1295
  [0.4.0]: https://github.com/cnicholas/processbehavior/compare/v0.3.3...v0.4.0
1187
1296
  [0.3.3]: https://github.com/cnicholas/processbehavior/compare/v0.3.2...v0.3.3
@@ -17,7 +17,7 @@ authors:
17
17
  repository-code: "https://github.com/cnicholas/processbehavior"
18
18
  url: "https://cnicholas.github.io/processbehavior/"
19
19
  license: Apache-2.0
20
- version: 0.5.0
20
+ version: 0.6.0
21
21
  keywords:
22
22
  - statistical process control
23
23
  - process behavior charts
@@ -74,13 +74,7 @@ The full test suite is the gate: if it fails, the change is not ready.
74
74
  The file `validation/PBTESTDATABASE_T100.csv` is ground-truth Minitab output
75
75
  for DS 1–6 from Bishop's reference materials. Numerical changes that touch
76
76
  residuals (R1–R5), DS detection, limit calculations, or variance
77
- decomposition must keep the relevant Bishop-reference tests green:
78
-
79
- ```bash
80
- pytest tests/test_bishop_reference.py -v # skips unless Bishop's residual workbooks are present
81
- ```
82
-
83
- There is also a full end-to-end check that compares every chart center, control
77
+ decomposition must keep the end-to-end check green. It compares every chart center, control
84
78
  limit, capability index and loss component against Bishop's VAS runs for the
85
79
  10-12-2026 manual (PM SDS 1–6, PM INERT, his known-effects data and the Medicare data):
86
80
 
@@ -97,10 +91,24 @@ summary.
97
91
  If a Bishop check fails after your change, the change is wrong — not the
98
92
  reference.
99
93
 
94
+ ## Docs notebooks
95
+
96
+ The docs site shows each notebook's saved outputs; it does not run them. After a change that
97
+ alters what a notebook prints or plots, refresh the saved outputs:
98
+
99
+ ```bash
100
+ python scripts/refresh_notebooks.py # every docs notebook
101
+ python scripts/refresh_notebooks.py docs/tutorials/signal-detection.ipynb
102
+ ```
103
+
104
+ CI runs `python scripts/refresh_notebooks.py --check` and fails when a notebook no longer runs or
105
+ no longer matches its saved outputs. Then read the text around any changed output: the check
106
+ compares outputs, not prose.
107
+
100
108
  ## Citing the methodology
101
109
 
102
110
  Code and docs cite Bishop's VAS documentation manual by edition and equation number:
103
- `(10-12 manual Eq 14-8)`, `Eqs 14-4..14-13`, `Eqs 11-16/17`. Name the edition once per file and use
111
+ `(11-7 manual Eq 13-8)`, `Eqs 13-4..13-13`, `Eqs 10-16/17`. Name the edition once per file and use
104
112
  the chapter-number form; `tests/test_manual_citations.py` checks both. The citations are read
105
113
  against the manual itself, so a new edition reports every place in the library whose equation
106
114
  changed or moved.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: processbehavior
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Python-native Process Behavior & Control Charts (spec-driven SPC with tidy outputs)
5
5
  Project-URL: Homepage, https://github.com/cnicholas/processbehavior
6
6
  Project-URL: Repository, https://github.com/cnicholas/processbehavior
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
23
  Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
24
25
  Classifier: Topic :: Scientific/Engineering
25
26
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
26
27
  Classifier: Typing :: Typed
@@ -33,12 +34,14 @@ Requires-Dist: typing-extensions>=4.0; python_version < '3.11'
33
34
  Provides-Extra: dev
34
35
  Requires-Dist: filelock>=3.20.3; extra == 'dev'
35
36
  Requires-Dist: ipykernel; extra == 'dev'
37
+ Requires-Dist: ipykernel>=6; extra == 'dev'
36
38
  Requires-Dist: jupyter-book>=2.0; extra == 'dev'
37
39
  Requires-Dist: kaleido>=0.2; extra == 'dev'
38
40
  Requires-Dist: lxml>=6.1.0; extra == 'dev'
39
41
  Requires-Dist: mypy>=1.10; extra == 'dev'
42
+ Requires-Dist: nbclient>=0.10; extra == 'dev'
40
43
  Requires-Dist: nbconvert>=7.17.1; extra == 'dev'
41
- Requires-Dist: nbmake>=1.5; extra == 'dev'
44
+ Requires-Dist: nbformat>=5.10; extra == 'dev'
42
45
  Requires-Dist: openpyxl>=3.1; extra == 'dev'
43
46
  Requires-Dist: orjson>=3.11.6; extra == 'dev'
44
47
  Requires-Dist: pillow>=12.2.0; extra == 'dev'
@@ -73,7 +76,9 @@ Requires-Dist: pre-commit>=3.7; extra == 'lint'
73
76
  Requires-Dist: ruff>=0.5; extra == 'lint'
74
77
  Requires-Dist: virtualenv>=20.36.1; extra == 'lint'
75
78
  Provides-Extra: test
76
- Requires-Dist: nbmake>=1.5; extra == 'test'
79
+ Requires-Dist: ipykernel>=6; extra == 'test'
80
+ Requires-Dist: nbclient>=0.10; extra == 'test'
81
+ Requires-Dist: nbformat>=5.10; extra == 'test'
77
82
  Requires-Dist: openpyxl>=3.1; extra == 'test'
78
83
  Requires-Dist: pyarrow>=14.0; extra == 'test'
79
84
  Requires-Dist: pytest-cov>=5; extra == 'test'
@@ -282,7 +287,7 @@ The full report renders to `validation/e2e_bishop_report.html`.
282
287
  - **Effects analysis**: Main effects, time effects, and interaction effects
283
288
  - **Stratified charts**: Automatic per-stratum limits for grouped individual data
284
289
  - **Staged charts**: `phased=True` gives each stage its own center and limits on X/mR and Xbar/S; `phase_statistics()` returns the numbers behind the steps
285
- - **Signal detection**: all eight Western Electric rules on X/mR; Rule 1 (3-sigma) on Xbar/S, where the run- and zone-based rules need a time order that subgroup comparisons don't have
290
+ - **Signal detection**: all eight Western Electric rules on X charts; Rule 1 (3-sigma) on mR, Xbar and S, where the run- and zone-based rules don't fit: subgroup comparisons have no time order, and successive moving ranges share a reading
286
291
  - **Beyond the charts**: capability indices (Cp/Cpk/Pp/Ppk), Taguchi loss decomposition, and maximum-information analysis — validated in the same Bishop-reference CI gate
287
292
  - **IDE support**: Column auto-completion via `ProcessBehavior(df).cols`
288
293
  - **Self-diagnostic errors**: Helpful messages that say what's available and how to fix it
@@ -199,7 +199,7 @@ The full report renders to `validation/e2e_bishop_report.html`.
199
199
  - **Effects analysis**: Main effects, time effects, and interaction effects
200
200
  - **Stratified charts**: Automatic per-stratum limits for grouped individual data
201
201
  - **Staged charts**: `phased=True` gives each stage its own center and limits on X/mR and Xbar/S; `phase_statistics()` returns the numbers behind the steps
202
- - **Signal detection**: all eight Western Electric rules on X/mR; Rule 1 (3-sigma) on Xbar/S, where the run- and zone-based rules need a time order that subgroup comparisons don't have
202
+ - **Signal detection**: all eight Western Electric rules on X charts; Rule 1 (3-sigma) on mR, Xbar and S, where the run- and zone-based rules don't fit: subgroup comparisons have no time order, and successive moving ranges share a reading
203
203
  - **Beyond the charts**: capability indices (Cp/Cpk/Pp/Ppk), Taguchi loss decomposition, and maximum-information analysis — validated in the same Bishop-reference CI gate
204
204
  - **IDE support**: Column auto-completion via `ProcessBehavior(df).cols`
205
205
  - **Self-diagnostic errors**: Helpful messages that say what's available and how to fix it
@@ -47,7 +47,7 @@ its ``.sds`` field. Internally some modules still use the legacy "SDS"
47
47
  prefix (e.g. ``sds_detector.py``); the model itself is three-state.
48
48
  """
49
49
 
50
- __version__ = '0.5.0'
50
+ __version__ = '0.6.0'
51
51
 
52
52
  # Result object
53
53
  from processbehavior.analysis_result import AnalysisResult
@@ -254,7 +254,7 @@ def _mr_family_limits(mr_spec: _MRChartSpec, *, mean, mR) -> pd.DataFrame:
254
254
  """Limits for one MR-family chart per row of a per-stratum or per-phase table.
255
255
 
256
256
  The array form of the scalar ``calculate_limits`` these sites used to call through
257
- ``DataFrame.apply(axis=1)``: the same formulae, 10-12 manual Eqs 12-10/12-11 (X) and
257
+ ``DataFrame.apply(axis=1)``: the same formulae, 11-7 manual Eqs 11-8/11-9 (X) and
258
258
  12-5 (mR). The scalar call built a ``pd.Series`` per stratum, about 0.1 ms each, so
259
259
  it cost 30s per chart at 300,000 strata. ``mean`` is ignored for the mR chart.
260
260
  """
@@ -921,7 +921,7 @@ class Analysis:
921
921
  """Return the cell-grid columns at which to compute Bishop's grand mean
922
922
  for an Xbar chart on this column.
923
923
 
924
- Each re-centred residual is R + Ybar (10-12 manual Eq 14-26; see
924
+ Each re-centred residual is R + Ybar (11-7 manual Eq 13-26; see
925
925
  `AnalysisDataSet._calculate_centered_residuals` and
926
926
  `residual_calculator.calculate_r6_residuals`). The canonical grand mean of an
927
927
  RCRk chart averages over the grain its effect lives at — equal weight per
@@ -1216,10 +1216,10 @@ class Analysis:
1216
1216
  def _xbar_limits(self, out: pd.DataFrame, s_bar: float, n_to_use: str) -> pd.DataFrame:
1217
1217
  """Xbar natural process limits for the subgroups in ``out`` (columns center, s, n, N).
1218
1218
 
1219
- Equal subgroup sizes (or n_mode="average", Eq 11-20): S-bar / c4(N) for every row.
1220
- Unequal sizes: sigma_hat is the average of each subgroup's S_r / c4(N_r) (10-12 manual
1221
- Eq 11-5) and each subgroup's limits are center ± multiplier · sigma_hat / sqrt(N_r)
1222
- (Eqs 11-16, 11-17), so they step with N_r, as VAS draws them. A one-reading subgroup
1219
+ Equal subgroup sizes (or n_mode="average", Eq 10-20): S-bar / c4(N) for every row.
1220
+ Unequal sizes: sigma_hat is the average of each subgroup's S_r / c4(N_r) (11-7 manual
1221
+ Eq 10-5) and each subgroup's limits are center ± multiplier · sigma_hat / sqrt(N_r)
1222
+ (Eqs 10-16, 10-17), so they step with N_r, as VAS draws them. A one-reading subgroup
1223
1223
  has no S_r: it takes no part in sigma_hat and is charted at center ± multiplier · sigma_hat.
1224
1224
  """
1225
1225
  if n_to_use == 'n':
@@ -1283,12 +1283,12 @@ class Analysis:
1283
1283
 
1284
1284
  Every subgroup with a value is charted. A one-reading subgroup has no S, so sigma-hat
1285
1285
  comes from the subgroups with n >= 2, and its limits are center ± 3·sigma-hat (10-12
1286
- manual Eqs 11-16, 11-17 with N_r = 1), as VAS draws them; the companion S chart keeps
1286
+ manual Eqs 10-16, 10-17 with N_r = 1), as VAS draws them; the companion S chart keeps
1287
1287
  only n >= 2. n counts the limits column, so a reading without one (R2's first value) is
1288
1288
  not charted, as in VAS. Returns None when no subgroup has n >= 2; the caller decides
1289
1289
  whether that raises (one chart) or is recorded (a stratum, a phase). Under
1290
1290
  ``n_mode='average'`` the ``N`` column becomes the mean fitted n and both charts take
1291
- the constant-N path (Eq 11-20).
1291
+ the constant-N path (Eq 10-20).
1292
1292
  """
1293
1293
  out = out[out['n'].ge(1)].copy() # n=0: every charted value missing
1294
1294
  fit = out['n'].ge(2)
@@ -2007,9 +2007,9 @@ class Analysis:
2007
2007
  A phase is one combination of the grouping factors: a contiguous run of subgroups in
2008
2008
  time order on the stage-by-stage layout that ``by=None`` already draws. Each phase is
2009
2009
  computed as the per-condition chart of ``by=[time]`` computes one stratum — the
2010
- unweighted mean of its subgroup means as centre (10-12 manual Eq 10-9 within the
2011
- phase), its own S-bar and sigma-hat (Eq 11-5), its own limits (Eqs 11-8, 11-9, 11-16,
2012
- 11-17) — and the phases are concatenated in display order with the stepped columns.
2010
+ unweighted mean of its subgroup means as centre (11-7 manual Figure 13.3 within the
2011
+ phase), its own S-bar and sigma-hat (Eq 10-5), its own limits (Eqs 10-8, 10-9, 10-16,
2012
+ 10-17) — and the phases are concatenated in display order with the stepped columns.
2013
2013
  Nothing is pooled across phases: Dr. Bishop (2026-10-18), "treat each stage separately
2014
2014
  as if it were a single analysis".
2015
2015
 
@@ -2177,10 +2177,10 @@ class Analysis:
2177
2177
 
2178
2178
  The X and mR charts share >85% of their calculation logic. The behavioral
2179
2179
  differences are encoded in ``mr_spec`` — no boolean flags needed. The
2180
- statistics are the 10-12 manual's Chapter 12: mR-bar over the T - 1
2181
- consecutive ranges (Eq 12-1), X centre Y-bar (Eqs 12-7, 12-9), X limits
2182
- Y-bar ± 3·mR-bar/d2 (Eqs 12-10, 12-11) and mR limits 0 … D4·mR-bar
2183
- (Eq 12-5), within each stratum or phase when the chart is split.
2180
+ statistics are the 11-7 manual's Chapter 11: mR-bar over the T - 1
2181
+ consecutive ranges (Eq 11-1), X centre Y-bar (Eq 11-7), X limits
2182
+ Y-bar ± 3·mR-bar/d2 (Eqs 11-8, 11-9) and mR limits 0 … D4·mR-bar
2183
+ (Eq 11-5), within each stratum or phase when the chart is split.
2184
2184
 
2185
2185
  Parameters
2186
2186
  ----------
@@ -2895,11 +2895,11 @@ class Analysis:
2895
2895
  return result
2896
2896
 
2897
2897
  # === Global limits path (unchanged) ===
2898
- # Moving range: mR-bar over the T - 1 consecutive ranges (10-12 manual Eq 12-1). A re-centred
2898
+ # Moving range: mR-bar over the T - 1 consecutive ranges (11-7 manual Eq 11-1). A re-centred
2899
2899
  # R2 chart takes them over R2 (_resolve_mr_source_column), not the plotted RCR2.
2900
2900
  out['mr'] = out[mr_source_col].diff().abs()
2901
2901
  mR = out['mr'].mean()
2902
- mean_ = out[value_col].mean() # Y-bar, the X chart's centre (Eqs 12-7, 12-9)
2902
+ mean_ = out[value_col].mean() # Y-bar, the X chart's centre (Eq 11-7)
2903
2903
 
2904
2904
  # R chart: drop first observation
2905
2905
  if mr_spec.drops_first_mr:
@@ -53,6 +53,7 @@ class AnalysisDataSet:
53
53
  # Store inputs
54
54
  self.spec = spec
55
55
  self.observed_design_state: int = observed_sds
56
+ self.n_input_rows: int = len(df) # Rows given to formulate(), before tidying
56
57
 
57
58
  # Initialize output containers
58
59
  self.interactions = {}
@@ -171,7 +172,7 @@ class AnalysisDataSet:
171
172
  def _check_r2_scale_factor(self, r2_method: str) -> None:
172
173
  """Record (and warn once) when the layout leaves the R2 scale factor undefined.
173
174
 
174
- The scaled-difference R2 (10-12 manual Eqs 14-4..14-13) needs at least two process
175
+ The scaled-difference R2 (11-7 manual Eqs 13-4..13-13) needs at least two process
175
176
  design conditions and more than K + 1 observations; outside that it has no value.
176
177
  """
177
178
  if r2_method != 'ma2':
@@ -243,7 +244,7 @@ class AnalysisDataSet:
243
244
 
244
245
  # Compute n_per_cell and cell means once
245
246
  self._n_per_cell = df.groupby('cell_key', observed=True)[y].transform('size')
246
- self._ybar_kt = df.groupby('cell_key', observed=True)[y].transform('mean') # Ȳ_kt (10-12 manual Eq 10-7)
247
+ self._ybar_kt = df.groupby('cell_key', observed=True)[y].transform('mean') # Ȳ_kt (11-7 manual Eq 9-6)
247
248
 
248
249
  # Handle edge case: empty data or all NaN
249
250
  if len(self._n_per_cell) == 0 or self._n_per_cell.isna().all():
@@ -355,12 +356,12 @@ class AnalysisDataSet:
355
356
  Calculate the re-centred residuals (RCR) and the R1 cell/marginal means (Rbar).
356
357
 
357
358
  Re-centring puts a residual back on the measurement scale without changing what it
358
- shows: RCR = R + Ȳ.. (Bishop's 10-12 manual Eq 14-26, for R1, R3, R4, R5 and R6; RCR6
359
+ shows: RCR = R + Ȳ.. (Bishop's 11-7 manual Eq 13-26, for R1, R3, R4, R5 and R6; RCR6
359
360
  is built per request in residual_calculator.calculate_r6_residuals). An effect chart of
360
361
  RCR5 by condition, RCR4 by period or RCR3 by subgroup therefore plots that effect plus
361
362
  R2, centred on the grand mean.
362
363
 
363
- RCR2 is not part of Eq 14-26; it is kept as Ȳ_kt + R2 (which reconstructs Y in ADS 1).
364
+ RCR2 is not part of Eq 13-26; it is kept as Ȳ_kt + R2 (which reconstructs Y in ADS 1).
364
365
 
365
366
  Note: This method intentionally mutates self.analysis_dataset in-place
366
367
  (adding columns directly to the DataFrame). This differs from
@@ -381,9 +382,9 @@ class AnalysisDataSet:
381
382
  df['Rbar_k'] = df.groupby([self.spec.rsg_var_name], observed=True)['R1'].transform('mean')
382
383
  df['Rbar_t'] = df.groupby([self.spec.time_var], observed=True)['R1'].transform('mean')
383
384
 
384
- # Re-centred residuals, Eq 14-26: RCR = R + Ybar
385
+ # Re-centred residuals, Eq 13-26: RCR = R + Ybar
385
386
  df['RCR1'] = df['Ybar'] + df['R1']
386
- df['RCR2'] = df['Ybar_kt'] + df['R2'] # not in Eq 14-26; reconstructs Y in ADS 1
387
+ df['RCR2'] = df['Ybar_kt'] + df['R2'] # not in Eq 13-26; reconstructs Y in ADS 1
387
388
  df['RCR3'] = df['Ybar'] + df['R3']
388
389
  df['RCR4'] = df['Ybar'] + df['R4']
389
390
  df['RCR5'] = df['Ybar'] + df['R5']
@@ -1053,9 +1053,10 @@ class AnalysisResult:
1053
1053
  Specific chart to analyze. If None, analyzes all charts.
1054
1054
  rules : str, list, or RuleSet, optional
1055
1055
  Rules to apply:
1056
- - 'standard': Rules 1-4 (default)
1057
- - 'extended': Rules 1-8
1058
- - 'all': All available rules
1056
+ - None (default): Rules 1-8 on X charts; Rule 1 on mR, Xbar and S charts
1057
+ - 'standard': Rules 1-4, on every chart
1058
+ - 'extended': Rules 1-8, on every chart
1059
+ - 'all': Same as 'extended'
1059
1060
  - List of rule names: ['rule_1', 'rule_2', ...]
1060
1061
  - RuleSet: Fluent API configuration
1061
1062
  config : SignalConfig, optional
@@ -1071,7 +1072,7 @@ class AnalysisResult:
1071
1072
 
1072
1073
  Examples
1073
1074
  --------
1074
- Simple usage (standard rules):
1075
+ Simple usage (default rules for each chart):
1075
1076
 
1076
1077
  >>> signals = result.detect_signals()
1077
1078
  >>> print(signals.summary)
@@ -1,5 +1,5 @@
1
1
  """
2
- Process Capability Analysis — Bishop Chapter 16.
2
+ Process Capability Analysis — Bishop Chapter 15 (11-7 manual).
3
3
 
4
4
  Computes capability indices given specification limits. Core logic lives as
5
5
  pure module-level functions (composition pattern matching ResidualCalculator /
@@ -19,7 +19,7 @@ Potential Capability (achievable by removing assignable causes):
19
19
 
20
20
  References
21
21
  ----------
22
- Bishop, T. *Variation Analysis System* — Chapter 16.
22
+ Bishop, T. *Variation Analysis System* — Chapter 15 (11-7 manual).
23
23
  """
24
24
 
25
25
  from __future__ import annotations
@@ -89,7 +89,7 @@ class SpecLimits:
89
89
  @dataclass(frozen=True)
90
90
  class CapabilityResult:
91
91
  """
92
- Result of a process capability assessment (Bishop Ch. 16).
92
+ Result of a process capability assessment (Bishop Ch. 15).
93
93
 
94
94
  Raw ``float`` values are stored unrounded. Use :meth:`as_dict` for a
95
95
  rounded dictionary or ``repr()`` for a formatted summary.
@@ -111,7 +111,7 @@ class CapabilityResult:
111
111
  pp, ppk_lower, ppk_upper, ppk : float | None
112
112
  Current capability indices (from overall sigma).
113
113
  sigma_hat_r2 : float | None
114
- Unbiased sigma from R2 residuals: S_R2 / c4(N_R2) (10-12 manual Eq 16-10).
114
+ Unbiased sigma from R2 residuals: S_R2 / c4(N_R2) (11-7 manual Eq 15-10).
115
115
  cp, cpk_lower, cpk_upper, cpk : float | None
116
116
  Potential capability indices (from R2 sigma), with CPL/CPU measured from
117
117
  ``potential_center``.
@@ -138,6 +138,11 @@ class CapabilityResult:
138
138
  ``y_bar + mean(R2)``. Equals ``y_bar`` when R2 averages to zero (ADS 1);
139
139
  with the scaled-difference R2 of ADS 2/3 it differs by mean(R2), which is
140
140
  small. None when potential capability is unavailable.
141
+ window_sigma_r2 : float | None
142
+ Windowed results only: σ̂ from the window's own R2 values, by the same
143
+ estimator as ``sigma_hat_r2``. The potential indices keep the full-study
144
+ ``sigma_hat_r2``; this shows how the window's noise compares. None when not
145
+ windowed or potential capability is unavailable.
141
146
  """
142
147
 
143
148
  # Input context
@@ -203,6 +208,9 @@ class CapabilityResult:
203
208
  # Potential view centre: mean of (y_bar + R2). Last field so positional construction is unchanged.
204
209
  potential_center: float | None = None
205
210
 
211
+ # Windowed only: sigma_hat from this window's own R2 values, beside the full-study one used above.
212
+ window_sigma_r2: float | None = None
213
+
206
214
  # ------------------------------------------------------------------
207
215
  # Visualization
208
216
  # ------------------------------------------------------------------
@@ -319,6 +327,7 @@ class CapabilityResult:
319
327
  out['time_var'] = self.time_var
320
328
  out['n_total'] = self.n_total
321
329
  out['window_warning'] = self.window_warning
330
+ out['window_sigma_r2'] = _r(self.window_sigma_r2)
322
331
  return out
323
332
 
324
333
  def __repr__(self) -> str:
@@ -339,6 +348,11 @@ class CapabilityResult:
339
348
  lines.append(' Potential Capability (R2 sigma):')
340
349
  lines.append(f' centre={d["potential_center"]}, sigma_hat_r2={d["sigma_hat_r2"]}')
341
350
  lines.append(f' Cp={d["cp"]} Cpk={d["cpk"]} (lower={d["cpk_lower"]}, upper={d["cpk_upper"]})')
351
+ if self.window is not None and self.window_sigma_r2 is not None:
352
+ lines.append(
353
+ f' sigma_hat_r2 is from the full study (N={d["n_total"]}); '
354
+ f'this window\'s own is {d["window_sigma_r2"]}'
355
+ )
342
356
 
343
357
  lines.append('')
344
358
  lines.append(' Z-scores:')
@@ -389,9 +403,9 @@ def compute_sigma_hat(values: np.ndarray) -> tuple[float, float]:
389
403
 
390
404
  Notes
391
405
  -----
392
- Bishop Ch. 16 — ddof=1 is mandatory. The c4 correction
406
+ Bishop Ch. 15 — ddof=1 is mandatory. The c4 correction
393
407
  removes the small-sample bias inherent in the sample standard
394
- deviation: sigma_hat = S / alpha_N (10-12 manual Eq 10-12).
408
+ deviation: sigma_hat = S / alpha_N (11-7 manual Eq 9-9).
395
409
  """
396
410
  n = len(values)
397
411
  s = float(np.std(values, ddof=1))
@@ -546,8 +560,8 @@ def _process_mean(frame: pd.DataFrame, response_var: str) -> float:
546
560
  Each experimental condition counts once, whatever its cell size, as for the
547
561
  Xbar centre line and the loss function's centring term. VAS prints this as the
548
562
  capability charts' PROCESS MEAN (PM SDS 6: 237.86, where the plain average of
549
- the readings is 237.834), and the 10-12 manual's one-sided indices (Eqs 16-5, 16-6,
550
- 16-12, 16-13) centre on it; on balanced data the two are equal.
563
+ the readings is 237.834), and the 11-7 manual's one-sided indices (Eqs 15-5, 15-6,
564
+ 15-12, 15-13) centre on it; on balanced data the two are equal.
551
565
  A study without cells (no factors or time) has one cell: the plain average.
552
566
  """
553
567
  valid = frame[frame[response_var].notna()]
@@ -690,6 +704,7 @@ def assess_capability(
690
704
  potential_unavailable_reason = None
691
705
  potential_values = None
692
706
  potential_center = None
707
+ window_sigma_r2 = None
693
708
  potential_outside = {}
694
709
 
695
710
  if ads.r2_unavailable_reason is not None:
@@ -699,9 +714,9 @@ def assess_capability(
699
714
  n_r2 = len(r2_values)
700
715
 
701
716
  if n_r2 >= 2:
702
- _, sigma_hat_r2 = compute_sigma_hat(r2_values) # Eq 16-10
717
+ _, sigma_hat_r2 = compute_sigma_hat(r2_values) # Eq 15-10
703
718
  potential_values = y_bar + r2_values
704
- # Centre the potential indices on the potential values themselves (Eqs 16-11..16-13 as
719
+ # Centre the potential indices on the potential values themselves (Eqs 15-11..15-13 as
705
720
  # VAS computes them): y_bar + mean(R2). Tom's Medicare run of 10/3/2026 prints
706
721
  # "PROCESS MEAN 10831.3" on the potential slide (y_bar 10832.4, mean R2 -1.15).
707
722
  potential_center = float(np.mean(potential_values))
@@ -711,15 +726,27 @@ def assess_capability(
711
726
  cpk_upper = pot['ppk_upper']
712
727
  cpk = pot['ppk']
713
728
  potential_outside = compute_pct_outside(potential_values, specs)
729
+ if window is not None:
730
+ window_r2 = frame['R2'].dropna().to_numpy(dtype=float)
731
+ if len(window_r2) >= 2:
732
+ _, window_sigma_r2 = compute_sigma_hat(window_r2)
714
733
  else:
715
734
  potential_unavailable_reason = (
716
735
  f'Too few R2 residual values ({n_r2}) for potential capability; need at least 2'
717
736
  )
718
737
  else:
719
- potential_unavailable_reason = (
720
- f'R2 residuals not available (ADS {ads.analytical_design_state.sds}); '
721
- f'Cp/Cpk require VAS residuals from a factorial+time design'
722
- )
738
+ needs = (('at least one factor', ads.spec.rsg_vars_list), ('a time variable', ads.spec.time_var))
739
+ missing = [what for what, present in needs if not present]
740
+ if missing:
741
+ potential_unavailable_reason = (
742
+ f'Cp/Cpk use the VAS R2 residuals, which need {" and ".join(missing)}; '
743
+ f'this study has {"none" if len(missing) == 1 else "neither"}.'
744
+ )
745
+ else:
746
+ potential_unavailable_reason = (
747
+ f'R2 residuals not available (ADS {ads.analytical_design_state.sds}); '
748
+ f'Cp/Cpk require VAS residuals from a factorial+time design'
749
+ )
723
750
 
724
751
  return CapabilityResult(
725
752
  specs=specs,
@@ -757,5 +784,6 @@ def assess_capability(
757
784
  n_total=n_total,
758
785
  window_warning=window_warning,
759
786
  potential_center=potential_center,
787
+ window_sigma_r2=window_sigma_r2,
760
788
  observed_values=y_values,
761
789
  )
@@ -73,6 +73,8 @@ class ExcelExporter:
73
73
 
74
74
  Creates a multi-sheet Excel workbook with organized analysis results:
75
75
  - Summary: Analysis metadata, SDS info, signal counts
76
+ - Provenance: library version, time written, variables, row counts,
77
+ design states and a fingerprint of the analysed data (with Summary)
76
78
  - Charts: One tab per chart (Xbar, S, stratified X, etc.)
77
79
  - Residuals: R1-R5 variance decomposition (if available)
78
80
  - Effects: Main effects (if calculated)
@@ -148,6 +150,7 @@ class ExcelExporter:
148
150
  # 1. Summary Tab
149
151
  if include_summary:
150
152
  self._write_summary_tab(writer, format_cells)
153
+ self._write_provenance_tab(writer, format_cells)
151
154
 
152
155
  # 2. Chart Tabs
153
156
  if include_charts:
@@ -236,6 +239,35 @@ class ExcelExporter:
236
239
  ws = writer.sheets['Summary']
237
240
  self._apply_formatting(ws)
238
241
 
242
+ def _write_provenance_tab(self, writer: pd.ExcelWriter, format_cells: bool) -> None:
243
+ """Write what produced this workbook, so a reader can tell which data and version it came from."""
244
+ import hashlib
245
+ from datetime import datetime, timezone
246
+
247
+ from .. import __version__
248
+
249
+ ads = self.result._ads
250
+ spec = ads.spec
251
+ data = ads.analysis_dataset
252
+ columns = [spec.response_var, *spec.rsg_vars_list, *([spec.time_var] if spec.time_var else [])]
253
+ row_hashes = pd.util.hash_pandas_object(data[columns], index=False).to_numpy()
254
+ rows = [
255
+ ('Library', f'processbehavior {__version__}'),
256
+ ('Written (UTC)', datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')),
257
+ ('Response', spec.response_var),
258
+ ('Factors', ', '.join(spec.rsg_vars_list) or '(none)'),
259
+ ('Time', spec.time_var or '(none)'),
260
+ ('Analysis type', self.result.summary['analysis_type']),
261
+ ('Input rows', ads.n_input_rows),
262
+ ('Rows analysed', len(data)),
263
+ ('Observed design state (ODS)', ads.observed_design_state),
264
+ ('Analytical design state (ADS)', ads.analytical_design_state.sds),
265
+ ('Data fingerprint (SHA-256)', hashlib.sha256(row_hashes.tobytes()).hexdigest()),
266
+ ]
267
+ pd.DataFrame(rows, columns=['Item', 'Value']).to_excel(writer, sheet_name='Provenance', index=False)
268
+ if format_cells:
269
+ self._apply_formatting(writer.sheets['Provenance'])
270
+
239
271
  def _write_chart_tabs(self, writer: pd.ExcelWriter, format_cells: bool) -> None:
240
272
  """Write tabs for each chart."""
241
273