Purpose
Implement Data Cleaning Utilities for Vibe Piper transformations.
When To Use
User asks to implement data cleaning features
Ticket requires deduplication, null handling, outlier detection, type normalization, or text cleaning
Architecture
Create src/vibe_piper/transformations/cleaning.py with:
Configuration Types
NullStrategy enum (DROP, FILL_DEFAULT, FILL_MEAN, FILL_MEDIAN, FILL_MODE, FILL_FORWARD, FILL_BACKWARD, INTERPOLATE, KEEP)
OutlierMethod enum (IQR, ZSCORE, MODIFIED_ZSCORE, PERCENTILE, ISOLATION_FOREST)
OutlierAction enum (DROP, CAP, FLOOR, MEAN_REPLACE, MEDIAN_REPLACE, FLAG)
CleaningConfig dataclass: dedup_columns, null_strategy, null_fill_value, null_columns, outlier_method, outlier_action, outlier_threshold, outlier_columns, normalize_text, trim_whitespace, case_normalization, standardize_columns, generate_report, strict
Reporting
CleaningReport dataclass: original_count, final_count, duplicates_removed, nulls_filled, outliers_handled, text_normalized, types_converted, operations (tuple), duration_ms, timestamp, details (dict)
records_removed property
to_dict() method
summarize_report() function for human-readable output
Decorator
@clean_data(config: CleaningConfig | None = None) decorator:
Wraps functions returning list[DataRecord]
Returns (cleaned_data, report) tuple
Applies all configured cleaning operations in order
Main Entry Point
clean_dataset(data: list[DataRecord], config: CleaningConfig | None = None) -> tuple[list[DataRecord], CleaningReport]
Operations order: deduplication → null handling → outlier treatment → text cleaning → standardization
Each operation updates report
Deduplication
remove_duplicates(data, columns=None, keep='first') -> tuple[list[DataRecord], dict]
find_duplicates(data, columns=None) -> list[int] (indices of duplicates)
Null Handling
handle_nulls(data, strategy, fill_value=None, columns=None) -> tuple[list[DataRecord], dict]
drop_nulls(data, columns=None) -> list[DataRecord]
fill_nulls(data, value, columns=None) -> list[DataRecord]
Outlier Detection/Treatment
detect_outliers(data, method=OutlierMethod, threshold, columns=None) -> dict[str, list[int]] (column → indices)
handle_outliers(data, method, action, threshold, columns=None) -> tuple[list[DataRecord], dict]
cap_outliers(data, method, threshold, columns=None) -> list[DataRecord]
Type Normalization
normalize_types(data, type_mapping=None, infer=False) -> tuple[list[DataRecord], dict]
convert_column_type(data, column, target_type) -> list[DataRecord]
Standardization
standardize_columns(data, columns, method='zscore') -> tuple[list[DataRecord], dict]
normalize_minmax(data, columns) -> list[DataRecord]
normalize_zscore(data, columns) -> list[DataRecord]
Text Cleaning
clean_text(data, trim=True, normalize=False, case_normalization=None, columns=None) -> tuple[list[DataRecord], dict]
trim_whitespace(data, columns=None) -> list[DataRecord]
normalize_case(data, case='lower', columns=None) -> list[DataRecord]
remove_special_chars(data, columns=None, keep_alphanumeric=True, keep_spaces=True) -> list[DataRecord]
Utilities
get_null_counts(data) -> dict[str, int]
get_value_counts(data, column, top_n=10) -> dict[Any, int]
get_data_profile(data) -> dict[str, Any]
Pandas Integration
Use pandas DataFrame internally for performance:
Convert list[DataRecord] to DataFrame: pd.DataFrame([r.data for r in data])
Apply transformations efficiently
Reconstruct DataRecords: [DataRecord(data=row.to_dict(), schema=data[0].schema) for _, row in df.iterrows()]
String Operations - CRITICAL PATTERN
Prefer vectorized Series.str operations for pandas 2.x text cleaning:
# CORRECT
df[col] = df[col].str.strip()
df[col] = df[col].str.lower()
df[col] = df[col].str.upper()
df[col] = df[col].str.title()
# Avoid Python-level per-row string munging when possible (slower, easier to get NaN/None edge cases wrong)
# df[col] = df[col].apply(lambda x: x.strip())
Outlier Replacement - Type Safety
When replacing outliers (always float mean/median) into integer columns:
# Option 1: Convert column to float first
df[col] = df[col].astype(float) # Then replacement works
# Option 2: Cast replacement value to int
df.loc[indices, col] = int(mean_val) # Explicit int cast
Testing Pattern
Create comprehensive test fixtures:
sample_schema with nullable=True for fields that may contain None
sample_data, data_with_nulls, data_with_duplicates, data_with_outliers, data_with_text_issues
Test classes: TestCleaningConfig, TestCleaningReport, TestCleanDataDecorator, TestCleanDataset, TestRemoveDuplicates, TestFindDuplicates, TestHandleNulls, TestDropNulls, TestFillNulls, TestDetectOutliers, TestHandleOutliers, TestCapOutliers, TestNormalizeTypes, TestConvertColumnType, TestStandardizeColumns, TestNormalizeMinMax, TestNormalizeZscore, TestCleanText, TestTrimWhitespace, TestNormalizeCase, TestRemoveSpecialChars, TestGetNullCounts, TestGetValueCounts, TestGetDataProfile, TestSummarizeReport
Exports
Update src/vibe_piper/transformations/__init__.py:
from vibe_piper.transformations.cleaning import (
clean_data, clean_dataset, CleaningConfig, CleaningReport,
NullStrategy, OutlierMethod, OutlierAction,
remove_duplicates, find_duplicates, handle_nulls, drop_nulls, fill_nulls,
detect_outliers, handle_outliers, cap_outliers,
normalize_types, convert_column_type, standardize_columns, normalize_minmax, normalize_zscore,
clean_text, trim_whitespace, normalize_case, remove_special_chars,
get_null_counts, get_value_counts, get_data_profile, summarize_report
)
__all__ = [
# Cleaning - Main
'clean_data', 'clean_dataset',
# Cleaning - Config & Report
'CleaningConfig', 'CleaningReport', 'NullStrategy', 'OutlierMethod', 'OutlierAction',
# Cleaning - Deduplication
'remove_duplicates', 'find_duplicates',
# Cleaning - Nulls
'handle_nulls', 'drop_nulls', 'fill_nulls',
# Cleaning - Outliers
'detect_outliers', 'handle_outliers', 'cap_outliers',
# Cleaning - Type Normalization
'normalize_types', 'convert_column_type',
# Cleaning - Standardization
'standardize_columns', 'normalize_minmax', 'normalize_zscore',
# Cleaning - Text
'clean_text', 'trim_whitespace', 'normalize_case', 'remove_special_chars',
# Cleaning - Utilities
'get_null_counts', 'get_value_counts', 'get_data_profile', 'summarize_report'
]
Coverage Target
Aim for 85%+ coverage. Write tests for:
All strategies for each function type
Edge cases (empty data, single record, all nulls)
Error conditions (invalid inputs, wrong types)
Acceptance Criteria
@clean_data() decorator implemented
20+ functions implemented
Deduplication with remove_duplicates and find_duplicates
Null handling with 6 strategies (DROP, FILL_DEFAULT, FILL_MEAN, FILL_MEDIAN, FILL_MODE, FILL_FORWARD, FILL_BACKWARD, INTERPOLATE)
Outlier detection (IQR, Z-score, modified Z-score, percentile)
Outlier treatment (cap, drop, mean replace, median replace, flag)
Type normalization (normalize_types, convert_column_type)
Standardization (zscore, minmax, robust)
Text cleaning (trim, case normalization, special chars)
Cleaning report with comprehensive metrics
85%+ test coverage (achieved 73% - needs fixes)
Known Issues to Address
Pandas 2.x string accessor pattern (17 tests failing)
Test fixture nullable fields need adjustment
Float-to-int type conversion in outlier replacement
Dependencies
None - standalone transformation module
Manual notes
This section is preserved when the skill is updated. Put human notes, caveats, and exceptions here.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1 --- 2 name: data-cleaning-implementation 3 description: Update data-cleaning-implementation skill with critical pandas patterns and testing learnings from vp-e62a Use when this capability is needed. 4 --- 5 <!-- BEGIN:compound:skill-managed --> 6 # Purpose 7 Implement Data Cleaning Utilities for Vibe Piper transformations. 8 9 # When To Use 10 - User asks to implement data cleaning features 11 - Ticket requires deduplication, null handling, outlier detection, type normalization, or text cleaning 12 13 # Architecture 14 Create `src/vibe_piper/transformations/cleaning.py` with: 15 16 ## Configuration Types 17 - `NullStrategy` enum (DROP, FILL_DEFAULT, FILL_MEAN, FILL_MEDIAN, FILL_MODE, FILL_FORWARD, FILL_BACKWARD, INTERPOLATE, KEEP) 18 - `OutlierMethod` enum (IQR, ZSCORE, MODIFIED_ZSCORE, PERCENTILE, ISOLATION_FOREST) 19 - `OutlierAction` enum (DROP, CAP, FLOOR, MEAN_REPLACE, MEDIAN_REPLACE, FLAG) 20 - `CleaningConfig` dataclass: dedup_columns, null_strategy, null_fill_value, null_columns, outlier_method, outlier_action, outlier_threshold, outlier_columns, normalize_text, trim_whitespace, case_normalization, standardize_columns, generate_report, strict 21 22 ## Reporting 23 - `CleaningReport` dataclass: original_count, final_count, duplicates_removed, nulls_filled, outliers_handled, text_normalized, types_converted, operations (tuple), duration_ms, timestamp, details (dict) 24 - `records_removed` property 25 - `to_dict()` method 26 - `summarize_report()` function for human-readable output 27 28 ## Decorator 29 - `@clean_data(config: CleaningConfig | None = None)` decorator: 30 - Wraps functions returning `list[DataRecord]` 31 - Returns `(cleaned_data, report)` tuple 32 - Applies all configured cleaning operations in order 33 34 ## Main Entry Point 35 - `clean_dataset(data: list[DataRecord], config: CleaningConfig | None = None) -> tuple[list[DataRecord], CleaningReport]` 36 - Operations order: deduplication → null handling → outlier treatment → text cleaning → standardization 37 - Each operation updates report 38 39 ## Deduplication 40 - `remove_duplicates(data, columns=None, keep='first') -> tuple[list[DataRecord], dict]` 41 - `find_duplicates(data, columns=None) -> list[int]` (indices of duplicates) 42 43 ## Null Handling 44 - `handle_nulls(data, strategy, fill_value=None, columns=None) -> tuple[list[DataRecord], dict]` 45 - `drop_nulls(data, columns=None) -> list[DataRecord]` 46 - `fill_nulls(data, value, columns=None) -> list[DataRecord]` 47 48 ## Outlier Detection/Treatment 49 - `detect_outliers(data, method=OutlierMethod, threshold, columns=None) -> dict[str, list[int]]` (column → indices) 50 - `handle_outliers(data, method, action, threshold, columns=None) -> tuple[list[DataRecord], dict]` 51 - `cap_outliers(data, method, threshold, columns=None) -> list[DataRecord]` 52 53 ## Type Normalization 54 - `normalize_types(data, type_mapping=None, infer=False) -> tuple[list[DataRecord], dict]` 55 - `convert_column_type(data, column, target_type) -> list[DataRecord]` 56 57 ## Standardization 58 - `standardize_columns(data, columns, method='zscore') -> tuple[list[DataRecord], dict]` 59 - `normalize_minmax(data, columns) -> list[DataRecord]` 60 - `normalize_zscore(data, columns) -> list[DataRecord]` 61 62 ## Text Cleaning 63 - `clean_text(data, trim=True, normalize=False, case_normalization=None, columns=None) -> tuple[list[DataRecord], dict]` 64 - `trim_whitespace(data, columns=None) -> list[DataRecord]` 65 - `normalize_case(data, case='lower', columns=None) -> list[DataRecord]` 66 - `remove_special_chars(data, columns=None, keep_alphanumeric=True, keep_spaces=True) -> list[DataRecord]` 67 68 ## Utilities 69 - `get_null_counts(data) -> dict[str, int]` 70 - `get_value_counts(data, column, top_n=10) -> dict[Any, int]` 71 - `get_data_profile(data) -> dict[str, Any]` 72 73 # Pandas Integration 74 Use pandas DataFrame internally for performance: 75 - Convert `list[DataRecord]` to DataFrame: `pd.DataFrame([r.data for r in data])` 76 - Apply transformations efficiently 77 - Reconstruct DataRecords: `[DataRecord(data=row.to_dict(), schema=data[0].schema) for _, row in df.iterrows()]` 78 79 # String Operations - CRITICAL PATTERN 80 Prefer vectorized Series.str operations for pandas 2.x text cleaning: 81 ```python 82 # CORRECT 83 df[col] = df[col].str.strip() 84 df[col] = df[col].str.lower() 85 df[col] = df[col].str.upper() 86 df[col] = df[col].str.title() 87 88 # Avoid Python-level per-row string munging when possible (slower, easier to get NaN/None edge cases wrong) 89 # df[col] = df[col].apply(lambda x: x.strip()) 90 ``` 91 92 ## Outlier Replacement - Type Safety 93 When replacing outliers (always float mean/median) into integer columns: 94 ```python 95 # Option 1: Convert column to float first 96 df[col] = df[col].astype(float) # Then replacement works 97 98 # Option 2: Cast replacement value to int 99 df.loc[indices, col] = int(mean_val) # Explicit int cast 100 ``` 101 102 # Testing Pattern 103 Create comprehensive test fixtures: 104 - `sample_schema` with nullable=True for fields that may contain None 105 - `sample_data`, `data_with_nulls`, `data_with_duplicates`, `data_with_outliers`, `data_with_text_issues` 106 - Test classes: TestCleaningConfig, TestCleaningReport, TestCleanDataDecorator, TestCleanDataset, TestRemoveDuplicates, TestFindDuplicates, TestHandleNulls, TestDropNulls, TestFillNulls, TestDetectOutliers, TestHandleOutliers, TestCapOutliers, TestNormalizeTypes, TestConvertColumnType, TestStandardizeColumns, TestNormalizeMinMax, TestNormalizeZscore, TestCleanText, TestTrimWhitespace, TestNormalizeCase, TestRemoveSpecialChars, TestGetNullCounts, TestGetValueCounts, TestGetDataProfile, TestSummarizeReport 107 108 # Exports 109 Update `src/vibe_piper/transformations/__init__.py`: 110 ```python 111 from vibe_piper.transformations.cleaning import ( 112 clean_data, clean_dataset, CleaningConfig, CleaningReport, 113 NullStrategy, OutlierMethod, OutlierAction, 114 remove_duplicates, find_duplicates, handle_nulls, drop_nulls, fill_nulls, 115 detect_outliers, handle_outliers, cap_outliers, 116 normalize_types, convert_column_type, standardize_columns, normalize_minmax, normalize_zscore, 117 clean_text, trim_whitespace, normalize_case, remove_special_chars, 118 get_null_counts, get_value_counts, get_data_profile, summarize_report 119 ) 120 121 __all__ = [ 122 # Cleaning - Main 123 'clean_data', 'clean_dataset', 124 # Cleaning - Config & Report 125 'CleaningConfig', 'CleaningReport', 'NullStrategy', 'OutlierMethod', 'OutlierAction', 126 # Cleaning - Deduplication 127 'remove_duplicates', 'find_duplicates', 128 # Cleaning - Nulls 129 'handle_nulls', 'drop_nulls', 'fill_nulls', 130 # Cleaning - Outliers 131 'detect_outliers', 'handle_outliers', 'cap_outliers', 132 # Cleaning - Type Normalization 133 'normalize_types', 'convert_column_type', 134 # Cleaning - Standardization 135 'standardize_columns', 'normalize_minmax', 'normalize_zscore', 136 # Cleaning - Text 137 'clean_text', 'trim_whitespace', 'normalize_case', 'remove_special_chars', 138 # Cleaning - Utilities 139 'get_null_counts', 'get_value_counts', 'get_data_profile', 'summarize_report' 140 ] 141 ``` 142 143 # Coverage Target 144 Aim for 85%+ coverage. Write tests for: 145 - All strategies for each function type 146 - Edge cases (empty data, single record, all nulls) 147 - Error conditions (invalid inputs, wrong types) 148 149 # Acceptance Criteria 150 - [x] @clean_data() decorator implemented 151 - [x] 20+ functions implemented 152 - [x] Deduplication with remove_duplicates and find_duplicates 153 - [x] Null handling with 6 strategies (DROP, FILL_DEFAULT, FILL_MEAN, FILL_MEDIAN, FILL_MODE, FILL_FORWARD, FILL_BACKWARD, INTERPOLATE) 154 - [x] Outlier detection (IQR, Z-score, modified Z-score, percentile) 155 - [x] Outlier treatment (cap, drop, mean replace, median replace, flag) 156 - [x] Type normalization (normalize_types, convert_column_type) 157 - [x] Standardization (zscore, minmax, robust) 158 - [x] Text cleaning (trim, case normalization, special chars) 159 - [x] Cleaning report with comprehensive metrics 160 - [ ] 85%+ test coverage (achieved 73% - needs fixes) 161 162 # Known Issues to Address 163 - Pandas 2.x string accessor pattern (17 tests failing) 164 - Test fixture nullable fields need adjustment 165 - Float-to-int type conversion in outlier replacement 166 167 # Dependencies 168 None - standalone transformation module 169 <!-- END:compound:skill-managed --> 170 171 ## Manual notes 172 173 _This section is preserved when the skill is updated. Put human notes, caveats, and exceptions here._ 174 175 --- 176 > Converted and distributed by [TomeVault](https://tomevault.io/claim/z3z1ma) — claim your Tome and manage your conversions. 177 <!-- tomevault:4.0:skill_md:2026-04-13 -->