DataExcept 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.
- dataexcept-0.1.0/CHANGELOG.md +42 -0
- dataexcept-0.1.0/CITATION.cff +14 -0
- dataexcept-0.1.0/LICENSE +22 -0
- dataexcept-0.1.0/PKG-INFO +397 -0
- dataexcept-0.1.0/README.md +369 -0
- dataexcept-0.1.0/dataexcept/__init__.py +81 -0
- dataexcept-0.1.0/dataexcept/__main__.py +74 -0
- dataexcept-0.1.0/dataexcept/database_exceptions.py +71 -0
- dataexcept-0.1.0/dataexcept/dataengineering_exceptions.py +125 -0
- dataexcept-0.1.0/dataexcept/datascience_exceptions/__init__.py +85 -0
- dataexcept-0.1.0/dataexcept/datascience_exceptions/base.py +17 -0
- dataexcept-0.1.0/dataexcept/datascience_exceptions/ingestion.py +312 -0
- dataexcept-0.1.0/dataexcept/datascience_exceptions/operations.py +130 -0
- dataexcept-0.1.0/dataexcept/datascience_exceptions/training.py +508 -0
- dataexcept-0.1.0/dataexcept/exceptions/__init__.py +36 -0
- dataexcept-0.1.0/dataexcept/exceptions/authentication.py +21 -0
- dataexcept-0.1.0/dataexcept/exceptions/base.py +4 -0
- dataexcept-0.1.0/dataexcept/exceptions/configuration.py +10 -0
- dataexcept-0.1.0/dataexcept/exceptions/external.py +42 -0
- dataexcept-0.1.0/dataexcept/exceptions/lifecycle.py +13 -0
- dataexcept-0.1.0/dataexcept/exceptions/notification.py +49 -0
- dataexcept-0.1.0/dataexcept/exceptions/parsing.py +31 -0
- dataexcept-0.1.0/dataexcept/exceptions/scheduling.py +21 -0
- dataexcept-0.1.0/dataexcept/exceptions/validation.py +12 -0
- dataexcept-0.1.0/dataexcept/io_exceptions.py +66 -0
- dataexcept-0.1.0/dataexcept/job_exceptions.py +59 -0
- dataexcept-0.1.0/dataexcept/logging_helpers.py +76 -0
- dataexcept-0.1.0/dataexcept/network_exceptions.py +102 -0
- dataexcept-0.1.0/dataexcept/pandas_exceptions.py +143 -0
- dataexcept-0.1.0/dataexcept/pipeline_exceptions.py +205 -0
- dataexcept-0.1.0/dataexcept/py.typed +1 -0
- dataexcept-0.1.0/dataexcept/security_exceptions.py +68 -0
- dataexcept-0.1.0/pyproject.toml +83 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-08-24
|
|
11
|
+
|
|
12
|
+
First public release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Hierarchical exception classes for data science, machine learning and data
|
|
17
|
+
engineering workflows. Catch a specific failure or a broad category, and get
|
|
18
|
+
a message that names the value that caused it rather than a bare
|
|
19
|
+
`ValueError`.
|
|
20
|
+
- Domain modules for validation, configuration, authentication, parsing,
|
|
21
|
+
serialization, scheduling, notification, lifecycle and external-service
|
|
22
|
+
errors, plus dedicated pandas, database, network, I/O, pipeline and security
|
|
23
|
+
exception groups.
|
|
24
|
+
- `dataexcept.logging_helpers` with `log_exception`, `log_and_raise` and
|
|
25
|
+
`log_then_raise`, for logging exceptions with structured context and
|
|
26
|
+
re-raising without losing the traceback.
|
|
27
|
+
- A `dataexcept` command-line entry point that lists the exported exception
|
|
28
|
+
classes and reports the installed version.
|
|
29
|
+
- A `py.typed` marker, backed by a mypy-clean codebase that CI enforces, so
|
|
30
|
+
downstream type checkers get annotations that are actually correct.
|
|
31
|
+
- Documentation at
|
|
32
|
+
[diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/),
|
|
33
|
+
including an API reference generated from the docstrings.
|
|
34
|
+
|
|
35
|
+
### Notes
|
|
36
|
+
|
|
37
|
+
- Supports Python 3.10 through 3.13.
|
|
38
|
+
- Published to PyPI via OIDC trusted publishing; no long-lived API token is
|
|
39
|
+
involved in a release.
|
|
40
|
+
|
|
41
|
+
[Unreleased]: https://github.com/DiogoRibeiro7/DataExcept/compare/v0.1.0...HEAD
|
|
42
|
+
[0.1.0]: https://github.com/DiogoRibeiro7/DataExcept/releases/tag/v0.1.0
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it using the following metadata."
|
|
3
|
+
title: "DataExcept"
|
|
4
|
+
version: "0.1.0"
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: "Ribeiro"
|
|
7
|
+
given-names: "Diogo"
|
|
8
|
+
email: "dfr@esmad.ipp.pt"
|
|
9
|
+
orcid: "https://orcid.org/0009-0001-2022-7072"
|
|
10
|
+
affiliation: "ESMAD - Instituto Politécnico do Porto"
|
|
11
|
+
type: software
|
|
12
|
+
url: "https://github.com/DiogoRibeiro7/DataExcept"
|
|
13
|
+
repository-code: "https://github.com/DiogoRibeiro7/DataExcept"
|
|
14
|
+
date-released: "2025-06-16"
|
dataexcept-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Diogo Ribeiro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DataExcept
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python package providing structured, easily-extendable custom exception types.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: exceptions,errors,logging
|
|
8
|
+
Author: Diogo Ribeiro
|
|
9
|
+
Author-email: dfr@esmad.ipp.pt
|
|
10
|
+
Maintainer: Diogo Ribeiro
|
|
11
|
+
Maintainer-email: diogo.debastos.ribeiro@gmail.com
|
|
12
|
+
Requires-Python: >=3.10,<3.14
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Requires-Dist: tomli ; python_version < "3.11"
|
|
21
|
+
Project-URL: Changelog, https://github.com/DiogoRibeiro7/DataExcept/releases
|
|
22
|
+
Project-URL: Documentation, https://diogoribeiro7.github.io/DataExcept/
|
|
23
|
+
Project-URL: Homepage, https://github.com/DiogoRibeiro7/DataExcept
|
|
24
|
+
Project-URL: Issues, https://github.com/DiogoRibeiro7/DataExcept/issues
|
|
25
|
+
Project-URL: Repository, https://github.com/DiogoRibeiro7/DataExcept
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# DataExcept
|
|
29
|
+
|
|
30
|
+
[](https://github.com/DiogoRibeiro7/DataExcept/actions/workflows/ci.yml) [](https://badge.fury.io/py/DataExcept) [](https://pypi.org/project/DataExcept/) [](https://diogoribeiro7.github.io/DataExcept/htmlcov/) [](https://diogoribeiro7.github.io/DataExcept/) [](https://opensource.org/licenses/MIT) [](https://github.com/psf/black) [](https://mypy-lang.org/)
|
|
31
|
+
|
|
32
|
+
**DataExcept** is a production-ready Python library that provides **structured, hierarchical exception classes** specifically designed for **data science**, **machine learning**, and **data engineering** workflows. Stop debugging generic `ValueError`s and `RuntimeError`s -- get meaningful, actionable error messages that help you understand exactly what went wrong in your data pipeline.
|
|
33
|
+
|
|
34
|
+
## 🚀 Why DataExcept?
|
|
35
|
+
|
|
36
|
+
❌ Without DataExcept | ✅ With DataExcept
|
|
37
|
+
------------------------------- | -------------------------------------------------------------------------------------
|
|
38
|
+
`ValueError: Invalid value` | `DataValidationError: Invalid value for 'age': -1`
|
|
39
|
+
`RuntimeError: Training failed` | `ConvergenceError: Model 'RandomForest' failed to converge after 100 iterations`
|
|
40
|
+
`Exception: Prediction error` | `ModelInferenceError: Inference failed for model 'CNN': CUDA out of memory`
|
|
41
|
+
`KeyError: column not found` | `MissingColumnError: Missing required column 'customer_id' in DataFrame 'sales_data'`
|
|
42
|
+
|
|
43
|
+
## 🎯 Key Features
|
|
44
|
+
|
|
45
|
+
- **🏗️ Hierarchical Structure**: Catch specific errors or broad categories
|
|
46
|
+
- **📊 Data Science Focused**: 40+ exceptions covering ML pipelines, feature engineering, model training
|
|
47
|
+
- **🔧 Production Ready**: Comprehensive logging helpers and error context
|
|
48
|
+
- **📚 Academic Quality**: Proper documentation, type hints, and citation support
|
|
49
|
+
- **🐍 Python 3.10+**: Modern Python with full type safety
|
|
50
|
+
- **🧪 Well Tested**: Broad test suite with comprehensive edge case handling (see the coverage badge above)
|
|
51
|
+
|
|
52
|
+
## 📦 Quick Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install DataExcept
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For development:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/DiogoRibeiro7/DataExcept.git
|
|
62
|
+
cd DataExcept
|
|
63
|
+
poetry install
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 🏃♂️ Quick Start
|
|
67
|
+
|
|
68
|
+
### Basic Usage
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from dataexcept import ValidationError, ModelTrainingError
|
|
72
|
+
from dataexcept.datascience_exceptions import DataLoadingError
|
|
73
|
+
import pandas as pd
|
|
74
|
+
|
|
75
|
+
# Data validation with context
|
|
76
|
+
def validate_dataframe(df: pd.DataFrame) -> None:
|
|
77
|
+
if 'customer_id' not in df.columns:
|
|
78
|
+
raise ValidationError(
|
|
79
|
+
field='customer_id',
|
|
80
|
+
value=list(df.columns),
|
|
81
|
+
message="Customer ID column is required for processing"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Model training with specific error types
|
|
85
|
+
def train_model(model_type: str, epochs: int) -> None:
|
|
86
|
+
try:
|
|
87
|
+
# Your training code here
|
|
88
|
+
if epochs > 1000:
|
|
89
|
+
raise ModelTrainingError(
|
|
90
|
+
model_type=model_type,
|
|
91
|
+
epoch=epochs,
|
|
92
|
+
message=f"Training {model_type} exceeded reasonable epoch limit"
|
|
93
|
+
)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
# Wrap unknown errors with context
|
|
96
|
+
raise ModelTrainingError(model_type, message=f"Unexpected error: {e}")
|
|
97
|
+
|
|
98
|
+
# File operations with detailed context
|
|
99
|
+
def load_dataset(file_path: str) -> pd.DataFrame:
|
|
100
|
+
try:
|
|
101
|
+
return pd.read_csv(file_path)
|
|
102
|
+
except FileNotFoundError as e:
|
|
103
|
+
raise DataLoadingError(source=file_path, original=e)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Exception Hierarchies
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from dataexcept import JobError
|
|
110
|
+
from dataexcept.datascience_exceptions import ModelTrainingError, ConvergenceError
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
# Your ML pipeline
|
|
114
|
+
train_complex_model()
|
|
115
|
+
except ConvergenceError:
|
|
116
|
+
# Handle specific convergence issues
|
|
117
|
+
logger.warning("Model didn't converge, trying with different parameters")
|
|
118
|
+
train_with_fallback_params()
|
|
119
|
+
except ModelTrainingError:
|
|
120
|
+
# Handle any training-related error
|
|
121
|
+
logger.error("Training failed, falling back to simpler model")
|
|
122
|
+
train_simple_model()
|
|
123
|
+
except JobError:
|
|
124
|
+
# Handle any job-related error
|
|
125
|
+
logger.error("Job failed, notifying administrators")
|
|
126
|
+
send_alert()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 🏗️ Exception Categories
|
|
130
|
+
|
|
131
|
+
### 📊 Data Science & ML
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from dataexcept.datascience_exceptions import *
|
|
135
|
+
|
|
136
|
+
# Data ingestion and validation
|
|
137
|
+
DataLoadingError("data.csv", FileNotFoundError())
|
|
138
|
+
DataValidationError("age", -5, "Age cannot be negative")
|
|
139
|
+
MissingDataError("income", "Required for credit scoring")
|
|
140
|
+
|
|
141
|
+
# Feature engineering and preprocessing
|
|
142
|
+
FeatureEngineeringError("log_transform", "Cannot take log of negative values")
|
|
143
|
+
DataNormalizationError("StandardScaler", "Division by zero in variance calculation")
|
|
144
|
+
DataImbalanceError(ratio=0.05, threshold=0.1)
|
|
145
|
+
|
|
146
|
+
# Model training and evaluation
|
|
147
|
+
ModelTrainingError("RandomForest", epoch=45)
|
|
148
|
+
ConvergenceError("GradientBoosting", iterations=1000)
|
|
149
|
+
OverfittingError(train_metric=0.98, val_metric=0.65)
|
|
150
|
+
BiasDetectionError("gender", bias_score=0.15, threshold=0.1)
|
|
151
|
+
|
|
152
|
+
# Model deployment and inference
|
|
153
|
+
ModelInferenceError("CNN", RuntimeError("CUDA out of memory"))
|
|
154
|
+
ModelCompatibilityError("2.1.0", "1.8.0")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 🔧 Data Engineering & ETL
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from dataexcept.dataengineering_exceptions import *
|
|
161
|
+
|
|
162
|
+
ETLJobError("daily_customer_pipeline")
|
|
163
|
+
SchemaEvolutionError("v2.1", reason="Incompatible column type change")
|
|
164
|
+
DataTransformationError("currency_conversion", "Invalid exchange rate")
|
|
165
|
+
BatchProcessingError("batch_2023_11_13", original=TimeoutError())
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 🐼 Pandas Operations
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from dataexcept.pandas_exceptions import *
|
|
172
|
+
|
|
173
|
+
MissingColumnError("customer_id", dataframe="sales_df")
|
|
174
|
+
DtypeMismatchError("revenue", expected=["float64", "int64"], found="object")
|
|
175
|
+
MergeKeyError(["customer_id"], ["cust_id"])
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 🔗 Infrastructure & Networking
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from dataexcept.network_exceptions import *
|
|
182
|
+
from dataexcept.database_exceptions import *
|
|
183
|
+
|
|
184
|
+
HostUnreachableError("api.example.com")
|
|
185
|
+
DatabaseConnectionError("postgresql://prod-db:5432/analytics")
|
|
186
|
+
QueryExecutionError("SELECT * FROM large_table", original=TimeoutError())
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## 🔍 Advanced Features
|
|
190
|
+
|
|
191
|
+
### Smart Logging Integration
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from dataexcept.logging_helpers import log_and_raise, log_exception
|
|
195
|
+
import logging
|
|
196
|
+
|
|
197
|
+
logger = logging.getLogger(__name__)
|
|
198
|
+
|
|
199
|
+
# Context manager for automatic logging
|
|
200
|
+
with log_and_raise(logger=logger, context={"job_id": "ETL_001", "batch": "2023-11-13"}):
|
|
201
|
+
process_daily_batch()
|
|
202
|
+
|
|
203
|
+
# Manual exception logging with context
|
|
204
|
+
try:
|
|
205
|
+
risky_operation()
|
|
206
|
+
except Exception as exc:
|
|
207
|
+
log_exception(
|
|
208
|
+
exc,
|
|
209
|
+
logger=logger,
|
|
210
|
+
context={"user_id": "12345", "operation": "feature_extraction"}
|
|
211
|
+
)
|
|
212
|
+
raise
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Command Line Interface
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# List all available exception classes
|
|
219
|
+
$ dataexcept list
|
|
220
|
+
JobError
|
|
221
|
+
ValidationError
|
|
222
|
+
DataScienceError
|
|
223
|
+
ModelTrainingError
|
|
224
|
+
... (40+ more)
|
|
225
|
+
|
|
226
|
+
# Check version
|
|
227
|
+
$ dataexcept --version
|
|
228
|
+
dataexcept 0.1.0
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## 🎯 Use Cases
|
|
232
|
+
|
|
233
|
+
### 🏭 Production ML Pipelines
|
|
234
|
+
|
|
235
|
+
- **Model Training**: Distinguish between convergence issues, data problems, and infrastructure failures
|
|
236
|
+
- **Feature Engineering**: Track which transformation steps fail and why
|
|
237
|
+
- **Model Serving**: Provide actionable error messages for inference failures
|
|
238
|
+
- **Data Drift**: Alert when model assumptions are violated
|
|
239
|
+
|
|
240
|
+
### 📈 Data Engineering
|
|
241
|
+
|
|
242
|
+
- **ETL Pipelines**: Clear error categorization for debugging complex data flows
|
|
243
|
+
- **Data Quality**: Structured validation errors with field-level context
|
|
244
|
+
- **Schema Evolution**: Track migration failures and compatibility issues
|
|
245
|
+
- **Batch Processing**: Identify whether failures are data-related or system-related
|
|
246
|
+
|
|
247
|
+
### 🔬 Research & Academia
|
|
248
|
+
|
|
249
|
+
- **Reproducible Experiments**: Consistent error handling across research codebases
|
|
250
|
+
- **Citation Support**: Proper academic attribution with CITATION.cff
|
|
251
|
+
- **Documentation**: Auto-generated API docs with comprehensive examples
|
|
252
|
+
|
|
253
|
+
## 📚 Real-World Example
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
"""
|
|
257
|
+
Complete ML pipeline with DataExcept error handling
|
|
258
|
+
"""
|
|
259
|
+
import pandas as pd
|
|
260
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
261
|
+
from dataexcept import ValidationError
|
|
262
|
+
from dataexcept.datascience_exceptions import *
|
|
263
|
+
from dataexcept.pandas_exceptions import *
|
|
264
|
+
from dataexcept.logging_helpers import log_and_raise
|
|
265
|
+
import logging
|
|
266
|
+
|
|
267
|
+
def ml_pipeline(data_path: str, target_col: str):
|
|
268
|
+
logger = logging.getLogger(__name__)
|
|
269
|
+
|
|
270
|
+
with log_and_raise(logger=logger, context={"pipeline": "customer_churn"}):
|
|
271
|
+
# 1\. Data Loading
|
|
272
|
+
try:
|
|
273
|
+
df = pd.read_csv(data_path)
|
|
274
|
+
except FileNotFoundError as e:
|
|
275
|
+
raise DataLoadingError(source=data_path, original=e)
|
|
276
|
+
|
|
277
|
+
# 2\. Data Validation
|
|
278
|
+
if target_col not in df.columns:
|
|
279
|
+
raise MissingColumnError(target_col, dataframe="training_data")
|
|
280
|
+
|
|
281
|
+
if df[target_col].dtype not in ['int64', 'bool']:
|
|
282
|
+
raise DtypeMismatchError(
|
|
283
|
+
target_col,
|
|
284
|
+
expected=['int64', 'bool'],
|
|
285
|
+
found=str(df[target_col].dtype)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# 3\. Data Quality Checks
|
|
289
|
+
missing_ratio = df.isnull().sum().sum() / (df.shape[0] * df.shape[1])
|
|
290
|
+
if missing_ratio > 0.3:
|
|
291
|
+
raise DataValidationError(
|
|
292
|
+
field="missing_data_ratio",
|
|
293
|
+
value=missing_ratio,
|
|
294
|
+
message=f"Dataset has {missing_ratio:.1%} missing values, exceeds 30% threshold"
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
# 4\. Class Imbalance Check
|
|
298
|
+
class_ratio = df[target_col].value_counts().min() / df[target_col].value_counts().max()
|
|
299
|
+
if class_ratio < 0.1:
|
|
300
|
+
raise DataImbalanceError(ratio=class_ratio, threshold=0.1)
|
|
301
|
+
|
|
302
|
+
# 5\. Feature Engineering
|
|
303
|
+
try:
|
|
304
|
+
df['log_revenue'] = np.log(df['revenue'] + 1)
|
|
305
|
+
except Exception as e:
|
|
306
|
+
raise FeatureEngineeringError("log_transform", cause=str(e))
|
|
307
|
+
|
|
308
|
+
# 6\. Model Training
|
|
309
|
+
try:
|
|
310
|
+
model = RandomForestClassifier(n_estimators=100)
|
|
311
|
+
X = df.drop(columns=[target_col])
|
|
312
|
+
y = df[target_col]
|
|
313
|
+
model.fit(X, y)
|
|
314
|
+
except Exception as e:
|
|
315
|
+
raise ModelTrainingError("RandomForest", message=f"Training failed: {e}")
|
|
316
|
+
|
|
317
|
+
# 7\. Model Validation
|
|
318
|
+
train_score = model.score(X, y)
|
|
319
|
+
if train_score < 0.6:
|
|
320
|
+
raise UnderfittingError(train_metric=train_score, threshold=0.6)
|
|
321
|
+
|
|
322
|
+
return model
|
|
323
|
+
|
|
324
|
+
# Usage
|
|
325
|
+
if __name__ == "__main__":
|
|
326
|
+
try:
|
|
327
|
+
model = ml_pipeline("customer_data.csv", "churned")
|
|
328
|
+
print("✅ Pipeline completed successfully!")
|
|
329
|
+
except DataLoadingError as e:
|
|
330
|
+
print(f"❌ Data loading failed: {e}")
|
|
331
|
+
except MissingColumnError as e:
|
|
332
|
+
print(f"❌ Schema validation failed: {e}")
|
|
333
|
+
except DataImbalanceError as e:
|
|
334
|
+
print(f"⚠️ Data quality issue: {e}")
|
|
335
|
+
except ModelTrainingError as e:
|
|
336
|
+
print(f"❌ Model training failed: {e}")
|
|
337
|
+
except Exception as e:
|
|
338
|
+
print(f"💥 Unexpected error: {e}")
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## 🤝 Contributing
|
|
342
|
+
|
|
343
|
+
We welcome contributions! See our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
# Development setup
|
|
347
|
+
git clone https://github.com/DiogoRibeiro7/DataExcept.git
|
|
348
|
+
cd DataExcept
|
|
349
|
+
make install # poetry install --with dev,docs
|
|
350
|
+
pre-commit install
|
|
351
|
+
|
|
352
|
+
make check # lint, formatting, mypy and tests - everything CI runs
|
|
353
|
+
make help # list all targets
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Please also read the [Code of Conduct](CODE_OF_CONDUCT.md). Security issues go
|
|
357
|
+
through [SECURITY.md](SECURITY.md), not the public issue tracker.
|
|
358
|
+
|
|
359
|
+
## 📖 Documentation
|
|
360
|
+
|
|
361
|
+
- **Full Documentation**: [diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/)
|
|
362
|
+
- **API Reference**: [API Docs](https://diogoribeiro7.github.io/DataExcept/api/)
|
|
363
|
+
- **Advanced Usage**: [Advanced Guide](https://diogoribeiro7.github.io/DataExcept/advanced_usage/)
|
|
364
|
+
- **CLI Reference**: [CLI Guide](https://diogoribeiro7.github.io/DataExcept/cli/)
|
|
365
|
+
- **Changelog**: [CHANGELOG.md](CHANGELOG.md)
|
|
366
|
+
|
|
367
|
+
## 🎓 Citation
|
|
368
|
+
|
|
369
|
+
If you use DataExcept in your research, please cite it:
|
|
370
|
+
|
|
371
|
+
```bibtex
|
|
372
|
+
@software{ribeiro_dataexcept_2025,
|
|
373
|
+
author = {Ribeiro, Diogo},
|
|
374
|
+
title = {DataExcept: Structured Exception Handling for Data Science},
|
|
375
|
+
url = {https://github.com/DiogoRibeiro7/DataExcept},
|
|
376
|
+
version = {0.1.0},
|
|
377
|
+
year = {2025},
|
|
378
|
+
publisher = {GitHub}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## 📄 License
|
|
383
|
+
|
|
384
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
385
|
+
|
|
386
|
+
## 🏆 About the Author
|
|
387
|
+
|
|
388
|
+
**Diogo Ribeiro** is a Lead Data Scientist at Mysense.ai and researcher/instructor at ESMAD (Instituto Politécnico do Porto). With expertise in machine learning, statistical analysis, and production ML systems, he created DataExcept to solve real-world error handling challenges in data science workflows.
|
|
389
|
+
|
|
390
|
+
- 🔗 **ORCID**: [0009-0001-2022-7072](https://orcid.org/0009-0001-2022-7072)
|
|
391
|
+
- 🌐 **Website**: [diogoribeiro7.github.io](https://diogoribeiro7.github.io/)
|
|
392
|
+
- 🏢 **Affiliation**: ESMAD - Instituto Politécnico do Porto
|
|
393
|
+
|
|
394
|
+
--------------------------------------------------------------------------------
|
|
395
|
+
|
|
396
|
+
⭐ **Star this repo** if DataExcept helps you build better data pipelines!
|
|
397
|
+
|