ravenclaw 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.
- ravenclaw-0.1.0/PKG-INFO +281 -0
- ravenclaw-0.1.0/README.md +255 -0
- ravenclaw-0.1.0/ravenclaw/__init__.py +23 -0
- ravenclaw-0.1.0/ravenclaw/clustering/__init__.py +5 -0
- ravenclaw-0.1.0/ravenclaw/clustering/kmeans/__init__.py +7 -0
- ravenclaw-0.1.0/ravenclaw/clustering/kmeans/auto_cluster.py +155 -0
- ravenclaw-0.1.0/ravenclaw/clustering/kmeans/auto_kmeans.py +286 -0
- ravenclaw-0.1.0/ravenclaw/clustering/kmeans/cluster.py +62 -0
- ravenclaw-0.1.0/ravenclaw/clustering/kmeans/k_selection.py +96 -0
- ravenclaw-0.1.0/ravenclaw/clustering/kmeans/preprocessing.py +24 -0
- ravenclaw-0.1.0/ravenclaw/clustering/utils.py +77 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/__init__.py +29 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/__init__.py +24 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/convert_date_to_day_of_week.py +80 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/convert_date_to_day_of_year.py +78 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/convert_datetime_to_day_of_year.py +78 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/convert_datetime_to_hour_of_day.py +78 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/convert_time_to_hour_of_day.py +83 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/convert_to_sin_cos.py +31 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/encode_day_of_week.py +107 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/encode_day_of_year.py +68 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/encode_hour_of_day.py +82 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_conversion/encode_one_hot.py +110 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/__init__.py +26 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/column_classifier.py +48 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/column_type_detectors.py +495 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/find_categorical_columns.py +54 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/find_date_columns.py +127 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/find_day_of_week_columns.py +121 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/find_float_columns.py +46 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/find_integer_columns.py +66 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/find_numeric_columns.py +46 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/column_detection/find_time_columns.py +81 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/__init__.py +25 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/base_encoder.py +58 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_date_to_day_of_week_converter.py +101 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_date_to_day_of_year_converter.py +116 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_datetime_converter.py +118 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_day_of_week_encoder.py +77 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_day_of_year_encoder.py +77 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_hour_of_day_encoder.py +116 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_non_numeric_encoder.py +158 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_one_hot_encoder.py +83 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_string_temporal_converter.py +267 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/df_time_to_hour_of_day_converter.py +99 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/df_encoders/feature_engineering_pipeline.py +255 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/encode_categorical_features.py +51 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/utils/__init__.py +27 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/utils/sample_size_calculators.py +100 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/utils/sampling_helper.py +131 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/utils/string_temporal_detector.py +299 -0
- ravenclaw-0.1.0/ravenclaw/preprocessing/utils/value_constraints.py +53 -0
- ravenclaw-0.1.0/ravenclaw/wrangling/__init__.py +37 -0
- ravenclaw-0.1.0/ravenclaw/wrangling/column_reordering.py +129 -0
- ravenclaw-0.1.0/ravenclaw/wrangling/join_wisely.py +164 -0
- ravenclaw-0.1.0/ravenclaw/wrangling/select_extreme.py +210 -0
- ravenclaw-0.1.0/ravenclaw/wrangling/split_rows.py +103 -0
- ravenclaw-0.1.0/ravenclaw/wrangling/standardize_columns.py +163 -0
- ravenclaw-0.1.0/ravenclaw.egg-info/PKG-INFO +281 -0
- ravenclaw-0.1.0/ravenclaw.egg-info/SOURCES.txt +63 -0
- ravenclaw-0.1.0/ravenclaw.egg-info/dependency_links.txt +1 -0
- ravenclaw-0.1.0/ravenclaw.egg-info/requires.txt +8 -0
- ravenclaw-0.1.0/ravenclaw.egg-info/top_level.txt +1 -0
- ravenclaw-0.1.0/setup.cfg +4 -0
- ravenclaw-0.1.0/setup.py +42 -0
ravenclaw-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ravenclaw
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Data wrangling and feature engineering toolkit for pandas DataFrames
|
|
5
|
+
Home-page: https://github.com/idin/ravenclaw
|
|
6
|
+
Author: Idin
|
|
7
|
+
Author-email: py@idin.net
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: pandas>=1.3.0
|
|
20
|
+
Requires-Dist: numpy>=1.20.0
|
|
21
|
+
Requires-Dist: scikit-learn>=1.0.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
24
|
+
Requires-Dist: black; extra == "dev"
|
|
25
|
+
Requires-Dist: flake8; extra == "dev"
|
|
26
|
+
|
|
27
|
+
# Ravenclaw - Intelligent Data Preprocessing and Clustering
|
|
28
|
+
|
|
29
|
+
A comprehensive toolkit for automated feature engineering and clustering with pandas DataFrames. Ravenclaw intelligently detects column types, converts temporal strings, creates cyclical encodings, and performs automatic clustering - all with minimal configuration.
|
|
30
|
+
|
|
31
|
+
## 🎯 Key Features
|
|
32
|
+
|
|
33
|
+
### **Intelligent Preprocessing Pipeline**
|
|
34
|
+
- **Automatic Detection**: Identifies datetime, date, time, and categorical columns in string format
|
|
35
|
+
- **Smart Conversion**: Converts string temporal data to proper types with format learning
|
|
36
|
+
- **Cyclical Encoding**: Creates sin/cos features for temporal data (perfect for ML models)
|
|
37
|
+
- **One-Hot Encoding**: Handles categorical variables with temporal safety checks
|
|
38
|
+
- **Sklearn Compatible**: Standard fit/transform interface for seamless integration
|
|
39
|
+
|
|
40
|
+
### **Advanced Clustering**
|
|
41
|
+
- **AutoKMeans**: Automatically determines optimal number of clusters
|
|
42
|
+
- **Multiple Methods**: Silhouette, Calinski-Harabasz, Davies-Bouldin, and Elbow methods
|
|
43
|
+
- **Built-in Preprocessing**: Integrated scaling and imputation
|
|
44
|
+
- **Column Selection**: Flexible include/exclude functionality
|
|
45
|
+
- **Sklearn Interface**: Drop-in replacement for standard KMeans
|
|
46
|
+
|
|
47
|
+
## 🚀 Quick Start
|
|
48
|
+
|
|
49
|
+
### Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install ravenclaw
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Complete ML Pipeline Example
|
|
56
|
+
|
|
57
|
+
Here's how to preprocess mixed data and cluster it in a single pipeline:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import pandas as pd
|
|
61
|
+
import numpy as np
|
|
62
|
+
from ravenclaw.preprocessing.df_encoders import FeatureEngineeringPipeline
|
|
63
|
+
from ravenclaw.clustering.kmeans import AutoKMeans
|
|
64
|
+
from sklearn.pipeline import Pipeline
|
|
65
|
+
|
|
66
|
+
# Create sample mixed data (the kind you get in real life!)
|
|
67
|
+
np.random.seed(42)
|
|
68
|
+
df = pd.DataFrame({
|
|
69
|
+
'timestamp': ['2023-01-15 14:30:00', '2023-06-30 09:15:00', '2023-12-25 18:45:00'] * 50,
|
|
70
|
+
'date_created': ['2023-01-15', '2023-06-30', '2023-12-25'] * 50,
|
|
71
|
+
'time_of_day': ['14:30:00', '09:15:00', '18:45:00'] * 50,
|
|
72
|
+
'category': ['Premium', 'Standard', 'Basic'] * 50,
|
|
73
|
+
'region': ['North', 'South', 'East', 'West'] * 37 + ['North', 'South', 'East'],
|
|
74
|
+
'revenue': np.random.normal(1000, 200, 150),
|
|
75
|
+
'customer_age': np.random.randint(18, 80, 150),
|
|
76
|
+
'satisfaction': np.random.uniform(1, 5, 150)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
# Add some missing values (real data is messy!)
|
|
80
|
+
df.loc[np.random.choice(150, 10), 'revenue'] = np.nan
|
|
81
|
+
df.loc[np.random.choice(150, 5), 'satisfaction'] = np.nan
|
|
82
|
+
|
|
83
|
+
print(f"Original data: {df.shape[1]} columns")
|
|
84
|
+
print(f"Column types: {df.dtypes.value_counts().to_dict()}")
|
|
85
|
+
|
|
86
|
+
# Step 1: Feature Engineering Pipeline
|
|
87
|
+
# Automatically detects and converts temporal strings, creates cyclical features
|
|
88
|
+
preprocessor = FeatureEngineeringPipeline.create_default()
|
|
89
|
+
processed_df = preprocessor.fit_transform(df)
|
|
90
|
+
|
|
91
|
+
print(f"After preprocessing: {processed_df.shape[1]} columns")
|
|
92
|
+
print(f"New features: {list(processed_df.columns)}")
|
|
93
|
+
|
|
94
|
+
# Step 2: Automatic Clustering with Built-in Imputation
|
|
95
|
+
# AutoKMeans handles missing values and scaling automatically
|
|
96
|
+
clusterer = AutoKMeans(
|
|
97
|
+
max_k=8, # Try up to 8 clusters
|
|
98
|
+
method='silhouette', # Use silhouette score for k-selection
|
|
99
|
+
scale=True, # Scale features (important for clustering)
|
|
100
|
+
impute=True, # Handle missing values
|
|
101
|
+
impute_strategy='median', # Use median for imputation
|
|
102
|
+
ignore_non_numeric=True, # Silently skip non-numeric columns
|
|
103
|
+
random_state=42
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Fit and predict clusters
|
|
107
|
+
cluster_labels = clusterer.fit_predict(processed_df)
|
|
108
|
+
|
|
109
|
+
print(f"Optimal clusters found: {clusterer.n_clusters_}")
|
|
110
|
+
print(f"Cluster distribution: {np.bincount(cluster_labels)}")
|
|
111
|
+
|
|
112
|
+
# Add clusters back to original data for analysis
|
|
113
|
+
df['cluster'] = cluster_labels
|
|
114
|
+
print("\nCluster summary:")
|
|
115
|
+
print(df.groupby('cluster')[['revenue', 'customer_age', 'satisfaction']].mean())
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Sklearn Pipeline Integration
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from sklearn.pipeline import Pipeline
|
|
122
|
+
from sklearn.preprocessing import StandardScaler
|
|
123
|
+
|
|
124
|
+
# Create a complete sklearn pipeline
|
|
125
|
+
ml_pipeline = Pipeline([
|
|
126
|
+
('preprocessing', FeatureEngineeringPipeline.create_default()),
|
|
127
|
+
('clustering', AutoKMeans(max_k=5, scale=True, impute=True))
|
|
128
|
+
])
|
|
129
|
+
|
|
130
|
+
# One-line fit and predict
|
|
131
|
+
cluster_labels = ml_pipeline.fit_predict(df)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 🔧 Advanced Usage
|
|
135
|
+
|
|
136
|
+
### Custom Preprocessing Pipeline
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from ravenclaw.preprocessing.df_encoders import (
|
|
140
|
+
StringTemporalConverter,
|
|
141
|
+
DateTimeConverter,
|
|
142
|
+
DayOfYearEncoder,
|
|
143
|
+
HourOfDayEncoder,
|
|
144
|
+
OneHotEncoder,
|
|
145
|
+
FeatureEngineeringPipeline
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Build custom pipeline with specific encoders
|
|
149
|
+
custom_pipeline = FeatureEngineeringPipeline([
|
|
150
|
+
StringTemporalConverter(), # String temporal → proper types
|
|
151
|
+
DateTimeConverter(), # datetime → day_of_year + hour_of_day
|
|
152
|
+
DayOfYearEncoder(), # day_of_year → sin/cos features
|
|
153
|
+
HourOfDayEncoder(), # hour_of_day → sin/cos features
|
|
154
|
+
OneHotEncoder(exclude_columns=['timestamp']) # Categorical → binary (skip temporal)
|
|
155
|
+
])
|
|
156
|
+
|
|
157
|
+
result = custom_pipeline.fit_transform(df)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Clustering with Column Selection
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# Cluster only specific columns
|
|
164
|
+
clusterer = AutoKMeans(
|
|
165
|
+
include_columns=['revenue', 'customer_age', 'satisfaction'],
|
|
166
|
+
max_k=6,
|
|
167
|
+
method='calinski_harabasz',
|
|
168
|
+
impute=True,
|
|
169
|
+
impute_strategy='mean'
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
labels = clusterer.fit_predict(df)
|
|
173
|
+
|
|
174
|
+
# Get clustering diagnostics
|
|
175
|
+
diagnostics = clusterer.get_diagnostics()
|
|
176
|
+
print(f"Tried k values: {diagnostics['candidate_ks']}")
|
|
177
|
+
print(f"Scores: {diagnostics['scores']}")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Individual Encoder Usage
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from ravenclaw.preprocessing.df_encoders import StringTemporalConverter
|
|
184
|
+
|
|
185
|
+
# Just convert temporal strings
|
|
186
|
+
converter = StringTemporalConverter()
|
|
187
|
+
converter.fit(df)
|
|
188
|
+
|
|
189
|
+
# See what formats were learned
|
|
190
|
+
formats = converter.get_learned_formats()
|
|
191
|
+
print("Detected formats:", formats)
|
|
192
|
+
|
|
193
|
+
# Transform the data
|
|
194
|
+
converted_df = converter.transform(df)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 🧠 How It Works
|
|
198
|
+
|
|
199
|
+
### Intelligent Column Detection
|
|
200
|
+
Ravenclaw uses priority-based classification to handle ambiguous cases:
|
|
201
|
+
```
|
|
202
|
+
datetime > date > time > day_of_week > day_of_year > hour_of_day > categorical
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Automatic K-Selection Methods
|
|
206
|
+
- **Silhouette Score**: Measures cluster cohesion and separation
|
|
207
|
+
- **Calinski-Harabasz**: Ratio of between-cluster to within-cluster variance
|
|
208
|
+
- **Davies-Bouldin**: Average similarity between clusters
|
|
209
|
+
- **Elbow Method**: Finds the "elbow" in the inertia curve
|
|
210
|
+
|
|
211
|
+
### Built-in Imputation
|
|
212
|
+
AutoKMeans includes sklearn's SimpleImputer with strategies:
|
|
213
|
+
- `'mean'`: Replace with column mean (numeric only)
|
|
214
|
+
- `'median'`: Replace with column median (numeric only)
|
|
215
|
+
- `'most_frequent'`: Replace with mode (works for all types)
|
|
216
|
+
- `'constant'`: Replace with a constant value
|
|
217
|
+
|
|
218
|
+
## 📊 What You Get
|
|
219
|
+
|
|
220
|
+
### Before Ravenclaw
|
|
221
|
+
```python
|
|
222
|
+
# Manual feature engineering pain
|
|
223
|
+
df['hour'] = pd.to_datetime(df['timestamp']).dt.hour
|
|
224
|
+
df['day_of_year'] = pd.to_datetime(df['timestamp']).dt.dayofyear
|
|
225
|
+
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
|
|
226
|
+
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
|
|
227
|
+
# ... repeat for every temporal column
|
|
228
|
+
# ... handle missing values
|
|
229
|
+
# ... scale features
|
|
230
|
+
# ... try different k values
|
|
231
|
+
# ... compare clustering metrics
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### After Ravenclaw
|
|
235
|
+
```python
|
|
236
|
+
# One-liner magic ✨
|
|
237
|
+
pipeline = FeatureEngineeringPipeline.create_default()
|
|
238
|
+
processed_df = pipeline.fit_transform(df)
|
|
239
|
+
labels = AutoKMeans(max_k=8).fit_predict(processed_df)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## 🛠️ Development
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Clone and setup
|
|
246
|
+
git clone https://github.com/idin/ravenclaw.git
|
|
247
|
+
cd ravenclaw
|
|
248
|
+
|
|
249
|
+
# Create conda environment
|
|
250
|
+
conda env create -f environment.yml
|
|
251
|
+
conda activate ravenclaw
|
|
252
|
+
|
|
253
|
+
# Install in development mode
|
|
254
|
+
pip install -e .
|
|
255
|
+
|
|
256
|
+
# Run tests (244 tests, all passing!)
|
|
257
|
+
pytest tests/
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## 📈 Project Status
|
|
261
|
+
|
|
262
|
+
- ✅ **244 Tests Passing** - Comprehensive test coverage
|
|
263
|
+
- ✅ **Production Ready** - Used in real ML workflows
|
|
264
|
+
- ✅ **Zero Warnings** - Clean, maintainable codebase
|
|
265
|
+
- ✅ **Sklearn Compatible** - Drop-in replacement for standard tools
|
|
266
|
+
- ✅ **Type Safe** - Full type hints and error handling
|
|
267
|
+
|
|
268
|
+
## 🎯 Perfect For
|
|
269
|
+
|
|
270
|
+
- **Data Scientists**: Spend less time on preprocessing, more on insights
|
|
271
|
+
- **ML Engineers**: Robust, tested pipelines for production
|
|
272
|
+
- **Analysts**: Intelligent automation for common data tasks
|
|
273
|
+
- **Anyone**: Who works with messy, real-world pandas DataFrames
|
|
274
|
+
|
|
275
|
+
## 📄 License
|
|
276
|
+
|
|
277
|
+
MIT License - Use it, love it, contribute to it!
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
*Ravenclaw: Because your data deserves intelligent preprocessing* 🧙♂️✨
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Ravenclaw - Intelligent Data Preprocessing and Clustering
|
|
2
|
+
|
|
3
|
+
A comprehensive toolkit for automated feature engineering and clustering with pandas DataFrames. Ravenclaw intelligently detects column types, converts temporal strings, creates cyclical encodings, and performs automatic clustering - all with minimal configuration.
|
|
4
|
+
|
|
5
|
+
## 🎯 Key Features
|
|
6
|
+
|
|
7
|
+
### **Intelligent Preprocessing Pipeline**
|
|
8
|
+
- **Automatic Detection**: Identifies datetime, date, time, and categorical columns in string format
|
|
9
|
+
- **Smart Conversion**: Converts string temporal data to proper types with format learning
|
|
10
|
+
- **Cyclical Encoding**: Creates sin/cos features for temporal data (perfect for ML models)
|
|
11
|
+
- **One-Hot Encoding**: Handles categorical variables with temporal safety checks
|
|
12
|
+
- **Sklearn Compatible**: Standard fit/transform interface for seamless integration
|
|
13
|
+
|
|
14
|
+
### **Advanced Clustering**
|
|
15
|
+
- **AutoKMeans**: Automatically determines optimal number of clusters
|
|
16
|
+
- **Multiple Methods**: Silhouette, Calinski-Harabasz, Davies-Bouldin, and Elbow methods
|
|
17
|
+
- **Built-in Preprocessing**: Integrated scaling and imputation
|
|
18
|
+
- **Column Selection**: Flexible include/exclude functionality
|
|
19
|
+
- **Sklearn Interface**: Drop-in replacement for standard KMeans
|
|
20
|
+
|
|
21
|
+
## 🚀 Quick Start
|
|
22
|
+
|
|
23
|
+
### Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install ravenclaw
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Complete ML Pipeline Example
|
|
30
|
+
|
|
31
|
+
Here's how to preprocess mixed data and cluster it in a single pipeline:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import pandas as pd
|
|
35
|
+
import numpy as np
|
|
36
|
+
from ravenclaw.preprocessing.df_encoders import FeatureEngineeringPipeline
|
|
37
|
+
from ravenclaw.clustering.kmeans import AutoKMeans
|
|
38
|
+
from sklearn.pipeline import Pipeline
|
|
39
|
+
|
|
40
|
+
# Create sample mixed data (the kind you get in real life!)
|
|
41
|
+
np.random.seed(42)
|
|
42
|
+
df = pd.DataFrame({
|
|
43
|
+
'timestamp': ['2023-01-15 14:30:00', '2023-06-30 09:15:00', '2023-12-25 18:45:00'] * 50,
|
|
44
|
+
'date_created': ['2023-01-15', '2023-06-30', '2023-12-25'] * 50,
|
|
45
|
+
'time_of_day': ['14:30:00', '09:15:00', '18:45:00'] * 50,
|
|
46
|
+
'category': ['Premium', 'Standard', 'Basic'] * 50,
|
|
47
|
+
'region': ['North', 'South', 'East', 'West'] * 37 + ['North', 'South', 'East'],
|
|
48
|
+
'revenue': np.random.normal(1000, 200, 150),
|
|
49
|
+
'customer_age': np.random.randint(18, 80, 150),
|
|
50
|
+
'satisfaction': np.random.uniform(1, 5, 150)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
# Add some missing values (real data is messy!)
|
|
54
|
+
df.loc[np.random.choice(150, 10), 'revenue'] = np.nan
|
|
55
|
+
df.loc[np.random.choice(150, 5), 'satisfaction'] = np.nan
|
|
56
|
+
|
|
57
|
+
print(f"Original data: {df.shape[1]} columns")
|
|
58
|
+
print(f"Column types: {df.dtypes.value_counts().to_dict()}")
|
|
59
|
+
|
|
60
|
+
# Step 1: Feature Engineering Pipeline
|
|
61
|
+
# Automatically detects and converts temporal strings, creates cyclical features
|
|
62
|
+
preprocessor = FeatureEngineeringPipeline.create_default()
|
|
63
|
+
processed_df = preprocessor.fit_transform(df)
|
|
64
|
+
|
|
65
|
+
print(f"After preprocessing: {processed_df.shape[1]} columns")
|
|
66
|
+
print(f"New features: {list(processed_df.columns)}")
|
|
67
|
+
|
|
68
|
+
# Step 2: Automatic Clustering with Built-in Imputation
|
|
69
|
+
# AutoKMeans handles missing values and scaling automatically
|
|
70
|
+
clusterer = AutoKMeans(
|
|
71
|
+
max_k=8, # Try up to 8 clusters
|
|
72
|
+
method='silhouette', # Use silhouette score for k-selection
|
|
73
|
+
scale=True, # Scale features (important for clustering)
|
|
74
|
+
impute=True, # Handle missing values
|
|
75
|
+
impute_strategy='median', # Use median for imputation
|
|
76
|
+
ignore_non_numeric=True, # Silently skip non-numeric columns
|
|
77
|
+
random_state=42
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Fit and predict clusters
|
|
81
|
+
cluster_labels = clusterer.fit_predict(processed_df)
|
|
82
|
+
|
|
83
|
+
print(f"Optimal clusters found: {clusterer.n_clusters_}")
|
|
84
|
+
print(f"Cluster distribution: {np.bincount(cluster_labels)}")
|
|
85
|
+
|
|
86
|
+
# Add clusters back to original data for analysis
|
|
87
|
+
df['cluster'] = cluster_labels
|
|
88
|
+
print("\nCluster summary:")
|
|
89
|
+
print(df.groupby('cluster')[['revenue', 'customer_age', 'satisfaction']].mean())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Sklearn Pipeline Integration
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from sklearn.pipeline import Pipeline
|
|
96
|
+
from sklearn.preprocessing import StandardScaler
|
|
97
|
+
|
|
98
|
+
# Create a complete sklearn pipeline
|
|
99
|
+
ml_pipeline = Pipeline([
|
|
100
|
+
('preprocessing', FeatureEngineeringPipeline.create_default()),
|
|
101
|
+
('clustering', AutoKMeans(max_k=5, scale=True, impute=True))
|
|
102
|
+
])
|
|
103
|
+
|
|
104
|
+
# One-line fit and predict
|
|
105
|
+
cluster_labels = ml_pipeline.fit_predict(df)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 🔧 Advanced Usage
|
|
109
|
+
|
|
110
|
+
### Custom Preprocessing Pipeline
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from ravenclaw.preprocessing.df_encoders import (
|
|
114
|
+
StringTemporalConverter,
|
|
115
|
+
DateTimeConverter,
|
|
116
|
+
DayOfYearEncoder,
|
|
117
|
+
HourOfDayEncoder,
|
|
118
|
+
OneHotEncoder,
|
|
119
|
+
FeatureEngineeringPipeline
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Build custom pipeline with specific encoders
|
|
123
|
+
custom_pipeline = FeatureEngineeringPipeline([
|
|
124
|
+
StringTemporalConverter(), # String temporal → proper types
|
|
125
|
+
DateTimeConverter(), # datetime → day_of_year + hour_of_day
|
|
126
|
+
DayOfYearEncoder(), # day_of_year → sin/cos features
|
|
127
|
+
HourOfDayEncoder(), # hour_of_day → sin/cos features
|
|
128
|
+
OneHotEncoder(exclude_columns=['timestamp']) # Categorical → binary (skip temporal)
|
|
129
|
+
])
|
|
130
|
+
|
|
131
|
+
result = custom_pipeline.fit_transform(df)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Clustering with Column Selection
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# Cluster only specific columns
|
|
138
|
+
clusterer = AutoKMeans(
|
|
139
|
+
include_columns=['revenue', 'customer_age', 'satisfaction'],
|
|
140
|
+
max_k=6,
|
|
141
|
+
method='calinski_harabasz',
|
|
142
|
+
impute=True,
|
|
143
|
+
impute_strategy='mean'
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
labels = clusterer.fit_predict(df)
|
|
147
|
+
|
|
148
|
+
# Get clustering diagnostics
|
|
149
|
+
diagnostics = clusterer.get_diagnostics()
|
|
150
|
+
print(f"Tried k values: {diagnostics['candidate_ks']}")
|
|
151
|
+
print(f"Scores: {diagnostics['scores']}")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Individual Encoder Usage
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from ravenclaw.preprocessing.df_encoders import StringTemporalConverter
|
|
158
|
+
|
|
159
|
+
# Just convert temporal strings
|
|
160
|
+
converter = StringTemporalConverter()
|
|
161
|
+
converter.fit(df)
|
|
162
|
+
|
|
163
|
+
# See what formats were learned
|
|
164
|
+
formats = converter.get_learned_formats()
|
|
165
|
+
print("Detected formats:", formats)
|
|
166
|
+
|
|
167
|
+
# Transform the data
|
|
168
|
+
converted_df = converter.transform(df)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 🧠 How It Works
|
|
172
|
+
|
|
173
|
+
### Intelligent Column Detection
|
|
174
|
+
Ravenclaw uses priority-based classification to handle ambiguous cases:
|
|
175
|
+
```
|
|
176
|
+
datetime > date > time > day_of_week > day_of_year > hour_of_day > categorical
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Automatic K-Selection Methods
|
|
180
|
+
- **Silhouette Score**: Measures cluster cohesion and separation
|
|
181
|
+
- **Calinski-Harabasz**: Ratio of between-cluster to within-cluster variance
|
|
182
|
+
- **Davies-Bouldin**: Average similarity between clusters
|
|
183
|
+
- **Elbow Method**: Finds the "elbow" in the inertia curve
|
|
184
|
+
|
|
185
|
+
### Built-in Imputation
|
|
186
|
+
AutoKMeans includes sklearn's SimpleImputer with strategies:
|
|
187
|
+
- `'mean'`: Replace with column mean (numeric only)
|
|
188
|
+
- `'median'`: Replace with column median (numeric only)
|
|
189
|
+
- `'most_frequent'`: Replace with mode (works for all types)
|
|
190
|
+
- `'constant'`: Replace with a constant value
|
|
191
|
+
|
|
192
|
+
## 📊 What You Get
|
|
193
|
+
|
|
194
|
+
### Before Ravenclaw
|
|
195
|
+
```python
|
|
196
|
+
# Manual feature engineering pain
|
|
197
|
+
df['hour'] = pd.to_datetime(df['timestamp']).dt.hour
|
|
198
|
+
df['day_of_year'] = pd.to_datetime(df['timestamp']).dt.dayofyear
|
|
199
|
+
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
|
|
200
|
+
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
|
|
201
|
+
# ... repeat for every temporal column
|
|
202
|
+
# ... handle missing values
|
|
203
|
+
# ... scale features
|
|
204
|
+
# ... try different k values
|
|
205
|
+
# ... compare clustering metrics
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### After Ravenclaw
|
|
209
|
+
```python
|
|
210
|
+
# One-liner magic ✨
|
|
211
|
+
pipeline = FeatureEngineeringPipeline.create_default()
|
|
212
|
+
processed_df = pipeline.fit_transform(df)
|
|
213
|
+
labels = AutoKMeans(max_k=8).fit_predict(processed_df)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## 🛠️ Development
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Clone and setup
|
|
220
|
+
git clone https://github.com/idin/ravenclaw.git
|
|
221
|
+
cd ravenclaw
|
|
222
|
+
|
|
223
|
+
# Create conda environment
|
|
224
|
+
conda env create -f environment.yml
|
|
225
|
+
conda activate ravenclaw
|
|
226
|
+
|
|
227
|
+
# Install in development mode
|
|
228
|
+
pip install -e .
|
|
229
|
+
|
|
230
|
+
# Run tests (244 tests, all passing!)
|
|
231
|
+
pytest tests/
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 📈 Project Status
|
|
235
|
+
|
|
236
|
+
- ✅ **244 Tests Passing** - Comprehensive test coverage
|
|
237
|
+
- ✅ **Production Ready** - Used in real ML workflows
|
|
238
|
+
- ✅ **Zero Warnings** - Clean, maintainable codebase
|
|
239
|
+
- ✅ **Sklearn Compatible** - Drop-in replacement for standard tools
|
|
240
|
+
- ✅ **Type Safe** - Full type hints and error handling
|
|
241
|
+
|
|
242
|
+
## 🎯 Perfect For
|
|
243
|
+
|
|
244
|
+
- **Data Scientists**: Spend less time on preprocessing, more on insights
|
|
245
|
+
- **ML Engineers**: Robust, tested pipelines for production
|
|
246
|
+
- **Analysts**: Intelligent automation for common data tasks
|
|
247
|
+
- **Anyone**: Who works with messy, real-world pandas DataFrames
|
|
248
|
+
|
|
249
|
+
## 📄 License
|
|
250
|
+
|
|
251
|
+
MIT License - Use it, love it, contribute to it!
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
*Ravenclaw: Because your data deserves intelligent preprocessing* 🧙♂️✨
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Ravenclaw - Data Wrangling and Feature Engineering Package.
|
|
2
|
+
|
|
3
|
+
A comprehensive toolkit for data wrangling, feature engineering, and data cleaning
|
|
4
|
+
with pandas DataFrames. Provides intelligent column detection, feature encoding,
|
|
5
|
+
data preprocessing utilities, and advanced DataFrame manipulation functions.
|
|
6
|
+
|
|
7
|
+
Submodules:
|
|
8
|
+
- preprocessing: Feature engineering and data preprocessing utilities
|
|
9
|
+
- clustering: K-means clustering with automatic k-selection
|
|
10
|
+
- wrangling: DataFrame manipulation and analysis utilities
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from ravenclaw.preprocessing import column_detection
|
|
14
|
+
from ravenclaw.clustering import kmeans
|
|
15
|
+
from ravenclaw.wrangling import join_wisely, standardize_columns
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|
|
19
|
+
__author__ = "Idin"
|
|
20
|
+
__email__ = "py@idin.net"
|
|
21
|
+
__description__ = "Data wrangling and feature engineering toolkit for pandas DataFrames"
|
|
22
|
+
|
|
23
|
+
__all__ = []
|