silver-data 0.1.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.
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2024-08-04
9
+
10
+ ### Added
11
+ - Initial release of silver-data
12
+ - Dataset loading from CSV, JSON, JSONL, and pandas DataFrames
13
+ - Dataset inspection with column statistics and metadata
14
+ - Dataset validation with error and warning reporting
15
+ - Deterministic dataset fingerprinting for caching and reproducibility
16
+ - Train/validation/test splitting with customizable ratios
17
+ - Immutable dataset design for safe data handling
18
+ - Full type hints for better IDE support
19
+ - Comprehensive test suite with >90% coverage
20
+ - Support for Python 3.8-3.12
21
+
22
+ ### Features
23
+ - `Dataset.from_records()` - Create datasets from Python dictionaries
24
+ - `Dataset.from_csv()` - Load datasets from CSV files
25
+ - `Dataset.from_json()` - Parse JSON data into datasets
26
+ - `Dataset.from_jsonl()` - Handle JSONL format for streaming data
27
+ - `Dataset.from_pandas()` - Convert pandas DataFrames to Silver datasets
28
+ - `Dataset.inspect()` - Get detailed dataset statistics
29
+ - `Dataset.validate()` - Check for data quality issues
30
+ - `Dataset.fingerprint()` - Generate deterministic dataset identifiers
31
+ - `Dataset.split()` - Split datasets for ML workflows
32
+ - `Dataset.to_pandas()` - Convert back to pandas for compatibility
33
+
34
+ ## [Unreleased]
@@ -0,0 +1,114 @@
1
+ # Contributing to silver-data
2
+
3
+ Thank you for your interest in contributing to silver-data! This document provides guidelines and instructions for contributing to the project.
4
+
5
+ ## Development Setup
6
+
7
+ ### Prerequisites
8
+ - Python 3.8 or higher
9
+ - Git
10
+ - Virtual environment (recommended)
11
+
12
+ ### Setting Up Development Environment
13
+
14
+ 1. **Clone the repository**
15
+ ```bash
16
+ git clone https://github.com/adfgdartec/silver-data.git
17
+ cd silver-data
18
+ ```
19
+
20
+ 2. **Create a virtual environment**
21
+ ```bash
22
+ python -m venv venv
23
+ source venv/bin/activate # On Windows: venv\Scripts\activate
24
+ ```
25
+
26
+ 3. **Install development dependencies**
27
+ ```bash
28
+ pip install -e ".[dev]"
29
+ ```
30
+
31
+ 4. **Run tests**
32
+ ```bash
33
+ pytest
34
+ ```
35
+
36
+ 5. **Run tests with coverage**
37
+ ```bash
38
+ pytest --cov=silver_data --cov-report=html
39
+ ```
40
+
41
+ ## Code Style
42
+
43
+ We use the following tools to maintain code quality:
44
+
45
+ - **flake8** for linting
46
+ - **mypy** for type checking
47
+ - **pytest** for testing
48
+
49
+ Run all quality checks:
50
+ ```bash
51
+ flake8 src/ tests/
52
+ mypy src/
53
+ pytest
54
+ ```
55
+
56
+ ## Making Changes
57
+
58
+ 1. **Create a branch**
59
+ ```bash
60
+ git checkout -b feature/your-feature-name
61
+ ```
62
+
63
+ 2. **Make your changes**
64
+ - Write clear, descriptive commit messages
65
+ - Add tests for new functionality
66
+ - Update documentation as needed
67
+
68
+ 3. **Run tests**
69
+ ```bash
70
+ pytest
71
+ ```
72
+
73
+ 4. **Submit a pull request**
74
+ - Describe your changes clearly
75
+ - Reference any related issues
76
+ - Ensure all tests pass
77
+
78
+ ## Testing
79
+
80
+ We aim for high test coverage. When adding new features:
81
+
82
+ - Write unit tests for new functions
83
+ - Test edge cases and error conditions
84
+ - Ensure existing tests still pass
85
+
86
+ ### Test Structure
87
+ ```
88
+ tests/
89
+ ├── __init__.py
90
+ └── test_data.py
91
+ ```
92
+
93
+ ## Documentation
94
+
95
+ - Update docstrings for any modified functions
96
+ - Add examples for new features
97
+ - Update README.md if user-facing changes are made
98
+
99
+ ## Release Process
100
+
101
+ Releases are managed by maintainers:
102
+
103
+ 1. Update version in `pyproject.toml`
104
+ 2. Update `CHANGELOG.md`
105
+ 3. Create a GitHub release
106
+ 4. Package will be automatically published to PyPI
107
+
108
+ ## Questions?
109
+
110
+ Feel free to open an issue for questions or discussions about contributions.
111
+
112
+ ## License
113
+
114
+ By contributing, you agree that your contributions will be licensed under the Apache-2.0 License.
@@ -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,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CHANGELOG.md
4
+ include CONTRIBUTING.md
5
+ recursive-include src *.py
6
+ recursive-include tests *.py
@@ -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,191 @@
1
+ # silver-data
2
+
3
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
4
+ [![License](https://img.shields.io/badge/license-Apache%202.0-green.svg)](LICENSE)
5
+ [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
6
+ [![Code Style](https://img.shields.io/badge/code%20style-flake8-blue.svg)](https://flake8.pycqa.org/)
7
+
8
+ 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.
9
+
10
+ The base install uses only the Python standard library for records, JSON, JSONL,
11
+ and CSV. Add pandas only when you need DataFrame conversion:
12
+
13
+ ```bash
14
+ pip install 'silver-data[pandas]'
15
+ ```
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install silver-data
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```python
26
+ from silver_data import Dataset
27
+
28
+ # Load from CSV file
29
+ dataset = Dataset.from_csv("my_data", "path/to/data.csv")
30
+
31
+ # Optional: load from pandas
32
+ import pandas as pd
33
+ df = pd.read_csv("path/to/data.csv")
34
+ dataset = Dataset.from_pandas("my_data", df)
35
+
36
+ # Inspect dataset
37
+ report = dataset.inspect()
38
+ print(f"Rows: {report.rows}, Columns: {len(report.columns)}")
39
+ for col in report.columns:
40
+ print(f" {col.name}: {col.value_type} ({col.unique} unique, {col.missing} missing)")
41
+
42
+ # Validate dataset
43
+ validation = dataset.validate()
44
+ if not validation.valid:
45
+ print("Errors:", validation.errors)
46
+ if validation.warnings:
47
+ print("Warnings:", validation.warnings)
48
+
49
+ # Split dataset for ML workflows
50
+ train, val, test = dataset.split(train=0.8, validation=0.1, test=0.1)
51
+ print(f"Train: {len(train.records())}, Val: {len(val.records())}, Test: {len(test.records())}")
52
+ ```
53
+
54
+ ## Features
55
+
56
+ - **Multiple Data Sources**: Load from CSV, JSON, JSONL, and pandas DataFrames
57
+ - **Dataset Inspection**: Get detailed column statistics and metadata
58
+ - **Data Validation**: Automatic detection of missing values, inconsistent columns, and data quality issues
59
+ - **Deterministic Fingerprinting**: Generate unique identifiers for datasets to ensure reproducibility
60
+ - **Smart Splitting**: Train/validation/test splitting with customizable ratios
61
+ - **Immutable Design**: Safe data handling with copy-on-write semantics
62
+ - **Type Safety**: Full type hints for better IDE support and fewer bugs
63
+
64
+ ## Use Cases
65
+
66
+ ### ML Pipeline Integration
67
+
68
+ ```python
69
+ from silver_data import Dataset
70
+ import pandas as pd
71
+
72
+ # Load and validate training data
73
+ df = pd.read_csv("train.csv")
74
+ dataset = Dataset.from_pandas("training", df)
75
+
76
+ # Ensure data quality before training
77
+ validation = dataset.validate()
78
+ if not validation.valid:
79
+ raise ValueError(f"Dataset validation failed: {validation.errors}")
80
+
81
+ # Split for cross-validation
82
+ train_split, val_split, test_split = dataset.split(train=0.7, validation=0.15, test=0.15)
83
+
84
+ # Use fingerprints for caching
85
+ cache_key = dataset.fingerprint()
86
+ print(f"Dataset fingerprint: {cache_key}")
87
+ ```
88
+
89
+ ### Data Quality Monitoring
90
+
91
+ ```python
92
+ from silver_data import Dataset
93
+
94
+ # Monitor data drift over time
95
+ dataset_v1 = Dataset.from_csv("data_v1", "data_2024_01.csv")
96
+ dataset_v2 = Dataset.from_csv("data_v2", "data_2024_02.csv")
97
+
98
+ if dataset_v1.fingerprint() != dataset_v2.fingerprint():
99
+ print("Dataset has changed - retrain models")
100
+
101
+ # Check for new data quality issues
102
+ report_v2 = dataset_v2.inspect()
103
+ for col in report_v2.columns:
104
+ if col.missing > len(dataset_v2.records()) * 0.1: # More than 10% missing
105
+ print(f"Warning: {col.name} has high missing rate: {col.missing}")
106
+ ```
107
+
108
+ ### Experiment Reproducibility
109
+
110
+ ```python
111
+ from silver_data import Dataset
112
+
113
+ # Ensure exact same data across experiments
114
+ dataset = Dataset.from_csv("experiment", "data.csv")
115
+ experiment_id = f"exp_{dataset.fingerprint()}"
116
+
117
+ # Log for reproducibility
118
+ print(f"Running experiment {experiment_id} with dataset fingerprint {dataset.fingerprint()}")
119
+ ```
120
+
121
+ ## Advanced Usage
122
+
123
+ ### Custom Data Loading
124
+
125
+ ```python
126
+ from silver_data import Dataset
127
+ import json
128
+
129
+ # Load from custom JSON format
130
+ with open("custom_data.json") as f:
131
+ data = json.load(f)
132
+ dataset = Dataset.from_json("custom", data)
133
+
134
+ # Load from streaming JSONL
135
+ with open("streaming_data.jsonl") as f:
136
+ dataset = Dataset.from_jsonl("streaming", f.read())
137
+ ```
138
+
139
+ ### Data Type Analysis
140
+
141
+ ```python
142
+ from silver_data import Dataset
143
+
144
+ dataset = Dataset.from_csv("analysis", "mixed_data.csv")
145
+ report = dataset.inspect()
146
+
147
+ # Analyze column types
148
+ string_cols = [c.name for c in report.columns if c.value_type == "string"]
149
+ numeric_cols = [c.name for c in report.columns if c.value_type == "number"]
150
+ mixed_cols = [c.name for c in report.columns if c.value_type == "mixed"]
151
+
152
+ print(f"String columns: {string_cols}")
153
+ print(f"Numeric columns: {numeric_cols}")
154
+ print(f"Mixed type columns: {mixed_cols}")
155
+ ```
156
+
157
+ ## Requirements
158
+
159
+ - Python 3.8+
160
+ - pandas 1.0+
161
+
162
+ ## Development
163
+
164
+ ```bash
165
+ # Install development dependencies
166
+ pip install -e ".[dev]"
167
+
168
+ # Run tests
169
+ pytest
170
+
171
+ # Run tests with coverage
172
+ pytest --cov=silver_data --cov-report=html
173
+
174
+ # Run linting
175
+ flake8 src/ tests/
176
+ mypy src/
177
+ ```
178
+
179
+ ## Contributing
180
+
181
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
182
+
183
+ ## License
184
+
185
+ Apache-2.0 - see [LICENSE](LICENSE) file for details.
186
+
187
+ ## Related Packages
188
+
189
+ - [silver-run](https://github.com/adfgdartec/silver-run) - Training lifecycle management
190
+ - [silver-diagnostics](https://github.com/adfgdartec/silver-diagnostics) - ML diagnostics
191
+ - [silver-adapters](https://github.com/adfgdartec/silver-adapters) - Framework adapters
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "silver-data"
7
+ version = "0.1.0"
8
+ description = "Inspectable, deterministic dataset contracts and loaders for Silver."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "Apache-2.0"
12
+ authors = []
13
+ keywords = ["machine-learning", "datasets", "csv", "data-validation", "python"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Science/Research",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.8",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
25
+ ]
26
+ dependencies = []
27
+
28
+ [project.optional-dependencies]
29
+ pandas = ["pandas>=1.0.0"]
30
+ dev = [
31
+ "pytest>=7.0.0",
32
+ "pandas>=1.0",
33
+ "pytest-cov>=4.0.0",
34
+ "flake8>=6.0.0",
35
+ "mypy>=1.0.0",
36
+ "build>=0.10.0",
37
+ "twine>=4.0.0",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/adfgdartec/silver-data"
42
+ Repository = "https://github.com/adfgdartec/silver-data"
43
+ Issues = "https://github.com/adfgdartec/silver-data/issues"
44
+
45
+ [tool.setuptools.packages.find]
46
+ where = ["src"]
47
+
48
+ [tool.setuptools.package-dir]
49
+ "" = "src"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ ]
@@ -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())
@@ -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,16 @@
1
+ CHANGELOG.md
2
+ CONTRIBUTING.md
3
+ LICENSE
4
+ MANIFEST.in
5
+ README.md
6
+ pyproject.toml
7
+ src/silver_data/__init__.py
8
+ src/silver_data/dataset.py
9
+ src/silver_data/models.py
10
+ src/silver_data.egg-info/PKG-INFO
11
+ src/silver_data.egg-info/SOURCES.txt
12
+ src/silver_data.egg-info/dependency_links.txt
13
+ src/silver_data.egg-info/requires.txt
14
+ src/silver_data.egg-info/top_level.txt
15
+ tests/__init__.py
16
+ tests/test_data.py
@@ -0,0 +1,12 @@
1
+
2
+ [dev]
3
+ pytest>=7.0.0
4
+ pandas>=1.0
5
+ pytest-cov>=4.0.0
6
+ flake8>=6.0.0
7
+ mypy>=1.0.0
8
+ build>=0.10.0
9
+ twine>=4.0.0
10
+
11
+ [pandas]
12
+ pandas>=1.0.0
@@ -0,0 +1 @@
1
+ silver_data
@@ -0,0 +1 @@
1
+ # Tests for silver-data package
@@ -0,0 +1,194 @@
1
+ import pytest
2
+ import pandas as pd
3
+ from silver_data import (
4
+ Dataset,
5
+ DatasetColumn,
6
+ DatasetReport,
7
+ DatasetValidation,
8
+ DatasetSplit,
9
+ )
10
+
11
+
12
+ class TestDatasetCreation:
13
+ def test_from_records(self):
14
+ records = [
15
+ {"name": "Alice", "age": 30, "city": "NYC"},
16
+ {"name": "Bob", "age": 25, "city": "LA"},
17
+ ]
18
+ dataset = Dataset.from_records("test", records)
19
+ assert dataset.name == "test"
20
+ assert len(dataset.records()) == 2
21
+
22
+ def test_from_records_empty_raises_error(self):
23
+ with pytest.raises(ValueError, match="Dataset must contain at least one row"):
24
+ Dataset.from_records("test", [])
25
+
26
+ def test_from_records_empty_name_raises_error(self):
27
+ with pytest.raises(ValueError, match="Dataset name is required"):
28
+ Dataset.from_records("", [{"a": 1}])
29
+
30
+ def test_from_json_single_object(self):
31
+ dataset = Dataset.from_json("test", {"name": "Alice", "age": 30})
32
+ assert len(dataset.records()) == 1
33
+ assert dataset.records()[0] == {"name": "Alice", "age": 30}
34
+
35
+ def test_from_json_array(self):
36
+ dataset = Dataset.from_json("test", [{"name": "Alice"}, {"name": "Bob"}])
37
+ assert len(dataset.records()) == 2
38
+
39
+ def test_from_jsonl(self):
40
+ jsonl = '{"name": "Alice", "age": 30}\n{"name": "Bob", "age": 25}'
41
+ dataset = Dataset.from_jsonl("test", jsonl)
42
+ assert len(dataset.records()) == 2
43
+
44
+ def test_from_jsonl_invalid_raises_error(self):
45
+ with pytest.raises(ValueError, match="JSONL rows must be objects"):
46
+ Dataset.from_jsonl("test", '["not", "an", "object"]')
47
+
48
+ def test_from_pandas(self):
49
+ df = pd.DataFrame({"name": ["Alice", "Bob"], "age": [30, 25]})
50
+ dataset = Dataset.from_pandas("test", df)
51
+ assert len(dataset.records()) == 2
52
+ assert dataset.records()[0]["name"] == "Alice"
53
+
54
+
55
+ class TestDatasetInspection:
56
+ def test_columns(self):
57
+ records = [
58
+ {"name": "Alice", "age": 30},
59
+ {"name": "Bob", "age": 25, "city": "LA"},
60
+ ]
61
+ dataset = Dataset.from_records("test", records)
62
+ columns = dataset.columns()
63
+ assert "age" in columns
64
+ assert "name" in columns
65
+ assert "city" in columns
66
+
67
+ def test_inspect(self):
68
+ records = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
69
+ dataset = Dataset.from_records("test", records)
70
+ report = dataset.inspect()
71
+
72
+ assert isinstance(report, DatasetReport)
73
+ assert report.name == "test"
74
+ assert report.rows == 2
75
+ assert len(report.columns) == 2
76
+ assert report.fingerprint == dataset.fingerprint()
77
+
78
+ def test_inspect_column_types(self):
79
+ records = [
80
+ {"name": "Alice", "age": 30, "active": True},
81
+ {"name": "Bob", "age": 25, "active": False},
82
+ ]
83
+ dataset = Dataset.from_records("test", records)
84
+ report = dataset.inspect()
85
+
86
+ col_dict = {col.name: col for col in report.columns}
87
+ assert col_dict["name"].value_type == "string"
88
+ assert col_dict["age"].value_type == "number"
89
+ assert col_dict["active"].value_type == "boolean"
90
+
91
+ def test_inspect_missing_values(self):
92
+ records = [{"name": "Alice", "age": 30}, {"name": None, "age": 25}]
93
+ dataset = Dataset.from_records("test", records)
94
+ report = dataset.inspect()
95
+
96
+ col_dict = {col.name: col for col in report.columns}
97
+ assert col_dict["name"].missing == 1
98
+
99
+
100
+ class TestDatasetValidation:
101
+ def test_validate_valid_dataset(self):
102
+ records = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
103
+ dataset = Dataset.from_records("test", records)
104
+ validation = dataset.validate()
105
+
106
+ assert validation.valid == True
107
+ assert len(validation.errors) == 0
108
+
109
+ def test_validate_inconsistent_columns(self):
110
+ records = [
111
+ {"name": "Alice", "age": 30},
112
+ {"name": "Bob", "age": 25, "city": "LA"},
113
+ ]
114
+ dataset = Dataset.from_records("test", records)
115
+ validation = dataset.validate()
116
+
117
+ assert validation.valid == True # warnings don't make it invalid
118
+ assert len(validation.warnings) > 0
119
+ assert "do not all contain the same columns" in validation.warnings[0]
120
+
121
+ def test_validate_missing_values(self):
122
+ records = [{"name": "Alice", "age": 30}, {"name": None, "age": 25}]
123
+ dataset = Dataset.from_records("test", records)
124
+ validation = dataset.validate()
125
+
126
+ assert len(validation.warnings) > 0
127
+ assert "missing values" in validation.warnings[0]
128
+
129
+
130
+ class TestDatasetFingerprint:
131
+ def test_fingerprint_deterministic(self):
132
+ records = [{"name": "Alice", "age": 30}]
133
+ dataset1 = Dataset.from_records("test", records)
134
+ dataset2 = Dataset.from_records("test", records)
135
+
136
+ assert dataset1.fingerprint() == dataset2.fingerprint()
137
+
138
+ def test_fingerprint_different_data(self):
139
+ dataset1 = Dataset.from_records("test", [{"name": "Alice"}])
140
+ dataset2 = Dataset.from_records("test", [{"name": "Bob"}])
141
+
142
+ assert dataset1.fingerprint() != dataset2.fingerprint()
143
+
144
+
145
+ class TestDatasetSplit:
146
+ def test_split_default_ratios(self):
147
+ records = [{"value": i} for i in range(100)]
148
+ dataset = Dataset.from_records("test", records)
149
+ split = dataset.split()
150
+
151
+ assert isinstance(split, DatasetSplit)
152
+ assert len(split.train.records()) == 80
153
+ assert len(split.validation.records()) == 10
154
+ assert len(split.test.records()) == 10
155
+
156
+ def test_split_custom_ratios(self):
157
+ records = [{"value": i} for i in range(100)]
158
+ dataset = Dataset.from_records("test", records)
159
+ split = dataset.split(train=0.6, validation=0.2, test=0.2)
160
+
161
+ assert len(split.train.records()) == 60
162
+ assert len(split.validation.records()) == 20
163
+ assert len(split.test.records()) == 20
164
+
165
+ def test_split_invalid_ratios_raises_error(self):
166
+ dataset = Dataset.from_records("test", [{"value": 1}])
167
+
168
+ with pytest.raises(ValueError, match="must sum to 1"):
169
+ dataset.split(train=0.5, validation=0.3, test=0.1)
170
+
171
+ with pytest.raises(ValueError, match="must be non-negative"):
172
+ dataset.split(train=-0.1)
173
+
174
+
175
+ class TestDatasetToPandas:
176
+ def test_to_pandas(self):
177
+ records = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
178
+ dataset = Dataset.from_records("test", records)
179
+ df = dataset.to_pandas()
180
+
181
+ assert isinstance(df, pd.DataFrame)
182
+ assert len(df) == 2
183
+ assert list(df.columns) == ["age", "name"] # alphabetical order
184
+
185
+
186
+ class TestDatasetImmutability:
187
+ def test_records_returns_copy(self):
188
+ records = [{"name": "Alice"}]
189
+ dataset = Dataset.from_records("test", records)
190
+
191
+ returned_records = dataset.records()
192
+ returned_records[0]["name"] = "Bob"
193
+
194
+ assert dataset.records()[0]["name"] == "Alice"