process-improve 1.82.0__tar.gz → 1.83.2__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 (217) hide show
  1. {process_improve-1.82.0 → process_improve-1.83.2}/PKG-INFO +1 -1
  2. {process_improve-1.82.0 → process_improve-1.83.2}/pyproject.toml +1 -1
  3. {process_improve-1.82.0 → process_improve-1.83.2}/pyproject.toml.orig +1 -1
  4. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_mbpls.py +131 -29
  5. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_pca.py +20 -12
  6. {process_improve-1.82.0 → process_improve-1.83.2}/README.md +0 -0
  7. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/__init__.py +0 -0
  8. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/_extras.py +0 -0
  9. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/_linalg.py +0 -0
  10. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/_random.py +0 -0
  11. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/_remote_data.py +0 -0
  12. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/__init__.py +0 -0
  13. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/_batch_monitor.py +0 -0
  14. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/_batch_pca.py +0 -0
  15. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/_batch_plots.py +0 -0
  16. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/_batch_pls.py +0 -0
  17. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/_common.py +0 -0
  18. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/_online.py +0 -0
  19. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/alignment_helpers.py +0 -0
  20. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/control.py +0 -0
  21. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/data_input.py +0 -0
  22. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/datasets.py +0 -0
  23. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/features.py +0 -0
  24. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/plotting.py +0 -0
  25. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/preprocessing.py +0 -0
  26. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/batch/tools.py +0 -0
  27. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/bivariate/__init__.py +0 -0
  28. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/bivariate/_elbow_peak.py +0 -0
  29. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/bivariate/methods.py +0 -0
  30. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/bivariate/tools.py +0 -0
  31. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/chemistry/__init__.py +0 -0
  32. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/chemistry/preprocessing.py +0 -0
  33. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/config.py +0 -0
  34. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/batch/batch-fake-data.csv +0 -0
  35. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/batch/details.txt +0 -0
  36. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/batch/dryer.csv +0 -0
  37. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/batch/nylon.csv +0 -0
  38. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/experiments/boilingpot.csv +0 -0
  39. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/experiments/golf.csv +0 -0
  40. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/experiments/pollutant.csv +0 -0
  41. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/experiments/solar.csv +0 -0
  42. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/experiments/test_doe1.csv +0 -0
  43. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/monitoring/batch-yield-and-purity.csv +0 -0
  44. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/monitoring/rubber-colour.csv +0 -0
  45. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/C.csv +0 -0
  46. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/Hotellings_T2_A3.csv +0 -0
  47. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/Hotellings_T2_A6.csv +0 -0
  48. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/LDPE.csv +0 -0
  49. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/P.csv +0 -0
  50. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/R.csv +0 -0
  51. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/T.csv +0 -0
  52. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/U.csv +0 -0
  53. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/W.csv +0 -0
  54. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/LDPE/Yhat_A6.csv +0 -0
  55. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/cheddar-cheese.csv +0 -0
  56. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/dtu-pectin/NOTICE +0 -0
  57. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/dtu-pectin/extraction1.csv +0 -0
  58. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/dtu-pectin/extraction2.csv +0 -0
  59. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/dtu-pectin/extraction3.csv +0 -0
  60. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/dtu-pectin/ftir1.csv +0 -0
  61. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/dtu-pectin/ftir2.csv +0 -0
  62. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/dtu-pectin/ftir3.csv +0 -0
  63. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/kamyr.csv +0 -0
  64. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/solvents.csv +0 -0
  65. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tablet-spectra.csv +0 -0
  66. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/SOURCE +0 -0
  67. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group1.csv +0 -0
  68. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group2.csv +0 -0
  69. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group3.csv +0 -0
  70. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group4.csv +0 -0
  71. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group5.csv +0 -0
  72. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/process_conditions.csv +0 -0
  73. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group1.csv +0 -0
  74. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group2.csv +0 -0
  75. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group3.csv +0 -0
  76. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group4.csv +0 -0
  77. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group5.csv +0 -0
  78. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/datasets/multivariate/tpls-pyphi/quality_indicators.csv +0 -0
  79. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/__init__.py +0 -0
  80. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/__init__.py +0 -0
  81. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/_shared.py +0 -0
  82. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/box_cox.py +0 -0
  83. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/curvature.py +0 -0
  84. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/diagnostics.py +0 -0
  85. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/lack_of_fit.py +0 -0
  86. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/lenth.py +0 -0
  87. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/model_selection.py +0 -0
  88. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/ols_extractors.py +0 -0
  89. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_analyses/prediction.py +0 -0
  90. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_desirability.py +0 -0
  91. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_lm.py +0 -0
  92. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_moment_aberration.py +0 -0
  93. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/__init__.py +0 -0
  94. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/analyze_experiment.py +0 -0
  95. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/augment_design.py +0 -0
  96. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/create_factorial_design.py +0 -0
  97. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/doe_knowledge.py +0 -0
  98. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/evaluate_design.py +0 -0
  99. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/fit_linear_model.py +0 -0
  100. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/generate_design.py +0 -0
  101. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/optimize_responses.py +0 -0
  102. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/recommend_strategy.py +0 -0
  103. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/trade_off_table.py +0 -0
  104. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/_tools/visualize_doe.py +0 -0
  105. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/analysis.py +0 -0
  106. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/augment.py +0 -0
  107. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/datasets.py +0 -0
  108. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs.py +0 -0
  109. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_factorial.py +0 -0
  110. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_mixture.py +0 -0
  111. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_omars.py +0 -0
  112. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_omars_ilp.py +0 -0
  113. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_optimal.py +0 -0
  114. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_response_surface.py +0 -0
  115. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_screening.py +0 -0
  116. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/designs_utils.py +0 -0
  117. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/evaluate.py +0 -0
  118. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/factor.py +0 -0
  119. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/__init__.py +0 -0
  120. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/api.py +0 -0
  121. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/data/concepts.yaml +0 -0
  122. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/data/decision_rules.yaml +0 -0
  123. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/data/design_types.yaml +0 -0
  124. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/data/diagnostics.yaml +0 -0
  125. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/engine.py +0 -0
  126. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/knowledge/models.py +0 -0
  127. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/models.py +0 -0
  128. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/omars.py +0 -0
  129. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/omars_trade_off.py +0 -0
  130. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/optimal.py +0 -0
  131. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/optimization.py +0 -0
  132. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/simulations.py +0 -0
  133. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/strategy/__init__.py +0 -0
  134. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/strategy/budget.py +0 -0
  135. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/strategy/domain_templates.py +0 -0
  136. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/strategy/engine.py +0 -0
  137. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/strategy/models.py +0 -0
  138. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/structures.py +0 -0
  139. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/tools.py +0 -0
  140. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/trade_off.py +0 -0
  141. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/__init__.py +0 -0
  142. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/api.py +0 -0
  143. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/__init__.py +0 -0
  144. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/cube_plot.py +0 -0
  145. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/design_quality.py +0 -0
  146. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/diagnostics.py +0 -0
  147. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/effects.py +0 -0
  148. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/optimization_plots.py +0 -0
  149. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/registry.py +0 -0
  150. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/significance.py +0 -0
  151. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/square_plot.py +0 -0
  152. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/experiments/visualization/plots/surfaces.py +0 -0
  153. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/interactions.py +0 -0
  154. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/mcp_server.py +0 -0
  155. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/monitoring/__init__.py +0 -0
  156. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/monitoring/control_charts.py +0 -0
  157. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/monitoring/metrics.py +0 -0
  158. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/monitoring/tools.py +0 -0
  159. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/__init__.py +0 -0
  160. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_adaptive.py +0 -0
  161. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_base.py +0 -0
  162. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_common.py +0 -0
  163. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_diagnostics.py +0 -0
  164. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_limits.py +0 -0
  165. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_mbpca.py +0 -0
  166. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_nipals.py +0 -0
  167. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_null.py +0 -0
  168. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_opls.py +0 -0
  169. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_pca_pls.py +0 -0
  170. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_pls.py +0 -0
  171. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_preprocessing.py +0 -0
  172. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_projection.py +0 -0
  173. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_resampling.py +0 -0
  174. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/_tpls.py +0 -0
  175. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/methods.py +0 -0
  176. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/plots.py +0 -0
  177. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/multivariate/tools.py +0 -0
  178. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/py.typed +0 -0
  179. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/recipes.py +0 -0
  180. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/regression/__init__.py +0 -0
  181. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/regression/_robust_regression.py +0 -0
  182. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/regression/methods.py +0 -0
  183. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/regression/tools.py +0 -0
  184. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/__init__.py +0 -0
  185. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/analysis.py +0 -0
  186. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/designed.py +0 -0
  187. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/diagnostics.py +0 -0
  188. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/ingest.py +0 -0
  189. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/mam.py +0 -0
  190. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/panel.py +0 -0
  191. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/recipes.py +0 -0
  192. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/tools.py +0 -0
  193. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/sensory/validation.py +0 -0
  194. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/simulation/__init__.py +0 -0
  195. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/simulation/batch.py +0 -0
  196. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/simulation/context.py +0 -0
  197. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/simulation/model.py +0 -0
  198. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/simulation/recipes.py +0 -0
  199. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/simulation/tools.py +0 -0
  200. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/tool_safety.py +0 -0
  201. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/tool_spec.py +0 -0
  202. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/univariate/__init__.py +0 -0
  203. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/univariate/metrics.py +0 -0
  204. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/univariate/tools.py +0 -0
  205. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/__init__.py +0 -0
  206. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/adapters/__init__.py +0 -0
  207. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/adapters/base.py +0 -0
  208. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/adapters/echarts_adapter.py +0 -0
  209. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/adapters/plotly_adapter.py +0 -0
  210. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/charts/__init__.py +0 -0
  211. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/charts/boxplot.py +0 -0
  212. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/colors.py +0 -0
  213. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/raincloud.py +0 -0
  214. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/spec.py +0 -0
  215. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/themes.py +0 -0
  216. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/tools.py +0 -0
  217. {process_improve-1.82.0 → process_improve-1.83.2}/src/process_improve/visualization/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: process-improve
3
- Version: 1.82.0
3
+ Version: 1.83.2
4
4
  Summary: Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis.
5
5
  Keywords: Designed Experiments,Latent Variables,PCA,PLS,Multivariate Data Analysis,Batch data analysis
6
6
  Author: Kevin Dunn
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "process-improve"
3
- version = "1.82.0"
3
+ version = "1.83.2"
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"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "process-improve"
3
- version = "1.82.0"
3
+ version = "1.83.2"
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"
@@ -39,6 +39,63 @@ except ImportError: # pragma: no cover - exercised via env-without-plotly
39
39
  logger = logging.getLogger(__name__)
40
40
 
41
41
 
42
+ def _stacked_super_weights(
43
+ block_weights: dict[str, np.ndarray],
44
+ super_weight: np.ndarray,
45
+ sqrt_kb: dict[str, float],
46
+ block_names: Sequence[str],
47
+ ) -> np.ndarray:
48
+ r"""Give the super score's weight on every variable, stacked over the blocks in ``block_names`` order.
49
+
50
+ Written out over the variables rather than over the blocks,
51
+
52
+ .. math::
53
+ t_i = \frac{1}{\mathbf{w}_s' \mathbf{w}_s} \sum_b w_{s,b}
54
+ \frac{\mathbf{x}_{ib}' \mathbf{w}_b}{\sqrt{K_b}}
55
+ = \frac{\mathbf{x}_i' \mathbf{g}}{\mathbf{w}_s' \mathbf{w}_s},
56
+ \qquad
57
+ \mathbf{g} = \big[\, w_{s,b}\, \mathbf{w}_b / \sqrt{K_b} \,\big]_b
58
+
59
+ the super score is one linear combination of all the variables of all the blocks.
60
+ ``g`` is that combination.
61
+ """
62
+ return np.concatenate([super_weight[b] * block_weights[name] / sqrt_kb[name] for b, name in enumerate(block_names)])
63
+
64
+
65
+ def _pooled_super_score(
66
+ x_def: dict[str, np.ndarray],
67
+ stacked_weights: np.ndarray,
68
+ super_weight: np.ndarray,
69
+ block_names: Sequence[str],
70
+ ) -> np.ndarray:
71
+ r"""Score every row by one masked regression of the whole row onto the stacked weights.
72
+
73
+ The alternative is to score each block on its own and add the per-block scores up. On a
74
+ complete row the two give the same number, but they part company as soon as a row has missing
75
+ cells: a block observed in one variable out of twenty still hands a score built on that one
76
+ variable into the sum, weighted as though it were as well determined as the others. Regressing
77
+ the whole row at once instead lets every cell the row does have carry its share, wherever in
78
+ the blocks that cell sits.
79
+
80
+ The ratio is rescaled by :math:`\mathbf{g}'\mathbf{g}` so a complete row reproduces the
81
+ weighted sum of block scores exactly, which is what leaves every complete-data model
82
+ unchanged. A row with nothing observed under a non-zero weight has no score: it comes back
83
+ as NaN.
84
+ """
85
+ stacked_x = np.hstack([x_def[name] for name in block_names])
86
+ observed = ~np.isnan(stacked_x)
87
+ numerator = np.nan_to_num(stacked_x) @ stacked_weights
88
+ denominator = observed @ (stacked_weights**2)
89
+ scale = float(stacked_weights @ stacked_weights) / _nz(float(super_weight @ super_weight))
90
+ usable = denominator > 0
91
+ return np.where(usable, numerator / np.where(usable, denominator, 1.0) * scale, np.nan)
92
+
93
+
94
+ def _rows_with_data(values: np.ndarray) -> np.ndarray:
95
+ """Report, for each row, whether it has at least one observed cell."""
96
+ return np.any(~np.isnan(values), axis=1)
97
+
98
+
42
99
  class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
43
100
  r"""Multi-block PLS (hierarchical / superblock formulation).
44
101
 
@@ -75,6 +132,14 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
75
132
  complete (slower than ``"dense"`` but produces equivalent
76
133
  results).
77
134
 
135
+ With missing data the super score of each row is estimated by one masked regression of
136
+ the whole row onto the stacked block weights, rather than by adding up a score per block.
137
+ The two are the same number on a complete row, so no fitted model changes; on an
138
+ incomplete row the pooled form lets every observed cell carry its share, wherever in the
139
+ blocks it sits, instead of letting a block seen in one variable speak as loudly as a block
140
+ seen in twenty. A row with nothing observed in one block is therefore still scored, from
141
+ the blocks it does have; only a row observed in no block at all is refused.
142
+
78
143
  missing_data_settings : dict or None, default=None
79
144
  Settings for the iterative ``"nipals"`` path. Keys: ``md_tol``
80
145
  (convergence tolerance on the score-vector change between
@@ -101,7 +166,8 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
101
166
  y_preproc_ : MCUVScaler
102
167
  Preprocessor used on Y.
103
168
  super_scores_ : pd.DataFrame, shape (n_samples, n_components)
104
- Super-block (consensus) X-scores ``T``.
169
+ Super-block (consensus) X-scores ``T``. Finite for every row that has at least one
170
+ observed cell, in any block.
105
171
  super_y_scores_ : pd.DataFrame, shape (n_samples, n_components)
106
172
  Super-block Y-scores ``U``.
107
173
  super_weights_ : pd.DataFrame, shape (n_blocks, n_components)
@@ -114,7 +180,9 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
114
180
  Variable-importance in projection for each X-block, indexed by
115
181
  block name.
116
182
  block_scores_ : dict[str, pd.DataFrame]
117
- Per-block X-scores ``t_b``, each shape ``(n_samples, n_components)``.
183
+ Per-block X-scores ``t_b``, each shape ``(n_samples, n_components)``. NaN for a row with
184
+ nothing observed in that block: the block has no score of its own there, and reporting
185
+ zero would place the row at the block's average instead.
118
186
  block_weights_ : dict[str, pd.DataFrame]
119
187
  Per-block X-weights ``w_b``, each shape ``(K_b, n_components)``.
120
188
  Each column has unit norm.
@@ -122,7 +190,8 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
122
190
  Per-block X-loadings ``p_b`` (used for deflation), each shape
123
191
  ``(K_b, n_components)``.
124
192
  block_spe_ : dict[str, pd.DataFrame]
125
- Per-block squared prediction error per sample and component.
193
+ Per-block squared prediction error per sample and component. NaN where the row has
194
+ nothing observed in that block, for the same reason as ``block_scores_``.
126
195
  block_hotellings_t2_ : dict[str, pd.DataFrame]
127
196
  Per-block cumulative Hotelling's T^2 per sample and component.
128
197
  block_vip_ : dict[str, pd.Series]
@@ -306,10 +375,12 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
306
375
  raise ValueError("Tolerance should not be too large.")
307
376
  if not settings["md_tol"] > epsqrt**1.95:
308
377
  raise ValueError("Tolerance must exceed machine precision.")
309
- # Degeneracy guards: any column or any (block, row) entirely NaN
310
- # leaves the masked NIPALS denominator at zero, which would
311
- # silently produce a spurious score or loading. Refuse the fit
312
- # rather than coerce the user into a misleading result.
378
+ # Degeneracy guards. A column with nothing in it leaves the masked NIPALS denominator
379
+ # at zero for that variable's loading, so it is refused. A *row* with nothing in one
380
+ # block is allowed: the super score is estimated from the cells that row has in the
381
+ # other blocks, which is the multiblock case of an observation missing one whole
382
+ # analysis. Only a row with nothing observed in any block carries no information at
383
+ # all, and that one is refused.
313
384
  for name in self.block_names_:
314
385
  values = X[name].values
315
386
  col_all_nan = np.all(np.isnan(values), axis=0)
@@ -318,13 +389,15 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
318
389
  raise ValueError(
319
390
  f"Block '{name}' has columns with all values missing: {bad}. Drop these columns before fitting."
320
391
  )
321
- row_all_nan = np.all(np.isnan(values), axis=1)
322
- if np.any(row_all_nan):
323
- bad_rows = np.where(row_all_nan)[0].tolist()
324
- raise ValueError(
325
- f"Block '{name}' has rows with all values missing at positions {bad_rows}. "
326
- "Drop these observations or impute them before fitting."
327
- )
392
+ observed_anywhere = np.zeros(n_samples, dtype=bool)
393
+ for name in self.block_names_:
394
+ observed_anywhere |= _rows_with_data(X[name].values)
395
+ if not np.all(observed_anywhere):
396
+ bad_rows = np.where(~observed_anywhere)[0].tolist()
397
+ raise ValueError(
398
+ f"Rows at positions {bad_rows} have all values missing in every X-block. "
399
+ "Drop these observations or impute them before fitting."
400
+ )
328
401
  y_values = y.values
329
402
  y_col_all_nan = np.all(np.isnan(y_values), axis=0)
330
403
  if np.any(y_col_all_nan):
@@ -348,6 +421,9 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
348
421
 
349
422
  # Algorithmic block weighting: X_b / sqrt(K_b)
350
423
  sqrt_kb = {name: float(np.sqrt(self.block_widths_[name])) for name in self.block_names_}
424
+ # A row with nothing observed in a block has no score *for that block*; it still has a
425
+ # super score, estimated from the blocks it does have.
426
+ block_has_data = {name: _rows_with_data(x_blocks_pp[name]) for name in self.block_names_}
351
427
 
352
428
  # Storage (numpy arrays during fit; wrapped in pandas at the end)
353
429
  super_scores_np = np.zeros((n_samples, n_components))
@@ -425,7 +501,7 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
425
501
  t_b = quick_regress(x_def[name], w_b.reshape(-1, 1)).flatten() / sqrt_kb[name]
426
502
  local_w[name] = w_b
427
503
  local_t[name] = t_b
428
- t_b_summary[:, b_idx] = t_b
504
+ t_b_summary[:, b_idx] = np.where(block_has_data[name], t_b, np.nan)
429
505
  else:
430
506
  for b_idx, name in enumerate(self.block_names_):
431
507
  w_b = x_def[name].T @ u_a / _nz(float(u_a @ u_a))
@@ -435,9 +511,25 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
435
511
  local_t[name] = t_b
436
512
  t_b_summary[:, b_idx] = t_b
437
513
 
438
- w_s = t_b_summary.T @ u_a / _nz(float(u_a @ u_a))
514
+ if algo == "nipals":
515
+ # Masked, so a block this row has nothing in does not vote on the super weight.
516
+ w_s = quick_regress(t_b_summary, u_a.reshape(-1, 1)).flatten()
517
+ else:
518
+ w_s = t_b_summary.T @ u_a / _nz(float(u_a @ u_a))
439
519
  w_s = w_s / _nz(float(np.linalg.norm(w_s)))
440
- t_super = t_b_summary @ w_s / _nz(float(w_s @ w_s))
520
+ # One masked regression of the whole row onto the stacked weights, rather than a
521
+ # weighted sum of per-block scores: identical on complete rows, and on a row with
522
+ # missing cells it uses every cell the row has instead of one score per block.
523
+ t_super = _pooled_super_score(
524
+ x_def,
525
+ _stacked_super_weights(local_w, w_s, sqrt_kb, self.block_names_),
526
+ w_s,
527
+ self.block_names_,
528
+ )
529
+ # A row observed only where the weights are zero has no score for this component:
530
+ # place it at the centre so the iteration stays finite. The guard above has already
531
+ # refused a row with nothing observed anywhere.
532
+ t_super = np.nan_to_num(t_super)
441
533
  if algo == "nipals":
442
534
  t_super_col = t_super.reshape(-1, 1)
443
535
  c_a = quick_regress(y_def, t_super_col).flatten()
@@ -468,7 +560,7 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
468
560
  x_def[name] = x_def[name] - np.outer(t_super, p_b)
469
561
  block_loadings_np[name][:, a] = p_b
470
562
  block_weights_np[name][:, a] = local_w[name]
471
- block_scores_np[name][:, a] = local_t[name]
563
+ block_scores_np[name][:, a] = np.where(block_has_data[name], local_t[name], np.nan)
472
564
  y_def = y_def - np.outer(t_super, c_a)
473
565
 
474
566
  super_scores_np[:, a] = t_super
@@ -490,7 +582,9 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
490
582
  1 - ssq_remain_per_var / np.where(ssq_x_init_per_var[name] > 0, ssq_x_init_per_var[name], 1.0),
491
583
  np.nan,
492
584
  )
493
- block_spe_np[name][:, a] = np.sqrt(np.nansum(x_def[name] ** 2, axis=1))
585
+ block_spe_np[name][:, a] = np.where(
586
+ block_has_data[name], np.sqrt(np.nansum(x_def[name] ** 2, axis=1)), np.nan
587
+ )
494
588
  ssq_y_remain_per_var = np.nansum(y_def**2, axis=0)
495
589
  r2_y_cum[a] = 1 - np.sum(ssq_y_remain_per_var) / ssq_y_init if ssq_y_init > 0 else np.nan
496
590
  r2_y_var_cum[:, a] = np.where(
@@ -694,10 +788,13 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
694
788
 
695
789
  sqrt_kb = {name: float(np.sqrt(self.block_widths_[name])) for name in self.block_names_}
696
790
  for a in range(int(component) - 1):
697
- t_b_row = np.column_stack(
698
- [x_def[name] @ self.block_weights_[name].values[:, a] / sqrt_kb[name] for name in self.block_names_]
791
+ w_s = self.super_weights_.values[:, a]
792
+ weights = {name: self.block_weights_[name].values[:, a] for name in self.block_names_}
793
+ t_super = np.nan_to_num(
794
+ _pooled_super_score(
795
+ x_def, _stacked_super_weights(weights, w_s, sqrt_kb, self.block_names_), w_s, self.block_names_
796
+ )
699
797
  )
700
- t_super = t_b_row @ self.super_weights_.values[:, a]
701
798
  for name in self.block_names_:
702
799
  p_b = self.block_loadings_[name].values[:, a]
703
800
  x_def[name] = x_def[name] - np.outer(t_super, p_b)
@@ -974,19 +1071,24 @@ class MBPLS(_HotellingsT2LimitMixin, RegressorMixin, BaseEstimator):
974
1071
  block_scores: dict[str, np.ndarray] = {name: np.zeros((n_new, n_components)) for name in self.block_names_}
975
1072
 
976
1073
  x_def = {name: x_pp[name].copy() for name in self.block_names_}
1074
+ block_has_data = {name: _rows_with_data(x_pp[name]) for name in self.block_names_}
977
1075
  for a in range(n_components):
978
- t_b_row = np.zeros((n_new, len(self.block_names_)))
979
- for b_idx, name in enumerate(self.block_names_):
1076
+ weights = {}
1077
+ for name in self.block_names_:
980
1078
  w_b = self.block_weights_[name].values[:, a]
981
- t_b = x_def[name] @ w_b / sqrt_kb[name]
982
- block_scores[name][:, a] = t_b
983
- t_b_row[:, b_idx] = t_b
1079
+ weights[name] = w_b
1080
+ t_b = quick_regress(x_def[name], w_b.reshape(-1, 1)).flatten() / sqrt_kb[name]
1081
+ block_scores[name][:, a] = np.where(block_has_data[name], t_b, np.nan)
984
1082
  w_s = self.super_weights_.values[:, a]
985
- t_super = t_b_row @ w_s
1083
+ # The same pooled regression the fit uses, so a projected score matches a fitted one.
1084
+ t_super = _pooled_super_score(
1085
+ x_def, _stacked_super_weights(weights, w_s, sqrt_kb, self.block_names_), w_s, self.block_names_
1086
+ )
986
1087
  super_scores[:, a] = t_super
1088
+ t_super_finite = np.nan_to_num(t_super)
987
1089
  for name in self.block_names_:
988
1090
  p_b = self.block_loadings_[name].values[:, a]
989
- x_def[name] = x_def[name] - np.outer(t_super, p_b)
1091
+ x_def[name] = x_def[name] - np.outer(t_super_finite, p_b)
990
1092
 
991
1093
  component_names = list(range(1, n_components + 1))
992
1094
  super_scores_df = pd.DataFrame(super_scores, index=sample_index, columns=component_names)
@@ -156,6 +156,10 @@ def _pca_ekf_press( # noqa: PLR0913, PLR0915, PLR0912, C901
156
156
  X = np.asarray(X, dtype=float)
157
157
  n, p = X.shape
158
158
  rng = np.random.default_rng(random_state)
159
+ # A cell the caller never measured has no true value to predict, so it is
160
+ # never held out and never scored. It still has to be filled for the SVD,
161
+ # which cannot see a NaN, so EM imputes it alongside the held-out cells.
162
+ observed = ~np.isnan(X)
159
163
 
160
164
  total_folds = n_folds * n_repeats
161
165
  # NaN, not zero: a fold that holds out no cells has no PRESS, and leaving
@@ -176,20 +180,21 @@ def _pca_ekf_press( # noqa: PLR0913, PLR0915, PLR0912, C901
176
180
  press_input_units = np.zeros(max_components)
177
181
  fold_counter = 0
178
182
 
179
- n_cells = n * p
183
+ observed_positions = np.flatnonzero(observed)
184
+ n_cells = observed_positions.size
180
185
  fold_size = n_cells // n_folds
181
186
 
182
187
  for _ in range(n_repeats):
183
- # Assign cells to folds via a balanced random permutation so every
184
- # fold has ~n*p/n_folds cells (not all the same column, not the
185
- # same row).
188
+ # Assign the observed cells to folds via a balanced random permutation
189
+ # so every fold has ~n_cells/n_folds cells (not all the same column,
190
+ # not the same row). Unmeasured cells stay at -1 and match no fold.
186
191
  perm = rng.permutation(n_cells)
187
- fold = np.empty(n_cells, dtype=np.int64)
192
+ flat_fold = np.full(n * p, -1, dtype=np.int64)
188
193
  for k in range(n_folds):
189
194
  start = k * fold_size
190
195
  end = (k + 1) * fold_size if k < n_folds - 1 else n_cells
191
- fold[perm[start:end]] = k
192
- fold = fold.reshape(n, p)
196
+ flat_fold[observed_positions[perm[start:end]]] = k
197
+ fold = flat_fold.reshape(n, p)
193
198
 
194
199
  for k in range(n_folds):
195
200
  mask = fold == k # (n, p) bool, True where the cell is held out
@@ -203,8 +208,11 @@ def _pca_ekf_press( # noqa: PLR0913, PLR0915, PLR0912, C901
203
208
  # scales the matrix it fits, and recomputes nothing inside EM.
204
209
  col_centre = np.zeros(p)
205
210
  col_scale = np.ones(p)
211
+ # Held out this fold, or never measured: both are unknown to the
212
+ # fit and both are filled by EM below. Only ``mask`` is scored.
213
+ impute = mask | ~observed
206
214
  for j in range(p):
207
- in_fold = X[~mask[:, j], j]
215
+ in_fold = X[~impute[:, j], j]
208
216
  if in_fold.size > 1:
209
217
  col_centre[j] = float(in_fold.mean())
210
218
  if scale_inside_folds:
@@ -218,7 +226,7 @@ def _pca_ekf_press( # noqa: PLR0913, PLR0915, PLR0912, C901
218
226
  # ``scale_inside_folds=True``, and the same value under False).
219
227
  Xtr = X.copy()
220
228
  for j in range(p):
221
- m_j = mask[:, j]
229
+ m_j = impute[:, j]
222
230
  if m_j.any():
223
231
  Xtr[m_j, j] = col_centre[j]
224
232
 
@@ -239,7 +247,7 @@ def _pca_ekf_press( # noqa: PLR0913, PLR0915, PLR0912, C901
239
247
  # re-expressed in different units dominates the norm and changes
240
248
  # how many EM iterations are taken (#546). Under
241
249
  # scale_inside_folds=False the divisor is 1 throughout.
242
- prev_held = Xa[mask] / cell_scale
250
+ prev_held = Xa[impute] / np.broadcast_to(col_scale, X.shape)[impute]
243
251
  for _iteration in range(n_iter):
244
252
  if scale_inside_folds:
245
253
  # Centre and scale by the FIXED in-fold constants; the
@@ -260,8 +268,8 @@ def _pca_ekf_press( # noqa: PLR0913, PLR0915, PLR0912, C901
260
268
  rank = min(a, S.shape[0])
261
269
  recon_centred = (Xc @ Vt[:rank].T) @ Vt[:rank]
262
270
  recon = recon_centred + col_mean
263
- Xa[mask] = recon[mask]
264
- held = Xa[mask] / cell_scale
271
+ Xa[impute] = recon[impute]
272
+ held = Xa[impute] / np.broadcast_to(col_scale, X.shape)[impute]
265
273
  delta = np.linalg.norm(held - prev_held)
266
274
  scale = max(1.0, float(np.linalg.norm(prev_held)))
267
275
  if delta < tol * scale: