silver-data 0.1.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.
@@ -0,0 +1,19 @@
1
+ from .dataset import Dataset
2
+ from .models import (
3
+ DataRecord,
4
+ DatasetColumn,
5
+ DatasetReport,
6
+ DatasetSplit,
7
+ DatasetValidation,
8
+ Primitive,
9
+ )
10
+
11
+ __all__ = [
12
+ "Dataset",
13
+ "DatasetColumn",
14
+ "DatasetReport",
15
+ "DatasetValidation",
16
+ "DatasetSplit",
17
+ "Primitive",
18
+ "DataRecord",
19
+ ]
silver_data/dataset.py ADDED
@@ -0,0 +1,160 @@
1
+ import csv
2
+ import hashlib
3
+ import json
4
+ from typing import Any, List, Union
5
+
6
+ from .models import (
7
+ DataRecord,
8
+ DatasetColumn,
9
+ DatasetReport,
10
+ DatasetSplit,
11
+ DatasetValidation,
12
+ )
13
+
14
+
15
+ class Dataset:
16
+ def __init__(self, name: str, rows: List[DataRecord]):
17
+ if not name.strip():
18
+ raise ValueError("Dataset name is required")
19
+ self.name = name
20
+ self._rows = [dict(row) for row in rows]
21
+
22
+ @classmethod
23
+ def from_records(cls, name: str, rows: List[DataRecord]) -> "Dataset":
24
+ if len(rows) == 0:
25
+ raise ValueError("Dataset must contain at least one row")
26
+ return cls(name, [dict(row) for row in rows])
27
+
28
+ @classmethod
29
+ def from_json(
30
+ cls, name: str, value: Union[List[DataRecord], DataRecord]
31
+ ) -> "Dataset":
32
+ if isinstance(value, dict):
33
+ return cls.from_records(name, [value])
34
+ return cls.from_records(name, value)
35
+
36
+ @classmethod
37
+ def from_jsonl(cls, name: str, value: str) -> "Dataset":
38
+ lines = [line.strip() for line in value.strip().splitlines() if line.strip()]
39
+ records = []
40
+ for line in lines:
41
+ record = json.loads(line)
42
+ if not isinstance(record, dict) or isinstance(record, list):
43
+ raise ValueError("JSONL rows must be objects")
44
+ records.append(record)
45
+ return cls.from_records(name, records)
46
+
47
+ @classmethod
48
+ def from_csv(cls, name: str, path: str) -> "Dataset":
49
+ try:
50
+ import pandas as pd
51
+ except ImportError:
52
+ pd = None
53
+ if pd is not None:
54
+ return cls.from_records(name, pd.read_csv(path).to_dict(orient="records"))
55
+ with open(path, newline="", encoding="utf-8") as stream:
56
+ return cls.from_records(name, list(csv.DictReader(stream)))
57
+
58
+ @classmethod
59
+ def from_pandas(cls, name: str, df: Any) -> "Dataset":
60
+ if not hasattr(df, "to_dict"):
61
+ raise TypeError("from_pandas requires an object with to_dict")
62
+ return cls.from_records(name, df.to_dict(orient="records"))
63
+
64
+ def records(self) -> List[DataRecord]:
65
+ return [dict(row) for row in self._rows]
66
+
67
+ def columns(self) -> List[str]:
68
+ all_columns = set()
69
+ for row in self._rows:
70
+ all_columns.update(row.keys())
71
+ return sorted(all_columns)
72
+
73
+ def inspect(self) -> DatasetReport:
74
+ columns_report = []
75
+ for name in self.columns():
76
+ values = [row.get(name) for row in self._rows]
77
+ present = [value for value in values if value is not None]
78
+ if present:
79
+ types = {type(value).__name__ for value in present}
80
+ if len(types) == 1:
81
+ type_name = type(present[0]).__name__
82
+ value_type = {
83
+ "str": "string",
84
+ "int": "number",
85
+ "float": "number",
86
+ "bool": "boolean",
87
+ }.get(type_name, "mixed")
88
+ else:
89
+ value_type = "mixed"
90
+ else:
91
+ value_type = "null"
92
+ columns_report.append(
93
+ DatasetColumn(
94
+ name=name,
95
+ value_type=value_type,
96
+ missing=len(values) - len(present),
97
+ unique=len({json.dumps(value, sort_keys=True) for value in values}),
98
+ )
99
+ )
100
+ return DatasetReport(
101
+ name=self.name,
102
+ rows=len(self._rows),
103
+ columns=columns_report,
104
+ fingerprint=self.fingerprint(),
105
+ )
106
+
107
+ def validate(self) -> DatasetValidation:
108
+ errors = []
109
+ warnings = []
110
+ column_count = len(self.columns())
111
+ inconsistent_rows = [
112
+ index
113
+ for index, row in enumerate(self._rows)
114
+ if len(row.keys()) != column_count
115
+ ]
116
+ if inconsistent_rows:
117
+ warnings.append(
118
+ f"Rows do not all contain the same columns: {inconsistent_rows[:5]}"
119
+ )
120
+ for column in self.inspect().columns:
121
+ if column.missing > 0:
122
+ warnings.append(f"{column.name} has {column.missing} missing values")
123
+ return DatasetValidation(
124
+ valid=len(errors) == 0, errors=errors, warnings=warnings
125
+ )
126
+
127
+ def fingerprint(self) -> str:
128
+ data = json.dumps({"name": self.name, "rows": self._rows}, sort_keys=True)
129
+ return hashlib.md5(data.encode()).hexdigest()[:8]
130
+
131
+ def split(
132
+ self, train: float = 0.8, validation: float = 0.1, test: float = 0.1
133
+ ) -> DatasetSplit:
134
+ if train < 0 or validation < 0 or test < 0:
135
+ raise ValueError("Dataset split ratios must be non-negative")
136
+ if abs(train + validation + test - 1.0) > 1e-9:
137
+ raise ValueError("Dataset split ratios must sum to 1")
138
+ train_end = int(len(self._rows) * train)
139
+ validation_end = train_end + int(len(self._rows) * validation)
140
+ return DatasetSplit(
141
+ train=Dataset._from_rows(f"{self.name}/train", self._rows[:train_end]),
142
+ validation=Dataset._from_rows(
143
+ f"{self.name}/validation", self._rows[train_end:validation_end]
144
+ ),
145
+ test=Dataset._from_rows(f"{self.name}/test", self._rows[validation_end:]),
146
+ )
147
+
148
+ @classmethod
149
+ def _from_rows(cls, name: str, rows: List[DataRecord]) -> "Dataset":
150
+ return cls(name, [dict(row) for row in rows])
151
+
152
+ def to_pandas(self) -> Any:
153
+ try:
154
+ import pandas as pd
155
+ except ImportError as error:
156
+ raise ImportError(
157
+ "Dataset.to_pandas() requires the optional 'pandas' dependency; "
158
+ "install silver-data[pandas]"
159
+ ) from error
160
+ return pd.DataFrame(self._rows, columns=self.columns())
silver_data/models.py ADDED
@@ -0,0 +1,35 @@
1
+ from dataclasses import dataclass
2
+ from typing import Dict, List, Literal, Union
3
+
4
+ Primitive = Union[str, int, float, bool, None]
5
+ DataRecord = Dict[str, Primitive]
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class DatasetColumn:
10
+ name: str
11
+ value_type: Literal["string", "number", "boolean", "null", "mixed"]
12
+ missing: int
13
+ unique: int
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class DatasetReport:
18
+ name: str
19
+ rows: int
20
+ columns: List[DatasetColumn]
21
+ fingerprint: str
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class DatasetValidation:
26
+ valid: bool
27
+ errors: List[str]
28
+ warnings: List[str]
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class DatasetSplit:
33
+ train: "Dataset"
34
+ validation: "Dataset"
35
+ test: "Dataset"
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.4
2
+ Name: silver-data
3
+ Version: 0.1.0
4
+ Summary: Inspectable, deterministic dataset contracts and loaders for Silver.
5
+ License-Expression: Apache-2.0
6
+ Project-URL: Homepage, https://github.com/adfgdartec/silver-data
7
+ Project-URL: Repository, https://github.com/adfgdartec/silver-data
8
+ Project-URL: Issues, https://github.com/adfgdartec/silver-data/issues
9
+ Keywords: machine-learning,datasets,csv,data-validation,python
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: pandas
24
+ Requires-Dist: pandas>=1.0.0; extra == "pandas"
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
27
+ Requires-Dist: pandas>=1.0; extra == "dev"
28
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
29
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
30
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
31
+ Requires-Dist: build>=0.10.0; extra == "dev"
32
+ Requires-Dist: twine>=4.0.0; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # silver-data
36
+
37
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
38
+ [![License](https://img.shields.io/badge/license-Apache%202.0-green.svg)](LICENSE)
39
+ [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
40
+ [![Code Style](https://img.shields.io/badge/code%20style-flake8-blue.svg)](https://flake8.pycqa.org/)
41
+
42
+ Inspectable, deterministic dataset contracts and loaders for Silver. A Python package designed for ML researchers who need reliable dataset handling with built-in validation and reproducibility features.
43
+
44
+ The base install uses only the Python standard library for records, JSON, JSONL,
45
+ and CSV. Add pandas only when you need DataFrame conversion:
46
+
47
+ ```bash
48
+ pip install 'silver-data[pandas]'
49
+ ```
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install silver-data
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ```python
60
+ from silver_data import Dataset
61
+
62
+ # Load from CSV file
63
+ dataset = Dataset.from_csv("my_data", "path/to/data.csv")
64
+
65
+ # Optional: load from pandas
66
+ import pandas as pd
67
+ df = pd.read_csv("path/to/data.csv")
68
+ dataset = Dataset.from_pandas("my_data", df)
69
+
70
+ # Inspect dataset
71
+ report = dataset.inspect()
72
+ print(f"Rows: {report.rows}, Columns: {len(report.columns)}")
73
+ for col in report.columns:
74
+ print(f" {col.name}: {col.value_type} ({col.unique} unique, {col.missing} missing)")
75
+
76
+ # Validate dataset
77
+ validation = dataset.validate()
78
+ if not validation.valid:
79
+ print("Errors:", validation.errors)
80
+ if validation.warnings:
81
+ print("Warnings:", validation.warnings)
82
+
83
+ # Split dataset for ML workflows
84
+ train, val, test = dataset.split(train=0.8, validation=0.1, test=0.1)
85
+ print(f"Train: {len(train.records())}, Val: {len(val.records())}, Test: {len(test.records())}")
86
+ ```
87
+
88
+ ## Features
89
+
90
+ - **Multiple Data Sources**: Load from CSV, JSON, JSONL, and pandas DataFrames
91
+ - **Dataset Inspection**: Get detailed column statistics and metadata
92
+ - **Data Validation**: Automatic detection of missing values, inconsistent columns, and data quality issues
93
+ - **Deterministic Fingerprinting**: Generate unique identifiers for datasets to ensure reproducibility
94
+ - **Smart Splitting**: Train/validation/test splitting with customizable ratios
95
+ - **Immutable Design**: Safe data handling with copy-on-write semantics
96
+ - **Type Safety**: Full type hints for better IDE support and fewer bugs
97
+
98
+ ## Use Cases
99
+
100
+ ### ML Pipeline Integration
101
+
102
+ ```python
103
+ from silver_data import Dataset
104
+ import pandas as pd
105
+
106
+ # Load and validate training data
107
+ df = pd.read_csv("train.csv")
108
+ dataset = Dataset.from_pandas("training", df)
109
+
110
+ # Ensure data quality before training
111
+ validation = dataset.validate()
112
+ if not validation.valid:
113
+ raise ValueError(f"Dataset validation failed: {validation.errors}")
114
+
115
+ # Split for cross-validation
116
+ train_split, val_split, test_split = dataset.split(train=0.7, validation=0.15, test=0.15)
117
+
118
+ # Use fingerprints for caching
119
+ cache_key = dataset.fingerprint()
120
+ print(f"Dataset fingerprint: {cache_key}")
121
+ ```
122
+
123
+ ### Data Quality Monitoring
124
+
125
+ ```python
126
+ from silver_data import Dataset
127
+
128
+ # Monitor data drift over time
129
+ dataset_v1 = Dataset.from_csv("data_v1", "data_2024_01.csv")
130
+ dataset_v2 = Dataset.from_csv("data_v2", "data_2024_02.csv")
131
+
132
+ if dataset_v1.fingerprint() != dataset_v2.fingerprint():
133
+ print("Dataset has changed - retrain models")
134
+
135
+ # Check for new data quality issues
136
+ report_v2 = dataset_v2.inspect()
137
+ for col in report_v2.columns:
138
+ if col.missing > len(dataset_v2.records()) * 0.1: # More than 10% missing
139
+ print(f"Warning: {col.name} has high missing rate: {col.missing}")
140
+ ```
141
+
142
+ ### Experiment Reproducibility
143
+
144
+ ```python
145
+ from silver_data import Dataset
146
+
147
+ # Ensure exact same data across experiments
148
+ dataset = Dataset.from_csv("experiment", "data.csv")
149
+ experiment_id = f"exp_{dataset.fingerprint()}"
150
+
151
+ # Log for reproducibility
152
+ print(f"Running experiment {experiment_id} with dataset fingerprint {dataset.fingerprint()}")
153
+ ```
154
+
155
+ ## Advanced Usage
156
+
157
+ ### Custom Data Loading
158
+
159
+ ```python
160
+ from silver_data import Dataset
161
+ import json
162
+
163
+ # Load from custom JSON format
164
+ with open("custom_data.json") as f:
165
+ data = json.load(f)
166
+ dataset = Dataset.from_json("custom", data)
167
+
168
+ # Load from streaming JSONL
169
+ with open("streaming_data.jsonl") as f:
170
+ dataset = Dataset.from_jsonl("streaming", f.read())
171
+ ```
172
+
173
+ ### Data Type Analysis
174
+
175
+ ```python
176
+ from silver_data import Dataset
177
+
178
+ dataset = Dataset.from_csv("analysis", "mixed_data.csv")
179
+ report = dataset.inspect()
180
+
181
+ # Analyze column types
182
+ string_cols = [c.name for c in report.columns if c.value_type == "string"]
183
+ numeric_cols = [c.name for c in report.columns if c.value_type == "number"]
184
+ mixed_cols = [c.name for c in report.columns if c.value_type == "mixed"]
185
+
186
+ print(f"String columns: {string_cols}")
187
+ print(f"Numeric columns: {numeric_cols}")
188
+ print(f"Mixed type columns: {mixed_cols}")
189
+ ```
190
+
191
+ ## Requirements
192
+
193
+ - Python 3.8+
194
+ - pandas 1.0+
195
+
196
+ ## Development
197
+
198
+ ```bash
199
+ # Install development dependencies
200
+ pip install -e ".[dev]"
201
+
202
+ # Run tests
203
+ pytest
204
+
205
+ # Run tests with coverage
206
+ pytest --cov=silver_data --cov-report=html
207
+
208
+ # Run linting
209
+ flake8 src/ tests/
210
+ mypy src/
211
+ ```
212
+
213
+ ## Contributing
214
+
215
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
216
+
217
+ ## License
218
+
219
+ Apache-2.0 - see [LICENSE](LICENSE) file for details.
220
+
221
+ ## Related Packages
222
+
223
+ - [silver-run](https://github.com/adfgdartec/silver-run) - Training lifecycle management
224
+ - [silver-diagnostics](https://github.com/adfgdartec/silver-diagnostics) - ML diagnostics
225
+ - [silver-adapters](https://github.com/adfgdartec/silver-adapters) - Framework adapters
@@ -0,0 +1,8 @@
1
+ silver_data/__init__.py,sha256=xafu4vIPLaZ26ypIrmMyrzElSbkBb6ieG_KGEKGV6xA,315
2
+ silver_data/dataset.py,sha256=SIikl97ZpervJsT4PkWsf7H-P8gjphMOZN6emOvl-X4,5876
3
+ silver_data/models.py,sha256=EaxPmK8L-s3FMkwIir2xvtlA6xP-7zgRVM-KEB08hP4,683
4
+ silver_data-0.1.0.dist-info/licenses/LICENSE,sha256=9uEgZddcCAZ2jYIKny4rlbkQMZFhKRvSGbxctAg4wjI,517
5
+ silver_data-0.1.0.dist-info/METADATA,sha256=azOwbu62C6CwY9ptzl762I1jnOlKbKDtfKjXWFFUXYQ,6952
6
+ silver_data-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
+ silver_data-0.1.0.dist-info/top_level.txt,sha256=PlSkMomi6jHDrFFWnkgEeH_LAYiD7OWITwI4ckTVrkY,12
8
+ silver_data-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,12 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+
4
+ Copyright 2026 Silver Contributors
5
+
6
+ Licensed under the Apache License, Version 2.0. You may obtain a copy of the
7
+ License at https://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software distributed
10
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
12
+ specific language governing permissions and limitations under the License.
@@ -0,0 +1 @@
1
+ silver_data