scitex 2.14.0__py3-none-any.whl → 2.15.2__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 +71 -17
- scitex/_env_loader.py +156 -0
- scitex/_mcp_resources/__init__.py +37 -0
- scitex/_mcp_resources/_cheatsheet.py +135 -0
- scitex/_mcp_resources/_figrecipe.py +138 -0
- scitex/_mcp_resources/_formats.py +102 -0
- scitex/_mcp_resources/_modules.py +337 -0
- scitex/_mcp_resources/_session.py +149 -0
- scitex/_mcp_tools/__init__.py +4 -0
- scitex/_mcp_tools/audio.py +66 -0
- scitex/_mcp_tools/diagram.py +11 -95
- scitex/_mcp_tools/introspect.py +210 -0
- scitex/_mcp_tools/plt.py +260 -305
- scitex/_mcp_tools/scholar.py +74 -0
- scitex/_mcp_tools/social.py +244 -0
- scitex/_mcp_tools/template.py +24 -0
- scitex/_mcp_tools/writer.py +21 -204
- scitex/ai/_gen_ai/_PARAMS.py +10 -7
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
- scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
- scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
- scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
- scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
- scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
- scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
- scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
- scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
- scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
- scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
- scitex/audio/README.md +40 -36
- scitex/audio/__init__.py +129 -61
- scitex/audio/_branding.py +185 -0
- scitex/audio/_mcp/__init__.py +32 -0
- scitex/audio/_mcp/handlers.py +59 -6
- scitex/audio/_mcp/speak_handlers.py +238 -0
- scitex/audio/_relay.py +225 -0
- scitex/audio/_tts.py +18 -10
- scitex/audio/engines/base.py +17 -10
- scitex/audio/engines/elevenlabs_engine.py +7 -2
- scitex/audio/mcp_server.py +228 -75
- scitex/canvas/README.md +1 -1
- scitex/canvas/editor/_dearpygui/__init__.py +25 -0
- scitex/canvas/editor/_dearpygui/_editor.py +147 -0
- scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
- scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
- scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
- scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
- scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
- scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
- scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
- scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
- scitex/canvas/editor/_dearpygui/_selection.py +295 -0
- scitex/canvas/editor/_dearpygui/_state.py +93 -0
- scitex/canvas/editor/_dearpygui/_utils.py +61 -0
- scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
- scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
- scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
- scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
- scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
- scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
- scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
- scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
- scitex/canvas/editor/flask_editor/_core.py +25 -1684
- scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
- scitex/cli/__init__.py +38 -43
- scitex/cli/audio.py +76 -27
- scitex/cli/capture.py +13 -20
- scitex/cli/introspect.py +481 -0
- scitex/cli/main.py +200 -109
- scitex/cli/mcp.py +60 -34
- scitex/cli/plt.py +357 -0
- scitex/cli/repro.py +15 -8
- scitex/cli/resource.py +15 -8
- scitex/cli/scholar/__init__.py +23 -8
- scitex/cli/scholar/_crossref_scitex.py +296 -0
- scitex/cli/scholar/_fetch.py +25 -3
- scitex/cli/social.py +314 -0
- scitex/cli/stats.py +15 -8
- scitex/cli/template.py +129 -12
- scitex/cli/tex.py +15 -8
- scitex/cli/writer.py +132 -8
- scitex/cloud/__init__.py +41 -2
- scitex/config/README.md +1 -1
- scitex/config/__init__.py +16 -2
- scitex/config/_env_registry.py +256 -0
- scitex/context/__init__.py +22 -0
- scitex/dev/__init__.py +20 -1
- scitex/diagram/__init__.py +42 -19
- scitex/diagram/mcp_server.py +13 -125
- scitex/gen/__init__.py +50 -14
- scitex/gen/_list_packages.py +4 -4
- scitex/introspect/__init__.py +82 -0
- scitex/introspect/_call_graph.py +303 -0
- scitex/introspect/_class_hierarchy.py +163 -0
- scitex/introspect/_core.py +41 -0
- scitex/introspect/_docstring.py +131 -0
- scitex/introspect/_examples.py +113 -0
- scitex/introspect/_imports.py +271 -0
- scitex/{gen/_inspect_module.py → introspect/_list_api.py} +43 -54
- scitex/introspect/_mcp/__init__.py +41 -0
- scitex/introspect/_mcp/handlers.py +233 -0
- scitex/introspect/_members.py +155 -0
- scitex/introspect/_resolve.py +89 -0
- scitex/introspect/_signature.py +131 -0
- scitex/introspect/_source.py +80 -0
- scitex/introspect/_type_hints.py +172 -0
- scitex/io/_save.py +1 -2
- scitex/io/bundle/README.md +1 -1
- scitex/logging/_formatters.py +19 -9
- scitex/mcp_server.py +98 -5
- scitex/os/__init__.py +4 -0
- scitex/{gen → os}/_check_host.py +4 -5
- scitex/plt/__init__.py +245 -550
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
- scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/plt/gallery/README.md +1 -1
- scitex/plt/utils/_hitmap/__init__.py +82 -0
- scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
- scitex/plt/utils/_hitmap/_color_application.py +346 -0
- scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
- scitex/plt/utils/_hitmap/_constants.py +40 -0
- scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
- scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
- scitex/plt/utils/_hitmap/_query.py +113 -0
- scitex/plt/utils/_hitmap.py +46 -1616
- scitex/plt/utils/_metadata/__init__.py +80 -0
- scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
- scitex/plt/utils/_metadata/_artists/_base.py +195 -0
- scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
- scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
- scitex/plt/utils/_metadata/_artists/_images.py +80 -0
- scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
- scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
- scitex/plt/utils/_metadata/_artists/_text.py +106 -0
- scitex/plt/utils/_metadata/_csv.py +416 -0
- scitex/plt/utils/_metadata/_detect.py +225 -0
- scitex/plt/utils/_metadata/_legend.py +127 -0
- scitex/plt/utils/_metadata/_rounding.py +117 -0
- scitex/plt/utils/_metadata/_verification.py +202 -0
- scitex/schema/README.md +1 -1
- scitex/scholar/__init__.py +8 -0
- scitex/scholar/_mcp/crossref_handlers.py +265 -0
- scitex/scholar/core/Scholar.py +63 -1700
- scitex/scholar/core/_mixins/__init__.py +36 -0
- scitex/scholar/core/_mixins/_enrichers.py +270 -0
- scitex/scholar/core/_mixins/_library_handlers.py +100 -0
- scitex/scholar/core/_mixins/_loaders.py +103 -0
- scitex/scholar/core/_mixins/_pdf_download.py +375 -0
- scitex/scholar/core/_mixins/_pipeline.py +312 -0
- scitex/scholar/core/_mixins/_project_handlers.py +125 -0
- scitex/scholar/core/_mixins/_savers.py +69 -0
- scitex/scholar/core/_mixins/_search.py +103 -0
- scitex/scholar/core/_mixins/_services.py +88 -0
- scitex/scholar/core/_mixins/_url_finding.py +105 -0
- scitex/scholar/crossref_scitex.py +367 -0
- scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/scholar/examples/00_run_all.sh +120 -0
- scitex/scholar/jobs/_executors.py +27 -3
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
- scitex/scholar/pdf_download/_cli.py +154 -0
- scitex/scholar/pdf_download/strategies/__init__.py +11 -8
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
- scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
- scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
- scitex/scholar/pipelines/_single_steps.py +71 -36
- scitex/scholar/storage/_LibraryManager.py +97 -1695
- scitex/scholar/storage/_mixins/__init__.py +30 -0
- scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
- scitex/scholar/storage/_mixins/_library_operations.py +218 -0
- scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
- scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
- scitex/scholar/storage/_mixins/_resolution.py +376 -0
- scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
- scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
- scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
- scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
- scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
- scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
- scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
- scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
- scitex/security/README.md +3 -3
- scitex/session/README.md +1 -1
- scitex/session/__init__.py +26 -7
- scitex/session/_decorator.py +1 -1
- scitex/sh/README.md +1 -1
- scitex/sh/__init__.py +7 -4
- scitex/social/__init__.py +155 -0
- scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/stats/_mcp/_handlers/__init__.py +31 -0
- scitex/stats/_mcp/_handlers/_corrections.py +113 -0
- scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
- scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
- scitex/stats/_mcp/_handlers/_format.py +94 -0
- scitex/stats/_mcp/_handlers/_normality.py +110 -0
- scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
- scitex/stats/_mcp/_handlers/_power.py +247 -0
- scitex/stats/_mcp/_handlers/_recommend.py +102 -0
- scitex/stats/_mcp/_handlers/_run_test.py +279 -0
- scitex/stats/_mcp/_handlers/_stars.py +48 -0
- scitex/stats/_mcp/handlers.py +19 -1171
- scitex/stats/auto/_stat_style.py +175 -0
- scitex/stats/auto/_style_definitions.py +411 -0
- scitex/stats/auto/_styles.py +22 -620
- scitex/stats/descriptive/__init__.py +11 -8
- scitex/stats/descriptive/_ci.py +39 -0
- scitex/stats/power/_power.py +15 -4
- scitex/str/__init__.py +2 -1
- scitex/str/_title_case.py +63 -0
- scitex/template/README.md +1 -1
- scitex/template/__init__.py +25 -10
- scitex/template/_code_templates.py +147 -0
- scitex/template/_mcp/handlers.py +81 -0
- scitex/template/_mcp/tool_schemas.py +55 -0
- scitex/template/_templates/__init__.py +51 -0
- scitex/template/_templates/audio.py +233 -0
- scitex/template/_templates/canvas.py +312 -0
- scitex/template/_templates/capture.py +268 -0
- scitex/template/_templates/config.py +43 -0
- scitex/template/_templates/diagram.py +294 -0
- scitex/template/_templates/io.py +107 -0
- scitex/template/_templates/module.py +53 -0
- scitex/template/_templates/plt.py +202 -0
- scitex/template/_templates/scholar.py +267 -0
- scitex/template/_templates/session.py +130 -0
- scitex/template/_templates/session_minimal.py +43 -0
- scitex/template/_templates/session_plot.py +67 -0
- scitex/template/_templates/session_stats.py +77 -0
- scitex/template/_templates/stats.py +323 -0
- scitex/template/_templates/writer.py +296 -0
- scitex/template/clone_writer_directory.py +5 -5
- scitex/ui/_backends/_email.py +10 -2
- scitex/ui/_backends/_webhook.py +5 -1
- scitex/web/_search_pubmed.py +10 -6
- scitex/writer/README.md +1 -1
- scitex/writer/_mcp/handlers.py +11 -744
- scitex/writer/_mcp/tool_schemas.py +5 -335
- scitex-2.15.2.dist-info/METADATA +648 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/RECORD +246 -150
- scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
- scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
- scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
- scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
- scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
- scitex/diagram/_compile.py +0 -312
- scitex/diagram/_diagram.py +0 -355
- scitex/diagram/_mcp/__init__.py +0 -4
- scitex/diagram/_mcp/handlers.py +0 -400
- scitex/diagram/_mcp/tool_schemas.py +0 -157
- scitex/diagram/_presets.py +0 -173
- scitex/diagram/_schema.py +0 -182
- scitex/diagram/_split.py +0 -278
- scitex/gen/_ci.py +0 -12
- scitex/gen/_title_case.py +0 -89
- scitex/plt/_mcp/__init__.py +0 -4
- scitex/plt/_mcp/_handlers_annotation.py +0 -102
- scitex/plt/_mcp/_handlers_figure.py +0 -195
- scitex/plt/_mcp/_handlers_plot.py +0 -252
- scitex/plt/_mcp/_handlers_style.py +0 -219
- scitex/plt/_mcp/handlers.py +0 -74
- scitex/plt/_mcp/tool_schemas.py +0 -497
- scitex/plt/mcp_server.py +0 -231
- scitex/scholar/data/.gitkeep +0 -0
- scitex/scholar/data/README.md +0 -44
- scitex/scholar/data/bib_files/bibliography.bib +0 -1952
- scitex/scholar/data/bib_files/neurovista.bib +0 -277
- scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
- scitex/scholar/data/bib_files/openaccess.bib +0 -89
- scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
- scitex/scholar/data/bib_files/pac.bib +0 -698
- scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
- scitex/scholar/data/bib_files/pac_processed.bib +0 -0
- scitex/scholar/data/bib_files/pac_titles.txt +0 -75
- scitex/scholar/data/bib_files/paywalled.bib +0 -98
- scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
- scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
- scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
- scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
- scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_seizure.bib +0 -46
- scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
- scitex/scholar/data/impact_factor.db +0 -0
- scitex/scholar/examples/SUGGESTIONS.md +0 -865
- scitex/scholar/examples/dev.py +0 -38
- scitex-2.14.0.dist-info/METADATA +0 -1238
- /scitex/{gen → context}/_detect_environment.py +0 -0
- /scitex/{gen → context}/_get_notebook_path.py +0 -0
- /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/WHEEL +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/entry_points.txt +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/ai/classification/timeseries/_sliding_window_plotting.py
|
|
4
|
+
|
|
5
|
+
"""Plotting mixin for TimeSeriesSlidingWindowSplit visualization."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
import matplotlib.patches as patches
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
import scitex as stx
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ._sliding_window_core import TimeSeriesSlidingWindowSplitCore
|
|
19
|
+
|
|
20
|
+
__all__ = ["SlidingWindowPlottingMixin"]
|
|
21
|
+
|
|
22
|
+
COLORS = stx.plt.color.PARAMS
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SlidingWindowPlottingMixin:
|
|
26
|
+
"""Mixin class providing plot_splits visualization for sliding window splitters."""
|
|
27
|
+
|
|
28
|
+
def plot_splits(
|
|
29
|
+
self: TimeSeriesSlidingWindowSplitCore,
|
|
30
|
+
X,
|
|
31
|
+
y=None,
|
|
32
|
+
timestamps=None,
|
|
33
|
+
figsize=(12, 6),
|
|
34
|
+
save_path=None,
|
|
35
|
+
):
|
|
36
|
+
"""Visualize the sliding window splits as rectangles.
|
|
37
|
+
|
|
38
|
+
Shows train (blue), validation (green), and test (red) sets.
|
|
39
|
+
When val_ratio=0, only shows train and test.
|
|
40
|
+
When undersampling is enabled, shows dropped samples in gray.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
X : array-like
|
|
45
|
+
Training data
|
|
46
|
+
y : array-like, optional
|
|
47
|
+
Target variable (required for undersampling visualization)
|
|
48
|
+
timestamps : array-like, optional
|
|
49
|
+
Timestamps (if None, uses sample indices)
|
|
50
|
+
figsize : tuple, default (12, 6)
|
|
51
|
+
Figure size
|
|
52
|
+
save_path : str, optional
|
|
53
|
+
Path to save the plot
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
fig : matplotlib.figure.Figure
|
|
58
|
+
The created figure
|
|
59
|
+
"""
|
|
60
|
+
if timestamps is None:
|
|
61
|
+
timestamps = np.arange(len(X))
|
|
62
|
+
|
|
63
|
+
time_order = np.argsort(timestamps)
|
|
64
|
+
|
|
65
|
+
# Get splits WITH undersampling (if enabled)
|
|
66
|
+
if self.val_ratio > 0:
|
|
67
|
+
splits = list(self.split_with_val(X, y, timestamps))[:10]
|
|
68
|
+
split_type = "train-val-test"
|
|
69
|
+
else:
|
|
70
|
+
splits = list(self.split(X, y, timestamps))[:10]
|
|
71
|
+
split_type = "train-test"
|
|
72
|
+
|
|
73
|
+
if not splits:
|
|
74
|
+
raise ValueError("No splits generated")
|
|
75
|
+
|
|
76
|
+
# Get splits WITHOUT undersampling to show dropped samples
|
|
77
|
+
splits_no_undersample = None
|
|
78
|
+
if self.undersample and y is not None:
|
|
79
|
+
original_undersample = self.undersample
|
|
80
|
+
self.undersample = False
|
|
81
|
+
if self.val_ratio > 0:
|
|
82
|
+
splits_no_undersample = list(self.split_with_val(X, y, timestamps))[:10]
|
|
83
|
+
else:
|
|
84
|
+
splits_no_undersample = list(self.split(X, y, timestamps))[:10]
|
|
85
|
+
self.undersample = original_undersample
|
|
86
|
+
|
|
87
|
+
fig, ax = stx.plt.subplots(figsize=figsize)
|
|
88
|
+
|
|
89
|
+
# Plot each fold
|
|
90
|
+
for fold, split_indices in enumerate(splits):
|
|
91
|
+
y_pos = fold
|
|
92
|
+
self._plot_fold_rectangles(ax, split_indices, time_order, y_pos, fold)
|
|
93
|
+
|
|
94
|
+
# Plot dropped samples if undersampling
|
|
95
|
+
if splits_no_undersample is not None:
|
|
96
|
+
self._plot_dropped_samples(ax, splits, splits_no_undersample, time_order, y)
|
|
97
|
+
|
|
98
|
+
# Plot kept samples
|
|
99
|
+
self._plot_kept_samples(ax, splits, time_order, y)
|
|
100
|
+
|
|
101
|
+
# Format plot
|
|
102
|
+
self._format_plot(ax, X, splits, split_type, y)
|
|
103
|
+
|
|
104
|
+
plt.tight_layout()
|
|
105
|
+
|
|
106
|
+
if save_path:
|
|
107
|
+
fig.savefig(save_path, dpi=150, bbox_inches="tight")
|
|
108
|
+
|
|
109
|
+
return fig
|
|
110
|
+
|
|
111
|
+
def _plot_fold_rectangles(self, ax, split_indices, time_order, y_pos, fold):
|
|
112
|
+
"""Plot train/val/test rectangles for a fold."""
|
|
113
|
+
if len(split_indices) == 3:
|
|
114
|
+
train_idx, val_idx, test_idx = split_indices
|
|
115
|
+
self._plot_train_rect(ax, train_idx, time_order, y_pos, fold)
|
|
116
|
+
if len(val_idx) > 0:
|
|
117
|
+
self._plot_val_rect(ax, val_idx, time_order, y_pos, fold)
|
|
118
|
+
self._plot_test_rect(ax, test_idx, time_order, y_pos, fold, is_3way=True)
|
|
119
|
+
else:
|
|
120
|
+
train_idx, test_idx = split_indices
|
|
121
|
+
self._plot_train_rect(ax, train_idx, time_order, y_pos, fold)
|
|
122
|
+
self._plot_test_rect(ax, test_idx, time_order, y_pos, fold, is_3way=False)
|
|
123
|
+
|
|
124
|
+
def _plot_train_rect(self, ax, train_idx, time_order, y_pos, fold):
|
|
125
|
+
"""Plot training window rectangle."""
|
|
126
|
+
train_positions = [np.where(time_order == idx)[0][0] for idx in train_idx]
|
|
127
|
+
if train_positions:
|
|
128
|
+
train_start = min(train_positions)
|
|
129
|
+
train_end = max(train_positions)
|
|
130
|
+
train_rect = patches.Rectangle(
|
|
131
|
+
(train_start, y_pos - 0.3),
|
|
132
|
+
train_end - train_start + 1,
|
|
133
|
+
0.6,
|
|
134
|
+
linewidth=1,
|
|
135
|
+
edgecolor="blue",
|
|
136
|
+
facecolor="lightblue",
|
|
137
|
+
alpha=0.7,
|
|
138
|
+
label="Train" if fold == 0 else "",
|
|
139
|
+
)
|
|
140
|
+
ax.add_patch(train_rect)
|
|
141
|
+
|
|
142
|
+
def _plot_val_rect(self, ax, val_idx, time_order, y_pos, fold):
|
|
143
|
+
"""Plot validation window rectangle."""
|
|
144
|
+
val_positions = [np.where(time_order == idx)[0][0] for idx in val_idx]
|
|
145
|
+
if val_positions:
|
|
146
|
+
val_start = min(val_positions)
|
|
147
|
+
val_end = max(val_positions)
|
|
148
|
+
val_rect = patches.Rectangle(
|
|
149
|
+
(val_start, y_pos - 0.3),
|
|
150
|
+
val_end - val_start + 1,
|
|
151
|
+
0.6,
|
|
152
|
+
linewidth=1,
|
|
153
|
+
edgecolor="green",
|
|
154
|
+
facecolor="lightgreen",
|
|
155
|
+
alpha=0.7,
|
|
156
|
+
label="Validation" if fold == 0 else "",
|
|
157
|
+
)
|
|
158
|
+
ax.add_patch(val_rect)
|
|
159
|
+
|
|
160
|
+
def _plot_test_rect(self, ax, test_idx, time_order, y_pos, fold, is_3way):
|
|
161
|
+
"""Plot test window rectangle."""
|
|
162
|
+
test_positions = [np.where(time_order == idx)[0][0] for idx in test_idx]
|
|
163
|
+
if test_positions:
|
|
164
|
+
test_start = min(test_positions)
|
|
165
|
+
test_end = max(test_positions)
|
|
166
|
+
if is_3way:
|
|
167
|
+
edgecolor = COLORS["RGBA_NORM"]["red"]
|
|
168
|
+
facecolor = COLORS["RGBA_NORM"]["red"]
|
|
169
|
+
else:
|
|
170
|
+
edgecolor = "red"
|
|
171
|
+
facecolor = "lightcoral"
|
|
172
|
+
test_rect = patches.Rectangle(
|
|
173
|
+
(test_start, y_pos - 0.3),
|
|
174
|
+
test_end - test_start + 1,
|
|
175
|
+
0.6,
|
|
176
|
+
linewidth=1,
|
|
177
|
+
edgecolor=edgecolor,
|
|
178
|
+
facecolor=facecolor,
|
|
179
|
+
alpha=0.7,
|
|
180
|
+
label="Test" if fold == 0 else "",
|
|
181
|
+
)
|
|
182
|
+
ax.add_patch(test_rect)
|
|
183
|
+
|
|
184
|
+
def _plot_dropped_samples(self, ax, splits, splits_no_undersample, time_order, y):
|
|
185
|
+
"""Plot dropped samples from undersampling."""
|
|
186
|
+
np.random.seed(42)
|
|
187
|
+
jitter_strength = 0.15
|
|
188
|
+
|
|
189
|
+
for fold, split_indices_no_us in enumerate(splits_no_undersample):
|
|
190
|
+
y_pos = fold
|
|
191
|
+
split_indices_us = splits[fold]
|
|
192
|
+
|
|
193
|
+
if len(split_indices_no_us) == 3:
|
|
194
|
+
train_idx_no_us, val_idx_no_us, _ = split_indices_no_us
|
|
195
|
+
train_idx_us, val_idx_us, _ = split_indices_us
|
|
196
|
+
|
|
197
|
+
dropped_train = np.setdiff1d(train_idx_no_us, train_idx_us)
|
|
198
|
+
if len(dropped_train) > 0:
|
|
199
|
+
positions = [
|
|
200
|
+
np.where(time_order == idx)[0][0] for idx in dropped_train
|
|
201
|
+
]
|
|
202
|
+
jitter = np.random.normal(0, jitter_strength, len(positions))
|
|
203
|
+
ax.scatter(
|
|
204
|
+
positions,
|
|
205
|
+
y_pos + jitter,
|
|
206
|
+
c="gray",
|
|
207
|
+
s=15,
|
|
208
|
+
alpha=0.3,
|
|
209
|
+
marker="x",
|
|
210
|
+
label="Dropped (train)" if fold == 0 else "",
|
|
211
|
+
zorder=2,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
dropped_val = np.setdiff1d(val_idx_no_us, val_idx_us)
|
|
215
|
+
if len(dropped_val) > 0:
|
|
216
|
+
positions = [
|
|
217
|
+
np.where(time_order == idx)[0][0] for idx in dropped_val
|
|
218
|
+
]
|
|
219
|
+
jitter = np.random.normal(0, jitter_strength, len(positions))
|
|
220
|
+
ax.scatter(
|
|
221
|
+
positions,
|
|
222
|
+
y_pos + jitter,
|
|
223
|
+
c="gray",
|
|
224
|
+
s=15,
|
|
225
|
+
alpha=0.3,
|
|
226
|
+
marker="x",
|
|
227
|
+
label="Dropped (val)" if fold == 0 else "",
|
|
228
|
+
zorder=2,
|
|
229
|
+
)
|
|
230
|
+
else:
|
|
231
|
+
train_idx_no_us, _ = split_indices_no_us
|
|
232
|
+
train_idx_us, _ = split_indices_us
|
|
233
|
+
|
|
234
|
+
dropped_train = np.setdiff1d(train_idx_no_us, train_idx_us)
|
|
235
|
+
if len(dropped_train) > 0:
|
|
236
|
+
positions = [
|
|
237
|
+
np.where(time_order == idx)[0][0] for idx in dropped_train
|
|
238
|
+
]
|
|
239
|
+
jitter = np.random.normal(0, jitter_strength, len(positions))
|
|
240
|
+
ax.scatter(
|
|
241
|
+
positions,
|
|
242
|
+
y_pos + jitter,
|
|
243
|
+
c="gray",
|
|
244
|
+
s=15,
|
|
245
|
+
alpha=0.3,
|
|
246
|
+
marker="x",
|
|
247
|
+
label="Dropped samples" if fold == 0 else "",
|
|
248
|
+
zorder=2,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def _plot_kept_samples(self, ax, splits, time_order, y):
|
|
252
|
+
"""Plot kept samples with colors."""
|
|
253
|
+
np.random.seed(42)
|
|
254
|
+
jitter_strength = 0.15
|
|
255
|
+
|
|
256
|
+
for fold, split_indices in enumerate(splits):
|
|
257
|
+
y_pos = fold
|
|
258
|
+
|
|
259
|
+
if len(split_indices) == 3:
|
|
260
|
+
train_idx, val_idx, test_idx = split_indices
|
|
261
|
+
self._scatter_indices(
|
|
262
|
+
ax, train_idx, time_order, y_pos, y, fold, "train"
|
|
263
|
+
)
|
|
264
|
+
if len(val_idx) > 0:
|
|
265
|
+
self._scatter_indices(
|
|
266
|
+
ax, val_idx, time_order, y_pos, y, fold, "val"
|
|
267
|
+
)
|
|
268
|
+
self._scatter_indices(ax, test_idx, time_order, y_pos, y, fold, "test")
|
|
269
|
+
else:
|
|
270
|
+
train_idx, test_idx = split_indices
|
|
271
|
+
self._scatter_indices(
|
|
272
|
+
ax, train_idx, time_order, y_pos, y, fold, "train"
|
|
273
|
+
)
|
|
274
|
+
self._scatter_indices(ax, test_idx, time_order, y_pos, y, fold, "test")
|
|
275
|
+
|
|
276
|
+
def _scatter_indices(self, ax, indices, time_order, y_pos, y, fold, set_type):
|
|
277
|
+
"""Scatter plot indices with jittering."""
|
|
278
|
+
jitter_strength = 0.15
|
|
279
|
+
positions = [np.where(time_order == idx)[0][0] for idx in indices]
|
|
280
|
+
|
|
281
|
+
if not positions:
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
jitter = np.random.normal(0, jitter_strength, len(positions))
|
|
285
|
+
|
|
286
|
+
color_map = {
|
|
287
|
+
"train": ("darkblue", "o", "Train"),
|
|
288
|
+
"val": ("darkgreen", "^", "Val"),
|
|
289
|
+
"test": ("darkred", "s", "Test"),
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if y is not None:
|
|
293
|
+
class_colors = {
|
|
294
|
+
"train": (
|
|
295
|
+
COLORS["RGBA_NORM"]["blue"],
|
|
296
|
+
COLORS["RGBA_NORM"]["lightblue"],
|
|
297
|
+
),
|
|
298
|
+
"val": (COLORS["RGBA_NORM"]["yellow"], COLORS["RGBA_NORM"]["orange"]),
|
|
299
|
+
"test": (COLORS["RGBA_NORM"]["red"], COLORS["RGBA_NORM"]["brown"]),
|
|
300
|
+
}
|
|
301
|
+
colors = [
|
|
302
|
+
class_colors[set_type][0] if y[idx] == 0 else class_colors[set_type][1]
|
|
303
|
+
for idx in indices
|
|
304
|
+
]
|
|
305
|
+
ax.scatter(
|
|
306
|
+
positions,
|
|
307
|
+
y_pos + jitter,
|
|
308
|
+
c=colors,
|
|
309
|
+
s=20,
|
|
310
|
+
alpha=0.7,
|
|
311
|
+
marker=color_map[set_type][1],
|
|
312
|
+
label=f"{color_map[set_type][2]} (class 0)" if fold == 0 else "",
|
|
313
|
+
zorder=3,
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
ax.scatter(
|
|
317
|
+
positions,
|
|
318
|
+
y_pos + jitter,
|
|
319
|
+
c=color_map[set_type][0],
|
|
320
|
+
s=20,
|
|
321
|
+
alpha=0.7,
|
|
322
|
+
marker=color_map[set_type][1],
|
|
323
|
+
label=f"{color_map[set_type][2]} points" if fold == 0 else "",
|
|
324
|
+
zorder=3,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
def _format_plot(self, ax, X, splits, split_type, y):
|
|
328
|
+
"""Format the plot with labels and legend."""
|
|
329
|
+
ax.set_ylim(-0.5, len(splits) - 0.5)
|
|
330
|
+
ax.set_xlim(0, len(X))
|
|
331
|
+
ax.set_xlabel("Temporal Position (sorted by timestamp)")
|
|
332
|
+
ax.set_ylabel("Fold")
|
|
333
|
+
|
|
334
|
+
gap_text = f", Gap: {self.gap}" if self.gap > 0 else ""
|
|
335
|
+
val_text = f", Val ratio: {self.val_ratio:.1%}" if self.val_ratio > 0 else ""
|
|
336
|
+
ax.set_title(
|
|
337
|
+
f"Sliding Window Split Visualization ({split_type})\\n"
|
|
338
|
+
f"Window: {self.window_size}, Step: {self.step_size}, Test: {self.test_size}{gap_text}{val_text}\\n"
|
|
339
|
+
f"Rectangles show windows, dots show actual data points"
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
ax.set_yticks(range(len(splits)))
|
|
343
|
+
ax.set_yticklabels([f"Fold {i}" for i in range(len(splits))])
|
|
344
|
+
|
|
345
|
+
if y is not None:
|
|
346
|
+
unique_classes, class_counts = np.unique(y, return_counts=True)
|
|
347
|
+
total_class_info = ", ".join(
|
|
348
|
+
[
|
|
349
|
+
f"Class {cls}: n={count}"
|
|
350
|
+
for cls, count in zip(unique_classes, class_counts)
|
|
351
|
+
]
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
first_split = splits[0]
|
|
355
|
+
if len(first_split) == 3:
|
|
356
|
+
train_idx, val_idx, test_idx = first_split
|
|
357
|
+
fold_info = f"Fold 0: Train n={len(train_idx)}, Val n={len(val_idx)}, Test n={len(test_idx)}"
|
|
358
|
+
else:
|
|
359
|
+
train_idx, test_idx = first_split
|
|
360
|
+
fold_info = f"Fold 0: Train n={len(train_idx)}, Test n={len(test_idx)}"
|
|
361
|
+
|
|
362
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
363
|
+
legend_title = f"Total: {total_class_info}\\n{fold_info}"
|
|
364
|
+
ax.legend(handles, labels, loc="upper right", title=legend_title)
|
|
365
|
+
else:
|
|
366
|
+
ax.legend(loc="upper right")
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# EOF
|
scitex/audio/README.md
CHANGED
|
@@ -27,6 +27,8 @@ scitex audio speak "Hello world"
|
|
|
27
27
|
scitex audio speak "Bonjour" --backend gtts --voice fr
|
|
28
28
|
scitex audio backends # List available backends
|
|
29
29
|
scitex audio check # Check audio status (WSL)
|
|
30
|
+
scitex audio relay # Start HTTP relay server (for remote audio)
|
|
31
|
+
scitex audio serve # Start MCP server
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
## MCP Server
|
|
@@ -46,75 +48,77 @@ Add to `~/.claude/mcp.json`:
|
|
|
46
48
|
}
|
|
47
49
|
```
|
|
48
50
|
|
|
49
|
-
### Remote Audio (
|
|
51
|
+
### Remote Audio (HTTP Relay)
|
|
50
52
|
|
|
51
|
-
Enable remote agents to play audio on local speakers.
|
|
53
|
+
Enable remote agents to play audio on local speakers using a simple HTTP relay.
|
|
52
54
|
|
|
53
55
|
**Architecture:**
|
|
54
56
|
```
|
|
55
57
|
┌─────────────────────────┐ ┌─────────────────────────┐
|
|
56
58
|
│ Remote (e.g., NAS) │ │ Local (WSL/Windows) │
|
|
57
59
|
│ │ │ │
|
|
58
|
-
│ Claude Agent
|
|
59
|
-
│
|
|
60
|
-
│
|
|
61
|
-
│
|
|
60
|
+
│ Claude Agent uses │ │ scitex audio relay │
|
|
61
|
+
│ audio_speak_relay ─────┼─ SSH ───────▶│ --port 31293 │
|
|
62
|
+
│ │ Reverse │ │ │
|
|
63
|
+
│ localhost:31293 │ Tunnel │ ▼ │
|
|
62
64
|
│ │ │ 🔊 Speakers │
|
|
63
65
|
└─────────────────────────┘ └─────────────────────────┘
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
**Step 1: Local machine - Start
|
|
68
|
+
**Step 1: Local machine - Start relay server**
|
|
67
69
|
```bash
|
|
68
|
-
scitex audio
|
|
70
|
+
scitex audio relay --port 31293
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
**Step 2: SSH
|
|
73
|
+
**Step 2: SSH with reverse tunnel**
|
|
74
|
+
```bash
|
|
75
|
+
ssh -R 31293:localhost:31293 remote-server
|
|
76
|
+
```
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
Or add to `~/.ssh/config`:
|
|
74
79
|
```
|
|
75
80
|
Host nas
|
|
76
81
|
HostName 192.168.x.x
|
|
77
82
|
User youruser
|
|
78
|
-
RemoteForward
|
|
83
|
+
RemoteForward 31293 127.0.0.1:31293
|
|
79
84
|
```
|
|
80
85
|
|
|
81
|
-
**Step 3: Remote
|
|
86
|
+
**Step 3: Remote agent uses relay**
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"scitex-audio-remote": {
|
|
88
|
-
"type": "sse",
|
|
89
|
-
"url": "http://localhost:8084/sse"
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
```
|
|
88
|
+
The `audio_speak_relay` MCP tool auto-detects:
|
|
89
|
+
1. `SCITEX_AUDIO_RELAY_URL` env var
|
|
90
|
+
2. Localhost:31293 (SSH reverse tunnel)
|
|
91
|
+
3. SSH_CLIENT IP (auto-detected from SSH session)
|
|
94
92
|
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
ssh nas # RemoteForward creates tunnel automatically
|
|
98
|
-
```
|
|
93
|
+
### Environment Variables
|
|
99
94
|
|
|
100
|
-
|
|
95
|
+
| Variable | Default | Description |
|
|
96
|
+
|----------|---------|-------------|
|
|
97
|
+
| `SCITEX_AUDIO_PORT` | 31293 | Server/relay port |
|
|
98
|
+
| `SCITEX_AUDIO_MODE` | auto | `local`, `remote`, or `auto` |
|
|
99
|
+
| `SCITEX_AUDIO_RELAY_URL` | (auto) | Full relay URL |
|
|
100
|
+
| `SCITEX_AUDIO_RELAY_HOST` | (none) | Relay host |
|
|
101
|
+
| `SCITEX_AUDIO_RELAY_PORT` | 31293 | Relay port |
|
|
101
102
|
|
|
102
103
|
### Server Transports
|
|
103
104
|
|
|
104
105
|
| Transport | Command | Use Case |
|
|
105
106
|
|-----------|---------|----------|
|
|
106
107
|
| stdio | `scitex audio serve` | Claude Desktop (default) |
|
|
107
|
-
| sse | `scitex audio serve -t sse --port
|
|
108
|
-
| http | `scitex audio serve -t http --port
|
|
108
|
+
| sse | `scitex audio serve -t sse --port 31293` | Remote MCP agents |
|
|
109
|
+
| http | `scitex audio serve -t http --port 31293` | HTTP MCP clients |
|
|
110
|
+
| relay | `scitex audio relay --port 31293` | Simple HTTP relay |
|
|
109
111
|
|
|
110
|
-
### Tools
|
|
112
|
+
### MCP Tools
|
|
111
113
|
|
|
112
114
|
| Tool | Description |
|
|
113
115
|
|------|-------------|
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
116
|
+
| `audio_speak` | Text to speech (plays on server) |
|
|
117
|
+
| `audio_speak_local` | TTS on server machine |
|
|
118
|
+
| `audio_speak_relay` | TTS via relay (remote playback) |
|
|
119
|
+
| `audio_list_backends` | Show available backends |
|
|
120
|
+
| `audio_check_audio_status` | Check WSL audio connectivity |
|
|
121
|
+
| `audio_announce_context` | Announce current directory and git branch |
|
|
118
122
|
|
|
119
123
|
## Backends
|
|
120
124
|
|
|
@@ -126,4 +130,4 @@ Now Claude agents on the remote machine can use `mcp__scitex-audio-remote__speak
|
|
|
126
130
|
|
|
127
131
|
## Cross-Process Locking
|
|
128
132
|
|
|
129
|
-
The
|
|
133
|
+
The relay server uses FIFO locking to ensure only one audio plays at a time across all Claude Code sessions. This prevents audio overlap when multiple agents are running.
|