scitex 2.0.0__py2.py3-none-any.whl → 2.1.0__py2.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 +53 -15
- scitex/__main__.py +72 -26
- scitex/__version__.py +1 -1
- scitex/_sh.py +145 -23
- scitex/ai/__init__.py +30 -16
- scitex/ai/_gen_ai/_Anthropic.py +5 -7
- scitex/ai/_gen_ai/_BaseGenAI.py +2 -2
- scitex/ai/_gen_ai/_DeepSeek.py +10 -2
- scitex/ai/_gen_ai/_Google.py +2 -2
- scitex/ai/_gen_ai/_Llama.py +2 -2
- scitex/ai/_gen_ai/_OpenAI.py +2 -2
- scitex/ai/_gen_ai/_PARAMS.py +51 -65
- scitex/ai/_gen_ai/_Perplexity.py +2 -2
- scitex/ai/_gen_ai/__init__.py +25 -14
- scitex/ai/_gen_ai/_format_output_func.py +4 -4
- scitex/ai/classification/{classifier_server.py → Classifier.py} +5 -5
- scitex/ai/classification/CrossValidationExperiment.py +374 -0
- scitex/ai/classification/__init__.py +43 -4
- scitex/ai/classification/reporters/_BaseClassificationReporter.py +281 -0
- scitex/ai/classification/reporters/_ClassificationReporter.py +773 -0
- scitex/ai/classification/reporters/_MultiClassificationReporter.py +406 -0
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +1834 -0
- scitex/ai/classification/reporters/__init__.py +11 -0
- scitex/ai/classification/reporters/reporter_utils/_Plotter.py +1028 -0
- scitex/ai/classification/reporters/reporter_utils/__init__.py +80 -0
- scitex/ai/classification/reporters/reporter_utils/aggregation.py +457 -0
- scitex/ai/classification/reporters/reporter_utils/data_models.py +313 -0
- scitex/ai/classification/reporters/reporter_utils/reporting.py +1056 -0
- scitex/ai/classification/reporters/reporter_utils/storage.py +221 -0
- scitex/ai/classification/reporters/reporter_utils/validation.py +395 -0
- scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
- scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
- scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +139 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
- scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +84 -0
- scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
- scitex/ai/classification/timeseries/__init__.py +39 -0
- scitex/ai/classification/timeseries/_normalize_timestamp.py +436 -0
- scitex/ai/clustering/_umap.py +2 -2
- scitex/ai/feature_extraction/vit.py +1 -0
- scitex/ai/feature_selection/__init__.py +30 -0
- scitex/ai/feature_selection/feature_selection.py +364 -0
- scitex/ai/loss/multi_task_loss.py +1 -1
- scitex/ai/metrics/__init__.py +51 -4
- scitex/ai/metrics/_calc_bacc.py +61 -0
- scitex/ai/metrics/_calc_bacc_from_conf_mat.py +38 -0
- scitex/ai/metrics/_calc_clf_report.py +78 -0
- scitex/ai/metrics/_calc_conf_mat.py +93 -0
- scitex/ai/metrics/_calc_feature_importance.py +183 -0
- scitex/ai/metrics/_calc_mcc.py +61 -0
- scitex/ai/metrics/_calc_pre_rec_auc.py +116 -0
- scitex/ai/metrics/_calc_roc_auc.py +110 -0
- scitex/ai/metrics/_calc_seizure_prediction_metrics.py +490 -0
- scitex/ai/metrics/{silhoute_score_block.py → _calc_silhouette_score.py} +15 -8
- scitex/ai/metrics/_normalize_labels.py +83 -0
- scitex/ai/plt/__init__.py +47 -8
- scitex/ai/plt/{_conf_mat.py → _plot_conf_mat.py} +158 -87
- scitex/ai/plt/_plot_feature_importance.py +323 -0
- scitex/ai/plt/_plot_learning_curve.py +345 -0
- scitex/ai/plt/_plot_optuna_study.py +225 -0
- scitex/ai/plt/_plot_pre_rec_curve.py +290 -0
- scitex/ai/plt/_plot_roc_curve.py +255 -0
- scitex/ai/training/{learning_curve_logger.py → _LearningCurveLogger.py} +197 -213
- scitex/ai/training/__init__.py +2 -2
- scitex/ai/utils/grid_search.py +3 -3
- scitex/benchmark/__init__.py +52 -0
- scitex/benchmark/benchmark.py +400 -0
- scitex/benchmark/monitor.py +370 -0
- scitex/benchmark/profiler.py +297 -0
- scitex/browser/__init__.py +48 -0
- scitex/browser/automation/CookieHandler.py +216 -0
- scitex/browser/automation/__init__.py +7 -0
- scitex/browser/collaboration/__init__.py +55 -0
- scitex/browser/collaboration/auth_helpers.py +94 -0
- scitex/browser/collaboration/collaborative_agent.py +136 -0
- scitex/browser/collaboration/credential_manager.py +188 -0
- scitex/browser/collaboration/interactive_panel.py +400 -0
- scitex/browser/collaboration/persistent_browser.py +170 -0
- scitex/browser/collaboration/shared_session.py +383 -0
- scitex/browser/collaboration/standard_interactions.py +246 -0
- scitex/browser/collaboration/visual_feedback.py +181 -0
- scitex/browser/core/BrowserMixin.py +326 -0
- scitex/browser/core/ChromeProfileManager.py +446 -0
- scitex/browser/core/__init__.py +9 -0
- scitex/browser/debugging/__init__.py +18 -0
- scitex/browser/debugging/_browser_logger.py +657 -0
- scitex/browser/debugging/_highlight_element.py +143 -0
- scitex/browser/debugging/_show_grid.py +154 -0
- scitex/browser/interaction/__init__.py +24 -0
- scitex/browser/interaction/click_center.py +149 -0
- scitex/browser/interaction/click_with_fallbacks.py +206 -0
- scitex/browser/interaction/close_popups.py +498 -0
- scitex/browser/interaction/fill_with_fallbacks.py +209 -0
- scitex/browser/pdf/__init__.py +14 -0
- scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +200 -0
- scitex/browser/pdf/detect_chrome_pdf_viewer.py +198 -0
- scitex/browser/remote/CaptchaHandler.py +434 -0
- scitex/browser/remote/ZenRowsAPIClient.py +347 -0
- scitex/browser/remote/ZenRowsBrowserManager.py +570 -0
- scitex/browser/remote/__init__.py +11 -0
- scitex/browser/stealth/HumanBehavior.py +344 -0
- scitex/browser/stealth/StealthManager.py +1008 -0
- scitex/browser/stealth/__init__.py +9 -0
- scitex/browser/template.py +122 -0
- scitex/capture/__init__.py +110 -0
- scitex/capture/__main__.py +25 -0
- scitex/capture/capture.py +848 -0
- scitex/capture/cli.py +233 -0
- scitex/capture/gif.py +344 -0
- scitex/capture/mcp_server.py +961 -0
- scitex/capture/session.py +70 -0
- scitex/capture/utils.py +705 -0
- scitex/cli/__init__.py +17 -0
- scitex/cli/cloud.py +447 -0
- scitex/cli/main.py +42 -0
- scitex/cli/scholar.py +280 -0
- scitex/context/_suppress_output.py +5 -3
- scitex/db/__init__.py +30 -3
- scitex/db/__main__.py +75 -0
- scitex/db/_check_health.py +381 -0
- scitex/db/_delete_duplicates.py +25 -386
- scitex/db/_inspect.py +335 -114
- scitex/db/_inspect_optimized.py +301 -0
- scitex/db/{_PostgreSQL.py → _postgresql/_PostgreSQL.py} +3 -3
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BackupMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BatchMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BlobMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ConnectionMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_MaintenanceMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_QueryMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_SchemaMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TransactionMixin.py +1 -1
- scitex/db/_postgresql/__init__.py +6 -0
- scitex/db/_sqlite3/_SQLite3.py +210 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +581 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +517 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_BatchMixin.py +1 -1
- scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +281 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +548 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +583 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ConnectionMixin.py +29 -13
- scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +583 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ImportExportMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_IndexMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_MaintenanceMixin.py +2 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_QueryMixin.py +37 -10
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_RowMixin.py +46 -6
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TableMixin.py +56 -10
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TransactionMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/__init__.py +14 -2
- scitex/db/_sqlite3/__init__.py +7 -0
- scitex/db/_sqlite3/_delete_duplicates.py +274 -0
- scitex/decorators/__init__.py +2 -0
- scitex/decorators/_cache_disk.py +13 -5
- scitex/decorators/_cache_disk_async.py +49 -0
- scitex/decorators/_deprecated.py +175 -10
- scitex/decorators/_timeout.py +1 -1
- scitex/dev/_analyze_code_flow.py +2 -2
- scitex/dict/_DotDict.py +73 -15
- scitex/dict/_DotDict_v01-not-handling-recursive-instantiations.py +442 -0
- scitex/dict/_DotDict_v02-not-serializing-Path-object.py +446 -0
- scitex/dict/__init__.py +2 -0
- scitex/dict/_flatten.py +27 -0
- scitex/dsp/_crop.py +2 -2
- scitex/dsp/_demo_sig.py +2 -2
- scitex/dsp/_detect_ripples.py +2 -2
- scitex/dsp/_hilbert.py +2 -2
- scitex/dsp/_listen.py +6 -6
- scitex/dsp/_modulation_index.py +2 -2
- scitex/dsp/_pac.py +1 -1
- scitex/dsp/_psd.py +2 -2
- scitex/dsp/_resample.py +2 -1
- scitex/dsp/_time.py +3 -2
- scitex/dsp/_wavelet.py +3 -2
- scitex/dsp/add_noise.py +2 -2
- scitex/dsp/example.py +1 -0
- scitex/dsp/filt.py +10 -9
- scitex/dsp/template.py +3 -2
- scitex/dsp/utils/_differential_bandpass_filters.py +1 -1
- scitex/dsp/utils/pac.py +2 -2
- scitex/dt/_normalize_timestamp.py +432 -0
- scitex/errors.py +572 -0
- scitex/gen/_DimHandler.py +2 -2
- scitex/gen/__init__.py +37 -7
- scitex/gen/_deprecated_close.py +80 -0
- scitex/gen/_deprecated_start.py +26 -0
- scitex/gen/_detect_environment.py +152 -0
- scitex/gen/_detect_notebook_path.py +169 -0
- scitex/gen/_embed.py +6 -2
- scitex/gen/_get_notebook_path.py +257 -0
- scitex/gen/_less.py +1 -1
- scitex/gen/_list_packages.py +2 -2
- scitex/gen/_norm.py +44 -9
- scitex/gen/_norm_cache.py +269 -0
- scitex/gen/_src.py +3 -5
- scitex/gen/_title_case.py +3 -3
- scitex/io/__init__.py +28 -6
- scitex/io/_glob.py +13 -7
- scitex/io/_load.py +108 -21
- scitex/io/_load_cache.py +303 -0
- scitex/io/_load_configs.py +40 -15
- scitex/io/{_H5Explorer.py → _load_modules/_H5Explorer.py} +80 -17
- scitex/io/_load_modules/_ZarrExplorer.py +114 -0
- scitex/io/_load_modules/_bibtex.py +207 -0
- scitex/io/_load_modules/_hdf5.py +53 -178
- scitex/io/_load_modules/_json.py +5 -3
- scitex/io/_load_modules/_pdf.py +871 -16
- scitex/io/_load_modules/_sqlite3.py +15 -0
- scitex/io/_load_modules/_txt.py +41 -12
- scitex/io/_load_modules/_yaml.py +4 -3
- scitex/io/_load_modules/_zarr.py +126 -0
- scitex/io/_save.py +429 -171
- scitex/io/_save_modules/__init__.py +6 -0
- scitex/io/_save_modules/_bibtex.py +194 -0
- scitex/io/_save_modules/_csv.py +8 -4
- scitex/io/_save_modules/_excel.py +174 -15
- scitex/io/_save_modules/_hdf5.py +251 -226
- scitex/io/_save_modules/_image.py +1 -3
- scitex/io/_save_modules/_json.py +49 -4
- scitex/io/_save_modules/_listed_dfs_as_csv.py +1 -3
- scitex/io/_save_modules/_listed_scalars_as_csv.py +1 -3
- scitex/io/_save_modules/_tex.py +277 -0
- scitex/io/_save_modules/_yaml.py +42 -3
- scitex/io/_save_modules/_zarr.py +160 -0
- scitex/io/utils/__init__.py +20 -0
- scitex/io/utils/h5_to_zarr.py +616 -0
- scitex/linalg/_geometric_median.py +6 -2
- scitex/{gen/_tee.py → logging/_Tee.py} +43 -84
- scitex/logging/__init__.py +122 -0
- scitex/logging/_config.py +158 -0
- scitex/logging/_context.py +103 -0
- scitex/logging/_formatters.py +128 -0
- scitex/logging/_handlers.py +64 -0
- scitex/logging/_levels.py +35 -0
- scitex/logging/_logger.py +163 -0
- scitex/logging/_print_capture.py +95 -0
- scitex/ml/__init__.py +69 -0
- scitex/{ai/genai/anthropic.py → ml/_gen_ai/_Anthropic.py} +13 -19
- scitex/{ai/genai/base_genai.py → ml/_gen_ai/_BaseGenAI.py} +5 -5
- scitex/{ai/genai/deepseek.py → ml/_gen_ai/_DeepSeek.py} +11 -16
- scitex/{ai/genai/google.py → ml/_gen_ai/_Google.py} +7 -15
- scitex/{ai/genai/groq.py → ml/_gen_ai/_Groq.py} +1 -8
- scitex/{ai/genai/llama.py → ml/_gen_ai/_Llama.py} +3 -16
- scitex/{ai/genai/openai.py → ml/_gen_ai/_OpenAI.py} +3 -3
- scitex/{ai/genai/params.py → ml/_gen_ai/_PARAMS.py} +51 -65
- scitex/{ai/genai/perplexity.py → ml/_gen_ai/_Perplexity.py} +3 -14
- scitex/ml/_gen_ai/__init__.py +43 -0
- scitex/{ai/genai/calc_cost.py → ml/_gen_ai/_calc_cost.py} +1 -1
- scitex/{ai/genai/format_output_func.py → ml/_gen_ai/_format_output_func.py} +4 -4
- scitex/{ai/genai/genai_factory.py → ml/_gen_ai/_genai_factory.py} +8 -8
- scitex/ml/activation/__init__.py +8 -0
- scitex/ml/activation/_define.py +11 -0
- scitex/{ai/classifier_server.py → ml/classification/Classifier.py} +5 -5
- scitex/ml/classification/CrossValidationExperiment.py +374 -0
- scitex/ml/classification/__init__.py +46 -0
- scitex/ml/classification/reporters/_BaseClassificationReporter.py +281 -0
- scitex/ml/classification/reporters/_ClassificationReporter.py +773 -0
- scitex/ml/classification/reporters/_MultiClassificationReporter.py +406 -0
- scitex/ml/classification/reporters/_SingleClassificationReporter.py +1834 -0
- scitex/ml/classification/reporters/__init__.py +11 -0
- scitex/ml/classification/reporters/reporter_utils/_Plotter.py +1028 -0
- scitex/ml/classification/reporters/reporter_utils/__init__.py +80 -0
- scitex/ml/classification/reporters/reporter_utils/aggregation.py +457 -0
- scitex/ml/classification/reporters/reporter_utils/data_models.py +313 -0
- scitex/ml/classification/reporters/reporter_utils/reporting.py +1056 -0
- scitex/ml/classification/reporters/reporter_utils/storage.py +221 -0
- scitex/ml/classification/reporters/reporter_utils/validation.py +395 -0
- scitex/ml/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
- scitex/ml/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
- scitex/ml/classification/timeseries/_TimeSeriesMetadata.py +139 -0
- scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
- scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
- scitex/ml/classification/timeseries/_TimeSeriesStrategy.py +84 -0
- scitex/ml/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
- scitex/ml/classification/timeseries/__init__.py +39 -0
- scitex/ml/classification/timeseries/_normalize_timestamp.py +436 -0
- scitex/ml/clustering/__init__.py +11 -0
- scitex/ml/clustering/_pca.py +115 -0
- scitex/ml/clustering/_umap.py +376 -0
- scitex/ml/feature_extraction/__init__.py +56 -0
- scitex/ml/feature_extraction/vit.py +149 -0
- scitex/ml/feature_selection/__init__.py +30 -0
- scitex/ml/feature_selection/feature_selection.py +364 -0
- scitex/ml/loss/_L1L2Losses.py +34 -0
- scitex/ml/loss/__init__.py +12 -0
- scitex/ml/loss/multi_task_loss.py +47 -0
- scitex/ml/metrics/__init__.py +56 -0
- scitex/ml/metrics/_calc_bacc.py +61 -0
- scitex/ml/metrics/_calc_bacc_from_conf_mat.py +38 -0
- scitex/ml/metrics/_calc_clf_report.py +78 -0
- scitex/ml/metrics/_calc_conf_mat.py +93 -0
- scitex/ml/metrics/_calc_feature_importance.py +183 -0
- scitex/ml/metrics/_calc_mcc.py +61 -0
- scitex/ml/metrics/_calc_pre_rec_auc.py +116 -0
- scitex/ml/metrics/_calc_roc_auc.py +110 -0
- scitex/ml/metrics/_calc_seizure_prediction_metrics.py +490 -0
- scitex/ml/metrics/_calc_silhouette_score.py +503 -0
- scitex/ml/metrics/_normalize_labels.py +83 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/__init__.py +0 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/__init__.py +3 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +207 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +238 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +215 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/rangerqh.py +184 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/setup.py +24 -0
- scitex/ml/optim/__init__.py +13 -0
- scitex/ml/optim/_get_set.py +31 -0
- scitex/ml/optim/_optimizers.py +71 -0
- scitex/ml/plt/__init__.py +60 -0
- scitex/ml/plt/_plot_conf_mat.py +663 -0
- scitex/ml/plt/_plot_feature_importance.py +323 -0
- scitex/ml/plt/_plot_learning_curve.py +345 -0
- scitex/ml/plt/_plot_optuna_study.py +225 -0
- scitex/ml/plt/_plot_pre_rec_curve.py +290 -0
- scitex/ml/plt/_plot_roc_curve.py +255 -0
- scitex/ml/sk/__init__.py +11 -0
- scitex/ml/sk/_clf.py +58 -0
- scitex/ml/sk/_to_sktime.py +100 -0
- scitex/ml/sklearn/__init__.py +26 -0
- scitex/ml/sklearn/clf.py +58 -0
- scitex/ml/sklearn/to_sktime.py +100 -0
- scitex/{ai/training/early_stopping.py → ml/training/_EarlyStopping.py} +1 -2
- scitex/{ai → ml/training}/_LearningCurveLogger.py +198 -242
- scitex/ml/training/__init__.py +7 -0
- scitex/ml/utils/__init__.py +22 -0
- scitex/ml/utils/_check_params.py +50 -0
- scitex/ml/utils/_default_dataset.py +46 -0
- scitex/ml/utils/_format_samples_for_sktime.py +26 -0
- scitex/ml/utils/_label_encoder.py +134 -0
- scitex/ml/utils/_merge_labels.py +22 -0
- scitex/ml/utils/_sliding_window_data_augmentation.py +11 -0
- scitex/ml/utils/_under_sample.py +51 -0
- scitex/ml/utils/_verify_n_gpus.py +16 -0
- scitex/ml/utils/grid_search.py +148 -0
- scitex/nn/_BNet.py +15 -9
- scitex/nn/_Filters.py +2 -2
- scitex/nn/_ModulationIndex.py +2 -2
- scitex/nn/_PAC.py +1 -1
- scitex/nn/_Spectrogram.py +12 -3
- scitex/nn/__init__.py +9 -10
- scitex/path/__init__.py +18 -0
- scitex/path/_clean.py +4 -0
- scitex/path/_find.py +9 -4
- scitex/path/_symlink.py +348 -0
- scitex/path/_version.py +4 -3
- scitex/pd/__init__.py +2 -0
- scitex/pd/_get_unique.py +99 -0
- scitex/plt/__init__.py +114 -5
- scitex/plt/_subplots/_AxesWrapper.py +1 -3
- scitex/plt/_subplots/_AxisWrapper.py +7 -3
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +47 -13
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +160 -2
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +26 -4
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +322 -0
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +1 -0
- scitex/plt/_subplots/_FigWrapper.py +62 -6
- scitex/plt/_subplots/_export_as_csv.py +43 -27
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +5 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +81 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +20 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +35 -18
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +15 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_conf_mat.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_ecdf.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_fillv.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_heatmap.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_image.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_joyplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_line.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_ci.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_std.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_median_iqr.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_raster.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_rectangle.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +35 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter_hist.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_shaded_line.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_violin.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +6 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +60 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters.py +56 -59
- scitex/plt/ax/_style/_hide_spines.py +1 -3
- scitex/plt/ax/_style/_rotate_labels.py +180 -76
- scitex/plt/ax/_style/_rotate_labels_v01.py +248 -0
- scitex/plt/ax/_style/_set_meta.py +11 -4
- scitex/plt/ax/_style/_set_supxyt.py +3 -3
- scitex/plt/ax/_style/_set_xyt.py +3 -3
- scitex/plt/ax/_style/_share_axes.py +2 -2
- scitex/plt/color/__init__.py +4 -4
- scitex/plt/color/{_get_colors_from_cmap.py → _get_colors_from_conf_matap.py} +7 -7
- scitex/plt/utils/_configure_mpl.py +99 -86
- scitex/plt/utils/_histogram_utils.py +1 -3
- scitex/plt/utils/_is_valid_axis.py +1 -3
- scitex/plt/utils/_scitex_config.py +1 -0
- scitex/repro/__init__.py +75 -0
- scitex/{reproduce → repro}/_gen_ID.py +1 -1
- scitex/{reproduce → repro}/_gen_timestamp.py +1 -1
- scitex/repro_rng/_RandomStateManager.py +590 -0
- scitex/repro_rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
- scitex/repro_rng/__init__.py +39 -0
- scitex/reproduce/__init__.py +25 -13
- scitex/reproduce/_hash_array.py +22 -0
- scitex/resource/_get_processor_usages.py +4 -4
- scitex/resource/_get_specs.py +2 -2
- scitex/resource/_log_processor_usages.py +2 -2
- scitex/rng/_RandomStateManager.py +590 -0
- scitex/rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
- scitex/rng/__init__.py +39 -0
- scitex/scholar/__init__.py +309 -19
- scitex/scholar/__main__.py +319 -0
- scitex/scholar/auth/ScholarAuthManager.py +308 -0
- scitex/scholar/auth/__init__.py +12 -0
- scitex/scholar/auth/core/AuthenticationGateway.py +473 -0
- scitex/scholar/auth/core/BrowserAuthenticator.py +386 -0
- scitex/scholar/auth/core/StrategyResolver.py +309 -0
- scitex/scholar/auth/core/__init__.py +16 -0
- scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +120 -0
- scitex/scholar/auth/gateway/_OpenURLResolver.py +209 -0
- scitex/scholar/auth/gateway/__init__.py +38 -0
- scitex/scholar/auth/gateway/_resolve_functions.py +101 -0
- scitex/scholar/auth/providers/BaseAuthenticator.py +166 -0
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +484 -0
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +619 -0
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +686 -0
- scitex/scholar/auth/providers/__init__.py +18 -0
- scitex/scholar/auth/session/AuthCacheManager.py +189 -0
- scitex/scholar/auth/session/SessionManager.py +159 -0
- scitex/scholar/auth/session/__init__.py +11 -0
- scitex/scholar/auth/sso/BaseSSOAutomator.py +373 -0
- scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +378 -0
- scitex/scholar/auth/sso/SSOAutomator.py +180 -0
- scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +380 -0
- scitex/scholar/auth/sso/__init__.py +15 -0
- scitex/scholar/browser/ScholarBrowserManager.py +705 -0
- scitex/scholar/browser/__init__.py +38 -0
- scitex/scholar/browser/utils/__init__.py +13 -0
- scitex/scholar/browser/utils/click_and_wait.py +205 -0
- scitex/scholar/browser/utils/close_unwanted_pages.py +140 -0
- scitex/scholar/browser/utils/wait_redirects.py +732 -0
- scitex/scholar/config/PublisherRules.py +132 -0
- scitex/scholar/config/ScholarConfig.py +126 -0
- scitex/scholar/config/__init__.py +17 -0
- scitex/scholar/core/Paper.py +627 -0
- scitex/scholar/core/Papers.py +722 -0
- scitex/scholar/core/Scholar.py +1975 -0
- scitex/scholar/core/__init__.py +9 -0
- scitex/scholar/impact_factor/ImpactFactorEngine.py +204 -0
- scitex/scholar/impact_factor/__init__.py +20 -0
- scitex/scholar/impact_factor/estimation/ImpactFactorEstimationEngine.py +0 -0
- scitex/scholar/impact_factor/estimation/__init__.py +40 -0
- scitex/scholar/impact_factor/estimation/build_database.py +0 -0
- scitex/scholar/impact_factor/estimation/core/__init__.py +28 -0
- scitex/scholar/impact_factor/estimation/core/cache_manager.py +523 -0
- scitex/scholar/impact_factor/estimation/core/calculator.py +355 -0
- scitex/scholar/impact_factor/estimation/core/journal_matcher.py +428 -0
- scitex/scholar/integration/__init__.py +59 -0
- scitex/scholar/integration/base.py +502 -0
- scitex/scholar/integration/mendeley/__init__.py +22 -0
- scitex/scholar/integration/mendeley/exporter.py +166 -0
- scitex/scholar/integration/mendeley/importer.py +236 -0
- scitex/scholar/integration/mendeley/linker.py +79 -0
- scitex/scholar/integration/mendeley/mapper.py +212 -0
- scitex/scholar/integration/zotero/__init__.py +27 -0
- scitex/scholar/integration/zotero/__main__.py +264 -0
- scitex/scholar/integration/zotero/exporter.py +351 -0
- scitex/scholar/integration/zotero/importer.py +372 -0
- scitex/scholar/integration/zotero/linker.py +415 -0
- scitex/scholar/integration/zotero/mapper.py +286 -0
- scitex/scholar/metadata_engines/ScholarEngine.py +588 -0
- scitex/scholar/metadata_engines/__init__.py +21 -0
- scitex/scholar/metadata_engines/individual/ArXivEngine.py +397 -0
- scitex/scholar/metadata_engines/individual/CrossRefEngine.py +274 -0
- scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +263 -0
- scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +350 -0
- scitex/scholar/metadata_engines/individual/PubMedEngine.py +329 -0
- scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +438 -0
- scitex/scholar/metadata_engines/individual/URLDOIEngine.py +410 -0
- scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +487 -0
- scitex/scholar/metadata_engines/individual/__init__.py +7 -0
- scitex/scholar/metadata_engines/utils/_PubMedConverter.py +469 -0
- scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +283 -0
- scitex/scholar/metadata_engines/utils/__init__.py +30 -0
- scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +103 -0
- scitex/scholar/metadata_engines/utils/_standardize_metadata.py +376 -0
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +579 -0
- scitex/scholar/pdf_download/__init__.py +5 -0
- scitex/scholar/pdf_download/strategies/__init__.py +38 -0
- scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +376 -0
- scitex/scholar/pdf_download/strategies/direct_download.py +131 -0
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +167 -0
- scitex/scholar/pdf_download/strategies/manual_download_utils.py +996 -0
- scitex/scholar/pdf_download/strategies/response_body.py +207 -0
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +364 -0
- scitex/scholar/pipelines/ScholarPipelineParallel.py +478 -0
- scitex/scholar/pipelines/ScholarPipelineSingle.py +767 -0
- scitex/scholar/pipelines/__init__.py +49 -0
- scitex/scholar/storage/BibTeXHandler.py +1018 -0
- scitex/scholar/storage/PaperIO.py +468 -0
- scitex/scholar/storage/ScholarLibrary.py +182 -0
- scitex/scholar/storage/_DeduplicationManager.py +548 -0
- scitex/scholar/storage/_LibraryCacheManager.py +724 -0
- scitex/scholar/storage/_LibraryManager.py +1835 -0
- scitex/scholar/storage/__init__.py +28 -0
- scitex/scholar/url_finder/ScholarURLFinder.py +379 -0
- scitex/scholar/url_finder/__init__.py +7 -0
- scitex/scholar/url_finder/strategies/__init__.py +33 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +261 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_dropdown.py +67 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +204 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +256 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +165 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +163 -0
- scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +70 -0
- scitex/scholar/utils/__init__.py +22 -0
- scitex/scholar/utils/bibtex/__init__.py +9 -0
- scitex/scholar/utils/bibtex/_parse_bibtex.py +71 -0
- scitex/scholar/utils/cleanup/__init__.py +8 -0
- scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +96 -0
- scitex/scholar/utils/cleanup/cleanup_old_extractions.py +117 -0
- scitex/scholar/utils/text/_TextNormalizer.py +407 -0
- scitex/scholar/utils/text/__init__.py +9 -0
- scitex/scholar/zotero/__init__.py +38 -0
- scitex/session/__init__.py +51 -0
- scitex/session/_lifecycle.py +736 -0
- scitex/session/_manager.py +102 -0
- scitex/session/template.py +122 -0
- scitex/stats/__init__.py +30 -26
- scitex/stats/correct/__init__.py +21 -0
- scitex/stats/correct/_correct_bonferroni.py +551 -0
- scitex/stats/correct/_correct_fdr.py +634 -0
- scitex/stats/correct/_correct_holm.py +548 -0
- scitex/stats/correct/_correct_sidak.py +499 -0
- scitex/stats/descriptive/__init__.py +85 -0
- scitex/stats/descriptive/_circular.py +540 -0
- scitex/stats/descriptive/_describe.py +219 -0
- scitex/stats/descriptive/_nan.py +518 -0
- scitex/stats/descriptive/_real.py +189 -0
- scitex/stats/effect_sizes/__init__.py +41 -0
- scitex/stats/effect_sizes/_cliffs_delta.py +325 -0
- scitex/stats/effect_sizes/_cohens_d.py +342 -0
- scitex/stats/effect_sizes/_epsilon_squared.py +315 -0
- scitex/stats/effect_sizes/_eta_squared.py +302 -0
- scitex/stats/effect_sizes/_prob_superiority.py +296 -0
- scitex/stats/posthoc/__init__.py +19 -0
- scitex/stats/posthoc/_dunnett.py +463 -0
- scitex/stats/posthoc/_games_howell.py +383 -0
- scitex/stats/posthoc/_tukey_hsd.py +367 -0
- scitex/stats/power/__init__.py +19 -0
- scitex/stats/power/_power.py +433 -0
- scitex/stats/template.py +119 -0
- scitex/stats/utils/__init__.py +62 -0
- scitex/stats/utils/_effect_size.py +985 -0
- scitex/stats/utils/_formatters.py +270 -0
- scitex/stats/utils/_normalizers.py +927 -0
- scitex/stats/utils/_power.py +433 -0
- scitex/stats_v01/_EffectSizeCalculator.py +488 -0
- scitex/stats_v01/_StatisticalValidator.py +411 -0
- scitex/stats_v01/__init__.py +60 -0
- scitex/stats_v01/_additional_tests.py +415 -0
- scitex/{stats → stats_v01}/_p2stars.py +19 -5
- scitex/stats_v01/_two_sample_tests.py +141 -0
- scitex/stats_v01/desc/__init__.py +83 -0
- scitex/stats_v01/desc/_circular.py +540 -0
- scitex/stats_v01/desc/_describe.py +219 -0
- scitex/stats_v01/desc/_nan.py +518 -0
- scitex/{stats/desc/_nan.py → stats_v01/desc/_nan_v01-20250920_145731.py} +23 -12
- scitex/stats_v01/desc/_real.py +189 -0
- scitex/stats_v01/tests/__corr_test_optimized.py +221 -0
- scitex/stats_v01/tests/_corr_test_optimized.py +179 -0
- scitex/str/__init__.py +1 -3
- scitex/str/_clean_path.py +6 -2
- scitex/str/_latex_fallback.py +267 -160
- scitex/str/_parse.py +44 -36
- scitex/str/_printc.py +1 -3
- scitex/template/__init__.py +87 -0
- scitex/template/_create_project.py +267 -0
- scitex/template/create_pip_project.py +80 -0
- scitex/template/create_research.py +80 -0
- scitex/template/create_singularity.py +80 -0
- scitex/units.py +291 -0
- scitex/utils/_compress_hdf5.py +14 -3
- scitex/utils/_email.py +21 -2
- scitex/utils/_grid.py +6 -4
- scitex/utils/_notify.py +13 -10
- scitex/utils/_verify_scitex_format.py +589 -0
- scitex/utils/_verify_scitex_format_v01.py +370 -0
- scitex/utils/template.py +122 -0
- scitex/web/_search_pubmed.py +62 -16
- scitex-2.1.0.dist-info/LICENSE +21 -0
- scitex-2.1.0.dist-info/METADATA +677 -0
- scitex-2.1.0.dist-info/RECORD +919 -0
- {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/WHEEL +1 -1
- scitex-2.1.0.dist-info/entry_points.txt +3 -0
- scitex/ai/__Classifiers.py +0 -101
- scitex/ai/classification/classification_reporter.py +0 -1137
- scitex/ai/classification/classifiers.py +0 -101
- scitex/ai/classification_reporter.py +0 -1161
- scitex/ai/genai/__init__.py +0 -277
- scitex/ai/genai/anthropic_provider.py +0 -320
- scitex/ai/genai/anthropic_refactored.py +0 -109
- scitex/ai/genai/auth_manager.py +0 -200
- scitex/ai/genai/base_provider.py +0 -291
- scitex/ai/genai/chat_history.py +0 -307
- scitex/ai/genai/cost_tracker.py +0 -276
- scitex/ai/genai/deepseek_provider.py +0 -251
- scitex/ai/genai/google_provider.py +0 -228
- scitex/ai/genai/groq_provider.py +0 -248
- scitex/ai/genai/image_processor.py +0 -250
- scitex/ai/genai/llama_provider.py +0 -214
- scitex/ai/genai/mock_provider.py +0 -127
- scitex/ai/genai/model_registry.py +0 -304
- scitex/ai/genai/openai_provider.py +0 -293
- scitex/ai/genai/perplexity_provider.py +0 -205
- scitex/ai/genai/provider_base.py +0 -302
- scitex/ai/genai/provider_factory.py +0 -370
- scitex/ai/genai/response_handler.py +0 -235
- scitex/ai/layer/_Pass.py +0 -21
- scitex/ai/layer/__init__.py +0 -10
- scitex/ai/layer/_switch.py +0 -8
- scitex/ai/metrics/_bACC.py +0 -51
- scitex/ai/plt/_learning_curve.py +0 -194
- scitex/ai/plt/_optuna_study.py +0 -111
- scitex/ai/plt/aucs/__init__.py +0 -2
- scitex/ai/plt/aucs/example.py +0 -60
- scitex/ai/plt/aucs/pre_rec_auc.py +0 -223
- scitex/ai/plt/aucs/roc_auc.py +0 -246
- scitex/ai/sampling/undersample.py +0 -29
- scitex/db/_SQLite3.py +0 -2136
- scitex/db/_SQLite3Mixins/_BlobMixin.py +0 -229
- scitex/gen/_close.py +0 -222
- scitex/gen/_start.py +0 -451
- scitex/general/__init__.py +0 -5
- scitex/io/_load_modules/_db.py +0 -24
- scitex/life/__init__.py +0 -10
- scitex/life/_monitor_rain.py +0 -49
- scitex/reproduce/_fix_seeds.py +0 -45
- scitex/res/__init__.py +0 -5
- scitex/scholar/_local_search.py +0 -454
- scitex/scholar/_paper.py +0 -244
- scitex/scholar/_pdf_downloader.py +0 -325
- scitex/scholar/_search.py +0 -393
- scitex/scholar/_vector_search.py +0 -370
- scitex/scholar/_web_sources.py +0 -457
- scitex/stats/desc/__init__.py +0 -40
- scitex-2.0.0.dist-info/METADATA +0 -307
- scitex-2.0.0.dist-info/RECORD +0 -572
- scitex-2.0.0.dist-info/licenses/LICENSE +0 -7
- /scitex/ai/{act → activation}/__init__.py +0 -0
- /scitex/ai/{act → activation}/_define.py +0 -0
- /scitex/ai/{early_stopping.py → training/_EarlyStopping.py} +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ImportExportMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_IndexMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_RowMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TableMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/__init__.py +0 -0
- /scitex/{stats → stats_v01}/_calc_partial_corr.py +0 -0
- /scitex/{stats → stats_v01}/_corr_test_multi.py +0 -0
- /scitex/{stats → stats_v01}/_corr_test_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_describe_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_multiple_corrections.py +0 -0
- /scitex/{stats → stats_v01}/_nan_stats.py +0 -0
- /scitex/{stats → stats_v01}/_p2stars_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_statistical_tests.py +0 -0
- /scitex/{stats/desc/_describe.py → stats_v01/desc/_describe_v01-20250920_145731.py} +0 -0
- /scitex/{stats/desc/_real.py → stats_v01/desc/_real_v01-20250920_145731.py} +0 -0
- /scitex/{stats → stats_v01}/multiple/__init__.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_bonferroni_correction.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_fdr_correction.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_multicompair.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test_multi.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test_single.py +0 -0
- /scitex/{stats → stats_v01}/tests/__init__.py +0 -0
- /scitex/{stats → stats_v01}/tests/_brunner_munzel_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/_nocorrelation_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/_smirnov_grubbs.py +0 -0
- {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/top_level.txt +0 -0
scitex/io/_save.py
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
# Timestamp: "2025-
|
|
4
|
-
# File: /
|
|
3
|
+
# Timestamp: "2025-10-16 03:09:46 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/io/_save.py
|
|
5
5
|
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
6
7
|
import os
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/io/_save.py"
|
|
10
|
+
)
|
|
9
11
|
__DIR__ = os.path.dirname(__FILE__)
|
|
10
12
|
# ----------------------------------------
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
__FILE__ = __file__
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
import warnings
|
|
15
17
|
|
|
16
18
|
"""
|
|
17
19
|
1. Functionality:
|
|
@@ -28,9 +30,11 @@ THIS_FILE = "/home/ywatanabe/proj/scitex_repo/src/scitex/io/_save.py"
|
|
|
28
30
|
|
|
29
31
|
"""Imports"""
|
|
30
32
|
import inspect
|
|
31
|
-
import logging
|
|
32
33
|
import os as _os
|
|
33
|
-
from
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Any, Union
|
|
36
|
+
|
|
37
|
+
from scitex import logging
|
|
34
38
|
|
|
35
39
|
from .._sh import sh
|
|
36
40
|
from ..path._clean import clean
|
|
@@ -41,119 +45,123 @@ from ..str._readable_bytes import readable_bytes
|
|
|
41
45
|
|
|
42
46
|
# Import save functions from the new modular structure
|
|
43
47
|
from ._save_modules import (
|
|
48
|
+
save_catboost,
|
|
44
49
|
save_csv,
|
|
45
50
|
save_excel,
|
|
51
|
+
save_hdf5,
|
|
52
|
+
save_html,
|
|
53
|
+
save_image,
|
|
54
|
+
save_joblib,
|
|
55
|
+
save_json,
|
|
56
|
+
save_matlab,
|
|
57
|
+
save_mp4,
|
|
46
58
|
save_npy,
|
|
47
59
|
save_npz,
|
|
48
60
|
save_pickle,
|
|
49
61
|
save_pickle_compressed,
|
|
50
|
-
|
|
62
|
+
save_tex,
|
|
63
|
+
save_text,
|
|
51
64
|
save_torch,
|
|
52
|
-
save_json,
|
|
53
65
|
save_yaml,
|
|
54
|
-
|
|
55
|
-
save_matlab,
|
|
56
|
-
save_catboost,
|
|
57
|
-
save_text,
|
|
58
|
-
save_html,
|
|
59
|
-
save_image,
|
|
60
|
-
save_mp4,
|
|
61
|
-
save_listed_dfs_as_csv,
|
|
62
|
-
save_listed_scalars_as_csv,
|
|
63
|
-
save_optuna_study_as_csv_and_pngs,
|
|
66
|
+
save_zarr,
|
|
64
67
|
)
|
|
68
|
+
from ._save_modules._bibtex import save_bibtex
|
|
69
|
+
|
|
70
|
+
logger = logging.getLogger()
|
|
65
71
|
|
|
66
72
|
|
|
67
73
|
def _get_figure_with_data(obj):
|
|
68
74
|
"""
|
|
69
75
|
Extract figure or axes object that may contain plotting data for CSV export.
|
|
70
|
-
|
|
76
|
+
|
|
71
77
|
Parameters
|
|
72
78
|
----------
|
|
73
79
|
obj : various matplotlib objects
|
|
74
80
|
Could be Figure, Axes, FigWrapper, AxisWrapper, or other matplotlib objects
|
|
75
|
-
|
|
81
|
+
|
|
76
82
|
Returns
|
|
77
83
|
-------
|
|
78
84
|
object or None
|
|
79
85
|
Figure or axes object that has export_as_csv methods, or None if not found
|
|
80
86
|
"""
|
|
81
|
-
import matplotlib.pyplot as plt
|
|
82
|
-
import matplotlib.figure
|
|
83
87
|
import matplotlib.axes
|
|
84
|
-
|
|
88
|
+
import matplotlib.figure
|
|
89
|
+
import matplotlib.pyplot as plt
|
|
90
|
+
|
|
85
91
|
# Check if object already has export methods (SciTeX wrapped objects)
|
|
86
|
-
if hasattr(obj,
|
|
92
|
+
if hasattr(obj, "export_as_csv"):
|
|
87
93
|
return obj
|
|
88
|
-
|
|
94
|
+
|
|
89
95
|
# Handle matplotlib Figure objects
|
|
90
96
|
if isinstance(obj, matplotlib.figure.Figure):
|
|
91
97
|
# Get the current axes that might be wrapped with SciTeX functionality
|
|
92
98
|
current_ax = plt.gca()
|
|
93
|
-
if hasattr(current_ax,
|
|
99
|
+
if hasattr(current_ax, "export_as_csv"):
|
|
94
100
|
return current_ax
|
|
95
|
-
|
|
101
|
+
|
|
96
102
|
# Check all axes in the figure
|
|
97
103
|
for ax in obj.axes:
|
|
98
|
-
if hasattr(ax,
|
|
104
|
+
if hasattr(ax, "export_as_csv"):
|
|
99
105
|
return ax
|
|
100
|
-
|
|
106
|
+
|
|
101
107
|
return None
|
|
102
|
-
|
|
103
|
-
# Handle matplotlib Axes objects
|
|
108
|
+
|
|
109
|
+
# Handle matplotlib Axes objects
|
|
104
110
|
if isinstance(obj, matplotlib.axes.Axes):
|
|
105
|
-
if hasattr(obj,
|
|
111
|
+
if hasattr(obj, "export_as_csv"):
|
|
106
112
|
return obj
|
|
107
113
|
return None
|
|
108
|
-
|
|
114
|
+
|
|
109
115
|
# Handle FigWrapper or similar SciTeX objects
|
|
110
|
-
if hasattr(obj,
|
|
116
|
+
if hasattr(obj, "figure") and hasattr(obj.figure, "axes"):
|
|
111
117
|
# Check if the wrapper itself has export methods
|
|
112
|
-
if hasattr(obj,
|
|
118
|
+
if hasattr(obj, "export_as_csv"):
|
|
113
119
|
return obj
|
|
114
|
-
|
|
120
|
+
|
|
115
121
|
# Check the underlying figure's axes
|
|
116
122
|
for ax in obj.figure.axes:
|
|
117
|
-
if hasattr(ax,
|
|
123
|
+
if hasattr(ax, "export_as_csv"):
|
|
118
124
|
return ax
|
|
119
|
-
|
|
125
|
+
|
|
120
126
|
return None
|
|
121
|
-
|
|
127
|
+
|
|
122
128
|
# Handle AxisWrapper or similar SciTeX objects
|
|
123
|
-
if hasattr(obj,
|
|
124
|
-
if hasattr(obj,
|
|
129
|
+
if hasattr(obj, "_axis_mpl") or hasattr(obj, "_ax"):
|
|
130
|
+
if hasattr(obj, "export_as_csv"):
|
|
125
131
|
return obj
|
|
126
132
|
return None
|
|
127
|
-
|
|
133
|
+
|
|
128
134
|
# Try to get the current figure and its axes as fallback
|
|
129
135
|
try:
|
|
130
136
|
current_fig = plt.gcf()
|
|
131
|
-
current_ax = plt.gca()
|
|
132
|
-
|
|
133
|
-
if hasattr(current_ax,
|
|
137
|
+
current_ax = plt.gca()
|
|
138
|
+
|
|
139
|
+
if hasattr(current_ax, "export_as_csv"):
|
|
134
140
|
return current_ax
|
|
135
|
-
elif hasattr(current_fig,
|
|
141
|
+
elif hasattr(current_fig, "export_as_csv"):
|
|
136
142
|
return current_fig
|
|
137
|
-
|
|
143
|
+
|
|
138
144
|
# Check all axes in current figure
|
|
139
145
|
for ax in current_fig.axes:
|
|
140
|
-
if hasattr(ax,
|
|
146
|
+
if hasattr(ax, "export_as_csv"):
|
|
141
147
|
return ax
|
|
142
|
-
|
|
148
|
+
|
|
143
149
|
except:
|
|
144
150
|
pass
|
|
145
|
-
|
|
151
|
+
|
|
146
152
|
return None
|
|
147
153
|
|
|
148
154
|
|
|
149
155
|
def save(
|
|
150
156
|
obj: Any,
|
|
151
|
-
specified_path: str,
|
|
157
|
+
specified_path: Union[str, Path],
|
|
152
158
|
makedirs: bool = True,
|
|
153
159
|
verbose: bool = True,
|
|
154
160
|
symlink_from_cwd: bool = False,
|
|
161
|
+
symlink_to: Union[str, Path] = None,
|
|
155
162
|
dry_run: bool = False,
|
|
156
163
|
no_csv: bool = False,
|
|
164
|
+
use_caller_path: bool = False,
|
|
157
165
|
**kwargs,
|
|
158
166
|
) -> None:
|
|
159
167
|
"""
|
|
@@ -163,16 +171,22 @@ def save(
|
|
|
163
171
|
----------
|
|
164
172
|
obj : Any
|
|
165
173
|
The object to be saved. Can be a NumPy array, PyTorch tensor, Pandas DataFrame, or any serializable object.
|
|
166
|
-
specified_path : str
|
|
167
|
-
The file name or path where the object should be saved. The file extension determines the format.
|
|
174
|
+
specified_path : Union[str, Path]
|
|
175
|
+
The file name or path where the object should be saved. Can be a string or pathlib.Path object. The file extension determines the format.
|
|
168
176
|
makedirs : bool, optional
|
|
169
177
|
If True, create the directory path if it does not exist. Default is True.
|
|
170
178
|
verbose : bool, optional
|
|
171
179
|
If True, print a message upon successful saving. Default is True.
|
|
172
180
|
symlink_from_cwd : bool, optional
|
|
173
181
|
If True, create a _symlink from the current working directory. Default is False.
|
|
182
|
+
symlink_to : Union[str, Path], optional
|
|
183
|
+
If specified, create a symlink at this path pointing to the saved file. Default is None.
|
|
174
184
|
dry_run : bool, optional
|
|
175
185
|
If True, simulate the saving process without actually writing files. Default is False.
|
|
186
|
+
use_caller_path : bool, optional
|
|
187
|
+
If True, intelligently determine the script path by skipping internal library frames.
|
|
188
|
+
This is useful when stx.io.save is called from within scitex library code.
|
|
189
|
+
Default is False.
|
|
176
190
|
**kwargs
|
|
177
191
|
Additional keyword arguments to pass to the underlying save function of the specific format.
|
|
178
192
|
|
|
@@ -221,6 +235,10 @@ def save(
|
|
|
221
235
|
>>> scitex.io.save(data_dict, "data.json")
|
|
222
236
|
"""
|
|
223
237
|
try:
|
|
238
|
+
# Convert Path objects to strings for consistency
|
|
239
|
+
if isinstance(specified_path, Path):
|
|
240
|
+
specified_path = str(specified_path)
|
|
241
|
+
|
|
224
242
|
########################################
|
|
225
243
|
# DO NOT MODIFY THIS SECTION
|
|
226
244
|
########################################
|
|
@@ -230,7 +248,10 @@ def save(
|
|
|
230
248
|
# When called in /path/to/script.py,
|
|
231
249
|
# data will be saved under `/path/to/script.py_out/`
|
|
232
250
|
#
|
|
233
|
-
#
|
|
251
|
+
# When called in a Jupyter notebook /path/to/notebook.ipynb,
|
|
252
|
+
# data will be saved under `/path/to/notebook_out/`
|
|
253
|
+
#
|
|
254
|
+
# When called in ipython environment,
|
|
234
255
|
# data will be saved under `/tmp/{_os.getenv("USER")/`
|
|
235
256
|
#
|
|
236
257
|
########################################
|
|
@@ -240,26 +261,29 @@ def save(
|
|
|
240
261
|
if specified_path.startswith('f"') or specified_path.startswith("f'"):
|
|
241
262
|
# Remove the f prefix and quotes
|
|
242
263
|
path_content = specified_path[2:-1]
|
|
243
|
-
|
|
264
|
+
|
|
244
265
|
# Get the caller's frame to access their local variables
|
|
245
266
|
frame = inspect.currentframe().f_back
|
|
246
267
|
try:
|
|
247
268
|
# Use string formatting with the caller's locals and globals
|
|
248
269
|
# This is much safer than eval() as it only does string substitution
|
|
249
270
|
import re
|
|
271
|
+
|
|
250
272
|
# Find all {variable} patterns
|
|
251
|
-
variables = re.findall(r
|
|
273
|
+
variables = re.findall(r"\{([^}]+)\}", path_content)
|
|
252
274
|
format_dict = {}
|
|
253
275
|
for var in variables:
|
|
254
276
|
# Only allow simple variable names, not arbitrary expressions
|
|
255
|
-
if re.match(r
|
|
277
|
+
if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", var):
|
|
256
278
|
if var in frame.f_locals:
|
|
257
279
|
format_dict[var] = frame.f_locals[var]
|
|
258
280
|
elif var in frame.f_globals:
|
|
259
281
|
format_dict[var] = frame.f_globals[var]
|
|
260
282
|
else:
|
|
261
|
-
raise ValueError(
|
|
262
|
-
|
|
283
|
+
raise ValueError(
|
|
284
|
+
f"Invalid variable name in f-string: {var}"
|
|
285
|
+
)
|
|
286
|
+
|
|
263
287
|
# Use str.format() which is safe
|
|
264
288
|
specified_path = path_content.format(**format_dict)
|
|
265
289
|
finally:
|
|
@@ -271,14 +295,72 @@ def save(
|
|
|
271
295
|
|
|
272
296
|
# When relative path
|
|
273
297
|
else:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
298
|
+
# Import here to avoid circular imports
|
|
299
|
+
from ..gen._detect_environment import detect_environment
|
|
300
|
+
from ..gen._get_notebook_path import get_notebook_info_simple
|
|
301
|
+
|
|
302
|
+
# Detect the current environment
|
|
303
|
+
env_type = detect_environment()
|
|
304
|
+
|
|
305
|
+
if env_type == "jupyter":
|
|
306
|
+
# Special handling for Jupyter notebooks
|
|
307
|
+
notebook_name, notebook_dir = get_notebook_info_simple()
|
|
308
|
+
|
|
309
|
+
if notebook_name:
|
|
310
|
+
# Remove .ipynb extension and add _out
|
|
311
|
+
notebook_base = _os.path.splitext(notebook_name)[0]
|
|
312
|
+
sdir = _os.path.join(
|
|
313
|
+
notebook_dir or _os.getcwd(), f"{notebook_base}_out"
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
# Fallback if we can't detect notebook name
|
|
317
|
+
sdir = _os.path.join(_os.getcwd(), "notebook_out")
|
|
318
|
+
|
|
319
|
+
spath = _os.path.join(sdir, specified_path)
|
|
320
|
+
|
|
321
|
+
elif env_type == "script":
|
|
322
|
+
# Regular script handling
|
|
323
|
+
if use_caller_path:
|
|
324
|
+
# Smart path detection: skip internal scitex library frames
|
|
325
|
+
script_path = None
|
|
326
|
+
scitex_src_path = _os.path.join(
|
|
327
|
+
_os.path.dirname(__file__), "..", ".."
|
|
328
|
+
)
|
|
329
|
+
scitex_src_path = _os.path.abspath(scitex_src_path)
|
|
330
|
+
|
|
331
|
+
# Walk through the call stack from caller to find the first non-scitex frame
|
|
332
|
+
for frame_info in inspect.stack()[1:]:
|
|
333
|
+
frame_path = _os.path.abspath(frame_info.filename)
|
|
334
|
+
# Skip frames from scitex library
|
|
335
|
+
if not frame_path.startswith(scitex_src_path):
|
|
336
|
+
script_path = frame_path
|
|
337
|
+
break
|
|
338
|
+
|
|
339
|
+
# Fallback to stack[1] if we couldn't find a non-scitex frame
|
|
340
|
+
if script_path is None:
|
|
341
|
+
script_path = inspect.stack()[1].filename
|
|
342
|
+
else:
|
|
343
|
+
script_path = inspect.stack()[1].filename
|
|
344
|
+
|
|
345
|
+
sdir = clean_path(_os.path.splitext(script_path)[0] + "_out")
|
|
346
|
+
spath = _os.path.join(sdir, specified_path)
|
|
347
|
+
|
|
348
|
+
else:
|
|
349
|
+
# IPython console or interactive mode
|
|
350
|
+
script_path = inspect.stack()[1].filename
|
|
351
|
+
|
|
352
|
+
if (
|
|
353
|
+
("ipython" in script_path)
|
|
354
|
+
or ("<stdin>" in script_path)
|
|
355
|
+
or env_type in ["ipython", "interactive"]
|
|
356
|
+
):
|
|
357
|
+
script_path = f'/tmp/{_os.getenv("USER")}'
|
|
358
|
+
sdir = script_path
|
|
359
|
+
else:
|
|
360
|
+
# Unknown environment, use current directory
|
|
361
|
+
sdir = _os.path.join(_os.getcwd(), "output")
|
|
362
|
+
|
|
363
|
+
spath = _os.path.join(sdir, specified_path)
|
|
282
364
|
|
|
283
365
|
# Sanitization
|
|
284
366
|
spath_final = clean(spath)
|
|
@@ -291,17 +373,27 @@ def save(
|
|
|
291
373
|
# Removes spath and spath_cwd to prevent potential circular links
|
|
292
374
|
# Skip deletion for CSV files to allow caching to work
|
|
293
375
|
# Also skip deletion for HDF5 files when a key is specified
|
|
294
|
-
should_skip_deletion = (
|
|
295
|
-
spath_final.endswith(
|
|
296
|
-
|
|
376
|
+
should_skip_deletion = spath_final.endswith(".csv") or (
|
|
377
|
+
(spath_final.endswith(".hdf5") or spath_final.endswith(".h5"))
|
|
378
|
+
and "key" in kwargs
|
|
297
379
|
)
|
|
298
|
-
|
|
380
|
+
|
|
299
381
|
if not should_skip_deletion:
|
|
300
382
|
for path in [spath_final, spath_cwd]:
|
|
301
383
|
sh(f"rm -f {path}", verbose=False)
|
|
302
384
|
|
|
303
385
|
if dry_run:
|
|
304
|
-
|
|
386
|
+
# Get relative path from current working directory
|
|
387
|
+
try:
|
|
388
|
+
rel_path = _os.path.relpath(spath, _os.getcwd())
|
|
389
|
+
except ValueError:
|
|
390
|
+
rel_path = spath
|
|
391
|
+
|
|
392
|
+
if verbose:
|
|
393
|
+
print()
|
|
394
|
+
logger.success(
|
|
395
|
+
color_text(f"(dry run) Saved to: ./{rel_path}", c="yellow")
|
|
396
|
+
)
|
|
305
397
|
return
|
|
306
398
|
|
|
307
399
|
# Ensure directory exists
|
|
@@ -314,20 +406,27 @@ def save(
|
|
|
314
406
|
spath_final,
|
|
315
407
|
verbose=verbose,
|
|
316
408
|
symlink_from_cwd=symlink_from_cwd,
|
|
409
|
+
symlink_to=symlink_to,
|
|
317
410
|
dry_run=dry_run,
|
|
318
411
|
no_csv=no_csv,
|
|
319
412
|
**kwargs,
|
|
320
413
|
)
|
|
321
414
|
|
|
322
|
-
# Symbolic
|
|
415
|
+
# Symbolic links
|
|
323
416
|
_symlink(spath, spath_cwd, symlink_from_cwd, verbose)
|
|
417
|
+
_symlink_to(spath_final, symlink_to, verbose)
|
|
418
|
+
return Path(spath)
|
|
419
|
+
# return True
|
|
324
420
|
|
|
325
421
|
except Exception as e:
|
|
326
|
-
|
|
327
|
-
f"Error occurred while saving: {str(e)}"
|
|
328
|
-
f"Debug: Initial script_path = {inspect.stack()[1].filename}"
|
|
329
|
-
f"Debug: Final spath = {spath}"
|
|
422
|
+
logger.error(
|
|
423
|
+
f"Error occurred while saving: {str(e)}\n"
|
|
424
|
+
f"Debug: Initial script_path = {inspect.stack()[1].filename}\n"
|
|
425
|
+
f"Debug: Final spath = {spath}\n"
|
|
426
|
+
f"Debug: specified_path type = {type(specified_path)}\n"
|
|
427
|
+
f"Debug: specified_path = {specified_path}"
|
|
330
428
|
)
|
|
429
|
+
return False
|
|
331
430
|
|
|
332
431
|
|
|
333
432
|
def _symlink(spath, spath_cwd, symlink_from_cwd, verbose):
|
|
@@ -337,7 +436,32 @@ def _symlink(spath, spath_cwd, symlink_from_cwd, verbose):
|
|
|
337
436
|
sh(f"rm -f {spath_cwd}", verbose=False)
|
|
338
437
|
sh(f"ln -sfr {spath} {spath_cwd}", verbose=False)
|
|
339
438
|
if verbose:
|
|
340
|
-
|
|
439
|
+
# Get file extension to provide more informative message
|
|
440
|
+
ext = _os.path.splitext(spath_cwd)[1].lower()
|
|
441
|
+
logger.success(color_text(f"(Symlinked to: {spath_cwd})"))
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _symlink_to(spath_final, symlink_to, verbose):
|
|
445
|
+
"""Create a symbolic link at the specified path pointing to the saved file."""
|
|
446
|
+
if symlink_to:
|
|
447
|
+
# Convert Path objects to strings for consistency
|
|
448
|
+
if isinstance(symlink_to, Path):
|
|
449
|
+
symlink_to = str(symlink_to)
|
|
450
|
+
|
|
451
|
+
# Clean the symlink path
|
|
452
|
+
symlink_to = clean(symlink_to)
|
|
453
|
+
|
|
454
|
+
# Ensure the symlink directory exists
|
|
455
|
+
_os.makedirs(_os.path.dirname(symlink_to), exist_ok=True)
|
|
456
|
+
|
|
457
|
+
# Remove existing symlink or file
|
|
458
|
+
sh(f"rm -f {symlink_to}", verbose=False)
|
|
459
|
+
|
|
460
|
+
# Create the symlink using relative path for robustness
|
|
461
|
+
sh(f"ln -sfr {spath_final} {symlink_to}", verbose=False)
|
|
462
|
+
|
|
463
|
+
if verbose:
|
|
464
|
+
print(color_text(f"\n(Symlinked to: {symlink_to})", "yellow"))
|
|
341
465
|
|
|
342
466
|
|
|
343
467
|
def _save(
|
|
@@ -347,18 +471,40 @@ def _save(
|
|
|
347
471
|
symlink_from_cwd=False,
|
|
348
472
|
dry_run=False,
|
|
349
473
|
no_csv=False,
|
|
474
|
+
symlink_to=None,
|
|
350
475
|
**kwargs,
|
|
351
476
|
):
|
|
477
|
+
# Don't use object's own save method - use consistent handlers
|
|
478
|
+
# This ensures all saves go through the same pipeline and get
|
|
479
|
+
# the yellow confirmation message
|
|
480
|
+
|
|
352
481
|
# Get file extension
|
|
353
482
|
ext = _os.path.splitext(spath)[1].lower()
|
|
354
|
-
|
|
483
|
+
|
|
355
484
|
# Try dispatch dictionary first for O(1) lookup
|
|
356
485
|
if ext in _FILE_HANDLERS:
|
|
357
486
|
# Check if handler needs special parameters
|
|
358
|
-
if ext in [
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
487
|
+
if ext in [
|
|
488
|
+
".png",
|
|
489
|
+
".jpg",
|
|
490
|
+
".jpeg",
|
|
491
|
+
".gif",
|
|
492
|
+
".tiff",
|
|
493
|
+
".tif",
|
|
494
|
+
".svg",
|
|
495
|
+
".pdf",
|
|
496
|
+
]:
|
|
497
|
+
_FILE_HANDLERS[ext](
|
|
498
|
+
obj,
|
|
499
|
+
spath,
|
|
500
|
+
no_csv=no_csv,
|
|
501
|
+
symlink_from_cwd=symlink_from_cwd,
|
|
502
|
+
symlink_to=symlink_to,
|
|
503
|
+
dry_run=dry_run,
|
|
504
|
+
**kwargs,
|
|
505
|
+
)
|
|
506
|
+
elif ext in [".hdf5", ".h5", ".zarr"]:
|
|
507
|
+
# HDF5 and Zarr files may need special 'key' parameter
|
|
362
508
|
_FILE_HANDLERS[ext](obj, spath, **kwargs)
|
|
363
509
|
else:
|
|
364
510
|
_FILE_HANDLERS[ext](obj, spath, **kwargs)
|
|
@@ -375,165 +521,277 @@ def _save(
|
|
|
375
521
|
if _os.path.exists(spath):
|
|
376
522
|
file_size = getsize(spath)
|
|
377
523
|
file_size = readable_bytes(file_size)
|
|
378
|
-
|
|
524
|
+
# Get relative path from current working directory
|
|
525
|
+
try:
|
|
526
|
+
rel_path = _os.path.relpath(spath, _os.getcwd())
|
|
527
|
+
except ValueError:
|
|
528
|
+
rel_path = spath
|
|
379
529
|
|
|
530
|
+
print()
|
|
531
|
+
logger.success(f"Saved to: ./{rel_path} ({file_size})")
|
|
380
532
|
|
|
381
|
-
|
|
533
|
+
|
|
534
|
+
def _save_separate_legends(
|
|
535
|
+
obj, spath, symlink_from_cwd=False, dry_run=False, **kwargs
|
|
536
|
+
):
|
|
382
537
|
"""Save separate legend files if ax.legend('separate') was used."""
|
|
383
|
-
|
|
538
|
+
if dry_run:
|
|
539
|
+
return
|
|
540
|
+
|
|
384
541
|
import matplotlib.figure
|
|
385
|
-
|
|
542
|
+
import matplotlib.pyplot as plt
|
|
543
|
+
|
|
386
544
|
# Get the matplotlib figure object
|
|
387
545
|
fig = None
|
|
388
546
|
if isinstance(obj, matplotlib.figure.Figure):
|
|
389
547
|
fig = obj
|
|
390
|
-
elif hasattr(obj,
|
|
548
|
+
elif hasattr(obj, "_fig_mpl"):
|
|
391
549
|
fig = obj._fig_mpl
|
|
392
|
-
elif hasattr(obj,
|
|
550
|
+
elif hasattr(obj, "figure"):
|
|
393
551
|
if isinstance(obj.figure, matplotlib.figure.Figure):
|
|
394
552
|
fig = obj.figure
|
|
395
|
-
elif hasattr(obj.figure,
|
|
553
|
+
elif hasattr(obj.figure, "_fig_mpl"):
|
|
396
554
|
fig = obj.figure._fig_mpl
|
|
397
|
-
|
|
555
|
+
|
|
398
556
|
if fig is None:
|
|
399
557
|
return
|
|
400
|
-
|
|
558
|
+
|
|
401
559
|
# Check if there are separate legend parameters stored
|
|
402
|
-
if not hasattr(fig,
|
|
560
|
+
if not hasattr(fig, "_separate_legend_params"):
|
|
403
561
|
return
|
|
404
|
-
|
|
562
|
+
|
|
405
563
|
# Save each legend as a separate file
|
|
406
564
|
base_path = _os.path.splitext(spath)[0]
|
|
407
565
|
ext = _os.path.splitext(spath)[1]
|
|
408
|
-
|
|
566
|
+
|
|
409
567
|
for legend_params in fig._separate_legend_params:
|
|
410
568
|
# Create a new figure for the legend
|
|
411
|
-
legend_fig = plt.figure(figsize=legend_params[
|
|
569
|
+
legend_fig = plt.figure(figsize=legend_params["figsize"])
|
|
412
570
|
legend_ax = legend_fig.add_subplot(111)
|
|
413
|
-
|
|
571
|
+
|
|
414
572
|
# Create the legend
|
|
415
573
|
legend = legend_ax.legend(
|
|
416
|
-
legend_params[
|
|
417
|
-
legend_params[
|
|
418
|
-
loc=
|
|
419
|
-
frameon=legend_params[
|
|
420
|
-
fancybox=legend_params[
|
|
421
|
-
shadow=legend_params[
|
|
422
|
-
**legend_params[
|
|
574
|
+
legend_params["handles"],
|
|
575
|
+
legend_params["labels"],
|
|
576
|
+
loc="center",
|
|
577
|
+
frameon=legend_params["frameon"],
|
|
578
|
+
fancybox=legend_params["fancybox"],
|
|
579
|
+
shadow=legend_params["shadow"],
|
|
580
|
+
**legend_params["kwargs"],
|
|
423
581
|
)
|
|
424
|
-
|
|
582
|
+
|
|
425
583
|
# Remove axes
|
|
426
|
-
legend_ax.axis(
|
|
427
|
-
|
|
584
|
+
legend_ax.axis("off")
|
|
585
|
+
|
|
428
586
|
# Adjust layout to fit the legend
|
|
429
587
|
legend_fig.tight_layout()
|
|
430
|
-
|
|
588
|
+
|
|
431
589
|
# Save the legend figure
|
|
432
590
|
legend_filename = f"{base_path}_{legend_params['axis_id']}_legend{ext}"
|
|
433
591
|
save_image(legend_fig, legend_filename, **kwargs)
|
|
434
|
-
|
|
592
|
+
|
|
435
593
|
# Close the legend figure to free memory
|
|
436
594
|
plt.close(legend_fig)
|
|
437
|
-
|
|
595
|
+
|
|
438
596
|
if not dry_run and _os.path.exists(legend_filename):
|
|
439
597
|
file_size = getsize(legend_filename)
|
|
440
598
|
file_size = readable_bytes(file_size)
|
|
441
|
-
print(
|
|
599
|
+
print(
|
|
600
|
+
color_text(
|
|
601
|
+
f"\nSaved legend to: {legend_filename} ({file_size})",
|
|
602
|
+
c="yellow",
|
|
603
|
+
)
|
|
604
|
+
)
|
|
442
605
|
|
|
443
606
|
|
|
444
|
-
def _handle_image_with_csv(
|
|
607
|
+
def _handle_image_with_csv(
|
|
608
|
+
obj,
|
|
609
|
+
spath,
|
|
610
|
+
no_csv=False,
|
|
611
|
+
symlink_from_cwd=False,
|
|
612
|
+
dry_run=False,
|
|
613
|
+
symlink_to=None,
|
|
614
|
+
**kwargs,
|
|
615
|
+
):
|
|
445
616
|
"""Handle image file saving with optional CSV export."""
|
|
617
|
+
if dry_run:
|
|
618
|
+
return
|
|
619
|
+
|
|
446
620
|
save_image(obj, spath, **kwargs)
|
|
447
|
-
|
|
621
|
+
|
|
448
622
|
# Handle separate legend saving
|
|
449
|
-
_save_separate_legends(
|
|
450
|
-
|
|
623
|
+
_save_separate_legends(
|
|
624
|
+
obj,
|
|
625
|
+
spath,
|
|
626
|
+
symlink_from_cwd=symlink_from_cwd,
|
|
627
|
+
dry_run=dry_run,
|
|
628
|
+
**kwargs,
|
|
629
|
+
)
|
|
630
|
+
|
|
451
631
|
if not no_csv:
|
|
452
632
|
ext = _os.path.splitext(spath)[1].lower()
|
|
453
633
|
ext_wo_dot = ext.replace(".", "")
|
|
454
|
-
|
|
634
|
+
|
|
455
635
|
try:
|
|
456
636
|
# Get the figure object that may contain plot data
|
|
457
637
|
fig_obj = _get_figure_with_data(obj)
|
|
458
|
-
|
|
638
|
+
|
|
459
639
|
if fig_obj is not None:
|
|
460
640
|
# Save regular CSV if export method exists
|
|
461
|
-
if hasattr(fig_obj,
|
|
641
|
+
if hasattr(fig_obj, "export_as_csv"):
|
|
462
642
|
csv_data = fig_obj.export_as_csv()
|
|
463
643
|
if csv_data is not None and not csv_data.empty:
|
|
464
|
-
|
|
644
|
+
# Save CSV in same directory as image with .csv extension
|
|
645
|
+
# Example: ./path/to/output/fig.jpg -> ./path/to/output/fig.csv
|
|
646
|
+
csv_path = _os.path.splitext(spath)[0] + ".csv"
|
|
647
|
+
# Ensure parent directory exists (should already exist from image save)
|
|
648
|
+
_os.makedirs(_os.path.dirname(csv_path), exist_ok=True)
|
|
649
|
+
# Save directly using _save to avoid path doubling
|
|
650
|
+
# Don't pass image-specific kwargs to CSV save
|
|
651
|
+
_save(
|
|
465
652
|
csv_data,
|
|
466
|
-
|
|
467
|
-
|
|
653
|
+
csv_path,
|
|
654
|
+
verbose=True,
|
|
655
|
+
symlink_from_cwd=False, # Will handle symlink manually
|
|
468
656
|
dry_run=dry_run,
|
|
469
657
|
no_csv=True,
|
|
470
|
-
**kwargs,
|
|
471
658
|
)
|
|
472
|
-
|
|
659
|
+
|
|
660
|
+
# Create symlink_to for CSV if it was specified for the image
|
|
661
|
+
if symlink_to:
|
|
662
|
+
csv_symlink_to = (
|
|
663
|
+
_os.path.splitext(symlink_to)[0] + ".csv"
|
|
664
|
+
)
|
|
665
|
+
_symlink_to(csv_path, csv_symlink_to, True)
|
|
666
|
+
|
|
667
|
+
# Create symlink for CSV manually if needed
|
|
668
|
+
if symlink_from_cwd:
|
|
669
|
+
# Get the relative path from the original specified path
|
|
670
|
+
# This preserves the directory structure for the symlink
|
|
671
|
+
import inspect
|
|
672
|
+
|
|
673
|
+
frame_info = inspect.stack()
|
|
674
|
+
# Find the original specified_path from the parent save() call
|
|
675
|
+
for frame in frame_info:
|
|
676
|
+
if "specified_path" in frame.frame.f_locals:
|
|
677
|
+
original_path = frame.frame.f_locals[
|
|
678
|
+
"specified_path"
|
|
679
|
+
]
|
|
680
|
+
if isinstance(original_path, str):
|
|
681
|
+
# Replace extension to get CSV path structure
|
|
682
|
+
csv_relative = original_path.replace(
|
|
683
|
+
_os.path.splitext(original_path)[
|
|
684
|
+
1
|
|
685
|
+
],
|
|
686
|
+
".csv",
|
|
687
|
+
)
|
|
688
|
+
csv_cwd = _os.path.join(
|
|
689
|
+
_os.getcwd(), csv_relative
|
|
690
|
+
)
|
|
691
|
+
_symlink(csv_path, csv_cwd, True, True)
|
|
692
|
+
break
|
|
693
|
+
else:
|
|
694
|
+
# Fallback to basename if we can't find the original path
|
|
695
|
+
csv_cwd = (
|
|
696
|
+
_os.getcwd()
|
|
697
|
+
+ "/"
|
|
698
|
+
+ _os.path.basename(csv_path)
|
|
699
|
+
)
|
|
700
|
+
_symlink(csv_path, csv_cwd, True, True)
|
|
701
|
+
|
|
473
702
|
# Save SigmaPlot CSV if method exists
|
|
474
|
-
if hasattr(fig_obj,
|
|
703
|
+
if hasattr(fig_obj, "export_as_csv_for_sigmaplot"):
|
|
475
704
|
sigmaplot_data = fig_obj.export_as_csv_for_sigmaplot()
|
|
476
705
|
if sigmaplot_data is not None and not sigmaplot_data.empty:
|
|
477
|
-
|
|
706
|
+
# For CSV files, we want them in the same directory as the image
|
|
707
|
+
csv_sigmaplot_path = spath.replace(
|
|
708
|
+
ext_wo_dot, "csv"
|
|
709
|
+
).replace(".csv", "_for_sigmaplot.csv")
|
|
710
|
+
# Save directly using _save to avoid path doubling
|
|
711
|
+
# Don't pass image-specific kwargs to CSV save
|
|
712
|
+
_save(
|
|
478
713
|
sigmaplot_data,
|
|
479
|
-
|
|
480
|
-
|
|
714
|
+
csv_sigmaplot_path,
|
|
715
|
+
verbose=True,
|
|
716
|
+
symlink_from_cwd=False, # Will handle symlink manually
|
|
481
717
|
dry_run=dry_run,
|
|
482
718
|
no_csv=True,
|
|
483
|
-
**kwargs,
|
|
484
719
|
)
|
|
485
|
-
|
|
486
|
-
|
|
720
|
+
|
|
721
|
+
# Create symlink_to for SigmaPlot CSV if it was specified for the image
|
|
722
|
+
if symlink_to:
|
|
723
|
+
csv_sigmaplot_symlink_to = (
|
|
724
|
+
_os.path.splitext(symlink_to)[0]
|
|
725
|
+
+ "_for_sigmaplot.csv"
|
|
726
|
+
)
|
|
727
|
+
_symlink_to(
|
|
728
|
+
csv_sigmaplot_path,
|
|
729
|
+
csv_sigmaplot_symlink_to,
|
|
730
|
+
True,
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
# Create symlink for SigmaPlot CSV manually if needed
|
|
734
|
+
if symlink_from_cwd:
|
|
735
|
+
csv_cwd = (
|
|
736
|
+
_os.getcwd()
|
|
737
|
+
+ "/"
|
|
738
|
+
+ _os.path.basename(csv_sigmaplot_path)
|
|
739
|
+
)
|
|
740
|
+
_symlink(csv_sigmaplot_path, csv_cwd, True, True)
|
|
741
|
+
except Exception as e:
|
|
742
|
+
import warnings
|
|
743
|
+
|
|
744
|
+
warnings.warn(f"CSV export failed: {e}")
|
|
745
|
+
import traceback
|
|
746
|
+
|
|
747
|
+
traceback.print_exc()
|
|
487
748
|
|
|
488
749
|
|
|
489
750
|
# Dispatch dictionary for O(1) file format lookup
|
|
490
751
|
_FILE_HANDLERS = {
|
|
491
752
|
# Excel formats
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
753
|
+
".xlsx": save_excel,
|
|
754
|
+
".xls": save_excel,
|
|
495
755
|
# NumPy formats
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
756
|
+
".npy": save_npy,
|
|
757
|
+
".npz": save_npz,
|
|
499
758
|
# Pickle formats
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
759
|
+
".pkl": save_pickle,
|
|
760
|
+
".pickle": save_pickle,
|
|
761
|
+
".pkl.gz": save_pickle_compressed,
|
|
504
762
|
# Other binary formats
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
763
|
+
".joblib": save_joblib,
|
|
764
|
+
".pth": save_torch,
|
|
765
|
+
".pt": save_torch,
|
|
766
|
+
".mat": save_matlab,
|
|
767
|
+
".cbm": save_catboost,
|
|
511
768
|
# Text formats
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
769
|
+
".json": save_json,
|
|
770
|
+
".yaml": save_yaml,
|
|
771
|
+
".yml": save_yaml,
|
|
772
|
+
".txt": save_text,
|
|
773
|
+
".md": save_text,
|
|
774
|
+
".py": save_text,
|
|
775
|
+
".css": save_text,
|
|
776
|
+
".js": save_text,
|
|
777
|
+
".tex": save_tex,
|
|
778
|
+
# Bibliography
|
|
779
|
+
".bib": save_bibtex,
|
|
521
780
|
# Data formats
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
781
|
+
".html": save_html,
|
|
782
|
+
".hdf5": save_hdf5,
|
|
783
|
+
".h5": save_hdf5,
|
|
784
|
+
".zarr": save_zarr,
|
|
526
785
|
# Media formats
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
786
|
+
".mp4": save_mp4,
|
|
787
|
+
".png": _handle_image_with_csv,
|
|
788
|
+
".jpg": _handle_image_with_csv,
|
|
789
|
+
".jpeg": _handle_image_with_csv,
|
|
790
|
+
".gif": _handle_image_with_csv,
|
|
791
|
+
".tiff": _handle_image_with_csv,
|
|
792
|
+
".tif": _handle_image_with_csv,
|
|
793
|
+
".svg": _handle_image_with_csv,
|
|
794
|
+
".pdf": _handle_image_with_csv,
|
|
536
795
|
}
|
|
537
796
|
|
|
538
|
-
|
|
539
797
|
# EOF
|