scitex 2.5.0__py3-none-any.whl → 2.7.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 +19 -8
- scitex/__main__.py +2 -1
- scitex/__version__.py +1 -1
- scitex/_optional_deps.py +13 -20
- scitex/ai/__init__.py +5 -0
- scitex/ai/_gen_ai/_Anthropic.py +3 -1
- scitex/ai/_gen_ai/_BaseGenAI.py +3 -2
- scitex/ai/_gen_ai/_DeepSeek.py +1 -1
- scitex/ai/_gen_ai/_Google.py +3 -2
- scitex/ai/_gen_ai/_Llama.py +4 -2
- scitex/ai/_gen_ai/_OpenAI.py +3 -1
- scitex/ai/_gen_ai/_PARAMS.py +1 -0
- scitex/ai/_gen_ai/_Perplexity.py +3 -1
- scitex/ai/_gen_ai/__init__.py +1 -0
- scitex/ai/_gen_ai/_format_output_func.py +3 -1
- scitex/ai/classification/CrossValidationExperiment.py +8 -14
- scitex/ai/classification/examples/timeseries_cv_demo.py +128 -112
- scitex/ai/classification/reporters/_BaseClassificationReporter.py +2 -0
- scitex/ai/classification/reporters/_ClassificationReporter.py +30 -45
- scitex/ai/classification/reporters/_MultiClassificationReporter.py +8 -11
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +126 -182
- scitex/ai/classification/reporters/__init__.py +1 -1
- scitex/ai/classification/reporters/reporter_utils/_Plotter.py +213 -119
- scitex/ai/classification/reporters/reporter_utils/__init__.py +28 -36
- scitex/ai/classification/reporters/reporter_utils/aggregation.py +125 -143
- scitex/ai/classification/reporters/reporter_utils/data_models.py +128 -120
- scitex/ai/classification/reporters/reporter_utils/reporting.py +507 -340
- scitex/ai/classification/reporters/reporter_utils/storage.py +4 -1
- scitex/ai/classification/reporters/reporter_utils/validation.py +141 -154
- scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +204 -129
- scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +215 -171
- scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +17 -17
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +67 -143
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +67 -143
- scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +12 -13
- scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +231 -144
- scitex/ai/classification/timeseries/__init__.py +2 -4
- scitex/ai/classification/timeseries/_normalize_timestamp.py +3 -0
- scitex/ai/clustering/_pca.py +0 -1
- scitex/ai/clustering/_umap.py +1 -2
- scitex/ai/feature_extraction/__init__.py +10 -8
- scitex/ai/feature_extraction/vit.py +0 -1
- scitex/ai/feature_selection/feature_selection.py +3 -8
- scitex/ai/metrics/_calc_conf_mat.py +2 -0
- scitex/ai/metrics/_calc_feature_importance.py +3 -7
- scitex/ai/metrics/_calc_pre_rec_auc.py +5 -5
- scitex/ai/metrics/_calc_roc_auc.py +4 -2
- scitex/ai/metrics/_calc_seizure_prediction_metrics.py +35 -20
- scitex/ai/metrics/_calc_silhouette_score.py +1 -3
- scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +0 -3
- scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +0 -3
- scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +0 -3
- scitex/ai/optim/_optimizers.py +1 -1
- scitex/ai/plt/__init__.py +6 -1
- scitex/ai/plt/_plot_feature_importance.py +1 -3
- scitex/ai/plt/_plot_learning_curve.py +9 -24
- scitex/ai/plt/_plot_optuna_study.py +4 -3
- scitex/ai/plt/_plot_pre_rec_curve.py +9 -15
- scitex/ai/plt/_plot_roc_curve.py +6 -8
- scitex/ai/plt/_stx_conf_mat.py +121 -122
- scitex/ai/sampling/undersample.py +3 -2
- scitex/ai/sklearn/__init__.py +2 -2
- scitex/ai/training/_LearningCurveLogger.py +23 -10
- scitex/ai/utils/_check_params.py +0 -1
- scitex/benchmark/__init__.py +15 -25
- scitex/benchmark/benchmark.py +124 -117
- scitex/benchmark/monitor.py +117 -107
- scitex/benchmark/profiler.py +61 -58
- scitex/bridge/__init__.py +110 -0
- scitex/bridge/_helpers.py +149 -0
- scitex/bridge/_plt_vis.py +529 -0
- scitex/bridge/_protocol.py +283 -0
- scitex/bridge/_stats_plt.py +261 -0
- scitex/bridge/_stats_vis.py +265 -0
- scitex/browser/__init__.py +0 -2
- scitex/browser/auth/__init__.py +0 -0
- scitex/browser/auth/google.py +16 -11
- scitex/browser/automation/CookieHandler.py +2 -3
- scitex/browser/collaboration/__init__.py +3 -0
- scitex/browser/collaboration/auth_helpers.py +3 -1
- scitex/browser/collaboration/collaborative_agent.py +2 -0
- scitex/browser/collaboration/interactive_panel.py +2 -2
- scitex/browser/collaboration/shared_session.py +20 -11
- scitex/browser/collaboration/standard_interactions.py +1 -0
- scitex/browser/core/BrowserMixin.py +12 -30
- scitex/browser/core/ChromeProfileManager.py +9 -24
- scitex/browser/debugging/_browser_logger.py +15 -25
- scitex/browser/debugging/_failure_capture.py +9 -2
- scitex/browser/debugging/_highlight_element.py +15 -6
- scitex/browser/debugging/_show_grid.py +5 -6
- scitex/browser/debugging/_sync_session.py +4 -3
- scitex/browser/debugging/_test_monitor.py +14 -5
- scitex/browser/debugging/_visual_cursor.py +46 -35
- scitex/browser/interaction/click_center.py +4 -3
- scitex/browser/interaction/click_with_fallbacks.py +7 -10
- scitex/browser/interaction/close_popups.py +79 -66
- scitex/browser/interaction/fill_with_fallbacks.py +8 -8
- scitex/browser/pdf/__init__.py +3 -1
- scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +11 -10
- scitex/browser/pdf/detect_chrome_pdf_viewer.py +3 -6
- scitex/browser/remote/CaptchaHandler.py +109 -96
- scitex/browser/remote/ZenRowsAPIClient.py +91 -97
- scitex/browser/remote/ZenRowsBrowserManager.py +138 -112
- scitex/browser/stealth/HumanBehavior.py +4 -9
- scitex/browser/stealth/StealthManager.py +11 -26
- scitex/capture/__init__.py +17 -17
- scitex/capture/__main__.py +2 -3
- scitex/capture/capture.py +23 -51
- scitex/capture/cli.py +14 -39
- scitex/capture/gif.py +5 -9
- scitex/capture/mcp_server.py +7 -20
- scitex/capture/session.py +4 -3
- scitex/capture/utils.py +18 -53
- scitex/cli/__init__.py +1 -1
- scitex/cli/cloud.py +158 -116
- scitex/cli/config.py +224 -0
- scitex/cli/main.py +41 -40
- scitex/cli/scholar.py +60 -27
- scitex/cli/security.py +14 -20
- scitex/cli/web.py +87 -90
- scitex/cli/writer.py +51 -45
- scitex/cloud/__init__.py +14 -11
- scitex/cloud/_matplotlib_hook.py +6 -6
- scitex/config/README.md +313 -0
- scitex/config/{PriorityConfig.py → _PriorityConfig.py} +114 -17
- scitex/config/_ScitexConfig.py +319 -0
- scitex/config/__init__.py +41 -9
- scitex/config/_paths.py +325 -0
- scitex/config/default.yaml +81 -0
- scitex/context/_suppress_output.py +2 -3
- scitex/db/_BaseMixins/_BaseBackupMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseBatchMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseBlobMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseImportExportMixin.py +1 -3
- scitex/db/_BaseMixins/_BaseIndexMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseMaintenanceMixin.py +1 -3
- scitex/db/_BaseMixins/_BaseQueryMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseRowMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseTableMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseTransactionMixin.py +1 -3
- scitex/db/_BaseMixins/__init__.py +1 -1
- scitex/db/__init__.py +9 -1
- scitex/db/__main__.py +8 -21
- scitex/db/_check_health.py +15 -31
- scitex/db/_delete_duplicates.py +7 -4
- scitex/db/_inspect.py +22 -38
- scitex/db/_inspect_optimized.py +89 -85
- scitex/db/_postgresql/_PostgreSQL.py +0 -1
- scitex/db/_postgresql/_PostgreSQLMixins/_BlobMixin.py +3 -1
- scitex/db/_postgresql/_PostgreSQLMixins/_ConnectionMixin.py +1 -3
- scitex/db/_postgresql/_PostgreSQLMixins/_ImportExportMixin.py +1 -3
- scitex/db/_postgresql/_PostgreSQLMixins/_MaintenanceMixin.py +1 -4
- scitex/db/_postgresql/_PostgreSQLMixins/_QueryMixin.py +3 -3
- scitex/db/_postgresql/_PostgreSQLMixins/_RowMixin.py +3 -1
- scitex/db/_postgresql/_PostgreSQLMixins/_TransactionMixin.py +1 -3
- scitex/db/_postgresql/__init__.py +1 -1
- scitex/db/_sqlite3/_SQLite3.py +2 -4
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +11 -12
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +19 -14
- scitex/db/_sqlite3/_SQLite3Mixins/_BatchMixin.py +3 -1
- scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +7 -7
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +118 -111
- scitex/db/_sqlite3/_SQLite3Mixins/_ConnectionMixin.py +8 -10
- scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +17 -45
- scitex/db/_sqlite3/_SQLite3Mixins/_ImportExportMixin.py +1 -3
- scitex/db/_sqlite3/_SQLite3Mixins/_IndexMixin.py +3 -1
- scitex/db/_sqlite3/_SQLite3Mixins/_QueryMixin.py +3 -4
- scitex/db/_sqlite3/_SQLite3Mixins/_RowMixin.py +9 -9
- scitex/db/_sqlite3/_SQLite3Mixins/_TableMixin.py +18 -11
- scitex/db/_sqlite3/_SQLite3Mixins/__init__.py +1 -0
- scitex/db/_sqlite3/__init__.py +1 -1
- scitex/db/_sqlite3/_delete_duplicates.py +13 -11
- scitex/decorators/__init__.py +29 -4
- scitex/decorators/_auto_order.py +43 -43
- scitex/decorators/_batch_fn.py +12 -6
- scitex/decorators/_cache_disk.py +8 -9
- scitex/decorators/_cache_disk_async.py +8 -7
- scitex/decorators/_combined.py +19 -13
- scitex/decorators/_converters.py +16 -3
- scitex/decorators/_deprecated.py +32 -22
- scitex/decorators/_numpy_fn.py +18 -4
- scitex/decorators/_pandas_fn.py +17 -5
- scitex/decorators/_signal_fn.py +17 -3
- scitex/decorators/_torch_fn.py +32 -15
- scitex/decorators/_xarray_fn.py +23 -9
- scitex/dev/_analyze_code_flow.py +0 -2
- scitex/dict/_DotDict.py +15 -19
- scitex/dict/_flatten.py +1 -0
- scitex/dict/_listed_dict.py +1 -0
- scitex/dict/_pop_keys.py +1 -0
- scitex/dict/_replace.py +1 -0
- scitex/dict/_safe_merge.py +1 -0
- scitex/dict/_to_str.py +2 -3
- scitex/dsp/__init__.py +13 -4
- scitex/dsp/_crop.py +3 -1
- scitex/dsp/_detect_ripples.py +3 -1
- scitex/dsp/_modulation_index.py +3 -1
- scitex/dsp/_time.py +3 -1
- scitex/dsp/_wavelet.py +0 -1
- scitex/dsp/example.py +0 -5
- scitex/dsp/filt.py +4 -0
- scitex/dsp/utils/__init__.py +4 -1
- scitex/dsp/utils/pac.py +3 -3
- scitex/dt/_normalize_timestamp.py +4 -1
- scitex/errors.py +3 -6
- scitex/etc/__init__.py +1 -1
- scitex/gen/_DimHandler.py +6 -6
- scitex/gen/__init__.py +5 -1
- scitex/gen/_deprecated_close.py +1 -0
- scitex/gen/_deprecated_start.py +5 -3
- scitex/gen/_detect_environment.py +44 -41
- scitex/gen/_detect_notebook_path.py +51 -47
- scitex/gen/_embed.py +1 -1
- scitex/gen/_get_notebook_path.py +81 -62
- scitex/gen/_inspect_module.py +0 -1
- scitex/gen/_norm.py +16 -7
- scitex/gen/_norm_cache.py +78 -65
- scitex/gen/_print_config.py +0 -3
- scitex/gen/_src.py +2 -3
- scitex/gen/_title_case.py +3 -2
- scitex/gen/_to_even.py +8 -8
- scitex/gen/_transpose.py +3 -3
- scitex/gen/misc.py +0 -3
- scitex/gists/_SigMacro_processFigure_S.py +2 -2
- scitex/gists/_SigMacro_toBlue.py +2 -2
- scitex/gists/__init__.py +4 -1
- scitex/git/_branch.py +19 -11
- scitex/git/_clone.py +23 -15
- scitex/git/_commit.py +10 -12
- scitex/git/_init.py +15 -38
- scitex/git/_remote.py +9 -3
- scitex/git/_result.py +3 -0
- scitex/git/_retry.py +2 -5
- scitex/git/_types.py +4 -0
- scitex/git/_validation.py +8 -8
- scitex/git/_workflow.py +4 -4
- scitex/io/__init__.py +2 -1
- scitex/io/_glob.py +2 -2
- scitex/io/_json2md.py +3 -3
- scitex/io/_load.py +6 -8
- scitex/io/_load_cache.py +71 -71
- scitex/io/_load_configs.py +2 -3
- scitex/io/_load_modules/_H5Explorer.py +6 -12
- scitex/io/_load_modules/_ZarrExplorer.py +3 -3
- scitex/io/_load_modules/_bibtex.py +62 -63
- scitex/io/_load_modules/_canvas.py +4 -9
- scitex/io/_load_modules/_catboost.py +7 -2
- scitex/io/_load_modules/_hdf5.py +2 -0
- scitex/io/_load_modules/_image.py +5 -1
- scitex/io/_load_modules/_matlab.py +3 -1
- scitex/io/_load_modules/_optuna.py +0 -1
- scitex/io/_load_modules/_pdf.py +38 -29
- scitex/io/_load_modules/_sqlite3.py +1 -0
- scitex/io/_load_modules/_txt.py +2 -0
- scitex/io/_load_modules/_xml.py +9 -9
- scitex/io/_load_modules/_zarr.py +12 -10
- scitex/io/_metadata.py +76 -37
- scitex/io/_qr_utils.py +18 -13
- scitex/io/_save.py +220 -63
- scitex/io/_save_modules/__init__.py +7 -2
- scitex/io/_save_modules/_bibtex.py +66 -61
- scitex/io/_save_modules/_canvas.py +5 -6
- scitex/io/_save_modules/_catboost.py +2 -2
- scitex/io/_save_modules/_csv.py +4 -4
- scitex/io/_save_modules/_excel.py +5 -9
- scitex/io/_save_modules/_hdf5.py +9 -21
- scitex/io/_save_modules/_html.py +5 -5
- scitex/io/_save_modules/_image.py +105 -8
- scitex/io/_save_modules/_joblib.py +2 -2
- scitex/io/_save_modules/_json.py +51 -6
- scitex/io/_save_modules/_listed_dfs_as_csv.py +2 -1
- scitex/io/_save_modules/_listed_scalars_as_csv.py +2 -1
- scitex/io/_save_modules/_matlab.py +2 -2
- scitex/io/_save_modules/_numpy.py +6 -8
- scitex/io/_save_modules/_pickle.py +4 -4
- scitex/io/_save_modules/_plotly.py +3 -3
- scitex/io/_save_modules/_tex.py +23 -25
- scitex/io/_save_modules/_text.py +2 -2
- scitex/io/_save_modules/_yaml.py +9 -9
- scitex/io/_save_modules/_zarr.py +15 -15
- scitex/io/utils/__init__.py +2 -1
- scitex/io/utils/h5_to_zarr.py +173 -155
- scitex/linalg/__init__.py +1 -1
- scitex/linalg/_geometric_median.py +4 -3
- scitex/logging/_Tee.py +5 -7
- scitex/logging/__init__.py +18 -19
- scitex/logging/_config.py +4 -1
- scitex/logging/_context.py +6 -5
- scitex/logging/_formatters.py +2 -3
- scitex/logging/_handlers.py +19 -20
- scitex/logging/_levels.py +9 -17
- scitex/logging/_logger.py +74 -15
- scitex/logging/_print_capture.py +17 -17
- scitex/nn/_BNet.py +1 -3
- scitex/nn/_Filters.py +6 -2
- scitex/nn/_ModulationIndex.py +3 -1
- scitex/nn/_PAC.py +3 -2
- scitex/nn/_PSD.py +0 -1
- scitex/nn/__init__.py +16 -3
- scitex/path/_clean.py +10 -8
- scitex/path/_find.py +1 -1
- scitex/path/_get_spath.py +1 -2
- scitex/path/_mk_spath.py +1 -1
- scitex/path/_symlink.py +5 -10
- scitex/pd/__init__.py +4 -1
- scitex/pd/_force_df.py +24 -24
- scitex/pd/_get_unique.py +1 -0
- scitex/pd/_merge_columns.py +1 -1
- scitex/pd/_round.py +11 -7
- scitex/pd/_to_xy.py +0 -1
- scitex/plt/REQUESTS.md +191 -0
- scitex/plt/__init__.py +185 -87
- scitex/plt/_subplots/_AxesWrapper.py +22 -6
- scitex/plt/_subplots/_AxisWrapper.py +100 -39
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +74 -52
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +183 -73
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +61 -45
- scitex/plt/_subplots/_AxisWrapperMixins/_TrackingMixin.py +26 -14
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +80 -73
- scitex/plt/_subplots/_FigWrapper.py +93 -60
- scitex/plt/_subplots/_SubplotsWrapper.py +135 -68
- scitex/plt/_subplots/__init__.py +10 -0
- scitex/plt/_subplots/_export_as_csv.py +89 -47
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +1 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +6 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +88 -38
- scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +25 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +53 -23
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +38 -25
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contourf.py +17 -9
- scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +70 -124
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +12 -10
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +31 -17
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +33 -21
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hexbin.py +14 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +43 -29
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist2d.py +14 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +27 -11
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +7 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +9 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pie.py +15 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +85 -46
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +52 -27
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +1 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +16 -17
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +7 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -8
- scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +17 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +43 -26
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +68 -47
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +52 -64
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +55 -50
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +9 -11
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +63 -29
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +4 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +6 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +44 -40
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +46 -39
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +46 -39
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +75 -94
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stem.py +12 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_step.py +12 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +10 -8
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +17 -15
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +10 -9
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_fillv.py +35 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +18 -18
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +24 -18
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +9 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_line.py +34 -23
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +15 -13
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +12 -10
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +15 -13
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +11 -9
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_rectangle.py +84 -56
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +35 -32
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_shaded_line.py +46 -30
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_violin.py +51 -51
- scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +32 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +34 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +44 -37
- scitex/plt/_subplots/_export_as_csv_formatters/verify_formatters.py +91 -74
- scitex/plt/_tpl.py +6 -5
- scitex/plt/ax/_plot/__init__.py +24 -0
- scitex/plt/ax/_plot/_add_fitted_line.py +12 -11
- scitex/plt/ax/_plot/_plot_circular_hist.py +3 -1
- scitex/plt/ax/_plot/_plot_statistical_shaded_line.py +25 -19
- scitex/plt/ax/_plot/_stx_conf_mat.py +6 -3
- scitex/plt/ax/_plot/_stx_ecdf.py +5 -3
- scitex/plt/ax/_plot/_stx_fillv.py +4 -2
- scitex/plt/ax/_plot/_stx_heatmap.py +7 -4
- scitex/plt/ax/_plot/_stx_image.py +7 -5
- scitex/plt/ax/_plot/_stx_joyplot.py +32 -10
- scitex/plt/ax/_plot/_stx_raster.py +26 -11
- scitex/plt/ax/_plot/_stx_rectangle.py +2 -2
- scitex/plt/ax/_plot/_stx_shaded_line.py +15 -11
- scitex/plt/ax/_plot/_stx_violin.py +3 -1
- scitex/plt/ax/_style/_add_marginal_ax.py +6 -4
- scitex/plt/ax/_style/_auto_scale_axis.py +14 -10
- scitex/plt/ax/_style/_extend.py +3 -1
- scitex/plt/ax/_style/_force_aspect.py +5 -3
- scitex/plt/ax/_style/_format_units.py +2 -2
- scitex/plt/ax/_style/_hide_spines.py +5 -1
- scitex/plt/ax/_style/_map_ticks.py +5 -3
- scitex/plt/ax/_style/_rotate_labels.py +5 -4
- scitex/plt/ax/_style/_rotate_labels_v01.py +73 -63
- scitex/plt/ax/_style/_set_log_scale.py +120 -85
- scitex/plt/ax/_style/_set_meta.py +99 -76
- scitex/plt/ax/_style/_set_supxyt.py +33 -16
- scitex/plt/ax/_style/_set_xyt.py +27 -18
- scitex/plt/ax/_style/_share_axes.py +15 -5
- scitex/plt/ax/_style/_show_spines.py +58 -57
- scitex/plt/ax/_style/_style_barplot.py +1 -1
- scitex/plt/ax/_style/_style_boxplot.py +25 -14
- scitex/plt/ax/_style/_style_errorbar.py +0 -0
- scitex/plt/ax/_style/_style_scatter.py +1 -1
- scitex/plt/ax/_style/_style_suptitles.py +3 -3
- scitex/plt/ax/_style/_style_violinplot.py +8 -2
- scitex/plt/color/__init__.py +34 -2
- scitex/plt/color/_add_hue_col.py +1 -0
- scitex/plt/color/_colors.py +0 -1
- scitex/plt/color/_get_colors_from_conf_matap.py +3 -1
- scitex/plt/color/_vizualize_colors.py +0 -1
- scitex/plt/docs/FIGURE_ARCHITECTURE.md +155 -97
- scitex/plt/gallery/README.md +75 -0
- scitex/plt/gallery/__init__.py +29 -0
- scitex/plt/gallery/_generate.py +153 -0
- scitex/plt/gallery/_plots.py +594 -0
- scitex/plt/gallery/_registry.py +153 -0
- scitex/plt/styles/__init__.py +9 -9
- scitex/plt/styles/_plot_defaults.py +62 -61
- scitex/plt/styles/_plot_postprocess.py +126 -77
- scitex/plt/styles/_style_loader.py +0 -0
- scitex/plt/styles/presets.py +43 -18
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +110 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +88 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +103 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +106 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +83 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +93 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +91 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +91 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +91 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +97 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +98 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +101 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +93 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +83 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +121 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +110 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +94 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +109 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +108 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +93 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +99 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +100 -0
- scitex/plt/utils/__init__.py +29 -2
- scitex/plt/utils/_close.py +8 -3
- scitex/plt/utils/_collect_figure_metadata.py +3031 -265
- scitex/plt/utils/_colorbar.py +15 -17
- scitex/plt/utils/_configure_mpl.py +22 -14
- scitex/plt/utils/_crop.py +60 -27
- scitex/plt/utils/_csv_column_naming.py +123 -72
- scitex/plt/utils/_dimension_viewer.py +7 -19
- scitex/plt/utils/_figure_from_axes_mm.py +70 -16
- scitex/plt/utils/_figure_mm.py +3 -2
- scitex/plt/utils/_get_actual_font.py +5 -4
- scitex/plt/utils/_histogram_utils.py +52 -48
- scitex/plt/utils/_is_valid_axis.py +19 -13
- scitex/plt/utils/_mk_colorbar.py +3 -3
- scitex/plt/utils/_scientific_captions.py +202 -139
- scitex/plt/utils/_scitex_config.py +98 -98
- scitex/plt/utils/_units.py +0 -0
- scitex/plt/utils/metadata/__init__.py +36 -0
- scitex/plt/utils/metadata/_artist_extraction.py +119 -0
- scitex/plt/utils/metadata/_axes_metadata.py +93 -0
- scitex/plt/utils/metadata/_collection_artists.py +292 -0
- scitex/plt/utils/metadata/_core.py +208 -0
- scitex/plt/utils/metadata/_csv_column_extraction.py +186 -0
- scitex/plt/utils/metadata/_csv_hash.py +115 -0
- scitex/plt/utils/metadata/_csv_verification.py +95 -0
- scitex/plt/utils/metadata/_data_linkage.py +263 -0
- scitex/plt/utils/metadata/_dimensions.py +239 -0
- scitex/plt/utils/metadata/_figure_metadata.py +58 -0
- scitex/plt/utils/metadata/_image_text_artists.py +168 -0
- scitex/plt/utils/metadata/_label_parsing.py +82 -0
- scitex/plt/utils/metadata/_legend_extraction.py +120 -0
- scitex/plt/utils/metadata/_line_artists.py +367 -0
- scitex/plt/utils/metadata/_line_semantic_handling.py +173 -0
- scitex/plt/utils/metadata/_patch_artists.py +211 -0
- scitex/plt/utils/metadata/_plot_content.py +26 -0
- scitex/plt/utils/metadata/_plot_type_detection.py +184 -0
- scitex/plt/utils/metadata/_precision.py +134 -0
- scitex/plt/utils/metadata/_precision_config.py +68 -0
- scitex/plt/utils/metadata/_precision_sections.py +211 -0
- scitex/plt/utils/metadata/_recipe_extraction.py +267 -0
- scitex/plt/utils/metadata/_style_parsing.py +174 -0
- scitex/repro/_RandomStateManager.py +33 -38
- scitex/repro/__init__.py +16 -7
- scitex/repro/_gen_ID.py +7 -9
- scitex/repro/_gen_timestamp.py +7 -6
- scitex/repro/_hash_array.py +8 -12
- scitex/reproduce/__init__.py +1 -1
- scitex/resource/_get_processor_usages.py +3 -1
- scitex/resource/_log_processor_usages.py +3 -1
- scitex/rng/__init__.py +1 -1
- scitex/schema/README.md +178 -0
- scitex/schema/__init__.py +144 -0
- scitex/schema/_canvas.py +444 -0
- scitex/schema/_stats.py +762 -0
- scitex/schema/_validation.py +590 -0
- scitex/scholar/.legacy/Scholar.py +5 -12
- scitex/scholar/.legacy/_Scholar.py +66 -99
- scitex/scholar/.legacy/_ScholarAPI.py +75 -66
- scitex/scholar/.legacy/_tmp/search_engine/_BaseSearchEngine.py +3 -3
- scitex/scholar/.legacy/_tmp/search_engine/_UnifiedSearcher.py +4 -9
- scitex/scholar/.legacy/_tmp/search_engine/__init__.py +14 -21
- scitex/scholar/.legacy/_tmp/search_engine/local/_LocalSearchEngine.py +40 -37
- scitex/scholar/.legacy/_tmp/search_engine/local/_VectorSearchEngine.py +31 -28
- scitex/scholar/.legacy/_tmp/search_engine/web/_ArxivSearchEngine.py +74 -65
- scitex/scholar/.legacy/_tmp/search_engine/web/_CrossRefSearchEngine.py +122 -116
- scitex/scholar/.legacy/_tmp/search_engine/web/_GoogleScholarSearchEngine.py +65 -59
- scitex/scholar/.legacy/_tmp/search_engine/web/_PubMedSearchEngine.py +121 -107
- scitex/scholar/.legacy/_tmp/search_engine/web/_SemanticScholarSearchEngine.py +5 -12
- scitex/scholar/.legacy/database/_DatabaseEntry.py +49 -45
- scitex/scholar/.legacy/database/_DatabaseIndex.py +131 -94
- scitex/scholar/.legacy/database/_LibraryManager.py +65 -63
- scitex/scholar/.legacy/database/_PaperDatabase.py +138 -124
- scitex/scholar/.legacy/database/_ScholarDatabaseIntegration.py +14 -36
- scitex/scholar/.legacy/database/_StorageIntegratedDB.py +192 -156
- scitex/scholar/.legacy/database/_ZoteroCompatibleDB.py +300 -237
- scitex/scholar/.legacy/database/__init__.py +2 -1
- scitex/scholar/.legacy/database/manage.py +92 -84
- scitex/scholar/.legacy/lookup/_LookupIndex.py +157 -101
- scitex/scholar/.legacy/lookup/__init__.py +2 -1
- scitex/scholar/.legacy/metadata/doi/batch/_MetadataHandlerForBatchDOIResolution.py +4 -9
- scitex/scholar/.legacy/metadata/doi/batch/_ProgressManagerForBatchDOIResolution.py +10 -23
- scitex/scholar/.legacy/metadata/doi/batch/_SourceStatsManagerForBatchDOIResolution.py +4 -9
- scitex/scholar/.legacy/metadata/doi/batch/__init__.py +3 -1
- scitex/scholar/.legacy/metadata/doi/resolvers/_BatchDOIResolver.py +10 -25
- scitex/scholar/.legacy/metadata/doi/resolvers/_BibTeXDOIResolver.py +19 -49
- scitex/scholar/.legacy/metadata/doi/resolvers/_DOIResolver.py +1 -0
- scitex/scholar/.legacy/metadata/doi/resolvers/_SingleDOIResolver.py +8 -20
- scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
- scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +49 -37
- scitex/scholar/.legacy/metadata/doi/sources/_ArXivSource.py +11 -30
- scitex/scholar/.legacy/metadata/doi/sources/_BaseDOISource.py +19 -47
- scitex/scholar/.legacy/metadata/doi/sources/_CrossRefLocalSource.py +1 -0
- scitex/scholar/.legacy/metadata/doi/sources/_CrossRefSource.py +12 -33
- scitex/scholar/.legacy/metadata/doi/sources/_OpenAlexSource.py +8 -20
- scitex/scholar/.legacy/metadata/doi/sources/_PubMedSource.py +10 -27
- scitex/scholar/.legacy/metadata/doi/sources/_SemanticScholarSource.py +11 -29
- scitex/scholar/.legacy/metadata/doi/sources/_SourceManager.py +8 -21
- scitex/scholar/.legacy/metadata/doi/sources/_SourceResolutionStrategy.py +24 -55
- scitex/scholar/.legacy/metadata/doi/sources/_SourceRotationManager.py +8 -21
- scitex/scholar/.legacy/metadata/doi/sources/_URLDOISource.py +9 -16
- scitex/scholar/.legacy/metadata/doi/sources/_UnifiedSource.py +8 -22
- scitex/scholar/.legacy/metadata/doi/sources/__init__.py +1 -0
- scitex/scholar/.legacy/metadata/doi/utils/_PubMedConverter.py +4 -8
- scitex/scholar/.legacy/metadata/doi/utils/_RateLimitHandler.py +17 -43
- scitex/scholar/.legacy/metadata/doi/utils/_TextNormalizer.py +8 -18
- scitex/scholar/.legacy/metadata/doi/utils/_URLDOIExtractor.py +4 -8
- scitex/scholar/.legacy/metadata/doi/utils/__init__.py +1 -0
- scitex/scholar/.legacy/metadata/doi/utils/_to_complete_metadata_structure.py +1 -0
- scitex/scholar/.legacy/metadata/enrichment/_LibraryEnricher.py +2 -3
- scitex/scholar/.legacy/metadata/enrichment/enrichers/_ImpactFactorEnricher.py +6 -12
- scitex/scholar/.legacy/metadata/enrichment/enrichers/_SmartEnricher.py +5 -10
- scitex/scholar/.legacy/metadata/enrichment/sources/_UnifiedMetadataSource.py +4 -5
- scitex/scholar/.legacy/metadata/query_to_full_meta_json.py +8 -12
- scitex/scholar/.legacy/metadata/urls/_URLMetadataHandler.py +3 -3
- scitex/scholar/.legacy/metadata/urls/_ZoteroTranslatorRunner.py +15 -21
- scitex/scholar/.legacy/metadata/urls/__init__.py +3 -3
- scitex/scholar/.legacy/metadata/urls/_finder.py +4 -6
- scitex/scholar/.legacy/metadata/urls/_handler.py +7 -15
- scitex/scholar/.legacy/metadata/urls/_resolver.py +6 -12
- scitex/scholar/.legacy/search/_Embedder.py +74 -69
- scitex/scholar/.legacy/search/_SemanticSearch.py +91 -90
- scitex/scholar/.legacy/search/_SemanticSearchEngine.py +104 -109
- scitex/scholar/.legacy/search/_UnifiedSearcher.py +530 -471
- scitex/scholar/.legacy/search/_VectorDatabase.py +111 -92
- scitex/scholar/.legacy/search/__init__.py +1 -0
- scitex/scholar/.legacy/storage/_EnhancedStorageManager.py +182 -154
- scitex/scholar/.legacy/storage/__init__.py +2 -1
- scitex/scholar/__init__.py +0 -2
- scitex/scholar/__main__.py +1 -3
- scitex/scholar/auth/ScholarAuthManager.py +13 -36
- scitex/scholar/auth/core/AuthenticationGateway.py +15 -29
- scitex/scholar/auth/core/BrowserAuthenticator.py +22 -57
- scitex/scholar/auth/core/StrategyResolver.py +10 -27
- scitex/scholar/auth/core/__init__.py +5 -1
- scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +11 -21
- scitex/scholar/auth/gateway/_OpenURLResolver.py +10 -18
- scitex/scholar/auth/gateway/_resolve_functions.py +3 -3
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -0
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +7 -14
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +29 -57
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +87 -73
- scitex/scholar/auth/session/AuthCacheManager.py +12 -22
- scitex/scholar/auth/session/SessionManager.py +4 -6
- scitex/scholar/auth/sso/BaseSSOAutomator.py +13 -19
- scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +16 -45
- scitex/scholar/auth/sso/SSOAutomator.py +8 -15
- scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +13 -23
- scitex/scholar/browser/ScholarBrowserManager.py +31 -56
- scitex/scholar/browser/__init__.py +1 -0
- scitex/scholar/browser/utils/click_and_wait.py +3 -4
- scitex/scholar/browser/utils/close_unwanted_pages.py +4 -7
- scitex/scholar/browser/utils/wait_redirects.py +15 -40
- scitex/scholar/citation_graph/__init__.py +0 -0
- scitex/scholar/citation_graph/builder.py +3 -7
- scitex/scholar/citation_graph/database.py +4 -11
- scitex/scholar/citation_graph/example.py +5 -10
- scitex/scholar/citation_graph/models.py +0 -0
- scitex/scholar/cli/_url_utils.py +1 -1
- scitex/scholar/cli/chrome.py +5 -3
- scitex/scholar/cli/download_pdf.py +13 -14
- scitex/scholar/cli/handlers/bibtex_handler.py +4 -12
- scitex/scholar/cli/handlers/doi_handler.py +1 -3
- scitex/scholar/cli/handlers/project_handler.py +6 -20
- scitex/scholar/cli/open_browser.py +41 -39
- scitex/scholar/cli/open_browser_auto.py +31 -39
- scitex/scholar/cli/open_browser_monitored.py +27 -24
- scitex/scholar/config/ScholarConfig.py +5 -8
- scitex/scholar/config/__init__.py +1 -0
- scitex/scholar/config/core/_CascadeConfig.py +3 -3
- scitex/scholar/config/core/_PathManager.py +16 -28
- scitex/scholar/core/Paper.py +79 -78
- scitex/scholar/core/Papers.py +16 -27
- scitex/scholar/core/Scholar.py +98 -229
- scitex/scholar/core/journal_normalizer.py +52 -49
- scitex/scholar/core/oa_cache.py +27 -23
- scitex/scholar/core/open_access.py +17 -8
- scitex/scholar/docs/template.py +4 -3
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/clf_svm.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/download.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_conf_mat.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_digits.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_umap_space.py +0 -0
- scitex/scholar/examples/00_config.py +10 -9
- scitex/scholar/examples/01_auth.py +3 -0
- scitex/scholar/examples/02_browser.py +14 -10
- scitex/scholar/examples/03_01-engine.py +3 -0
- scitex/scholar/examples/03_02-engine-for-bibtex.py +4 -3
- scitex/scholar/examples/04_01-url.py +9 -9
- scitex/scholar/examples/04_02-url-for-bibtex.py +7 -3
- scitex/scholar/examples/04_02-url-for-dois.py +87 -97
- scitex/scholar/examples/05_download_pdf.py +10 -4
- scitex/scholar/examples/06_find_and_download.py +6 -6
- scitex/scholar/examples/06_parse_bibtex.py +17 -17
- scitex/scholar/examples/07_storage_integration.py +6 -9
- scitex/scholar/examples/99_fullpipeline-for-bibtex.py +14 -15
- scitex/scholar/examples/99_fullpipeline-for-one-entry.py +31 -23
- scitex/scholar/examples/99_maintenance.py +3 -0
- scitex/scholar/examples/dev.py +2 -3
- scitex/scholar/examples/zotero_integration.py +11 -18
- scitex/scholar/impact_factor/ImpactFactorEngine.py +7 -9
- scitex/scholar/impact_factor/estimation/__init__.py +4 -4
- scitex/scholar/impact_factor/estimation/core/__init__.py +3 -7
- scitex/scholar/impact_factor/estimation/core/cache_manager.py +223 -211
- scitex/scholar/impact_factor/estimation/core/calculator.py +165 -131
- scitex/scholar/impact_factor/estimation/core/journal_matcher.py +217 -172
- scitex/scholar/impact_factor/jcr/ImpactFactorJCREngine.py +6 -14
- scitex/scholar/impact_factor/jcr/build_database.py +4 -3
- scitex/scholar/integration/base.py +9 -17
- scitex/scholar/integration/mendeley/exporter.py +2 -4
- scitex/scholar/integration/mendeley/importer.py +3 -3
- scitex/scholar/integration/mendeley/linker.py +3 -3
- scitex/scholar/integration/mendeley/mapper.py +9 -6
- scitex/scholar/integration/zotero/__main__.py +26 -43
- scitex/scholar/integration/zotero/exporter.py +15 -11
- scitex/scholar/integration/zotero/importer.py +12 -10
- scitex/scholar/integration/zotero/linker.py +8 -12
- scitex/scholar/integration/zotero/mapper.py +17 -12
- scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
- scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +47 -35
- scitex/scholar/metadata_engines/ScholarEngine.py +21 -43
- scitex/scholar/metadata_engines/__init__.py +1 -0
- scitex/scholar/metadata_engines/individual/ArXivEngine.py +15 -37
- scitex/scholar/metadata_engines/individual/CrossRefEngine.py +15 -42
- scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +24 -45
- scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +11 -21
- scitex/scholar/metadata_engines/individual/PubMedEngine.py +10 -27
- scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +28 -35
- scitex/scholar/metadata_engines/individual/URLDOIEngine.py +11 -22
- scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +20 -49
- scitex/scholar/metadata_engines/utils/_PubMedConverter.py +4 -8
- scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +5 -10
- scitex/scholar/metadata_engines/utils/__init__.py +2 -0
- scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +3 -0
- scitex/scholar/metadata_engines/utils/_standardize_metadata.py +2 -3
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +25 -37
- scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +11 -19
- scitex/scholar/pdf_download/strategies/direct_download.py +5 -9
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +3 -3
- scitex/scholar/pdf_download/strategies/manual_download_utils.py +6 -13
- scitex/scholar/pdf_download/strategies/open_access_download.py +49 -31
- scitex/scholar/pdf_download/strategies/response_body.py +8 -19
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +9 -18
- scitex/scholar/pipelines/ScholarPipelineMetadataParallel.py +25 -26
- scitex/scholar/pipelines/ScholarPipelineMetadataSingle.py +62 -23
- scitex/scholar/pipelines/ScholarPipelineParallel.py +13 -30
- scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +299 -220
- scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +202 -165
- scitex/scholar/pipelines/ScholarPipelineSingle.py +25 -51
- scitex/scholar/pipelines/SearchQueryParser.py +55 -55
- scitex/scholar/search_engines/ScholarSearchEngine.py +31 -27
- scitex/scholar/search_engines/_BaseSearchEngine.py +20 -23
- scitex/scholar/search_engines/individual/ArXivSearchEngine.py +53 -35
- scitex/scholar/search_engines/individual/CrossRefSearchEngine.py +47 -40
- scitex/scholar/search_engines/individual/OpenAlexSearchEngine.py +55 -50
- scitex/scholar/search_engines/individual/PubMedSearchEngine.py +8 -10
- scitex/scholar/search_engines/individual/SemanticScholarSearchEngine.py +55 -49
- scitex/scholar/storage/BibTeXHandler.py +150 -95
- scitex/scholar/storage/PaperIO.py +3 -6
- scitex/scholar/storage/ScholarLibrary.py +70 -49
- scitex/scholar/storage/_DeduplicationManager.py +52 -25
- scitex/scholar/storage/_LibraryCacheManager.py +19 -46
- scitex/scholar/storage/_LibraryManager.py +65 -175
- scitex/scholar/url_finder/ScholarURLFinder.py +9 -25
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +1 -1
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +6 -10
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +4 -6
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +8 -15
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +3 -3
- scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +3 -3
- scitex/scholar/url_finder/translators/core/patterns.py +6 -4
- scitex/scholar/url_finder/translators/core/registry.py +6 -9
- scitex/scholar/url_finder/translators/individual/BOFiP_Impots.py +60 -52
- scitex/scholar/url_finder/translators/individual/Baidu_Scholar.py +54 -62
- scitex/scholar/url_finder/translators/individual/Bangkok_Post.py +38 -44
- scitex/scholar/url_finder/translators/individual/Baruch_Foundation.py +43 -47
- scitex/scholar/url_finder/translators/individual/Beobachter.py +46 -50
- scitex/scholar/url_finder/translators/individual/Bezneng_Gajit.py +37 -41
- scitex/scholar/url_finder/translators/individual/BibLaTeX.py +59 -52
- scitex/scholar/url_finder/translators/individual/BibTeX.py +83 -79
- scitex/scholar/url_finder/translators/individual/Biblio_com.py +48 -51
- scitex/scholar/url_finder/translators/individual/Bibliontology_RDF.py +58 -56
- scitex/scholar/url_finder/translators/individual/Camara_Brasileira_do_Livro_ISBN.py +102 -99
- scitex/scholar/url_finder/translators/individual/CanLII.py +49 -43
- scitex/scholar/url_finder/translators/individual/Canada_com.py +36 -40
- scitex/scholar/url_finder/translators/individual/Canadian_Letters_and_Images.py +43 -43
- scitex/scholar/url_finder/translators/individual/Canadiana_ca.py +77 -66
- scitex/scholar/url_finder/translators/individual/Cascadilla_Proceedings_Project.py +68 -62
- scitex/scholar/url_finder/translators/individual/Central_and_Eastern_European_Online_Library_Journals.py +60 -60
- scitex/scholar/url_finder/translators/individual/Champlain_Society_Collection.py +63 -61
- scitex/scholar/url_finder/translators/individual/Chicago_Journal_of_Theoretical_Computer_Science.py +74 -58
- scitex/scholar/url_finder/translators/individual/Christian_Science_Monitor.py +32 -38
- scitex/scholar/url_finder/translators/individual/Columbia_University_Press.py +51 -47
- scitex/scholar/url_finder/translators/individual/Common_Place.py +66 -57
- scitex/scholar/url_finder/translators/individual/Cornell_LII.py +66 -62
- scitex/scholar/url_finder/translators/individual/Cornell_University_Press.py +38 -45
- scitex/scholar/url_finder/translators/individual/CourtListener.py +52 -56
- scitex/scholar/url_finder/translators/individual/DAI_Zenon.py +53 -54
- scitex/scholar/url_finder/translators/individual/access_medicine.py +27 -33
- scitex/scholar/url_finder/translators/individual/acm.py +1 -1
- scitex/scholar/url_finder/translators/individual/acm_digital_library.py +93 -63
- scitex/scholar/url_finder/translators/individual/airiti.py +3 -1
- scitex/scholar/url_finder/translators/individual/aosic.py +3 -1
- scitex/scholar/url_finder/translators/individual/archive_ouverte_aosic.py +3 -1
- scitex/scholar/url_finder/translators/individual/archive_ouverte_en_sciences_de_l_information_et_de_la_communication___aosic_.py +6 -2
- scitex/scholar/url_finder/translators/individual/artforum.py +35 -27
- scitex/scholar/url_finder/translators/individual/arxiv.py +1 -1
- scitex/scholar/url_finder/translators/individual/arxiv_org.py +8 -4
- scitex/scholar/url_finder/translators/individual/atlanta_journal_constitution.py +22 -18
- scitex/scholar/url_finder/translators/individual/atypon_journals.py +19 -11
- scitex/scholar/url_finder/translators/individual/austlii_and_nzlii.py +48 -44
- scitex/scholar/url_finder/translators/individual/australian_dictionary_of_biography.py +21 -17
- scitex/scholar/url_finder/translators/individual/bailii.py +22 -19
- scitex/scholar/url_finder/translators/individual/bbc.py +46 -42
- scitex/scholar/url_finder/translators/individual/bbc_genome.py +37 -25
- scitex/scholar/url_finder/translators/individual/biblioteca_nacional_de_maestros.py +24 -20
- scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationale_quebec_pistard.py +42 -43
- scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationales_quebec.py +87 -81
- scitex/scholar/url_finder/translators/individual/bibliotheque_nationale_france.py +39 -37
- scitex/scholar/url_finder/translators/individual/bibsys.py +32 -28
- scitex/scholar/url_finder/translators/individual/bioconductor.py +58 -52
- scitex/scholar/url_finder/translators/individual/biomed_central.py +23 -15
- scitex/scholar/url_finder/translators/individual/biorxiv.py +26 -13
- scitex/scholar/url_finder/translators/individual/blogger.py +39 -43
- scitex/scholar/url_finder/translators/individual/bloomberg.py +48 -52
- scitex/scholar/url_finder/translators/individual/bloomsbury_food_library.py +37 -37
- scitex/scholar/url_finder/translators/individual/bluesky.py +30 -28
- scitex/scholar/url_finder/translators/individual/bnf_isbn.py +1 -1
- scitex/scholar/url_finder/translators/individual/bocc.py +66 -60
- scitex/scholar/url_finder/translators/individual/boe.py +52 -52
- scitex/scholar/url_finder/translators/individual/brill.py +3 -1
- scitex/scholar/url_finder/translators/individual/business_standard.py +36 -38
- scitex/scholar/url_finder/translators/individual/cabi_cab_abstracts.py +39 -41
- scitex/scholar/url_finder/translators/individual/cambridge.py +3 -1
- scitex/scholar/url_finder/translators/individual/cambridge_core.py +30 -24
- scitex/scholar/url_finder/translators/individual/caod.py +50 -46
- scitex/scholar/url_finder/translators/individual/cbc.py +91 -67
- scitex/scholar/url_finder/translators/individual/ccfr_bnf.py +49 -53
- scitex/scholar/url_finder/translators/individual/cia_world_factbook.py +43 -33
- scitex/scholar/url_finder/translators/individual/crossref_rest.py +208 -174
- scitex/scholar/url_finder/translators/individual/current_affairs.py +29 -35
- scitex/scholar/url_finder/translators/individual/dabi.py +70 -66
- scitex/scholar/url_finder/translators/individual/dagens_nyheter.py +3 -1
- scitex/scholar/url_finder/translators/individual/dagstuhl.py +10 -15
- scitex/scholar/url_finder/translators/individual/dar_almandumah.py +13 -9
- scitex/scholar/url_finder/translators/individual/dart_europe.py +19 -22
- scitex/scholar/url_finder/translators/individual/data_gov.py +2 -2
- scitex/scholar/url_finder/translators/individual/databrary.py +27 -28
- scitex/scholar/url_finder/translators/individual/datacite_json.py +152 -137
- scitex/scholar/url_finder/translators/individual/dataverse.py +68 -64
- scitex/scholar/url_finder/translators/individual/daum_news.py +38 -38
- scitex/scholar/url_finder/translators/individual/dblp.py +4 -8
- scitex/scholar/url_finder/translators/individual/dblp_computer_science_bibliography.py +8 -3
- scitex/scholar/url_finder/translators/individual/dbpia.py +5 -3
- scitex/scholar/url_finder/translators/individual/defense_technical_information_center.py +30 -28
- scitex/scholar/url_finder/translators/individual/delpher.py +102 -79
- scitex/scholar/url_finder/translators/individual/demographic_research.py +35 -31
- scitex/scholar/url_finder/translators/individual/denik_cz.py +58 -54
- scitex/scholar/url_finder/translators/individual/depatisnet.py +7 -10
- scitex/scholar/url_finder/translators/individual/der_freitag.py +81 -66
- scitex/scholar/url_finder/translators/individual/der_spiegel.py +56 -54
- scitex/scholar/url_finder/translators/individual/digibib_net.py +3 -1
- scitex/scholar/url_finder/translators/individual/digizeitschriften.py +3 -1
- scitex/scholar/url_finder/translators/individual/dpla.py +13 -14
- scitex/scholar/url_finder/translators/individual/dspace.py +2 -2
- scitex/scholar/url_finder/translators/individual/ebrary.py +3 -1
- scitex/scholar/url_finder/translators/individual/ebscohost.py +3 -1
- scitex/scholar/url_finder/translators/individual/electronic_colloquium_on_computational_complexity.py +3 -1
- scitex/scholar/url_finder/translators/individual/elife.py +3 -1
- scitex/scholar/url_finder/translators/individual/elsevier_health_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/emerald.py +3 -1
- scitex/scholar/url_finder/translators/individual/emerald_insight.py +3 -1
- scitex/scholar/url_finder/translators/individual/epicurious.py +3 -1
- scitex/scholar/url_finder/translators/individual/eurogamerusgamer.py +3 -1
- scitex/scholar/url_finder/translators/individual/fachportal_padagogik.py +3 -1
- scitex/scholar/url_finder/translators/individual/frontiers.py +1 -1
- scitex/scholar/url_finder/translators/individual/gale_databases.py +3 -1
- scitex/scholar/url_finder/translators/individual/gms_german_medical_science.py +6 -2
- scitex/scholar/url_finder/translators/individual/ieee_computer_society.py +6 -2
- scitex/scholar/url_finder/translators/individual/ieee_xplore.py +41 -35
- scitex/scholar/url_finder/translators/individual/inter_research_science_center.py +6 -2
- scitex/scholar/url_finder/translators/individual/jisc_historical_texts.py +3 -1
- scitex/scholar/url_finder/translators/individual/jstor.py +14 -12
- scitex/scholar/url_finder/translators/individual/korean_national_library.py +3 -1
- scitex/scholar/url_finder/translators/individual/la_times.py +3 -1
- scitex/scholar/url_finder/translators/individual/landesbibliographie_baden_wurttemberg.py +3 -1
- scitex/scholar/url_finder/translators/individual/legislative_insight.py +3 -1
- scitex/scholar/url_finder/translators/individual/libraries_tasmania.py +3 -1
- scitex/scholar/url_finder/translators/individual/library_catalog__koha_.py +3 -1
- scitex/scholar/url_finder/translators/individual/lingbuzz.py +2 -2
- scitex/scholar/url_finder/translators/individual/max_planck_institute_for_the_history_of_science_virtual_laboratory_library.py +3 -1
- scitex/scholar/url_finder/translators/individual/mdpi.py +12 -6
- scitex/scholar/url_finder/translators/individual/microbiology_society_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/midas_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/nagoya_university_opac.py +3 -1
- scitex/scholar/url_finder/translators/individual/nature_publishing_group.py +32 -19
- scitex/scholar/url_finder/translators/individual/ntsb_accident_reports.py +3 -1
- scitex/scholar/url_finder/translators/individual/openedition_journals.py +8 -4
- scitex/scholar/url_finder/translators/individual/orcid.py +16 -15
- scitex/scholar/url_finder/translators/individual/oxford.py +25 -19
- scitex/scholar/url_finder/translators/individual/oxford_dictionaries_premium.py +3 -1
- scitex/scholar/url_finder/translators/individual/ozon_ru.py +3 -1
- scitex/scholar/url_finder/translators/individual/plos.py +9 -12
- scitex/scholar/url_finder/translators/individual/polygon.py +3 -1
- scitex/scholar/url_finder/translators/individual/primo.py +3 -1
- scitex/scholar/url_finder/translators/individual/project_muse.py +3 -1
- scitex/scholar/url_finder/translators/individual/pubfactory_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/pubmed.py +71 -65
- scitex/scholar/url_finder/translators/individual/pubmed_central.py +8 -6
- scitex/scholar/url_finder/translators/individual/rechtspraak_nl.py +3 -1
- scitex/scholar/url_finder/translators/individual/sage_journals.py +25 -17
- scitex/scholar/url_finder/translators/individual/sciencedirect.py +36 -17
- scitex/scholar/url_finder/translators/individual/semantics_visual_library.py +3 -1
- scitex/scholar/url_finder/translators/individual/silverchair.py +70 -52
- scitex/scholar/url_finder/translators/individual/sora.py +3 -1
- scitex/scholar/url_finder/translators/individual/springer.py +15 -11
- scitex/scholar/url_finder/translators/individual/ssrn.py +3 -3
- scitex/scholar/url_finder/translators/individual/stanford_encyclopedia_of_philosophy.py +3 -1
- scitex/scholar/url_finder/translators/individual/superlib.py +3 -1
- scitex/scholar/url_finder/translators/individual/treesearch.py +3 -1
- scitex/scholar/url_finder/translators/individual/university_of_chicago_press_books.py +3 -1
- scitex/scholar/url_finder/translators/individual/vlex.py +3 -1
- scitex/scholar/url_finder/translators/individual/web_of_science.py +3 -1
- scitex/scholar/url_finder/translators/individual/web_of_science_nextgen.py +3 -1
- scitex/scholar/url_finder/translators/individual/wiley.py +31 -25
- scitex/scholar/url_finder/translators/individual/wilson_center_digital_archive.py +3 -1
- scitex/scholar/utils/bibtex/_parse_bibtex.py +3 -3
- scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +5 -9
- scitex/scholar/utils/text/_TextNormalizer.py +249 -176
- scitex/scholar/utils/validation/DOIValidator.py +31 -28
- scitex/scholar/utils/validation/__init__.py +0 -0
- scitex/scholar/utils/validation/validate_library_dois.py +61 -57
- scitex/scholar/zotero/__init__.py +1 -1
- scitex/security/cli.py +7 -20
- scitex/security/github.py +45 -32
- scitex/session/__init__.py +8 -9
- scitex/session/_decorator.py +49 -42
- scitex/session/_lifecycle.py +39 -39
- scitex/session/_manager.py +24 -20
- scitex/sh/__init__.py +4 -3
- scitex/sh/_execute.py +10 -7
- scitex/sh/_security.py +3 -3
- scitex/sh/_types.py +2 -3
- scitex/stats/__init__.py +57 -6
- scitex/stats/_schema.py +42 -569
- scitex/stats/auto/__init__.py +188 -0
- scitex/stats/auto/_context.py +331 -0
- scitex/stats/auto/_formatting.py +679 -0
- scitex/stats/auto/_rules.py +901 -0
- scitex/stats/auto/_selector.py +554 -0
- scitex/stats/auto/_styles.py +721 -0
- scitex/stats/correct/__init__.py +4 -4
- scitex/stats/correct/_correct_bonferroni.py +43 -34
- scitex/stats/correct/_correct_fdr.py +14 -40
- scitex/stats/correct/_correct_fdr_.py +39 -46
- scitex/stats/correct/_correct_holm.py +14 -32
- scitex/stats/correct/_correct_sidak.py +36 -21
- scitex/stats/descriptive/_circular.py +20 -21
- scitex/stats/descriptive/_describe.py +19 -5
- scitex/stats/descriptive/_nan.py +5 -7
- scitex/stats/descriptive/_real.py +4 -3
- scitex/stats/effect_sizes/__init__.py +10 -11
- scitex/stats/effect_sizes/_cliffs_delta.py +35 -32
- scitex/stats/effect_sizes/_cohens_d.py +30 -31
- scitex/stats/effect_sizes/_epsilon_squared.py +19 -22
- scitex/stats/effect_sizes/_eta_squared.py +23 -27
- scitex/stats/effect_sizes/_prob_superiority.py +18 -21
- scitex/stats/posthoc/__init__.py +3 -3
- scitex/stats/posthoc/_dunnett.py +75 -55
- scitex/stats/posthoc/_games_howell.py +61 -43
- scitex/stats/posthoc/_tukey_hsd.py +42 -34
- scitex/stats/power/__init__.py +2 -2
- scitex/stats/power/_power.py +56 -56
- scitex/stats/tests/__init__.py +1 -1
- scitex/stats/tests/correlation/__init__.py +1 -1
- scitex/stats/tests/correlation/_test_pearson.py +28 -38
- scitex/stats/utils/__init__.py +14 -17
- scitex/stats/utils/_effect_size.py +85 -78
- scitex/stats/utils/_formatters.py +49 -43
- scitex/stats/utils/_normalizers.py +7 -14
- scitex/stats/utils/_power.py +56 -56
- scitex/str/__init__.py +1 -0
- scitex/str/_clean_path.py +3 -3
- scitex/str/_factor_out_digits.py +86 -58
- scitex/str/_format_plot_text.py +180 -111
- scitex/str/_latex.py +19 -19
- scitex/str/_latex_fallback.py +9 -10
- scitex/str/_parse.py +3 -6
- scitex/str/_print_debug.py +13 -13
- scitex/str/_printc.py +2 -0
- scitex/str/_search.py +3 -3
- scitex/template/.legacy/_clone_project.py +9 -13
- scitex/template/__init__.py +10 -2
- scitex/template/_clone_project.py +7 -2
- scitex/template/_copy.py +1 -0
- scitex/template/_customize.py +3 -6
- scitex/template/_git_strategy.py +2 -3
- scitex/template/_rename.py +1 -0
- scitex/template/clone_pip_project.py +6 -7
- scitex/template/clone_research.py +7 -10
- scitex/template/clone_singularity.py +6 -7
- scitex/template/clone_writer_directory.py +6 -7
- scitex/tex/_preview.py +26 -11
- scitex/tex/_to_vec.py +10 -7
- scitex/torch/__init__.py +11 -1
- scitex/types/_ArrayLike.py +2 -0
- scitex/types/_is_listed_X.py +3 -3
- scitex/units.py +110 -77
- scitex/utils/_compress_hdf5.py +3 -3
- scitex/utils/_email.py +8 -4
- scitex/utils/_notify.py +14 -8
- scitex/utils/_search.py +6 -6
- scitex/utils/_verify_scitex_format.py +17 -42
- scitex/utils/_verify_scitex_format_v01.py +12 -34
- scitex/utils/template.py +4 -3
- scitex/vis/__init__.py +0 -0
- scitex/vis/backend/__init__.py +3 -3
- scitex/vis/backend/{export.py → _export.py} +1 -1
- scitex/vis/backend/{parser.py → _parser.py} +1 -3
- scitex/vis/backend/{render.py → _render.py} +1 -1
- scitex/vis/canvas.py +15 -3
- scitex/vis/editor/__init__.py +0 -0
- scitex/vis/editor/_dearpygui_editor.py +450 -304
- scitex/vis/editor/_defaults.py +114 -123
- scitex/vis/editor/_edit.py +38 -26
- scitex/vis/editor/_flask_editor.py +8 -8
- scitex/vis/editor/_mpl_editor.py +63 -48
- scitex/vis/editor/_qt_editor.py +210 -159
- scitex/vis/editor/_tkinter_editor.py +146 -89
- scitex/vis/editor/flask_editor/__init__.py +10 -10
- scitex/vis/editor/flask_editor/_bbox.py +529 -0
- scitex/vis/editor/flask_editor/{core.py → _core.py} +45 -29
- scitex/vis/editor/flask_editor/_plotter.py +567 -0
- scitex/vis/editor/flask_editor/_renderer.py +393 -0
- scitex/vis/editor/flask_editor/{utils.py → _utils.py} +13 -14
- scitex/vis/editor/flask_editor/templates/__init__.py +5 -5
- scitex/vis/editor/flask_editor/templates/{html.py → _html.py} +234 -16
- scitex/vis/editor/flask_editor/templates/_scripts.py +1261 -0
- scitex/vis/editor/flask_editor/templates/{styles.py → _styles.py} +192 -2
- scitex/vis/io/__init__.py +5 -5
- scitex/vis/io/{canvas.py → _canvas.py} +8 -4
- scitex/vis/io/{data.py → _data.py} +13 -9
- scitex/vis/io/{directory.py → _directory.py} +7 -4
- scitex/vis/io/{export.py → _export.py} +15 -12
- scitex/vis/io/{load.py → _load.py} +1 -1
- scitex/vis/io/{panel.py → _panel.py} +21 -13
- scitex/vis/io/{save.py → _save.py} +0 -0
- scitex/vis/model/__init__.py +7 -7
- scitex/vis/model/{annotations.py → _annotations.py} +2 -4
- scitex/vis/model/{axes.py → _axes.py} +1 -1
- scitex/vis/model/{figure.py → _figure.py} +0 -0
- scitex/vis/model/{guides.py → _guides.py} +1 -1
- scitex/vis/model/{plot.py → _plot.py} +2 -4
- scitex/vis/model/{plot_types.py → _plot_types.py} +0 -0
- scitex/vis/model/{styles.py → _styles.py} +0 -0
- scitex/vis/utils/__init__.py +2 -2
- scitex/vis/utils/{defaults.py → _defaults.py} +1 -2
- scitex/vis/utils/{validate.py → _validate.py} +3 -9
- scitex/web/__init__.py +7 -1
- scitex/web/_scraping.py +54 -38
- scitex/web/_search_pubmed.py +30 -14
- scitex/writer/.legacy/Writer_v01-refactored.py +4 -4
- scitex/writer/.legacy/_compile.py +18 -28
- scitex/writer/Writer.py +8 -21
- scitex/writer/__init__.py +11 -11
- scitex/writer/_clone_writer_project.py +2 -6
- scitex/writer/_compile/__init__.py +1 -0
- scitex/writer/_compile/_parser.py +1 -0
- scitex/writer/_compile/_runner.py +35 -38
- scitex/writer/_compile/_validator.py +1 -0
- scitex/writer/_compile/manuscript.py +1 -0
- scitex/writer/_compile/revision.py +1 -0
- scitex/writer/_compile/supplementary.py +1 -0
- scitex/writer/_compile_async.py +5 -12
- scitex/writer/_project/__init__.py +1 -0
- scitex/writer/_project/_create.py +10 -25
- scitex/writer/_project/_trees.py +4 -9
- scitex/writer/_project/_validate.py +2 -3
- scitex/writer/_validate_tree_structures.py +7 -18
- scitex/writer/dataclasses/__init__.py +8 -10
- scitex/writer/dataclasses/config/_CONSTANTS.py +2 -3
- scitex/writer/dataclasses/config/_WriterConfig.py +4 -9
- scitex/writer/dataclasses/contents/_ManuscriptContents.py +14 -25
- scitex/writer/dataclasses/contents/_RevisionContents.py +21 -16
- scitex/writer/dataclasses/contents/_SupplementaryContents.py +21 -24
- scitex/writer/dataclasses/core/_Document.py +2 -3
- scitex/writer/dataclasses/core/_DocumentSection.py +8 -23
- scitex/writer/dataclasses/results/_CompilationResult.py +2 -3
- scitex/writer/dataclasses/results/_LaTeXIssue.py +3 -6
- scitex/writer/dataclasses/results/_SaveSectionsResponse.py +20 -9
- scitex/writer/dataclasses/results/_SectionReadResponse.py +24 -10
- scitex/writer/dataclasses/tree/_ConfigTree.py +7 -4
- scitex/writer/dataclasses/tree/_ManuscriptTree.py +10 -13
- scitex/writer/dataclasses/tree/_RevisionTree.py +16 -17
- scitex/writer/dataclasses/tree/_ScriptsTree.py +10 -5
- scitex/writer/dataclasses/tree/_SharedTree.py +10 -13
- scitex/writer/dataclasses/tree/_SupplementaryTree.py +15 -14
- scitex/writer/utils/.legacy_git_retry.py +3 -8
- scitex/writer/utils/_parse_latex_logs.py +2 -3
- scitex/writer/utils/_parse_script_args.py +20 -23
- scitex/writer/utils/_watch.py +5 -5
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/METADATA +4 -10
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/RECORD +1071 -975
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +0 -583
- scitex/plt/_subplots/_export_as_csv_formatters.py +0 -112
- scitex/vis/editor/flask_editor/bbox.py +0 -216
- scitex/vis/editor/flask_editor/plotter.py +0 -130
- scitex/vis/editor/flask_editor/renderer.py +0 -184
- scitex/vis/editor/flask_editor/templates/scripts.py +0 -614
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/WHEEL +0 -0
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# ----------------------------------------
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
import os
|
|
8
|
+
|
|
8
9
|
__FILE__ = __file__
|
|
9
10
|
__DIR__ = os.path.dirname(__FILE__)
|
|
10
11
|
# ----------------------------------------
|
|
@@ -36,7 +37,7 @@ def generate_org_report(
|
|
|
36
37
|
) -> Path:
|
|
37
38
|
"""
|
|
38
39
|
Generate org-mode report with inline images and optional pandoc conversions.
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
Parameters
|
|
41
42
|
----------
|
|
42
43
|
results : Dict[str, Any]
|
|
@@ -49,7 +50,7 @@ def generate_org_report(
|
|
|
49
50
|
Whether to print progress messages
|
|
50
51
|
convert_formats : bool, default True
|
|
51
52
|
Whether to use pandoc to generate other formats
|
|
52
|
-
|
|
53
|
+
|
|
53
54
|
Returns
|
|
54
55
|
-------
|
|
55
56
|
Path
|
|
@@ -57,24 +58,37 @@ def generate_org_report(
|
|
|
57
58
|
"""
|
|
58
59
|
output_path = Path(output_path)
|
|
59
60
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
60
|
-
|
|
61
|
+
|
|
61
62
|
# Try to get CONFIG from results first (passed from memory)
|
|
62
63
|
config_data = None
|
|
63
64
|
# Debug logging
|
|
64
65
|
from scitex.logging import getLogger
|
|
66
|
+
|
|
65
67
|
logger = getLogger(__name__)
|
|
66
|
-
|
|
68
|
+
|
|
67
69
|
# First check if session_config was passed in results
|
|
68
|
-
if
|
|
70
|
+
if "session_config" in results and results["session_config"] is not None:
|
|
69
71
|
# Convert CONFIG object to dict, only keeping useful fields
|
|
70
|
-
session_config = results[
|
|
72
|
+
session_config = results["session_config"]
|
|
71
73
|
config_data = {}
|
|
72
|
-
|
|
74
|
+
|
|
73
75
|
# List of keys to include (uppercase attributes that are not methods)
|
|
74
|
-
useful_keys = [
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
useful_keys = [
|
|
77
|
+
"ID",
|
|
78
|
+
"PID",
|
|
79
|
+
"START_TIME",
|
|
80
|
+
"END_TIME",
|
|
81
|
+
"RUN_TIME",
|
|
82
|
+
"FILE",
|
|
83
|
+
"FILE_PATH",
|
|
84
|
+
"SDIR",
|
|
85
|
+
"SDIR_PATH",
|
|
86
|
+
"REL_SDIR",
|
|
87
|
+
"REL_SDIR_PATH",
|
|
88
|
+
"ARGS",
|
|
89
|
+
"EXIT_STATUS",
|
|
90
|
+
]
|
|
91
|
+
|
|
78
92
|
for key in useful_keys:
|
|
79
93
|
if hasattr(session_config, key):
|
|
80
94
|
value = getattr(session_config, key)
|
|
@@ -85,31 +99,35 @@ def generate_org_report(
|
|
|
85
99
|
else:
|
|
86
100
|
# Use str() for cleaner output
|
|
87
101
|
config_data[key] = str(value)
|
|
88
|
-
|
|
102
|
+
|
|
89
103
|
logger.info(f"Using session CONFIG from memory with {len(config_data)} keys")
|
|
90
|
-
|
|
104
|
+
|
|
91
105
|
# Fallback to loading from file if not provided
|
|
92
106
|
if config_data is None:
|
|
93
107
|
try:
|
|
94
108
|
# Try different possible locations for CONFIG.yaml
|
|
95
109
|
# The report is generated in classification_results/reports/
|
|
96
110
|
# CONFIG.yaml is in the session directory under CONFIGS/
|
|
97
|
-
|
|
111
|
+
|
|
98
112
|
# Get the session directory from the output path
|
|
99
113
|
# output_path is like: /path/to/RUNNING/ID/classification_results/reports/report.org
|
|
100
114
|
# We need: /path/to/RUNNING/ID/CONFIGS/CONFIG.yaml
|
|
101
|
-
|
|
115
|
+
|
|
102
116
|
# output_path.parent = /path/to/RUNNING/ID/classification_results/reports/
|
|
103
117
|
# output_path.parent.parent = /path/to/RUNNING/ID/classification_results/
|
|
104
118
|
# output_path.parent.parent.parent = /path/to/RUNNING/ID/ <- Session root!
|
|
105
|
-
|
|
119
|
+
|
|
106
120
|
session_dir = output_path.parent.parent.parent
|
|
107
121
|
possible_paths = [
|
|
108
|
-
session_dir
|
|
122
|
+
session_dir
|
|
123
|
+
/ "CONFIGS"
|
|
124
|
+
/ "CONFIG.yaml", # This should be the correct path
|
|
109
125
|
]
|
|
110
|
-
|
|
126
|
+
|
|
111
127
|
logger.info(f"Looking for CONFIG.yaml from: {output_path}")
|
|
112
|
-
logger.info(
|
|
128
|
+
logger.info(
|
|
129
|
+
f"Output path parent dirs: {output_path.parent}, {output_path.parent.parent}, {output_path.parent.parent.parent}"
|
|
130
|
+
)
|
|
113
131
|
config_path = None
|
|
114
132
|
for path in possible_paths:
|
|
115
133
|
logger.info(f"Checking path: {path}, exists: {path.exists()}")
|
|
@@ -117,22 +135,28 @@ def generate_org_report(
|
|
|
117
135
|
config_path = path
|
|
118
136
|
logger.info(f"Found CONFIG at path: {config_path}")
|
|
119
137
|
break
|
|
120
|
-
|
|
138
|
+
|
|
121
139
|
# Try to get from session output directory if not found
|
|
122
|
-
if
|
|
140
|
+
if (
|
|
141
|
+
not config_path
|
|
142
|
+
and "config" in results
|
|
143
|
+
and "output_dir" in results["config"]
|
|
144
|
+
):
|
|
123
145
|
# The output_dir is like /path/to/RUNNING/ID/classification_results
|
|
124
146
|
# We need to go to /path/to/RUNNING/ID/CONFIGS/CONFIG.yaml
|
|
125
|
-
output_dir = Path(results[
|
|
147
|
+
output_dir = Path(results["config"]["output_dir"])
|
|
126
148
|
# Go up to session dir (from classification_results to session root)
|
|
127
|
-
session_dir = output_dir.parent
|
|
149
|
+
session_dir = output_dir.parent
|
|
128
150
|
config_path = session_dir / "CONFIGS" / "CONFIG.yaml"
|
|
129
|
-
logger.info(
|
|
151
|
+
logger.info(
|
|
152
|
+
f"Trying session path: {config_path}, exists: {config_path.exists()}"
|
|
153
|
+
)
|
|
130
154
|
if not config_path.exists():
|
|
131
155
|
config_path = None
|
|
132
|
-
|
|
156
|
+
|
|
133
157
|
if config_path and config_path.exists():
|
|
134
158
|
logger.info(f"Found CONFIG at: {config_path}")
|
|
135
|
-
with open(config_path,
|
|
159
|
+
with open(config_path, "r") as config_file:
|
|
136
160
|
config_data = yaml.safe_load(config_file)
|
|
137
161
|
logger.info(f"Successfully loaded CONFIG with {len(config_data)} keys")
|
|
138
162
|
else:
|
|
@@ -141,25 +165,28 @@ def generate_org_report(
|
|
|
141
165
|
# Always log the error for debugging
|
|
142
166
|
logger.warning(f"Could not load CONFIG.yaml: {e}")
|
|
143
167
|
import traceback
|
|
168
|
+
|
|
144
169
|
logger.warning(f"Traceback: {traceback.format_exc()}")
|
|
145
|
-
|
|
146
|
-
with open(output_path,
|
|
170
|
+
|
|
171
|
+
with open(output_path, "w") as f:
|
|
147
172
|
# Header
|
|
148
173
|
f.write("#+TITLE: Classification Results Report\n")
|
|
149
174
|
f.write(f"#+AUTHOR: SciTeX Classification Reporter\n")
|
|
150
175
|
f.write(f"#+DATE: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
151
176
|
f.write("#+OPTIONS: toc:2 num:t\n")
|
|
152
177
|
f.write("#+STARTUP: overview inlineimages\n")
|
|
153
|
-
f.write(
|
|
178
|
+
f.write(
|
|
179
|
+
"#+HTML_HEAD: <style>img { cursor: zoom-in; } img:active { transform: scale(2.5); z-index: 999; position: relative; }</style>\n"
|
|
180
|
+
)
|
|
154
181
|
f.write("#+ATTR_ORG: :width 400\n\n") # Default width for all images
|
|
155
|
-
|
|
182
|
+
|
|
156
183
|
# Get configuration and fold info
|
|
157
|
-
config = results.get(
|
|
158
|
-
n_folds = config.get(
|
|
159
|
-
|
|
184
|
+
config = results.get("config", {})
|
|
185
|
+
n_folds = config.get("n_folds", len(results.get("folds", [])))
|
|
186
|
+
|
|
160
187
|
# Dataset information (sample sizes) - extract from folds
|
|
161
188
|
f.write("* Dataset Information\n\n")
|
|
162
|
-
if
|
|
189
|
+
if "folds" in results and results["folds"]:
|
|
163
190
|
# Create table header
|
|
164
191
|
sample_header = "| Fold | Train Total | Train Seizure | Train Interictal | Test Total | Test Seizure | Test Interictal |"
|
|
165
192
|
sample_separator = "|------|-------------|---------------|------------------|------------|--------------|-----------------|"
|
|
@@ -168,15 +195,15 @@ def generate_org_report(
|
|
|
168
195
|
f.write(sample_separator + "\n")
|
|
169
196
|
|
|
170
197
|
# Add sample size info for each fold if available
|
|
171
|
-
for fold_data in results[
|
|
172
|
-
fold_id = fold_data.get(
|
|
198
|
+
for fold_data in results["folds"]:
|
|
199
|
+
fold_id = fold_data.get("fold_id", "?")
|
|
173
200
|
# Sample sizes might be in fold_data directly or we need to compute
|
|
174
|
-
n_train = fold_data.get(
|
|
175
|
-
n_test = fold_data.get(
|
|
176
|
-
n_train_seizure = fold_data.get(
|
|
177
|
-
n_train_interictal = fold_data.get(
|
|
178
|
-
n_test_seizure = fold_data.get(
|
|
179
|
-
n_test_interictal = fold_data.get(
|
|
201
|
+
n_train = fold_data.get("n_train", "-")
|
|
202
|
+
n_test = fold_data.get("n_test", "-")
|
|
203
|
+
n_train_seizure = fold_data.get("n_train_seizure", "-")
|
|
204
|
+
n_train_interictal = fold_data.get("n_train_interictal", "-")
|
|
205
|
+
n_test_seizure = fold_data.get("n_test_seizure", "-")
|
|
206
|
+
n_test_interictal = fold_data.get("n_test_interictal", "-")
|
|
180
207
|
|
|
181
208
|
row = f"| {fold_id:02d} | {n_train} | {n_train_seizure} | {n_train_interictal} | {n_test} | {n_test_seizure} | {n_test_interictal} |"
|
|
182
209
|
f.write(row + "\n")
|
|
@@ -185,7 +212,7 @@ def generate_org_report(
|
|
|
185
212
|
f.write("* Summary Performance\n\n")
|
|
186
213
|
|
|
187
214
|
# Create comprehensive metrics table including per-fold values
|
|
188
|
-
if
|
|
215
|
+
if "summary" in results and results["summary"]:
|
|
189
216
|
# Build header with fold columns
|
|
190
217
|
header = "| Metric |"
|
|
191
218
|
separator = "|--------|"
|
|
@@ -199,23 +226,25 @@ def generate_org_report(
|
|
|
199
226
|
f.write(separator + "\n")
|
|
200
227
|
|
|
201
228
|
# Display metrics in specific order
|
|
202
|
-
metric_order = [
|
|
229
|
+
metric_order = ["balanced_accuracy", "mcc", "roc_auc", "pr_auc"]
|
|
203
230
|
metric_display_names = {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
231
|
+
"balanced_accuracy": "Balanced Accuracy",
|
|
232
|
+
"mcc": "MCC",
|
|
233
|
+
"roc_auc": "ROC AUC",
|
|
234
|
+
"pr_auc": "PR AUC",
|
|
208
235
|
}
|
|
209
236
|
|
|
210
237
|
# Collect fold values
|
|
211
238
|
for metric_name in metric_order:
|
|
212
|
-
if metric_name in results[
|
|
213
|
-
stats = results[
|
|
214
|
-
if isinstance(stats, dict) and
|
|
215
|
-
row =
|
|
239
|
+
if metric_name in results["summary"]:
|
|
240
|
+
stats = results["summary"][metric_name]
|
|
241
|
+
if isinstance(stats, dict) and "mean" in stats:
|
|
242
|
+
row = (
|
|
243
|
+
f"| {metric_display_names.get(metric_name, metric_name)} |"
|
|
244
|
+
)
|
|
216
245
|
|
|
217
246
|
# Add individual fold values (rounded to 3 digits)
|
|
218
|
-
fold_values = stats.get(
|
|
247
|
+
fold_values = stats.get("values", [])
|
|
219
248
|
for i in range(n_folds):
|
|
220
249
|
if i < len(fold_values):
|
|
221
250
|
row += f" {fold_values[i]:.3f} |"
|
|
@@ -223,61 +252,76 @@ def generate_org_report(
|
|
|
223
252
|
row += " - |"
|
|
224
253
|
|
|
225
254
|
# Add mean ± std (rounded to 3 digits)
|
|
226
|
-
mean = stats.get(
|
|
227
|
-
std = stats.get(
|
|
255
|
+
mean = stats.get("mean", 0)
|
|
256
|
+
std = stats.get("std", 0)
|
|
228
257
|
row += f" {mean:.3f} ± {std:.3f} |"
|
|
229
258
|
f.write(row + "\n")
|
|
230
259
|
f.write("\n")
|
|
231
260
|
|
|
232
261
|
# Feature Importance section
|
|
233
|
-
if
|
|
262
|
+
if "summary" in results and "feature_importance" in results["summary"]:
|
|
234
263
|
f.write("* Feature Importance\n\n")
|
|
235
|
-
feature_imp = results[
|
|
264
|
+
feature_imp = results["summary"]["feature_importance"]
|
|
236
265
|
|
|
237
|
-
if
|
|
266
|
+
if "mean" in feature_imp:
|
|
238
267
|
# Create feature importance table
|
|
239
268
|
f.write("| Feature | Mean | Std | Min | Max | CV |\n")
|
|
240
269
|
f.write("|---------|------|-----|-----|-----|----|\n")
|
|
241
270
|
|
|
242
271
|
# Sort by mean importance (descending)
|
|
243
272
|
features_sorted = sorted(
|
|
244
|
-
feature_imp[
|
|
245
|
-
key=lambda x: x[1],
|
|
246
|
-
reverse=True
|
|
273
|
+
feature_imp["mean"].items(), key=lambda x: x[1], reverse=True
|
|
247
274
|
)
|
|
248
275
|
|
|
249
276
|
for feature_name, mean_imp in features_sorted:
|
|
250
|
-
std_imp = feature_imp[
|
|
251
|
-
min_imp = feature_imp[
|
|
252
|
-
max_imp = feature_imp[
|
|
253
|
-
cv_imp = feature_imp[
|
|
277
|
+
std_imp = feature_imp["std"].get(feature_name, 0)
|
|
278
|
+
min_imp = feature_imp["min"].get(feature_name, 0)
|
|
279
|
+
max_imp = feature_imp["max"].get(feature_name, 0)
|
|
280
|
+
cv_imp = feature_imp["cv"].get(feature_name, 0)
|
|
254
281
|
|
|
255
|
-
f.write(
|
|
256
|
-
|
|
282
|
+
f.write(
|
|
283
|
+
f"| {feature_name} | {mean_imp:.3f} | {std_imp:.3f} | "
|
|
284
|
+
f"{min_imp:.3f} | {max_imp:.3f} | {cv_imp:.3f} |\n"
|
|
285
|
+
)
|
|
257
286
|
f.write("\n")
|
|
258
287
|
|
|
259
|
-
f.write(
|
|
260
|
-
|
|
288
|
+
f.write(
|
|
289
|
+
"*Note:* CV = Coefficient of Variation (std/mean), "
|
|
290
|
+
"indicating stability across folds.\n\n"
|
|
291
|
+
)
|
|
261
292
|
|
|
262
293
|
# Visualizations section
|
|
263
|
-
if include_plots and
|
|
294
|
+
if include_plots and "plots" in results:
|
|
264
295
|
f.write("* Visualizations\n\n")
|
|
265
|
-
|
|
296
|
+
|
|
266
297
|
# Confusion Matrices - all in one section
|
|
267
298
|
f.write("** Confusion Matrices\n\n")
|
|
268
299
|
f.write("#+BEGIN_EXPORT html\n")
|
|
269
|
-
f.write(
|
|
300
|
+
f.write(
|
|
301
|
+
"<div style='display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; max-width: 100%;'>\n"
|
|
302
|
+
)
|
|
270
303
|
f.write("#+END_EXPORT\n\n")
|
|
271
|
-
|
|
304
|
+
|
|
272
305
|
# CV Summary confusion matrix first
|
|
273
|
-
cv_summary_plots = {
|
|
306
|
+
cv_summary_plots = {
|
|
307
|
+
k: v
|
|
308
|
+
for k, v in results["plots"].items()
|
|
309
|
+
if "cv_summary" in k or "cv-summary" in k
|
|
310
|
+
}
|
|
274
311
|
# Support both old (confusion_matrix) and new (confusion-matrix) naming
|
|
275
|
-
cm_plots = [
|
|
276
|
-
|
|
312
|
+
cm_plots = [
|
|
313
|
+
v
|
|
314
|
+
for k, v in cv_summary_plots.items()
|
|
315
|
+
if ("confusion_matrix" in k or "confusion-matrix" in k)
|
|
316
|
+
]
|
|
317
|
+
|
|
277
318
|
if cm_plots:
|
|
278
319
|
for plot_path in cm_plots:
|
|
279
|
-
rel_path = _make_relative_path(
|
|
280
|
-
|
|
320
|
+
rel_path = _make_relative_path(
|
|
321
|
+
output_path.parent,
|
|
322
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
323
|
+
/ plot_path,
|
|
324
|
+
)
|
|
281
325
|
f.write("#+BEGIN_EXPORT html\n")
|
|
282
326
|
f.write("<div style='text-align: center;'>\n")
|
|
283
327
|
f.write("#+END_EXPORT\n")
|
|
@@ -288,47 +332,65 @@ def generate_org_report(
|
|
|
288
332
|
f.write("#+BEGIN_EXPORT html\n")
|
|
289
333
|
f.write("</div>\n")
|
|
290
334
|
f.write("#+END_EXPORT\n\n")
|
|
291
|
-
|
|
335
|
+
|
|
292
336
|
# Individual fold confusion matrices
|
|
293
337
|
for fold in range(n_folds):
|
|
294
338
|
# Look for plots with exact fold matching
|
|
295
|
-
fold_key = f
|
|
296
|
-
fold_plots = {
|
|
339
|
+
fold_key = f"fold_{fold:02d}"
|
|
340
|
+
fold_plots = {
|
|
341
|
+
k: v for k, v in results["plots"].items() if fold_key in k
|
|
342
|
+
}
|
|
297
343
|
# Support both old (confusion_matrix) and new (confusion-matrix) naming
|
|
298
|
-
fold_cm = [
|
|
299
|
-
|
|
344
|
+
fold_cm = [
|
|
345
|
+
v
|
|
346
|
+
for k, v in fold_plots.items()
|
|
347
|
+
if ("confusion_matrix" in k or "confusion-matrix" in k)
|
|
348
|
+
]
|
|
349
|
+
|
|
300
350
|
if fold_cm and len(fold_cm) > 0:
|
|
301
351
|
# Take only the first matching confusion matrix for this fold
|
|
302
352
|
plot_path = fold_cm[0]
|
|
303
|
-
rel_path = _make_relative_path(
|
|
304
|
-
|
|
353
|
+
rel_path = _make_relative_path(
|
|
354
|
+
output_path.parent,
|
|
355
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
356
|
+
/ plot_path,
|
|
357
|
+
)
|
|
305
358
|
f.write("#+BEGIN_EXPORT html\n")
|
|
306
359
|
f.write("<div style='text-align: center;'>\n")
|
|
307
360
|
f.write("#+END_EXPORT\n")
|
|
308
361
|
f.write("#+ATTR_ORG: :width 250\n")
|
|
309
|
-
f.write("#+ATTR_HTML: :width 100% :style max-width:250px\n")
|
|
362
|
+
f.write("#+ATTR_HTML: :width 100% :style max-width:250px\n")
|
|
310
363
|
f.write(f"#+CAPTION: Fold {fold:02d}\n")
|
|
311
364
|
f.write(f"[[file:{rel_path}]]\n")
|
|
312
365
|
f.write("#+BEGIN_EXPORT html\n")
|
|
313
366
|
f.write("</div>\n")
|
|
314
367
|
f.write("#+END_EXPORT\n\n")
|
|
315
|
-
|
|
368
|
+
|
|
316
369
|
f.write("#+BEGIN_EXPORT html\n")
|
|
317
370
|
f.write("</div>\n")
|
|
318
371
|
f.write("#+END_EXPORT\n\n")
|
|
319
|
-
|
|
372
|
+
|
|
320
373
|
# ROC Curves - all in one section
|
|
321
374
|
f.write("** ROC Curves\n\n")
|
|
322
375
|
f.write("#+BEGIN_EXPORT html\n")
|
|
323
|
-
f.write(
|
|
376
|
+
f.write(
|
|
377
|
+
"<div style='display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; max-width: 100%;'>\n"
|
|
378
|
+
)
|
|
324
379
|
f.write("#+END_EXPORT\n\n")
|
|
325
|
-
|
|
380
|
+
|
|
326
381
|
# CV Summary ROC curve (support both old and new naming)
|
|
327
|
-
roc_plots = [
|
|
382
|
+
roc_plots = [
|
|
383
|
+
v
|
|
384
|
+
for k, v in cv_summary_plots.items()
|
|
385
|
+
if ("roc_curve" in k or "roc-curve" in k)
|
|
386
|
+
]
|
|
328
387
|
if roc_plots:
|
|
329
388
|
for plot_path in roc_plots:
|
|
330
|
-
rel_path = _make_relative_path(
|
|
331
|
-
|
|
389
|
+
rel_path = _make_relative_path(
|
|
390
|
+
output_path.parent,
|
|
391
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
392
|
+
/ plot_path,
|
|
393
|
+
)
|
|
332
394
|
f.write("#+BEGIN_EXPORT html\n")
|
|
333
395
|
f.write("<div style='text-align: center;'>\n")
|
|
334
396
|
f.write("#+END_EXPORT\n")
|
|
@@ -339,20 +401,29 @@ def generate_org_report(
|
|
|
339
401
|
f.write("#+BEGIN_EXPORT html\n")
|
|
340
402
|
f.write("</div>\n")
|
|
341
403
|
f.write("#+END_EXPORT\n\n")
|
|
342
|
-
|
|
404
|
+
|
|
343
405
|
# Individual fold ROC curves
|
|
344
406
|
for fold in range(n_folds):
|
|
345
407
|
# Look for plots with exact fold matching
|
|
346
|
-
fold_key = f
|
|
347
|
-
fold_plots = {
|
|
408
|
+
fold_key = f"fold_{fold:02d}"
|
|
409
|
+
fold_plots = {
|
|
410
|
+
k: v for k, v in results["plots"].items() if fold_key in k
|
|
411
|
+
}
|
|
348
412
|
# Support both old (roc_curve) and new (roc-curve) naming
|
|
349
|
-
fold_roc = [
|
|
350
|
-
|
|
413
|
+
fold_roc = [
|
|
414
|
+
v
|
|
415
|
+
for k, v in fold_plots.items()
|
|
416
|
+
if ("roc_curve" in k or "roc-curve" in k)
|
|
417
|
+
]
|
|
418
|
+
|
|
351
419
|
if fold_roc and len(fold_roc) > 0:
|
|
352
420
|
# Take only the first matching ROC curve for this fold
|
|
353
421
|
plot_path = fold_roc[0]
|
|
354
|
-
rel_path = _make_relative_path(
|
|
355
|
-
|
|
422
|
+
rel_path = _make_relative_path(
|
|
423
|
+
output_path.parent,
|
|
424
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
425
|
+
/ plot_path,
|
|
426
|
+
)
|
|
356
427
|
f.write("#+BEGIN_EXPORT html\n")
|
|
357
428
|
f.write("<div style='text-align: center;'>\n")
|
|
358
429
|
f.write("#+END_EXPORT\n")
|
|
@@ -363,23 +434,32 @@ def generate_org_report(
|
|
|
363
434
|
f.write("#+BEGIN_EXPORT html\n")
|
|
364
435
|
f.write("</div>\n")
|
|
365
436
|
f.write("#+END_EXPORT\n\n")
|
|
366
|
-
|
|
437
|
+
|
|
367
438
|
f.write("#+BEGIN_EXPORT html\n")
|
|
368
439
|
f.write("</div>\n")
|
|
369
440
|
f.write("#+END_EXPORT\n\n")
|
|
370
|
-
|
|
371
|
-
# PR Curves - all in one section
|
|
441
|
+
|
|
442
|
+
# PR Curves - all in one section
|
|
372
443
|
f.write("** Precision-Recall Curves\n\n")
|
|
373
444
|
f.write("#+BEGIN_EXPORT html\n")
|
|
374
|
-
f.write(
|
|
445
|
+
f.write(
|
|
446
|
+
"<div style='display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; max-width: 100%;'>\n"
|
|
447
|
+
)
|
|
375
448
|
f.write("#+END_EXPORT\n\n")
|
|
376
|
-
|
|
449
|
+
|
|
377
450
|
# CV Summary PR curve (support both old and new naming)
|
|
378
|
-
pr_plots = [
|
|
451
|
+
pr_plots = [
|
|
452
|
+
v
|
|
453
|
+
for k, v in cv_summary_plots.items()
|
|
454
|
+
if ("pr_curve" in k or "pr-curve" in k)
|
|
455
|
+
]
|
|
379
456
|
if pr_plots:
|
|
380
457
|
for plot_path in pr_plots:
|
|
381
|
-
rel_path = _make_relative_path(
|
|
382
|
-
|
|
458
|
+
rel_path = _make_relative_path(
|
|
459
|
+
output_path.parent,
|
|
460
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
461
|
+
/ plot_path,
|
|
462
|
+
)
|
|
383
463
|
f.write("#+BEGIN_EXPORT html\n")
|
|
384
464
|
f.write("<div style='text-align: center;'>\n")
|
|
385
465
|
f.write("#+END_EXPORT\n")
|
|
@@ -390,20 +470,29 @@ def generate_org_report(
|
|
|
390
470
|
f.write("#+BEGIN_EXPORT html\n")
|
|
391
471
|
f.write("</div>\n")
|
|
392
472
|
f.write("#+END_EXPORT\n\n")
|
|
393
|
-
|
|
473
|
+
|
|
394
474
|
# Individual fold PR curves
|
|
395
475
|
for fold in range(n_folds):
|
|
396
476
|
# Look for plots with exact fold matching
|
|
397
|
-
fold_key = f
|
|
398
|
-
fold_plots = {
|
|
477
|
+
fold_key = f"fold_{fold:02d}"
|
|
478
|
+
fold_plots = {
|
|
479
|
+
k: v for k, v in results["plots"].items() if fold_key in k
|
|
480
|
+
}
|
|
399
481
|
# Support both old (pr_curve) and new (pr-curve) naming
|
|
400
|
-
fold_pr = [
|
|
401
|
-
|
|
482
|
+
fold_pr = [
|
|
483
|
+
v
|
|
484
|
+
for k, v in fold_plots.items()
|
|
485
|
+
if ("pr_curve" in k or "pr-curve" in k)
|
|
486
|
+
]
|
|
487
|
+
|
|
402
488
|
if fold_pr and len(fold_pr) > 0:
|
|
403
489
|
# Take only the first matching PR curve for this fold
|
|
404
490
|
plot_path = fold_pr[0]
|
|
405
|
-
rel_path = _make_relative_path(
|
|
406
|
-
|
|
491
|
+
rel_path = _make_relative_path(
|
|
492
|
+
output_path.parent,
|
|
493
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
494
|
+
/ plot_path,
|
|
495
|
+
)
|
|
407
496
|
f.write("#+BEGIN_EXPORT html\n")
|
|
408
497
|
f.write("<div style='text-align: center;'>\n")
|
|
409
498
|
f.write("#+END_EXPORT\n")
|
|
@@ -414,105 +503,128 @@ def generate_org_report(
|
|
|
414
503
|
f.write("#+BEGIN_EXPORT html\n")
|
|
415
504
|
f.write("</div>\n")
|
|
416
505
|
f.write("#+END_EXPORT\n\n")
|
|
417
|
-
|
|
506
|
+
|
|
418
507
|
f.write("#+BEGIN_EXPORT html\n")
|
|
419
508
|
f.write("</div>\n")
|
|
420
509
|
f.write("#+END_EXPORT\n\n")
|
|
421
|
-
|
|
510
|
+
|
|
422
511
|
# Experiment Configuration section at the end (less prominent)
|
|
423
|
-
logger.info(
|
|
512
|
+
logger.info(
|
|
513
|
+
f"config_data is: {config_data is not None}, type: {type(config_data)}"
|
|
514
|
+
)
|
|
424
515
|
if config_data:
|
|
425
516
|
logger.info(f"Writing CONFIG section with {len(config_data)} keys")
|
|
426
517
|
f.write("* Experiment Configuration\n\n")
|
|
427
518
|
f.write("| Parameter | Value |\n")
|
|
428
519
|
f.write("|-----------|-------|\n")
|
|
429
|
-
|
|
520
|
+
|
|
430
521
|
# Display configuration in a clean format
|
|
431
522
|
for key, value in sorted(config_data.items()):
|
|
432
523
|
# Format the key nicely
|
|
433
|
-
display_key = key.replace(
|
|
524
|
+
display_key = key.replace("_", " ").title()
|
|
434
525
|
# Format value for org-mode table
|
|
435
526
|
display_value = str(value)
|
|
436
527
|
# Escape pipe characters in values for org table
|
|
437
|
-
display_value = display_value.replace(
|
|
528
|
+
display_value = display_value.replace("|", "\\vert{}")
|
|
438
529
|
# For paths, show just the relative part if too long
|
|
439
|
-
if
|
|
530
|
+
if "SDIR" in key.upper() and len(display_value) > 80:
|
|
440
531
|
# Try to show the end part which is more informative
|
|
441
|
-
if
|
|
442
|
-
parts = display_value.split(
|
|
532
|
+
if "/" in display_value:
|
|
533
|
+
parts = display_value.split("/")
|
|
443
534
|
# Keep last few parts
|
|
444
535
|
if len(parts) > 4:
|
|
445
|
-
display_value =
|
|
536
|
+
display_value = ".../" + "/".join(parts[-4:])
|
|
446
537
|
# Wrap in verbatim for paths to avoid formatting issues
|
|
447
|
-
if
|
|
538
|
+
if "/" in display_value or "_" in display_value:
|
|
448
539
|
display_value = f"~{display_value}~"
|
|
449
540
|
f.write(f"| {display_key} | {display_value} |\n")
|
|
450
541
|
f.write("\n")
|
|
451
|
-
|
|
542
|
+
|
|
452
543
|
# Footer
|
|
453
544
|
f.write("\n* Report Generation\n\n")
|
|
454
545
|
f.write(f"- Generated at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
455
546
|
f.write("- Generated by: SciTeX Classification Reporter\n")
|
|
456
547
|
f.write("- Format: Org-mode\n")
|
|
457
|
-
|
|
548
|
+
|
|
458
549
|
if verbose:
|
|
459
550
|
from scitex.logging import getLogger
|
|
551
|
+
|
|
460
552
|
logger = getLogger(__name__)
|
|
461
553
|
logger.info(f"Generated org-mode report: {output_path}")
|
|
462
|
-
|
|
554
|
+
|
|
463
555
|
# Optional pandoc conversions
|
|
464
556
|
if convert_formats:
|
|
465
557
|
import subprocess
|
|
466
558
|
import shutil
|
|
467
559
|
import os
|
|
468
|
-
|
|
560
|
+
|
|
469
561
|
# Check for pandoc in regular PATH or module system
|
|
470
|
-
pandoc_cmd = shutil.which(
|
|
562
|
+
pandoc_cmd = shutil.which("pandoc")
|
|
471
563
|
if not pandoc_cmd:
|
|
472
564
|
# Try module system path
|
|
473
|
-
module_pandoc =
|
|
565
|
+
module_pandoc = (
|
|
566
|
+
"/apps/easybuild-2022/easybuild/software/Core/Pandoc/3.1.2/bin/pandoc"
|
|
567
|
+
)
|
|
474
568
|
if os.path.exists(module_pandoc):
|
|
475
569
|
pandoc_cmd = module_pandoc
|
|
476
|
-
|
|
570
|
+
|
|
477
571
|
if pandoc_cmd:
|
|
478
572
|
conversions = [
|
|
479
573
|
# (output_filename, extra_args, description)
|
|
480
|
-
(output_path.with_suffix(
|
|
481
|
-
(
|
|
482
|
-
|
|
483
|
-
|
|
574
|
+
(output_path.with_suffix(".md"), [], "markdown"),
|
|
575
|
+
(
|
|
576
|
+
output_path.with_suffix(".html"),
|
|
577
|
+
["--standalone", "--embed-resources"],
|
|
578
|
+
"HTML",
|
|
579
|
+
),
|
|
580
|
+
(output_path.with_suffix(".tex"), [], "LaTeX"),
|
|
581
|
+
(
|
|
582
|
+
output_path.with_suffix(".docx"),
|
|
583
|
+
["--resource-path=" + str(output_path.parent.parent)],
|
|
584
|
+
"DOCX",
|
|
585
|
+
),
|
|
484
586
|
]
|
|
485
|
-
|
|
587
|
+
|
|
486
588
|
for output_file, extra_args, format_name in conversions:
|
|
487
589
|
try:
|
|
488
|
-
cmd = [
|
|
590
|
+
cmd = [
|
|
591
|
+
pandoc_cmd,
|
|
592
|
+
str(output_path),
|
|
593
|
+
"-o",
|
|
594
|
+
str(output_file),
|
|
595
|
+
] + extra_args
|
|
489
596
|
result = subprocess.run(
|
|
490
|
-
cmd,
|
|
491
|
-
capture_output=True,
|
|
492
|
-
text=True,
|
|
493
|
-
timeout=30
|
|
597
|
+
cmd, capture_output=True, text=True, timeout=30
|
|
494
598
|
)
|
|
495
599
|
if result.returncode == 0 and verbose:
|
|
496
600
|
logger.info(f"Generated {format_name} report: {output_file}")
|
|
497
601
|
elif verbose and result.returncode != 0:
|
|
498
|
-
logger.warning(
|
|
602
|
+
logger.warning(
|
|
603
|
+
f"{format_name} generation failed: {result.stderr}"
|
|
604
|
+
)
|
|
499
605
|
except subprocess.TimeoutExpired:
|
|
500
606
|
if verbose:
|
|
501
607
|
logger.warning(f"{format_name} conversion timed out")
|
|
502
608
|
except Exception as e:
|
|
503
609
|
if verbose:
|
|
504
610
|
logger.warning(f"{format_name} conversion failed: {e}")
|
|
505
|
-
|
|
611
|
+
|
|
506
612
|
# Convert to PDF (requires LaTeX)
|
|
507
|
-
if shutil.which(
|
|
613
|
+
if shutil.which("xelatex") or shutil.which("pdflatex"):
|
|
508
614
|
try:
|
|
509
|
-
pdf_path = output_path.with_suffix(
|
|
510
|
-
pdf_engine =
|
|
615
|
+
pdf_path = output_path.with_suffix(".pdf")
|
|
616
|
+
pdf_engine = "xelatex" if shutil.which("xelatex") else "pdflatex"
|
|
511
617
|
result = subprocess.run(
|
|
512
|
-
[
|
|
618
|
+
[
|
|
619
|
+
pandoc_cmd,
|
|
620
|
+
str(output_path),
|
|
621
|
+
f"--pdf-engine={pdf_engine}",
|
|
622
|
+
"-o",
|
|
623
|
+
str(pdf_path),
|
|
624
|
+
],
|
|
513
625
|
capture_output=True,
|
|
514
626
|
text=True,
|
|
515
|
-
timeout=60
|
|
627
|
+
timeout=60,
|
|
516
628
|
)
|
|
517
629
|
if result.returncode == 0 and verbose:
|
|
518
630
|
logger.info(f"Generated PDF report: {pdf_path}")
|
|
@@ -525,8 +637,10 @@ def generate_org_report(
|
|
|
525
637
|
if verbose:
|
|
526
638
|
logger.warning(f"PDF conversion failed: {e}")
|
|
527
639
|
elif verbose:
|
|
528
|
-
logger.info(
|
|
529
|
-
|
|
640
|
+
logger.info(
|
|
641
|
+
"Pandoc not found. Skipping format conversions. Try 'module load Pandoc/3.1.2'"
|
|
642
|
+
)
|
|
643
|
+
|
|
530
644
|
return output_path
|
|
531
645
|
|
|
532
646
|
|
|
@@ -538,7 +652,7 @@ def generate_markdown_report(
|
|
|
538
652
|
) -> Path:
|
|
539
653
|
"""
|
|
540
654
|
Generate comprehensive markdown report.
|
|
541
|
-
|
|
655
|
+
|
|
542
656
|
Parameters
|
|
543
657
|
----------
|
|
544
658
|
results : Dict[str, Any]
|
|
@@ -549,7 +663,7 @@ def generate_markdown_report(
|
|
|
549
663
|
Whether to include plot images
|
|
550
664
|
verbose : bool, default True
|
|
551
665
|
Whether to print progress messages
|
|
552
|
-
|
|
666
|
+
|
|
553
667
|
Returns
|
|
554
668
|
-------
|
|
555
669
|
Path
|
|
@@ -557,105 +671,129 @@ def generate_markdown_report(
|
|
|
557
671
|
"""
|
|
558
672
|
output_path = Path(output_path)
|
|
559
673
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
560
|
-
|
|
561
|
-
with open(output_path,
|
|
674
|
+
|
|
675
|
+
with open(output_path, "w") as f:
|
|
562
676
|
# Header
|
|
563
677
|
f.write("# Classification Report\n\n")
|
|
564
678
|
f.write(f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
|
565
|
-
|
|
679
|
+
|
|
566
680
|
# Experiment Info
|
|
567
|
-
config = results.get(
|
|
681
|
+
config = results.get("config", {})
|
|
568
682
|
f.write("## Experiment Information\n\n")
|
|
569
683
|
f.write(f"- **Experiment Name:** {config.get('name', 'Unknown')}\n")
|
|
570
684
|
f.write(f"- **Number of Folds:** {config.get('n_folds', 'N/A')}\n")
|
|
571
685
|
f.write(f"- **Output Directory:** `{config.get('output_dir', 'N/A')}`\n\n")
|
|
572
|
-
|
|
686
|
+
|
|
573
687
|
# Summary Statistics
|
|
574
|
-
if
|
|
688
|
+
if "summary" in results and results["summary"]:
|
|
575
689
|
f.write("## Summary Statistics\n\n")
|
|
576
690
|
f.write("| Metric | Mean ± Std | Min | Max |\n")
|
|
577
691
|
f.write("|--------|------------|-----|-----|\n")
|
|
578
|
-
|
|
579
|
-
for metric_name, stats in results[
|
|
580
|
-
if isinstance(stats, dict) and
|
|
581
|
-
mean = stats.get(
|
|
582
|
-
std = stats.get(
|
|
583
|
-
min_val = stats.get(
|
|
584
|
-
max_val = stats.get(
|
|
585
|
-
metric_display = metric_name.replace(
|
|
586
|
-
f.write(
|
|
587
|
-
|
|
692
|
+
|
|
693
|
+
for metric_name, stats in results["summary"].items():
|
|
694
|
+
if isinstance(stats, dict) and "mean" in stats:
|
|
695
|
+
mean = stats.get("mean", 0)
|
|
696
|
+
std = stats.get("std", 0)
|
|
697
|
+
min_val = stats.get("min", 0)
|
|
698
|
+
max_val = stats.get("max", 0)
|
|
699
|
+
metric_display = metric_name.replace("_", " ").title()
|
|
700
|
+
f.write(
|
|
701
|
+
f"| {metric_display} | {mean:.3f} ± {std:.3f} | "
|
|
702
|
+
f"{min_val:.3f} | {max_val:.3f} |\n"
|
|
703
|
+
)
|
|
588
704
|
f.write("\n")
|
|
589
|
-
|
|
705
|
+
|
|
590
706
|
# CV Summary Results with Plots
|
|
591
|
-
if include_plots and
|
|
707
|
+
if include_plots and "plots" in results:
|
|
592
708
|
f.write("## CV Summary Results\n\n")
|
|
593
|
-
|
|
709
|
+
|
|
594
710
|
# Find cv_summary plots
|
|
595
|
-
cv_summary_plots = {
|
|
596
|
-
|
|
711
|
+
cv_summary_plots = {
|
|
712
|
+
k: v
|
|
713
|
+
for k, v in results["plots"].items()
|
|
714
|
+
if "cv_summary" in k or "cv-summary" in k
|
|
715
|
+
}
|
|
597
716
|
|
|
598
717
|
if cv_summary_plots:
|
|
599
718
|
# Confusion Matrix (support both old and new naming)
|
|
600
|
-
cm_plots = [
|
|
601
|
-
|
|
719
|
+
cm_plots = [
|
|
720
|
+
v
|
|
721
|
+
for k, v in cv_summary_plots.items()
|
|
722
|
+
if ("confusion_matrix" in k or "confusion-matrix" in k)
|
|
723
|
+
]
|
|
602
724
|
if cm_plots:
|
|
603
725
|
f.write("### CV Summary Confusion Matrix\n\n")
|
|
604
726
|
for plot_path in cm_plots:
|
|
605
|
-
rel_path = _make_relative_path(
|
|
606
|
-
|
|
727
|
+
rel_path = _make_relative_path(
|
|
728
|
+
output_path.parent,
|
|
729
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
730
|
+
/ plot_path,
|
|
731
|
+
)
|
|
607
732
|
f.write(f"\n\n")
|
|
608
|
-
|
|
733
|
+
|
|
609
734
|
# ROC Curve (support both old and new naming)
|
|
610
|
-
roc_plots = [
|
|
611
|
-
|
|
735
|
+
roc_plots = [
|
|
736
|
+
v
|
|
737
|
+
for k, v in cv_summary_plots.items()
|
|
738
|
+
if ("roc_curve" in k or "roc-curve" in k)
|
|
739
|
+
]
|
|
612
740
|
if roc_plots:
|
|
613
741
|
f.write("### CV Summary ROC Curve\n\n")
|
|
614
742
|
for plot_path in roc_plots:
|
|
615
|
-
rel_path = _make_relative_path(
|
|
616
|
-
|
|
743
|
+
rel_path = _make_relative_path(
|
|
744
|
+
output_path.parent,
|
|
745
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
746
|
+
/ plot_path,
|
|
747
|
+
)
|
|
617
748
|
f.write(f"\n\n")
|
|
618
|
-
|
|
749
|
+
|
|
619
750
|
# PR Curve (support both old and new naming)
|
|
620
|
-
pr_plots = [
|
|
621
|
-
|
|
751
|
+
pr_plots = [
|
|
752
|
+
v
|
|
753
|
+
for k, v in cv_summary_plots.items()
|
|
754
|
+
if ("pr_curve" in k or "pr-curve" in k)
|
|
755
|
+
]
|
|
622
756
|
if pr_plots:
|
|
623
757
|
f.write("### CV Summary Precision-Recall Curve\n\n")
|
|
624
758
|
for plot_path in pr_plots:
|
|
625
|
-
rel_path = _make_relative_path(
|
|
626
|
-
|
|
759
|
+
rel_path = _make_relative_path(
|
|
760
|
+
output_path.parent,
|
|
761
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
762
|
+
/ plot_path,
|
|
763
|
+
)
|
|
627
764
|
f.write(f"\n\n")
|
|
628
|
-
|
|
765
|
+
|
|
629
766
|
# Per-Fold Results (abbreviated for brevity)
|
|
630
|
-
if
|
|
767
|
+
if "folds" in results and results["folds"]:
|
|
631
768
|
f.write("## Per-Fold Results\n\n")
|
|
632
|
-
|
|
769
|
+
|
|
633
770
|
# Create summary table
|
|
634
771
|
f.write("| Fold | Balanced Accuracy | ROC AUC | PR AUC | MCC |\n")
|
|
635
772
|
f.write("|------|-------------------|---------|--------|-----|\n")
|
|
636
|
-
|
|
637
|
-
for fold_data in results[
|
|
638
|
-
fold_id = fold_data.get(
|
|
639
|
-
|
|
773
|
+
|
|
774
|
+
for fold_data in results["folds"]:
|
|
775
|
+
fold_id = fold_data.get("fold_id", 0)
|
|
776
|
+
|
|
640
777
|
# Extract metrics
|
|
641
|
-
bacc = _extract_metric_value(fold_data.get(
|
|
642
|
-
roc = _extract_metric_value(fold_data.get(
|
|
643
|
-
pr = _extract_metric_value(fold_data.get(
|
|
644
|
-
mcc = _extract_metric_value(fold_data.get(
|
|
645
|
-
|
|
778
|
+
bacc = _extract_metric_value(fold_data.get("balanced_accuracy"))
|
|
779
|
+
roc = _extract_metric_value(fold_data.get("roc_auc"))
|
|
780
|
+
pr = _extract_metric_value(fold_data.get("pr_auc"))
|
|
781
|
+
mcc = _extract_metric_value(fold_data.get("mcc"))
|
|
782
|
+
|
|
646
783
|
f.write(f"| {fold_id:02d} | ")
|
|
647
784
|
f.write(f"{bacc:.3f} | " if bacc is not None else "N/A | ")
|
|
648
785
|
f.write(f"{roc:.3f} | " if roc is not None else "N/A | ")
|
|
649
786
|
f.write(f"{pr:.3f} | " if pr is not None else "N/A | ")
|
|
650
787
|
f.write(f"{mcc:.3f} |\n" if mcc is not None else "N/A |\n")
|
|
651
|
-
|
|
788
|
+
|
|
652
789
|
f.write("\n")
|
|
653
|
-
|
|
790
|
+
|
|
654
791
|
if verbose:
|
|
655
792
|
from scitex.logging import getLogger
|
|
793
|
+
|
|
656
794
|
logger = getLogger(__name__)
|
|
657
795
|
logger.info(f"Generated markdown report: {output_path}")
|
|
658
|
-
|
|
796
|
+
|
|
659
797
|
return output_path
|
|
660
798
|
|
|
661
799
|
|
|
@@ -666,7 +804,7 @@ def generate_latex_report(
|
|
|
666
804
|
) -> Path:
|
|
667
805
|
"""
|
|
668
806
|
Generate LaTeX report for academic papers.
|
|
669
|
-
|
|
807
|
+
|
|
670
808
|
Parameters
|
|
671
809
|
----------
|
|
672
810
|
results : Dict[str, Any]
|
|
@@ -675,7 +813,7 @@ def generate_latex_report(
|
|
|
675
813
|
Output file path
|
|
676
814
|
verbose : bool, default True
|
|
677
815
|
Whether to print progress messages
|
|
678
|
-
|
|
816
|
+
|
|
679
817
|
Returns
|
|
680
818
|
-------
|
|
681
819
|
Path
|
|
@@ -683,8 +821,8 @@ def generate_latex_report(
|
|
|
683
821
|
"""
|
|
684
822
|
output_path = Path(output_path)
|
|
685
823
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
686
|
-
|
|
687
|
-
with open(output_path,
|
|
824
|
+
|
|
825
|
+
with open(output_path, "w") as f:
|
|
688
826
|
# Document setup
|
|
689
827
|
f.write("\\documentclass[11pt]{article}\n")
|
|
690
828
|
f.write("\\usepackage{booktabs}\n")
|
|
@@ -692,24 +830,28 @@ def generate_latex_report(
|
|
|
692
830
|
f.write("\\usepackage{float}\n")
|
|
693
831
|
f.write("\\usepackage{amsmath}\n")
|
|
694
832
|
f.write("\\usepackage{hyperref}\n\n")
|
|
695
|
-
|
|
833
|
+
|
|
696
834
|
f.write("\\title{Classification Report}\n")
|
|
697
835
|
f.write(f"\\date{{\\today}}\n\n")
|
|
698
|
-
|
|
836
|
+
|
|
699
837
|
f.write("\\begin{document}\n")
|
|
700
838
|
f.write("\\maketitle\n\n")
|
|
701
|
-
|
|
839
|
+
|
|
702
840
|
# Experiment Information
|
|
703
|
-
config = results.get(
|
|
841
|
+
config = results.get("config", {})
|
|
704
842
|
f.write("\\section{Experiment Information}\n\n")
|
|
705
843
|
f.write("\\begin{itemize}\n")
|
|
706
|
-
f.write(
|
|
844
|
+
f.write(
|
|
845
|
+
f"\\item \\textbf{{Experiment Name:}} {_latex_escape(config.get('name', 'Unknown'))}\n"
|
|
846
|
+
)
|
|
707
847
|
f.write(f"\\item \\textbf{{Number of Folds:}} {config.get('n_folds', 'N/A')}\n")
|
|
708
|
-
f.write(
|
|
848
|
+
f.write(
|
|
849
|
+
f"\\item \\textbf{{Output Directory:}} \\texttt{{{_latex_escape(str(config.get('output_dir', 'N/A')))}}}\n"
|
|
850
|
+
)
|
|
709
851
|
f.write("\\end{itemize}\n\n")
|
|
710
|
-
|
|
852
|
+
|
|
711
853
|
# Summary Statistics
|
|
712
|
-
if
|
|
854
|
+
if "summary" in results and results["summary"]:
|
|
713
855
|
f.write("\\section{Summary Statistics}\n\n")
|
|
714
856
|
f.write("\\begin{table}[H]\n")
|
|
715
857
|
f.write("\\centering\n")
|
|
@@ -717,105 +859,128 @@ def generate_latex_report(
|
|
|
717
859
|
f.write("\\toprule\n")
|
|
718
860
|
f.write("Metric & Mean $\\pm$ Std & Min & Max \\\\\n")
|
|
719
861
|
f.write("\\midrule\n")
|
|
720
|
-
|
|
721
|
-
for metric_name, stats in results[
|
|
722
|
-
if isinstance(stats, dict) and
|
|
723
|
-
mean = stats.get(
|
|
724
|
-
std = stats.get(
|
|
725
|
-
min_val = stats.get(
|
|
726
|
-
max_val = stats.get(
|
|
727
|
-
metric_display = metric_name.replace(
|
|
728
|
-
f.write(
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
862
|
+
|
|
863
|
+
for metric_name, stats in results["summary"].items():
|
|
864
|
+
if isinstance(stats, dict) and "mean" in stats:
|
|
865
|
+
mean = stats.get("mean", 0)
|
|
866
|
+
std = stats.get("std", 0)
|
|
867
|
+
min_val = stats.get("min", 0)
|
|
868
|
+
max_val = stats.get("max", 0)
|
|
869
|
+
metric_display = metric_name.replace("_", " ").title()
|
|
870
|
+
f.write(
|
|
871
|
+
f"{_latex_escape(metric_display)} & "
|
|
872
|
+
f"${mean:.3f} \\pm {std:.3f}$ & "
|
|
873
|
+
f"{min_val:.3f} & {max_val:.3f} \\\\\n"
|
|
874
|
+
)
|
|
875
|
+
|
|
732
876
|
f.write("\\bottomrule\n")
|
|
733
877
|
f.write("\\end{tabular}\n")
|
|
734
878
|
f.write("\\caption{Cross-validation performance metrics}\n")
|
|
735
879
|
f.write("\\label{tab:cv_metrics}\n")
|
|
736
880
|
f.write("\\end{table}\n\n")
|
|
737
|
-
|
|
881
|
+
|
|
738
882
|
# Plots (if available)
|
|
739
|
-
if
|
|
883
|
+
if "plots" in results:
|
|
740
884
|
f.write("\\section{Visualizations}\n\n")
|
|
741
|
-
|
|
885
|
+
|
|
742
886
|
# CV Summary plots (support both old and new naming)
|
|
743
|
-
cv_summary_plots = {
|
|
744
|
-
|
|
887
|
+
cv_summary_plots = {
|
|
888
|
+
k: v
|
|
889
|
+
for k, v in results["plots"].items()
|
|
890
|
+
if "cv_summary" in k or "cv-summary" in k
|
|
891
|
+
}
|
|
745
892
|
|
|
746
893
|
if cv_summary_plots:
|
|
747
894
|
# Find specific plot types (support both old underscore and new hyphen naming)
|
|
748
895
|
for plot_type, plot_type_alt, title in [
|
|
749
|
-
(
|
|
750
|
-
|
|
751
|
-
|
|
896
|
+
(
|
|
897
|
+
"confusion_matrix",
|
|
898
|
+
"confusion-matrix",
|
|
899
|
+
"CV Summary Confusion Matrix",
|
|
900
|
+
),
|
|
901
|
+
("roc_curve", "roc-curve", "CV Summary ROC Curve"),
|
|
902
|
+
("pr_curve", "pr-curve", "CV Summary Precision-Recall Curve"),
|
|
752
903
|
]:
|
|
753
|
-
type_plots = [
|
|
754
|
-
|
|
904
|
+
type_plots = [
|
|
905
|
+
v
|
|
906
|
+
for k, v in cv_summary_plots.items()
|
|
907
|
+
if (plot_type in k or plot_type_alt in k)
|
|
908
|
+
]
|
|
755
909
|
if type_plots:
|
|
756
910
|
f.write(f"\\subsection{{{title}}}\n\n")
|
|
757
911
|
for plot_path in type_plots:
|
|
758
|
-
rel_path = _make_relative_path(
|
|
759
|
-
|
|
912
|
+
rel_path = _make_relative_path(
|
|
913
|
+
output_path.parent,
|
|
914
|
+
Path(results.get("config", {}).get("output_dir", "."))
|
|
915
|
+
/ plot_path,
|
|
916
|
+
)
|
|
760
917
|
f.write("\\begin{figure}[H]\n")
|
|
761
918
|
f.write("\\centering\n")
|
|
762
|
-
f.write(
|
|
919
|
+
f.write(
|
|
920
|
+
f"\\includegraphics[width=0.8\\textwidth]{{{rel_path}}}\n"
|
|
921
|
+
)
|
|
763
922
|
f.write(f"\\caption{{{title}}}\n")
|
|
764
923
|
f.write(f"\\label{{fig:{plot_type}_cv_summary}}\n")
|
|
765
924
|
f.write("\\end{figure}\n\n")
|
|
766
|
-
|
|
925
|
+
|
|
767
926
|
f.write("\\end{document}\n")
|
|
768
|
-
|
|
927
|
+
|
|
769
928
|
if verbose:
|
|
770
929
|
from scitex.logging import getLogger
|
|
930
|
+
|
|
771
931
|
logger = getLogger(__name__)
|
|
772
932
|
logger.info(f"Generated LaTeX report: {output_path}")
|
|
773
|
-
|
|
933
|
+
|
|
774
934
|
return output_path
|
|
775
935
|
|
|
776
936
|
|
|
777
937
|
def create_summary_statistics(results: Dict[str, Any]) -> Dict[str, Any]:
|
|
778
938
|
"""
|
|
779
939
|
Create comprehensive summary statistics from results.
|
|
780
|
-
|
|
940
|
+
|
|
781
941
|
Parameters
|
|
782
942
|
----------
|
|
783
943
|
results : Dict[str, Any]
|
|
784
944
|
Classification results
|
|
785
|
-
|
|
945
|
+
|
|
786
946
|
Returns
|
|
787
947
|
-------
|
|
788
948
|
Dict[str, Any]
|
|
789
949
|
Summary statistics
|
|
790
950
|
"""
|
|
791
951
|
summary = {}
|
|
792
|
-
|
|
793
|
-
if
|
|
952
|
+
|
|
953
|
+
if "folds" in results:
|
|
794
954
|
# Aggregate metrics across folds
|
|
795
955
|
metrics_to_aggregate = [
|
|
796
|
-
|
|
797
|
-
|
|
956
|
+
"balanced_accuracy",
|
|
957
|
+
"roc_auc",
|
|
958
|
+
"pr_auc",
|
|
959
|
+
"mcc",
|
|
960
|
+
"precision",
|
|
961
|
+
"recall",
|
|
962
|
+
"f1_score",
|
|
798
963
|
]
|
|
799
|
-
|
|
964
|
+
|
|
800
965
|
for metric_name in metrics_to_aggregate:
|
|
801
966
|
values = []
|
|
802
|
-
for fold_data in results[
|
|
967
|
+
for fold_data in results["folds"]:
|
|
803
968
|
if metric_name in fold_data:
|
|
804
969
|
value = _extract_metric_value(fold_data[metric_name])
|
|
805
970
|
if value is not None:
|
|
806
971
|
values.append(value)
|
|
807
|
-
|
|
972
|
+
|
|
808
973
|
if values:
|
|
809
974
|
values = np.array(values)
|
|
810
975
|
summary[metric_name] = {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
976
|
+
"mean": float(np.mean(values)),
|
|
977
|
+
"std": float(np.std(values)),
|
|
978
|
+
"min": float(np.min(values)),
|
|
979
|
+
"max": float(np.max(values)),
|
|
980
|
+
"median": float(np.median(values)),
|
|
981
|
+
"values": values.tolist(),
|
|
817
982
|
}
|
|
818
|
-
|
|
983
|
+
|
|
819
984
|
return summary
|
|
820
985
|
|
|
821
986
|
|
|
@@ -826,7 +991,7 @@ def export_for_paper(
|
|
|
826
991
|
) -> Dict[str, Path]:
|
|
827
992
|
"""
|
|
828
993
|
Export results in formats suitable for academic papers.
|
|
829
|
-
|
|
994
|
+
|
|
830
995
|
Parameters
|
|
831
996
|
----------
|
|
832
997
|
results : Dict[str, Any]
|
|
@@ -835,7 +1000,7 @@ def export_for_paper(
|
|
|
835
1000
|
Output directory for exports
|
|
836
1001
|
verbose : bool, default True
|
|
837
1002
|
Whether to print progress messages
|
|
838
|
-
|
|
1003
|
+
|
|
839
1004
|
Returns
|
|
840
1005
|
-------
|
|
841
1006
|
Dict[str, Path]
|
|
@@ -843,83 +1008,88 @@ def export_for_paper(
|
|
|
843
1008
|
"""
|
|
844
1009
|
output_dir = Path(output_dir)
|
|
845
1010
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
846
|
-
|
|
1011
|
+
|
|
847
1012
|
exported_files = {}
|
|
848
|
-
|
|
1013
|
+
|
|
849
1014
|
# Export summary metrics as LaTeX table
|
|
850
|
-
if
|
|
1015
|
+
if "summary" in results:
|
|
851
1016
|
latex_table_path = output_dir / "metrics_table.tex"
|
|
852
|
-
_export_metrics_table_latex(results[
|
|
853
|
-
exported_files[
|
|
854
|
-
|
|
1017
|
+
_export_metrics_table_latex(results["summary"], latex_table_path)
|
|
1018
|
+
exported_files["metrics_table"] = latex_table_path
|
|
1019
|
+
|
|
855
1020
|
# Also export as CSV for easier processing
|
|
856
1021
|
csv_table_path = output_dir / "summary_table.csv"
|
|
857
|
-
_export_summary_table_csv(results[
|
|
858
|
-
exported_files[
|
|
859
|
-
|
|
1022
|
+
_export_summary_table_csv(results["summary"], csv_table_path)
|
|
1023
|
+
exported_files["summary_table"] = csv_table_path
|
|
1024
|
+
|
|
860
1025
|
# Export raw results as JSON
|
|
861
1026
|
import json
|
|
1027
|
+
|
|
862
1028
|
raw_results_path = output_dir / "raw_results.json"
|
|
863
|
-
with open(raw_results_path,
|
|
1029
|
+
with open(raw_results_path, "w") as f:
|
|
864
1030
|
# Create serializable version of results
|
|
865
1031
|
serializable_results = _make_serializable(results)
|
|
866
1032
|
json.dump(serializable_results, f, indent=2)
|
|
867
|
-
exported_files[
|
|
868
|
-
|
|
1033
|
+
exported_files["raw_results"] = raw_results_path
|
|
1034
|
+
|
|
869
1035
|
# Export confusion matrix as CSV
|
|
870
|
-
if
|
|
1036
|
+
if "overall_confusion_matrix" in results:
|
|
871
1037
|
cm_path = output_dir / "confusion_matrix.csv"
|
|
872
|
-
cm_data = np.array(results[
|
|
1038
|
+
cm_data = np.array(results["overall_confusion_matrix"])
|
|
873
1039
|
cm_df = pd.DataFrame(cm_data)
|
|
874
1040
|
cm_df.to_csv(cm_path, index=True)
|
|
875
|
-
exported_files[
|
|
876
|
-
|
|
1041
|
+
exported_files["confusion_matrix"] = cm_path
|
|
1042
|
+
|
|
877
1043
|
# Copy key plots
|
|
878
|
-
config = results.get(
|
|
879
|
-
base_dir = Path(results.get(
|
|
880
|
-
|
|
881
|
-
if
|
|
1044
|
+
config = results.get("config", {})
|
|
1045
|
+
base_dir = Path(results.get("config", {}).get("output_dir", "."))
|
|
1046
|
+
|
|
1047
|
+
if "plots" in results:
|
|
882
1048
|
plots_dir = output_dir / "figures"
|
|
883
1049
|
plots_dir.mkdir(exist_ok=True)
|
|
884
|
-
|
|
1050
|
+
|
|
885
1051
|
# Copy cv_summary plots with standardized names
|
|
886
|
-
cv_summary_plots = {
|
|
887
|
-
|
|
888
|
-
|
|
1052
|
+
cv_summary_plots = {
|
|
1053
|
+
k: v for k, v in results["plots"].items() if "cv_summary" in k
|
|
1054
|
+
}
|
|
1055
|
+
|
|
889
1056
|
for plot_key, plot_path in cv_summary_plots.items():
|
|
890
1057
|
src_path = base_dir / plot_path
|
|
891
1058
|
if src_path.exists():
|
|
892
1059
|
# Standardize filename
|
|
893
|
-
if
|
|
1060
|
+
if "confusion_matrix" in plot_key:
|
|
894
1061
|
dest_name = "confusion_matrix_cv_summary.jpg"
|
|
895
|
-
elif
|
|
1062
|
+
elif "roc_curve" in plot_key:
|
|
896
1063
|
dest_name = "roc_curve_cv_summary.jpg"
|
|
897
|
-
elif
|
|
1064
|
+
elif "pr_curve" in plot_key:
|
|
898
1065
|
dest_name = "pr_curve_cv_summary.jpg"
|
|
899
1066
|
else:
|
|
900
1067
|
dest_name = src_path.name
|
|
901
|
-
|
|
1068
|
+
|
|
902
1069
|
dest_path = plots_dir / dest_name
|
|
903
1070
|
import shutil
|
|
1071
|
+
|
|
904
1072
|
shutil.copy2(src_path, dest_path)
|
|
905
1073
|
exported_files[f"figure_{dest_name.split('.')[0]}"] = dest_path
|
|
906
|
-
|
|
1074
|
+
|
|
907
1075
|
if verbose:
|
|
908
1076
|
from scitex.logging import getLogger
|
|
1077
|
+
|
|
909
1078
|
logger = getLogger(__name__)
|
|
910
1079
|
logger.info(f"Exported {len(exported_files)} files for paper to {output_dir}")
|
|
911
|
-
|
|
1080
|
+
|
|
912
1081
|
return exported_files
|
|
913
1082
|
|
|
914
1083
|
|
|
915
1084
|
# Helper functions
|
|
916
1085
|
|
|
1086
|
+
|
|
917
1087
|
def _extract_metric_value(metric_data: Any) -> Optional[float]:
|
|
918
1088
|
"""Extract numeric value from metric data."""
|
|
919
1089
|
if metric_data is None:
|
|
920
1090
|
return None
|
|
921
|
-
if isinstance(metric_data, dict) and
|
|
922
|
-
return float(metric_data[
|
|
1091
|
+
if isinstance(metric_data, dict) and "value" in metric_data:
|
|
1092
|
+
return float(metric_data["value"])
|
|
923
1093
|
if isinstance(metric_data, (int, float, np.number)):
|
|
924
1094
|
return float(metric_data)
|
|
925
1095
|
return None
|
|
@@ -938,17 +1108,17 @@ def _make_relative_path(from_dir: Path, to_path: Path) -> str:
|
|
|
938
1108
|
common_parts = 0
|
|
939
1109
|
from_parts = from_dir.parts
|
|
940
1110
|
to_parts = Path(to_path).parts
|
|
941
|
-
|
|
1111
|
+
|
|
942
1112
|
for fp, tp in zip(from_parts, to_parts):
|
|
943
1113
|
if fp == tp:
|
|
944
1114
|
common_parts += 1
|
|
945
1115
|
else:
|
|
946
1116
|
break
|
|
947
|
-
|
|
1117
|
+
|
|
948
1118
|
# Build relative path
|
|
949
1119
|
ups = len(from_parts) - common_parts
|
|
950
|
-
rel_parts = [
|
|
951
|
-
return
|
|
1120
|
+
rel_parts = [".."] * ups + list(to_parts[common_parts:])
|
|
1121
|
+
return "/".join(rel_parts)
|
|
952
1122
|
except:
|
|
953
1123
|
# Fallback to absolute path
|
|
954
1124
|
return str(to_path)
|
|
@@ -958,32 +1128,29 @@ def _latex_escape(text: str) -> str:
|
|
|
958
1128
|
"""Escape special LaTeX characters."""
|
|
959
1129
|
if not isinstance(text, str):
|
|
960
1130
|
text = str(text)
|
|
961
|
-
|
|
1131
|
+
|
|
962
1132
|
replacements = {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1133
|
+
"\\": "\\textbackslash{}",
|
|
1134
|
+
"{": "\\{",
|
|
1135
|
+
"}": "\\}",
|
|
1136
|
+
"$": "\\$",
|
|
1137
|
+
"&": "\\&",
|
|
1138
|
+
"%": "\\%",
|
|
1139
|
+
"#": "\\#",
|
|
1140
|
+
"_": "\\_",
|
|
1141
|
+
"~": "\\textasciitilde{}",
|
|
1142
|
+
"^": "\\textasciicircum{}",
|
|
973
1143
|
}
|
|
974
|
-
|
|
1144
|
+
|
|
975
1145
|
for old, new in replacements.items():
|
|
976
1146
|
text = text.replace(old, new)
|
|
977
|
-
|
|
1147
|
+
|
|
978
1148
|
return text
|
|
979
1149
|
|
|
980
1150
|
|
|
981
|
-
def _export_metrics_table_latex(
|
|
982
|
-
summary: Dict[str, Any],
|
|
983
|
-
output_path: Path
|
|
984
|
-
) -> None:
|
|
1151
|
+
def _export_metrics_table_latex(summary: Dict[str, Any], output_path: Path) -> None:
|
|
985
1152
|
"""Export summary metrics as a LaTeX table."""
|
|
986
|
-
with open(output_path,
|
|
1153
|
+
with open(output_path, "w") as f:
|
|
987
1154
|
f.write("% Classification metrics summary table\n")
|
|
988
1155
|
f.write("\\begin{table}[htbp]\n")
|
|
989
1156
|
f.write("\\centering\n")
|
|
@@ -991,18 +1158,20 @@ def _export_metrics_table_latex(
|
|
|
991
1158
|
f.write("\\toprule\n")
|
|
992
1159
|
f.write("Metric & Mean $\\pm$ Std & Min & Max \\\\\n")
|
|
993
1160
|
f.write("\\midrule\n")
|
|
994
|
-
|
|
1161
|
+
|
|
995
1162
|
for metric_name, stats in summary.items():
|
|
996
|
-
if isinstance(stats, dict) and
|
|
997
|
-
mean = stats.get(
|
|
998
|
-
std = stats.get(
|
|
999
|
-
min_val = stats.get(
|
|
1000
|
-
max_val = stats.get(
|
|
1001
|
-
metric_display = metric_name.replace(
|
|
1002
|
-
f.write(
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1163
|
+
if isinstance(stats, dict) and "mean" in stats:
|
|
1164
|
+
mean = stats.get("mean", 0)
|
|
1165
|
+
std = stats.get("std", 0)
|
|
1166
|
+
min_val = stats.get("min", 0)
|
|
1167
|
+
max_val = stats.get("max", 0)
|
|
1168
|
+
metric_display = metric_name.replace("_", " ").title()
|
|
1169
|
+
f.write(
|
|
1170
|
+
f"{_latex_escape(metric_display)} & "
|
|
1171
|
+
f"${mean:.3f} \\pm {std:.3f}$ & "
|
|
1172
|
+
f"{min_val:.3f} & {max_val:.3f} \\\\\n"
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1006
1175
|
f.write("\\bottomrule\n")
|
|
1007
1176
|
f.write("\\end{tabular}\n")
|
|
1008
1177
|
f.write("\\caption{Classification performance metrics}\n")
|
|
@@ -1010,24 +1179,21 @@ def _export_metrics_table_latex(
|
|
|
1010
1179
|
f.write("\\end{table}\n")
|
|
1011
1180
|
|
|
1012
1181
|
|
|
1013
|
-
def _export_summary_table_csv(
|
|
1014
|
-
summary: Dict[str, Any],
|
|
1015
|
-
output_path: Path
|
|
1016
|
-
) -> None:
|
|
1182
|
+
def _export_summary_table_csv(summary: Dict[str, Any], output_path: Path) -> None:
|
|
1017
1183
|
"""Export summary metrics as CSV table."""
|
|
1018
1184
|
# Create DataFrame from summary
|
|
1019
1185
|
data = []
|
|
1020
1186
|
for metric_name, stats in summary.items():
|
|
1021
|
-
if isinstance(stats, dict) and
|
|
1187
|
+
if isinstance(stats, dict) and "mean" in stats:
|
|
1022
1188
|
row = {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1189
|
+
"Metric": metric_name.replace("_", " ").title(),
|
|
1190
|
+
"Mean": stats.get("mean", 0),
|
|
1191
|
+
"Std": stats.get("std", 0),
|
|
1192
|
+
"Min": stats.get("min", 0),
|
|
1193
|
+
"Max": stats.get("max", 0),
|
|
1028
1194
|
}
|
|
1029
1195
|
data.append(row)
|
|
1030
|
-
|
|
1196
|
+
|
|
1031
1197
|
if data:
|
|
1032
1198
|
df = pd.DataFrame(data)
|
|
1033
1199
|
df.to_csv(output_path, index=False)
|
|
@@ -1041,7 +1207,7 @@ def _make_serializable(obj: Any) -> Any:
|
|
|
1041
1207
|
return float(obj)
|
|
1042
1208
|
elif isinstance(obj, pd.DataFrame):
|
|
1043
1209
|
# Convert DataFrame to dict with orient='list' for JSON serialization
|
|
1044
|
-
return obj.to_dict(orient=
|
|
1210
|
+
return obj.to_dict(orient="list")
|
|
1045
1211
|
elif isinstance(obj, pd.Series):
|
|
1046
1212
|
return obj.tolist()
|
|
1047
1213
|
elif isinstance(obj, dict):
|
|
@@ -1053,4 +1219,5 @@ def _make_serializable(obj: Any) -> Any:
|
|
|
1053
1219
|
else:
|
|
1054
1220
|
return obj
|
|
1055
1221
|
|
|
1056
|
-
|
|
1222
|
+
|
|
1223
|
+
# EOF
|