jarvisplot 2.1.2__tar.gz → 2.1.4__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 (242) hide show
  1. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/JarvisPLOT.egg-info/PKG-INFO +1 -1
  2. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/JarvisPLOT.egg-info/SOURCES.txt +1 -0
  3. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/PKG-INFO +1 -1
  4. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/corrplot_runtime.py +39 -11
  5. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/layout_runtime.py +22 -2
  6. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/preprocessor.py +13 -10
  7. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/profile_runtime.py +54 -32
  8. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/colors/colormaps.json +15 -0
  9. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/corrplot/diamond.json +2 -1
  10. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/core_runtime.py +46 -0
  11. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/method_notes.yaml +1 -1
  12. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/type-correlation-matrix.yaml +3 -1
  13. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/transform_contracts.py +2 -1
  14. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/utils/cmaps.py +3 -1
  15. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/cap.py +1 -0
  16. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/pyproject.toml +1 -1
  17. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_axis_title.py +26 -0
  18. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_cmaps.py +15 -0
  19. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_corrplot.py +73 -0
  20. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_figure_contracts.py +10 -3
  21. jarvisplot-2.1.4/tests/test_profile_runtime.py +141 -0
  22. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
  23. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/JarvisPLOT.egg-info/entry_points.txt +0 -0
  24. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/JarvisPLOT.egg-info/requires.txt +0 -0
  25. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/JarvisPLOT.egg-info/top_level.txt +0 -0
  26. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/README.md +0 -0
  27. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/__init__.py +0 -0
  28. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/adapters_rect.py +0 -0
  29. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/adapters_ternary.py +0 -0
  30. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/bin_stat_runtime.py +0 -0
  31. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
  32. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/colorbar_runtime.py +0 -0
  33. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/config_runtime.py +0 -0
  34. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/corr_layout.py +0 -0
  35. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/corr_layout_diamond.py +0 -0
  36. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/corr_order.py +0 -0
  37. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/correlation_runtime.py +0 -0
  38. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/data_pipelines.py +0 -0
  39. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/debug_config.py +0 -0
  40. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/density_cell_runtime.py +0 -0
  41. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/design_runtime.py +0 -0
  42. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/dynesty_runtime.py +0 -0
  43. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/figure.py +0 -0
  44. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/figure_types.py +0 -0
  45. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/helper.py +0 -0
  46. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/interp_2d_runtime.py +0 -0
  47. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/interp_natural_neighbor.py +0 -0
  48. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/interp_natural_neighbor_exact.py +0 -0
  49. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/layer_runtime.py +0 -0
  50. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/method_registry.py +0 -0
  51. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/posterior_density_runtime.py +0 -0
  52. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/posterior_hpd.py +0 -0
  53. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/posterior_mesh.py +0 -0
  54. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/preprocessor_runtime.py +0 -0
  55. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/significance_runtime.py +0 -0
  56. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/Figure/style_runtime.py +0 -0
  57. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/__init__.py +0 -0
  58. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/agent_digest.py +0 -0
  59. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/agent_io.py +0 -0
  60. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cache_store.py +0 -0
  61. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/capabilities.py +0 -0
  62. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
  63. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +0 -0
  64. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/rect.json +0 -0
  65. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -0
  66. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -0
  67. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/rect_marginal.json +0 -0
  68. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/rectratio.json +0 -0
  69. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/ternary.json +0 -0
  70. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -0
  71. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
  72. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -0
  73. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
  74. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
  75. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/args.json +0 -0
  76. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/corrplot/matrix.json +0 -0
  77. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/default/output.json +0 -0
  78. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/flowchart/default.json +0 -0
  79. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
  80. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -0
  81. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/gambit/2x1/ternary.json +0 -0
  82. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -0
  83. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
  84. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/flowchart/JarvisHEP.png +0 -0
  85. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/flowchart/calculator.png +0 -0
  86. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/flowchart/inputfile.png +0 -0
  87. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/flowchart/opera.png +0 -0
  88. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/flowchart/outputfile.png +0 -0
  89. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/flowchart/sampler.png +0 -0
  90. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/gambit.png +0 -0
  91. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/icons/gambit_small.png +0 -0
  92. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cards/style_preference.json +0 -0
  93. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cli.py +0 -0
  94. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/cli_help.py +0 -0
  95. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/client.py +0 -0
  96. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/column_demand.py +0 -0
  97. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/column_probe.py +0 -0
  98. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/config.py +0 -0
  99. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/config_address.py +0 -0
  100. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/core.py +0 -0
  101. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/core_assets.py +0 -0
  102. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/data_access.py +0 -0
  103. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/data_loader.py +0 -0
  104. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/data_loader_hdf5.py +0 -0
  105. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/data_loader_runtime.py +0 -0
  106. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/data_loader_summary.py +0 -0
  107. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/dataset_types.py +0 -0
  108. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/diagnostic_guidance.py +0 -0
  109. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/diagnostics.py +0 -0
  110. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/dryrun_runtime.py +0 -0
  111. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/expr_names.py +0 -0
  112. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/fix_apply.py +0 -0
  113. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/flowchart.py +0 -0
  114. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/inner_func.py +0 -0
  115. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/man_catalog.py +0 -0
  116. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/man_methods.py +0 -0
  117. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/man_render_agent.py +0 -0
  118. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/man_render_human.py +0 -0
  119. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/man_transforms.py +0 -0
  120. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/agent-output.yaml +0 -0
  121. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/cli-map.yaml +0 -0
  122. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/data-columns.yaml +0 -0
  123. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/debug-overlay.yaml +0 -0
  124. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/diagnostics.yaml +0 -0
  125. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/dryrun-viz.yaml +0 -0
  126. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/layer-method.yaml +0 -0
  127. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/manifest.yaml +0 -0
  128. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/style-axes.yaml +0 -0
  129. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/type-posterior-2d.yaml +0 -0
  130. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/type-profile-2d.yaml +0 -0
  131. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/validate.yaml +0 -0
  132. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/workflow.yaml +0 -0
  133. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/manual_cards/yaml-root.yaml +0 -0
  134. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/memtrace.py +0 -0
  135. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/method_contracts.py +0 -0
  136. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/render_health.py +0 -0
  137. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/core/common.json +0 -0
  138. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/core/dataset.json +0 -0
  139. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/core/figure.json +0 -0
  140. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/core/layer.json +0 -0
  141. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/core/transform.json +0 -0
  142. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/manifest.json +0 -0
  143. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/bar.json +0 -0
  144. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/barh.json +0 -0
  145. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/contour.json +0 -0
  146. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/contourf.json +0 -0
  147. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/corrplot.json +0 -0
  148. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/dynesty_runplot.json +0 -0
  149. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/errorbar.json +0 -0
  150. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/fill.json +0 -0
  151. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/fill_between.json +0 -0
  152. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/fill_betweenx.json +0 -0
  153. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/hist.json +0 -0
  154. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/hist2d.json +0 -0
  155. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/imshow.json +0 -0
  156. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/jpcontour.json +0 -0
  157. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/jpcontourf.json +0 -0
  158. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/jpfield.json +0 -0
  159. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/pcolor.json +0 -0
  160. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/pcolormesh.json +0 -0
  161. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/plot.json +0 -0
  162. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/quiver.json +0 -0
  163. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/scatter.json +0 -0
  164. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/stairs.json +0 -0
  165. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/step.json +0 -0
  166. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/tricontour.json +0 -0
  167. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/tricontourf.json +0 -0
  168. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/tripcolor.json +0 -0
  169. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/tripcolor_axes.json +0 -0
  170. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/triplot.json +0 -0
  171. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/voronoi.json +0 -0
  172. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/methods/voronoif.json +0 -0
  173. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema/plot-config.json +0 -0
  174. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema_catalog.py +0 -0
  175. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/schema_diagnostics.py +0 -0
  176. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/templates_catalog.py +0 -0
  177. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/utils/__init__.py +0 -0
  178. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/utils/dataframes.py +0 -0
  179. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/utils/expression.py +0 -0
  180. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/utils/interpolator.py +0 -0
  181. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/utils/pathing.py +0 -0
  182. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/validation.py +0 -0
  183. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/__init__.py +0 -0
  184. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/config_cmd.py +0 -0
  185. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/data.py +0 -0
  186. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/doctor.py +0 -0
  187. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/dryrun.py +0 -0
  188. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/explain.py +0 -0
  189. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/man.py +0 -0
  190. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/suggest.py +0 -0
  191. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/template.py +0 -0
  192. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/verbs/validate.py +0 -0
  193. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/jarvisplot/yaml_io.py +0 -0
  194. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/setup.cfg +0 -0
  195. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_agent_digest.py +0 -0
  196. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_agent_envelope.py +0 -0
  197. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_capabilities.py +0 -0
  198. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_card_debug_shape.py +0 -0
  199. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_cli_help.py +0 -0
  200. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_cli_verbs.py +0 -0
  201. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_column_existence.py +0 -0
  202. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_config_addressing.py +0 -0
  203. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_config_edit.py +0 -0
  204. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_config_expand.py +0 -0
  205. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_correlation_runtime.py +0 -0
  206. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_data_access.py +0 -0
  207. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_data_describe.py +0 -0
  208. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_data_head_eval.py +0 -0
  209. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_data_loader_contracts.py +0 -0
  210. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_data_pipelines.py +0 -0
  211. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_debug_config.py +0 -0
  212. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_debug_switches.py +0 -0
  213. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_debug_yaml_override.py +0 -0
  214. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_density_cell_transform.py +0 -0
  215. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_design_runtime.py +0 -0
  216. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_diagnostic_guidance.py +0 -0
  217. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_docs_status_labels.py +0 -0
  218. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_doctor_partial.py +0 -0
  219. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_explain.py +0 -0
  220. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_figure_type_expander.py +0 -0
  221. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_fix_application.py +0 -0
  222. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_flowchart_renderer.py +0 -0
  223. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_imshow_adapter.py +0 -0
  224. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_interp_2d_transform.py +0 -0
  225. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_interp_natural_neighbor.py +0 -0
  226. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_interpolator_contracts.py +0 -0
  227. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_jpcontour_methods.py +0 -0
  228. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_man_cli.py +0 -0
  229. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_method_contracts.py +0 -0
  230. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_parse_data_metadata.py +0 -0
  231. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_pathing_contracts.py +0 -0
  232. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_posterior_hpd.py +0 -0
  233. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_render_health.py +0 -0
  234. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_row_ledger.py +0 -0
  235. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_schema_catalog.py +0 -0
  236. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_suggest.py +0 -0
  237. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_suggest_axes.py +0 -0
  238. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_template_catalog.py +0 -0
  239. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_template_json.py +0 -0
  240. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_ternary_debug.py +0 -0
  241. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_transform_contracts.py +0 -0
  242. {jarvisplot-2.1.2 → jarvisplot-2.1.4}/tests/test_validate_no_matplotlib.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.1.2
3
+ Version: 2.1.4
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -227,6 +227,7 @@ tests/test_method_contracts.py
227
227
  tests/test_parse_data_metadata.py
228
228
  tests/test_pathing_contracts.py
229
229
  tests/test_posterior_hpd.py
230
+ tests/test_profile_runtime.py
230
231
  tests/test_render_health.py
231
232
  tests/test_row_ledger.py
232
233
  tests/test_schema_catalog.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.1.2
3
+ Version: 2.1.4
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -45,10 +45,15 @@ GLYPHS = ("circle", "square", "ellipse", "color", "shade", "pie", "number")
45
45
 
46
46
  _TRIANGLES = ("full", "upper", "lower")
47
47
 
48
+ #: What the tint may be told to mean. `selected` is the author's own grouping,
49
+ #: `alternate` the figure's own, `none` nothing.
50
+ _STRIPES = ("alternate", "selected", "none")
51
+
48
52
  #: Formals resolved at config time. They arrive because the user wrote them
49
53
  #: in the layer, and are dropped here rather than forwarded to matplotlib.
50
54
  _CONFIG_TIME = ("order", "hclust.method", "addrect", "col", "tl.pos", "tl.cex",
51
- "tl.col", "tl.srt", "tl.offset", "cl.cex", "edge.numbers.cex")
55
+ "tl.col", "tl.srt", "tl.offset", "cl.cex", "edge.numbers.cex",
56
+ "stripe.variables")
52
57
 
53
58
 
54
59
  def _pop(kwargs: dict, name: str, default=None):
@@ -270,14 +275,25 @@ def _notch_vertices(n: int, side: str = "left", diag: bool = False):
270
275
  )
271
276
 
272
277
 
273
- def _shaded_variables(n: int, blocks=None) -> np.ndarray:
274
- """Which variables carry the tint: alternate blocks, or alternate names.
278
+ def _shaded_variables(n: int, blocks=None, selected=None) -> np.ndarray:
279
+ """Which variables carry the tint.
280
+
281
+ Three answers, and they say different things. ``selected`` is an explicit
282
+ set of positions -- the author naming the variables that matter, so the
283
+ tint is *their* grouping and nothing is inferred. Otherwise it is read off
284
+ the figure: alternate **blocks** when the clustering cut some, so the
285
+ shading says where one group ends and the next begins (which is what the
286
+ boxes were for), and alternate **names** when it did not.
275
287
 
276
- A block is a run of adjacent positions cut from the clustering tree, so
277
- tinting every other one makes the shading say where one group ends and the
278
- next begins -- which is what the boxes were for.
288
+ ``selected`` arrives as positions, not names: the order is settled at
289
+ config time and only the order knows which position a name ended up at.
279
290
  """
280
291
  shaded = np.zeros(int(n), dtype=bool)
292
+ if selected is not None:
293
+ for position in selected:
294
+ if 0 <= int(position) < int(n):
295
+ shaded[int(position)] = True
296
+ return shaded
281
297
  if blocks:
282
298
  for position, (start, end) in enumerate(blocks):
283
299
  if position % 2 == 0:
@@ -728,9 +744,21 @@ def draw_corrplot(ax, **kwargs):
728
744
  # Diamond only. See the stripe block below for why it is not decoration.
729
745
  stripe = _pop(kwargs, "stripe", "alternate")
730
746
  stripe = "none" if stripe is False else str(stripe).strip().lower()
731
- if stripe not in ("alternate", "none"):
747
+ if stripe not in _STRIPES:
748
+ raise ValueError(
749
+ "corrplot stripe must be one of {}; got {!r}. `selected` takes the "
750
+ "variables to tint in `stripe.variables`.".format(
751
+ ", ".join(_STRIPES), stripe
752
+ )
753
+ )
754
+ # Positions, resolved from `stripe.variables` at config time -- see
755
+ # `core_runtime._corr_shaded_positions`. The names cannot be resolved here
756
+ # because `order` has already moved them.
757
+ shaded_positions = kwargs.pop("__corr_shaded__", None)
758
+ if stripe == "selected" and shaded_positions is None:
732
759
  raise ValueError(
733
- "corrplot stripe must be alternate or none; got {!r}.".format(stripe)
760
+ "corrplot stripe: selected needs stripe.variables -- the names to "
761
+ "tint. Without them the tint would say nothing."
734
762
  )
735
763
  stripe_color = _pop(kwargs, "stripe.col", "#EFEFEF")
736
764
  # Diamond only. See `_edge_rules`: the rotated matrix has no top or bottom
@@ -883,7 +911,7 @@ def draw_corrplot(ax, **kwargs):
883
911
  edge_color, clip, zorder + 40,
884
912
  )
885
913
 
886
- if diamond and stripe == "alternate" and np.any(drawn):
914
+ if diamond and stripe != "none" and np.any(drawn):
887
915
  # Every other variable's cells, tinted. Not decoration: a variable's
888
916
  # pairs run away from its name as a V, and at 40 names a reader
889
917
  # following one of those arms has nothing to hold on to. The band is
@@ -899,7 +927,7 @@ def draw_corrplot(ax, **kwargs):
899
927
  #
900
928
  # The union of those variables' cells rather than one band each, so a
901
929
  # cell where two bands cross is filled once and the tint stays even.
902
- shaded = _shaded_variables(n, blocks)
930
+ shaded = _shaded_variables(n, blocks, shaded_positions)
903
931
  band = shaded[index_x.astype(int)] | shaded[index_y.astype(int)]
904
932
  band &= drawn
905
933
  if np.any(band):
@@ -1091,7 +1119,7 @@ def draw_corrplot(ax, **kwargs):
1091
1119
  # block above), so a box around each of them would be the same fact drawn
1092
1120
  # twice, in the heaviest ink on the figure. `addrect` still decides *what*
1093
1121
  # the blocks are; it just stops drawing them.
1094
- if blocks and not (diamond and stripe != "none"):
1122
+ if blocks and not (diamond and stripe == "alternate"):
1095
1123
  for start, end in blocks:
1096
1124
  if diamond:
1097
1125
  ax.add_patch(
@@ -533,15 +533,35 @@ def apply_manual_ticks(fig, ax_obj, which: str, ticks_cfg: dict):
533
533
  if pos is None:
534
534
  return
535
535
  target = ax_obj.ax if hasattr(ax_obj, "ax") else ax_obj
536
+
537
+ # Matplotlib's set_xticks/set_yticks expands the view limits when a
538
+ # requested tick lies outside the current range. That is surprising for
539
+ # Jarvis-PLOT cards, where an explicit frame.xlim/ylim is authoritative
540
+ # (e.g. ylim: [200, 400] with a stale tick at 600). Preserve the range
541
+ # only when autoscaling was already disabled; otherwise leave the normal
542
+ # autoscaling behaviour untouched for axes whose limits are data-driven.
543
+ axis = str(which).lower()
544
+ if axis not in {"x", "y"}:
545
+ return
546
+ get_lim = target.get_xlim if axis == "x" else target.get_ylim
547
+ set_lim = target.set_xlim if axis == "x" else target.set_ylim
548
+ get_autoscale = (
549
+ target.get_autoscalex_on if axis == "x" else target.get_autoscaley_on
550
+ )
551
+ original_lim = get_lim()
552
+ autoscale_was_on = get_autoscale()
536
553
  try:
537
- if which == "x":
554
+ if axis == "x":
538
555
  target.set_xticks(pos)
539
556
  if labs is not None:
540
557
  target.set_xticklabels(labs)
541
- elif which == "y":
558
+ elif axis == "y":
542
559
  target.set_yticks(pos)
543
560
  if labs is not None:
544
561
  target.set_yticklabels(labs)
545
562
  except Exception as e:
546
563
  if fig.logger:
547
564
  fig.logger.warning(f"Manual ticks apply failed on {which}-axis: {e}")
565
+ finally:
566
+ if not autoscale_was_on:
567
+ set_lim(original_lim)
@@ -851,7 +851,7 @@ class DataPreprocessor:
851
851
  eff_transform = self._effective_transform(source, transform)
852
852
  payload = {
853
853
  "kind": "pipeline",
854
- "algo": "pregrid-v6",
854
+ "algo": "pregrid-v7-objective-finite-voronoi",
855
855
  "source": self._source_token(source, combine=combine),
856
856
  "transform": eff_transform,
857
857
  "combine": str(combine),
@@ -900,12 +900,11 @@ class DataPreprocessor:
900
900
  if isinstance(item, Mapping):
901
901
  src = item.get("source")
902
902
  tf = self._effective_transform(src, item.get("transform"))
903
- tokens.append(
904
- {
905
- "source": self._source_token(src),
906
- "transform": tf,
907
- }
908
- )
903
+ token = {"source": self._source_token(src), "transform": tf}
904
+ profile_sig = self._runtime_profile_signature(tf)
905
+ if profile_sig is not None:
906
+ token["profile_signature"] = profile_sig
907
+ tokens.append(token)
909
908
  payload = {
910
909
  "name": layer.get("name"),
911
910
  "axes": layer.get("axes"),
@@ -928,7 +927,7 @@ class DataPreprocessor:
928
927
  cfg = deepcopy(cfg)
929
928
  else:
930
929
  cfg = {"value": cfg}
931
- tokens.append({"kind": "profile", "cfg": cfg})
930
+ tokens.append({"kind": "profile", "algo": "objective-finite-voronoi-v2", "cfg": cfg})
932
931
  return tokens
933
932
 
934
933
  def _runtime_profile_signature(self, transform: Any) -> Optional[str]:
@@ -1332,13 +1331,17 @@ class DataPreprocessor:
1332
1331
 
1333
1332
  @staticmethod
1334
1333
  def _preprofile_profile_cfg(profile_cfg: Any) -> Any:
1335
- # Preprofile cache identity is intentionally coordinates-only:
1336
- # runtime profile method/bin/objective changes must reuse same preprofile.
1334
+ # Runtime method/bin can share a preprofile, but objective and pregrid
1335
+ # controls change which source rows survive and must enter its identity.
1337
1336
  if not isinstance(profile_cfg, Mapping):
1338
1337
  return {}
1339
1338
  slim: Dict[str, Any] = {}
1340
1339
  if "coordinates" in profile_cfg:
1341
1340
  slim["coordinates"] = deepcopy(profile_cfg["coordinates"])
1341
+ slim["objective"] = str(profile_cfg.get("objective", "max")).lower()
1342
+ for key in ("pregrid", "pregrid_bin"):
1343
+ if key in profile_cfg:
1344
+ slim[key] = deepcopy(profile_cfg[key])
1342
1345
  return slim
1343
1346
 
1344
1347
  def _split_prebuild_transform(self, transform: Any):
@@ -451,11 +451,26 @@ def profiling(df, prof, logger):
451
451
  else:
452
452
  z = df["z"]
453
453
 
454
- logger.debug("After loading profiling x, y, z. ")
454
+ if logger:
455
+ logger.debug("After loading profiling x, y, z. ")
455
456
 
456
- x = np.asarray(x)
457
- y = np.asarray(y)
458
- z = np.asarray(z)
457
+ x = np.asarray(x, dtype=float)
458
+ y = np.asarray(y, dtype=float)
459
+ z = np.asarray(z, dtype=float)
460
+ # A single invalid objective used to poison min/max(z), making every
461
+ # normalized dz NaN and silently disabling the z-aware exclusion rule.
462
+ valid = np.isfinite(x) & np.isfinite(y) & np.isfinite(z)
463
+ for cfg, values in ((xcfg, x), (ycfg, y), (zcfg, z)):
464
+ if str(cfg.get("scale", "linear")).lower() == "log":
465
+ valid &= values > 0
466
+ if not np.all(valid):
467
+ df = df.iloc[np.flatnonzero(valid)]
468
+ x, y, z = x[valid], y[valid], z[valid]
469
+ if not len(z):
470
+ out = df.copy()
471
+ for cfg, values, default in ((xcfg, x, "x"), (ycfg, y, "y"), (zcfg, z, "z")):
472
+ out[cfg.get("name", default)] = values
473
+ return out
459
474
 
460
475
  if grid == "ternary":
461
476
  xlim = xcfg.get("lim", [0, 1])
@@ -535,20 +550,12 @@ def profiling(df, prof, logger):
535
550
  },
536
551
  )
537
552
 
538
- if xscale == "log":
539
- xx = (np.log(xx) - np.log(xlim[0])) / (np.log(xlim[1]) - np.log(xlim[0]))
540
- else:
541
- xx = (xx - xlim[0]) / (xlim[1] - xlim[0])
542
-
543
- if yscale == "log":
544
- yy = (np.log(yy) - np.log(ylim[0])) / (np.log(ylim[1]) - np.log(ylim[0]))
545
- else:
546
- yy = (yy - ylim[0]) / (ylim[1] - ylim[0])
547
-
548
- if zscale == "log":
549
- zz = (np.log(zz) - np.log(zlim[0])) / (np.log(zlim[1]) - np.log(zlim[0]))
550
- else:
551
- zz = (zz - zlim[0]) / (zlim[1] - zlim[0])
553
+ xx = _normalize_profile_axis(xx, xlim, xscale)
554
+ yy = _normalize_profile_axis(yy, ylim, yscale)
555
+ zz = _normalize_profile_axis(zz, zlim, zscale)
556
+ # Empty-grid seeds are processed last and carry NaN in the output. Give
557
+ # them a finite exclusion coordinate, including for logarithmic z.
558
+ zz[len(order):] = 2.0 if obj == "min" else -1.0
552
559
 
553
560
  msk = profile_bridson_sorted(xx, yy, zz, radius)
554
561
  keep_pos = np.flatnonzero(msk).astype(np.int64, copy=False)
@@ -557,6 +564,22 @@ def profiling(df, prof, logger):
557
564
  keep_source = keep_pos[keep_pos < source_count]
558
565
  keep_grid = keep_pos[keep_pos >= source_count] - source_count
559
566
 
567
+ # Greedy thinning only chooses the spatial partition: a high-valued point
568
+ # can be suppressed by an earlier seed, then a worse seed can survive on
569
+ # the other side of it. Re-profile *all* input rows within the nearest-seed
570
+ # (Voronoi) cells so those suppressed candidates still contribute. Select
571
+ # the entire winning source row, not just its z at another row's x/y.
572
+ if keep_source.size:
573
+ from scipy.spatial import cKDTree
574
+
575
+ source_xy = np.column_stack([xx[:source_count], yy[:source_count]])
576
+ tree = cKDTree(source_xy[keep_source])
577
+ _, cell = tree.query(source_xy)
578
+ winners = np.full(keep_source.size, source_count, dtype=np.int64)
579
+ # Input is already ordered best first for either max or min.
580
+ np.minimum.at(winners, cell, np.arange(source_count, dtype=np.int64))
581
+ keep_source = np.sort(winners[winners < source_count])
582
+
560
583
  source_rows = order[keep_source]
561
584
  source_x = x[source_rows]
562
585
  source_y = y[source_rows]
@@ -594,7 +617,7 @@ def profiling(df, prof, logger):
594
617
 
595
618
 
596
619
  def _preprofiling(df, prof, logger):
597
- """Lightweight pre-profiling for cache prebuild."""
620
+ """Keep the requested finite extremum per pre-bin for cache prebuild."""
598
621
 
599
622
  def _norm(arr, lim, scale):
600
623
  arr = np.asarray(arr, dtype=float)
@@ -765,14 +788,14 @@ def _preprofiling(df, prof, logger):
765
788
 
766
789
  xnorm = _norm(xvals, xlim, xscale)
767
790
  ynorm = _norm(yvals, ylim, yscale)
768
- valid = np.isfinite(xnorm) & np.isfinite(ynorm)
791
+ valid = np.isfinite(xnorm) & np.isfinite(ynorm) & np.isfinite(zvals)
769
792
  if not np.any(valid):
770
793
  if logger:
771
- logger.warning("Preprofiling got no finite points; returning original dataframe.")
772
- out = df.copy(deep=False)
773
- out[xind] = xvals
774
- out[yind] = yvals
775
- out[zind] = zvals
794
+ logger.warning("Preprofiling got no finite points; returning an empty dataframe.")
795
+ out = df.iloc[:0].copy()
796
+ out[xind] = xvals[:0]
797
+ out[yind] = yvals[:0]
798
+ out[zind] = zvals[:0]
776
799
  return out
777
800
 
778
801
  xv = np.clip(xnorm[valid], 0.0, 1.0 - 1e-12)
@@ -783,20 +806,19 @@ def _preprofiling(df, prof, logger):
783
806
 
784
807
  idx_src = np.flatnonzero(valid).astype(np.int64)
785
808
  zraw = np.asarray(zvals, dtype=float)[valid]
786
- zmax = np.where(np.isfinite(zraw), zraw, -np.inf)
787
- zmin = np.where(np.isfinite(zraw), zraw, np.inf)
788
809
  tmp = pd.DataFrame(
789
810
  {
790
811
  "__cell__": cell,
791
- "__zmax__": zmax,
792
- "__zmin__": zmin,
812
+ "__z__": zraw,
793
813
  "__pos__": idx_src,
794
814
  }
795
815
  )
796
816
 
797
- loc_max = tmp.groupby("__cell__", sort=False)["__zmax__"].idxmax().to_numpy(dtype=np.int64, copy=False)
798
- loc_min = tmp.groupby("__cell__", sort=False)["__zmin__"].idxmin().to_numpy(dtype=np.int64, copy=False)
799
- loc_all = np.unique(np.concatenate([loc_max, loc_min], axis=0))
817
+ if str(prof.get("objective", "max")).lower() == "min":
818
+ loc = tmp.groupby("__cell__", sort=False)["__z__"].idxmin()
819
+ else:
820
+ loc = tmp.groupby("__cell__", sort=False)["__z__"].idxmax()
821
+ loc_all = np.sort(loc.to_numpy(dtype=np.int64, copy=False))
800
822
  keep_pos = tmp.iloc[loc_all]["__pos__"].to_numpy(dtype=np.int64, copy=False)
801
823
  reduced = df.iloc[keep_pos].copy()
802
824
  reduced[xind] = xvals[keep_pos]
@@ -95,6 +95,21 @@
95
95
  [1.0, "#575AA6"]
96
96
  ]
97
97
  },
98
+ {
99
+ "name": "Jpurples",
100
+ "type": "linear",
101
+ "colors": [
102
+ [0.000, "#FFFFFF"],
103
+ [0.125, "#EFEDF5"],
104
+ [0.250, "#DADAEB"],
105
+ [0.375, "#BCBDDC"],
106
+ [0.500, "#9E9AC8"],
107
+ [0.625, "#807CBA"],
108
+ [0.750, "#6950A3"],
109
+ [0.875, "#53268F"],
110
+ [1.000, "#3F007D"]
111
+ ]
112
+ },
98
113
  {
99
114
  "name": "SpectralB",
100
115
  "type": "linear",
@@ -498,7 +498,8 @@
498
498
  "type": {"type": "enum", "values": ["upper", "lower"], "default": "upper", "forbidden": ["full"]},
499
499
  "diag": {"type": "boolean", "default": true},
500
500
  "side": {"type": "enum", "values": ["left", "right"], "default": "right"},
501
- "stripe": {"type": "enum", "values": ["alternate", "none"], "default": "alternate", "false_alias": "none"},
501
+ "stripe": {"type": "enum", "values": ["alternate", "selected", "none"], "default": "alternate", "false_alias": "none"},
502
+ "stripe.variables": {"type": "list_of_strings", "default": null, "requires": {"stripe": "selected"}},
502
503
  "order": {"type": "enum", "values": ["original", "AOE", "FPC", "hclust", "alphabet"], "default": "original"},
503
504
  "hclust.method": {"type": "enum", "values": ["single", "complete", "average", "weighted", "mcquitty", "median", "centroid", "ward.D", "ward.D2"], "default": "complete"},
504
505
  "addrect": {"type": "integer_or_null", "default": null, "requires": {"order": "hclust"}},
@@ -590,6 +590,47 @@ def _solve_corr_diamond(card, info, style, columns, axes_name):
590
590
  return geom, solved
591
591
 
592
592
 
593
+ def _corr_shaded_positions(style: Mapping, columns, name: str):
594
+ """`stripe.variables` resolved against the order the figure ended up in.
595
+
596
+ It has to happen here and not at draw time. The renderer sees positions,
597
+ and which position a variable holds is `order`'s answer, settled a moment
598
+ ago -- a name resolved against the *unordered* columns would tint a
599
+ different variable on every reordering and never say so.
600
+
601
+ A name that is not in the matrix is an error rather than a silent miss, for
602
+ the same reason: a tint that quietly did nothing is indistinguishable from
603
+ a tint that was not asked for.
604
+ """
605
+ stripe = style.get("stripe", "alternate")
606
+ stripe = "none" if stripe is False else str(stripe or "").strip().lower()
607
+ wanted = style.get("stripe.variables")
608
+ if wanted is None:
609
+ return None
610
+ if stripe != "selected":
611
+ raise ValueError(
612
+ "figure '{}' gives corrplot stripe.variables with stripe: {}. The "
613
+ "names are only read by stripe: selected; leaving them here would "
614
+ "draw the other tint and say nothing about them.".format(name, stripe)
615
+ )
616
+ if isinstance(wanted, str) or not isinstance(wanted, (list, tuple, set)):
617
+ raise ValueError(
618
+ "figure '{}' gives corrplot stripe.variables: {!r}. It takes a "
619
+ "list of variable names.".format(name, wanted)
620
+ )
621
+ at = {str(column): position for position, column in enumerate(columns)}
622
+ missing = [str(v) for v in wanted if str(v) not in at]
623
+ if missing:
624
+ raise ValueError(
625
+ "figure '{}' asks corrplot to tint {}, which {} not in the matrix. "
626
+ "The variables drawn are: {}.".format(
627
+ name, ", ".join(missing), "is" if len(missing) == 1 else "are",
628
+ ", ".join(str(c) for c in columns),
629
+ )
630
+ )
631
+ return sorted(at[str(v)] for v in wanted)
632
+
633
+
593
634
  def _as_corr_bool(value, default: bool = False) -> bool:
594
635
  if value is None:
595
636
  return default
@@ -1000,6 +1041,11 @@ def _prebuild_one(core, info: dict, card: Mapping) -> None:
1000
1041
  # but placed and sized by the solve.
1001
1042
  layer["style"]["__corr_number_mm__"] = geom.number_pad_mm
1002
1043
  layer["style"]["__corr_number_pt__"] = geom.number_size_pt
1044
+ # `stripe.variables` names variables; the renderer draws positions, and
1045
+ # only the order settled above knows which is which.
1046
+ shaded = _corr_shaded_positions(style, columns, info.get("name", "?"))
1047
+ if shaded is not None:
1048
+ layer["style"]["__corr_shaded__"] = shaded
1003
1049
 
1004
1050
  if blocks:
1005
1051
  # The boxes are cuts of the tree the order came from, so they are
@@ -62,7 +62,7 @@ corrplot:
62
62
  - "Reserved: only the [corrplot, matrix] or [corrplot, diamond] style card provides its axes (axcorr / axccorr)."
63
63
  - "No coordinates needed — x_index / y_index / rho are the transform's own output columns."
64
64
  - "style takes R's formals: method (circle|square|ellipse|color|shade|pie|number), type, diag, glyph.scale, outline, addgrid.col, plus the card's own ellipse.scale."
65
- - "Use [corrplot, matrix] for the square matrix; [corrplot, diamond] keeps one triangle, rotates it 45 degrees, and adds side: left|right (default right, which puts the triangle on the left of the page) plus stripe: alternate|none."
65
+ - "Use [corrplot, matrix] for the square matrix; [corrplot, diamond] keeps one triangle, rotates it 45 degrees, and adds side: left|right (default right, which puts the triangle on the left of the page) plus stripe: alternate|selected|none. stripe: selected takes the variables to tint in stripe.variables, resolved against the solved order; diag: true|false is the self-correlation on the diagonal, on by default."
66
66
  - "order (original|AOE|FPC|hclust|alphabet) and addrect are resolved before the figure is built, then written into the transform's `columns`."
67
67
  - "sig.level switches on the significance test; unlike R no p.mat is needed, because the table carries n per pair."
68
68
  - "The glyph is sized from the solved cell and the coefficient text is back-solved from it, so both survive the 170 mm width clamp."
@@ -45,7 +45,9 @@ human:
45
45
  `style: [corrplot, diamond]` picks the other card: the same matrix as
46
46
  one triangle turned 45 degrees, names in a plain column of horizontal
47
47
  text, each variable's pairs a V rooted at its own name. Same formals,
48
- plus `side: left | right` (default `right`, the triangle on the left of the page) and `stripe: alternate | none`.
48
+ plus `side: left | right` (default `right`, the triangle on the left of the page) and `stripe: alternate | selected | none`
49
+ (`selected` names the variables in `stripe.variables`), plus
50
+ `diag: true | false` for the self-correlation on the diagonal.
49
51
  - kind: yaml
50
52
  title: Minimal figure
51
53
  lexer: yaml
@@ -499,7 +499,8 @@ TRANSFORM_CONTRACTS: dict[str, dict[str, Any]] = {
499
499
  "description": (
500
500
  "Optional coarse pre-binning before Bridson/grid profile "
501
501
  "(large tables). Mapping: {bin, enable}; false disables; "
502
- "omit for auto-prebin from row count."
502
+ "omit for auto-prebin from row count. Keeps one finite "
503
+ "extremum per cell according to objective (max/min)."
503
504
  ),
504
505
  "properties": {
505
506
  "bin": {
@@ -40,7 +40,9 @@ def _register(name: str, cmap: Colormap, force: bool) -> bool:
40
40
  cmap.name = name
41
41
  except Exception:
42
42
  pass
43
- if "override" in sig.parameters:
43
+ if "force" in sig.parameters:
44
+ kwargs["force"] = bool(force)
45
+ elif "override" in sig.parameters:
44
46
  kwargs["override"] = bool(force)
45
47
  except Exception:
46
48
  # If signature introspection fails, try safest call (cmap only).
@@ -170,6 +170,7 @@ _CMAP_DESCRIPTIONS = {
170
170
  "gambit_cmap": "Continuous black → blue → cyan → yellow map.",
171
171
  "jarvis_rainbow": "Continuous purple → blue → cyan → green → orange → red map.",
172
172
  "jarvis_rainbow2": "Continuous red → yellow → green → blue map.",
173
+ "Jpurples": "Matplotlib Purples-style continuous map with a pure-white zero endpoint.",
173
174
  "SpectralB": "Spectral-style continuous map from red through green to blue.",
174
175
  "RdBuB": "Diverging red → white → blue map.",
175
176
  "chrisB": "Diverging black → red → white → blue → navy map.",
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "JarvisPLOT"
7
- version = "2.1.2"
7
+ version = "2.1.4"
8
8
  description = "Jarvis-PLOT: YAML-driven plotting engine"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -257,3 +257,29 @@ def test_side_axes_honour_manual_tick_positions():
257
257
  assert [t.get_text() for t in ax.ax.get_yticklabels()] == list("abcde")
258
258
  finally:
259
259
  plt.close(raw_fig)
260
+
261
+
262
+ def test_manual_ticks_do_not_expand_explicit_axis_limits():
263
+ """Out-of-range manual ticks must not override an explicit frame limit."""
264
+ import matplotlib
265
+
266
+ matplotlib.use("Agg")
267
+ import matplotlib.pyplot as plt
268
+
269
+ from jarvisplot.Figure.layout_runtime import apply_manual_ticks
270
+
271
+ raw_fig, ax = plt.subplots(figsize=(3.3, 3.0))
272
+ ax.set_ylim(200, 400)
273
+ apply_manual_ticks(
274
+ SimpleNamespace(logger=_logger()),
275
+ ax,
276
+ "y",
277
+ {
278
+ "positions": [200, 300, 400, 500, 600],
279
+ "labels": ["200", "300", "400", "500", "600"],
280
+ },
281
+ )
282
+
283
+ assert ax.get_ylim() == pytest.approx((200.0, 400.0))
284
+ assert list(ax.get_yticks()) == [200, 300, 400, 500, 600]
285
+ plt.close(raw_fig)
@@ -4,6 +4,7 @@ import json
4
4
  from pathlib import Path
5
5
 
6
6
  import matplotlib as mpl
7
+ import numpy as np
7
8
 
8
9
  from jarvisplot.utils import cmaps
9
10
 
@@ -40,3 +41,17 @@ def test_official_tab5_is_registered_as_five_discrete_tab10_colors():
40
41
  )
41
42
  assert spec["type"] == "listed"
42
43
  assert len(spec["colors"]) == 5
44
+
45
+
46
+ def test_jpurples_matches_purples_style_with_a_pure_white_zero_endpoint():
47
+ summary = cmaps.register_from_json(COLORMAPS_JSON, force=True)
48
+
49
+ assert "Jpurples" in summary["registered"]
50
+ assert "Jpurples_r" in summary["registered"]
51
+
52
+ jpurples = mpl.colormaps["Jpurples"]
53
+ purples = mpl.colormaps["Purples"]
54
+
55
+ assert mpl.colors.to_hex(jpurples(0.0), keep_alpha=False) == "#ffffff"
56
+ for value in (0.125, 0.25, 0.5, 0.75, 1.0):
57
+ assert np.allclose(jpurples(value), purples(value), atol=1 / 255)