mlschema 0.1.6__tar.gz → 0.2.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.
- {mlschema-0.1.6 → mlschema-0.2.0}/.gitignore +6 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/CONTRIBUTING.md +2 -2
- mlschema-0.2.0/PKG-INFO +450 -0
- mlschema-0.2.0/README.md +406 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/SECURITY.md +3 -3
- {mlschema-0.1.6 → mlschema-0.2.0}/THIRD_PARTY_LICENSES.md +10 -10
- mlschema-0.2.0/docs/changelog.md +310 -0
- mlschema-0.2.0/docs/index.md +242 -0
- mlschema-0.2.0/docs/installation.md +228 -0
- mlschema-0.2.0/docs/schema-standard.md +618 -0
- mlschema-0.2.0/docs/usage.md +416 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/pyproject.toml +78 -108
- mlschema-0.2.0/src/mlschema/__init__.py +23 -0
- mlschema-0.2.0/src/mlschema/core/__init__.py +41 -0
- mlschema-0.2.0/src/mlschema/core/app/__init__.py +13 -0
- mlschema-0.2.0/src/mlschema/core/app/inference.py +191 -0
- mlschema-0.2.0/src/mlschema/core/app/kind.py +106 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/domain/__init__.py +1 -2
- mlschema-0.2.0/src/mlschema/core/exceptions/__init__.py +27 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/exceptions/_base.py +9 -9
- mlschema-0.2.0/src/mlschema/core/exceptions/empty_dataframe.py +30 -0
- mlschema-0.2.0/src/mlschema/core/exceptions/field_builder.py +17 -0
- mlschema-0.2.0/src/mlschema/core/exceptions/field_kind.py +37 -0
- mlschema-0.2.0/src/mlschema/core/exceptions/field_kind_conflict.py +28 -0
- mlschema-0.2.0/src/mlschema/core/exceptions/field_service.py +37 -0
- mlschema-0.2.0/src/mlschema/core/exceptions/unknown_field_kind.py +28 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/util/dtypes.py +0 -1
- mlschema-0.2.0/src/mlschema/mls.py +5 -0
- mlschema-0.2.0/src/mlschema/strategies/__init__.py +23 -0
- mlschema-0.2.0/src/mlschema/strategies/app/__init__.py +25 -0
- mlschema-0.2.0/src/mlschema/strategies/app/_base.py +31 -0
- mlschema-0.2.0/src/mlschema/strategies/app/boolean.py +33 -0
- mlschema-0.2.0/src/mlschema/strategies/app/category.py +38 -0
- mlschema-0.2.0/src/mlschema/strategies/app/date.py +35 -0
- mlschema-0.2.0/src/mlschema/strategies/app/factory.py +44 -0
- mlschema-0.2.0/src/mlschema/strategies/app/number.py +36 -0
- mlschema-0.2.0/src/mlschema/strategies/app/series.py +86 -0
- mlschema-0.2.0/src/mlschema/strategies/app/text.py +30 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/__init__.py +1 -8
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/field_types.py +2 -2
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/series_field.py +3 -3
- mlschema-0.2.0/test/architecture/test_public_surface.py +103 -0
- mlschema-0.2.0/test/architecture/test_test_suite_structure.py +43 -0
- mlschema-0.2.0/test/integration/test_infer_schema_workflows.py +89 -0
- mlschema-0.2.0/test/integration/test_schema_output_matrix.py +67 -0
- mlschema-0.2.0/test/load/test_infer_schema_load.py +43 -0
- mlschema-0.2.0/test/unit/test_base_field_contract.py +53 -0
- mlschema-0.2.0/test/unit/test_builtin_builders.py +99 -0
- mlschema-0.2.0/test/unit/test_builtin_dtype_matrix.py +134 -0
- mlschema-0.2.0/test/unit/test_builtin_edge_cases.py +127 -0
- mlschema-0.2.0/test/unit/test_dtype_normalization.py +68 -0
- mlschema-0.2.0/test/unit/test_factory_and_context.py +49 -0
- mlschema-0.2.0/test/unit/test_field_models.py +199 -0
- mlschema-0.2.0/test/unit/test_inference_errors.py +120 -0
- mlschema-0.2.0/test/unit/test_kind_api.py +43 -0
- mlschema-0.1.6/PKG-INFO +0 -308
- mlschema-0.1.6/README.md +0 -262
- mlschema-0.1.6/docs/changelog.md +0 -115
- mlschema-0.1.6/docs/index.md +0 -91
- mlschema-0.1.6/docs/installation.md +0 -123
- mlschema-0.1.6/docs/schema-standard.md +0 -488
- mlschema-0.1.6/docs/usage.md +0 -247
- mlschema-0.1.6/src/mlschema/__init__.py +0 -36
- mlschema-0.1.6/src/mlschema/core/__init__.py +0 -36
- mlschema-0.1.6/src/mlschema/core/app/__init__.py +0 -7
- mlschema-0.1.6/src/mlschema/core/app/registry.py +0 -115
- mlschema-0.1.6/src/mlschema/core/app/service.py +0 -123
- mlschema-0.1.6/src/mlschema/core/app/strategy.py +0 -132
- mlschema-0.1.6/src/mlschema/core/domain/base_report.py +0 -28
- mlschema-0.1.6/src/mlschema/core/exceptions/__init__.py +0 -24
- mlschema-0.1.6/src/mlschema/core/exceptions/registry.py +0 -74
- mlschema-0.1.6/src/mlschema/core/exceptions/service.py +0 -82
- mlschema-0.1.6/src/mlschema/mls.py +0 -70
- mlschema-0.1.6/src/mlschema/strategies/__init__.py +0 -46
- mlschema-0.1.6/src/mlschema/strategies/app/__init__.py +0 -17
- mlschema-0.1.6/src/mlschema/strategies/app/boolean_strategy.py +0 -34
- mlschema-0.1.6/src/mlschema/strategies/app/category_strategy.py +0 -56
- mlschema-0.1.6/src/mlschema/strategies/app/date_strategy.py +0 -44
- mlschema-0.1.6/src/mlschema/strategies/app/number_strategy.py +0 -63
- mlschema-0.1.6/src/mlschema/strategies/app/series_strategy.py +0 -150
- mlschema-0.1.6/src/mlschema/strategies/app/text_strategy.py +0 -45
- mlschema-0.1.6/src/mlschema/strategies/domain/classifier_report.py +0 -26
- mlschema-0.1.6/src/mlschema/strategies/domain/regressor_report.py +0 -26
- mlschema-0.1.6/src/mlschema/strategies/domain/report_types.py +0 -8
- mlschema-0.1.6/test/integration/test_mlschema.py +0 -839
- mlschema-0.1.6/test/unit/strategies/test_boolean_strategy.py +0 -218
- mlschema-0.1.6/test/unit/strategies/test_category_strategy.py +0 -412
- mlschema-0.1.6/test/unit/strategies/test_date_strategy.py +0 -464
- mlschema-0.1.6/test/unit/strategies/test_number_strategy.py +0 -573
- mlschema-0.1.6/test/unit/strategies/test_text_strategy.py +0 -585
- mlschema-0.1.6/test/unit/test_mls.py +0 -519
- {mlschema-0.1.6 → mlschema-0.2.0}/AUTHORS.md +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/LICENSE +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/docs/reference.md +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/domain/base_field.py +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/util/__init__.py +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/py.typed +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/boolean_field.py +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/category_field.py +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/date_field.py +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/number_field.py +0 -0
- {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/text_field.py +0 -0
|
@@ -329,8 +329,8 @@ What actually happens
|
|
|
329
329
|
## Environment
|
|
330
330
|
- OS: [OS independent]
|
|
331
331
|
- Python version: [e.g., 3.14.0]
|
|
332
|
-
- MLSchema version: [e.g., 0.
|
|
333
|
-
- pandas version: [e.g.,
|
|
332
|
+
- MLSchema version: [e.g., 0.2.0]
|
|
333
|
+
- pandas version: [e.g., 3.0.3]
|
|
334
334
|
|
|
335
335
|
## Additional Context
|
|
336
336
|
Screenshots, error messages, stack traces, etc.
|
mlschema-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mlschema
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Infer validated, frontend-ready field schemas from pandas DataFrames.
|
|
5
|
+
Project-URL: Homepage, https://github.com/UlloaSP/mlschema
|
|
6
|
+
Project-URL: Documentation, https://ulloasp.github.io/mlschema/
|
|
7
|
+
Project-URL: Repository, https://github.com/UlloaSP/mlschema
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/UlloaSP/mlschema/issues
|
|
9
|
+
Project-URL: Source Code, https://github.com/UlloaSP/mlschema
|
|
10
|
+
Project-URL: Changelog, https://ulloasp.github.io/mlschema/changelog/
|
|
11
|
+
Project-URL: Discussions, https://github.com/UlloaSP/mlschema/discussions
|
|
12
|
+
Project-URL: Funding, https://github.com/sponsors/UlloaSP
|
|
13
|
+
Project-URL: Release Notes, https://github.com/UlloaSP/mlschema/releases
|
|
14
|
+
Author-email: Pablo Ulloa Santín <pablo.ulloa.santin@udc.es>
|
|
15
|
+
Maintainer-email: Pablo Ulloa Santín <pablo.ulloa.santin@udc.es>
|
|
16
|
+
License: MIT
|
|
17
|
+
License-File: AUTHORS.md
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Keywords: data-contracts,data-science,data-validation,dataframe,field-schema,form-generation,frontend-forms,frontend-integration,json,json-schema,machine-learning,mlops,pandas,pydantic,schema-inference,validation
|
|
20
|
+
Classifier: Development Status :: 4 - Beta
|
|
21
|
+
Classifier: Framework :: Pydantic
|
|
22
|
+
Classifier: Framework :: Pydantic :: 2
|
|
23
|
+
Classifier: Intended Audience :: Developers
|
|
24
|
+
Classifier: Intended Audience :: Information Technology
|
|
25
|
+
Classifier: Intended Audience :: Science/Research
|
|
26
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
27
|
+
Classifier: Natural Language :: English
|
|
28
|
+
Classifier: Operating System :: OS Independent
|
|
29
|
+
Classifier: Programming Language :: Python :: 3
|
|
30
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
32
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
33
|
+
Classifier: Topic :: Scientific/Engineering
|
|
34
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
35
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
36
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
37
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
38
|
+
Classifier: Topic :: Utilities
|
|
39
|
+
Classifier: Typing :: Typed
|
|
40
|
+
Requires-Python: <3.15,>=3.14
|
|
41
|
+
Requires-Dist: pandas<4.0.0,>=3.0.3
|
|
42
|
+
Requires-Dist: pydantic<3.0.0,>=2.13.4
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# MLSchema
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/mlschema/)
|
|
48
|
+
[](https://pypi.org/project/mlschema/)
|
|
49
|
+
[](https://github.com/UlloaSP/mlschema/actions/workflows/ci.yml)
|
|
50
|
+
[](https://github.com/UlloaSP/mlschema/blob/main/LICENSE)
|
|
51
|
+
|
|
52
|
+
> Turn pandas DataFrames into validated, front-end-ready field schemas.
|
|
53
|
+
|
|
54
|
+
`mlschema` is a lightweight Python SDK for deriving JSON-serialisable field contracts from tabular data. It is designed for model inputs, prediction forms, review tools, annotation workflows, dashboards, and any frontend that needs to render fields from a `pandas.DataFrame` without hand-writing the same schema twice.
|
|
55
|
+
|
|
56
|
+
It pairs naturally with [mlform](https://github.com/UlloaSP/mlform), but the generated schema is plain JSON-compatible data and can be consumed by any frontend or service layer.
|
|
57
|
+
|
|
58
|
+
## Why MLSchema
|
|
59
|
+
|
|
60
|
+
DataFrame columns already carry useful contract information: names, dtypes, categories, nullability, dates, numeric values, and structured pairs. MLSchema turns that information into a validated field list.
|
|
61
|
+
|
|
62
|
+
Instead of maintaining separate form definitions beside the data pipeline, use `infer_schema(df)` as the baseline and refine only what is genuinely product-specific: labels, bounds, defaults, units, placeholders, UI hints, or custom field kinds.
|
|
63
|
+
|
|
64
|
+
```python id="54fwnz"
|
|
65
|
+
import pandas as pd
|
|
66
|
+
|
|
67
|
+
from mlschema import infer_schema
|
|
68
|
+
|
|
69
|
+
df = pd.DataFrame(
|
|
70
|
+
{
|
|
71
|
+
"name": ["Ada", "Linus", "Grace"],
|
|
72
|
+
"score": [98.5, 86.0, 91.0],
|
|
73
|
+
"role": pd.Categorical(["engineer", "engineer", "scientist"]),
|
|
74
|
+
"active": [True, False, True],
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
schema = infer_schema(df)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```json id="mt9rru"
|
|
82
|
+
[
|
|
83
|
+
{
|
|
84
|
+
"kind": "text",
|
|
85
|
+
"label": "name",
|
|
86
|
+
"required": true
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"kind": "number",
|
|
90
|
+
"label": "score",
|
|
91
|
+
"required": true,
|
|
92
|
+
"step": 0.1
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"kind": "category",
|
|
96
|
+
"label": "role",
|
|
97
|
+
"required": true,
|
|
98
|
+
"options": ["engineer", "scientist"]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"kind": "boolean",
|
|
102
|
+
"label": "active",
|
|
103
|
+
"required": true
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Key Features
|
|
109
|
+
|
|
110
|
+
* Function-first API: `infer_schema(df)`.
|
|
111
|
+
* Builtin inference for `text`, `number`, `category`, `boolean`, `date`, and two-axis `series` fields.
|
|
112
|
+
* Pydantic v2 validation before any schema is returned.
|
|
113
|
+
* JSON-serialisable field-list output for frontend and service integration.
|
|
114
|
+
* Field refinements through `overrides`.
|
|
115
|
+
* Domain-specific behaviour through custom builders.
|
|
116
|
+
* New frontend contracts through strict custom kinds.
|
|
117
|
+
* Typed public API with `py.typed`, Pyright, Ruff, pytest, and CI.
|
|
118
|
+
|
|
119
|
+
## Requirements
|
|
120
|
+
|
|
121
|
+
* Python `>=3.14,<3.15`
|
|
122
|
+
* pandas `>=3.0.3,<4.0.0`
|
|
123
|
+
* pydantic `>=2.13.4,<3.0.0`
|
|
124
|
+
|
|
125
|
+
## Installation
|
|
126
|
+
|
|
127
|
+
```bash id="ejpqxw"
|
|
128
|
+
uv add mlschema
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Alternative package managers:
|
|
132
|
+
|
|
133
|
+
```bash id="gqitqn"
|
|
134
|
+
pip install mlschema
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```bash id="2n70wl"
|
|
138
|
+
poetry add mlschema
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Pin a version when reproducible environments matter:
|
|
142
|
+
|
|
143
|
+
```bash id="yrqcr7"
|
|
144
|
+
uv add "mlschema==0.2.0"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Quick Start
|
|
148
|
+
|
|
149
|
+
```python id="f7024l"
|
|
150
|
+
import pandas as pd
|
|
151
|
+
|
|
152
|
+
from mlschema import infer_schema
|
|
153
|
+
|
|
154
|
+
df = pd.DataFrame(
|
|
155
|
+
{
|
|
156
|
+
"customer": ["Ada", "Linus", "Grace"],
|
|
157
|
+
"age": [42, 55, 38],
|
|
158
|
+
"tier": pd.Categorical(["pro", "free", "pro"], categories=["free", "pro"]),
|
|
159
|
+
"created": pd.date_range("2024-01-01", periods=3),
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
schema = infer_schema(df)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The result can be returned from an API, stored as a contract, passed to a form renderer, or used in tests to detect schema drift.
|
|
167
|
+
|
|
168
|
+
MLSchema works best when DataFrame dtypes are deliberate. Numeric columns should use numeric dtypes, categorical columns should use `category`, date columns should use pandas datetime dtypes, and boolean columns should use boolean dtypes. Ambiguous object columns fall back to `text`.
|
|
169
|
+
|
|
170
|
+
## Schema Output
|
|
171
|
+
|
|
172
|
+
The canonical output is a field list.
|
|
173
|
+
|
|
174
|
+
There is no top-level envelope by default. MLSchema returns the contract directly:
|
|
175
|
+
|
|
176
|
+
```json id="25et3y"
|
|
177
|
+
[
|
|
178
|
+
{
|
|
179
|
+
"kind": "text",
|
|
180
|
+
"label": "customer",
|
|
181
|
+
"required": true
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"kind": "number",
|
|
185
|
+
"label": "age",
|
|
186
|
+
"required": true,
|
|
187
|
+
"step": 1
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"kind": "category",
|
|
191
|
+
"label": "tier",
|
|
192
|
+
"required": true,
|
|
193
|
+
"options": ["free", "pro"]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"kind": "date",
|
|
197
|
+
"label": "created",
|
|
198
|
+
"required": true
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Each field includes:
|
|
204
|
+
|
|
205
|
+
* `kind`: the frontend discriminator.
|
|
206
|
+
* `label`: the human-readable label, inferred from the column name unless overridden.
|
|
207
|
+
* `required`: `true` when the source column contains no missing values.
|
|
208
|
+
* kind-specific metadata, such as `step`, `options`, `field1`, `field2`, or validation bounds.
|
|
209
|
+
|
|
210
|
+
Optional values set to `None` are omitted from the output.
|
|
211
|
+
|
|
212
|
+
## Builtin Kinds
|
|
213
|
+
|
|
214
|
+
Builtin kinds are enabled by default and resolved in a fixed order.
|
|
215
|
+
|
|
216
|
+
| Kind | Detection | Notes |
|
|
217
|
+
| ---------- | ------------------------------------------------------------ | ---------------------------------------------------------------------- |
|
|
218
|
+
| `series` | Non-null cells are 2-element tuples, lists, or dictionaries. | Infers `field1` and `field2` recursively. |
|
|
219
|
+
| `boolean` | `bool`, `boolean` | Emits a boolean field contract. |
|
|
220
|
+
| `category` | `category` | Emits `options` from categorical categories. |
|
|
221
|
+
| `date` | `datetime64[ns]`, `datetime64[us]`, `datetime64` | Emits a date field contract. |
|
|
222
|
+
| `number` | `int64`, `int32`, `float64`, `float32` | Emits `step: 1` for integer columns and `step: 0.1` for float columns. |
|
|
223
|
+
| `text` | fallback | Claims columns not handled by earlier kinds. |
|
|
224
|
+
|
|
225
|
+
The order matters. `series` runs before `text` because it detects pair-shaped object cells by content. `text` runs last as the safe fallback.
|
|
226
|
+
|
|
227
|
+
## Series Columns
|
|
228
|
+
|
|
229
|
+
A `series` field represents a two-axis value stored in a single DataFrame column, such as timestamp-value readings.
|
|
230
|
+
|
|
231
|
+
```python id="vjf8bq"
|
|
232
|
+
import pandas as pd
|
|
233
|
+
|
|
234
|
+
from mlschema import infer_schema
|
|
235
|
+
|
|
236
|
+
df = pd.DataFrame(
|
|
237
|
+
{
|
|
238
|
+
"readings": [
|
|
239
|
+
(pd.Timestamp("2024-01-01"), 23.5),
|
|
240
|
+
(pd.Timestamp("2024-01-02"), 24.1),
|
|
241
|
+
(pd.Timestamp("2024-01-03"), 22.8),
|
|
242
|
+
],
|
|
243
|
+
}
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
schema = infer_schema(df)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
```json id="f1a308"
|
|
250
|
+
[
|
|
251
|
+
{
|
|
252
|
+
"kind": "series",
|
|
253
|
+
"label": "readings",
|
|
254
|
+
"required": true,
|
|
255
|
+
"field1": {
|
|
256
|
+
"kind": "date",
|
|
257
|
+
"label": "field1",
|
|
258
|
+
"required": true
|
|
259
|
+
},
|
|
260
|
+
"field2": {
|
|
261
|
+
"kind": "number",
|
|
262
|
+
"label": "field2",
|
|
263
|
+
"required": true,
|
|
264
|
+
"step": 0.1
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Supported cell shapes are:
|
|
271
|
+
|
|
272
|
+
```python id="iv6b5b"
|
|
273
|
+
(timestamp, value)
|
|
274
|
+
[timestamp, value]
|
|
275
|
+
{"timestamp": timestamp, "value": value}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Nested series are rejected. Cardinality constraints such as `minPoints` and `maxPoints` can be added with `overrides`.
|
|
279
|
+
|
|
280
|
+
## Refining Fields With Overrides
|
|
281
|
+
|
|
282
|
+
Inference provides the structural baseline. Production interfaces often need clearer labels, ranges, defaults, units, placeholders, or UI metadata.
|
|
283
|
+
|
|
284
|
+
```python id="v9tboo"
|
|
285
|
+
schema = infer_schema(
|
|
286
|
+
df,
|
|
287
|
+
overrides={
|
|
288
|
+
"age": {
|
|
289
|
+
"label": "Age",
|
|
290
|
+
"description": "Customer age in years.",
|
|
291
|
+
"min": 0,
|
|
292
|
+
"max": 120,
|
|
293
|
+
"step": 1,
|
|
294
|
+
"unit": "years",
|
|
295
|
+
},
|
|
296
|
+
"tier": {
|
|
297
|
+
"label": "Plan",
|
|
298
|
+
"defaultValue": "pro",
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Overrides are applied after inference and before final validation. Missing columns and invalid constraints fail explicitly instead of producing a broken schema.
|
|
305
|
+
|
|
306
|
+
## Extending MLSchema
|
|
307
|
+
|
|
308
|
+
Use a custom builder when an existing kind is correct, but the column needs domain-aware metadata.
|
|
309
|
+
|
|
310
|
+
```python id="0r5mdy"
|
|
311
|
+
from pandas import Series
|
|
312
|
+
|
|
313
|
+
from mlschema import FieldContext, infer_schema
|
|
314
|
+
|
|
315
|
+
def money_builder(series: Series, ctx: FieldContext) -> dict | None:
|
|
316
|
+
if ctx.name != "amount_eur":
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
"kind": "number",
|
|
321
|
+
"label": "Amount",
|
|
322
|
+
"required": ctx.required,
|
|
323
|
+
"step": 0.01,
|
|
324
|
+
"unit": "EUR",
|
|
325
|
+
"min": 0,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
schema = infer_schema(df, builders=[money_builder])
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Use a custom kind when the frontend needs a new field discriminator and a dedicated validation model.
|
|
332
|
+
|
|
333
|
+
```python id="96x5rn"
|
|
334
|
+
from typing import Literal
|
|
335
|
+
|
|
336
|
+
from pandas import Series
|
|
337
|
+
|
|
338
|
+
from mlschema import BaseField, FieldContext, infer_schema, kind
|
|
339
|
+
|
|
340
|
+
class DurationField(BaseField):
|
|
341
|
+
kind: Literal["duration"] = "duration"
|
|
342
|
+
unit: Literal["seconds"] = "seconds"
|
|
343
|
+
minSeconds: int
|
|
344
|
+
maxSeconds: int
|
|
345
|
+
|
|
346
|
+
def duration_builder(series: Series, ctx: FieldContext) -> dict | None:
|
|
347
|
+
if ctx.dtype not in {"timedelta64[ns]", "timedelta64[us]"}:
|
|
348
|
+
return None
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
"kind": "duration",
|
|
352
|
+
"label": ctx.name,
|
|
353
|
+
"required": ctx.required,
|
|
354
|
+
"unit": "seconds",
|
|
355
|
+
"minSeconds": int(series.min().total_seconds()),
|
|
356
|
+
"maxSeconds": int(series.max().total_seconds()),
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
schema = infer_schema(
|
|
360
|
+
df,
|
|
361
|
+
kinds=[
|
|
362
|
+
kind(model=DurationField, infer=duration_builder),
|
|
363
|
+
],
|
|
364
|
+
)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Resolution is predictable:
|
|
368
|
+
|
|
369
|
+
```text id="kwtd5b"
|
|
370
|
+
user builders
|
|
371
|
+
custom kind builders
|
|
372
|
+
builtin builders
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
The first builder returning a field dictionary owns the column.
|
|
376
|
+
|
|
377
|
+
## Validation And Errors
|
|
378
|
+
|
|
379
|
+
MLSchema validates the generated contract before returning it.
|
|
380
|
+
|
|
381
|
+
Common errors include:
|
|
382
|
+
|
|
383
|
+
| Error | Meaning |
|
|
384
|
+
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
385
|
+
| `EmptyDataFrameError` | The input DataFrame has no rows or no columns. |
|
|
386
|
+
| `FieldBuilderError` | A builder returned an invalid payload, omitted `kind`, no builder matched, or an override targeted a missing column. |
|
|
387
|
+
| `UnknownFieldKindError` | A builder emitted a kind with no registered field model. |
|
|
388
|
+
| `FieldKindAlreadyRegisteredError` | Duplicate kind names were registered. |
|
|
389
|
+
| `FieldKindError` | `kind()` received an invalid field model. |
|
|
390
|
+
| `pydantic.ValidationError` | The final field payload violates its Pydantic model. |
|
|
391
|
+
|
|
392
|
+
Library exceptions are available from `mlschema.core.exceptions` and re-exported from `mlschema.core`.
|
|
393
|
+
|
|
394
|
+
## With mlform
|
|
395
|
+
|
|
396
|
+
MLSchema focuses on inference and validation. [mlform](https://github.com/UlloaSP/mlform) can consume the generated field list to render interactive forms.
|
|
397
|
+
|
|
398
|
+
The split is intentional: Python owns the data contract; the frontend owns rendering, interaction, and submission.
|
|
399
|
+
|
|
400
|
+
## Documentation
|
|
401
|
+
|
|
402
|
+
* Documentation: https://ulloasp.github.io/mlschema/
|
|
403
|
+
* Usage guide: https://ulloasp.github.io/mlschema/usage/
|
|
404
|
+
* Schema standard: https://ulloasp.github.io/mlschema/schema-standard/
|
|
405
|
+
* API reference: https://ulloasp.github.io/mlschema/reference/
|
|
406
|
+
* Changelog: https://ulloasp.github.io/mlschema/changelog/
|
|
407
|
+
|
|
408
|
+
## Tooling And Quality
|
|
409
|
+
|
|
410
|
+
* MIT-licensed package distributed as wheel and sdist.
|
|
411
|
+
* Built with Hatchling.
|
|
412
|
+
* Typed with `py.typed`.
|
|
413
|
+
* Tested with `pytest` and `pytest-cov`.
|
|
414
|
+
* Checked with `ruff` and `pyright`.
|
|
415
|
+
* CI provided by GitHub Actions.
|
|
416
|
+
|
|
417
|
+
## Contributing
|
|
418
|
+
|
|
419
|
+
Contributions are welcome.
|
|
420
|
+
|
|
421
|
+
Useful commands for local development:
|
|
422
|
+
|
|
423
|
+
```bash id="2fzokc"
|
|
424
|
+
uv sync
|
|
425
|
+
uv run pre-commit install
|
|
426
|
+
uv run pytest
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Project links:
|
|
430
|
+
|
|
431
|
+
* Issues: https://github.com/UlloaSP/mlschema/issues
|
|
432
|
+
* Discussions: https://github.com/UlloaSP/mlschema/discussions
|
|
433
|
+
* Contributing guide: https://github.com/UlloaSP/mlschema/blob/main/CONTRIBUTING.md
|
|
434
|
+
|
|
435
|
+
## Security
|
|
436
|
+
|
|
437
|
+
Please report security concerns privately by emailing `pablo.ulloa.santin@udc.es`.
|
|
438
|
+
|
|
439
|
+
The disclosure process is documented in [SECURITY.md](https://github.com/UlloaSP/mlschema/blob/main/SECURITY.md).
|
|
440
|
+
|
|
441
|
+
## License
|
|
442
|
+
|
|
443
|
+
Released under the MIT License.
|
|
444
|
+
|
|
445
|
+
* License: https://github.com/UlloaSP/mlschema/blob/main/LICENSE
|
|
446
|
+
* Third-party notices: https://github.com/UlloaSP/mlschema/blob/main/THIRD_PARTY_LICENSES.md
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
Made by [Pablo Ulloa Santin](https://github.com/UlloaSP) and contributors.
|