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.
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/PKG-INFO +1 -1
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/PKG-INFO +1 -1
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corr_layout_diamond.py +9 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corrplot_runtime.py +242 -37
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/corrplot/diamond.json +4 -3
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/core_runtime.py +57 -2
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/method_notes.yaml +1 -1
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/type-correlation-matrix.yaml +3 -1
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/pyproject.toml +1 -1
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_corrplot.py +391 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/SOURCES.txt +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/entry_points.txt +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/requires.txt +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/JarvisPLOT.egg-info/top_level.txt +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/README.md +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/__init__.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/adapters_rect.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/adapters_ternary.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/bin_stat_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/colorbar_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/config_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corr_layout.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/corr_order.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/correlation_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/data_pipelines.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/debug_config.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/density_cell_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/design_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/dynesty_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/figure.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/figure_types.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/helper.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/interp_2d_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/interp_natural_neighbor.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/interp_natural_neighbor_exact.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/layer_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/layout_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/method_registry.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/posterior_density_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/posterior_hpd.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/posterior_mesh.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/preprocessor.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/preprocessor_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/profile_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/significance_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/Figure/style_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/__init__.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/agent_digest.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/agent_io.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cache_store.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/capabilities.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rect_marginal.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/rectratio.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/ternary.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/args.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/colors/colormaps.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/corrplot/matrix.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/default/output.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/flowchart/default.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/2x1/ternary.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/JarvisHEP.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/calculator.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/inputfile.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/opera.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/outputfile.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/flowchart/sampler.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/gambit.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/icons/gambit_small.png +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cards/style_preference.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cli.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/cli_help.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/client.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/column_demand.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/column_probe.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/config.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/config_address.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/core.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/core_assets.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_access.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader_hdf5.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/data_loader_summary.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/dataset_types.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/diagnostic_guidance.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/diagnostics.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/dryrun_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/expr_names.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/fix_apply.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/flowchart.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/inner_func.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_catalog.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_methods.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_render_agent.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_render_human.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/man_transforms.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/agent-output.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/cli-map.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/data-columns.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/debug-overlay.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/diagnostics.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/dryrun-viz.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/layer-method.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/manifest.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/style-axes.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/type-posterior-2d.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/type-profile-2d.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/validate.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/workflow.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/manual_cards/yaml-root.yaml +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/memtrace.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/method_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/render_health.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/common.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/dataset.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/figure.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/layer.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/core/transform.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/manifest.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/bar.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/barh.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/contour.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/contourf.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/corrplot.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/dynesty_runplot.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/errorbar.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/fill.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/fill_between.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/fill_betweenx.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/hist.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/hist2d.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/imshow.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/jpcontour.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/jpcontourf.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/jpfield.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/pcolor.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/pcolormesh.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/plot.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/quiver.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/scatter.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/stairs.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/step.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tricontour.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tricontourf.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tripcolor.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/tripcolor_axes.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/triplot.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/voronoi.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/methods/voronoif.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema/plot-config.json +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema_catalog.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/schema_diagnostics.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/templates_catalog.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/transform_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/__init__.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/cmaps.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/dataframes.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/expression.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/interpolator.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/utils/pathing.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/validation.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/__init__.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/cap.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/config_cmd.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/data.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/doctor.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/dryrun.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/explain.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/man.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/suggest.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/template.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/verbs/validate.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/jarvisplot/yaml_io.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/setup.cfg +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_agent_digest.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_agent_envelope.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_axis_title.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_capabilities.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_card_debug_shape.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_cli_help.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_cli_verbs.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_cmaps.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_column_existence.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_config_addressing.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_config_edit.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_config_expand.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_correlation_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_access.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_describe.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_head_eval.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_loader_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_data_pipelines.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_debug_config.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_debug_switches.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_debug_yaml_override.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_density_cell_transform.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_design_runtime.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_diagnostic_guidance.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_docs_status_labels.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_doctor_partial.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_explain.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_figure_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_figure_type_expander.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_fix_application.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_flowchart_renderer.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_imshow_adapter.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_interp_2d_transform.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_interp_natural_neighbor.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_interpolator_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_jpcontour_methods.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_man_cli.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_method_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_parse_data_metadata.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_pathing_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_posterior_hpd.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_render_health.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_row_ledger.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_schema_catalog.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_suggest.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_suggest_axes.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_template_catalog.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_template_json.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_ternary_debug.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_transform_contracts.py +0 -0
- {jarvisplot-2.1.1 → jarvisplot-2.1.3}/tests/test_validate_no_matplotlib.py +0 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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
|
|
747
|
+
if stripe not in _STRIPES:
|
|
631
748
|
raise ValueError(
|
|
632
|
-
"corrplot stripe must be
|
|
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,
|
|
729
|
-
|
|
730
|
-
|
|
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
|
|
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=
|
|
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=
|
|
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
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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 -
|
|
854
|
-
(x +
|
|
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
|
-
|
|
873
|
-
|
|
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
|
|
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":
|
|
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":
|
|
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
|
-
|
|
899
|
-
|
|
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
|