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
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-03 01:56:15 (ywatanabe)"
|
|
4
|
+
# File: /ssh:sp:/home/ywatanabe/proj/scitex_repo/src/scitex/ml/metrics/_calc_seizure_prediction_metrics.py
|
|
5
|
+
"""Calculate clinical seizure prediction metrics.
|
|
6
|
+
|
|
7
|
+
This module provides both window-based and event-based seizure prediction metrics
|
|
8
|
+
following FDA/clinical guidelines.
|
|
9
|
+
|
|
10
|
+
Two Approaches:
|
|
11
|
+
1. Window-based: Measures % of seizure time windows detected
|
|
12
|
+
2. Event-based: Measures % of seizure events detected (≥1 alarm per event)
|
|
13
|
+
|
|
14
|
+
Key Metrics:
|
|
15
|
+
- seizure_sensitivity: % detected (interpretation depends on window vs event-based)
|
|
16
|
+
- fp_per_hour: False positives per hour during interictal periods
|
|
17
|
+
- time_in_warning: % of total time in alarm state
|
|
18
|
+
|
|
19
|
+
Clinical Targets (FDA guidelines):
|
|
20
|
+
- Sensitivity ≥ 90%
|
|
21
|
+
- FP/h ≤ 0.2
|
|
22
|
+
- Time in warning ≤ 20%
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
from typing import Dict
|
|
26
|
+
import numpy as np
|
|
27
|
+
import pandas as pd
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def calc_seizure_window_prediction_metrics(
|
|
31
|
+
y_true: np.ndarray,
|
|
32
|
+
y_pred: np.ndarray,
|
|
33
|
+
metadata: pd.DataFrame,
|
|
34
|
+
window_duration_min: float = 1.0,
|
|
35
|
+
) -> Dict[str, float]:
|
|
36
|
+
"""Calculate clinical seizure prediction metrics (window-based).
|
|
37
|
+
|
|
38
|
+
This function calculates window-based sensitivity, meaning it measures
|
|
39
|
+
the percentage of seizure time windows that were correctly identified.
|
|
40
|
+
This is NOT event-based sensitivity (which would measure % of seizure
|
|
41
|
+
events detected regardless of how many windows within each event).
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
y_true : np.ndarray
|
|
46
|
+
True labels (string: 'seizure' or 'interictal_control')
|
|
47
|
+
y_pred : np.ndarray
|
|
48
|
+
Predicted labels (string: 'seizure' or 'interictal_control')
|
|
49
|
+
metadata : pd.DataFrame
|
|
50
|
+
Metadata with 'seizure_type' column indicating seizure/interictal periods
|
|
51
|
+
window_duration_min : float, optional
|
|
52
|
+
Duration of each time window in minutes (default: 1.0)
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
Dict[str, float]
|
|
57
|
+
Dictionary containing:
|
|
58
|
+
- seizure_sensitivity: % of seizure *time windows* detected (NOT event-based)
|
|
59
|
+
- fp_per_hour: False positives per hour during interictal periods
|
|
60
|
+
- time_in_warning: % of total time in alarm state
|
|
61
|
+
- n_seizure_windows: Number of seizure windows
|
|
62
|
+
- n_interictal_windows: Number of interictal windows
|
|
63
|
+
- n_true_positives: Correctly predicted seizure windows
|
|
64
|
+
- n_false_positives: Incorrectly predicted as seizure
|
|
65
|
+
- n_false_negatives: Missed seizure windows
|
|
66
|
+
- n_true_negatives: Correctly predicted as interictal
|
|
67
|
+
- meets_sensitivity_target: Whether sensitivity ≥ 90%
|
|
68
|
+
- meets_fp_target: Whether FP/h ≤ 0.2
|
|
69
|
+
- meets_tiw_target: Whether time in warning ≤ 20%
|
|
70
|
+
|
|
71
|
+
Notes
|
|
72
|
+
-----
|
|
73
|
+
- False positives are calculated only during interictal periods
|
|
74
|
+
- True positives/false negatives are calculated only during seizure periods
|
|
75
|
+
- Clinical targets based on FDA guidance for seizure prediction devices
|
|
76
|
+
- For event-based sensitivity, use calc_seizure_event_prediction_metrics instead
|
|
77
|
+
|
|
78
|
+
Example
|
|
79
|
+
-------
|
|
80
|
+
>>> # 1 seizure spanning 20 windows, detect 5 windows
|
|
81
|
+
>>> # Window-based sensitivity: 5/20 = 25%
|
|
82
|
+
>>> # This measures temporal coverage of the seizure
|
|
83
|
+
|
|
84
|
+
References
|
|
85
|
+
----------
|
|
86
|
+
FDA guidance on seizure prediction devices
|
|
87
|
+
"""
|
|
88
|
+
# Create masks for seizure and interictal periods
|
|
89
|
+
seizure_mask = metadata["seizure_type"] == "seizure"
|
|
90
|
+
interictal_mask = metadata["seizure_type"] == "interictal_control"
|
|
91
|
+
|
|
92
|
+
# Convert string labels to binary for calculations
|
|
93
|
+
y_true_bin = (y_true == "seizure").astype(int)
|
|
94
|
+
y_pred_bin = (y_pred == "seizure").astype(int)
|
|
95
|
+
|
|
96
|
+
# True positives (seizure windows correctly identified)
|
|
97
|
+
tp = np.sum((y_true_bin == 1) & (y_pred_bin == 1) & seizure_mask)
|
|
98
|
+
|
|
99
|
+
# False negatives (seizure windows missed)
|
|
100
|
+
fn = np.sum((y_true_bin == 1) & (y_pred_bin == 0) & seizure_mask)
|
|
101
|
+
|
|
102
|
+
# False positives (interictal windows incorrectly alarmed)
|
|
103
|
+
fp = np.sum((y_true_bin == 0) & (y_pred_bin == 1) & interictal_mask)
|
|
104
|
+
|
|
105
|
+
# True negatives (interictal windows correctly identified)
|
|
106
|
+
tn = np.sum((y_true_bin == 0) & (y_pred_bin == 0) & interictal_mask)
|
|
107
|
+
|
|
108
|
+
# Sensitivity (seizure detection rate) - WINDOW-BASED
|
|
109
|
+
n_seizures = seizure_mask.sum()
|
|
110
|
+
seizure_sensitivity = (tp / n_seizures * 100) if n_seizures > 0 else 0.0
|
|
111
|
+
|
|
112
|
+
# False positives per hour
|
|
113
|
+
n_interictal = interictal_mask.sum()
|
|
114
|
+
total_interictal_hours = (n_interictal * window_duration_min) / 60.0
|
|
115
|
+
fp_per_hour = (
|
|
116
|
+
fp / total_interictal_hours if total_interictal_hours > 0 else 0.0
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Time in warning (% of total time in alarm state)
|
|
120
|
+
total_windows = len(y_pred)
|
|
121
|
+
alarm_windows = np.sum(y_pred_bin == 1)
|
|
122
|
+
time_in_warning = (
|
|
123
|
+
(alarm_windows / total_windows * 100) if total_windows > 0 else 0.0
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
metrics = {
|
|
127
|
+
# Primary prediction metrics
|
|
128
|
+
"seizure_sensitivity": round(seizure_sensitivity, 3),
|
|
129
|
+
"fp_per_hour": round(fp_per_hour, 3),
|
|
130
|
+
"time_in_warning": round(time_in_warning, 3),
|
|
131
|
+
# Counts (time windows, not events)
|
|
132
|
+
"n_seizure_windows": int(n_seizures),
|
|
133
|
+
"n_interictal_windows": int(n_interictal),
|
|
134
|
+
"n_true_positives": int(tp),
|
|
135
|
+
"n_false_positives": int(fp),
|
|
136
|
+
"n_false_negatives": int(fn),
|
|
137
|
+
"n_true_negatives": int(tn),
|
|
138
|
+
# Clinical targets (FDA/clinical guidelines)
|
|
139
|
+
"meets_sensitivity_target": bool(seizure_sensitivity >= 90.0),
|
|
140
|
+
"meets_fp_target": bool(fp_per_hour <= 0.2),
|
|
141
|
+
"meets_tiw_target": bool(time_in_warning <= 20.0),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return metrics
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def calc_seizure_event_prediction_metrics(
|
|
148
|
+
y_true: np.ndarray,
|
|
149
|
+
y_pred: np.ndarray,
|
|
150
|
+
metadata: pd.DataFrame,
|
|
151
|
+
window_duration_min: float = 1.0,
|
|
152
|
+
) -> Dict[str, float]:
|
|
153
|
+
"""Calculate clinical seizure prediction metrics (event-based).
|
|
154
|
+
|
|
155
|
+
This function calculates event-based sensitivity, meaning it measures
|
|
156
|
+
whether each seizure EVENT was detected (at least one alarm raised),
|
|
157
|
+
regardless of how many windows within that event were predicted.
|
|
158
|
+
|
|
159
|
+
This is clinically more relevant as one timely alarm per seizure event
|
|
160
|
+
is sufficient for intervention, matching the clinical requirement:
|
|
161
|
+
"Did the system raise an alarm for this seizure?"
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
y_true : np.ndarray
|
|
166
|
+
True labels (string: 'seizure' or 'interictal_control')
|
|
167
|
+
y_pred : np.ndarray
|
|
168
|
+
Predicted labels (string: 'seizure' or 'interictal_control')
|
|
169
|
+
metadata : pd.DataFrame
|
|
170
|
+
Metadata with 'seizure_type' and 'seizure_id' columns.
|
|
171
|
+
seizure_id: Unique identifier for each seizure event (e.g., 'sz_001', 'sz_002')
|
|
172
|
+
Should be NaN or empty for interictal periods
|
|
173
|
+
window_duration_min : float, optional
|
|
174
|
+
Duration of each time window in minutes (default: 1.0)
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
Dict[str, float]
|
|
179
|
+
Dictionary containing:
|
|
180
|
+
- seizure_sensitivity: % of seizure *events* detected (event-based)
|
|
181
|
+
- fp_per_hour: False positives per hour during interictal periods
|
|
182
|
+
- time_in_warning: % of total time in alarm state
|
|
183
|
+
- n_seizure_events: Number of unique seizure events
|
|
184
|
+
- n_detected_events: Number of events with at least one alarm
|
|
185
|
+
- n_missed_events: Number of events with zero alarms
|
|
186
|
+
- n_interictal_windows: Number of interictal windows
|
|
187
|
+
- n_false_positives: Incorrectly predicted as seizure
|
|
188
|
+
- n_true_negatives: Correctly predicted as interictal
|
|
189
|
+
- meets_sensitivity_target: Whether sensitivity ≥ 90%
|
|
190
|
+
- meets_fp_target: Whether FP/h ≤ 0.2
|
|
191
|
+
- meets_tiw_target: Whether time in warning ≤ 20%
|
|
192
|
+
|
|
193
|
+
Notes
|
|
194
|
+
-----
|
|
195
|
+
- Requires 'seizure_id' column in metadata to group windows by event
|
|
196
|
+
- False positives are calculated only during interictal periods
|
|
197
|
+
- Event detection requires at least one window predicted as seizure
|
|
198
|
+
- Clinical targets based on FDA guidance for seizure prediction devices
|
|
199
|
+
- For window-based sensitivity, use calc_seizure_window_prediction_metrics instead
|
|
200
|
+
|
|
201
|
+
Example
|
|
202
|
+
-------
|
|
203
|
+
>>> # 1 seizure spanning 20 windows, detect just 1 window
|
|
204
|
+
>>> # Event-based sensitivity: 1/1 = 100% (event was detected!)
|
|
205
|
+
>>> # This measures "did we catch the seizure at all?"
|
|
206
|
+
|
|
207
|
+
References
|
|
208
|
+
----------
|
|
209
|
+
FDA guidance on seizure prediction devices
|
|
210
|
+
"""
|
|
211
|
+
# Validate required column
|
|
212
|
+
if "seizure_id" not in metadata.columns:
|
|
213
|
+
raise ValueError(
|
|
214
|
+
"metadata must contain 'seizure_id' column for event-based metrics. "
|
|
215
|
+
"Use calc_seizure_window_prediction_metrics for window-based metrics."
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Create masks
|
|
219
|
+
seizure_mask = metadata["seizure_type"] == "seizure"
|
|
220
|
+
interictal_mask = metadata["seizure_type"] == "interictal_control"
|
|
221
|
+
|
|
222
|
+
# Convert string labels to binary
|
|
223
|
+
y_true_bin = (y_true == "seizure").astype(int)
|
|
224
|
+
y_pred_bin = (y_pred == "seizure").astype(int)
|
|
225
|
+
|
|
226
|
+
# Event-based sensitivity calculation
|
|
227
|
+
# Group by seizure_id and check if any window in that event was predicted
|
|
228
|
+
seizure_events = metadata[seizure_mask]["seizure_id"].unique()
|
|
229
|
+
n_seizure_events = len(seizure_events)
|
|
230
|
+
|
|
231
|
+
detected_events = 0
|
|
232
|
+
for event_id in seizure_events:
|
|
233
|
+
event_mask = (metadata["seizure_id"] == event_id).values
|
|
234
|
+
# Check if at least one window in this event was predicted as seizure
|
|
235
|
+
event_predictions = y_pred_bin[event_mask]
|
|
236
|
+
if np.any(event_predictions == 1):
|
|
237
|
+
detected_events += 1
|
|
238
|
+
|
|
239
|
+
missed_events = n_seizure_events - detected_events
|
|
240
|
+
|
|
241
|
+
# Event-based sensitivity: % of events detected
|
|
242
|
+
seizure_sensitivity = (
|
|
243
|
+
(detected_events / n_seizure_events * 100) if n_seizure_events > 0 else 0.0
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# False positives (interictal windows incorrectly alarmed)
|
|
247
|
+
fp = np.sum((y_true_bin == 0) & (y_pred_bin == 1) & interictal_mask)
|
|
248
|
+
|
|
249
|
+
# True negatives (interictal windows correctly identified)
|
|
250
|
+
tn = np.sum((y_true_bin == 0) & (y_pred_bin == 0) & interictal_mask)
|
|
251
|
+
|
|
252
|
+
# False positives per hour
|
|
253
|
+
n_interictal = interictal_mask.sum()
|
|
254
|
+
total_interictal_hours = (n_interictal * window_duration_min) / 60.0
|
|
255
|
+
fp_per_hour = (
|
|
256
|
+
fp / total_interictal_hours if total_interictal_hours > 0 else 0.0
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Time in warning (% of total time in alarm state)
|
|
260
|
+
total_windows = len(y_pred)
|
|
261
|
+
alarm_windows = np.sum(y_pred_bin == 1)
|
|
262
|
+
time_in_warning = (
|
|
263
|
+
(alarm_windows / total_windows * 100) if total_windows > 0 else 0.0
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
metrics = {
|
|
267
|
+
# Primary prediction metrics
|
|
268
|
+
"seizure_sensitivity": round(seizure_sensitivity, 3),
|
|
269
|
+
"fp_per_hour": round(fp_per_hour, 3),
|
|
270
|
+
"time_in_warning": round(time_in_warning, 3),
|
|
271
|
+
# Counts (events, not windows)
|
|
272
|
+
"n_seizure_events": int(n_seizure_events),
|
|
273
|
+
"n_detected_events": int(detected_events),
|
|
274
|
+
"n_missed_events": int(missed_events),
|
|
275
|
+
"n_interictal_windows": int(n_interictal),
|
|
276
|
+
"n_false_positives": int(fp),
|
|
277
|
+
"n_true_negatives": int(tn),
|
|
278
|
+
# Clinical targets (FDA/clinical guidelines)
|
|
279
|
+
"meets_sensitivity_target": bool(seizure_sensitivity >= 90.0),
|
|
280
|
+
"meets_fp_target": bool(fp_per_hour <= 0.2),
|
|
281
|
+
"meets_tiw_target": bool(time_in_warning <= 20.0),
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return metrics
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# Backward compatibility aliases
|
|
288
|
+
calc_seizure_prediction_metrics = calc_seizure_window_prediction_metrics
|
|
289
|
+
calculate_seizure_prediction_metrics = calc_seizure_window_prediction_metrics
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def parse_args():
|
|
293
|
+
"""Parse command line arguments."""
|
|
294
|
+
import argparse
|
|
295
|
+
|
|
296
|
+
parser = argparse.ArgumentParser(
|
|
297
|
+
description="Demonstrate seizure prediction metrics calculation"
|
|
298
|
+
)
|
|
299
|
+
parser.add_argument(
|
|
300
|
+
"--n-windows",
|
|
301
|
+
type=int,
|
|
302
|
+
default=1000,
|
|
303
|
+
help="Number of time windows to simulate (default: %(default)s)",
|
|
304
|
+
)
|
|
305
|
+
parser.add_argument(
|
|
306
|
+
"--window-duration",
|
|
307
|
+
type=float,
|
|
308
|
+
default=1.0,
|
|
309
|
+
help="Duration of each window in minutes (default: %(default)s)",
|
|
310
|
+
)
|
|
311
|
+
parser.add_argument(
|
|
312
|
+
"--sensitivity",
|
|
313
|
+
type=float,
|
|
314
|
+
default=0.9,
|
|
315
|
+
help="Target sensitivity to simulate (default: %(default)s)",
|
|
316
|
+
)
|
|
317
|
+
args = parser.parse_args()
|
|
318
|
+
return args
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def main(args):
|
|
322
|
+
"""Demonstrate seizure prediction metrics with synthetic data."""
|
|
323
|
+
from scitex import logging
|
|
324
|
+
|
|
325
|
+
logger = logging.getLogger(__name__)
|
|
326
|
+
|
|
327
|
+
logger.info("Creating synthetic seizure prediction data")
|
|
328
|
+
logger.info(f" n_windows: {args.n_windows}")
|
|
329
|
+
logger.info(f" window_duration: {args.window_duration} min")
|
|
330
|
+
logger.info(f" target_sensitivity: {args.sensitivity * 100}%")
|
|
331
|
+
|
|
332
|
+
# Create synthetic test data
|
|
333
|
+
n_windows = args.n_windows
|
|
334
|
+
window_duration_min = args.window_duration
|
|
335
|
+
|
|
336
|
+
# Create labels and metadata with seizure_id for event-based metrics
|
|
337
|
+
y_true = np.array(["interictal_control"] * n_windows)
|
|
338
|
+
y_pred = np.array(["interictal_control"] * n_windows)
|
|
339
|
+
metadata = pd.DataFrame({
|
|
340
|
+
"seizure_type": ["interictal_control"] * n_windows,
|
|
341
|
+
"seizure_id": [None] * n_windows # seizure_id for event-based metrics
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
# Add TWO seizure events (event 1: 100-119, event 2: 500-529)
|
|
345
|
+
event1_indices = list(range(100, 120)) # 20 windows
|
|
346
|
+
event2_indices = list(range(500, 530)) # 30 windows
|
|
347
|
+
seizure_indices = event1_indices + event2_indices
|
|
348
|
+
|
|
349
|
+
y_true[event1_indices] = "seizure"
|
|
350
|
+
y_true[event2_indices] = "seizure"
|
|
351
|
+
metadata.loc[event1_indices, "seizure_type"] = "seizure"
|
|
352
|
+
metadata.loc[event1_indices, "seizure_id"] = "sz_001"
|
|
353
|
+
metadata.loc[event2_indices, "seizure_type"] = "seizure"
|
|
354
|
+
metadata.loc[event2_indices, "seizure_id"] = "sz_002"
|
|
355
|
+
|
|
356
|
+
logger.info(f"Created 2 seizure events spanning {len(seizure_indices)} windows total")
|
|
357
|
+
logger.info(f" Event 1 (sz_001): 20 windows")
|
|
358
|
+
logger.info(f" Event 2 (sz_002): 30 windows")
|
|
359
|
+
|
|
360
|
+
# Predict some seizures correctly based on target sensitivity
|
|
361
|
+
# For event-based demo: detect only 1 window from event 1, most of event 2
|
|
362
|
+
n_detect = int(len(seizure_indices) * args.sensitivity)
|
|
363
|
+
# Detect 1 window from event 1, rest from event 2
|
|
364
|
+
detected_indices = [event1_indices[0]] + event2_indices[:n_detect-1]
|
|
365
|
+
y_pred[detected_indices] = "seizure"
|
|
366
|
+
|
|
367
|
+
logger.info(f"Simulating detection of {n_detect}/{len(seizure_indices)} seizure windows")
|
|
368
|
+
logger.info(f" Event 1: 1/20 windows detected")
|
|
369
|
+
logger.info(f" Event 2: {n_detect-1}/30 windows detected")
|
|
370
|
+
|
|
371
|
+
# Add some false positives
|
|
372
|
+
fp_indices = [200, 300, 400, 600, 700]
|
|
373
|
+
y_pred[fp_indices] = "seizure"
|
|
374
|
+
|
|
375
|
+
logger.info(f"Added {len(fp_indices)} false positive alarms")
|
|
376
|
+
|
|
377
|
+
# Calculate WINDOW-BASED metrics
|
|
378
|
+
logger.info("")
|
|
379
|
+
logger.info("Calculating WINDOW-BASED seizure prediction metrics")
|
|
380
|
+
metrics_window = calc_seizure_window_prediction_metrics(
|
|
381
|
+
y_true, y_pred, metadata, window_duration_min
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# Print window-based results
|
|
385
|
+
logger.info("=" * 70)
|
|
386
|
+
logger.info("WINDOW-BASED Metrics (How well did we cover seizure duration?)")
|
|
387
|
+
logger.info("=" * 70)
|
|
388
|
+
logger.info(f"Seizure Sensitivity: {metrics_window['seizure_sensitivity']:.1f}%")
|
|
389
|
+
logger.info(f"False Positives/Hour: {metrics_window['fp_per_hour']:.3f}")
|
|
390
|
+
logger.info(f"Time in Warning: {metrics_window['time_in_warning']:.1f}%")
|
|
391
|
+
logger.info("")
|
|
392
|
+
logger.info("Counts:")
|
|
393
|
+
logger.info(f" Seizure windows: {metrics_window['n_seizure_windows']}")
|
|
394
|
+
logger.info(f" Interictal windows: {metrics_window['n_interictal_windows']}")
|
|
395
|
+
logger.info(f" True positives: {metrics_window['n_true_positives']}")
|
|
396
|
+
logger.info(f" False positives: {metrics_window['n_false_positives']}")
|
|
397
|
+
logger.info(f" False negatives: {metrics_window['n_false_negatives']}")
|
|
398
|
+
logger.info(f" True negatives: {metrics_window['n_true_negatives']}")
|
|
399
|
+
logger.info("")
|
|
400
|
+
logger.info("Clinical Targets (FDA Guidelines):")
|
|
401
|
+
logger.info(
|
|
402
|
+
f" Meets sensitivity target (≥90%): {metrics_window['meets_sensitivity_target']}"
|
|
403
|
+
)
|
|
404
|
+
logger.info(f" Meets FP/h target (≤0.2): {metrics_window['meets_fp_target']}")
|
|
405
|
+
logger.info(f" Meets time in warning target (≤20%): {metrics_window['meets_tiw_target']}")
|
|
406
|
+
logger.info("=" * 70)
|
|
407
|
+
|
|
408
|
+
# Calculate EVENT-BASED metrics
|
|
409
|
+
logger.info("")
|
|
410
|
+
logger.info("Calculating EVENT-BASED seizure prediction metrics")
|
|
411
|
+
metrics_event = calc_seizure_event_prediction_metrics(
|
|
412
|
+
y_true, y_pred, metadata, window_duration_min
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Print event-based results
|
|
416
|
+
logger.info("=" * 70)
|
|
417
|
+
logger.info("EVENT-BASED Metrics (Did we detect each seizure event?)")
|
|
418
|
+
logger.info("=" * 70)
|
|
419
|
+
logger.info(f"Seizure Sensitivity: {metrics_event['seizure_sensitivity']:.1f}%")
|
|
420
|
+
logger.info(f"False Positives/Hour: {metrics_event['fp_per_hour']:.3f}")
|
|
421
|
+
logger.info(f"Time in Warning: {metrics_event['time_in_warning']:.1f}%")
|
|
422
|
+
logger.info("")
|
|
423
|
+
logger.info("Counts:")
|
|
424
|
+
logger.info(f" Seizure events: {metrics_event['n_seizure_events']}")
|
|
425
|
+
logger.info(f" Detected events: {metrics_event['n_detected_events']}")
|
|
426
|
+
logger.info(f" Missed events: {metrics_event['n_missed_events']}")
|
|
427
|
+
logger.info(f" Interictal windows: {metrics_event['n_interictal_windows']}")
|
|
428
|
+
logger.info(f" False positives: {metrics_event['n_false_positives']}")
|
|
429
|
+
logger.info(f" True negatives: {metrics_event['n_true_negatives']}")
|
|
430
|
+
logger.info("")
|
|
431
|
+
logger.info("Clinical Targets (FDA Guidelines):")
|
|
432
|
+
logger.info(
|
|
433
|
+
f" Meets sensitivity target (≥90%): {metrics_event['meets_sensitivity_target']}"
|
|
434
|
+
)
|
|
435
|
+
logger.info(f" Meets FP/h target (≤0.2): {metrics_event['meets_fp_target']}")
|
|
436
|
+
logger.info(f" Meets time in warning target (≤20%): {metrics_event['meets_tiw_target']}")
|
|
437
|
+
logger.info("=" * 70)
|
|
438
|
+
|
|
439
|
+
# Comparison summary
|
|
440
|
+
logger.info("")
|
|
441
|
+
logger.info("=" * 70)
|
|
442
|
+
logger.info("KEY DIFFERENCE DEMONSTRATION")
|
|
443
|
+
logger.info("=" * 70)
|
|
444
|
+
logger.info(f"Window-based sensitivity: {metrics_window['seizure_sensitivity']:.1f}% (detected {metrics_window['n_true_positives']}/{metrics_window['n_seizure_windows']} windows)")
|
|
445
|
+
logger.info(f"Event-based sensitivity: {metrics_event['seizure_sensitivity']:.1f}% (detected {metrics_event['n_detected_events']}/{metrics_event['n_seizure_events']} events)")
|
|
446
|
+
logger.info("")
|
|
447
|
+
logger.info("Interpretation:")
|
|
448
|
+
logger.info(" - Window-based: Detected only 1 window from Event 1 → Low sensitivity")
|
|
449
|
+
logger.info(" - Event-based: Detected at least 1 window from BOTH events → 100% sensitivity!")
|
|
450
|
+
logger.info(" - Clinical relevance: One timely alarm per seizure is sufficient")
|
|
451
|
+
logger.info("=" * 70)
|
|
452
|
+
|
|
453
|
+
return 0
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def run_main():
|
|
457
|
+
"""Initialize scitex framework, run main function, and cleanup."""
|
|
458
|
+
global CONFIG, CC, sys, plt, rng
|
|
459
|
+
|
|
460
|
+
import sys
|
|
461
|
+
import matplotlib.pyplot as plt
|
|
462
|
+
import scitex as stx
|
|
463
|
+
|
|
464
|
+
args = parse_args()
|
|
465
|
+
|
|
466
|
+
CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
|
|
467
|
+
sys,
|
|
468
|
+
plt,
|
|
469
|
+
args=args,
|
|
470
|
+
file=__file__,
|
|
471
|
+
sdir_suffix=None,
|
|
472
|
+
verbose=False,
|
|
473
|
+
agg=True,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
exit_status = main(args)
|
|
477
|
+
|
|
478
|
+
stx.session.close(
|
|
479
|
+
CONFIG,
|
|
480
|
+
verbose=False,
|
|
481
|
+
notify=False,
|
|
482
|
+
message="",
|
|
483
|
+
exit_status=exit_status,
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
if __name__ == "__main__":
|
|
488
|
+
run_main()
|
|
489
|
+
|
|
490
|
+
# EOF
|
|
@@ -28,7 +28,7 @@ from sklearn.metrics.pairwise import pairwise_distances as _pairwise_distances
|
|
|
28
28
|
from sklearn.utils import check_random_state as _check_random_state
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def
|
|
31
|
+
def calc_silhouette_score_slow(
|
|
32
32
|
X, labels, metric="euclidean", sample_size=None, random_state=None, **kwds
|
|
33
33
|
):
|
|
34
34
|
"""Compute the mean Silhouette Coefficient of all samples.
|
|
@@ -98,10 +98,10 @@ def silhouette_score_slow(
|
|
|
98
98
|
raise ValueError("Distance matrix cannot be precomputed")
|
|
99
99
|
else:
|
|
100
100
|
X, labels = X[indices], labels[indices]
|
|
101
|
-
return _np.mean(
|
|
101
|
+
return _np.mean(calc_silhouette_samples_slow(X, labels, metric=metric, **kwds))
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
def
|
|
104
|
+
def calc_silhouette_samples_slow(X, labels, metric="euclidean", **kwds):
|
|
105
105
|
"""Compute the Silhouette Coefficient for each sample.
|
|
106
106
|
|
|
107
107
|
The Silhoeutte Coefficient is a measure of how well samples are clustered
|
|
@@ -230,7 +230,7 @@ def _nearest_cluster_distance_slow(X, labels, metric, i):
|
|
|
230
230
|
return b
|
|
231
231
|
|
|
232
232
|
|
|
233
|
-
def
|
|
233
|
+
def calc_silhouette_score_block(
|
|
234
234
|
X, labels, metric="euclidean", sample_size=None, random_state=None, n_jobs=1, **kwds
|
|
235
235
|
):
|
|
236
236
|
"""Compute the mean Silhouette Coefficient of all samples.
|
|
@@ -299,11 +299,11 @@ def silhouette_score_block(
|
|
|
299
299
|
else:
|
|
300
300
|
X, labels = X[indices], labels[indices]
|
|
301
301
|
return _np.mean(
|
|
302
|
-
|
|
302
|
+
calc_silhouette_samples_block(X, labels, metric=metric, n_jobs=n_jobs, **kwds)
|
|
303
303
|
)
|
|
304
304
|
|
|
305
305
|
|
|
306
|
-
def
|
|
306
|
+
def calc_silhouette_samples_block(X, labels, metric="euclidean", n_jobs=1, **kwds):
|
|
307
307
|
"""Compute the Silhouette Coefficient for each sample.
|
|
308
308
|
|
|
309
309
|
The Silhoeutte Coefficient is a measure of how well samples are clustered
|
|
@@ -484,13 +484,20 @@ if __name__ == "__main__":
|
|
|
484
484
|
t = time.time() - t0
|
|
485
485
|
print("Scikit silhouette (%fs): %f" % (t, s))
|
|
486
486
|
t0 = time.time()
|
|
487
|
-
s =
|
|
487
|
+
s = calc_silhouette_score_block(X, y)
|
|
488
488
|
t = time.time() - t0
|
|
489
489
|
print("Block silhouette (%fs): %f" % (t, s))
|
|
490
490
|
t0 = time.time()
|
|
491
|
-
s =
|
|
491
|
+
s = calc_silhouette_score_block(X, y, n_jobs=2)
|
|
492
492
|
t = time.time() - t0
|
|
493
493
|
print("Block silhouette parallel (%fs): %f" % (t, s))
|
|
494
494
|
|
|
495
495
|
|
|
496
|
+
|
|
497
|
+
# Backward compatibility aliases (deprecated, will be removed in future)
|
|
498
|
+
silhouette_score_slow = calc_silhouette_score_slow
|
|
499
|
+
silhouette_samples_slow = calc_silhouette_samples_slow
|
|
500
|
+
silhouette_score_block = calc_silhouette_score_block
|
|
501
|
+
silhouette_samples_block = calc_silhouette_samples_block
|
|
502
|
+
|
|
496
503
|
# EOF
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-02 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/ml/metrics/_normalize_labels.py
|
|
5
|
+
|
|
6
|
+
"""Label normalization utility for classification metrics."""
|
|
7
|
+
|
|
8
|
+
__FILE__ = __file__
|
|
9
|
+
|
|
10
|
+
from typing import List, Optional, Tuple
|
|
11
|
+
import numpy as np
|
|
12
|
+
from sklearn.preprocessing import LabelEncoder
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def normalize_labels(
|
|
16
|
+
y_true: np.ndarray,
|
|
17
|
+
y_pred: np.ndarray,
|
|
18
|
+
labels: Optional[List] = None,
|
|
19
|
+
) -> Tuple[np.ndarray, np.ndarray, List, LabelEncoder]:
|
|
20
|
+
"""
|
|
21
|
+
Normalize labels using sklearn.preprocessing.LabelEncoder.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
y_true : np.ndarray
|
|
26
|
+
True labels (can be str or int)
|
|
27
|
+
y_pred : np.ndarray
|
|
28
|
+
Predicted labels (can be str or int)
|
|
29
|
+
labels : List, optional
|
|
30
|
+
Expected label list. If provided, will be used as display names.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
y_true_norm : np.ndarray
|
|
35
|
+
Normalized true labels (integers 0, 1, 2, ...)
|
|
36
|
+
y_pred_norm : np.ndarray
|
|
37
|
+
Normalized predicted labels (integers 0, 1, 2, ...)
|
|
38
|
+
label_names : List
|
|
39
|
+
List of label names in order
|
|
40
|
+
encoder : LabelEncoder
|
|
41
|
+
Fitted encoder for inverse transform
|
|
42
|
+
|
|
43
|
+
Notes
|
|
44
|
+
-----
|
|
45
|
+
Uses sklearn.preprocessing.LabelEncoder for robust label handling.
|
|
46
|
+
Handles the edge case where data contains integers but labels are strings
|
|
47
|
+
(e.g., y_true=[0,1,0,1] with labels=['Negative', 'Positive']).
|
|
48
|
+
"""
|
|
49
|
+
# Get unique values from data
|
|
50
|
+
all_data_labels = np.unique(np.concatenate([y_true, y_pred]))
|
|
51
|
+
|
|
52
|
+
# Create encoder
|
|
53
|
+
le = LabelEncoder()
|
|
54
|
+
|
|
55
|
+
# Handle edge case: integer data with string label names
|
|
56
|
+
if labels is not None:
|
|
57
|
+
# Check if data is integers but labels are strings
|
|
58
|
+
data_is_int = isinstance(all_data_labels[0], (int, np.integer))
|
|
59
|
+
labels_are_str = isinstance(labels[0], str)
|
|
60
|
+
|
|
61
|
+
if data_is_int and labels_are_str:
|
|
62
|
+
# Data: [0, 1], labels: ['Negative', 'Positive']
|
|
63
|
+
# Fit encoder on the integer data
|
|
64
|
+
le.fit(all_data_labels)
|
|
65
|
+
# But use provided labels as names for display
|
|
66
|
+
label_names = labels
|
|
67
|
+
else:
|
|
68
|
+
# Normal case: fit on provided labels
|
|
69
|
+
le.fit(labels)
|
|
70
|
+
label_names = list(le.classes_)
|
|
71
|
+
else:
|
|
72
|
+
# No labels provided: fit on observed data
|
|
73
|
+
le.fit(all_data_labels)
|
|
74
|
+
label_names = list(le.classes_)
|
|
75
|
+
|
|
76
|
+
# Transform to integers
|
|
77
|
+
y_true_norm = le.transform(y_true)
|
|
78
|
+
y_pred_norm = le.transform(y_pred)
|
|
79
|
+
|
|
80
|
+
return y_true_norm, y_pred_norm, label_names, le
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# EOF
|