jarvisplot 2.1.5__tar.gz → 2.1.6__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.5 → jarvisplot-2.1.6}/JarvisPLOT.egg-info/PKG-INFO +12 -3
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/JarvisPLOT.egg-info/SOURCES.txt +6 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/PKG-INFO +12 -3
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/README.md +11 -2
- jarvisplot-2.1.6/jarvisplot/Figure/adaptive_cdf_runtime.py +131 -0
- jarvisplot-2.1.6/jarvisplot/Figure/distribution_1d_runtime.py +165 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/preprocessor.py +15 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/preprocessor_runtime.py +6 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/column_demand.py +10 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/data_loader.py +6 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/data_loader_runtime.py +6 -1
- jarvisplot-2.1.6/jarvisplot/distribution_1d_config.py +167 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/dryrun_runtime.py +12 -3
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/man_transforms.py +8 -5
- jarvisplot-2.1.6/jarvisplot/schema/core/distribution-1d.json +72 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/core/transform.json +8 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/manifest.json +3 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/transform_contracts.py +67 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/pyproject.toml +1 -1
- jarvisplot-2.1.6/tests/test_adaptive_cdf.py +141 -0
- jarvisplot-2.1.6/tests/test_distribution_1d_transform.py +234 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/JarvisPLOT.egg-info/dependency_links.txt +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/JarvisPLOT.egg-info/entry_points.txt +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/JarvisPLOT.egg-info/requires.txt +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/JarvisPLOT.egg-info/top_level.txt +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/__init__.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/adapters_rect.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/adapters_ternary.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/bin_stat_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/colorbar_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/config_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/corr_layout.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/corr_layout_diamond.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/corr_order.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/correlation_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/corrplot_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/data_pipelines.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/debug_config.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/density_cell_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/design_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/dynesty_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/figure.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/figure_types.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/helper.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/interp_2d_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/interp_natural_neighbor.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/interp_natural_neighbor_exact.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/layer_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/layout_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/method_registry.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/posterior_density_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/posterior_hpd.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/posterior_mesh.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/profile_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/significance_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/Figure/style_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/__init__.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/agent_digest.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/agent_io.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cache_store.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/capabilities.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/dynesty_runplot.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/rect.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/rect_marginal.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/rectratio.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/ternary.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/args.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/colors/colormaps.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/corrplot/diamond.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/corrplot/matrix.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/default/output.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/flowchart/default.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/gambit/2x1/ternary.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/flowchart/JarvisHEP.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/flowchart/calculator.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/flowchart/inputfile.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/flowchart/opera.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/flowchart/outputfile.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/flowchart/sampler.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/gambit.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/icons/gambit_small.png +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cards/style_preference.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cli.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/cli_help.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/client.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/column_probe.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/config.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/config_address.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/core.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/core_assets.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/core_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/data_access.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/data_generation_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/data_loader_hdf5.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/data_loader_summary.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/dataset_types.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/diagnostic_guidance.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/diagnostics.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/expr_names.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/fix_apply.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/flowchart.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/generated_data.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/inner_func.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/man_catalog.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/man_methods.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/man_render_agent.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/man_render_human.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/agent-output.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/cli-map.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/data-columns.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/debug-overlay.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/diagnostics.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/dryrun-viz.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/field-interp.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/generated-data.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/layer-method.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/manifest.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/method_notes.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/style-axes.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/type-correlation-matrix.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/type-posterior-2d.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/type-profile-2d.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/validate.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/workflow.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/manual_cards/yaml-root.yaml +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/memtrace.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/method_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/render_health.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/core/common.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/core/dataset.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/core/figure.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/core/generated-data.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/core/layer.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/bar.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/barh.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/contour.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/contourf.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/corrplot.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/dynesty_runplot.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/errorbar.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/fill.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/fill_between.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/fill_betweenx.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/hist.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/hist2d.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/imshow.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/jpcontour.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/jpcontourf.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/jpfield.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/pcolor.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/pcolormesh.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/plot.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/quiver.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/scatter.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/stairs.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/step.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/tricontour.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/tricontourf.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/tripcolor.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/tripcolor_axes.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/triplot.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/voronoi.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/methods/voronoif.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema/plot-config.json +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema_catalog.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/schema_diagnostics.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/templates_catalog.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/utils/__init__.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/utils/cmaps.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/utils/dataframes.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/utils/expression.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/utils/interpolator.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/utils/pathing.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/validation.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/__init__.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/cap.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/config_cmd.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/data.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/doctor.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/dryrun.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/explain.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/man.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/suggest.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/template.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/verbs/validate.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/jarvisplot/yaml_io.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/setup.cfg +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_agent_digest.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_agent_envelope.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_axis_title.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_capabilities.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_card_debug_shape.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_cli_help.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_cli_verbs.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_cmaps.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_column_existence.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_config_addressing.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_config_edit.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_config_expand.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_correlation_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_corrplot.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_data_access.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_data_describe.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_data_head_eval.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_data_loader_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_data_pipelines.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_debug_config.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_debug_switches.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_debug_yaml_override.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_density_cell_transform.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_design_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_diagnostic_guidance.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_docs_status_labels.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_doctor_partial.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_explain.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_figure_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_figure_type_expander.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_fix_application.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_flowchart_renderer.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_generated_data.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_imshow_adapter.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_interp_2d_transform.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_interp_natural_neighbor.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_interpolator_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_jpcontour_methods.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_man_cli.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_method_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_parse_data_metadata.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_pathing_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_posterior_hpd.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_profile_runtime.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_render_health.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_row_ledger.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_schema_catalog.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_suggest.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_suggest_axes.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_template_catalog.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_template_json.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_ternary_debug.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/tests/test_transform_contracts.py +0 -0
- {jarvisplot-2.1.5 → jarvisplot-2.1.6}/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.
|
|
3
|
+
Version: 2.1.6
|
|
4
4
|
Summary: Jarvis-PLOT: YAML-driven plotting engine
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -161,11 +161,20 @@ The loop `jplot man` prints is:
|
|
|
161
161
|
5. `jplot <file>` — render, only when the figure itself is needed.
|
|
162
162
|
|
|
163
163
|
`jplot man --json` is the machine-readable manual: 15 topics, plus a
|
|
164
|
-
live
|
|
165
|
-
(`jplot man scatter`, `jplot man transform.profile`). Method contracts come from
|
|
164
|
+
live pages for every registered layer method and transform step
|
|
165
|
+
(`jplot man scatter`, `jplot man transform.profile`, `jplot man PDF1D`). Method contracts come from
|
|
166
166
|
the same registry `jplot cap methods` reads, so a man page cannot drift from the
|
|
167
167
|
code.
|
|
168
168
|
|
|
169
|
+
For continuous one-dimensional distributions from raw samples, use `PDF1D`
|
|
170
|
+
(CDF plus PDF) or `CDF1D` in a data transform. `coordinates.x` owns `expr`,
|
|
171
|
+
`lim`, and `grid`; optional `repeat` computes independent batch means and
|
|
172
|
+
standard deviations. See [the contract](docs/specs/PDF_CDF_1D.md) and the
|
|
173
|
+
[runnable example](Example/pdf_cdf_1d.yaml).
|
|
174
|
+
Reconstruction defaults to adaptive true CDF anchors and `monotone_c2`
|
|
175
|
+
interpolation (C2 CDF / C1 PDF). Set `anchors: all` and `interpolation: pchip`
|
|
176
|
+
to reproduce the original all-node reconstruction.
|
|
177
|
+
|
|
169
178
|
Two things worth knowing before trusting a config:
|
|
170
179
|
|
|
171
180
|
- `jplot validate` checks shape and contracts and never touches the data;
|
|
@@ -30,6 +30,7 @@ jarvisplot/data_loader_summary.py
|
|
|
30
30
|
jarvisplot/dataset_types.py
|
|
31
31
|
jarvisplot/diagnostic_guidance.py
|
|
32
32
|
jarvisplot/diagnostics.py
|
|
33
|
+
jarvisplot/distribution_1d_config.py
|
|
33
34
|
jarvisplot/dryrun_runtime.py
|
|
34
35
|
jarvisplot/expr_names.py
|
|
35
36
|
jarvisplot/fix_apply.py
|
|
@@ -53,6 +54,7 @@ jarvisplot/yaml_io.py
|
|
|
53
54
|
jarvisplot/Figure/__init__.py
|
|
54
55
|
jarvisplot/Figure/adapters_rect.py
|
|
55
56
|
jarvisplot/Figure/adapters_ternary.py
|
|
57
|
+
jarvisplot/Figure/adaptive_cdf_runtime.py
|
|
56
58
|
jarvisplot/Figure/bin_stat_runtime.py
|
|
57
59
|
jarvisplot/Figure/colorbar_runtime.py
|
|
58
60
|
jarvisplot/Figure/config_runtime.py
|
|
@@ -65,6 +67,7 @@ jarvisplot/Figure/data_pipelines.py
|
|
|
65
67
|
jarvisplot/Figure/debug_config.py
|
|
66
68
|
jarvisplot/Figure/density_cell_runtime.py
|
|
67
69
|
jarvisplot/Figure/design_runtime.py
|
|
70
|
+
jarvisplot/Figure/distribution_1d_runtime.py
|
|
68
71
|
jarvisplot/Figure/dynesty_runtime.py
|
|
69
72
|
jarvisplot/Figure/figure.py
|
|
70
73
|
jarvisplot/Figure/figure_types.py
|
|
@@ -139,6 +142,7 @@ jarvisplot/schema/manifest.json
|
|
|
139
142
|
jarvisplot/schema/plot-config.json
|
|
140
143
|
jarvisplot/schema/core/common.json
|
|
141
144
|
jarvisplot/schema/core/dataset.json
|
|
145
|
+
jarvisplot/schema/core/distribution-1d.json
|
|
142
146
|
jarvisplot/schema/core/figure.json
|
|
143
147
|
jarvisplot/schema/core/generated-data.json
|
|
144
148
|
jarvisplot/schema/core/layer.json
|
|
@@ -190,6 +194,7 @@ jarvisplot/verbs/man.py
|
|
|
190
194
|
jarvisplot/verbs/suggest.py
|
|
191
195
|
jarvisplot/verbs/template.py
|
|
192
196
|
jarvisplot/verbs/validate.py
|
|
197
|
+
tests/test_adaptive_cdf.py
|
|
193
198
|
tests/test_agent_digest.py
|
|
194
199
|
tests/test_agent_envelope.py
|
|
195
200
|
tests/test_axis_title.py
|
|
@@ -215,6 +220,7 @@ tests/test_debug_yaml_override.py
|
|
|
215
220
|
tests/test_density_cell_transform.py
|
|
216
221
|
tests/test_design_runtime.py
|
|
217
222
|
tests/test_diagnostic_guidance.py
|
|
223
|
+
tests/test_distribution_1d_transform.py
|
|
218
224
|
tests/test_docs_status_labels.py
|
|
219
225
|
tests/test_doctor_partial.py
|
|
220
226
|
tests/test_explain.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: JarvisPLOT
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.6
|
|
4
4
|
Summary: Jarvis-PLOT: YAML-driven plotting engine
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -161,11 +161,20 @@ The loop `jplot man` prints is:
|
|
|
161
161
|
5. `jplot <file>` — render, only when the figure itself is needed.
|
|
162
162
|
|
|
163
163
|
`jplot man --json` is the machine-readable manual: 15 topics, plus a
|
|
164
|
-
live
|
|
165
|
-
(`jplot man scatter`, `jplot man transform.profile`). Method contracts come from
|
|
164
|
+
live pages for every registered layer method and transform step
|
|
165
|
+
(`jplot man scatter`, `jplot man transform.profile`, `jplot man PDF1D`). Method contracts come from
|
|
166
166
|
the same registry `jplot cap methods` reads, so a man page cannot drift from the
|
|
167
167
|
code.
|
|
168
168
|
|
|
169
|
+
For continuous one-dimensional distributions from raw samples, use `PDF1D`
|
|
170
|
+
(CDF plus PDF) or `CDF1D` in a data transform. `coordinates.x` owns `expr`,
|
|
171
|
+
`lim`, and `grid`; optional `repeat` computes independent batch means and
|
|
172
|
+
standard deviations. See [the contract](docs/specs/PDF_CDF_1D.md) and the
|
|
173
|
+
[runnable example](Example/pdf_cdf_1d.yaml).
|
|
174
|
+
Reconstruction defaults to adaptive true CDF anchors and `monotone_c2`
|
|
175
|
+
interpolation (C2 CDF / C1 PDF). Set `anchors: all` and `interpolation: pchip`
|
|
176
|
+
to reproduce the original all-node reconstruction.
|
|
177
|
+
|
|
169
178
|
Two things worth knowing before trusting a config:
|
|
170
179
|
|
|
171
180
|
- `jplot validate` checks shape and contracts and never touches the data;
|
|
@@ -139,11 +139,20 @@ The loop `jplot man` prints is:
|
|
|
139
139
|
5. `jplot <file>` — render, only when the figure itself is needed.
|
|
140
140
|
|
|
141
141
|
`jplot man --json` is the machine-readable manual: 15 topics, plus a
|
|
142
|
-
live
|
|
143
|
-
(`jplot man scatter`, `jplot man transform.profile`). Method contracts come from
|
|
142
|
+
live pages for every registered layer method and transform step
|
|
143
|
+
(`jplot man scatter`, `jplot man transform.profile`, `jplot man PDF1D`). Method contracts come from
|
|
144
144
|
the same registry `jplot cap methods` reads, so a man page cannot drift from the
|
|
145
145
|
code.
|
|
146
146
|
|
|
147
|
+
For continuous one-dimensional distributions from raw samples, use `PDF1D`
|
|
148
|
+
(CDF plus PDF) or `CDF1D` in a data transform. `coordinates.x` owns `expr`,
|
|
149
|
+
`lim`, and `grid`; optional `repeat` computes independent batch means and
|
|
150
|
+
standard deviations. See [the contract](docs/specs/PDF_CDF_1D.md) and the
|
|
151
|
+
[runnable example](Example/pdf_cdf_1d.yaml).
|
|
152
|
+
Reconstruction defaults to adaptive true CDF anchors and `monotone_c2`
|
|
153
|
+
interpolation (C2 CDF / C1 PDF). Set `anchors: all` and `interpolation: pchip`
|
|
154
|
+
to reproduce the original all-node reconstruction.
|
|
155
|
+
|
|
147
156
|
Two things worth knowing before trusting a config:
|
|
148
157
|
|
|
149
158
|
- `jplot validate` checks shape and contracts and never touches the data;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Adaptive empirical-CDF anchors and a local, monotone C2 quintic interpolant.
|
|
2
|
+
|
|
3
|
+
No bins, kernels, or nonlinear solver. The quartic PDF is represented in the
|
|
4
|
+
Bernstein basis; nonnegative coefficients certify nonnegativity on each whole
|
|
5
|
+
interval, rather than only at sampled output points.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from heapq import heappop, heappush
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from scipy.interpolate import BPoly, CubicSpline, PchipInterpolator
|
|
14
|
+
|
|
15
|
+
from ..distribution_1d_config import AnchorSettings
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def monotone_c2(x: np.ndarray, y: np.ndarray) -> BPoly:
|
|
19
|
+
"""Exact C2 Hermite quintics with locally limited slopes and curvatures.
|
|
20
|
+
|
|
21
|
+
On an interval of width h and secant m, the quartic derivative's Bernstein
|
|
22
|
+
coefficients are [d0, d0+h*a0/4, 5*m-2*(d0+d1)+h*(a1-a0)/4,
|
|
23
|
+
d1-h*a1/4, d1]. Limit curvatures first, then shrink each node's (d,a)
|
|
24
|
+
together so all five coefficients are nonnegative. Each shared node has
|
|
25
|
+
one value, slope, and curvature: the assembled CDF is C2 and PDF is C1.
|
|
26
|
+
The support endpoints have zero slope/curvature, allowing constant CDF
|
|
27
|
+
extension and zero PDF extension without a boundary derivative jump.
|
|
28
|
+
"""
|
|
29
|
+
x, y = np.asarray(x, dtype=float), np.asarray(y, dtype=float)
|
|
30
|
+
if x.ndim != 1 or len(x) < 2 or y.shape != x.shape or not np.all(np.isfinite(x)) or not np.all(np.isfinite(y)):
|
|
31
|
+
raise ValueError("monotone_c2 needs matching finite 1D anchor arrays")
|
|
32
|
+
h, mass = np.diff(x), np.diff(y)
|
|
33
|
+
if np.any(h <= 0) or np.any(mass < 0):
|
|
34
|
+
raise ValueError("monotone_c2 requires strictly increasing x and nondecreasing CDF anchors")
|
|
35
|
+
# A clamped cubic estimates smooth slopes/curvatures in linear time. Its
|
|
36
|
+
# unconstrained interpolant is never returned: the quintic limiter below
|
|
37
|
+
# certifies monotonicity even where the cubic estimate would overshoot.
|
|
38
|
+
seed = CubicSpline(x, y, bc_type=((1, 0.0), (1, 0.0)))
|
|
39
|
+
slopes = np.maximum(seed(x, 1), 0.0)
|
|
40
|
+
slopes[[0, -1]] = 0.0
|
|
41
|
+
curvature = seed(x, 2)
|
|
42
|
+
# Adjacent interval constraints for the first/last PDF control coefficients.
|
|
43
|
+
lower, upper = np.full(len(x), -np.inf), np.full(len(x), np.inf)
|
|
44
|
+
lower[:-1] = -4 * slopes[:-1] / h
|
|
45
|
+
upper[1:] = 4 * slopes[1:] / h
|
|
46
|
+
curvature = np.clip(curvature, lower, upper)
|
|
47
|
+
curvature[[0, -1]] = 0.0
|
|
48
|
+
# Both contributions are nonnegative after clipping. Reducing either node
|
|
49
|
+
# cannot violate another interval's constraint, so one local pass suffices.
|
|
50
|
+
left = 2 * slopes[:-1] + h * curvature[:-1] / 4
|
|
51
|
+
right = 2 * slopes[1:] - h * curvature[1:] / 4
|
|
52
|
+
available = 5 * mass / h
|
|
53
|
+
total = left + right
|
|
54
|
+
factors = np.ones(len(h))
|
|
55
|
+
active = total > available
|
|
56
|
+
factors[active] = (available[active] / total[active]) * (1 - 32 * np.finfo(float).eps)
|
|
57
|
+
node_factors = np.ones(len(x))
|
|
58
|
+
node_factors[:-1] = np.minimum(node_factors[:-1], factors)
|
|
59
|
+
node_factors[1:] = np.minimum(node_factors[1:], factors)
|
|
60
|
+
slopes *= node_factors
|
|
61
|
+
curvature *= node_factors
|
|
62
|
+
if not np.all(np.isfinite(slopes)) or not np.all(np.isfinite(curvature)):
|
|
63
|
+
raise ValueError("monotone_c2 cannot resolve this anchor spacing; inspect support and score precision")
|
|
64
|
+
return BPoly.from_derivatives(x, np.column_stack((y, slopes, curvature)), extrapolate=False)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def interpolate_cdf(x: np.ndarray, y: np.ndarray, method: str):
|
|
68
|
+
if method == "monotone_c2":
|
|
69
|
+
return monotone_c2(x, y)
|
|
70
|
+
if method == "pchip":
|
|
71
|
+
return PchipInterpolator(x, y, extrapolate=False)
|
|
72
|
+
raise ValueError(f"unknown CDF interpolation {method!r}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True)
|
|
76
|
+
class AnchorFit:
|
|
77
|
+
curve: BPoly | PchipInterpolator
|
|
78
|
+
indices: np.ndarray
|
|
79
|
+
max_error: float
|
|
80
|
+
stop_reason: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def fit_anchors(x: np.ndarray, y: np.ndarray, config: AnchorSettings, interpolation: str) -> AnchorFit:
|
|
84
|
+
"""Select only real empirical nodes plus the supplied support endpoints.
|
|
85
|
+
|
|
86
|
+
A bounded set of probability seeds starts the fit. Each pass evaluates
|
|
87
|
+
residuals against all empirical knots once, then scans disjoint intervals
|
|
88
|
+
and refines up to eight worst intervals. min_mass is the required observed
|
|
89
|
+
cumulative mass on EACH side of a proposed new node; it stops the selector
|
|
90
|
+
from chasing individual sample jumps. No output grid participates.
|
|
91
|
+
"""
|
|
92
|
+
if config.method == "all":
|
|
93
|
+
curve = interpolate_cdf(x, y, interpolation)
|
|
94
|
+
return AnchorFit(curve, np.arange(len(x)), 0.0, "all")
|
|
95
|
+
# Retain the first empirical value and the first empirical value reaching
|
|
96
|
+
# probability one, so the fit preserves the observed onset and upper tail.
|
|
97
|
+
last = min(int(np.searchsorted(y, 1.0)), len(x) - 1)
|
|
98
|
+
selected = {0, min(1, len(x) - 1), last, len(x) - 1}
|
|
99
|
+
seed_count = min(9, config.max_points - len(selected) + 1)
|
|
100
|
+
for target in np.linspace(0, 1, seed_count)[1:-1]:
|
|
101
|
+
index = min(int(np.searchsorted(y, target)), len(x) - 1)
|
|
102
|
+
ordered = sorted(selected)
|
|
103
|
+
slot = int(np.searchsorted(ordered, index))
|
|
104
|
+
if index in selected or slot == 0 or slot == len(ordered):
|
|
105
|
+
continue
|
|
106
|
+
if min(y[index] - y[ordered[slot - 1]], y[ordered[slot]] - y[index]) >= config.min_mass:
|
|
107
|
+
selected.add(index)
|
|
108
|
+
while True:
|
|
109
|
+
indices = np.array(sorted(selected), dtype=int)
|
|
110
|
+
curve = interpolate_cdf(x[indices], y[indices], interpolation)
|
|
111
|
+
residual = np.abs(curve(x) - y)
|
|
112
|
+
error = float(np.max(residual))
|
|
113
|
+
if error <= config.tolerance:
|
|
114
|
+
return AnchorFit(curve, indices, error, "tolerance")
|
|
115
|
+
if len(selected) >= config.max_points:
|
|
116
|
+
return AnchorFit(curve, indices, error, "max_points")
|
|
117
|
+
queue = []
|
|
118
|
+
for lo, hi in zip(indices[:-1], indices[1:]):
|
|
119
|
+
# Search only nodes with enough probability mass on both sides.
|
|
120
|
+
start = max(lo + 1, int(np.searchsorted(y, y[lo] + config.min_mass, side="left")))
|
|
121
|
+
end = min(hi, int(np.searchsorted(y, y[hi] - config.min_mass, side="right")))
|
|
122
|
+
if start >= end:
|
|
123
|
+
continue
|
|
124
|
+
index = start + int(np.argmax(residual[start:end]))
|
|
125
|
+
if residual[index] > config.tolerance:
|
|
126
|
+
heappush(queue, (-float(residual[index]), index))
|
|
127
|
+
if not queue:
|
|
128
|
+
return AnchorFit(curve, indices, error, "min_mass")
|
|
129
|
+
for _ in range(min(8, config.max_points - len(selected), len(queue))):
|
|
130
|
+
_, index = heappop(queue)
|
|
131
|
+
selected.add(index)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Raw weighted samples -> continuous CDF, optionally its analytic derivative.
|
|
2
|
+
|
|
3
|
+
PDF1D and CDF1D share the reconstruction. No histogram or sample-grid
|
|
4
|
+
renormalisation participates in fitting: the grid only evaluates the interpolant.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any, Mapping
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import pandas as pd
|
|
12
|
+
from scipy.interpolate import PchipInterpolator
|
|
13
|
+
|
|
14
|
+
from ..distribution_1d_config import KINDS, settings
|
|
15
|
+
from ..memtrace import memtrace_checkpoint, memtrace_object_inventory
|
|
16
|
+
from ..utils.expression import eval_dataframe_expression
|
|
17
|
+
from .adaptive_cdf_runtime import fit_anchors
|
|
18
|
+
|
|
19
|
+
ALGORITHM_REVISION = "weighted-ecdf-adaptive-bernstein-c2-v3"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _values(df: pd.DataFrame, expr: Any, logger=None) -> np.ndarray:
|
|
23
|
+
values = np.asarray(eval_dataframe_expression(df, expr, logger=logger), dtype=float)
|
|
24
|
+
if values.ndim == 0:
|
|
25
|
+
return np.full(len(df), float(values))
|
|
26
|
+
if values.shape != (len(df),):
|
|
27
|
+
raise ValueError(f"expression {expr!r} must return one value per input row, got {values.shape}")
|
|
28
|
+
return values
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _infer_limits(x: np.ndarray, scale: str) -> tuple[float, float]:
|
|
32
|
+
unique = np.unique(x)
|
|
33
|
+
if len(unique) < 2:
|
|
34
|
+
raise ValueError("one unique sample cannot define a continuous support; set coordinates.x.lim")
|
|
35
|
+
lo = unique[0] - 0.5 * (unique[1] - unique[0])
|
|
36
|
+
hi = unique[-1] + 0.5 * (unique[-1] - unique[-2])
|
|
37
|
+
if scale == "log":
|
|
38
|
+
if unique[0] <= 0:
|
|
39
|
+
raise ValueError("coordinates.x.scale: log requires positive samples")
|
|
40
|
+
lo = max(lo, unique[0] * 0.5)
|
|
41
|
+
if not np.isfinite(lo) or not np.isfinite(hi) or not lo < hi:
|
|
42
|
+
raise ValueError("cannot infer finite support; set coordinates.x.lim")
|
|
43
|
+
return float(lo), float(hi)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def empirical_knots(x: np.ndarray, weights: np.ndarray, limits: tuple[float, float]):
|
|
47
|
+
"""Inclusive empirical nodes plus support endpoints; return largest atom too."""
|
|
48
|
+
lo, hi = limits
|
|
49
|
+
order = np.argsort(x, kind="stable")
|
|
50
|
+
x, weights = x[order], weights[order]
|
|
51
|
+
unique, first = np.unique(x, return_index=True)
|
|
52
|
+
# Rescaling before summation avoids overflow without changing probability.
|
|
53
|
+
scaled = weights / weights.max()
|
|
54
|
+
mass = np.add.reduceat(scaled, first)
|
|
55
|
+
mass /= mass.sum()
|
|
56
|
+
cumulative = np.minimum(np.cumsum(mass), 1.0)
|
|
57
|
+
cumulative[-1] = 1.0
|
|
58
|
+
interior = (unique > lo) & (unique < hi)
|
|
59
|
+
knots = np.r_[lo, unique[interior], hi]
|
|
60
|
+
probabilities = np.r_[0.0, cumulative[interior], 1.0]
|
|
61
|
+
return knots, probabilities, float(mass.max())
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def build_cdf(x: np.ndarray, weights: np.ndarray, limits: tuple[float, float]) -> PchipInterpolator:
|
|
65
|
+
"""Original all-anchor PCHIP path, useful for exact empirical-node comparisons.
|
|
66
|
+
|
|
67
|
+
A sample exactly at lo cannot retain its jump with F(lo)=0; its mass is
|
|
68
|
+
carried into the first interval. Other empirical nodes retain their CDF.
|
|
69
|
+
Production reconstruction uses fit_anchors with the selected settings.
|
|
70
|
+
"""
|
|
71
|
+
knots, probabilities, _ = empirical_knots(x, weights, limits)
|
|
72
|
+
return PchipInterpolator(knots, probabilities, extrapolate=False)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def distribution_1d(df: pd.DataFrame, cfg: Mapping[str, Any], kind: str, logger=None) -> pd.DataFrame:
|
|
76
|
+
if kind not in KINDS:
|
|
77
|
+
raise ValueError(f"unknown distribution transform {kind!r}")
|
|
78
|
+
if not isinstance(df, pd.DataFrame) or df.empty:
|
|
79
|
+
raise ValueError(f"{kind} needs a nonempty pandas sample table")
|
|
80
|
+
opts = settings(cfg)
|
|
81
|
+
memtrace_checkpoint(logger, f"pipeline.{kind}.before", df)
|
|
82
|
+
x = _values(df, opts.x["expr"], logger)
|
|
83
|
+
weights = _values(df, opts.weight["expr"], logger) if opts.weight else np.ones(len(df))
|
|
84
|
+
if not np.all(np.isfinite(x)) or not np.all(np.isfinite(weights)):
|
|
85
|
+
raise ValueError(f"{kind} needs finite samples and weights; filter invalid rows first")
|
|
86
|
+
if np.any(weights < 0):
|
|
87
|
+
raise ValueError(f"{kind} needs nonnegative probability weights; signed event weights are not a CDF")
|
|
88
|
+
work = pd.DataFrame({"x": x, "weight": weights})
|
|
89
|
+
if opts.repeat is not None:
|
|
90
|
+
if opts.repeat not in df.columns:
|
|
91
|
+
raise KeyError(f"{kind} missing repeat column {opts.repeat!r}")
|
|
92
|
+
if df[opts.repeat].isna().any():
|
|
93
|
+
raise ValueError(f"{kind} repeat identifiers must not be missing")
|
|
94
|
+
work["repeat"] = df[opts.repeat].to_numpy()
|
|
95
|
+
positive = weights > 0
|
|
96
|
+
if not positive.any():
|
|
97
|
+
raise ValueError(f"{kind} weights sum to zero")
|
|
98
|
+
limits = opts.limits or _infer_limits(x[positive], opts.scale)
|
|
99
|
+
lo, hi = limits
|
|
100
|
+
if np.any((x[positive] < lo) | (x[positive] > hi)):
|
|
101
|
+
raise ValueError(f"{kind} coordinates.x.lim must contain all positive-weight samples; filter first to truncate")
|
|
102
|
+
grid = np.geomspace(lo, hi, opts.grid) if opts.scale == "log" else np.linspace(lo, hi, opts.grid)
|
|
103
|
+
memtrace_object_inventory(logger, f"pipeline.{kind}.samples", {"samples": work})
|
|
104
|
+
curves = ("cdf", "pdf") if kind == "PDF1D" else ("cdf",)
|
|
105
|
+
mean = {key: np.zeros(opts.grid) for key in curves}
|
|
106
|
+
m2 = {key: np.zeros(opts.grid) for key in curves}
|
|
107
|
+
groups = work.groupby("repeat", sort=False, observed=True) if opts.repeat else [(None, work)]
|
|
108
|
+
n = 0
|
|
109
|
+
boundary_mass = False
|
|
110
|
+
diagnostics = []
|
|
111
|
+
for label, block in groups:
|
|
112
|
+
block = block.loc[block.weight > 0]
|
|
113
|
+
if block.empty:
|
|
114
|
+
raise ValueError(f"{kind} repeat {label!r} weights sum to zero")
|
|
115
|
+
bx, bw = block.x.to_numpy(), block.weight.to_numpy()
|
|
116
|
+
boundary_mass |= bool(np.any(bx == lo))
|
|
117
|
+
knots, probabilities, largest_jump = empirical_knots(bx, bw, limits)
|
|
118
|
+
fit = fit_anchors(knots, probabilities, opts.anchors, opts.interpolation)
|
|
119
|
+
cdf = fit.curve
|
|
120
|
+
diagnostics.append({
|
|
121
|
+
"repeat": label.item() if isinstance(label, np.generic) else label,
|
|
122
|
+
"n_anchors": len(fit.indices), "n_candidates": len(knots),
|
|
123
|
+
"max_cdf_error": fit.max_error, "stop_reason": fit.stop_reason,
|
|
124
|
+
"largest_empirical_jump": largest_jump,
|
|
125
|
+
})
|
|
126
|
+
if logger is not None:
|
|
127
|
+
logger.debug(f"{kind} repeat={label!r}: anchors={len(fit.indices)}/{len(knots)}, "
|
|
128
|
+
f"max CDF node error={fit.max_error:.6g}, stopped={fit.stop_reason}")
|
|
129
|
+
if opts.anchors.method == "adaptive" and fit.max_error > opts.anchors.tolerance:
|
|
130
|
+
logger.warning(f"{kind} repeat={label!r}: CDF error {fit.max_error:.6g} exceeds "
|
|
131
|
+
f"tolerance {opts.anchors.tolerance:g}; stopped at {fit.stop_reason}. "
|
|
132
|
+
"Adjust anchors settings if finer CDF reconstruction is needed.")
|
|
133
|
+
if opts.anchors.method == "adaptive" and largest_jump > max(2 * opts.anchors.tolerance, opts.anchors.min_mass):
|
|
134
|
+
logger.warning(f"{kind} repeat={label!r}: empirical probability jump {largest_jump:.6g}; "
|
|
135
|
+
"a continuous density spreads this discrete mass over an interval")
|
|
136
|
+
values = {"cdf": cdf(grid)}
|
|
137
|
+
if kind == "PDF1D":
|
|
138
|
+
pdf = cdf.derivative()(grid)
|
|
139
|
+
tolerance = 128 * np.finfo(float).eps * max(1.0, float(np.max(np.abs(pdf))))
|
|
140
|
+
if np.min(pdf) < -tolerance:
|
|
141
|
+
raise ValueError(f"{kind} produced a negative density beyond floating-point tolerance")
|
|
142
|
+
values["pdf"] = np.maximum(pdf, 0.0)
|
|
143
|
+
n += 1
|
|
144
|
+
for key in curves:
|
|
145
|
+
if not np.all(np.isfinite(values[key])):
|
|
146
|
+
raise ValueError(f"{kind} produced nonfinite {key}; inspect sample spacing/support")
|
|
147
|
+
delta = values[key] - mean[key]
|
|
148
|
+
mean[key] += delta / n
|
|
149
|
+
m2[key] += delta * (values[key] - mean[key])
|
|
150
|
+
out = {opts.name: grid}
|
|
151
|
+
for key in curves:
|
|
152
|
+
out[key] = mean[key]
|
|
153
|
+
out[f"{key}_std"] = np.sqrt(np.maximum(m2[key], 0.0) / (n - 1)) if n > 1 else np.full(opts.grid, np.nan)
|
|
154
|
+
out["n_repeats"] = n
|
|
155
|
+
table = pd.DataFrame(out)
|
|
156
|
+
table.attrs["distribution_1d"] = {
|
|
157
|
+
"algorithm": ALGORITHM_REVISION, "interpolation": opts.interpolation,
|
|
158
|
+
"anchor_method": opts.anchors.method, "support": limits, "repeats": diagnostics,
|
|
159
|
+
}
|
|
160
|
+
if logger is not None:
|
|
161
|
+
if boundary_mass:
|
|
162
|
+
logger.warning(f"{kind}: mass at the lower support boundary is spread over the first CDF interval")
|
|
163
|
+
logger.debug(f"{kind}: {len(df)} samples -> {opts.grid} points, {n} repeats, support={limits}, std ddof=1")
|
|
164
|
+
memtrace_checkpoint(logger, f"pipeline.{kind}.after", table)
|
|
165
|
+
return table
|
|
@@ -327,12 +327,16 @@ class DataPreprocessor:
|
|
|
327
327
|
)
|
|
328
328
|
|
|
329
329
|
def _transform_input_columns(self, transform: Any) -> List[str]:
|
|
330
|
+
from ..distribution_1d_config import distribution_config, distribution_input_columns, distribution_kind
|
|
331
|
+
|
|
330
332
|
out: set[str] = set()
|
|
331
333
|
if not isinstance(transform, list):
|
|
332
334
|
return []
|
|
333
335
|
for step in transform:
|
|
334
336
|
if not isinstance(step, Mapping):
|
|
335
337
|
continue
|
|
338
|
+
if distribution_kind(step) is not None:
|
|
339
|
+
out.update(distribution_input_columns(distribution_config(step)))
|
|
336
340
|
if "filter" in step:
|
|
337
341
|
out.update(self._expr_symbols(step.get("filter")))
|
|
338
342
|
if "sortby" in step:
|
|
@@ -357,12 +361,17 @@ class DataPreprocessor:
|
|
|
357
361
|
return sorted(out)
|
|
358
362
|
|
|
359
363
|
def _transform_output_columns(self, transform: Any) -> List[str]:
|
|
364
|
+
from ..distribution_1d_config import distribution_config, distribution_kind, distribution_output_columns
|
|
365
|
+
|
|
360
366
|
out: set[str] = set()
|
|
361
367
|
if not isinstance(transform, list):
|
|
362
368
|
return []
|
|
363
369
|
for step in transform:
|
|
364
370
|
if not isinstance(step, Mapping):
|
|
365
371
|
continue
|
|
372
|
+
kind = distribution_kind(step)
|
|
373
|
+
if kind is not None:
|
|
374
|
+
out.update(distribution_output_columns(kind, distribution_config(step)))
|
|
366
375
|
if "add_column" in step:
|
|
367
376
|
add_cfg = step.get("add_column", {})
|
|
368
377
|
if isinstance(add_cfg, Mapping):
|
|
@@ -937,12 +946,18 @@ class DataPreprocessor:
|
|
|
937
946
|
return self._stable_hash(payload)
|
|
938
947
|
|
|
939
948
|
def _runtime_profile_tokens(self, transform: Any) -> List[Dict[str, Any]]:
|
|
949
|
+
from ..distribution_1d_config import distribution_kind
|
|
950
|
+
from .distribution_1d_runtime import ALGORITHM_REVISION
|
|
951
|
+
|
|
940
952
|
tokens: List[Dict[str, Any]] = []
|
|
941
953
|
if not isinstance(transform, list):
|
|
942
954
|
return tokens
|
|
943
955
|
for step in transform:
|
|
944
956
|
if not isinstance(step, Mapping):
|
|
945
957
|
continue
|
|
958
|
+
kind = distribution_kind(step)
|
|
959
|
+
if kind is not None:
|
|
960
|
+
tokens.append({"kind": kind, "algo": ALGORITHM_REVISION, "cfg": deepcopy(step)})
|
|
946
961
|
if "profile" in step:
|
|
947
962
|
cfg = step.get("profile", {})
|
|
948
963
|
if isinstance(cfg, Mapping):
|
|
@@ -16,6 +16,8 @@ from ..memtrace import memtrace_checkpoint, memtrace_object_inventory
|
|
|
16
16
|
from ..utils.expression import eval_dataframe_expression
|
|
17
17
|
from ..utils.pathing import resolve_project_path
|
|
18
18
|
from .bin_stat_runtime import bin_stat, bin_stat_config, is_bin_stat_transform
|
|
19
|
+
from ..distribution_1d_config import distribution_config, distribution_kind
|
|
20
|
+
from .distribution_1d_runtime import distribution_1d
|
|
19
21
|
from .correlation_runtime import correlation, correlation_config, is_correlation_transform
|
|
20
22
|
from .significance_runtime import (
|
|
21
23
|
is_significance_transform,
|
|
@@ -74,6 +76,8 @@ HEAVY_TRANSFORM_KEYS = frozenset(
|
|
|
74
76
|
"make_density_core",
|
|
75
77
|
"posterior_density",
|
|
76
78
|
"make_interp_2d",
|
|
79
|
+
"PDF1D",
|
|
80
|
+
"CDF1D",
|
|
77
81
|
"type",
|
|
78
82
|
# Not heavy to compute, but cross-block: what a ``to_df`` name resolves
|
|
79
83
|
# to, and the fact that a producing block hands the layer nothing, only
|
|
@@ -599,6 +603,8 @@ def apply_transforms_impl(
|
|
|
599
603
|
preprocessor._safe_nrows(df),
|
|
600
604
|
)
|
|
601
605
|
)
|
|
606
|
+
elif distribution_kind(trans) is not None:
|
|
607
|
+
df = distribution_1d(df, distribution_config(trans), distribution_kind(trans), preprocessor.logger)
|
|
602
608
|
elif is_bin_stat_transform(trans):
|
|
603
609
|
before_rows = preprocessor._safe_nrows(df)
|
|
604
610
|
df = bin_stat(df, bin_stat_config(trans), preprocessor.logger)
|
|
@@ -270,6 +270,11 @@ def _collect_expr_columns(obj: Any, out: Set[str]) -> None:
|
|
|
270
270
|
if key == "make_interp_2d":
|
|
271
271
|
out.update(_interp_2d_cfg_input_columns(v))
|
|
272
272
|
continue
|
|
273
|
+
if key in {"pdf1d", "cdf1d"}:
|
|
274
|
+
from .distribution_1d_config import distribution_input_columns
|
|
275
|
+
|
|
276
|
+
out.update(distribution_input_columns(v))
|
|
277
|
+
continue
|
|
273
278
|
if key == "bin_stat":
|
|
274
279
|
out.update(_bin_stat_cfg_columns(v, "input"))
|
|
275
280
|
continue
|
|
@@ -381,12 +386,17 @@ def _transform_needs_all_columns(transform: Any) -> bool:
|
|
|
381
386
|
|
|
382
387
|
def _transform_output_columns(transform: Any) -> Set[str]:
|
|
383
388
|
"""Return column names produced as outputs by a transform list."""
|
|
389
|
+
from .distribution_1d_config import distribution_kind, distribution_output_columns
|
|
390
|
+
|
|
384
391
|
out: Set[str] = set()
|
|
385
392
|
if not isinstance(transform, list):
|
|
386
393
|
return out
|
|
387
394
|
for step in transform:
|
|
388
395
|
if not isinstance(step, Mapping):
|
|
389
396
|
continue
|
|
397
|
+
kind = distribution_kind(step)
|
|
398
|
+
if kind is not None:
|
|
399
|
+
out.update(distribution_output_columns(kind, step.get(kind)))
|
|
390
400
|
if "bin_stat" in step:
|
|
391
401
|
out.update(_bin_stat_cfg_columns(step.get("bin_stat"), "output"))
|
|
392
402
|
ccfg = _correlation_transform_config(step)
|
|
@@ -154,6 +154,12 @@ class DataSet():
|
|
|
154
154
|
"columns": self.columns,
|
|
155
155
|
"full_load": bool(getattr(self, "full_load", False)),
|
|
156
156
|
}
|
|
157
|
+
from .distribution_1d_config import distribution_kind
|
|
158
|
+
|
|
159
|
+
if isinstance(self.transform, list) and any(distribution_kind(step) for step in self.transform):
|
|
160
|
+
from .Figure.distribution_1d_runtime import ALGORITHM_REVISION
|
|
161
|
+
|
|
162
|
+
extra["distribution_1d_algorithm"] = ALGORITHM_REVISION
|
|
157
163
|
if self.type in IN_MEMORY_DATASET_TYPES:
|
|
158
164
|
# Nothing on disk to fingerprint; identity is the declaration alone.
|
|
159
165
|
return {"path": None, "size": None, "mtime_ns": None, "md5": None, **extra}
|
|
@@ -31,6 +31,8 @@ from .utils.dataframes import polars_to_pandas
|
|
|
31
31
|
from .utils.pathing import resolve_project_path
|
|
32
32
|
from .Figure.preprocessor_runtime import add_column, drop_columns, filter_df, keep_columns, sort_by
|
|
33
33
|
from .Figure.profile_runtime import profiling
|
|
34
|
+
from .distribution_1d_config import distribution_config, distribution_kind
|
|
35
|
+
from .Figure.distribution_1d_runtime import distribution_1d
|
|
34
36
|
|
|
35
37
|
JP_ROW_IDX = "__jp_row_idx__"
|
|
36
38
|
|
|
@@ -204,6 +206,8 @@ def apply_dataset_transform(dataset, stage: str = "dataset") -> None:
|
|
|
204
206
|
cfg = dict(posterior_density_config(trans))
|
|
205
207
|
cfg.setdefault("_base_dir", getattr(dataset, "rootpath", None))
|
|
206
208
|
df = posterior_density(df, cfg, dataset.logger)
|
|
209
|
+
elif distribution_kind(trans) is not None:
|
|
210
|
+
df = distribution_1d(df, distribution_config(trans), distribution_kind(trans), dataset.logger)
|
|
207
211
|
elif is_interp_2d_transform(trans):
|
|
208
212
|
df = make_interp_2d(df, interp_2d_config(trans), dataset.logger)
|
|
209
213
|
elif "sortby" in trans:
|
|
@@ -411,7 +415,8 @@ def _apply_dataset_transform_polars(
|
|
|
411
415
|
elif "drop_columns" in trans:
|
|
412
416
|
lf = drop_columns(lf, trans.get("drop_columns"), dataset.logger)
|
|
413
417
|
elif (
|
|
414
|
-
|
|
418
|
+
distribution_kind(trans) is not None
|
|
419
|
+
or "profile" in trans
|
|
415
420
|
or "make_density_core" in trans
|
|
416
421
|
or "posterior_density" in trans
|
|
417
422
|
or "make_interp_2d" in trans
|