meerax 1.0.0__tar.gz

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.
Files changed (42) hide show
  1. meerax-1.0.0/LICENSE +21 -0
  2. meerax-1.0.0/PKG-INFO +187 -0
  3. meerax-1.0.0/README.md +137 -0
  4. meerax-1.0.0/VERSION +1 -0
  5. meerax-1.0.0/meerax/__init__.py +7 -0
  6. meerax-1.0.0/meerax/cli.py +73 -0
  7. meerax-1.0.0/meerax/data/__init__.py +12 -0
  8. meerax-1.0.0/meerax/data/imbalance.py +37 -0
  9. meerax-1.0.0/meerax/data/loader.py +48 -0
  10. meerax-1.0.0/meerax/data/split.py +52 -0
  11. meerax-1.0.0/meerax/eval/__init__.py +13 -0
  12. meerax-1.0.0/meerax/eval/classification.py +77 -0
  13. meerax-1.0.0/meerax/eval/text.py +42 -0
  14. meerax-1.0.0/meerax/eval/timeseries.py +62 -0
  15. meerax-1.0.0/meerax/llm/__init__.py +14 -0
  16. meerax-1.0.0/meerax/llm/base.py +51 -0
  17. meerax-1.0.0/meerax/llm/claude.py +69 -0
  18. meerax-1.0.0/meerax/llm/ollama.py +54 -0
  19. meerax-1.0.0/meerax/llm/openai_provider.py +70 -0
  20. meerax-1.0.0/meerax/llm/prompt.py +21 -0
  21. meerax-1.0.0/meerax/logging.py +20 -0
  22. meerax-1.0.0/meerax/report/__init__.py +3 -0
  23. meerax-1.0.0/meerax/report/builder.py +115 -0
  24. meerax-1.0.0/meerax/scaffold/__init__.py +0 -0
  25. meerax-1.0.0/meerax/scaffold/skeleton.py +128 -0
  26. meerax-1.0.0/meerax/scaffold/templates.py +131 -0
  27. meerax-1.0.0/meerax/vision/__init__.py +20 -0
  28. meerax-1.0.0/meerax/vision/dataset.py +49 -0
  29. meerax-1.0.0/meerax/vision/device.py +19 -0
  30. meerax-1.0.0/meerax/vision/gridplot.py +47 -0
  31. meerax-1.0.0/meerax/viz/__init__.py +3 -0
  32. meerax-1.0.0/meerax/viz/classification.py +51 -0
  33. meerax-1.0.0/meerax/viz/theme.py +42 -0
  34. meerax-1.0.0/meerax/viz/timeseries.py +40 -0
  35. meerax-1.0.0/meerax.egg-info/PKG-INFO +187 -0
  36. meerax-1.0.0/meerax.egg-info/SOURCES.txt +40 -0
  37. meerax-1.0.0/meerax.egg-info/dependency_links.txt +1 -0
  38. meerax-1.0.0/meerax.egg-info/entry_points.txt +2 -0
  39. meerax-1.0.0/meerax.egg-info/requires.txt +39 -0
  40. meerax-1.0.0/meerax.egg-info/top_level.txt +1 -0
  41. meerax-1.0.0/pyproject.toml +74 -0
  42. meerax-1.0.0/setup.cfg +4 -0
meerax-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sameer Maurya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
meerax-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: meerax
3
+ Version: 1.0.0
4
+ Summary: Shared ML utilities — LLM providers, evaluation metrics, visualization, and report generation for Sameer Maurya's projects and organization.
5
+ Author: Sameer Maurya
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/mauryasameer/the-forge
8
+ Project-URL: Repository, https://github.com/mauryasameer/the-forge
9
+ Project-URL: Issues, https://github.com/mauryasameer/the-forge/issues
10
+ Project-URL: Changelog, https://github.com/mauryasameer/the-forge/blob/main/CHANGELOG.md
11
+ Keywords: machine-learning,llm,evaluation,visualization
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: numpy>=1.26
19
+ Requires-Dist: pandas>=2.2
20
+ Requires-Dist: matplotlib>=3.8
21
+ Requires-Dist: scikit-learn>=1.4
22
+ Provides-Extra: llm
23
+ Requires-Dist: anthropic>=0.40; extra == "llm"
24
+ Requires-Dist: openai>=1.55; extra == "llm"
25
+ Requires-Dist: ollama>=0.3; extra == "llm"
26
+ Provides-Extra: stats
27
+ Requires-Dist: statsmodels>=0.14; extra == "stats"
28
+ Provides-Extra: nlp
29
+ Requires-Dist: nltk>=3.9; extra == "nlp"
30
+ Provides-Extra: imbalance
31
+ Requires-Dist: imbalanced-learn>=0.12; extra == "imbalance"
32
+ Provides-Extra: vision
33
+ Requires-Dist: torch>=2.2; extra == "vision"
34
+ Requires-Dist: torchvision>=0.17; extra == "vision"
35
+ Requires-Dist: pillow>=10.2; extra == "vision"
36
+ Provides-Extra: parquet
37
+ Requires-Dist: pyarrow>=15; extra == "parquet"
38
+ Provides-Extra: all
39
+ Requires-Dist: meerax[llm]; extra == "all"
40
+ Requires-Dist: meerax[stats]; extra == "all"
41
+ Requires-Dist: meerax[nlp]; extra == "all"
42
+ Requires-Dist: meerax[imbalance]; extra == "all"
43
+ Requires-Dist: meerax[vision]; extra == "all"
44
+ Requires-Dist: meerax[parquet]; extra == "all"
45
+ Provides-Extra: dev
46
+ Requires-Dist: pytest>=8; extra == "dev"
47
+ Requires-Dist: pytest-mock>=3.12; extra == "dev"
48
+ Requires-Dist: ruff==0.11.13; extra == "dev"
49
+ Dynamic: license-file
50
+
51
+ # meerax
52
+
53
+ ![Version](https://img.shields.io/badge/version-1.0.0-c8a96e)
54
+ ![Python](https://img.shields.io/badge/python-3.12-00e5cc)
55
+ ![License](https://img.shields.io/badge/license-MIT-informational)
56
+
57
+ Shared ML utilities — LLM providers, evaluation metrics, visualization, and report generation.
58
+ Used as an in-house dependency across all of Sameer Maurya's ML projects and organization.
59
+ Source repo: [the-forge](https://github.com/mauryasameer/the-forge) — kept its original name;
60
+ only the installable package was renamed to `meerax`.
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pip install git+https://github.com/mauryasameer/the-forge.git@v1.0.0
66
+ ```
67
+
68
+ Or pin in `requirements.txt`:
69
+
70
+ ```
71
+ meerax @ git+https://github.com/mauryasameer/the-forge.git@v1.0.0
72
+ ```
73
+
74
+ Once published to PyPI, this becomes `pip install meerax` / `meerax==1.0.0`.
75
+
76
+ ## Modules
77
+
78
+ | Module | What it gives you |
79
+ |---|---|
80
+ | `meerax.llm` | Swap-in LLM backends — Claude, OpenAI, Ollama behind one interface, text or images |
81
+ | `meerax.eval.classification` | F1, AUC-ROC, precision, recall in one call |
82
+ | `meerax.eval.timeseries` | RMSE, MAPE, SMAPE, ADF stationarity test |
83
+ | `meerax.eval.text` | BLEU-4, ROUGE-L for caption / summary quality |
84
+ | `meerax.viz` | Dark-themed matplotlib plots (confusion matrix, ROC, forecast, decomposition) |
85
+ | `meerax.data` | CSV/parquet loaders with schema validation, stratified + time splits, SMOTE |
86
+ | `meerax.report` | Self-contained dark-themed HTML model-card report builder |
87
+ | `meerax.logging` | One-call structured logger factory |
88
+ | `meerax.vision` | Image folder dataset loader (PyTorch) + translation-grid plotting (torch or numpy/TF images) |
89
+
90
+ ## Scaffolding Projects
91
+
92
+ Every project in the ecosystem follows the same PROJECT_STANDARDS.md layout and depends on
93
+ `meerax`. The `meerax` CLI (installed alongside the package) generates or retrofits that layout:
94
+
95
+ ```bash
96
+ # brand-new project
97
+ meerax new my-project --path ~/dev
98
+
99
+ # retrofit an existing, non-empty directory — additive only, never overwrites
100
+ cd ~/dev/my-existing-notebook-project
101
+ meerax init
102
+ ```
103
+
104
+ `meerax new` creates the full `src/{core,providers,services,utils,data}` + `tests/` + CI
105
+ skeleton, pins `requirements.txt` to the current `meerax` release, and runs `git init`.
106
+
107
+ `meerax init` fills in whatever's missing from that same layout without touching files that
108
+ already exist, and reports any top-level files it doesn't recognize (e.g. notebooks) so you can
109
+ move them into `src/` by hand.
110
+
111
+ ## Quick Start
112
+
113
+ ```python
114
+ from meerax.llm import ClaudeProvider, PromptTemplate
115
+ from meerax.eval import evaluate_classifier
116
+ from meerax.viz import apply_meerax_theme
117
+ from meerax.report import ReportBuilder, ReportSection
118
+
119
+ # LLM: swap provider without changing downstream code
120
+ llm = ClaudeProvider() # or OpenAIProvider() / OllamaProvider()
121
+ tpl = PromptTemplate("Explain {finding} to a risk manager in 3 sentences.")
122
+ response = llm.generate(tpl.render(finding="high AUC-ROC with low recall"))
123
+
124
+ # Eval
125
+ metrics = evaluate_classifier(y_true, y_pred, y_prob=probabilities)
126
+ print(metrics)
127
+ # Accuracy : 0.9823
128
+ # F1 : 0.8741
129
+ # AUC-ROC : 0.9912
130
+
131
+ # Viz + Report
132
+ apply_meerax_theme()
133
+ rb = ReportBuilder("Fraud Detection — Model Report v0.1.0")
134
+ rb.add_section(ReportSection(
135
+ title="Performance",
136
+ metrics=metrics.to_dict(),
137
+ content=response.content,
138
+ ))
139
+ rb.save("reports/model_report.html")
140
+ ```
141
+
142
+ ## LLM Provider Interface
143
+
144
+ All providers implement `LLMProvider.generate()` and `.chat()`. Swap with one line:
145
+
146
+ ```python
147
+ from meerax.llm import ClaudeProvider, OpenAIProvider, OllamaProvider
148
+
149
+ llm = ClaudeProvider() # needs ANTHROPIC_API_KEY
150
+ llm = OpenAIProvider() # needs OPENAI_API_KEY
151
+ llm = OllamaProvider() # needs Ollama running locally
152
+ ```
153
+
154
+ ## Benchmarks
155
+
156
+ Self-contained benchmark scripts in `benchmarks/`:
157
+
158
+ | Script | Description |
159
+ |---|---|
160
+ | `kv_cache_benchmark.py` | KV caching simulation at GPT-2 Medium scale |
161
+
162
+ ## Project Structure
163
+
164
+ ```
165
+ the-forge/
166
+ ├── meerax/ # Installable package
167
+ │ ├── llm/ # LLM provider abstraction
168
+ │ ├── eval/ # Evaluation metrics
169
+ │ ├── viz/ # Visualization utilities
170
+ │ ├── data/ # Data loading, splitting, resampling
171
+ │ ├── report/ # HTML report builder
172
+ │ ├── scaffold/ # Project skeleton templates + create/retrofit logic
173
+ │ ├── cli.py # `meerax new` / `meerax init` command entry point
174
+ │ ├── vision/ # Image dataset loader + translation-grid plotting
175
+ │ └── logging.py # Structured logger
176
+ ├── benchmarks/ # Standalone ML benchmark scripts
177
+ ├── tests/
178
+ │ └── unit/ # 80 unit tests, zero external deps
179
+ ├── LICENSE
180
+ ├── pyproject.toml
181
+ ├── requirements.txt
182
+ └── VERSION
183
+ ```
184
+
185
+ ---
186
+
187
+ [sameer-portfolio](https://github.com/mauryasameer/sameer-portfolio) · [mauryasameer.com](https://www.mauryasameer.com)
meerax-1.0.0/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # meerax
2
+
3
+ ![Version](https://img.shields.io/badge/version-1.0.0-c8a96e)
4
+ ![Python](https://img.shields.io/badge/python-3.12-00e5cc)
5
+ ![License](https://img.shields.io/badge/license-MIT-informational)
6
+
7
+ Shared ML utilities — LLM providers, evaluation metrics, visualization, and report generation.
8
+ Used as an in-house dependency across all of Sameer Maurya's ML projects and organization.
9
+ Source repo: [the-forge](https://github.com/mauryasameer/the-forge) — kept its original name;
10
+ only the installable package was renamed to `meerax`.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install git+https://github.com/mauryasameer/the-forge.git@v1.0.0
16
+ ```
17
+
18
+ Or pin in `requirements.txt`:
19
+
20
+ ```
21
+ meerax @ git+https://github.com/mauryasameer/the-forge.git@v1.0.0
22
+ ```
23
+
24
+ Once published to PyPI, this becomes `pip install meerax` / `meerax==1.0.0`.
25
+
26
+ ## Modules
27
+
28
+ | Module | What it gives you |
29
+ |---|---|
30
+ | `meerax.llm` | Swap-in LLM backends — Claude, OpenAI, Ollama behind one interface, text or images |
31
+ | `meerax.eval.classification` | F1, AUC-ROC, precision, recall in one call |
32
+ | `meerax.eval.timeseries` | RMSE, MAPE, SMAPE, ADF stationarity test |
33
+ | `meerax.eval.text` | BLEU-4, ROUGE-L for caption / summary quality |
34
+ | `meerax.viz` | Dark-themed matplotlib plots (confusion matrix, ROC, forecast, decomposition) |
35
+ | `meerax.data` | CSV/parquet loaders with schema validation, stratified + time splits, SMOTE |
36
+ | `meerax.report` | Self-contained dark-themed HTML model-card report builder |
37
+ | `meerax.logging` | One-call structured logger factory |
38
+ | `meerax.vision` | Image folder dataset loader (PyTorch) + translation-grid plotting (torch or numpy/TF images) |
39
+
40
+ ## Scaffolding Projects
41
+
42
+ Every project in the ecosystem follows the same PROJECT_STANDARDS.md layout and depends on
43
+ `meerax`. The `meerax` CLI (installed alongside the package) generates or retrofits that layout:
44
+
45
+ ```bash
46
+ # brand-new project
47
+ meerax new my-project --path ~/dev
48
+
49
+ # retrofit an existing, non-empty directory — additive only, never overwrites
50
+ cd ~/dev/my-existing-notebook-project
51
+ meerax init
52
+ ```
53
+
54
+ `meerax new` creates the full `src/{core,providers,services,utils,data}` + `tests/` + CI
55
+ skeleton, pins `requirements.txt` to the current `meerax` release, and runs `git init`.
56
+
57
+ `meerax init` fills in whatever's missing from that same layout without touching files that
58
+ already exist, and reports any top-level files it doesn't recognize (e.g. notebooks) so you can
59
+ move them into `src/` by hand.
60
+
61
+ ## Quick Start
62
+
63
+ ```python
64
+ from meerax.llm import ClaudeProvider, PromptTemplate
65
+ from meerax.eval import evaluate_classifier
66
+ from meerax.viz import apply_meerax_theme
67
+ from meerax.report import ReportBuilder, ReportSection
68
+
69
+ # LLM: swap provider without changing downstream code
70
+ llm = ClaudeProvider() # or OpenAIProvider() / OllamaProvider()
71
+ tpl = PromptTemplate("Explain {finding} to a risk manager in 3 sentences.")
72
+ response = llm.generate(tpl.render(finding="high AUC-ROC with low recall"))
73
+
74
+ # Eval
75
+ metrics = evaluate_classifier(y_true, y_pred, y_prob=probabilities)
76
+ print(metrics)
77
+ # Accuracy : 0.9823
78
+ # F1 : 0.8741
79
+ # AUC-ROC : 0.9912
80
+
81
+ # Viz + Report
82
+ apply_meerax_theme()
83
+ rb = ReportBuilder("Fraud Detection — Model Report v0.1.0")
84
+ rb.add_section(ReportSection(
85
+ title="Performance",
86
+ metrics=metrics.to_dict(),
87
+ content=response.content,
88
+ ))
89
+ rb.save("reports/model_report.html")
90
+ ```
91
+
92
+ ## LLM Provider Interface
93
+
94
+ All providers implement `LLMProvider.generate()` and `.chat()`. Swap with one line:
95
+
96
+ ```python
97
+ from meerax.llm import ClaudeProvider, OpenAIProvider, OllamaProvider
98
+
99
+ llm = ClaudeProvider() # needs ANTHROPIC_API_KEY
100
+ llm = OpenAIProvider() # needs OPENAI_API_KEY
101
+ llm = OllamaProvider() # needs Ollama running locally
102
+ ```
103
+
104
+ ## Benchmarks
105
+
106
+ Self-contained benchmark scripts in `benchmarks/`:
107
+
108
+ | Script | Description |
109
+ |---|---|
110
+ | `kv_cache_benchmark.py` | KV caching simulation at GPT-2 Medium scale |
111
+
112
+ ## Project Structure
113
+
114
+ ```
115
+ the-forge/
116
+ ├── meerax/ # Installable package
117
+ │ ├── llm/ # LLM provider abstraction
118
+ │ ├── eval/ # Evaluation metrics
119
+ │ ├── viz/ # Visualization utilities
120
+ │ ├── data/ # Data loading, splitting, resampling
121
+ │ ├── report/ # HTML report builder
122
+ │ ├── scaffold/ # Project skeleton templates + create/retrofit logic
123
+ │ ├── cli.py # `meerax new` / `meerax init` command entry point
124
+ │ ├── vision/ # Image dataset loader + translation-grid plotting
125
+ │ └── logging.py # Structured logger
126
+ ├── benchmarks/ # Standalone ML benchmark scripts
127
+ ├── tests/
128
+ │ └── unit/ # 80 unit tests, zero external deps
129
+ ├── LICENSE
130
+ ├── pyproject.toml
131
+ ├── requirements.txt
132
+ └── VERSION
133
+ ```
134
+
135
+ ---
136
+
137
+ [sameer-portfolio](https://github.com/mauryasameer/sameer-portfolio) · [mauryasameer.com](https://www.mauryasameer.com)
meerax-1.0.0/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,7 @@
1
+ from importlib.metadata import version
2
+
3
+ from meerax.logging import setup_logger
4
+
5
+ __version__ = version("meerax")
6
+
7
+ __all__ = ["setup_logger", "__version__"]
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import meerax
9
+ from meerax.scaffold.skeleton import create_tree, ensure_meerax_dependency, retrofit_tree
10
+
11
+
12
+ def cmd_new(args: argparse.Namespace) -> int:
13
+ root = Path(args.path) / args.name
14
+ if root.exists():
15
+ print(f"error: {root} already exists", file=sys.stderr)
16
+ return 1
17
+ result = create_tree(root, args.name)
18
+ ensure_meerax_dependency(root, meerax.__version__)
19
+ try:
20
+ subprocess.run(["git", "init"], cwd=root, check=True, capture_output=True)
21
+ except (subprocess.CalledProcessError, FileNotFoundError) as exc:
22
+ print(f"error: git init failed: {exc}", file=sys.stderr)
23
+ return 1
24
+ print(f"created {len(result.created)} files/dirs in {root}")
25
+ for path in result.created:
26
+ print(f" create {path.relative_to(root)}")
27
+ return 0
28
+
29
+
30
+ def cmd_init(args: argparse.Namespace) -> int:
31
+ root = Path(args.path)
32
+ if not root.exists():
33
+ print(f"error: {root} does not exist", file=sys.stderr)
34
+ return 1
35
+ result = retrofit_tree(root, root.resolve().name)
36
+ dep_status = ensure_meerax_dependency(root, meerax.__version__)
37
+ print(f"created {len(result.created)}, skipped {len(result.skipped)} (already present)")
38
+ for path in result.created:
39
+ print(f" create {path.relative_to(root)}")
40
+ for path in result.skipped:
41
+ print(f" skip {path.relative_to(root)}")
42
+ print(f"requirements.txt: {dep_status}")
43
+ if result.unrecognized:
44
+ print("unrecognized top-level entries (move into src/ manually):")
45
+ for path in result.unrecognized:
46
+ print(f" ? {path.relative_to(root)}")
47
+ return 0
48
+
49
+
50
+ def build_parser() -> argparse.ArgumentParser:
51
+ parser = argparse.ArgumentParser(prog="meerax")
52
+ subparsers = parser.add_subparsers(dest="command", required=True)
53
+
54
+ new_parser = subparsers.add_parser("new", help="scaffold a new project")
55
+ new_parser.add_argument("name")
56
+ new_parser.add_argument("--path", default=".")
57
+ new_parser.set_defaults(func=cmd_new)
58
+
59
+ init_parser = subparsers.add_parser("init", help="retrofit an existing project")
60
+ init_parser.add_argument("--path", default=".")
61
+ init_parser.set_defaults(func=cmd_init)
62
+
63
+ return parser
64
+
65
+
66
+ def main(argv: list[str] | None = None) -> int:
67
+ parser = build_parser()
68
+ args = parser.parse_args(argv)
69
+ return args.func(args)
70
+
71
+
72
+ if __name__ == "__main__":
73
+ sys.exit(main())
@@ -0,0 +1,12 @@
1
+ from meerax.data.imbalance import random_undersample, smote_oversample
2
+ from meerax.data.loader import load_csv, load_parquet
3
+ from meerax.data.split import stratified_split, time_split
4
+
5
+ __all__ = [
6
+ "load_csv",
7
+ "load_parquet",
8
+ "stratified_split",
9
+ "time_split",
10
+ "smote_oversample",
11
+ "random_undersample",
12
+ ]
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ import numpy as np
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def smote_oversample(
11
+ X: np.ndarray,
12
+ y: np.ndarray,
13
+ random_state: int = 42,
14
+ ) -> tuple[np.ndarray, np.ndarray]:
15
+ """SMOTE oversampling. Requires imbalanced-learn."""
16
+ try:
17
+ from imblearn.over_sampling import SMOTE
18
+ except ImportError as exc:
19
+ raise ImportError("Install imbalanced-learn: pip install imbalanced-learn") from exc
20
+ X_res, y_res = SMOTE(random_state=random_state).fit_resample(X, y)
21
+ logger.info("SMOTE: %d → %d samples", len(y), len(y_res))
22
+ return X_res, y_res
23
+
24
+
25
+ def random_undersample(
26
+ X: np.ndarray,
27
+ y: np.ndarray,
28
+ random_state: int = 42,
29
+ ) -> tuple[np.ndarray, np.ndarray]:
30
+ """Random majority-class undersampling. Requires imbalanced-learn."""
31
+ try:
32
+ from imblearn.under_sampling import RandomUnderSampler
33
+ except ImportError as exc:
34
+ raise ImportError("Install imbalanced-learn: pip install imbalanced-learn") from exc
35
+ X_res, y_res = RandomUnderSampler(random_state=random_state).fit_resample(X, y)
36
+ logger.info("RandomUnderSampler: %d → %d samples", len(y), len(y_res))
37
+ return X_res, y_res
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ import pandas as pd
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def load_csv(
12
+ path: str | Path,
13
+ required_columns: list[str] | None = None,
14
+ dtype: dict | None = None,
15
+ ) -> pd.DataFrame:
16
+ """Load a CSV with optional schema validation.
17
+
18
+ Raises:
19
+ FileNotFoundError: if the file does not exist.
20
+ ValueError: if required_columns are absent from the file.
21
+ """
22
+ path = Path(path)
23
+ if not path.exists():
24
+ raise FileNotFoundError(f"Data file not found: {path}")
25
+ df = pd.read_csv(path, dtype=dtype)
26
+ logger.info("Loaded %d rows × %d cols from %s", len(df), len(df.columns), path.name)
27
+ if required_columns:
28
+ missing = set(required_columns) - set(df.columns)
29
+ if missing:
30
+ raise ValueError(f"Missing required columns: {sorted(missing)}")
31
+ return df
32
+
33
+
34
+ def load_parquet(
35
+ path: str | Path,
36
+ required_columns: list[str] | None = None,
37
+ ) -> pd.DataFrame:
38
+ """Load a Parquet file with optional schema validation."""
39
+ path = Path(path)
40
+ if not path.exists():
41
+ raise FileNotFoundError(f"Data file not found: {path}")
42
+ df = pd.read_parquet(path)
43
+ logger.info("Loaded %d rows × %d cols from %s", len(df), len(df.columns), path.name)
44
+ if required_columns:
45
+ missing = set(required_columns) - set(df.columns)
46
+ if missing:
47
+ raise ValueError(f"Missing required columns: {sorted(missing)}")
48
+ return df
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ import pandas as pd
4
+
5
+
6
+ def stratified_split(
7
+ df: pd.DataFrame,
8
+ target_col: str,
9
+ train_ratio: float = 0.7,
10
+ val_ratio: float = 0.15,
11
+ random_state: int = 42,
12
+ ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
13
+ """Stratified train / val / test split preserving class balance."""
14
+ from sklearn.model_selection import train_test_split
15
+
16
+ test_ratio = 1.0 - train_ratio - val_ratio
17
+ if test_ratio <= 0:
18
+ raise ValueError("train_ratio + val_ratio must be < 1.0")
19
+
20
+ train, temp = train_test_split(
21
+ df,
22
+ test_size=val_ratio + test_ratio,
23
+ stratify=df[target_col],
24
+ random_state=random_state,
25
+ )
26
+ val, test = train_test_split(
27
+ temp,
28
+ test_size=test_ratio / (val_ratio + test_ratio),
29
+ stratify=temp[target_col],
30
+ random_state=random_state,
31
+ )
32
+ return (
33
+ train.reset_index(drop=True),
34
+ val.reset_index(drop=True),
35
+ test.reset_index(drop=True),
36
+ )
37
+
38
+
39
+ def time_split(
40
+ df: pd.DataFrame,
41
+ train_ratio: float = 0.8,
42
+ val_ratio: float = 0.1,
43
+ ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
44
+ """Chronological train / val / test split — no shuffling."""
45
+ n = len(df)
46
+ train_end = int(n * train_ratio)
47
+ val_end = int(n * (train_ratio + val_ratio))
48
+ return (
49
+ df.iloc[:train_end].copy(),
50
+ df.iloc[train_end:val_end].copy(),
51
+ df.iloc[val_end:].copy(),
52
+ )
@@ -0,0 +1,13 @@
1
+ from meerax.eval.classification import ClassificationMetrics, evaluate_classifier
2
+ from meerax.eval.text import bleu_score, rouge_l
3
+ from meerax.eval.timeseries import TimeSeriesMetrics, adf_stationarity, evaluate_forecast
4
+
5
+ __all__ = [
6
+ "ClassificationMetrics",
7
+ "evaluate_classifier",
8
+ "TimeSeriesMetrics",
9
+ "evaluate_forecast",
10
+ "adf_stationarity",
11
+ "bleu_score",
12
+ "rouge_l",
13
+ ]
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import numpy as np
6
+
7
+
8
+ @dataclass
9
+ class ClassificationMetrics:
10
+ accuracy: float
11
+ precision: float
12
+ recall: float
13
+ f1: float
14
+ auc_roc: float | None
15
+ support: int
16
+
17
+ def __str__(self) -> str:
18
+ lines = [
19
+ f"Accuracy : {self.accuracy:.4f}",
20
+ f"Precision: {self.precision:.4f}",
21
+ f"Recall : {self.recall:.4f}",
22
+ f"F1 : {self.f1:.4f}",
23
+ ]
24
+ if self.auc_roc is not None:
25
+ lines.append(f"AUC-ROC : {self.auc_roc:.4f}")
26
+ lines.append(f"Support : {self.support}")
27
+ return "\n".join(lines)
28
+
29
+ def to_dict(self) -> dict[str, float | int | None]:
30
+ return {
31
+ "accuracy": self.accuracy,
32
+ "precision": self.precision,
33
+ "recall": self.recall,
34
+ "f1": self.f1,
35
+ "auc_roc": self.auc_roc,
36
+ "support": self.support,
37
+ }
38
+
39
+
40
+ def evaluate_classifier(
41
+ y_true: np.ndarray,
42
+ y_pred: np.ndarray,
43
+ y_prob: np.ndarray | None = None,
44
+ average: str = "binary",
45
+ ) -> ClassificationMetrics:
46
+ """Compute a standard classification metric bundle.
47
+
48
+ Args:
49
+ y_true: Ground-truth labels.
50
+ y_pred: Hard predictions.
51
+ y_prob: Probability estimates for AUC-ROC (1-D for binary, 2-D for multiclass).
52
+ average: Averaging strategy passed to sklearn ('binary', 'macro', 'weighted').
53
+ """
54
+ from sklearn.metrics import (
55
+ accuracy_score,
56
+ f1_score,
57
+ precision_score,
58
+ recall_score,
59
+ roc_auc_score,
60
+ )
61
+
62
+ auc: float | None = None
63
+ if y_prob is not None:
64
+ try:
65
+ prob = y_prob if y_prob.ndim == 1 else y_prob[:, 1]
66
+ auc = float(roc_auc_score(y_true, prob))
67
+ except ValueError:
68
+ pass
69
+
70
+ return ClassificationMetrics(
71
+ accuracy=float(accuracy_score(y_true, y_pred)),
72
+ precision=float(precision_score(y_true, y_pred, average=average, zero_division=0)),
73
+ recall=float(recall_score(y_true, y_pred, average=average, zero_division=0)),
74
+ f1=float(f1_score(y_true, y_pred, average=average, zero_division=0)),
75
+ auc_roc=auc,
76
+ support=int(len(y_true)),
77
+ )