scitex 2.7.3__py3-none-any.whl → 2.10.0__py3-none-any.whl
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.
- scitex/__init__.py +15 -7
- scitex/__version__.py +1 -2
- scitex/_install_guide.py +250 -0
- scitex/_optional_deps.py +206 -39
- scitex/ai/_gen_ai/_Groq.py +2 -4
- scitex/ai/_gen_ai/_OpenAI.py +5 -2
- scitex/ai/_gen_ai/_Perplexity.py +20 -6
- scitex/audio/__init__.py +24 -15
- scitex/audio/_cross_process_lock.py +139 -0
- scitex/audio/_mcp_handlers.py +256 -0
- scitex/audio/_mcp_tool_schemas.py +203 -0
- scitex/audio/engines/elevenlabs_engine.py +5 -2
- scitex/audio/mcp_server.py +98 -457
- scitex/bridge/__init__.py +30 -19
- scitex/bridge/_figrecipe.py +245 -0
- scitex/bridge/_helpers.py +2 -1
- scitex/bridge/_plt_vis.py +23 -10
- scitex/bridge/_stats_plt.py +18 -5
- scitex/bridge/_stats_vis.py +16 -2
- scitex/browser/__init__.py +84 -44
- scitex/browser/automation/__init__.py +5 -1
- scitex/browser/core/BrowserMixin.py +17 -4
- scitex/browser/core/__init__.py +11 -2
- scitex/browser/remote/CaptchaHandler.py +1 -1
- scitex/browser/remote/ZenRowsAPIClient.py +1 -1
- scitex/capture/grid.py +487 -0
- scitex/capture/mcp_handlers.py +401 -0
- scitex/capture/mcp_tool_defs.py +192 -0
- scitex/capture/mcp_tools.py +241 -0
- scitex/capture/mcp_utils.py +30 -0
- scitex/cli/convert.py +421 -0
- scitex/cli/main.py +6 -4
- scitex/datetime/__init__.py +46 -0
- scitex/datetime/_linspace.py +100 -0
- scitex/datetime/_normalize_timestamp.py +306 -0
- scitex/db/_delete_duplicates.py +4 -4
- scitex/db/_sqlite3/_delete_duplicates.py +11 -2
- scitex/dev/plt/__init__.py +61 -62
- scitex/dev/plt/demo_plotters/__init__.py +0 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
- scitex/dev/plt/mpl/get_dir_ax.py +46 -0
- scitex/dev/plt/mpl/get_signatures.py +176 -0
- scitex/dev/plt/mpl/get_signatures_details.py +522 -0
- scitex/dev/plt/plot_mpl_axhline.py +0 -0
- scitex/dev/plt/plot_mpl_axhspan.py +0 -0
- scitex/dev/plt/plot_mpl_axvline.py +0 -0
- scitex/dev/plt/plot_mpl_axvspan.py +0 -0
- scitex/dev/plt/plot_mpl_bar.py +0 -0
- scitex/dev/plt/plot_mpl_barh.py +0 -0
- scitex/dev/plt/plot_mpl_boxplot.py +0 -0
- scitex/dev/plt/plot_mpl_contour.py +0 -0
- scitex/dev/plt/plot_mpl_contourf.py +0 -0
- scitex/dev/plt/plot_mpl_errorbar.py +0 -0
- scitex/dev/plt/plot_mpl_eventplot.py +0 -0
- scitex/dev/plt/plot_mpl_fill.py +0 -0
- scitex/dev/plt/plot_mpl_fill_between.py +0 -0
- scitex/dev/plt/plot_mpl_hexbin.py +0 -0
- scitex/dev/plt/plot_mpl_hist.py +0 -0
- scitex/dev/plt/plot_mpl_hist2d.py +0 -0
- scitex/dev/plt/plot_mpl_imshow.py +0 -0
- scitex/dev/plt/plot_mpl_pcolormesh.py +0 -0
- scitex/dev/plt/plot_mpl_pie.py +0 -0
- scitex/dev/plt/plot_mpl_plot.py +0 -0
- scitex/dev/plt/plot_mpl_quiver.py +0 -0
- scitex/dev/plt/plot_mpl_scatter.py +0 -0
- scitex/dev/plt/plot_mpl_stackplot.py +0 -0
- scitex/dev/plt/plot_mpl_stem.py +0 -0
- scitex/dev/plt/plot_mpl_step.py +0 -0
- scitex/dev/plt/plot_mpl_violinplot.py +0 -0
- scitex/dev/plt/plot_sns_barplot.py +0 -0
- scitex/dev/plt/plot_sns_boxplot.py +0 -0
- scitex/dev/plt/plot_sns_heatmap.py +0 -0
- scitex/dev/plt/plot_sns_histplot.py +0 -0
- scitex/dev/plt/plot_sns_kdeplot.py +0 -0
- scitex/dev/plt/plot_sns_lineplot.py +0 -0
- scitex/dev/plt/plot_sns_scatterplot.py +0 -0
- scitex/dev/plt/plot_sns_stripplot.py +0 -0
- scitex/dev/plt/plot_sns_swarmplot.py +0 -0
- scitex/dev/plt/plot_sns_violinplot.py +0 -0
- scitex/dev/plt/plot_stx_bar.py +0 -0
- scitex/dev/plt/plot_stx_barh.py +0 -0
- scitex/dev/plt/plot_stx_box.py +0 -0
- scitex/dev/plt/plot_stx_boxplot.py +0 -0
- scitex/dev/plt/plot_stx_conf_mat.py +0 -0
- scitex/dev/plt/plot_stx_contour.py +0 -0
- scitex/dev/plt/plot_stx_ecdf.py +0 -0
- scitex/dev/plt/plot_stx_errorbar.py +0 -0
- scitex/dev/plt/plot_stx_fill_between.py +0 -0
- scitex/dev/plt/plot_stx_fillv.py +0 -0
- scitex/dev/plt/plot_stx_heatmap.py +0 -0
- scitex/dev/plt/plot_stx_image.py +0 -0
- scitex/dev/plt/plot_stx_imshow.py +0 -0
- scitex/dev/plt/plot_stx_joyplot.py +0 -0
- scitex/dev/plt/plot_stx_kde.py +0 -0
- scitex/dev/plt/plot_stx_line.py +0 -0
- scitex/dev/plt/plot_stx_mean_ci.py +0 -0
- scitex/dev/plt/plot_stx_mean_std.py +0 -0
- scitex/dev/plt/plot_stx_median_iqr.py +0 -0
- scitex/dev/plt/plot_stx_raster.py +0 -0
- scitex/dev/plt/plot_stx_rectangle.py +0 -0
- scitex/dev/plt/plot_stx_scatter.py +0 -0
- scitex/dev/plt/plot_stx_shaded_line.py +0 -0
- scitex/dev/plt/plot_stx_violin.py +0 -0
- scitex/dev/plt/plot_stx_violinplot.py +0 -0
- scitex/diagram/README.md +197 -0
- scitex/diagram/__init__.py +48 -0
- scitex/diagram/_compile.py +312 -0
- scitex/diagram/_diagram.py +355 -0
- scitex/diagram/_presets.py +173 -0
- scitex/diagram/_schema.py +182 -0
- scitex/diagram/_split.py +278 -0
- scitex/dict/_pop_keys.py +1 -7
- scitex/dsp/__init__.py +15 -10
- scitex/dsp/add_noise.py +5 -2
- scitex/dsp/example.py +35 -22
- scitex/dsp/filt.py +8 -3
- scitex/dsp/reference.py +3 -2
- scitex/dsp/utils/__init__.py +2 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
- scitex/dt/__init__.py +39 -2
- scitex/errors.py +82 -521
- scitex/fig/__init__.py +4 -4
- scitex/fig/editor/__init__.py +5 -2
- scitex/fig/editor/_dearpygui_editor.py +1 -1
- scitex/fig/editor/_mpl_editor.py +1 -1
- scitex/fig/editor/_qt_editor.py +1 -1
- scitex/fig/editor/_tkinter_editor.py +1 -1
- scitex/fig/editor/edit/__init__.py +50 -0
- scitex/fig/editor/edit/backend_detector.py +109 -0
- scitex/fig/editor/edit/bundle_resolver.py +240 -0
- scitex/fig/editor/edit/editor_launcher.py +239 -0
- scitex/fig/editor/edit/manual_handler.py +53 -0
- scitex/fig/editor/edit/panel_loader.py +232 -0
- scitex/fig/editor/edit/path_resolver.py +67 -0
- scitex/fig/editor/flask_editor/_bbox.py +23 -0
- scitex/fig/editor/flask_editor/_core.py +908 -103
- scitex/fig/editor/flask_editor/_renderer.py +74 -0
- scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
- scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
- scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
- scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
- scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
- scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
- scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
- scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
- scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
- scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
- scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
- scitex/fig/editor/flask_editor/static/css/index.css +31 -0
- scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
- scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
- scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
- scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
- scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
- scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
- scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
- scitex/fig/editor/flask_editor/static/js/main.js +426 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
- scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
- scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
- scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
- scitex/fig/editor/flask_editor/templates/__init__.py +95 -5
- scitex/fig/editor/flask_editor/templates/_html.py +27 -9
- scitex/fig/editor/flask_editor/templates/_scripts.py +1928 -131
- scitex/fig/editor/flask_editor/templates/_styles.py +363 -51
- scitex/fig/io/_bundle.py +104 -19
- scitex/fts/README.md +262 -0
- scitex/fts/TODO.md +66 -0
- scitex/fts/__init__.py +90 -0
- scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
- scitex/fts/_bundle/_FTS.py +657 -0
- scitex/fts/_bundle/__init__.py +38 -0
- scitex/fts/_bundle/_children.py +216 -0
- scitex/fts/_bundle/_conversion/__init__.py +15 -0
- scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
- scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
- scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
- scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
- scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
- scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
- scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
- scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
- scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
- scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
- scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
- scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
- scitex/fts/_bundle/_extractors/__init__.py +32 -0
- scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
- scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
- scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
- scitex/fts/_bundle/_loader.py +134 -0
- scitex/fts/_bundle/_mpl_helpers.py +389 -0
- scitex/fts/_bundle/_saver.py +269 -0
- scitex/fts/_bundle/_storage.py +200 -0
- scitex/fts/_bundle/_utils/__init__.py +55 -0
- scitex/fts/_bundle/_utils/_const.py +26 -0
- scitex/fts/_bundle/_utils/_errors.py +73 -0
- scitex/fts/_bundle/_utils/_generate.py +21 -0
- scitex/fts/_bundle/_utils/_types.py +76 -0
- scitex/fts/_bundle/_validation.py +434 -0
- scitex/fts/_bundle/_zipbundle.py +165 -0
- scitex/fts/_fig/__init__.py +22 -0
- scitex/fts/_fig/_backend/__init__.py +53 -0
- scitex/fts/_fig/_backend/_export.py +165 -0
- scitex/fts/_fig/_backend/_parser.py +188 -0
- scitex/fts/_fig/_backend/_render.py +538 -0
- scitex/fts/_fig/_composite.py +345 -0
- scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
- scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_fig/_dataclasses/__init__.py +47 -0
- scitex/fts/_fig/_editor/__init__.py +14 -0
- scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
- scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
- scitex/fts/_fig/_editor/_defaults.py +300 -0
- scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
- scitex/fts/_fig/_models/_Annotations.py +115 -0
- scitex/fts/_fig/_models/_Axes.py +152 -0
- scitex/fts/_fig/_models/_Figure.py +138 -0
- scitex/fts/_fig/_models/_Guides.py +104 -0
- scitex/fts/_fig/_models/_Plot.py +123 -0
- scitex/fts/_fig/_models/_Styles.py +245 -0
- scitex/fts/_fig/_models/__init__.py +80 -0
- scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
- scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
- scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
- scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_fig/_utils/__init__.py +129 -0
- scitex/fts/_fig/_utils/_auto_layout.py +127 -0
- scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
- scitex/fts/_fig/_utils/_const_sizes.py +48 -0
- scitex/fts/_fig/_utils/_convert_coords.py +77 -0
- scitex/fts/_fig/_utils/_get_template.py +178 -0
- scitex/fts/_fig/_utils/_normalize.py +73 -0
- scitex/fts/_fig/_utils/_plot_layout.py +397 -0
- scitex/fts/_fig/_utils/_validate.py +197 -0
- scitex/fts/_kinds/__init__.py +45 -0
- scitex/fts/_kinds/_figure/__init__.py +19 -0
- scitex/fts/_kinds/_figure/_composite.py +345 -0
- scitex/fts/_kinds/_plot/__init__.py +25 -0
- scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
- scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
- scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
- scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
- scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
- scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
- scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
- scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
- scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
- scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
- scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
- scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
- scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
- scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
- scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
- scitex/fts/_kinds/_shape/__init__.py +141 -0
- scitex/fts/_kinds/_stats/__init__.py +56 -0
- scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_kinds/_table/__init__.py +72 -0
- scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
- scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
- scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
- scitex/fts/_kinds/_table/_latex/_export.py +279 -0
- scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
- scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
- scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
- scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
- scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
- scitex/fts/_kinds/_text/__init__.py +77 -0
- scitex/fts/_schemas/data_info.schema.json +75 -0
- scitex/fts/_schemas/encoding.schema.json +90 -0
- scitex/fts/_schemas/node.schema.json +145 -0
- scitex/fts/_schemas/render_manifest.schema.json +62 -0
- scitex/fts/_schemas/stats.schema.json +132 -0
- scitex/fts/_schemas/theme.schema.json +141 -0
- scitex/fts/_stats/__init__.py +48 -0
- scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_tables/__init__.py +65 -0
- scitex/fts/_tables/_latex/__init__.py +93 -0
- scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
- scitex/fts/_tables/_latex/_editor/_app.py +725 -0
- scitex/fts/_tables/_latex/_export.py +279 -0
- scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
- scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
- scitex/fts/_tables/_latex/_table_exporter.py +362 -0
- scitex/fts/_tables/_latex/_utils.py +369 -0
- scitex/fts/_tables/_latex/_validator.py +445 -0
- scitex/gen/__init__.py +66 -25
- scitex/gen/misc.py +28 -0
- scitex/io/__init__.py +47 -20
- scitex/io/_load.py +87 -36
- scitex/io/_load_modules/__init__.py +10 -7
- scitex/io/_load_modules/_pandas.py +6 -1
- scitex/io/_save.py +299 -1556
- scitex/io/_save_modules/__init__.py +76 -19
- scitex/io/_save_modules/_figure_utils.py +90 -0
- scitex/io/_save_modules/_image_csv.py +497 -0
- scitex/io/_save_modules/_legends.py +91 -0
- scitex/io/_save_modules/_pltz_bundle.py +356 -0
- scitex/io/_save_modules/_pltz_stx.py +536 -0
- scitex/io/_save_modules/_stx_bundle.py +104 -0
- scitex/io/_save_modules/_symlink.py +96 -0
- scitex/io/_save_modules/_yaml.py +1 -1
- scitex/io/_save_modules/_zarr.py +64 -18
- scitex/io/bundle/README.md +212 -0
- scitex/io/bundle/__init__.py +110 -0
- scitex/io/{_bundle.py → bundle/_core.py} +219 -89
- scitex/io/bundle/_nested.py +713 -0
- scitex/io/bundle/_types.py +74 -0
- scitex/io/bundle/_zip.py +487 -0
- scitex/io/utils/h5_to_zarr.py +1 -1
- scitex/logging/__init__.py +108 -13
- scitex/logging/_errors.py +508 -0
- scitex/logging/_formatters.py +30 -6
- scitex/logging/_warnings.py +261 -0
- scitex/plt/__init__.py +4 -1
- scitex/plt/_figrecipe.py +236 -0
- scitex/plt/_subplots/_AxisWrapper.py +6 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +0 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
- scitex/plt/_subplots/_FigWrapper.py +15 -0
- scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
- scitex/plt/_subplots/_export_as_csv.py +11 -0
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +0 -0
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
- scitex/plt/_subplots/_fonts.py +71 -0
- scitex/plt/_subplots/_mm_layout.py +282 -0
- scitex/plt/gallery/__init__.py +99 -2
- scitex/plt/io/_layered_bundle.py +0 -0
- scitex/plt/styles/_plot_postprocess.py +3 -1
- scitex/plt/utils/_configure_mpl.py +16 -19
- scitex/repro/_RandomStateManager.py +13 -8
- scitex/resource/__init__.py +19 -1
- scitex/resource/_utils/_get_env_info.py +13 -25
- scitex/schema/__init__.py +149 -160
- scitex/schema/_encoding.py +273 -0
- scitex/schema/_figure_elements.py +406 -0
- scitex/schema/_plot.py +0 -0
- scitex/schema/_theme.py +360 -0
- scitex/schema/_validation.py +0 -98
- scitex/scholar/__init__.py +56 -14
- scitex/scholar/auth/ScholarAuthManager.py +1 -1
- scitex/scholar/auth/__init__.py +11 -2
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
- scitex/scholar/config/ScholarConfig.py +1 -1
- scitex/scholar/core/Scholar.py +1 -1
- scitex/session/_decorator.py +18 -16
- scitex/session/_lifecycle.py +9 -11
- scitex/session/template.py +9 -8
- scitex/sh/test_sh.py +72 -0
- scitex/sh/test_sh_simple.py +61 -0
- scitex/stats/__init__.py +221 -97
- scitex/stats/_schema.py +21 -22
- scitex/stats/descriptive/_circular.py +212 -351
- scitex/stats/descriptive/_describe.py +81 -132
- scitex/stats/descriptive/_nan.py +205 -433
- scitex/stats/descriptive/_real.py +127 -141
- scitex/str/_format_plot_text.py +5 -5
- scitex/str/_latex.py +26 -84
- scitex/str/_latex_fallback.py +53 -47
- scitex/web/_search_pubmed.py +5 -4
- scitex/writer/tests/test_diff_between.py +451 -0
- scitex/writer/tests/test_document_section.py +311 -0
- scitex/writer/tests/test_document_workflow.py +393 -0
- scitex/writer/tests/test_writer.py +361 -0
- scitex/writer/tests/test_writer_integration.py +303 -0
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/RECORD +479 -108
- scitex/fig/editor/_edit.py +0 -751
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,63 +1,69 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
# Timestamp: "2025-12-27 (refactored)"
|
|
3
|
+
# File: scitex/stats/descriptive/_circular.py
|
|
4
|
+
"""
|
|
5
|
+
Circular statistics for angular data.
|
|
6
|
+
|
|
7
|
+
Uses torch when available (preserves tensor type), falls back to numpy.
|
|
8
|
+
"""
|
|
9
|
+
|
|
6
10
|
from __future__ import annotations
|
|
11
|
+
|
|
7
12
|
import os
|
|
13
|
+
import warnings
|
|
14
|
+
from typing import List, Optional, Tuple, Union
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
8
17
|
|
|
9
18
|
__FILE__ = __file__
|
|
10
19
|
__DIR__ = os.path.dirname(__FILE__)
|
|
11
|
-
# ----------------------------------------
|
|
12
20
|
|
|
13
|
-
|
|
21
|
+
# Optional torch support
|
|
22
|
+
try:
|
|
23
|
+
import torch
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- Computes circular skewness for asymmetry measurement
|
|
20
|
-
- Calculates circular kurtosis for tail behavior analysis
|
|
21
|
-
- Warns if input appears to be in degrees instead of radians
|
|
22
|
-
- Demonstrates circular statistics with synthetic data
|
|
23
|
-
- Saves visualization and statistical results
|
|
24
|
-
|
|
25
|
-
Dependencies:
|
|
26
|
-
- packages:
|
|
27
|
-
- torch
|
|
28
|
-
- numpy
|
|
29
|
-
- scitex
|
|
30
|
-
- matplotlib
|
|
31
|
-
|
|
32
|
-
IO:
|
|
33
|
-
- input-files:
|
|
34
|
-
- angles in radians as torch.Tensor
|
|
35
|
-
- histogram values as torch.Tensor
|
|
36
|
-
- output-files:
|
|
37
|
-
- ./circular_stats_demo.jpg
|
|
38
|
-
- ./circular_statistics.pkl
|
|
39
|
-
"""
|
|
25
|
+
HAS_TORCH = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
torch = None
|
|
28
|
+
HAS_TORCH = False
|
|
40
29
|
|
|
41
|
-
"""Imports"""
|
|
42
|
-
import argparse
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
31
|
+
def _is_torch_tensor(x):
|
|
32
|
+
"""Check if x is a torch tensor."""
|
|
33
|
+
return HAS_TORCH and isinstance(x, torch.Tensor)
|
|
34
|
+
|
|
48
35
|
|
|
49
|
-
|
|
36
|
+
def _normalize_axis(axis, dim):
|
|
37
|
+
"""Normalize axis/dim parameter."""
|
|
38
|
+
return dim if dim is not None else axis
|
|
50
39
|
|
|
51
|
-
logger = logging.getLogger(__name__)
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
def _ensure_more_than_2d(data) -> None:
|
|
42
|
+
"""Ensure data has at least 2 dimensions."""
|
|
43
|
+
ndim = data.ndim if hasattr(data, "ndim") else np.asarray(data).ndim
|
|
44
|
+
assert ndim >= 2, (
|
|
45
|
+
f"Input must be at least 2 dimensional with batch dimension as first axis, got {ndim}"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _check_angle_units(angles) -> None:
|
|
50
|
+
"""Check if angles might be in degrees and warn user."""
|
|
51
|
+
if _is_torch_tensor(angles):
|
|
52
|
+
max_val = torch.max(torch.abs(angles)).item()
|
|
53
|
+
else:
|
|
54
|
+
max_val = np.max(np.abs(angles))
|
|
55
|
+
|
|
56
|
+
if max_val > 2 * np.pi:
|
|
57
|
+
warnings.warn(
|
|
58
|
+
f"Maximum angle value is {max_val:.2f} (>2π). "
|
|
59
|
+
f"Consider using radians or angle wrapping.",
|
|
60
|
+
UserWarning,
|
|
61
|
+
)
|
|
54
62
|
|
|
55
63
|
|
|
56
|
-
# @batch_fn
|
|
57
|
-
@torch_fn
|
|
58
64
|
def describe_circular(
|
|
59
|
-
angles
|
|
60
|
-
values
|
|
65
|
+
angles,
|
|
66
|
+
values,
|
|
61
67
|
axis: int = -1,
|
|
62
68
|
dim: Optional[Union[int, Tuple[int, ...]]] = None,
|
|
63
69
|
keepdims: bool = False,
|
|
@@ -67,16 +73,16 @@ def describe_circular(
|
|
|
67
73
|
"circular_skewness",
|
|
68
74
|
"circular_kurtosis",
|
|
69
75
|
],
|
|
70
|
-
device
|
|
76
|
+
device=None,
|
|
71
77
|
batch_size: int = -1,
|
|
72
|
-
) -> Tuple[
|
|
73
|
-
"""
|
|
78
|
+
) -> Tuple[np.ndarray, List[str]]:
|
|
79
|
+
"""Compute various circular descriptive statistics.
|
|
74
80
|
|
|
75
81
|
Parameters
|
|
76
82
|
----------
|
|
77
|
-
angles :
|
|
83
|
+
angles : array-like
|
|
78
84
|
Input angles in radians with batch dimension as first axis
|
|
79
|
-
values :
|
|
85
|
+
values : array-like
|
|
80
86
|
Histogram values for each angle (must match angles shape)
|
|
81
87
|
axis : int, default=-1
|
|
82
88
|
Deprecated. Use dim instead
|
|
@@ -86,18 +92,18 @@ def describe_circular(
|
|
|
86
92
|
Whether to keep reduced dimensions
|
|
87
93
|
funcs : list of str or "all"
|
|
88
94
|
Circular statistical functions to compute
|
|
89
|
-
device :
|
|
90
|
-
Device
|
|
95
|
+
device : optional
|
|
96
|
+
Device for torch tensors (ignored for numpy)
|
|
91
97
|
batch_size : int, default=-1
|
|
92
|
-
Batch size for processing (
|
|
98
|
+
Batch size for processing (currently unused)
|
|
93
99
|
|
|
94
100
|
Returns
|
|
95
101
|
-------
|
|
96
|
-
Tuple[
|
|
102
|
+
Tuple[ndarray or Tensor, List[str]]
|
|
97
103
|
Computed circular statistics and their names
|
|
98
104
|
"""
|
|
99
|
-
dim = axis
|
|
100
|
-
dim = (dim,) if isinstance(dim, int) else tuple(dim)
|
|
105
|
+
dim = _normalize_axis(axis, dim)
|
|
106
|
+
dim = (dim,) if isinstance(dim, int) else tuple(dim) if dim is not None else None
|
|
101
107
|
|
|
102
108
|
func_names = funcs
|
|
103
109
|
func_candidates = {
|
|
@@ -115,425 +121,280 @@ def describe_circular(
|
|
|
115
121
|
|
|
116
122
|
calculated = [ff(angles, values, dim=dim, keepdims=keepdims) for ff in _funcs]
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
assert data.ndim >= 2, (
|
|
123
|
-
f"Input tensor must be more than 2 dimensional with batch dimension as first axis, got {data.ndim}"
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def _check_angle_units(angles: torch.Tensor) -> None:
|
|
128
|
-
"""Check if angles might be in degrees and warn user.
|
|
129
|
-
|
|
130
|
-
Parameters
|
|
131
|
-
----------
|
|
132
|
-
angles : torch.Tensor
|
|
133
|
-
Input angles to check
|
|
134
|
-
"""
|
|
135
|
-
max_val = torch.max(torch.abs(angles)).item()
|
|
136
|
-
if max_val > 2 * torch.pi:
|
|
137
|
-
logger.warning(
|
|
138
|
-
f"Maximum angle value is {max_val:.2f} (>2π). "
|
|
139
|
-
f"Consider using torch.deg2rad() or angle wrapping."
|
|
140
|
-
)
|
|
124
|
+
if _is_torch_tensor(angles):
|
|
125
|
+
return torch.stack(calculated, dim=-1), func_names
|
|
126
|
+
else:
|
|
127
|
+
return np.stack(calculated, axis=-1), func_names
|
|
141
128
|
|
|
142
129
|
|
|
143
|
-
# @batch_fn
|
|
144
|
-
@torch_fn
|
|
145
130
|
def circular_mean(
|
|
146
|
-
angles
|
|
147
|
-
values
|
|
131
|
+
angles,
|
|
132
|
+
values,
|
|
148
133
|
axis: int = -1,
|
|
149
134
|
dim: int = None,
|
|
150
135
|
batch_size: int = None,
|
|
151
136
|
keepdims: bool = False,
|
|
152
|
-
)
|
|
137
|
+
):
|
|
153
138
|
"""Compute circular mean of angles weighted by histogram values.
|
|
154
139
|
|
|
155
140
|
Parameters
|
|
156
141
|
----------
|
|
157
|
-
angles :
|
|
142
|
+
angles : array-like
|
|
158
143
|
Input angles in radians with batch dimension as first axis
|
|
159
|
-
values :
|
|
144
|
+
values : array-like
|
|
160
145
|
Histogram values for each angle (must match angles shape)
|
|
161
146
|
axis : int, default=-1
|
|
162
147
|
Axis along which to compute mean (deprecated, use dim)
|
|
163
148
|
dim : int, optional
|
|
164
149
|
Dimension along which to compute mean
|
|
165
150
|
batch_size : int, optional
|
|
166
|
-
Batch size for processing (
|
|
151
|
+
Batch size for processing (currently unused)
|
|
167
152
|
keepdims : bool, default=False
|
|
168
153
|
Whether to keep reduced dimensions
|
|
169
154
|
|
|
170
155
|
Returns
|
|
171
156
|
-------
|
|
172
|
-
|
|
157
|
+
ndarray or Tensor
|
|
173
158
|
Circular mean in range [0, 2π]
|
|
174
159
|
"""
|
|
175
|
-
|
|
176
160
|
_ensure_more_than_2d(angles)
|
|
177
161
|
_check_angle_units(angles)
|
|
178
|
-
assert angles.shape == values.shape, (
|
|
179
|
-
f"angles shape {angles.shape} must match values shape {values.shape}"
|
|
180
|
-
)
|
|
181
162
|
|
|
182
|
-
dim = axis
|
|
183
|
-
|
|
184
|
-
|
|
163
|
+
dim = _normalize_axis(axis, dim)
|
|
164
|
+
|
|
165
|
+
if _is_torch_tensor(angles):
|
|
166
|
+
assert angles.shape == values.shape, (
|
|
167
|
+
f"angles shape {angles.shape} must match values shape {values.shape}"
|
|
168
|
+
)
|
|
169
|
+
cos_angles = torch.cos(angles)
|
|
170
|
+
sin_angles = torch.sin(angles)
|
|
171
|
+
|
|
172
|
+
cos_component = torch.sum(values * cos_angles, dim=dim, keepdim=True)
|
|
173
|
+
sin_component = torch.sum(values * sin_angles, dim=dim, keepdim=True)
|
|
174
|
+
value_sum = torch.sum(values, dim=dim, keepdim=True)
|
|
175
|
+
cos_component = cos_component / value_sum
|
|
176
|
+
sin_component = sin_component / value_sum
|
|
177
|
+
|
|
178
|
+
mean_angle = torch.atan2(sin_component, cos_component)
|
|
179
|
+
mean_angle = torch.where(mean_angle < 0, mean_angle + 2 * np.pi, mean_angle)
|
|
180
|
+
return mean_angle if keepdims else mean_angle.squeeze(dim)
|
|
181
|
+
else:
|
|
182
|
+
angles = np.asarray(angles)
|
|
183
|
+
values = np.asarray(values)
|
|
184
|
+
assert angles.shape == values.shape, (
|
|
185
|
+
f"angles shape {angles.shape} must match values shape {values.shape}"
|
|
186
|
+
)
|
|
185
187
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
value_sum = torch.sum(values, dim=dim, keepdim=True)
|
|
189
|
-
cos_component = cos_component / value_sum
|
|
190
|
-
sin_component = sin_component / value_sum
|
|
188
|
+
cos_angles = np.cos(angles)
|
|
189
|
+
sin_angles = np.sin(angles)
|
|
191
190
|
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
cos_component = np.sum(values * cos_angles, axis=dim, keepdims=True)
|
|
192
|
+
sin_component = np.sum(values * sin_angles, axis=dim, keepdims=True)
|
|
193
|
+
value_sum = np.sum(values, axis=dim, keepdims=True)
|
|
194
|
+
cos_component = cos_component / value_sum
|
|
195
|
+
sin_component = sin_component / value_sum
|
|
194
196
|
|
|
195
|
-
|
|
197
|
+
mean_angle = np.arctan2(sin_component, cos_component)
|
|
198
|
+
mean_angle = np.where(mean_angle < 0, mean_angle + 2 * np.pi, mean_angle)
|
|
199
|
+
return mean_angle if keepdims else np.squeeze(mean_angle, axis=dim)
|
|
196
200
|
|
|
197
201
|
|
|
198
|
-
# @batch_fn
|
|
199
|
-
@torch_fn
|
|
200
202
|
def circular_concentration(
|
|
201
|
-
angles
|
|
202
|
-
values
|
|
203
|
+
angles,
|
|
204
|
+
values,
|
|
203
205
|
axis: int = -1,
|
|
204
206
|
dim: int = None,
|
|
205
207
|
batch_size: int = None,
|
|
206
208
|
keepdims: bool = False,
|
|
207
|
-
)
|
|
209
|
+
):
|
|
208
210
|
"""Compute circular concentration (mean resultant length).
|
|
209
211
|
|
|
210
212
|
Parameters
|
|
211
213
|
----------
|
|
212
|
-
angles :
|
|
213
|
-
Input angles in radians
|
|
214
|
-
values :
|
|
214
|
+
angles : array-like
|
|
215
|
+
Input angles in radians with batch dimension as first axis
|
|
216
|
+
values : array-like
|
|
215
217
|
Histogram values for each angle (must match angles shape)
|
|
216
218
|
axis : int, default=-1
|
|
217
219
|
Axis along which to compute concentration (deprecated, use dim)
|
|
218
220
|
dim : int, optional
|
|
219
221
|
Dimension along which to compute concentration
|
|
220
222
|
batch_size : int, optional
|
|
221
|
-
Batch size for processing (
|
|
223
|
+
Batch size for processing (currently unused)
|
|
222
224
|
keepdims : bool, default=False
|
|
223
225
|
Whether to keep reduced dimensions
|
|
224
226
|
|
|
225
227
|
Returns
|
|
226
228
|
-------
|
|
227
|
-
|
|
229
|
+
ndarray or Tensor
|
|
228
230
|
Concentration parameter in range [0, 1]
|
|
229
231
|
"""
|
|
230
232
|
_ensure_more_than_2d(angles)
|
|
231
233
|
_check_angle_units(angles)
|
|
232
|
-
assert angles.shape == values.shape, (
|
|
233
|
-
f"angles shape {angles.shape} must match values shape {values.shape}"
|
|
234
|
-
)
|
|
235
234
|
|
|
236
|
-
dim = axis
|
|
237
|
-
|
|
238
|
-
|
|
235
|
+
dim = _normalize_axis(axis, dim)
|
|
236
|
+
|
|
237
|
+
if _is_torch_tensor(angles):
|
|
238
|
+
assert angles.shape == values.shape
|
|
239
|
+
cos_angles = torch.cos(angles)
|
|
240
|
+
sin_angles = torch.sin(angles)
|
|
241
|
+
|
|
242
|
+
cos_component = torch.sum(values * cos_angles, dim=dim, keepdim=keepdims)
|
|
243
|
+
sin_component = torch.sum(values * sin_angles, dim=dim, keepdim=keepdims)
|
|
244
|
+
value_sum = torch.sum(values, dim=dim, keepdim=keepdims)
|
|
245
|
+
return torch.sqrt(cos_component**2 + sin_component**2) / value_sum
|
|
246
|
+
else:
|
|
247
|
+
angles = np.asarray(angles)
|
|
248
|
+
values = np.asarray(values)
|
|
249
|
+
assert angles.shape == values.shape
|
|
239
250
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
value_sum = torch.sum(values, dim=dim, keepdim=keepdims)
|
|
243
|
-
vector_length = torch.sqrt(cos_component**2 + sin_component**2) / value_sum
|
|
251
|
+
cos_angles = np.cos(angles)
|
|
252
|
+
sin_angles = np.sin(angles)
|
|
244
253
|
|
|
245
|
-
|
|
254
|
+
cos_component = np.sum(values * cos_angles, axis=dim, keepdims=keepdims)
|
|
255
|
+
sin_component = np.sum(values * sin_angles, axis=dim, keepdims=keepdims)
|
|
256
|
+
value_sum = np.sum(values, axis=dim, keepdims=keepdims)
|
|
257
|
+
return np.sqrt(cos_component**2 + sin_component**2) / value_sum
|
|
246
258
|
|
|
247
259
|
|
|
248
|
-
# @batch_fn
|
|
249
|
-
@torch_fn
|
|
250
260
|
def circular_skewness(
|
|
251
|
-
angles
|
|
252
|
-
values
|
|
261
|
+
angles,
|
|
262
|
+
values,
|
|
253
263
|
axis: int = -1,
|
|
254
264
|
dim: int = None,
|
|
255
265
|
batch_size: int = None,
|
|
256
266
|
keepdims: bool = False,
|
|
257
|
-
)
|
|
267
|
+
):
|
|
258
268
|
"""Compute circular skewness.
|
|
259
269
|
|
|
260
270
|
Parameters
|
|
261
271
|
----------
|
|
262
|
-
angles :
|
|
263
|
-
Input angles in radians
|
|
264
|
-
values :
|
|
272
|
+
angles : array-like
|
|
273
|
+
Input angles in radians with batch dimension as first axis
|
|
274
|
+
values : array-like
|
|
265
275
|
Histogram values for each angle (must match angles shape)
|
|
266
276
|
axis : int, default=-1
|
|
267
277
|
Axis along which to compute skewness (deprecated, use dim)
|
|
268
278
|
dim : int, optional
|
|
269
279
|
Dimension along which to compute skewness
|
|
270
280
|
batch_size : int, optional
|
|
271
|
-
Batch size for processing (
|
|
281
|
+
Batch size for processing (currently unused)
|
|
272
282
|
keepdims : bool, default=False
|
|
273
283
|
Whether to keep reduced dimensions
|
|
274
284
|
|
|
275
285
|
Returns
|
|
276
286
|
-------
|
|
277
|
-
|
|
287
|
+
ndarray or Tensor
|
|
278
288
|
Circular skewness
|
|
279
289
|
"""
|
|
280
290
|
_ensure_more_than_2d(angles)
|
|
281
291
|
_check_angle_units(angles)
|
|
282
|
-
assert angles.shape == values.shape, (
|
|
283
|
-
f"angles shape {angles.shape} must match values shape {values.shape}"
|
|
284
|
-
)
|
|
285
292
|
|
|
286
|
-
dim = axis
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
293
|
+
dim = _normalize_axis(axis, dim)
|
|
294
|
+
|
|
295
|
+
if _is_torch_tensor(angles):
|
|
296
|
+
assert angles.shape == values.shape
|
|
297
|
+
cos_angles = torch.cos(angles)
|
|
298
|
+
sin_angles = torch.sin(angles)
|
|
299
|
+
cos_2angles = torch.cos(2 * angles)
|
|
300
|
+
sin_2angles = torch.sin(2 * angles)
|
|
291
301
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
302
|
+
value_sum = torch.sum(values, dim=dim, keepdim=True)
|
|
303
|
+
c1 = torch.sum(values * cos_angles, dim=dim, keepdim=True) / value_sum
|
|
304
|
+
s1 = torch.sum(values * sin_angles, dim=dim, keepdim=True) / value_sum
|
|
305
|
+
c2 = torch.sum(values * cos_2angles, dim=dim, keepdim=True) / value_sum
|
|
306
|
+
s2 = torch.sum(values * sin_2angles, dim=dim, keepdim=True) / value_sum
|
|
307
|
+
|
|
308
|
+
skewness = (c2 * s1 - s2 * c1) / (1 - (c1**2 + s1**2)) ** (3 / 2)
|
|
309
|
+
return skewness if keepdims else skewness.squeeze(dim)
|
|
310
|
+
else:
|
|
311
|
+
angles = np.asarray(angles)
|
|
312
|
+
values = np.asarray(values)
|
|
313
|
+
assert angles.shape == values.shape
|
|
297
314
|
|
|
298
|
-
|
|
299
|
-
|
|
315
|
+
cos_angles = np.cos(angles)
|
|
316
|
+
sin_angles = np.sin(angles)
|
|
317
|
+
cos_2angles = np.cos(2 * angles)
|
|
318
|
+
sin_2angles = np.sin(2 * angles)
|
|
319
|
+
|
|
320
|
+
value_sum = np.sum(values, axis=dim, keepdims=True)
|
|
321
|
+
c1 = np.sum(values * cos_angles, axis=dim, keepdims=True) / value_sum
|
|
322
|
+
s1 = np.sum(values * sin_angles, axis=dim, keepdims=True) / value_sum
|
|
323
|
+
c2 = np.sum(values * cos_2angles, axis=dim, keepdims=True) / value_sum
|
|
324
|
+
s2 = np.sum(values * sin_2angles, axis=dim, keepdims=True) / value_sum
|
|
325
|
+
|
|
326
|
+
skewness = (c2 * s1 - s2 * c1) / (1 - (c1**2 + s1**2)) ** (3 / 2)
|
|
327
|
+
return skewness if keepdims else np.squeeze(skewness, axis=dim)
|
|
300
328
|
|
|
301
329
|
|
|
302
|
-
# @batch_fn
|
|
303
|
-
@torch_fn
|
|
304
330
|
def circular_kurtosis(
|
|
305
|
-
angles
|
|
306
|
-
values
|
|
331
|
+
angles,
|
|
332
|
+
values,
|
|
307
333
|
axis: int = -1,
|
|
308
334
|
dim: int = None,
|
|
309
335
|
batch_size: int = None,
|
|
310
336
|
keepdims: bool = False,
|
|
311
|
-
)
|
|
337
|
+
):
|
|
312
338
|
"""Compute circular kurtosis.
|
|
313
339
|
|
|
314
340
|
Parameters
|
|
315
341
|
----------
|
|
316
|
-
angles :
|
|
342
|
+
angles : array-like
|
|
317
343
|
Input angles in radians with batch dimension as first axis
|
|
318
|
-
values :
|
|
344
|
+
values : array-like
|
|
319
345
|
Histogram values for each angle (must match angles shape)
|
|
320
346
|
axis : int, default=-1
|
|
321
347
|
Axis along which to compute kurtosis (deprecated, use dim)
|
|
322
348
|
dim : int, optional
|
|
323
349
|
Dimension along which to compute kurtosis
|
|
324
350
|
batch_size : int, optional
|
|
325
|
-
Batch size for processing (
|
|
351
|
+
Batch size for processing (currently unused)
|
|
326
352
|
keepdims : bool, default=False
|
|
327
353
|
Whether to keep reduced dimensions
|
|
328
354
|
|
|
329
355
|
Returns
|
|
330
356
|
-------
|
|
331
|
-
|
|
357
|
+
ndarray or Tensor
|
|
332
358
|
Circular kurtosis
|
|
333
359
|
"""
|
|
334
360
|
_ensure_more_than_2d(angles)
|
|
335
361
|
_check_angle_units(angles)
|
|
336
|
-
assert angles.shape == values.shape, (
|
|
337
|
-
f"angles shape {angles.shape} must match values shape {values.shape}"
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
dim = axis if dim is None else dim
|
|
341
|
-
cos_angles = torch.cos(angles)
|
|
342
|
-
sin_angles = torch.sin(angles)
|
|
343
|
-
cos_2angles = torch.cos(2 * angles)
|
|
344
|
-
sin_2angles = torch.sin(2 * angles)
|
|
345
|
-
|
|
346
|
-
value_sum = torch.sum(values, dim=dim, keepdim=True)
|
|
347
|
-
c1 = torch.sum(values * cos_angles, dim=dim, keepdim=True) / value_sum
|
|
348
|
-
s1 = torch.sum(values * sin_angles, dim=dim, keepdim=True) / value_sum
|
|
349
|
-
c2 = torch.sum(values * cos_2angles, dim=dim, keepdim=True) / value_sum
|
|
350
|
-
s2 = torch.sum(values * sin_2angles, dim=dim, keepdim=True) / value_sum
|
|
351
|
-
|
|
352
|
-
kurtosis = (c2 * c1 + s2 * s1) / (1 - (c1**2 + s1**2)) ** 2
|
|
353
|
-
return kurtosis if keepdims else kurtosis.squeeze(dim)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def main(args) -> int:
|
|
357
|
-
"""Demonstrate circular statistics functions with synthetic data."""
|
|
358
|
-
|
|
359
|
-
# Generate synthetic circular histogram data
|
|
360
|
-
angles = torch.tensor([0.5, 1.2, 2.1, 3.8, 4.9, 5.7])
|
|
361
|
-
values = torch.tensor([1.0, 2.0, 1.5, 1.0, 3.0, 1.2])
|
|
362
|
-
|
|
363
|
-
angles = angles.reshape(1, -1)
|
|
364
|
-
values = values.reshape(1, -1)
|
|
365
|
-
|
|
366
|
-
# All at once
|
|
367
|
-
described, methods = describe_circular(angles, values)
|
|
368
|
-
described, methods = described[0], methods
|
|
369
|
-
for i_dd, dd in enumerate(described):
|
|
370
|
-
print(f"{methods[i_dd]}: {dd}")
|
|
371
|
-
|
|
372
|
-
# Compute circular statistics
|
|
373
|
-
c_mean = circular_mean(angles, values)
|
|
374
|
-
c_concentration = circular_concentration(angles, values)
|
|
375
|
-
c_skewness = circular_skewness(angles, values)
|
|
376
|
-
c_kurtosis = circular_kurtosis(angles, values)
|
|
377
|
-
|
|
378
|
-
# Store results
|
|
379
|
-
ii = 0
|
|
380
|
-
results = {
|
|
381
|
-
"angles": angles[ii],
|
|
382
|
-
"values": values[ii],
|
|
383
|
-
"circular_mean": c_mean[ii],
|
|
384
|
-
"circular_concentration": c_concentration[ii],
|
|
385
|
-
"circular_skewness": c_skewness[ii],
|
|
386
|
-
"circular_kurtosis": c_kurtosis[ii],
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
for k, v in results.items():
|
|
390
|
-
if isinstance(v, (np.ndarray, torch.Tensor)):
|
|
391
|
-
print(f"\n{k}, Type: {type(v)}, Shape: {v.shape}, Values: {v}")
|
|
392
|
-
elif isinstance(v, list):
|
|
393
|
-
print(f"\n{k}, Type: {type(v)}, Length: {len(v)}, Values: {v}")
|
|
394
|
-
else:
|
|
395
|
-
print(f"\n{k}, Type: {type(v)}, Values: {v}")
|
|
396
|
-
|
|
397
|
-
# Create visualization
|
|
398
|
-
fig, axes = plt.subplots(
|
|
399
|
-
2, 2, figsize=(12, 10), subplot_kw=dict(projection="polar")
|
|
400
|
-
)
|
|
401
|
-
|
|
402
|
-
# Plot 1: Histogram visualization
|
|
403
|
-
axes[0, 0].bar(
|
|
404
|
-
results["angles"],
|
|
405
|
-
results["values"],
|
|
406
|
-
width=0.3,
|
|
407
|
-
alpha=0.7,
|
|
408
|
-
color="blue",
|
|
409
|
-
)
|
|
410
|
-
axes[0, 0].axvline(
|
|
411
|
-
c_mean.item(),
|
|
412
|
-
color="red",
|
|
413
|
-
linewidth=2,
|
|
414
|
-
label=f"Mean: {c_mean.item():.2f}",
|
|
415
|
-
)
|
|
416
|
-
axes[0, 0].set_title("Circular Histogram")
|
|
417
|
-
axes[0, 0].legend()
|
|
418
|
-
|
|
419
|
-
# Plot 2: Concentration visualization
|
|
420
|
-
theta = torch.linspace(0, 2 * np.pi, 100)
|
|
421
|
-
radius = torch.ones_like(theta) * c_concentration.item()
|
|
422
|
-
axes[0, 1].plot(
|
|
423
|
-
theta,
|
|
424
|
-
radius,
|
|
425
|
-
"g-",
|
|
426
|
-
linewidth=2,
|
|
427
|
-
label=f"Concentration: {c_concentration.item():.3f}",
|
|
428
|
-
)
|
|
429
|
-
axes[0, 1].bar(
|
|
430
|
-
results["angles"],
|
|
431
|
-
results["values"],
|
|
432
|
-
width=0.3,
|
|
433
|
-
alpha=0.5,
|
|
434
|
-
color="blue",
|
|
435
|
-
)
|
|
436
|
-
axes[0, 1].set_title("Concentration Circle")
|
|
437
|
-
axes[0, 1].legend()
|
|
438
|
-
|
|
439
|
-
# Plot 3: Mean direction
|
|
440
|
-
axes[1, 0].bar(
|
|
441
|
-
results["angles"],
|
|
442
|
-
results["values"],
|
|
443
|
-
width=0.3,
|
|
444
|
-
alpha=0.7,
|
|
445
|
-
color="blue",
|
|
446
|
-
)
|
|
447
|
-
axes[1, 0].arrow(
|
|
448
|
-
0,
|
|
449
|
-
0,
|
|
450
|
-
np.cos(c_mean.item()) * c_concentration.item(),
|
|
451
|
-
np.sin(c_mean.item()) * c_concentration.item(),
|
|
452
|
-
head_width=0.1,
|
|
453
|
-
head_length=0.1,
|
|
454
|
-
fc="red",
|
|
455
|
-
ec="red",
|
|
456
|
-
)
|
|
457
|
-
axes[1, 0].set_title("Mean Vector")
|
|
458
|
-
|
|
459
|
-
# Plot 4: Statistics summary
|
|
460
|
-
axes[1, 1].axis("off")
|
|
461
|
-
stats_text = f"""Circular Statistics:
|
|
462
|
-
|
|
463
|
-
Mean: {c_mean.item():.3f} rad ({np.degrees(c_mean.item()):.1f}°)
|
|
464
|
-
Concentration: {c_concentration.item():.3f}
|
|
465
|
-
Skewness: {c_skewness.item():.3f}
|
|
466
|
-
Kurtosis: {c_kurtosis.item():.3f}"""
|
|
467
|
-
|
|
468
|
-
axes[1, 1].text(
|
|
469
|
-
0.1,
|
|
470
|
-
0.5,
|
|
471
|
-
stats_text,
|
|
472
|
-
transform=axes[1, 1].transAxes,
|
|
473
|
-
fontsize=12,
|
|
474
|
-
verticalalignment="center",
|
|
475
|
-
)
|
|
476
|
-
axes[1, 1].set_title("Statistics Summary")
|
|
477
|
-
|
|
478
|
-
plt.tight_layout()
|
|
479
|
-
stx.io.save(fig, "./circular_stats_demo.jpg")
|
|
480
362
|
|
|
481
|
-
|
|
482
|
-
stx.io.save(results, "./circular_statistics.pkl")
|
|
363
|
+
dim = _normalize_axis(axis, dim)
|
|
483
364
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
logger.info(f"Circular kurtosis: {c_kurtosis.item():.3f}")
|
|
365
|
+
if _is_torch_tensor(angles):
|
|
366
|
+
assert angles.shape == values.shape
|
|
367
|
+
cos_angles = torch.cos(angles)
|
|
368
|
+
sin_angles = torch.sin(angles)
|
|
369
|
+
cos_2angles = torch.cos(2 * angles)
|
|
370
|
+
sin_2angles = torch.sin(2 * angles)
|
|
491
371
|
|
|
492
|
-
|
|
372
|
+
value_sum = torch.sum(values, dim=dim, keepdim=True)
|
|
373
|
+
c1 = torch.sum(values * cos_angles, dim=dim, keepdim=True) / value_sum
|
|
374
|
+
s1 = torch.sum(values * sin_angles, dim=dim, keepdim=True) / value_sum
|
|
375
|
+
c2 = torch.sum(values * cos_2angles, dim=dim, keepdim=True) / value_sum
|
|
376
|
+
s2 = torch.sum(values * sin_2angles, dim=dim, keepdim=True) / value_sum
|
|
493
377
|
|
|
378
|
+
kurtosis = (c2 * c1 + s2 * s1) / (1 - (c1**2 + s1**2)) ** 2
|
|
379
|
+
return kurtosis if keepdims else kurtosis.squeeze(dim)
|
|
380
|
+
else:
|
|
381
|
+
angles = np.asarray(angles)
|
|
382
|
+
values = np.asarray(values)
|
|
383
|
+
assert angles.shape == values.shape
|
|
494
384
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
)
|
|
500
|
-
args = parser.parse_args()
|
|
501
|
-
return args
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
def run_main() -> None:
|
|
505
|
-
"""Initialize scitex framework, run main function, and cleanup."""
|
|
506
|
-
global CONFIG, CC, sys, plt, rng
|
|
507
|
-
|
|
508
|
-
import sys
|
|
509
|
-
|
|
510
|
-
import matplotlib.pyplot as plt
|
|
511
|
-
import scitex as stx
|
|
512
|
-
|
|
513
|
-
args = parse_args()
|
|
514
|
-
|
|
515
|
-
CONFIG, sys.stdout, sys.stderr, plt, CC, rng_manager = stx.session.start(
|
|
516
|
-
sys,
|
|
517
|
-
plt,
|
|
518
|
-
args=args,
|
|
519
|
-
file=__FILE__,
|
|
520
|
-
sdir_suffix=None,
|
|
521
|
-
verbose=False,
|
|
522
|
-
agg=True,
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
exit_status = main(args)
|
|
385
|
+
cos_angles = np.cos(angles)
|
|
386
|
+
sin_angles = np.sin(angles)
|
|
387
|
+
cos_2angles = np.cos(2 * angles)
|
|
388
|
+
sin_2angles = np.sin(2 * angles)
|
|
526
389
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
exit_status=exit_status,
|
|
533
|
-
)
|
|
390
|
+
value_sum = np.sum(values, axis=dim, keepdims=True)
|
|
391
|
+
c1 = np.sum(values * cos_angles, axis=dim, keepdims=True) / value_sum
|
|
392
|
+
s1 = np.sum(values * sin_angles, axis=dim, keepdims=True) / value_sum
|
|
393
|
+
c2 = np.sum(values * cos_2angles, axis=dim, keepdims=True) / value_sum
|
|
394
|
+
s2 = np.sum(values * sin_2angles, axis=dim, keepdims=True) / value_sum
|
|
534
395
|
|
|
396
|
+
kurtosis = (c2 * c1 + s2 * s1) / (1 - (c1**2 + s1**2)) ** 2
|
|
397
|
+
return kurtosis if keepdims else np.squeeze(kurtosis, axis=dim)
|
|
535
398
|
|
|
536
|
-
if __name__ == "__main__":
|
|
537
|
-
run_main()
|
|
538
399
|
|
|
539
400
|
# EOF
|