Skip to main content

Intuitive, chainable data cleaning and validation for data engineers and analysts

Project description

🧹 DataWrangle

Transform messy data into pristine datasets with elegance

Intuitive data cleaning, validation, and profiling for Python — Reduce boilerplate, catch errors early, and ship reliable data pipelines.


⭐ What's New

v0.1.0 includes must-have utilities for data engineers:

🔧 Column Operations - Standardize names, drop null columns, split/combine
📅 Date/Time Utilities - Multi-format parsing, feature extraction
🎲 Data Sampling - Stratified sampling, train/test splits
🔍 Data Comparison - DataFrame diffs, schema drift detection
Memory Optimization - Automatic dtype optimization

See examples below


📦 Installation

pip install datawrangle

🚀 Quick Start

import datawrangle as dw
import pandas as pd

# Load messy data
df = pd.read_csv('messy_data.csv')

# Clean with a simple config
cleaned = dw.clean_dataframe(df, config={
    'drop_duplicates': {'subset': ['id']},
    'fill_missing': {'age': 'mean', 'name': 'Unknown'},
    'convert_types': {'date': 'datetime', 'price': 'float'},
    'remove_outliers': {'column': 'salary', 'method': 'iqr'}
})

# Validate against schema
schema = {
    'id': {'type': int, 'constraints': {'non_null': True}},
    'email': {'type': str, 'constraints': {'regex': r'^[\w\.-]+@[\w\.-]+\.\w+$'}}
}
report = dw.validate_schema(cleaned, schema)

# Profile data quality
profile = dw.profile_data(cleaned, output='json')
print(f"Cleaned {len(df)}{len(cleaned)} rows")
print(f"Validation: {report['status']}")

Output:

Cleaned 6 → 4 rows
Validation: pass
Missing values filled, outliers removed

Or use the chainable API:

result = (
    dw.DataWrangleFrame(df)
    .drop_duplicates(subset=['id'])
    .fill_missing({'age': 'mean'})
    .normalize_text('email', {'lowercase': True})
    .validate(schema)
    .get_dataframe()
)

print(result[['id', 'name', 'email']].head())

Output:

   id     name              email
0   1    Alice  alice@example.com
1   2      Bob       bob@test.com
3   3  Charlie   charlie@demo.com
4   4    Diana  diana@company.com
5   5      Eve       eve@mail.com

✨ Features

Data Cleaning

  • Drop duplicates with flexible config
  • Fill missing values (mean, median, mode, forward/backward fill, custom)
  • Type conversion (int, float, datetime, string)
  • Outlier removal (IQR, z-score methods)

Schema Validation

  • Type checking with detailed constraints
  • Regex patterns, value ranges, date ranges
  • Non-null and positivity checks
  • Detailed error reporting

Text Normalization

  • Case conversion, punctuation removal
  • Pattern removal (URLs, phone numbers, etc.)
  • Whitespace normalization
  • Optional NLP (tokenization, stemming, lemmatization)

Data Profiling

  • Missing value analysis
  • Duplicate detection
  • Statistical summaries
  • Export to JSON, HTML, or Markdown
profile = dw.profile_data(df, output='summary')
print(f"Total rows: {profile['overview']['total_rows']}")
print(f"Duplicates: {profile['overview']['duplicate_rows']}")
# Output: Total rows: 5, Duplicates: 0

Plugin System

  • Register custom cleaning functions
  • Extend with domain-specific logic
  • Reusable validation rules

Column Operations ⭐ NEW

  • Standardize column names (snake_case, camelCase)
  • Drop columns with high null percentages
  • Reorder, split, and combine columns
  • Memory optimization and dtype inference

Date/Time Utilities ⭐ NEW

  • Parse dates with multiple format support
  • Extract date features (year, month, weekday, etc.)
  • Calculate date differences
  • Timezone handling

Data Sampling ⭐ NEW

  • Random and stratified sampling
  • Train/test splits with stratification
  • First/last N rows sampling

Data Comparison ⭐ NEW

  • Compare two DataFrames
  • Detect schema drift
  • Find row and value differences

📋 Examples

E-commerce Data Pipeline

import datawrangle as dw

# Clean and validate customer data
clean_customers = (
    dw.DataWrangleFrame(raw_customers)
    .drop_duplicates(subset=['customer_id'])
    .fill_missing({'lifetime_value': 0})
    .convert_types({'customer_id': 'int', 'registration_date': 'datetime'})
    .normalize_text('email', {'lowercase': True, 'strip': True})
    .remove_outliers('lifetime_value', method='iqr')
    .validate(customer_schema)
    .get_dataframe()
)

# Generate quality report
report = dw.profile_data(clean_customers, output='html')
print(f"Original: {len(raw_customers)} rows → Cleaned: {len(clean_customers)} rows")

Output:

Original: 7 rows → Cleaned: 5 rows
Duplicates removed, missing values filled, outliers detected

Custom Business Logic

@dw.register_cleaner
def standardize_country_codes(df):
    """Convert country names to ISO codes."""
    country_map = {
        'USA': 'US', 'United States': 'US',
        'UK': 'GB', 'United Kingdom': 'GB'
    }
    if 'country' in df.columns:
        df['country'] = df['country'].replace(country_map)
    return df

# Use your custom cleaner
df = dw.clean_dataframe(raw_data, custom_cleaners=['standardize_country_codes'])
print(df[['name', 'country']].head())

Output:

        name country
0  Product A      US
1  Product B      US
2  Product C      GB
3  Product D      GB
4  Product E  Germany

Column Standardization & Date Features

import datawrangle as dw

# Standardize messy column names
df = dw.standardize_column_names(df, style='snake_case')
# 'User Name' → 'user_name', 'Customer-ID' → 'customer_id'

# Drop columns with too many nulls
df = dw.drop_columns_with_nulls(df, threshold=0.5)  # >50% nulls

# Parse dates with multiple formats
df = dw.parse_dates(df, 'registration_date', formats=['%Y-%m-%d', '%d/%m/%Y'])

# Extract date features for ML
df = dw.extract_date_features(df, 'timestamp', ['year', 'month', 'is_weekend'])

# Optimize memory usage
df = dw.optimize_dtypes(df)
print(f"Memory optimized: {savings}% reduction")

Output:

Column names standardized: 7 columns
Dropped 2 columns with >50% nulls
Memory optimized: 35% reduction

Data Sampling & Comparison

# Stratified sampling for ML
train, test = dw.create_train_test_split(df, test_size=0.2, stratify_by='category')
print(f"Train: {len(train)}, Test: {len(test)}")

# Compare DataFrames for drift detection
drift = dw.find_schema_drift(current_data, expected_schema)
if drift['has_drift']:
    print(f"⚠️ Schema drift detected: {drift['changes']}")

# Sample 10% of data for quick analysis
sample = dw.sample_data(df, frac=0.1, method='random')

Output:

Train: 800, Test: 200
Schema drift detected: ['Type change in price: int64 → float64']

🎯 Use Cases

  • ETL/ELT Pipelines - Clean, standardize, validate before loading
  • ML Preprocessing - Sample, feature engineering, train/test splits
  • API Integration - Handle inconsistent formats, detect schema drift
  • Data Quality Reports - Automated profiling with exports
  • Compliance - Validate schemas, track data lineage
  • Data Migration - Compare datasets, detect changes, optimize storage
  • Production Monitoring - Detect schema drift in real-time

🛠️ Development

# Clone and install
git clone https://github.com/nii-dhii/DataWrangle.git
cd DataWrangle
pip install -e .[dev]

# Run tests
pytest tests/ -v --cov=datawrangle

# Format code
black datawrangle tests

# Lint code
flake8 datawrangle tests

🤝 Contributing

Contributions welcome! Whether it's bug reports, features, or docs.

Built with ❤️ by @nii-dhii

📚 Resources

📄 License

MIT License - see LICENSE file for details


⭐ Star us on GitHub • Made with ❤️ for the data community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

datawrangle-0.1.0.tar.gz (30.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

datawrangle-0.1.0-py3-none-any.whl (34.0 kB view details)

Uploaded Python 3

File details

Details for the file datawrangle-0.1.0.tar.gz.

File metadata

  • Download URL: datawrangle-0.1.0.tar.gz
  • Upload date:
  • Size: 30.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for datawrangle-0.1.0.tar.gz
Algorithm Hash digest
SHA256 91382a3cc5fea20a3c2f0cb361ac5441b8d2564816f127750e072a829ab437bf
MD5 f7226cd39d0bc1eb37ab2e85d8aad90f
BLAKE2b-256 c17a628ace364a4a5bc254cb18b1dc2f823d00edce313c623d01291668ce4c7f

See more details on using hashes here.

File details

Details for the file datawrangle-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: datawrangle-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 34.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for datawrangle-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2e2364c96550ab1830c7ced6de46f5041521997bcd4ec28ae35ba18e43d80e56
MD5 45366d889c1fd9725c901297e70a5708
BLAKE2b-256 4e158cd638d919089c9457cc66d250881ed8f25bca359443c00758233eb58e01

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page