jarvisplot 2.1.1__tar.gz → 2.1.3__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 (241) hide show
  1. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/PKG-INFO +1 -1
  2. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/PKG-INFO +1 -1
  3. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corr_layout_diamond.py +9 -0
  4. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corrplot_runtime.py +242 -37
  5. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/corrplot/diamond.json +4 -3
  6. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/core_runtime.py +57 -2
  7. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/method_notes.yaml +1 -1
  8. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/type-correlation-matrix.yaml +3 -1
  9. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/pyproject.toml +1 -1
  10. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_corrplot.py +391 -0
  11. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/SOURCES.txt +0 -0
  12. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
  13. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/entry_points.txt +0 -0
  14. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/requires.txt +0 -0
  15. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/top_level.txt +0 -0
  16. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/README.md +0 -0
  17. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/__init__.py +0 -0
  18. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/adapters_rect.py +0 -0
  19. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/adapters_ternary.py +0 -0
  20. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/bin_stat_runtime.py +0 -0
  21. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
  22. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/colorbar_runtime.py +0 -0
  23. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/config_runtime.py +0 -0
  24. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corr_layout.py +0 -0
  25. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corr_order.py +0 -0
  26. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/correlation_runtime.py +0 -0
  27. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/data_pipelines.py +0 -0
  28. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/debug_config.py +0 -0
  29. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/density_cell_runtime.py +0 -0
  30. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/design_runtime.py +0 -0
  31. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/dynesty_runtime.py +0 -0
  32. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/figure.py +0 -0
  33. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/figure_types.py +0 -0
  34. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/helper.py +0 -0
  35. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/interp_2d_runtime.py +0 -0
  36. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/interp_natural_neighbor.py +0 -0
  37. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/interp_natural_neighbor_exact.py +0 -0
  38. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/layer_runtime.py +0 -0
  39. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/layout_runtime.py +0 -0
  40. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/method_registry.py +0 -0
  41. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/posterior_density_runtime.py +0 -0
  42. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/posterior_hpd.py +0 -0
  43. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/posterior_mesh.py +0 -0
  44. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/preprocessor.py +0 -0
  45. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/preprocessor_runtime.py +0 -0
  46. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/profile_runtime.py +0 -0
  47. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/significance_runtime.py +0 -0
  48. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/style_runtime.py +0 -0
  49. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/__init__.py +0 -0
  50. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/agent_digest.py +0 -0
  51. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/agent_io.py +0 -0
  52. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cache_store.py +0 -0
  53. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/capabilities.py +0 -0
  54. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
  55. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +0 -0
  56. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect.json +0 -0
  57. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -0
  58. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -0
  59. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect_marginal.json +0 -0
  60. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rectratio.json +0 -0
  61. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/ternary.json +0 -0
  62. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -0
  63. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
  64. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -0
  65. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
  66. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
  67. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/args.json +0 -0
  68. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/colors/colormaps.json +0 -0
  69. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/corrplot/matrix.json +0 -0
  70. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/default/output.json +0 -0
  71. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/flowchart/default.json +0 -0
  72. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
  73. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -0
  74. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/2x1/ternary.json +0 -0
  75. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -0
  76. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
  77. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/JarvisHEP.png +0 -0
  78. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/calculator.png +0 -0
  79. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/inputfile.png +0 -0
  80. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/opera.png +0 -0
  81. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/outputfile.png +0 -0
  82. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/sampler.png +0 -0
  83. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/gambit.png +0 -0
  84. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/gambit_small.png +0 -0
  85. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/style_preference.json +0 -0
  86. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cli.py +0 -0
  87. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cli_help.py +0 -0
  88. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/client.py +0 -0
  89. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/column_demand.py +0 -0
  90. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/column_probe.py +0 -0
  91. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/config.py +0 -0
  92. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/config_address.py +0 -0
  93. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/core.py +0 -0
  94. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/core_assets.py +0 -0
  95. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_access.py +0 -0
  96. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader.py +0 -0
  97. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader_hdf5.py +0 -0
  98. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader_runtime.py +0 -0
  99. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader_summary.py +0 -0
  100. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/dataset_types.py +0 -0
  101. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/diagnostic_guidance.py +0 -0
  102. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/diagnostics.py +0 -0
  103. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/dryrun_runtime.py +0 -0
  104. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/expr_names.py +0 -0
  105. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/fix_apply.py +0 -0
  106. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/flowchart.py +0 -0
  107. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/inner_func.py +0 -0
  108. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_catalog.py +0 -0
  109. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_methods.py +0 -0
  110. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_render_agent.py +0 -0
  111. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_render_human.py +0 -0
  112. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_transforms.py +0 -0
  113. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/agent-output.yaml +0 -0
  114. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/cli-map.yaml +0 -0
  115. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/data-columns.yaml +0 -0
  116. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/debug-overlay.yaml +0 -0
  117. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/diagnostics.yaml +0 -0
  118. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/dryrun-viz.yaml +0 -0
  119. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/layer-method.yaml +0 -0
  120. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/manifest.yaml +0 -0
  121. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/style-axes.yaml +0 -0
  122. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/type-posterior-2d.yaml +0 -0
  123. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/type-profile-2d.yaml +0 -0
  124. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/validate.yaml +0 -0
  125. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/workflow.yaml +0 -0
  126. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/yaml-root.yaml +0 -0
  127. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/memtrace.py +0 -0
  128. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/method_contracts.py +0 -0
  129. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/render_health.py +0 -0
  130. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/common.json +0 -0
  131. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/dataset.json +0 -0
  132. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/figure.json +0 -0
  133. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/layer.json +0 -0
  134. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/transform.json +0 -0
  135. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/manifest.json +0 -0
  136. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/bar.json +0 -0
  137. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/barh.json +0 -0
  138. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/contour.json +0 -0
  139. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/contourf.json +0 -0
  140. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/corrplot.json +0 -0
  141. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/dynesty_runplot.json +0 -0
  142. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/errorbar.json +0 -0
  143. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/fill.json +0 -0
  144. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/fill_between.json +0 -0
  145. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/fill_betweenx.json +0 -0
  146. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/hist.json +0 -0
  147. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/hist2d.json +0 -0
  148. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/imshow.json +0 -0
  149. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/jpcontour.json +0 -0
  150. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/jpcontourf.json +0 -0
  151. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/jpfield.json +0 -0
  152. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/pcolor.json +0 -0
  153. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/pcolormesh.json +0 -0
  154. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/plot.json +0 -0
  155. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/quiver.json +0 -0
  156. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/scatter.json +0 -0
  157. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/stairs.json +0 -0
  158. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/step.json +0 -0
  159. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tricontour.json +0 -0
  160. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tricontourf.json +0 -0
  161. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tripcolor.json +0 -0
  162. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tripcolor_axes.json +0 -0
  163. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/triplot.json +0 -0
  164. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/voronoi.json +0 -0
  165. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/voronoif.json +0 -0
  166. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/plot-config.json +0 -0
  167. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema_catalog.py +0 -0
  168. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema_diagnostics.py +0 -0
  169. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/templates_catalog.py +0 -0
  170. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/transform_contracts.py +0 -0
  171. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/__init__.py +0 -0
  172. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/cmaps.py +0 -0
  173. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/dataframes.py +0 -0
  174. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/expression.py +0 -0
  175. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/interpolator.py +0 -0
  176. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/pathing.py +0 -0
  177. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/validation.py +0 -0
  178. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/__init__.py +0 -0
  179. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/cap.py +0 -0
  180. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/config_cmd.py +0 -0
  181. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/data.py +0 -0
  182. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/doctor.py +0 -0
  183. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/dryrun.py +0 -0
  184. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/explain.py +0 -0
  185. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/man.py +0 -0
  186. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/suggest.py +0 -0
  187. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/template.py +0 -0
  188. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/validate.py +0 -0
  189. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/yaml_io.py +0 -0
  190. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/setup.cfg +0 -0
  191. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_agent_digest.py +0 -0
  192. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_agent_envelope.py +0 -0
  193. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_axis_title.py +0 -0
  194. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_capabilities.py +0 -0
  195. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_card_debug_shape.py +0 -0
  196. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_cli_help.py +0 -0
  197. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_cli_verbs.py +0 -0
  198. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_cmaps.py +0 -0
  199. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_column_existence.py +0 -0
  200. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_config_addressing.py +0 -0
  201. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_config_edit.py +0 -0
  202. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_config_expand.py +0 -0
  203. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_correlation_runtime.py +0 -0
  204. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_access.py +0 -0
  205. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_describe.py +0 -0
  206. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_head_eval.py +0 -0
  207. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_loader_contracts.py +0 -0
  208. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_pipelines.py +0 -0
  209. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_debug_config.py +0 -0
  210. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_debug_switches.py +0 -0
  211. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_debug_yaml_override.py +0 -0
  212. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_density_cell_transform.py +0 -0
  213. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_design_runtime.py +0 -0
  214. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_diagnostic_guidance.py +0 -0
  215. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_docs_status_labels.py +0 -0
  216. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_doctor_partial.py +0 -0
  217. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_explain.py +0 -0
  218. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_figure_contracts.py +0 -0
  219. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_figure_type_expander.py +0 -0
  220. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_fix_application.py +0 -0
  221. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_flowchart_renderer.py +0 -0
  222. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_imshow_adapter.py +0 -0
  223. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_interp_2d_transform.py +0 -0
  224. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_interp_natural_neighbor.py +0 -0
  225. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_interpolator_contracts.py +0 -0
  226. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_jpcontour_methods.py +0 -0
  227. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_man_cli.py +0 -0
  228. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_method_contracts.py +0 -0
  229. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_parse_data_metadata.py +0 -0
  230. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_pathing_contracts.py +0 -0
  231. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_posterior_hpd.py +0 -0
  232. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_render_health.py +0 -0
  233. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_row_ledger.py +0 -0
  234. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_schema_catalog.py +0 -0
  235. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_suggest.py +0 -0
  236. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_suggest_axes.py +0 -0
  237. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_template_catalog.py +0 -0
  238. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_template_json.py +0 -0
  239. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_ternary_debug.py +0 -0
  240. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_transform_contracts.py +0 -0
  241. {jarvisplot-2.1.1 → jarvisplot-2.1.3}/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.1
3
+ Version: 2.1.3
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JarvisPLOT
3
- Version: 2.1.1
3
+ Version: 2.1.3
4
4
  Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -63,6 +63,15 @@ def diamond_extent(n: int, *, diag: bool = False) -> tuple[float, float, float,
63
63
 
64
64
  With ``diag`` the self-pairs come in at ``u = 0`` and push both ranges out
65
65
  by half a cell.
66
+
67
+ A half, not the quarter the pentagon of
68
+ :func:`corrplot_runtime._self_cell_vertices` actually reaches: that shape
69
+ is how a *fill* keeps a whole cell's area against the edge, and the round
70
+ glyphs do not take it. A circle on the diagonal is a circle, and at the
71
+ quarter it overhung the panel by 0.07 cells -- which is only 0.3 mm, but it
72
+ is 0.3 mm off the one gap the eye checks, the channel between the matrix
73
+ and the names, and it narrowed it on the diagonal rows alone. The panel
74
+ buys the half so that every glyph fits whatever shape it is.
66
75
  """
67
76
  n = max(int(n), 2)
68
77
  if diag:
@@ -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):
@@ -161,6 +166,14 @@ _DIAMOND_ROUND = 1.0 / np.sqrt(2.0)
161
166
  #: because a divider that is *nearly* the cell divider reads as a mistake.
162
167
  _GRID_LWD = 0.3
163
168
 
169
+ #: Where the tint sits: under everything. It is the background the figure is
170
+ #: drawn *on*, so it takes the lowest layer on the axes rather than one derived
171
+ #: from the glyph's -- an axis draws its own tick labels at 2.5, and a tint
172
+ #: placed relative to the glyphs lands above that and prints over the names.
173
+ #: Its three pieces (the cells, the notch, the band under the label) are one
174
+ #: band and share one number, so they cannot drift apart.
175
+ _TINT_ZORDER = 0.4
176
+
164
177
  #: Square glyphs are a polygon per cell and nothing else, so they skip the
165
178
  #: patch objects entirely -- see :func:`_glyph_vertices`.
166
179
  _POLYGON_GLYPHS = ("square", "color", "shade")
@@ -171,21 +184,87 @@ _POLYGON_GLYPHS = ("square", "color", "shade")
171
184
  _FILLED_GLYPHS = ("color", "shade")
172
185
 
173
186
 
174
- def _notch_vertices(n: int, side: str = "left"):
187
+ #: How far a self cell reaches past the matrix's near edge, in cells. See
188
+ #: :func:`_self_cell_vertices` -- it is what makes the pentagon's area a cell.
189
+ _SELF_REACH = 0.25
190
+
191
+
192
+ def _self_cell_vertices(rows, side: str = "left", scale: float = 1.0):
193
+ """``(m, 5, 2)`` outlines for the cells on the diagonal.
194
+
195
+ A variable's pair with *itself* sits at ``u = 0``, so half of an ordinary
196
+ cell would hang off the matrix and print over the names. Clipping it
197
+ instead leaves a triangle -- half the area of every other cell, which reads
198
+ as a fainter diagonal rather than as the constant it is.
199
+
200
+ So it is neither: the cell keeps its point into the matrix and is squared
201
+ off on the outside, and the rectangle that squares it off is exactly deep
202
+ enough to pay back what the clip took. The triangle is ``1/4``, the
203
+ rectangle is ``a x 1``, so ``a = 1/4`` puts the pentagon at ``1/2`` -- the
204
+ area of a whole cell, for every variable, which is the only way a diagonal
205
+ of constant rho can look constant.
206
+
207
+ It fits without moving anything: the wedge beside each name is empty by
208
+ construction (see :func:`_notch_vertices`, which is what used to fill it)
209
+ and the quarter cell outside is page the panel takes from the gap.
210
+
211
+ ``scale`` shrinks it about ``(0, k)`` exactly as ``glyph.scale`` shrinks
212
+ every other cell. Without it these are the only shapes on the figure drawn
213
+ at full size, and since they also stack without a gap in ``v`` they fuse
214
+ into one bar down the edge instead of reading as a cell each.
215
+ """
216
+ k = np.asarray(rows, dtype=float)
217
+ flip = -1.0 if side == "right" else 1.0
218
+ zero = np.zeros_like(k)
219
+ half = 0.5 * float(scale)
220
+ back = flip * -2.0 * _SELF_REACH * half + zero
221
+ point, edge = flip * half + zero, half
222
+ return np.stack(
223
+ [
224
+ np.stack([back, k - edge], axis=1),
225
+ np.stack([zero, k - edge], axis=1),
226
+ np.stack([point, k], axis=1),
227
+ np.stack([zero, k + edge], axis=1),
228
+ np.stack([back, k + edge], axis=1),
229
+ ],
230
+ axis=1,
231
+ )
232
+
233
+
234
+ def _notch_vertices(n: int, side: str = "left", diag: bool = False):
175
235
  """The half cells that make the matrix's near edge straight.
176
236
 
177
- A variable's V has its vertex at ``(0, k)``, and the two cells meeting
178
- there touch the edge at a point, not along it -- so the boundary beside the
179
- names comes out as a row of notches. The notch at ``k`` is the triangle
180
- ``(0, k-1/2) (1/2, k) (0, k+1/2)``: filling it with that variable's own
181
- tint carries the band from the name into the matrix without a gap, and
182
- turns the sawtooth into the straight edge the rules can close against.
237
+ Whatever stands at ``u = 0`` meets the edge beside the names at a *point*,
238
+ so that boundary comes out as a row of notches and the tint carrying a
239
+ variable's band in from its name pinches to nothing there. Filling the
240
+ notch is what turns the sawtooth into the straight edge the rules close
241
+ against, and the band into one band.
242
+
243
+ The diagonal moves the notch, it does not remove it -- which is what an
244
+ earlier version of this card assumed, and the band broke on every shaded
245
+ row. Without the diagonal the near edge is ``u = 0`` and the first cells
246
+ are the pairs at ``u = 1/2``, so the gap is the triangle
247
+ ``(0, k-1/2) (1/2, k) (0, k+1/2)`` *inside* the matrix. With it the near
248
+ edge is ``u = -1/2`` and the self cell's own left corner is the point, so
249
+ the gap is the pair of triangles either side of it -- together with that
250
+ corner, the rectangle ``[-1/2, 0] x [k-1/2, k+1/2]``.
183
251
 
184
252
  Returned for every variable; the caller picks the tinted ones.
185
253
  """
186
254
  k = np.arange(int(n), dtype=float)
187
255
  flip = -1.0 if side == "right" else 1.0
188
256
  zero = np.zeros_like(k)
257
+ if diag:
258
+ back = flip * -0.5 + zero
259
+ return np.stack(
260
+ [
261
+ np.stack([back, k - 0.5], axis=1),
262
+ np.stack([zero, k - 0.5], axis=1),
263
+ np.stack([zero, k + 0.5], axis=1),
264
+ np.stack([back, k + 0.5], axis=1),
265
+ ],
266
+ axis=1,
267
+ )
189
268
  return np.stack(
190
269
  [
191
270
  np.stack([zero, k - 0.5], axis=1),
@@ -196,14 +275,25 @@ def _notch_vertices(n: int, side: str = "left"):
196
275
  )
197
276
 
198
277
 
199
- def _shaded_variables(n: int, blocks=None) -> np.ndarray:
200
- """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.
201
287
 
202
- A block is a run of adjacent positions cut from the clustering tree, so
203
- tinting every other one makes the shading say where one group ends and the
204
- 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.
205
290
  """
206
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
207
297
  if blocks:
208
298
  for position, (start, end) in enumerate(blocks):
209
299
  if position % 2 == 0:
@@ -329,13 +419,25 @@ def _label_rules(ax, n: int, span, color, lw: float, clip: bool,
329
419
  colour and weight rather than a nearby pair, because a divider that only
330
420
  nearly matches reads as a second, different thing.
331
421
 
422
+ It starts where the *cells* meet, not where the axes end. Two cells of one
423
+ row touch at ``u = 0`` whichever layout is drawn -- without the diagonal
424
+ that is also the panel's near edge, so the two were the same number and the
425
+ difference never showed. With the diagonal the panel reaches half a cell
426
+ further out, and a rule begun at the edge leaves that half cell unruled:
427
+ the line between two names starts in mid air instead of at the matrix.
428
+
332
429
  Below the tick labels, like the band: an axis draws its labels at zorder
333
430
  2.5, and a rule over one of them is a strike-through.
334
431
  """
335
432
  from matplotlib.collections import LineCollection
336
433
 
337
434
  target = ax.ax if hasattr(ax, "ax") else ax
338
- x0, x1 = span
435
+ try:
436
+ lo, hi = target.get_xlim()
437
+ meet = (0.0 - float(lo)) / (float(hi) - float(lo))
438
+ except Exception:
439
+ meet = span[1] if span[0] < 0.0 else span[0]
440
+ x0, x1 = min(span[0], meet), max(span[1], meet)
339
441
  rules = LineCollection(
340
442
  [[(x0, k + 0.5), (x1, k + 0.5)] for k in range(int(n) - 1)],
341
443
  colors=color, linewidths=lw, zorder=0.6,
@@ -409,7 +511,7 @@ def _stripe_the_labels(ax, n: int, shaded, span, color, clip: bool) -> None:
409
511
  ax.axhspan(
410
512
  k - 0.5, k + 0.5, xmin=span[0], xmax=span[1],
411
513
  facecolor=color, edgecolor="none", linewidth=0.0,
412
- zorder=0.5, clip_on=clip,
514
+ zorder=_TINT_ZORDER, clip_on=clip,
413
515
  )
414
516
 
415
517
 
@@ -464,7 +566,7 @@ def _glyph_vertices(kind: str, cx, cy, rho, scale: float, corners=None):
464
566
 
465
567
 
466
568
  def _glyph_patches(kind: str, cx, cy, rho, scale: float, sign: float,
467
- ellipse_scale: float = 1.4):
569
+ ellipse_scale: float = 1.4, diamond: bool = False):
468
570
  """One matplotlib patch per drawn cell, in data coordinates.
469
571
 
470
572
  Only the round glyphs come through here; the square ones are built as bare
@@ -494,7 +596,22 @@ def _glyph_patches(kind: str, cx, cy, rho, scale: float, sign: float,
494
596
  # the box does not -- so at k = glyph.scale the widest one covers
495
597
  # 0.64 of the cell while a circle covers 0.9. sqrt(2) is the
496
598
  # factor that makes the two agree, which is where 1.4 comes from.
599
+ #
600
+ # And it is the *cell's* diagonal, not the page's. `ellipse` is
601
+ # the one glyph with a direction, and its direction means something
602
+ # about the two axes of the cell it sits in: turn the lattice 45
603
+ # degrees and leave the ellipse behind and every one of them is
604
+ # reporting the wrong pair. The index map sends the cell's own
605
+ # frame through a +45 degree rotation (`j+1` goes to `(+1/2, +1/2)`
606
+ # and `i+1` to `(-1/2, +1/2)`), so the glyph turns with it.
607
+ #
608
+ # `side: right` negates u, which reflects rather than rotates and
609
+ # sends an angle A to 135 - A. It needs no case of its own: the
610
+ # angle here is a constant +/-45, and 135 - A and A + 45 differ by
611
+ # 180 exactly when they differ at all -- and an ellipse's angle is
612
+ # read modulo 180.
497
613
  k = scale * ellipse_scale
614
+ lean = sign * 45.0 + (45.0 if diamond else 0.0)
498
615
  r = 0.0 if not np.isfinite(r) else float(np.clip(r, -1.0, 1.0))
499
616
  width = k * np.sqrt(max(1.0 + r, 0.0)) / np.sqrt(2.0)
500
617
  # |rho| = 1 collapses the minor axis to zero, which fills no
@@ -503,7 +620,7 @@ def _glyph_patches(kind: str, cx, cy, rho, scale: float, sign: float,
503
620
  # as a line, so the minor axis gets a hairline floor.
504
621
  height = max(k * np.sqrt(max(1.0 - r, 0.0)) / np.sqrt(2.0), 0.02 * k)
505
622
  patches.append(
506
- Ellipse((x, y), width=width, height=height, angle=sign * 45.0)
623
+ Ellipse((x, y), width=width, height=height, angle=lean)
507
624
  )
508
625
  elif kind == "pie":
509
626
  # Clockwise from twelve o'clock, as read off the page.
@@ -627,9 +744,21 @@ def draw_corrplot(ax, **kwargs):
627
744
  # Diamond only. See the stripe block below for why it is not decoration.
628
745
  stripe = _pop(kwargs, "stripe", "alternate")
629
746
  stripe = "none" if stripe is False else str(stripe).strip().lower()
630
- if stripe not in ("alternate", "none"):
747
+ if stripe not in _STRIPES:
631
748
  raise ValueError(
632
- "corrplot stripe must be alternate or none; got {!r}.".format(stripe)
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:
759
+ raise ValueError(
760
+ "corrplot stripe: selected needs stripe.variables -- the names to "
761
+ "tint. Without them the tint would say nothing."
633
762
  )
634
763
  stripe_color = _pop(kwargs, "stripe.col", "#EFEFEF")
635
764
  # Diamond only. See `_edge_rules`: the rotated matrix has no top or bottom
@@ -721,14 +850,53 @@ def draw_corrplot(ax, **kwargs):
721
850
  if sig_level is not None and insig == "blank":
722
851
  drawn &= ~insignificant
723
852
 
853
+ # The diagonal, when it is in. A self pair sits at `u = 0`, so anything
854
+ # built from an ordinary cell there hangs a quarter of a cell past the
855
+ # panel and prints over the names -- and everything on this figure except
856
+ # `_stripe_the_labels` is drawn well above the axis' own zorder of 2.5, so
857
+ # it prints *over* them, not under. Every shape cut from a cell therefore
858
+ # takes the pentagon on the diagonal: the fill, the grid outline, and the
859
+ # tint. See `_self_cell_vertices`.
860
+ self_cells = drawn & (index_x.astype(int) == index_y.astype(int))
861
+
862
+ def _with_self_cells(shapes, selection, closed_loop: bool, shape_scale=1.0):
863
+ """Swap the pentagon in wherever `shapes` covers a self pair.
864
+
865
+ `selection` is the mask over *all* cells that `shapes` was built from,
866
+ which is not the same mask each time -- the fill is drawn for `drawn`,
867
+ the tint for the shaded band, the grid for everything.
868
+ """
869
+ picked = self_cells[selection]
870
+ if not diamond or not np.any(picked):
871
+ return shapes
872
+ out = [np.asarray(shape) for shape in shapes]
873
+ pentagons = _self_cell_vertices(iy[selection & self_cells], side, shape_scale)
874
+ for slot, pentagon in zip(np.flatnonzero(picked), pentagons):
875
+ out[slot] = (
876
+ np.vstack([pentagon, pentagon[:1]]) if closed_loop else pentagon
877
+ )
878
+ return out
879
+
724
880
  label_span = _label_span(ax, side, label_column_mm) if diamond else None
725
881
 
726
882
  if diamond and edge_lwd > 0.0 and label_span is not None:
727
883
  # The column is a width in axes fractions; the outline is drawn in cell
728
- # units, and the panel is `u_span` cells wide.
729
- column_u = -label_span[0] * (n / 2.0) if side == "left" else (
730
- (label_span[1] - 1.0) * (n / 2.0)
731
- )
884
+ # units, so it is scaled by how many cells wide the panel is. Read off
885
+ # the axes rather than worked out as `n / 2`: with the diagonal in, the
886
+ # panel is a quarter cell wider than that (see `diamond_extent`), and a
887
+ # rule scaled by the wrong span stops short of the names it is meant to
888
+ # close over -- by 4% of the column, which at thirteen variables is the
889
+ # last two letters of the longest name left hanging outside it.
890
+ lo, hi = float(min(ax.get_xlim())), float(max(ax.get_xlim()))
891
+ u_span = hi - lo
892
+ # ... and measured from the panel's own near edge, which the diagonal
893
+ # moves out to `u = -1/4`. `_edge_rules` strikes the horizontal run
894
+ # from `-column_u` to `0`, so the near edge has to be added back in:
895
+ # left out, the rule stops a quarter cell short and the last letters of
896
+ # the longest name hang outside the box it is meant to close.
897
+ near_u = lo if side == "left" else -hi
898
+ column_u = (-label_span[0] if side == "left" else label_span[1] - 1.0)
899
+ column_u = column_u * u_span - near_u
732
900
  _edge_rules(ax, n, column_u, side, edge_color, edge_lwd, zorder + 40, clip)
733
901
  if diamond and grid_color and label_span is not None:
734
902
  _label_rules(ax, n, label_span, grid_color, _GRID_LWD, clip, zorder)
@@ -743,7 +911,7 @@ def draw_corrplot(ax, **kwargs):
743
911
  edge_color, clip, zorder + 40,
744
912
  )
745
913
 
746
- if diamond and stripe == "alternate" and np.any(drawn):
914
+ if diamond and stripe != "none" and np.any(drawn):
747
915
  # Every other variable's cells, tinted. Not decoration: a variable's
748
916
  # pairs run away from its name as a V, and at 40 names a reader
749
917
  # following one of those arms has nothing to hold on to. The band is
@@ -759,23 +927,29 @@ def draw_corrplot(ax, **kwargs):
759
927
  #
760
928
  # The union of those variables' cells rather than one band each, so a
761
929
  # cell where two bands cross is filled once and the tint stays even.
762
- shaded = _shaded_variables(n, blocks)
930
+ shaded = _shaded_variables(n, blocks, shaded_positions)
763
931
  band = shaded[index_x.astype(int)] | shaded[index_y.astype(int)]
764
932
  band &= drawn
765
933
  if np.any(band):
934
+ # The tint is a background and keeps the plain cell, diagonal
935
+ # included: it has to reach the panel's edge or the band it carries
936
+ # from the name into the matrix comes apart there. It can, because
937
+ # the panel buys the whole half cell -- which is also what stops it
938
+ # printing over the names, and that is a clearance, not a shape.
766
939
  cells = PolyCollection(
767
940
  np.stack([ix[band], iy[band]], axis=1)[:, None, :]
768
941
  + 0.5 * _DIAMOND_CORNERS[None, :, :],
769
942
  closed=True, facecolors=stripe_color, edgecolors="none",
770
- zorder=zorder - 20,
943
+ zorder=_TINT_ZORDER,
771
944
  )
772
945
  cells.set_clip_on(clip)
773
946
  ax.add_collection(cells)
774
947
  # The half cells beside the names, so the band reaches the matrix and
775
948
  # the edge it makes is straight enough for the rules to close against.
949
+ # The diagonal moves this, it does not remove it: see `_notch_vertices`.
776
950
  notches = PolyCollection(
777
- _notch_vertices(n, side)[shaded], closed=True,
778
- facecolors=stripe_color, edgecolors="none", zorder=zorder - 20,
951
+ _notch_vertices(n, side, diag)[shaded], closed=True,
952
+ facecolors=stripe_color, edgecolors="none", zorder=_TINT_ZORDER,
779
953
  )
780
954
  notches.set_clip_on(clip)
781
955
  ax.add_collection(notches)
@@ -790,9 +964,13 @@ def draw_corrplot(ax, **kwargs):
790
964
  else:
791
965
  if kind in _POLYGON_GLYPHS:
792
966
  artists = PolyCollection(
793
- _glyph_vertices(
794
- kind, ix[drawn], iy[drawn], rho[drawn], scale,
795
- corners=_DIAMOND_CORNERS if diamond else _CORNERS,
967
+ _with_self_cells(
968
+ _glyph_vertices(
969
+ kind, ix[drawn], iy[drawn], rho[drawn], scale,
970
+ corners=_DIAMOND_CORNERS if diamond else _CORNERS,
971
+ ),
972
+ drawn if kind in _FILLED_GLYPHS else np.zeros(ix.shape, dtype=bool),
973
+ closed_loop=False, shape_scale=scale,
796
974
  ),
797
975
  closed=True,
798
976
  )
@@ -800,7 +978,7 @@ def draw_corrplot(ax, **kwargs):
800
978
  artists = PatchCollection(
801
979
  _glyph_patches(
802
980
  kind, ix[drawn], iy[drawn], rho[drawn], round_scale, sign,
803
- ellipse_scale=ellipse_scale,
981
+ ellipse_scale=ellipse_scale, diamond=diamond,
804
982
  ),
805
983
  match_original=False,
806
984
  )
@@ -847,14 +1025,27 @@ def draw_corrplot(ax, **kwargs):
847
1025
  if kind == "shade":
848
1026
  # The sign is already in the colour; the hatch is what survives a
849
1027
  # greyscale print, which is the whole point of shade.
1028
+ #
1029
+ # It runs corner to corner of the cell, and *which* corners is the
1030
+ # layout's business. On the square card those are the diagonal ones,
1031
+ # `(+/-1, +/-1) * half`. On the diamond the same cell is turned 45
1032
+ # degrees, so its corners are the ones straight up and straight across
1033
+ # -- and a stroke still drawn to `(+/-1, +/-1)` reaches `half * sqrt2`
1034
+ # where the cell only reaches `half / sqrt2`: twice too long, over the
1035
+ # boundary and into the neighbour, on every cell of the figure.
1036
+ step = 0.5 * scale
1037
+ arm = (
1038
+ [(0.0, step), (step, 0.0)] if diamond else [(step, step), (step, -step)]
1039
+ )
850
1040
  hatch = LineCollection(
851
1041
  [
852
1042
  [
853
- (x - 0.5 * scale, y - sign * 0.5 * scale * np.sign(r)),
854
- (x + 0.5 * scale, y + sign * 0.5 * scale * np.sign(r)),
1043
+ (x - dx, y - sign * dy * np.sign(r)),
1044
+ (x + dx, y + sign * dy * np.sign(r)),
855
1045
  ]
856
1046
  for x, y, r in zip(ix[drawn], iy[drawn], rho[drawn])
857
1047
  if r != 0.0
1048
+ for dx, dy in [arm[0] if r > 0 else arm[1]]
858
1049
  ],
859
1050
  colors="#FFFFFF",
860
1051
  linewidths=0.4,
@@ -868,9 +1059,23 @@ def draw_corrplot(ax, **kwargs):
868
1059
  # picture, a quarter of the paths, and at 100 variables that is 40,000
869
1060
  # of them. The outline has to be per cell rather than n+1 ruled lines
870
1061
  # because a `triangle` selection leaves holes for the ruling to cross.
1062
+ # The pentagon is a shape for glyphs that *fill* their cell. Outlining
1063
+ # it under a round one would be drawing a boundary the glyph does not
1064
+ # keep: at `rho = 1` the circle on the diagonal overhangs it, and the
1065
+ # flat backs line up into a grey rail down the edge of the matrix with
1066
+ # the circles straddling it. So there the diagonal simply carries no
1067
+ # outline -- the matrix's own edge is already closed by `edge.lwd`.
1068
+ ruled = (
1069
+ np.ones(ix.shape, dtype=bool)
1070
+ if not diamond or kind in _FILLED_GLYPHS
1071
+ else ~self_cells
1072
+ )
871
1073
  grid = LineCollection(
872
- np.stack([ix, iy], axis=1)[:, None, :]
873
- + (_DIAMOND_LOOP if diamond else _CELL_LOOP)[None, :, :],
1074
+ _with_self_cells(
1075
+ np.stack([ix[ruled], iy[ruled]], axis=1)[:, None, :]
1076
+ + (_DIAMOND_LOOP if diamond else _CELL_LOOP)[None, :, :],
1077
+ ruled, closed_loop=True,
1078
+ ),
874
1079
  colors=grid_color, linewidths=_GRID_LWD, zorder=zorder - 10,
875
1080
  )
876
1081
  grid.set_clip_on(clip)
@@ -914,7 +1119,7 @@ def draw_corrplot(ax, **kwargs):
914
1119
  # block above), so a box around each of them would be the same fact drawn
915
1120
  # twice, in the heaviest ink on the figure. `addrect` still decides *what*
916
1121
  # the blocks are; it just stops drawing them.
917
- if blocks and not (diamond and stripe != "none"):
1122
+ if blocks and not (diamond and stripe == "alternate"):
918
1123
  for start, end in blocks:
919
1124
  if diamond:
920
1125
  ax.add_patch(
@@ -496,9 +496,10 @@
496
496
  "Options": {
497
497
  "method": {"type": "enum", "values": ["circle", "square", "ellipse", "color", "shade", "pie", "number"], "default": "circle"},
498
498
  "type": {"type": "enum", "values": ["upper", "lower"], "default": "upper", "forbidden": ["full"]},
499
- "diag": {"type": "boolean", "default": false},
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"}},
@@ -511,7 +512,7 @@
511
512
  "corrplot": {
512
513
  "method": "circle",
513
514
  "type": "upper",
514
- "diag": false,
515
+ "diag": true,
515
516
  "glyph.scale": 0.9,
516
517
  "ellipse.scale": 1.4,
517
518
  "outline": "sign",
@@ -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
@@ -895,8 +936,17 @@ def _prebuild_one(core, info: dict, card: Mapping) -> None:
895
936
  _enforce_corr_contract(info, contract, name)
896
937
 
897
938
  source = entry.get("source")
898
- dts = (getattr(core, "dataset_registry", {}) or {}).get(
899
- source if isinstance(source, str) else None
939
+ # A correlation layer may intentionally concatenate several root
940
+ # datasets. The runtime already supports source lists; the prebuild only
941
+ # needs one member's cheap column metadata to solve the shared geometry.
942
+ # Use the first registered member (the input tables are required to carry
943
+ # the same schema), while leaving the complete source list for the actual
944
+ # correlation transform below.
945
+ registry = getattr(core, "dataset_registry", {}) or {}
946
+ source_names = [source] if isinstance(source, str) else list(source or [])
947
+ dts = next(
948
+ (registry.get(item) for item in source_names if isinstance(item, str) and registry.get(item) is not None),
949
+ None,
900
950
  )
901
951
  names = list(getattr(dts, "keys", None) or [])
902
952
  if not names:
@@ -991,6 +1041,11 @@ def _prebuild_one(core, info: dict, card: Mapping) -> None:
991
1041
  # but placed and sized by the solve.
992
1042
  layer["style"]["__corr_number_mm__"] = geom.number_pad_mm
993
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
994
1049
 
995
1050
  if blocks:
996
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "JarvisPLOT"
7
- version = "2.1.1"
7
+ version = "2.1.3"
8
8
  description = "Jarvis-PLOT: YAML-driven plotting engine"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"