smartml-assistant 2.0.0__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.
- smartml/__init__.py +576 -0
- smartml/assistant.py +898 -0
- smartml/assistant_ds.py +341 -0
- smartml/core/__init__.py +82 -0
- smartml/core/analyzer.py +402 -0
- smartml/core/anomaly.py +273 -0
- smartml/core/clustering.py +345 -0
- smartml/core/explainer.py +64 -0
- smartml/core/feature_engineer.py +283 -0
- smartml/core/feature_selector.py +295 -0
- smartml/core/forecaster.py +151 -0
- smartml/core/loaders.py +165 -0
- smartml/core/persistence.py +112 -0
- smartml/core/preprocessor.py +210 -0
- smartml/core/reducer.py +89 -0
- smartml/core/reinforcement.py +197 -0
- smartml/core/reporter.py +156 -0
- smartml/core/self_supervised.py +128 -0
- smartml/core/semi_supervised.py +144 -0
- smartml/core/stats.py +129 -0
- smartml/core/textutil.py +72 -0
- smartml/core/trainer.py +442 -0
- smartml/core/tuner.py +136 -0
- smartml/core/visualizer.py +458 -0
- smartml/core/wrangler.py +192 -0
- smartml/py.typed +1 -0
- smartml_assistant-2.0.0.dist-info/METADATA +131 -0
- smartml_assistant-2.0.0.dist-info/RECORD +31 -0
- smartml_assistant-2.0.0.dist-info/WHEEL +5 -0
- smartml_assistant-2.0.0.dist-info/licenses/LICENSE +21 -0
- smartml_assistant-2.0.0.dist-info/top_level.txt +1 -0
smartml/__init__.py
ADDED
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SmartML — Production-grade AutoML engine with a conversational Assistant.
|
|
3
|
+
|
|
4
|
+
Quick start
|
|
5
|
+
-----------
|
|
6
|
+
**One-line auto-pilot:**
|
|
7
|
+
|
|
8
|
+
import smartml as sml
|
|
9
|
+
|
|
10
|
+
result = sml.auto(df, target="Price")
|
|
11
|
+
result.predict(new_df)
|
|
12
|
+
result.report()
|
|
13
|
+
result.explain()
|
|
14
|
+
|
|
15
|
+
**Task-specific shortcuts:**
|
|
16
|
+
|
|
17
|
+
sml.regression(df, target="SalePrice")
|
|
18
|
+
sml.classification(df, target="Species")
|
|
19
|
+
sml.clustering(df)
|
|
20
|
+
|
|
21
|
+
**Data loading:**
|
|
22
|
+
|
|
23
|
+
df = sml.load("data/train.csv")
|
|
24
|
+
df = sml.load("report.parquet")
|
|
25
|
+
|
|
26
|
+
**Conversational assistant:**
|
|
27
|
+
|
|
28
|
+
assistant = sml.Assistant(df, target="Price")
|
|
29
|
+
assistant.ask("Clean my dataset")
|
|
30
|
+
assistant.ask("Train the best regression model")
|
|
31
|
+
assistant.ask("Why did you choose this algorithm?")
|
|
32
|
+
assistant.ask("Show feature importance")
|
|
33
|
+
assistant.ask("Generate a report")
|
|
34
|
+
assistant.ask("Predict on this CSV file", file_path="new_data.csv")
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import logging
|
|
40
|
+
from typing import Any, Optional
|
|
41
|
+
|
|
42
|
+
import pandas as pd
|
|
43
|
+
|
|
44
|
+
from .assistant import Assistant, Session
|
|
45
|
+
from .core import (
|
|
46
|
+
analyzer,
|
|
47
|
+
preprocessor,
|
|
48
|
+
trainer as trainer_mod,
|
|
49
|
+
explainer,
|
|
50
|
+
visualizer,
|
|
51
|
+
reporter,
|
|
52
|
+
persistence,
|
|
53
|
+
feature_engineer,
|
|
54
|
+
feature_selector,
|
|
55
|
+
loaders,
|
|
56
|
+
clustering as clustering_mod,
|
|
57
|
+
anomaly,
|
|
58
|
+
semi_supervised as semi_supervised_mod,
|
|
59
|
+
reinforcement as reinforcement_mod,
|
|
60
|
+
self_supervised as self_supervised_mod,
|
|
61
|
+
forecaster as forecaster_mod,
|
|
62
|
+
reducer as reducer_mod,
|
|
63
|
+
stats as stats_mod,
|
|
64
|
+
wrangler as wrangler_mod,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
__version__ = "2.0.0"
|
|
68
|
+
__author__ = "SmartML Contributors"
|
|
69
|
+
__license__ = "MIT"
|
|
70
|
+
|
|
71
|
+
# Set up a library-level logger that is silent by default (NullHandler).
|
|
72
|
+
# Callers can configure logging at the application level.
|
|
73
|
+
logging.getLogger("smartml").addHandler(logging.NullHandler())
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
# High-level API for all ML Paradigms
|
|
77
|
+
"auto",
|
|
78
|
+
"regression",
|
|
79
|
+
"classification",
|
|
80
|
+
"clustering",
|
|
81
|
+
"clustering_pipeline",
|
|
82
|
+
"semi_supervised",
|
|
83
|
+
"reinforcement",
|
|
84
|
+
"self_supervised",
|
|
85
|
+
"forecast",
|
|
86
|
+
"load",
|
|
87
|
+
# Core classes
|
|
88
|
+
"Assistant",
|
|
89
|
+
"Session",
|
|
90
|
+
"SmartMLResult",
|
|
91
|
+
# Submodules
|
|
92
|
+
"analyzer",
|
|
93
|
+
"preprocessor",
|
|
94
|
+
"feature_engineer",
|
|
95
|
+
"feature_selector",
|
|
96
|
+
"trainer_mod",
|
|
97
|
+
"explainer",
|
|
98
|
+
"visualizer",
|
|
99
|
+
"reporter",
|
|
100
|
+
"persistence",
|
|
101
|
+
"loaders",
|
|
102
|
+
"clustering_mod",
|
|
103
|
+
"anomaly",
|
|
104
|
+
"semi_supervised_mod",
|
|
105
|
+
"reinforcement_mod",
|
|
106
|
+
"self_supervised_mod",
|
|
107
|
+
"forecaster_mod",
|
|
108
|
+
"reducer_mod",
|
|
109
|
+
"stats_mod",
|
|
110
|
+
"wrangler_mod",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# =========================================================================== #
|
|
115
|
+
# SmartMLResult — unified result object returned by high-level functions
|
|
116
|
+
# =========================================================================== #
|
|
117
|
+
|
|
118
|
+
class SmartMLResult:
|
|
119
|
+
"""Encapsulates a completed AutoML pipeline run.
|
|
120
|
+
|
|
121
|
+
Returned by :func:`auto`, :func:`regression`, :func:`classification`, and
|
|
122
|
+
:func:`clustering_pipeline`. Provides a consistent interface for
|
|
123
|
+
prediction, persistence, reporting, and explanation.
|
|
124
|
+
|
|
125
|
+
Attributes:
|
|
126
|
+
task: ML task type (``"classification"``, ``"regression"``,
|
|
127
|
+
``"clustering"``, etc.).
|
|
128
|
+
training_result: :class:`~smartml.core.trainer.TrainingResult` (None
|
|
129
|
+
for clustering).
|
|
130
|
+
cleaning_result: :class:`~smartml.core.preprocessor.CleaningResult`.
|
|
131
|
+
dataset_report: :class:`~smartml.core.analyzer.DatasetReport`.
|
|
132
|
+
clustering_result: :class:`~smartml.core.clustering.ClusteringResult`
|
|
133
|
+
(None for supervised tasks).
|
|
134
|
+
output_dir: Directory where reports and plots are written.
|
|
135
|
+
target: Target column name (None for unsupervised tasks).
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self,
|
|
140
|
+
task: str,
|
|
141
|
+
training_result: Optional[Any] = None,
|
|
142
|
+
cleaning_result: Optional[Any] = None,
|
|
143
|
+
dataset_report: Optional[Any] = None,
|
|
144
|
+
clustering_result: Optional[Any] = None,
|
|
145
|
+
output_dir: str = "smartml_output",
|
|
146
|
+
target: Optional[str] = None,
|
|
147
|
+
):
|
|
148
|
+
self.task = task
|
|
149
|
+
self.training_result = training_result
|
|
150
|
+
self.cleaning_result = cleaning_result
|
|
151
|
+
self.dataset_report = dataset_report
|
|
152
|
+
self.clustering_result = clustering_result
|
|
153
|
+
self.output_dir = output_dir
|
|
154
|
+
self.target = target
|
|
155
|
+
|
|
156
|
+
# ------------------------------------------------------------------ #
|
|
157
|
+
# Prediction
|
|
158
|
+
# ------------------------------------------------------------------ #
|
|
159
|
+
|
|
160
|
+
def predict(self, data: pd.DataFrame) -> pd.Series:
|
|
161
|
+
"""Generate predictions for new data.
|
|
162
|
+
|
|
163
|
+
The same preprocessing pipeline (imputation, encoding, scaling)
|
|
164
|
+
that was applied during training is applied here, ensuring
|
|
165
|
+
consistent predictions on unseen data.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
data: New feature DataFrame (must contain the same columns as
|
|
169
|
+
the training data, minus the target column).
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Series of predictions indexed to match *data*.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
RuntimeError: If the result is from a clustering pipeline
|
|
176
|
+
(use ``clustering_result.labels`` instead).
|
|
177
|
+
"""
|
|
178
|
+
if self.training_result is None:
|
|
179
|
+
raise RuntimeError(
|
|
180
|
+
"No trained model available. For clustering results, use "
|
|
181
|
+
".clustering_result.labels instead."
|
|
182
|
+
)
|
|
183
|
+
assert self.cleaning_result is not None
|
|
184
|
+
X_new = preprocessor.Preprocessor.transform_new(data, self.cleaning_result)
|
|
185
|
+
preds = self.training_result.best_model.predict(X_new)
|
|
186
|
+
return pd.Series(preds, index=data.index, name="prediction")
|
|
187
|
+
|
|
188
|
+
# ------------------------------------------------------------------ #
|
|
189
|
+
# Save / Load
|
|
190
|
+
# ------------------------------------------------------------------ #
|
|
191
|
+
|
|
192
|
+
def save(self, path: str = "smartml_model") -> str:
|
|
193
|
+
"""Save the trained model bundle to disk.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
path: Destination path (without extension; ``.joblib`` is added).
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Full path of the saved file.
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
RuntimeError: If no trained model is available.
|
|
203
|
+
"""
|
|
204
|
+
if self.training_result is None:
|
|
205
|
+
raise RuntimeError("No trained model to save.")
|
|
206
|
+
tr = self.training_result
|
|
207
|
+
return persistence.save_model(
|
|
208
|
+
path=path,
|
|
209
|
+
model=tr.best_model,
|
|
210
|
+
feature_names=tr.feature_names,
|
|
211
|
+
target=self.target or "",
|
|
212
|
+
task=tr.task,
|
|
213
|
+
encoders=self.cleaning_result.encoders if self.cleaning_result else {},
|
|
214
|
+
scaler=self.cleaning_result.scaler if self.cleaning_result else None,
|
|
215
|
+
model_name=tr.best_name,
|
|
216
|
+
impute_values=self.cleaning_result.impute_values if self.cleaning_result else {},
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# ------------------------------------------------------------------ #
|
|
220
|
+
# Explanation
|
|
221
|
+
# ------------------------------------------------------------------ #
|
|
222
|
+
|
|
223
|
+
def explain(self) -> str:
|
|
224
|
+
"""Return a natural-language feature importance explanation.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Explanation string from the explainer module.
|
|
228
|
+
|
|
229
|
+
Raises:
|
|
230
|
+
RuntimeError: If no trained model is available.
|
|
231
|
+
"""
|
|
232
|
+
if self.training_result is None:
|
|
233
|
+
raise RuntimeError("No trained model to explain.")
|
|
234
|
+
tr = self.training_result
|
|
235
|
+
imp = explainer.explain_feature_importance(
|
|
236
|
+
model=tr.best_model,
|
|
237
|
+
X_test=tr.X_test,
|
|
238
|
+
y_test=tr.y_test,
|
|
239
|
+
feature_names=tr.feature_names,
|
|
240
|
+
task=tr.task,
|
|
241
|
+
)
|
|
242
|
+
return imp.narrative
|
|
243
|
+
|
|
244
|
+
# ------------------------------------------------------------------ #
|
|
245
|
+
# Reporting
|
|
246
|
+
# ------------------------------------------------------------------ #
|
|
247
|
+
|
|
248
|
+
def report(self, fmt: str = "html") -> str:
|
|
249
|
+
"""Generate an HTML or Markdown report.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
fmt: ``"html"`` or ``"markdown"`` (``"md"`` also accepted).
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Path to the generated report file.
|
|
256
|
+
"""
|
|
257
|
+
import os
|
|
258
|
+
|
|
259
|
+
# Build a minimal session-like object the reporter understands
|
|
260
|
+
class _FakeSession:
|
|
261
|
+
def __init__(self, result: "SmartMLResult"):
|
|
262
|
+
self.dataset_report = result.dataset_report
|
|
263
|
+
self.cleaning_result = result.cleaning_result
|
|
264
|
+
self.training_result = result.training_result
|
|
265
|
+
self.target = result.target
|
|
266
|
+
self.visualizations: dict = {}
|
|
267
|
+
self.importance_result = None
|
|
268
|
+
|
|
269
|
+
session = _FakeSession(self)
|
|
270
|
+
|
|
271
|
+
os.makedirs(self.output_dir, exist_ok=True)
|
|
272
|
+
if fmt in ("markdown", "md"):
|
|
273
|
+
path = os.path.join(self.output_dir, "report.md")
|
|
274
|
+
return reporter.generate_markdown_report(session, path)
|
|
275
|
+
path = os.path.join(self.output_dir, "report.html")
|
|
276
|
+
return reporter.generate_html_report(session, path)
|
|
277
|
+
|
|
278
|
+
# ------------------------------------------------------------------ #
|
|
279
|
+
# String representation
|
|
280
|
+
# ------------------------------------------------------------------ #
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def summary(self) -> str:
|
|
284
|
+
"""Executive summary and leaderboard of the completed run."""
|
|
285
|
+
if self.training_result:
|
|
286
|
+
tr = self.training_result
|
|
287
|
+
lines = [
|
|
288
|
+
f"SmartML {self.task.capitalize()} Result:",
|
|
289
|
+
f" Best Model: {tr.best_name}",
|
|
290
|
+
f" Summary: {tr.best_reason}",
|
|
291
|
+
f" Leaderboard:"
|
|
292
|
+
]
|
|
293
|
+
for i, entry in enumerate(tr.leaderboard, 1):
|
|
294
|
+
marker = " <- best" if i == 1 else ""
|
|
295
|
+
metrics_str = ", ".join(f"{k}={v:.4f}" for k, v in list(entry.test_metrics.items())[:3])
|
|
296
|
+
lines.append(f" {i}. {entry.name}: CV={entry.cv_score:.4f}+/-{entry.cv_std:.4f} | {metrics_str}{marker}")
|
|
297
|
+
return "\n".join(lines)
|
|
298
|
+
if self.clustering_result:
|
|
299
|
+
cr = self.clustering_result
|
|
300
|
+
return f"SmartML Clustering Result:\n Algorithm: {cr.algorithm}\n Clusters: {cr.n_clusters}\n Silhouette: {cr.silhouette:.4f}"
|
|
301
|
+
return f"SmartML Result (task={self.task!r})"
|
|
302
|
+
|
|
303
|
+
def __repr__(self) -> str: # noqa: D105
|
|
304
|
+
if self.training_result:
|
|
305
|
+
return (
|
|
306
|
+
f"SmartMLResult(task={self.task!r}, "
|
|
307
|
+
f"best_model={self.training_result.best_name!r}, "
|
|
308
|
+
f"cv_score={self.training_result.leaderboard[0].cv_score:.4f})"
|
|
309
|
+
)
|
|
310
|
+
if self.clustering_result:
|
|
311
|
+
return (
|
|
312
|
+
f"SmartMLResult(task='clustering', "
|
|
313
|
+
f"algorithm={self.clustering_result.algorithm!r}, "
|
|
314
|
+
f"n_clusters={self.clustering_result.n_clusters})"
|
|
315
|
+
)
|
|
316
|
+
return f"SmartMLResult(task={self.task!r})"
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
# =========================================================================== #
|
|
320
|
+
# Top-level convenience functions
|
|
321
|
+
# =========================================================================== #
|
|
322
|
+
|
|
323
|
+
def auto(
|
|
324
|
+
data: pd.DataFrame,
|
|
325
|
+
target: str,
|
|
326
|
+
output_dir: str = "smartml_output",
|
|
327
|
+
task: Optional[str] = None,
|
|
328
|
+
) -> SmartMLResult:
|
|
329
|
+
"""Full AutoML pipeline: detect task → clean → train → return result.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
data: Input DataFrame.
|
|
333
|
+
target: Name of the target column.
|
|
334
|
+
output_dir: Directory for reports and saved models.
|
|
335
|
+
task: Force a specific task (``"classification"`` or
|
|
336
|
+
``"regression"``). Auto-detected if None.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
:class:`SmartMLResult` ready for ``.predict()``, ``.save()``,
|
|
340
|
+
``.explain()``, and ``.report()``.
|
|
341
|
+
|
|
342
|
+
Example::
|
|
343
|
+
|
|
344
|
+
import smartml as sml
|
|
345
|
+
from sklearn.datasets import load_iris
|
|
346
|
+
import pandas as pd
|
|
347
|
+
|
|
348
|
+
df = load_iris(as_frame=True).frame
|
|
349
|
+
result = sml.auto(df, target="target")
|
|
350
|
+
print(result)
|
|
351
|
+
"""
|
|
352
|
+
logger = logging.getLogger("smartml")
|
|
353
|
+
logger.info("SmartML.auto: analysing dataset (%d rows, %d cols)", *data.shape)
|
|
354
|
+
|
|
355
|
+
report = analyzer.analyze(data, target=target)
|
|
356
|
+
detected_task = task or report.task or "classification"
|
|
357
|
+
|
|
358
|
+
logger.info("Task: %s — %s", detected_task, report.task_reason)
|
|
359
|
+
|
|
360
|
+
pre = preprocessor.Preprocessor()
|
|
361
|
+
cleaning = pre.clean(data, target=target)
|
|
362
|
+
logger.info("Cleaning: %d step(s) applied.", len(cleaning.steps))
|
|
363
|
+
|
|
364
|
+
X = cleaning.df.drop(columns=[target], errors="ignore")
|
|
365
|
+
y = cleaning.df[target]
|
|
366
|
+
|
|
367
|
+
tr = trainer_mod.Trainer(task=detected_task)
|
|
368
|
+
training = tr.run(X, y)
|
|
369
|
+
logger.info(
|
|
370
|
+
"Training complete. Best model: %s (CV=%.4f)",
|
|
371
|
+
training.best_name,
|
|
372
|
+
training.leaderboard[0].cv_score,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return SmartMLResult(
|
|
376
|
+
task=detected_task,
|
|
377
|
+
training_result=training,
|
|
378
|
+
cleaning_result=cleaning,
|
|
379
|
+
dataset_report=report,
|
|
380
|
+
output_dir=output_dir,
|
|
381
|
+
target=target,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def regression(
|
|
386
|
+
data: pd.DataFrame,
|
|
387
|
+
target: str,
|
|
388
|
+
output_dir: str = "smartml_output",
|
|
389
|
+
) -> SmartMLResult:
|
|
390
|
+
"""Run a forced regression pipeline.
|
|
391
|
+
|
|
392
|
+
Equivalent to ``auto(data, target, task="regression")``.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
data: Input DataFrame.
|
|
396
|
+
target: Name of the continuous target column.
|
|
397
|
+
output_dir: Directory for outputs.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
:class:`SmartMLResult`.
|
|
401
|
+
"""
|
|
402
|
+
return auto(data, target=target, output_dir=output_dir, task="regression")
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def classification(
|
|
406
|
+
data: pd.DataFrame,
|
|
407
|
+
target: str,
|
|
408
|
+
output_dir: str = "smartml_output",
|
|
409
|
+
) -> SmartMLResult:
|
|
410
|
+
"""Run a forced classification pipeline.
|
|
411
|
+
|
|
412
|
+
Equivalent to ``auto(data, target, task="classification")``.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
data: Input DataFrame.
|
|
416
|
+
target: Name of the categorical/label target column.
|
|
417
|
+
output_dir: Directory for outputs.
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
:class:`SmartMLResult`.
|
|
421
|
+
"""
|
|
422
|
+
return auto(data, target=target, output_dir=output_dir, task="classification")
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def clustering_pipeline(
|
|
426
|
+
data: pd.DataFrame,
|
|
427
|
+
algorithm: str = "auto",
|
|
428
|
+
n_clusters: Optional[int] = None,
|
|
429
|
+
output_dir: str = "smartml_output",
|
|
430
|
+
) -> SmartMLResult:
|
|
431
|
+
"""Run an unsupervised clustering pipeline.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
data: Input DataFrame (all columns are used as features).
|
|
435
|
+
algorithm: Clustering algorithm name (``"auto"``, ``"kmeans"``,
|
|
436
|
+
``"dbscan"``, ``"agglomerative"``, ``"gmm"``).
|
|
437
|
+
n_clusters: Number of clusters. Auto-selected if None.
|
|
438
|
+
output_dir: Directory for outputs.
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
:class:`SmartMLResult` with a populated ``clustering_result``.
|
|
442
|
+
"""
|
|
443
|
+
logger = logging.getLogger("smartml")
|
|
444
|
+
logger.info("SmartML.clustering: dataset (%d rows, %d cols)", *data.shape)
|
|
445
|
+
|
|
446
|
+
ct = clustering_mod.ClusteringTrainer(
|
|
447
|
+
algorithm=algorithm, n_clusters=n_clusters
|
|
448
|
+
)
|
|
449
|
+
result = ct.run(data)
|
|
450
|
+
logger.info(
|
|
451
|
+
"Clustering complete: %s found %d clusters (silhouette=%.3f).",
|
|
452
|
+
result.algorithm, result.n_clusters, result.silhouette,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
return SmartMLResult(
|
|
456
|
+
task="clustering",
|
|
457
|
+
clustering_result=result,
|
|
458
|
+
output_dir=output_dir,
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def load(path: str, **kwargs: Any) -> pd.DataFrame:
|
|
463
|
+
"""Load a dataset from a file (CSV, Excel, JSON, Parquet, Feather, …).
|
|
464
|
+
|
|
465
|
+
This is a thin wrapper around :func:`smartml.core.loaders.load` exposed
|
|
466
|
+
at the package level for convenience.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
path: Path to the data file.
|
|
470
|
+
**kwargs: Forwarded to the underlying pandas reader.
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
Loaded DataFrame.
|
|
474
|
+
|
|
475
|
+
Example::
|
|
476
|
+
|
|
477
|
+
import smartml as sml
|
|
478
|
+
|
|
479
|
+
df = sml.load("data/titanic.csv")
|
|
480
|
+
df = sml.load("sales_2024.parquet")
|
|
481
|
+
"""
|
|
482
|
+
return loaders.load(path, **kwargs)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def clustering(
|
|
486
|
+
data: pd.DataFrame,
|
|
487
|
+
algorithm: str = "auto",
|
|
488
|
+
n_clusters: Optional[int] = None,
|
|
489
|
+
output_dir: str = "smartml_output",
|
|
490
|
+
) -> SmartMLResult:
|
|
491
|
+
"""Run an unsupervised clustering pipeline (alias of clustering_pipeline)."""
|
|
492
|
+
return clustering_pipeline(data, algorithm=algorithm, n_clusters=n_clusters, output_dir=output_dir)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def semi_supervised(
|
|
496
|
+
data: pd.DataFrame,
|
|
497
|
+
target: str,
|
|
498
|
+
algorithm: str = "auto",
|
|
499
|
+
unlabeled_identifier: int = -1,
|
|
500
|
+
random_state: int = 42,
|
|
501
|
+
) -> semi_supervised_mod.SemiSupervisedResult:
|
|
502
|
+
"""Run semi-supervised learning on partially labeled tabular data.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
data: Input DataFrame.
|
|
506
|
+
target: Target column containing labeled entries and unlabeled marks (-1 or NaN).
|
|
507
|
+
algorithm: 'self_training', 'label_spreading', 'label_propagation', or 'auto'.
|
|
508
|
+
unlabeled_identifier: Marker for unlabeled rows (default -1).
|
|
509
|
+
random_state: Seed for reproducibility.
|
|
510
|
+
"""
|
|
511
|
+
if target not in data.columns:
|
|
512
|
+
raise KeyError(f"Target column '{target}' not found.")
|
|
513
|
+
X = data.drop(columns=[target], errors="ignore")
|
|
514
|
+
y = data[target]
|
|
515
|
+
trainer = semi_supervised_mod.SemiSupervisedTrainer(
|
|
516
|
+
algorithm=algorithm, unlabeled_identifier=unlabeled_identifier, random_state=random_state
|
|
517
|
+
)
|
|
518
|
+
return trainer.fit(X, y)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def reinforcement(
|
|
522
|
+
data: pd.DataFrame,
|
|
523
|
+
action: str,
|
|
524
|
+
reward: str,
|
|
525
|
+
context_cols: Optional[list[str]] = None,
|
|
526
|
+
strategy: str = "auto",
|
|
527
|
+
**kwargs: Any,
|
|
528
|
+
) -> reinforcement_mod.BanditResult:
|
|
529
|
+
"""Run offline tabular contextual bandit / reinforcement decision optimization.
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
data: Logged interaction dataset.
|
|
533
|
+
action: Column indicating action/arm taken.
|
|
534
|
+
reward: Column indicating observed outcome/reward.
|
|
535
|
+
context_cols: Optional list of state/feature columns for contextual bandits (LinUCB).
|
|
536
|
+
strategy: 'linucb', 'thompson', 'epsilon_greedy', or 'auto'.
|
|
537
|
+
"""
|
|
538
|
+
opt = reinforcement_mod.TabularBanditOptimizer(strategy=strategy, **kwargs)
|
|
539
|
+
return opt.optimize(data, action_col=action, reward_col=reward, context_cols=context_cols)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def self_supervised(
|
|
543
|
+
data: pd.DataFrame,
|
|
544
|
+
target: Optional[str] = None,
|
|
545
|
+
embedding_dim: Optional[int] = None,
|
|
546
|
+
**kwargs: Any,
|
|
547
|
+
) -> self_supervised_mod.SelfSupervisedResult:
|
|
548
|
+
"""Extract dense tabular latent representations using Masked/Denoising Autoencoders.
|
|
549
|
+
|
|
550
|
+
Args:
|
|
551
|
+
data: Tabular dataset.
|
|
552
|
+
target: Optional target column to isolate and exclude from representations.
|
|
553
|
+
embedding_dim: Latent representation size (default: half of feature count, min 2).
|
|
554
|
+
"""
|
|
555
|
+
learner = self_supervised_mod.TabularSelfSupervisedLearner(embedding_dim=embedding_dim, **kwargs)
|
|
556
|
+
return learner.fit_transform(data, target_col=target)
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def forecast(
|
|
560
|
+
data: pd.DataFrame,
|
|
561
|
+
target: str,
|
|
562
|
+
date_col: Optional[str] = None,
|
|
563
|
+
horizon: int = 5,
|
|
564
|
+
**kwargs: Any,
|
|
565
|
+
) -> forecaster_mod.ForecastResult:
|
|
566
|
+
"""Run multi-step recursive time series forecasting with automatic lag features.
|
|
567
|
+
|
|
568
|
+
Args:
|
|
569
|
+
data: Time series or sequential DataFrame.
|
|
570
|
+
target: Numeric column to forecast.
|
|
571
|
+
date_col: Optional date/timestamp column. Auto-detected if omitted.
|
|
572
|
+
horizon: Number of future periods/steps to forecast.
|
|
573
|
+
"""
|
|
574
|
+
fc = forecaster_mod.TabularForecaster(**kwargs)
|
|
575
|
+
return fc.forecast(data, target_col=target, date_col=date_col, horizon=horizon)
|
|
576
|
+
|