process-improve 2.0.0__tar.gz → 2.2.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 (330) hide show
  1. {process_improve-2.0.0 → process_improve-2.2.0}/PKG-INFO +11 -25
  2. {process_improve-2.0.0 → process_improve-2.2.0}/README.md +8 -2
  3. {process_improve-2.0.0 → process_improve-2.2.0}/pyproject.toml +4 -24
  4. {process_improve-2.0.0 → process_improve-2.2.0}/pyproject.toml.orig +15 -30
  5. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/_batch_plots.py +2 -4
  6. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/control.py +27 -10
  7. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/features.py +28 -11
  8. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/preprocessing.py +34 -5
  9. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/bivariate/_elbow_peak.py +20 -7
  10. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/config.py +1 -1
  11. process_improve-2.2.0/src/process_improve/datasets/experiments/reference_catalogue/anderson-whitcomb-2007.json +20920 -0
  12. process_improve-2.2.0/src/process_improve/datasets/experiments/reference_catalogue/box-draper-2007.json +425391 -0
  13. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/box-hunter-hunter-1978.json +1137 -446
  14. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/davies-1956.json +398 -125
  15. process_improve-2.2.0/src/process_improve/datasets/experiments/reference_catalogue/derringer-suich-1980.json +1653 -0
  16. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/efi-mooc-quizzes.json +429 -217
  17. process_improve-2.2.0/src/process_improve/datasets/experiments/reference_catalogue/fries-hunter-1980.json +1626 -0
  18. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/goos-2025.json +50 -28
  19. process_improve-2.2.0/src/process_improve/datasets/experiments/reference_catalogue/goos-jones-2011.json +45443 -0
  20. process_improve-2.2.0/src/process_improve/datasets/experiments/reference_catalogue/jones-nachtsheim-2011.json +6012 -0
  21. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/jones-nachtsheim-2013.json +101 -51
  22. process_improve-2.2.0/src/process_improve/datasets/experiments/reference_catalogue/lenth-1989.json +1620 -0
  23. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/montgomery-2013.json +2850 -1169
  24. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/nair-ffd-tutorial-2004.json +14 -10
  25. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/nist-ehandbook-ch5.json +1166 -589
  26. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/nunez-ares-et-al-2023.json +70 -36
  27. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/nunez-ares-goos-2019.json +42 -15
  28. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/nunez-ares-goos-2020.json +241 -121
  29. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/pid-book.json +3170 -1705
  30. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/schmidt-launsby-2000.json +1684 -651
  31. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/ullmann-doe-2012.json +83 -3
  32. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/__init__.py +42 -0
  33. process_improve-2.2.0/src/process_improve/experiments/_analyses/_shared.py +145 -0
  34. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/aliasing.py +11 -4
  35. process_improve-2.2.0/src/process_improve/experiments/_analyses/box_cox.py +230 -0
  36. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/curvature.py +10 -6
  37. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/model_selection.py +40 -13
  38. process_improve-2.2.0/src/process_improve/experiments/_analyses/ols_extractors.py +360 -0
  39. process_improve-2.2.0/src/process_improve/experiments/_analyses/prediction.py +181 -0
  40. process_improve-2.2.0/src/process_improve/experiments/_analyses/split_plot.py +936 -0
  41. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/transforms.py +33 -2
  42. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_blocking.py +90 -10
  43. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_desirability.py +64 -8
  44. process_improve-2.2.0/src/process_improve/experiments/_expression_text.py +25 -0
  45. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_lm.py +119 -4
  46. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_region_geometry.py +74 -0
  47. process_improve-2.2.0/src/process_improve/experiments/_region_quadrature.py +305 -0
  48. process_improve-2.2.0/src/process_improve/experiments/_rules.py +396 -0
  49. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/analyze_experiment.py +17 -4
  50. process_improve-2.2.0/src/process_improve/experiments/_tools/analyze_omars.py +139 -0
  51. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/augment_design.py +36 -1
  52. process_improve-2.2.0/src/process_improve/experiments/_tools/compare_designs.py +94 -0
  53. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/evaluate_design.py +15 -1
  54. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/generate_design.py +25 -5
  55. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/optimize_responses.py +34 -2
  56. process_improve-2.2.0/src/process_improve/experiments/_tools/recommend_next_step.py +159 -0
  57. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/recommend_strategy.py +4 -1
  58. process_improve-2.2.0/src/process_improve/experiments/_tools/run_sheet.py +159 -0
  59. process_improve-2.2.0/src/process_improve/experiments/_tools/simulate_design_performance.py +111 -0
  60. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_uniform_sampling.py +67 -14
  61. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/analysis.py +501 -66
  62. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/augment.py +460 -52
  63. process_improve-2.2.0/src/process_improve/experiments/benchmark.py +356 -0
  64. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/compare.py +186 -17
  65. process_improve-2.2.0/src/process_improve/experiments/crossed_arrays.py +184 -0
  66. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/design_catalog.py +436 -33
  67. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs.py +347 -45
  68. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_constrained.py +459 -121
  69. process_improve-2.2.0/src/process_improve/experiments/designs_covariates.py +296 -0
  70. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_mixture.py +20 -11
  71. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_mixture_constrained.py +54 -34
  72. process_improve-2.2.0/src/process_improve/experiments/designs_mixture_process.py +659 -0
  73. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_optimal.py +139 -71
  74. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_response_surface.py +242 -23
  75. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_screening.py +120 -18
  76. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_space_filling.py +16 -4
  77. process_improve-2.2.0/src/process_improve/experiments/designs_split_plot.py +586 -0
  78. process_improve-2.2.0/src/process_improve/experiments/designs_three_level.py +213 -0
  79. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_utils.py +56 -11
  80. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/evaluate.py +663 -65
  81. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/factor.py +7 -0
  82. process_improve-2.2.0/src/process_improve/experiments/gaussian_process.py +350 -0
  83. process_improve-2.2.0/src/process_improve/experiments/incomplete_blocks.py +197 -0
  84. process_improve-2.2.0/src/process_improve/experiments/layouts.py +191 -0
  85. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/least_squares_means.py +36 -2
  86. process_improve-2.2.0/src/process_improve/experiments/model_comparison.py +199 -0
  87. process_improve-2.2.0/src/process_improve/experiments/next_step.py +2195 -0
  88. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/omars.py +78 -20
  89. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/optimization.py +369 -161
  90. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/projections.py +27 -9
  91. process_improve-2.2.0/src/process_improve/experiments/pseudocomponents.py +137 -0
  92. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/random_effects.py +90 -5
  93. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/reference_catalogue.py +604 -107
  94. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/region.py +593 -74
  95. process_improve-2.2.0/src/process_improve/experiments/run_sheet.py +330 -0
  96. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/separating.py +5 -1
  97. process_improve-2.2.0/src/process_improve/experiments/sequential.py +308 -0
  98. process_improve-2.2.0/src/process_improve/experiments/simulate.py +377 -0
  99. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/strategy/domain_templates.py +12 -6
  100. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/strategy/engine.py +274 -69
  101. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/strategy/models.py +16 -1
  102. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/structures.py +20 -4
  103. process_improve-2.2.0/src/process_improve/experiments/surface_checks.py +180 -0
  104. process_improve-2.2.0/src/process_improve/experiments/three_level_components.py +162 -0
  105. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/tools.py +13 -0
  106. process_improve-2.2.0/src/process_improve/experiments/variance_dispersion.py +279 -0
  107. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/optimization_plots.py +3 -3
  108. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/registry.py +10 -3
  109. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/surfaces.py +245 -33
  110. process_improve-2.2.0/src/process_improve/experiments/yates.py +190 -0
  111. process_improve-2.2.0/src/process_improve/monitoring/__init__.py +14 -0
  112. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/monitoring/control_charts.py +133 -37
  113. process_improve-2.2.0/src/process_improve/monitoring/evop.py +237 -0
  114. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/monitoring/metrics.py +16 -1
  115. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_asca.py +11 -8
  116. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_common.py +162 -13
  117. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_diagnostics.py +9 -1
  118. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_mbpca.py +34 -17
  119. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_mbpls.py +117 -27
  120. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_nipals.py +98 -66
  121. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_pca.py +94 -26
  122. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_pls.py +102 -17
  123. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_preprocessing.py +6 -2
  124. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_tpls.py +52 -14
  125. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/plots.py +33 -33
  126. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/recipes.py +36 -13
  127. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/regression/__init__.py +5 -1
  128. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/regression/_robust_regression.py +15 -2
  129. process_improve-2.2.0/src/process_improve/regression/box_tidwell.py +297 -0
  130. process_improve-2.2.0/src/process_improve/regression/nonlinear.py +193 -0
  131. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/regression/tools.py +3 -1
  132. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/analysis.py +10 -3
  133. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/mam.py +27 -1
  134. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/panel.py +7 -5
  135. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/simulation/tools.py +2 -2
  136. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/tool_safety.py +73 -8
  137. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/tool_spec.py +57 -2
  138. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/univariate/__init__.py +25 -0
  139. process_improve-2.2.0/src/process_improve/univariate/classical.py +709 -0
  140. process_improve-2.2.0/src/process_improve/univariate/error_transmission.py +142 -0
  141. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/univariate/metrics.py +68 -22
  142. process_improve-2.2.0/src/process_improve/univariate/reference_distribution.py +140 -0
  143. process_improve-2.2.0/src/process_improve/univariate/serial_correlation.py +190 -0
  144. process_improve-2.2.0/src/process_improve/univariate/stated_values.py +214 -0
  145. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/univariate/tools.py +8 -3
  146. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/spec.py +5 -3
  147. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/tools.py +13 -3
  148. process_improve-2.2.0/src/process_improve/workbench/__init__.py +60 -0
  149. process_improve-2.2.0/src/process_improve/workbench/_expressions.py +245 -0
  150. process_improve-2.2.0/src/process_improve/workbench/project.py +247 -0
  151. process_improve-2.2.0/src/process_improve/workbench/recipe.py +601 -0
  152. process_improve-2.2.0/src/process_improve/workbench/tools.py +583 -0
  153. process_improve-2.2.0/src/process_improve/workbench/variant.py +465 -0
  154. process_improve-2.0.0/src/process_improve/experiments/_analyses/_shared.py +0 -54
  155. process_improve-2.0.0/src/process_improve/experiments/_analyses/box_cox.py +0 -148
  156. process_improve-2.0.0/src/process_improve/experiments/_analyses/ols_extractors.py +0 -207
  157. process_improve-2.0.0/src/process_improve/experiments/_analyses/prediction.py +0 -99
  158. process_improve-2.0.0/src/process_improve/experiments/_analyses/split_plot.py +0 -402
  159. process_improve-2.0.0/src/process_improve/monitoring/__init__.py +0 -9
  160. process_improve-2.0.0/src/process_improve/univariate/classical.py +0 -350
  161. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/__init__.py +0 -0
  162. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/_extras.py +0 -0
  163. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/_linalg.py +0 -0
  164. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/_random.py +0 -0
  165. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/_remote_data.py +0 -0
  166. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/__init__.py +0 -0
  167. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/_batch_monitor.py +0 -0
  168. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/_batch_pca.py +0 -0
  169. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/_batch_pls.py +0 -0
  170. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/_common.py +0 -0
  171. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/_gaps.py +0 -0
  172. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/_online.py +0 -0
  173. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/alignment_helpers.py +0 -0
  174. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/data_input.py +0 -0
  175. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/datasets.py +0 -0
  176. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/plotting.py +0 -0
  177. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/batch/tools.py +0 -0
  178. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/bivariate/__init__.py +0 -0
  179. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/bivariate/methods.py +0 -0
  180. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/bivariate/tools.py +0 -0
  181. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/chemistry/__init__.py +0 -0
  182. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/chemistry/preprocessing.py +0 -0
  183. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/batch/batch-fake-data.csv +0 -0
  184. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/batch/details.txt +0 -0
  185. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/batch/dryer.csv +0 -0
  186. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/batch/nylon.csv +0 -0
  187. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/boilingpot.csv +0 -0
  188. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/corrosion.csv +0 -0
  189. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/golf.csv +0 -0
  190. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/pollutant.csv +0 -0
  191. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/NOTICE.md +0 -0
  192. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/reference_catalogue/topics.json +0 -0
  193. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/solar.csv +0 -0
  194. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/experiments/test_doe1.csv +0 -0
  195. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/monitoring/batch-yield-and-purity.csv +0 -0
  196. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/monitoring/rubber-colour.csv +0 -0
  197. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/C.csv +0 -0
  198. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/Hotellings_T2_A3.csv +0 -0
  199. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/Hotellings_T2_A6.csv +0 -0
  200. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/LDPE.csv +0 -0
  201. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/P.csv +0 -0
  202. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/R.csv +0 -0
  203. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/T.csv +0 -0
  204. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/U.csv +0 -0
  205. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/W.csv +0 -0
  206. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/LDPE/Yhat_A6.csv +0 -0
  207. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/cheddar-cheese.csv +0 -0
  208. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/dtu-pectin/NOTICE +0 -0
  209. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/dtu-pectin/extraction1.csv +0 -0
  210. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/dtu-pectin/extraction2.csv +0 -0
  211. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/dtu-pectin/extraction3.csv +0 -0
  212. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/dtu-pectin/ftir1.csv +0 -0
  213. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/dtu-pectin/ftir2.csv +0 -0
  214. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/dtu-pectin/ftir3.csv +0 -0
  215. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/kamyr.csv +0 -0
  216. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/solvents.csv +0 -0
  217. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tablet-spectra.csv +0 -0
  218. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/SOURCE +0 -0
  219. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group1.csv +0 -0
  220. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group2.csv +0 -0
  221. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group3.csv +0 -0
  222. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group4.csv +0 -0
  223. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group5.csv +0 -0
  224. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/process_conditions.csv +0 -0
  225. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group1.csv +0 -0
  226. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group2.csv +0 -0
  227. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group3.csv +0 -0
  228. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group4.csv +0 -0
  229. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group5.csv +0 -0
  230. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/datasets/multivariate/tpls-pyphi/quality_indicators.csv +0 -0
  231. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/__init__.py +0 -0
  232. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/diagnostics.py +0 -0
  233. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/glm.py +0 -0
  234. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/lack_of_fit.py +0 -0
  235. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/lenth.py +0 -0
  236. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_analyses/mixture.py +0 -0
  237. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_classical.py +0 -0
  238. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_finite_fields.py +0 -0
  239. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_moment_aberration.py +0 -0
  240. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_orthogonal_arrays.py +0 -0
  241. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/__init__.py +0 -0
  242. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/create_factorial_design.py +0 -0
  243. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/doe_knowledge.py +0 -0
  244. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/fit_linear_model.py +0 -0
  245. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/search_design_catalog.py +0 -0
  246. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/trade_off_table.py +0 -0
  247. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/_tools/visualize_doe.py +0 -0
  248. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/allocation.py +0 -0
  249. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/datasets.py +0 -0
  250. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/design_measures.py +0 -0
  251. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_factorial.py +0 -0
  252. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_omars.py +0 -0
  253. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_omars_ilp.py +0 -0
  254. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/designs_supersaturated.py +0 -0
  255. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/__init__.py +0 -0
  256. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/api.py +0 -0
  257. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/data/concepts.yaml +0 -0
  258. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/data/decision_rules.yaml +0 -0
  259. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/data/design_types.yaml +0 -0
  260. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/data/diagnostics.yaml +0 -0
  261. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/engine.py +0 -0
  262. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/knowledge/models.py +0 -0
  263. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/marginal_means.py +0 -0
  264. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/models.py +0 -0
  265. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/multistratum.py +0 -0
  266. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/omars_trade_off.py +0 -0
  267. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/optimal.py +0 -0
  268. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/run_order.py +0 -0
  269. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/simulations.py +0 -0
  270. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/strategy/__init__.py +0 -0
  271. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/strategy/budget.py +0 -0
  272. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/trade_off.py +0 -0
  273. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/true_process.py +0 -0
  274. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/__init__.py +0 -0
  275. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/api.py +0 -0
  276. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/__init__.py +0 -0
  277. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/cube_plot.py +0 -0
  278. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/design_quality.py +0 -0
  279. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/diagnostics.py +0 -0
  280. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/effects.py +0 -0
  281. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/significance.py +0 -0
  282. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/experiments/visualization/plots/square_plot.py +0 -0
  283. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/interactions.py +0 -0
  284. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/mcp_server.py +0 -0
  285. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/monitoring/tools.py +0 -0
  286. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/__init__.py +0 -0
  287. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_adaptive.py +0 -0
  288. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_base.py +0 -0
  289. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_categorical.py +0 -0
  290. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_cv_criteria.py +0 -0
  291. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_impute.py +0 -0
  292. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_limits.py +0 -0
  293. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_null.py +0 -0
  294. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_opls.py +0 -0
  295. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_pca_pls.py +0 -0
  296. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_plsda.py +0 -0
  297. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_prm.py +0 -0
  298. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_procrustes.py +0 -0
  299. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_projection.py +0 -0
  300. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_resampling.py +0 -0
  301. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/_robust.py +0 -0
  302. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/methods.py +0 -0
  303. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/multivariate/tools.py +0 -0
  304. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/py.typed +0 -0
  305. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/regression/_variance_components.py +0 -0
  306. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/regression/methods.py +0 -0
  307. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/__init__.py +0 -0
  308. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/designed.py +0 -0
  309. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/diagnostics.py +0 -0
  310. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/ingest.py +0 -0
  311. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/recipes.py +0 -0
  312. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/tools.py +0 -0
  313. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/sensory/validation.py +0 -0
  314. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/simulation/__init__.py +0 -0
  315. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/simulation/batch.py +0 -0
  316. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/simulation/context.py +0 -0
  317. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/simulation/latent.py +0 -0
  318. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/simulation/model.py +0 -0
  319. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/simulation/recipes.py +0 -0
  320. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/__init__.py +0 -0
  321. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/adapters/__init__.py +0 -0
  322. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/adapters/base.py +0 -0
  323. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/adapters/echarts_adapter.py +0 -0
  324. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/adapters/plotly_adapter.py +0 -0
  325. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/charts/__init__.py +0 -0
  326. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/charts/boxplot.py +0 -0
  327. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/colors.py +0 -0
  328. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/raincloud.py +0 -0
  329. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/themes.py +0 -0
  330. {process_improve-2.0.0 → process_improve-2.2.0}/src/process_improve/visualization/types.py +0 -0
@@ -1,16 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: process-improve
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis.
5
5
  Keywords: Designed Experiments,Design of Experiments,DoE,Optimal Design,Response Surface,Latent Variables,PCA,PLS,Multivariate Data Analysis,Chemometrics,Process Monitoring,Batch data analysis
6
6
  Author: Kevin Dunn
7
7
  Author-email: Kevin Dunn <kgdunn@gmail.com>
8
8
  License-Expression: MIT
9
- Classifier: Development Status :: 4 - Beta
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Science/Research
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.10
14
13
  Classifier: Programming Language :: Python :: 3.11
15
14
  Classifier: Programming Language :: Python :: 3.12
16
15
  Classifier: Programming Language :: Python :: 3.13
@@ -40,31 +39,13 @@ Requires-Dist: openpyxl>=3.1.5 ; extra == 'batch'
40
39
  Requires-Dist: ruptures>=1.1.9 ; extra == 'batch'
41
40
  Requires-Dist: scikit-image>=0.25.2 ; extra == 'batch'
42
41
  Requires-Dist: osqp>=1.0 ; extra == 'control'
43
- Requires-Dist: coverage>=7.13.1 ; extra == 'dev'
44
- Requires-Dist: hypothesis>=6.100 ; extra == 'dev'
45
- Requires-Dist: matplotlib-stubs>=0.3.11 ; extra == 'dev'
46
- Requires-Dist: mypy>=1.19.1,<2.5 ; extra == 'dev'
47
- Requires-Dist: pandas-stubs>=2.3.3.260113 ; extra == 'dev'
48
- Requires-Dist: plotly-stubs>=0.0.6 ; extra == 'dev'
49
- Requires-Dist: pre-commit>=4.5.1 ; extra == 'dev'
50
- Requires-Dist: pytest>=9.0.2 ; extra == 'dev'
51
- Requires-Dist: pytest-benchmark>=4.0.0 ; extra == 'dev'
52
- Requires-Dist: pytest-cov>=7.0.0 ; extra == 'dev'
53
- Requires-Dist: pytest-xdist>=3.8.0 ; extra == 'dev'
54
- Requires-Dist: pydata-sphinx-theme>=0.16.1 ; extra == 'dev'
55
- Requires-Dist: ruff>=0.11.0,<0.17 ; extra == 'dev'
56
- Requires-Dist: sphinx>=8.1.3 ; extra == 'dev'
57
- Requires-Dist: nbsphinx>=0.9.5 ; extra == 'dev'
58
- Requires-Dist: ipykernel>=6.29 ; extra == 'dev'
59
- Requires-Dist: tqdm-stubs>=0.2.1 ; extra == 'dev'
60
- Requires-Dist: watchdog>=6.0.0 ; extra == 'dev'
61
42
  Requires-Dist: numba>=0.63.1 ; extra == 'fast'
62
43
  Requires-Dist: mcp>=2.0 ; extra == 'mcp'
63
44
  Requires-Dist: matplotlib>=3.10.8 ; extra == 'plotting'
64
45
  Requires-Dist: plotly>=6.5.2 ; extra == 'plotting'
65
46
  Requires-Dist: ridgeplot>=0.5.0 ; extra == 'plotting'
66
47
  Requires-Dist: seaborn>=0.13.2 ; extra == 'plotting'
67
- Requires-Python: >=3.10
48
+ Requires-Python: >=3.11
68
49
  Project-URL: Homepage, https://github.com/kgdunn/process-improve
69
50
  Project-URL: Documentation, https://kgdunn.github.io/process-improve/
70
51
  Project-URL: Try it in your browser, https://kgdunn.github.io/process-improve/app/
@@ -75,7 +56,6 @@ Project-URL: Changelog, https://github.com/kgdunn/process-improve/blob/main/CHAN
75
56
  Provides-Extra: all
76
57
  Provides-Extra: batch
77
58
  Provides-Extra: control
78
- Provides-Extra: dev
79
59
  Provides-Extra: fast
80
60
  Provides-Extra: mcp
81
61
  Provides-Extra: plotting
@@ -97,7 +77,7 @@ variable moved, and how sure am I?*
97
77
  [![Python versions](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fkgdunn%2Fprocess-improve%2Fmain%2Fpyproject.toml&label=python)](https://pypi.org/project/process-improve/)
98
78
  [![Downloads](https://static.pepy.tech/badge/process-improve)](https://pepy.tech/project/process-improve)
99
79
  [![Downloads per month](https://static.pepy.tech/badge/process-improve/month)](https://pepy.tech/project/process-improve)
100
- [![CI](https://github.com/kgdunn/process-improve/actions/workflows/run-tests.yml/badge.svg?branch=main&event=push)](https://github.com/kgdunn/process-improve/actions/workflows/run-tests.yml?query=branch%3Amain)
80
+ [![CI](https://github.com/kgdunn/process-improve/actions/workflows/main-status.yml/badge.svg)](https://github.com/kgdunn/process-improve/actions/workflows/run-tests.yml?query=branch%3Amain)
101
81
  [![codecov](https://codecov.io/gh/kgdunn/process-improve/branch/main/graph/badge.svg)](https://codecov.io/gh/kgdunn/process-improve)
102
82
  [![Docs](https://img.shields.io/badge/docs-kgdunn.github.io-blue.svg)](https://kgdunn.github.io/process-improve/)
103
83
  [![License](https://img.shields.io/pypi/l/process-improve.svg)](https://github.com/kgdunn/process-improve/blob/main/LICENSE)
@@ -353,7 +333,7 @@ pip install 'process-improve[fast]' # adds numba (JIT speedups for ba
353
333
  pip install 'process-improve[all]' # everything above
354
334
  ```
355
335
 
356
- Requires Python 3.10 or newer. The core install pulls in `numpy`, `pandas`, `scipy`,
336
+ Requires Python 3.11 or newer. The core install pulls in `numpy`, `pandas`, `scipy`,
357
337
  `scikit-learn`, `statsmodels`, `patsy`, `pydantic`, `pyyaml`, `threadpoolctl` and `tqdm`.
358
338
  Heavier optional surfaces (plotting, Taguchi arrays, batch IO, batch control, the MCP server, numba JIT)
359
339
  live in extras, so a caller who only needs, say, `detect_multivariate_outliers` does not have
@@ -399,6 +379,12 @@ specific version.
399
379
 
400
380
  MIT: see [LICENSE](https://github.com/kgdunn/process-improve/blob/main/LICENSE) for details.
401
381
 
382
+ ## Trademarks
383
+
384
+ OMARS® is a registered trademark of KU Leuven Research & Development. process-improve uses the
385
+ name only for the class of designs defined by Núñez Ares and Goos (2020), and is not affiliated
386
+ with, or endorsed by, KU Leuven, Minitab or Effex.
387
+
402
388
  ## Contributing
403
389
 
404
390
  Bug reports, feature requests and pull requests are welcome. See
@@ -14,7 +14,7 @@ variable moved, and how sure am I?*
14
14
  [![Python versions](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fkgdunn%2Fprocess-improve%2Fmain%2Fpyproject.toml&label=python)](https://pypi.org/project/process-improve/)
15
15
  [![Downloads](https://static.pepy.tech/badge/process-improve)](https://pepy.tech/project/process-improve)
16
16
  [![Downloads per month](https://static.pepy.tech/badge/process-improve/month)](https://pepy.tech/project/process-improve)
17
- [![CI](https://github.com/kgdunn/process-improve/actions/workflows/run-tests.yml/badge.svg?branch=main&event=push)](https://github.com/kgdunn/process-improve/actions/workflows/run-tests.yml?query=branch%3Amain)
17
+ [![CI](https://github.com/kgdunn/process-improve/actions/workflows/main-status.yml/badge.svg)](https://github.com/kgdunn/process-improve/actions/workflows/run-tests.yml?query=branch%3Amain)
18
18
  [![codecov](https://codecov.io/gh/kgdunn/process-improve/branch/main/graph/badge.svg)](https://codecov.io/gh/kgdunn/process-improve)
19
19
  [![Docs](https://img.shields.io/badge/docs-kgdunn.github.io-blue.svg)](https://kgdunn.github.io/process-improve/)
20
20
  [![License](https://img.shields.io/pypi/l/process-improve.svg)](https://github.com/kgdunn/process-improve/blob/main/LICENSE)
@@ -270,7 +270,7 @@ pip install 'process-improve[fast]' # adds numba (JIT speedups for ba
270
270
  pip install 'process-improve[all]' # everything above
271
271
  ```
272
272
 
273
- Requires Python 3.10 or newer. The core install pulls in `numpy`, `pandas`, `scipy`,
273
+ Requires Python 3.11 or newer. The core install pulls in `numpy`, `pandas`, `scipy`,
274
274
  `scikit-learn`, `statsmodels`, `patsy`, `pydantic`, `pyyaml`, `threadpoolctl` and `tqdm`.
275
275
  Heavier optional surfaces (plotting, Taguchi arrays, batch IO, batch control, the MCP server, numba JIT)
276
276
  live in extras, so a caller who only needs, say, `detect_multivariate_outliers` does not have
@@ -316,6 +316,12 @@ specific version.
316
316
 
317
317
  MIT: see [LICENSE](https://github.com/kgdunn/process-improve/blob/main/LICENSE) for details.
318
318
 
319
+ ## Trademarks
320
+
321
+ OMARS® is a registered trademark of KU Leuven Research & Development. process-improve uses the
322
+ name only for the class of designs defined by Núñez Ares and Goos (2020), and is not affiliated
323
+ with, or endorsed by, KU Leuven, Minitab or Effex.
324
+
319
325
  ## Contributing
320
326
 
321
327
  Bug reports, feature requests and pull requests are welcome. See
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "process-improve"
3
- version = "2.0.0"
3
+ version = "2.2.0"
4
4
  description = "Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis."
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
- requires-python = ">=3.10"
7
+ requires-python = ">=3.11"
8
8
  keywords = [
9
9
  "Designed Experiments",
10
10
  "Design of Experiments",
@@ -20,11 +20,10 @@ keywords = [
20
20
  "Batch data analysis",
21
21
  ]
22
22
  classifiers = [
23
- "Development Status :: 4 - Beta",
23
+ "Development Status :: 5 - Production/Stable",
24
24
  "Intended Audience :: Science/Research",
25
25
  "Operating System :: OS Independent",
26
26
  "Programming Language :: Python :: 3",
27
- "Programming Language :: Python :: 3.10",
28
27
  "Programming Language :: Python :: 3.11",
29
28
  "Programming Language :: Python :: 3.12",
30
29
  "Programming Language :: Python :: 3.13",
@@ -75,26 +74,6 @@ all = [
75
74
  "scikit-image>=0.25.2",
76
75
  "seaborn>=0.13.2",
77
76
  ]
78
- dev = [
79
- "coverage>=7.13.1",
80
- "hypothesis>=6.100",
81
- "matplotlib-stubs>=0.3.11",
82
- "mypy>=1.19.1,<2.5",
83
- "pandas-stubs>=2.3.3.260113",
84
- "plotly-stubs>=0.0.6",
85
- "pre-commit>=4.5.1",
86
- "pytest>=9.0.2",
87
- "pytest-benchmark>=4.0.0",
88
- "pytest-cov>=7.0.0",
89
- "pytest-xdist>=3.8.0",
90
- "pydata-sphinx-theme>=0.16.1",
91
- "ruff>=0.11.0,<0.17",
92
- "sphinx>=8.1.3",
93
- "nbsphinx>=0.9.5",
94
- "ipykernel>=6.29",
95
- "tqdm-stubs>=0.2.1",
96
- "watchdog>=6.0.0",
97
- ]
98
77
 
99
78
  [project.scripts]
100
79
  process-improve-mcp = "process_improve.mcp_server:main"
@@ -168,6 +147,7 @@ select = ["ALL"]
168
147
  ignore = [
169
148
  "CPY001",
170
149
  "C408",
150
+ "UP042",
171
151
  "ARG001",
172
152
  "ANN002",
173
153
  "ANN003",
@@ -1,17 +1,16 @@
1
1
  [project]
2
2
  name = "process-improve"
3
- version = "2.0.0"
3
+ version = "2.2.0"
4
4
  description = 'Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis.'
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
- requires-python = ">=3.10"
7
+ requires-python = ">=3.11"
8
8
  keywords = ["Designed Experiments", "Design of Experiments", "DoE", "Optimal Design", "Response Surface", "Latent Variables", "PCA", "PLS", "Multivariate Data Analysis", "Chemometrics", "Process Monitoring", "Batch data analysis"]
9
9
  classifiers = [
10
- "Development Status :: 4 - Beta",
10
+ "Development Status :: 5 - Production/Stable",
11
11
  "Intended Audience :: Science/Research",
12
12
  "Operating System :: OS Independent",
13
13
  "Programming Language :: Python :: 3",
14
- "Programming Language :: Python :: 3.10",
15
14
  "Programming Language :: Python :: 3.11",
16
15
  "Programming Language :: Python :: 3.12",
17
16
  "Programming Language :: Python :: 3.13",
@@ -28,17 +27,18 @@ dependencies = [
28
27
  # MCP server, JIT speedups) live in extras; install
29
28
  # ``process-improve[all]`` to reproduce the pre-1.24.11 closure.
30
29
  "numpy>=2.2.6",
31
- # Both pandas majors are supported. pandas 3 needs Python 3.11, so CI's
32
- # Python 3.10 jobs test pandas 2.3 and the newer Pythons test pandas 3.
30
+ # Both pandas majors are supported. CI's Python 3.11 jobs install the lowest
31
+ # version each dependency allows, so they test pandas 2.3; the newer Pythons
32
+ # test pandas 3.
33
33
  "pandas>=2.3.3",
34
34
  "patsy>=1.0.2",
35
35
  "pydantic>=2.12.5",
36
36
  "pyyaml>=6.0",
37
37
  "scikit-learn>=1.7.2",
38
38
  # Declared directly because generate_omars solves its ILP with
39
- # scipy.optimize.milp (HiGHS). 1.15 is the oldest release CI runs (Python
40
- # 3.10 resolves 1.15.3); older releases bundle HiGHS 1.2, which reports a
41
- # node-limited stop differently and is not tested here.
39
+ # scipy.optimize.milp (HiGHS). 1.15 is the oldest release CI runs (the
40
+ # Python 3.11 jobs resolve 1.15.0); older releases bundle HiGHS 1.2, which
41
+ # reports a node-limited stop differently and is not tested here.
42
42
  "scipy>=1.15",
43
43
  "statsmodels>=0.14.6",
44
44
  # Already required by scikit-learn; declared because the REML fits of the
@@ -108,27 +108,6 @@ all = [
108
108
  "seaborn>=0.13.2",
109
109
  ]
110
110
 
111
- dev = [
112
- "coverage>=7.13.1",
113
- "hypothesis>=6.100",
114
- "matplotlib-stubs>=0.3.11",
115
- "mypy>=1.19.1,<2.5",
116
- "pandas-stubs>=2.3.3.260113",
117
- "plotly-stubs>=0.0.6",
118
- "pre-commit>=4.5.1",
119
- "pytest>=9.0.2",
120
- "pytest-benchmark>=4.0.0",
121
- "pytest-cov>=7.0.0",
122
- "pytest-xdist>=3.8.0",
123
- "pydata-sphinx-theme>=0.16.1",
124
- "ruff>=0.11.0,<0.17", # upper-bound so a future ruff release cannot silently break the lint gate (see mypy)
125
- "sphinx>=8.1.3",
126
- "nbsphinx>=0.9.5",
127
- "ipykernel>=6.29",
128
- "tqdm-stubs>=0.2.1",
129
- "watchdog>=6.0.0",
130
- ]
131
-
132
111
  [project.scripts]
133
112
  process-improve-mcp = "process_improve.mcp_server:main"
134
113
 
@@ -147,6 +126,8 @@ requires = ["uv_build>=0.7.2,<0.14"]
147
126
  build-backend = "uv_build"
148
127
 
149
128
  [dependency-groups]
129
+ # The contributors' tools. A dependency group rather than an extra, so the
130
+ # published wheel does not offer them to users (#513).
150
131
  dev = [
151
132
  "coverage>=7.13.1",
152
133
  "hypothesis>=6.100",
@@ -249,6 +230,10 @@ ignore = [
249
230
 
250
231
  "C408", # Unnecessary <dict/list/tuple> call - rewrite as a literal. Disagree with this.
251
232
 
233
+ # A `(str, Enum)` class turned into `StrEnum` changes what str() and format()
234
+ # return for its members, and these enums are public API.
235
+ "UP042", # Class inherits from both `str` and `enum.Enum`
236
+
252
237
  # Arguments
253
238
  "ARG001", # Unusued argument
254
239
 
@@ -42,8 +42,7 @@ if typing.TYPE_CHECKING:
42
42
 
43
43
  _UNFOLDED_INDEX_ERROR = (
44
44
  "needs the 2-level (tag, sequence) index that process_improve.batch.dict_to_wide produces. "
45
- "When scaling the unfolded matrix yourself, re-attach ``wide.columns`` after MCUVScaler, "
46
- "which drops the multi-level labels."
45
+ "Fit the model to that matrix, or to MCUVScaler's output of it, which keeps the index."
47
46
  )
48
47
 
49
48
 
@@ -203,8 +202,7 @@ def unfolded_contribution_plot(
203
202
  ``t2_contributions`` on :class:`process_improve.batch.BatchPCA` or
204
203
  :class:`process_improve.batch.BatchPLS`, or of the standalone
205
204
  :mod:`process_improve.multivariate` functions on a model fitted to a
206
- :func:`process_improve.batch.dict_to_wide` matrix whose column index
207
- was re-attached after scaling.
205
+ :func:`process_improve.batch.dict_to_wide` matrix, scaled or not.
208
206
  batch_id : Hashable, optional
209
207
  Which batch (row) to plot. Defaults to the first row.
210
208
  by_tag : bool, default=False
@@ -528,7 +528,10 @@ def midcourse_correction( # noqa: PLR0913, PLR0912, PLR0915, C901
528
528
  # bisect each active multiplier down so the achieved statistic lands just
529
529
  # inside its cap instead of far below it (the escalation overshoots).
530
530
  # Each statistic is non-increasing in its own multiplier for this convex
531
- # problem, so the per-coordinate bisection is well posed.
531
+ # problem, so the per-coordinate bisection is well posed. Lowering one
532
+ # multiplier can raise the other statistic, so a bisection step is taken
533
+ # only when both caps still hold: every accepted schedule meets both, and
534
+ # an "ok" status means they hold (#681).
532
535
  mu_spe = mu_t2 = 0.0
533
536
  n_solves = 0
534
537
  u_star = _solve(mu_spe, mu_t2)
@@ -538,6 +541,10 @@ def midcourse_correction( # noqa: PLR0913, PLR0912, PLR0915, C901
538
541
  spe_cap_value = float("inf") if spe_cap is None else float(spe_cap)
539
542
  t2_cap_value = float("inf") if t2_cap is None else float(t2_cap)
540
543
  cap_status = "ok"
544
+
545
+ def _within_caps(spe: float, t2: float) -> bool:
546
+ return spe <= spe_cap_value * (1 + _CAP_REL_TOL) and t2 <= t2_cap_value * (1 + _CAP_REL_TOL)
547
+
541
548
  for _ in range(30):
542
549
  spe_bad = spe_value > spe_cap_value * (1 + _CAP_REL_TOL)
543
550
  t2_bad = t2_value > t2_cap_value * (1 + _CAP_REL_TOL)
@@ -561,12 +568,12 @@ def midcourse_correction( # noqa: PLR0913, PLR0912, PLR0915, C901
561
568
  mid = 0.5 * (low_mu + high_mu)
562
569
  u_try = _solve(mid, mu_t2)
563
570
  n_solves += 1
564
- spe_try, _t2_try = _statistics(u_try)
565
- if spe_try > spe_cap_value * (1 + _CAP_REL_TOL):
571
+ spe_try, t2_try = _statistics(u_try)
572
+ if not _within_caps(spe_try, t2_try):
566
573
  low_mu = mid
567
574
  else:
568
575
  high_mu = mid
569
- u_star, spe_value, t2_value = u_try, spe_try, _t2_try
576
+ u_star, spe_value, t2_value = u_try, spe_try, t2_try
570
577
  if spe_try >= spe_cap_value * (1 - _CAP_REL_TOL):
571
578
  break
572
579
  mu_spe = high_mu
@@ -577,7 +584,7 @@ def midcourse_correction( # noqa: PLR0913, PLR0912, PLR0915, C901
577
584
  u_try = _solve(mu_spe, mid)
578
585
  n_solves += 1
579
586
  spe_try, t2_try = _statistics(u_try)
580
- if t2_try > t2_cap_value * (1 + _CAP_REL_TOL):
587
+ if not _within_caps(spe_try, t2_try):
581
588
  low_mu = mid
582
589
  else:
583
590
  high_mu = mid
@@ -585,9 +592,7 @@ def midcourse_correction( # noqa: PLR0913, PLR0912, PLR0915, C901
585
592
  if t2_try >= t2_cap_value * (1 - _CAP_REL_TOL):
586
593
  break
587
594
  mu_t2 = high_mu
588
- spe_ok = spe_value <= spe_cap_value * (1 + _CAP_REL_TOL)
589
- t2_ok = t2_value <= t2_cap_value * (1 + _CAP_REL_TOL)
590
- if spe_ok and t2_ok:
595
+ if _within_caps(spe_value, t2_value):
591
596
  break
592
597
 
593
598
  scores = b + A_F @ u_star
@@ -610,10 +615,22 @@ def midcourse_correction( # noqa: PLR0913, PLR0912, PLR0915, C901
610
615
  kind = name.split(":", 1)[0]
611
616
  key = "bounds" if kind == "bound" else "rate"
612
617
  typing.cast("list", active[key]).append(name.split(":", 1)[1])
618
+
619
+ def _binding(statistic: str) -> bool:
620
+ """Return whether a cap holds its statistic down: with its multiplier at zero, the statistic breaks it."""
621
+ nonlocal n_solves
622
+ n_solves += 1
623
+ if statistic == "spe":
624
+ return _statistics(_solve(0.0, mu_t2))[0] > spe_cap_value * (1 + _CAP_REL_TOL)
625
+ return _statistics(_solve(mu_spe, 0.0))[1] > t2_cap_value * (1 + _CAP_REL_TOL)
626
+
627
+ # A cap is active where its statistic sits at it (within the tolerance), or above it when it
628
+ # could not be met. Below it, a multiplier left over from the bisection is binding only if
629
+ # dropping it would break the cap: one bisected down because its cap is slack is not (#681).
613
630
  if spe_cap is not None:
614
- active["spe_cap"] = bool(mu_spe > 0 or spe_value >= spe_cap_value * (1 - _CAP_REL_TOL))
631
+ active["spe_cap"] = spe_value >= spe_cap_value * (1 - _CAP_REL_TOL) or (mu_spe > 0 and _binding("spe"))
615
632
  if t2_cap is not None:
616
- active["t2_cap"] = bool(mu_t2 > 0 or t2_value >= t2_cap_value * (1 - _CAP_REL_TOL))
633
+ active["t2_cap"] = t2_value >= t2_cap_value * (1 - _CAP_REL_TOL) or (mu_t2 > 0 and _binding("t2"))
617
634
 
618
635
  return Bunch(
619
636
  mv=mv,
@@ -50,10 +50,12 @@ def _prepare_data( # noqa: C901, PLR0912
50
50
  if phase_col is None:
51
51
  phase_col = "__phase_grouper__"
52
52
 
53
+ # The fake grouping columns are added with `assign`, which returns a new frame: an
54
+ # in-place `insert` added them to the caller's DataFrame as well.
53
55
  if phase_col not in df:
54
56
  # Cannot use "None" or 'nan'; else it won't group in the
55
57
  # groupby([batch_col, phase_col]) statement below.
56
- df.insert(0, phase_col, "NoPhases")
58
+ df = df.assign(**{phase_col: "NoPhases"})
57
59
 
58
60
  # If no batch_col: add a fake column with all the same values
59
61
  if batch_col not in df or batch_col is None:
@@ -61,7 +63,7 @@ def _prepare_data( # noqa: C901, PLR0912
61
63
  if batch_col not in df:
62
64
  # Cannot use "None" or 'nan'; else it won't group in the
63
65
  # groupby([batch_col, phase_col]) statement below.
64
- df.insert(0, batch_col, 1)
66
+ df = df.assign(**{batch_col: 1})
65
67
 
66
68
  if tags is None:
67
69
  tags = list(df.columns)
@@ -226,12 +228,18 @@ def f_iqr(
226
228
  The difference between the 75th percentile and the 25th percentile of a
227
229
  sample this is the 25 % trimmed range, an example of an L - estimator.
228
230
 
229
- See also: f_std
231
+ Missing values are omitted, as ``f_std`` and ``f_robust_mad`` omit them.
232
+
233
+ See also: f_std, f_robust_mad
230
234
  """
231
235
  base_name = "iqr"
236
+
237
+ def _iqr(col: pd.Series) -> float:
238
+ return float(iqr(col, nan_policy="omit"))
239
+
232
240
  prepared, tags, output, _ = _prepare_data(data, tags, batch_col, phase_col)
233
241
  f_names = [(tag + "_" + base_name) for tag in tags]
234
- output = prepared.agg(iqr)
242
+ output = prepared.agg(_iqr)
235
243
  return output.rename(columns=dict(zip(tags, f_names, strict=False)))
236
244
 
237
245
 
@@ -282,8 +290,6 @@ def f_sum(
282
290
 
283
291
  If the x-axis (time) data are evenly-spaced, then this is directly
284
292
  proportional to the area under the trace (curve/trajectory).
285
-
286
- See also: f_cumsum
287
293
  """
288
294
  base_name = "sum"
289
295
  prepared, tags, output, _ = _prepare_data(data, tags, batch_col, phase_col)
@@ -310,6 +316,9 @@ def f_area(
310
316
  The spacing of the x-axis is taken into account, so, this will produce
311
317
  accurate areas if the data are not evenly-spaced in time along the x-axis.
312
318
 
319
+ A datetime or timedelta time axis is measured in seconds, so the area is in the tag's
320
+ units multiplied by seconds. A numeric time axis is used in its own units.
321
+
313
322
  The area is calculated using the trapezoidal rule.
314
323
 
315
324
  See also: f_sum, f_cumsum
@@ -323,7 +332,13 @@ def f_area(
323
332
  for batch_id, this_batch in prepared:
324
333
  # Average width of the base * height. For time series, the average width of the base is
325
334
  # the same as the sampling intervals. Therefore the diff of the index.
326
- half_base_factor = np.diff(this_batch.index)
335
+ time_axis = this_batch.index
336
+ if isinstance(time_axis, pd.DatetimeIndex | pd.TimedeltaIndex):
337
+ # A plain diff gives timedelta64 widths, which made the area a timedelta that a
338
+ # float column cannot hold. Seconds keep it a number in a stated unit.
339
+ half_base_factor = np.asarray(pd.TimedeltaIndex(time_axis[1:] - time_axis[:-1]).total_seconds())
340
+ else:
341
+ half_base_factor = np.diff(time_axis)
327
342
  for tag in tags:
328
343
  # Now the sum makes sense: gets the area under the curve by adding
329
344
  # up the smaller trapezoids: consider the trapezoids as rotated by 90 degrees.
@@ -433,7 +448,7 @@ def f_rupture(
433
448
  ``display`` helper that uses matplotlib; plotting belongs to the caller, and the rest
434
449
  of this project plots with plotly.
435
450
 
436
- See also: f_elbow, f_cross
451
+ See also: f_elbow, f_crossing
437
452
  """
438
453
  default_settings: dict = {"model": "rbf", "penalty": None, "min_size": 2, "jump": 5}
439
454
  if settings:
@@ -779,8 +794,10 @@ def f_crossing( # noqa: PLR0913
779
794
 
780
795
  f_name = tag + "_" + base_name
781
796
 
782
- def _cross_first(x: pd.DataFrame) -> np.ndarray | list:
783
- return cross(x[tag], threshold, direction, only_index=only_index, first_point_only=True)
797
+ def _cross_first(x: pd.DataFrame) -> np.ndarray | list | float:
798
+ crossing = cross(x[tag], threshold, direction, only_index=only_index, first_point_only=True)
799
+ # `cross` returns an empty array when there is no crossing; the documented feature is NaN.
800
+ return np.nan if np.size(crossing) == 0 else crossing
784
801
 
785
802
  # pandas-stubs' DFCallable1 protocol does not include ``np.ndarray`` among the allowed
786
803
  # return types, although groupby.apply accepts array-returning callables at runtime.
@@ -806,7 +823,7 @@ def f_elbow( # noqa: PLR0913
806
823
  The elbow is calculated against whichever variable is given by `x_axis_tag` (usually a time-
807
824
  based tag).
808
825
 
809
- The function returns the *value* on the x-axis where the elbox occurs. Sometimes you might
826
+ The function returns the *value* on the x-axis where the elbow occurs. Sometimes you might
810
827
  want the *index* of the value, so you can also find the corresponding y-axis value. Use
811
828
  `only_index=True` for such cases.
812
829
  """
@@ -582,6 +582,10 @@ _HUBER_CUTOFF = 1.345
582
582
  #: Smallest batch weight :func:`_batch_weights` will return, so no batch is ever silenced.
583
583
  _MIN_BATCH_WEIGHT = 1e-3
584
584
 
585
+ #: Iterations of :func:`batch_dtw` that align every batch to the ``reference_batch``. Each later
586
+ #: iteration aligns to the average trajectory of the iteration before it.
587
+ _ITERATIONS_ON_THE_REFERENCE_BATCH = 3
588
+
585
589
 
586
590
  def _batch_weights(aligned_batches: dict, batch_weighting: str) -> np.ndarray:
587
591
  """
@@ -721,7 +725,7 @@ def one_iteration_dtw(
721
725
  aligned_batches[batch_id] = result
722
726
  successful_alignments += 1
723
727
 
724
- except ValueError as exc: # noqa: PERF203
728
+ except ValueError as exc:
725
729
  # Chain the cause: a band constraint that is too narrow explains itself, and
726
730
  # that explanation is the actionable part. `from None` discarded it.
727
731
  raise ValueError(f"Failed on batch {batch_id}: {exc}") from exc
@@ -818,6 +822,11 @@ def batch_dtw( # noqa: PLR0915
818
822
  "interpolate_method": "cubic", # any scipy.interpolate.interp1d method
819
823
  }
820
824
 
825
+ Every batch is aligned to ``reference_batch`` in the first three iterations. From
826
+ the fourth iteration on, the reference is the average trajectory of the iteration
827
+ before, so the result depends less on which batch was chosen as the reference. The
828
+ number three is fixed, not a setting.
829
+
821
830
  The default settings resample the time axis to 100 points, starting at 0 and ending
822
831
  at 99, so each point is one percent of the batch's duration however long the batch
823
832
  actually ran. Lower the delta for a finer axis (``0.5`` gives 200 points, ``0.25``
@@ -932,7 +941,12 @@ def batch_dtw( # noqa: PLR0915
932
941
  average_batch = None
933
942
  delta_weight: np.floating | np.ndarray = np.linalg.norm(weight_vector)
934
943
  iter_step = 0
935
- while (np.linalg.norm(delta_weight) > settings["tolerance"]) and (iter_step <= settings["maximum_iterations"]):
944
+ # The first iteration runs whatever the tolerance: it is the one that aligns the batches,
945
+ # and everything after the loop reads its results. A tolerance at or above the norm of
946
+ # the unit weights (the square root of the number of columns) used to skip it entirely.
947
+ while iter_step == 0 or (
948
+ (np.linalg.norm(delta_weight) > settings["tolerance"]) and (iter_step <= settings["maximum_iterations"])
949
+ ):
936
950
  if settings["show_progress"]:
937
951
  print(f"Iter = {iter_step} and norm = {np.linalg.norm(delta_weight)}") # noqa: T201
938
952
 
@@ -946,7 +960,7 @@ def batch_dtw( # noqa: PLR0915
946
960
  weight_matrix = np.diag(weight_vector)
947
961
  weight_history = np.vstack((weight_history, weight_vector.copy()))
948
962
 
949
- if iter_step > 3:
963
+ if iter_step > _ITERATIONS_ON_THE_REFERENCE_BATCH:
950
964
  refbatch_sc = average_batch
951
965
 
952
966
  aligned_batches, average_batch = one_iteration_dtw(
@@ -1029,9 +1043,10 @@ def batch_dtw( # noqa: PLR0915
1029
1043
  result.synced = synced_interpolated
1030
1044
  aligned_df_collection.append(synced_interpolated)
1031
1045
 
1032
- # Make the batch_id label consistent
1046
+ # Each frame's batch_id column already holds its own dictionary key. Casting the whole
1047
+ # column to the type of the last key turned an integer key into a string one whenever
1048
+ # the keys were of mixed types, so the caller could no longer look its batch up.
1033
1049
  aligned_df = pd.concat(aligned_df_collection)
1034
- aligned_df["batch_id"] = aligned_df["batch_id"].astype(type(batch_id))
1035
1050
 
1036
1051
  last_average_batch = reverse_scaling(dict(avg=cast("pd.DataFrame", average_batch)), scale_df)["avg"]
1037
1052
  aligned_batch_dfdict = melted_to_dict(aligned_df, batch_id_col="batch_id")
@@ -1083,6 +1098,12 @@ def resample_to_reference(
1083
1098
  -------
1084
1099
  dict
1085
1100
  Batch data, in the standard format.
1101
+
1102
+ Raises
1103
+ ------
1104
+ ValueError
1105
+ If a batch, the reference among them, has fewer than 2 rows: there is nothing to
1106
+ interpolate between.
1086
1107
  """
1087
1108
  default_settings = {
1088
1109
  "interpolate_kind": "cubic", # must be a valid "scipy.interpolate.interp1d" `kind`
@@ -1090,6 +1111,14 @@ def resample_to_reference(
1090
1111
  if settings:
1091
1112
  default_settings.update(settings)
1092
1113
  settings = default_settings
1114
+ # Each batch's time axis is scaled to [0, 1] by dividing by its last row number, which is
1115
+ # 0 for a single row: the division gave NaN and scipy then refused an "all-nan" axis.
1116
+ too_short = {batch_id: batch.shape[0] for batch_id, batch in batches.items() if batch.shape[0] < 2}
1117
+ if too_short:
1118
+ raise ValueError(
1119
+ f"resample_to_reference needs at least 2 rows in every batch, to interpolate between; "
1120
+ f"these batches have fewer (batch: rows): {too_short}."
1121
+ )
1093
1122
  out = {}
1094
1123
  target_time = np.arange(0, batches[reference_batch].shape[0])
1095
1124
  target_time = target_time / target_time[-1]
@@ -26,6 +26,10 @@ def find_elbow_point(x: np.ndarray, y: np.ndarray, max_iter: int = 41) -> int |
26
26
  the median of the accumulated intersection points; the median location is
27
27
  where the intersections should stabilise.
28
28
 
29
+ Both axes are first scaled to span 0 to 1, so that "closest" does not add up
30
+ distances in two different units. The elbow is therefore the same whatever
31
+ units `x` and `y` are in: time in seconds or in hours gives the same index.
32
+
29
33
  Will probably not work well on few data points. If so, try fitting a spline
30
34
  to the raw data and then repeat with the interpolated data.
31
35
 
@@ -81,19 +85,22 @@ def find_elbow_point(x: np.ndarray, y: np.ndarray, max_iter: int = 41) -> int |
81
85
  if len(x) <= 10:
82
86
  raise ValueError("Requires more than 10 values in the vectors (not including missing data).")
83
87
  idx_sort = x.argsort()
84
- x = x[idx_sort]
85
- y = y[idx_sort]
88
+ x = _scale_to_unit_range(x[idx_sort])
89
+ y = _scale_to_unit_range(y[idx_sort])
86
90
  N = x.size
87
91
 
88
92
  int_x_list: list[float] = []
89
93
  int_y_list: list[float] = []
94
+ # On fewer than about 2 * max_iter points the floored window sizes repeat, so each size is
95
+ # fitted once. A repeat still adds its intersection again, which keeps the median unchanged.
96
+ intersections: dict[int, tuple] = {}
90
97
  for i in np.floor(np.linspace(0, int(N / 2 - start) + 1, max_iter)):
91
98
  idx = int(start + i)
92
- lo_x, lo_y = x[0:idx], y[0:idx]
93
- hi_x, hi_y = x[-idx:], y[-idx:]
94
- lft = fit_robust_lm(lo_x, lo_y)
95
- rgt = fit_robust_lm(hi_x, hi_y)
96
- int_x, int_y = find_line_intersection(lft[1], lft[0], rgt[1], rgt[0])
99
+ if idx not in intersections:
100
+ lft = fit_robust_lm(x[0:idx], y[0:idx])
101
+ rgt = fit_robust_lm(x[-idx:], y[-idx:])
102
+ intersections[idx] = find_line_intersection(lft[1], lft[0], rgt[1], rgt[0])
103
+ int_x, int_y = intersections[idx]
97
104
  int_x_list.append(int_x)
98
105
  int_y_list.append(int_y)
99
106
 
@@ -110,6 +117,12 @@ def find_elbow_point(x: np.ndarray, y: np.ndarray, max_iter: int = 41) -> int |
110
117
  return int(np.argmin(calculate_line_length(mid_x, mid_y, x, y)))
111
118
 
112
119
 
120
+ def _scale_to_unit_range(values: np.ndarray) -> np.ndarray:
121
+ """Shift and scale ``values`` to span 0 to 1; a constant vector is only shifted, to 0."""
122
+ span = float(np.ptp(values))
123
+ return (values - values.min()) / (span if span > 0 else 1.0)
124
+
125
+
113
126
  def find_line_intersection(m1: float, b1: float, m2: float, b2: float) -> tuple:
114
127
  """
115
128
  Find the intersection point of two lines.
@@ -251,7 +251,7 @@ class Settings:
251
251
 
252
252
  @property
253
253
  def max_memory_mb(self) -> int:
254
- """Per-subprocess RSS cap for tool execution (MiB)."""
254
+ """Address space a tool's subprocess may map beyond what its imports mapped (MiB)."""
255
255
  return self._get("max_memory_mb", _read_env_int)
256
256
 
257
257
  @max_memory_mb.setter