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.
- meerax-1.0.0/LICENSE +21 -0
- meerax-1.0.0/PKG-INFO +187 -0
- meerax-1.0.0/README.md +137 -0
- meerax-1.0.0/VERSION +1 -0
- meerax-1.0.0/meerax/__init__.py +7 -0
- meerax-1.0.0/meerax/cli.py +73 -0
- meerax-1.0.0/meerax/data/__init__.py +12 -0
- meerax-1.0.0/meerax/data/imbalance.py +37 -0
- meerax-1.0.0/meerax/data/loader.py +48 -0
- meerax-1.0.0/meerax/data/split.py +52 -0
- meerax-1.0.0/meerax/eval/__init__.py +13 -0
- meerax-1.0.0/meerax/eval/classification.py +77 -0
- meerax-1.0.0/meerax/eval/text.py +42 -0
- meerax-1.0.0/meerax/eval/timeseries.py +62 -0
- meerax-1.0.0/meerax/llm/__init__.py +14 -0
- meerax-1.0.0/meerax/llm/base.py +51 -0
- meerax-1.0.0/meerax/llm/claude.py +69 -0
- meerax-1.0.0/meerax/llm/ollama.py +54 -0
- meerax-1.0.0/meerax/llm/openai_provider.py +70 -0
- meerax-1.0.0/meerax/llm/prompt.py +21 -0
- meerax-1.0.0/meerax/logging.py +20 -0
- meerax-1.0.0/meerax/report/__init__.py +3 -0
- meerax-1.0.0/meerax/report/builder.py +115 -0
- meerax-1.0.0/meerax/scaffold/__init__.py +0 -0
- meerax-1.0.0/meerax/scaffold/skeleton.py +128 -0
- meerax-1.0.0/meerax/scaffold/templates.py +131 -0
- meerax-1.0.0/meerax/vision/__init__.py +20 -0
- meerax-1.0.0/meerax/vision/dataset.py +49 -0
- meerax-1.0.0/meerax/vision/device.py +19 -0
- meerax-1.0.0/meerax/vision/gridplot.py +47 -0
- meerax-1.0.0/meerax/viz/__init__.py +3 -0
- meerax-1.0.0/meerax/viz/classification.py +51 -0
- meerax-1.0.0/meerax/viz/theme.py +42 -0
- meerax-1.0.0/meerax/viz/timeseries.py +40 -0
- meerax-1.0.0/meerax.egg-info/PKG-INFO +187 -0
- meerax-1.0.0/meerax.egg-info/SOURCES.txt +40 -0
- meerax-1.0.0/meerax.egg-info/dependency_links.txt +1 -0
- meerax-1.0.0/meerax.egg-info/entry_points.txt +2 -0
- meerax-1.0.0/meerax.egg-info/requires.txt +39 -0
- meerax-1.0.0/meerax.egg-info/top_level.txt +1 -0
- meerax-1.0.0/pyproject.toml +74 -0
- 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
|
+

|
|
54
|
+

|
|
55
|
+

|
|
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
|
+

|
|
4
|
+

|
|
5
|
+

|
|
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,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
|
+
)
|