osmotic-data-filter 0.1.8__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,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: osmotic-data-filter
3
+ Version: 0.1.8
4
+ Summary: A physical chemistry-inspired, safe, and hybrid data bias handling framework
5
+ Author-email: Jana Elkasrawy <kasrawymahmoud@gmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: numpy
13
+ Requires-Dist: pandas
14
+ Requires-Dist: matplotlib
15
+ Requires-Dist: seaborn
16
+
17
+
18
+ readme_content = r"""# Osmotic Data Filter (ODF)
19
+
20
+
21
+ **Osmotic Data Filter (ODF)** is a scientifically grounded, non-destructive data preprocessing Python library designed to handle outliers and anomalies without row deletion or hard clamping. Engineered to meet strict scientific reproducibility standards (e.g., ISEF requirements), ODF compresses extreme statistical deviations toward safe critical boundaries while fully preserving dataset volume, structure, and integrity.
22
+
23
+ ---
24
+
25
+ ## 🌟 Key Features (v0.1.8)
26
+
27
+ * **Non-Destructive Outlier Adjustment (`action='adjust'`)**: Compresses outliers toward lower/upper limit boundaries without dropping rows or clamping values to artificial caps.
28
+ * **Smart Scaling Modes (`osmotic_scale`)**: Adaptive scaling designed for both independent and time-series/contextual datasets.
29
+ * **Flexible Post-Adjustment Rounding (`round_to_integer`)**: Optional rounding of adjusted values to nearest integers while strictly preserving `float` data types to prevent DataFrame schema conflicts.
30
+ * **Outlier Flagging (`action='flag'`)**: Adds explicit boolean indicator columns (`<column>_is_outlier`) without altering underlying numerical values.
31
+ * **Comprehensive Visual & Health Metrics**: Built-in functions (`check_skewness_health` and `plot_osmotic_dashboard`) for quantitative skewness reporting and automated dual-panel exploratory dashboards.
32
+
33
+ ---
34
+
35
+ ## 🧭 Choosing the Right `osmotic_scale` (Guide)
36
+
37
+ ODF provides three strict modes for the `osmotic_scale` parameter, tailored to different data types:
38
+
39
+ 1. **`'adj_dnmc'` (Local Dynamic - *Default*):**
40
+ * **When to use:** Ideal for time-series data, sequential measurements, or datasets with local clusters and high local variance.
41
+ * **Why:** It adapts to the local neighborhood of each outlier, preserving structural trends and contextual continuity without destroying local variations.
42
+
43
+ 2. **`'adj_auto'` (Global Automatic):**
44
+ * **When to use:** Best for cross-sectional data, independent statistical measurements, or uniform distributions.
45
+ * **Why:** It evaluates global distribution ratios and applies a consistent overall compression logic.
46
+
47
+ 3. **Fixed Numeric Scale (`float` between 0 and 1, e.g., `0.3`):**
48
+ * **When to use:** When you need strict, deterministic, or experimentally controlled compression ratios mandated by specific research protocols.
49
+
50
+ ---
51
+
52
+
53
+ ## 🚀 Quick Start Example
54
+
55
+ ```python
56
+ import numpy as np
57
+ import pandas as pd
58
+ import osmotic_data_filter as odf
59
+
60
+ # 1. Create sample dataset with extreme outliers
61
+ data = {
62
+ 'feature_col': [10, 12, 11, 14, 15, 12, 900, -400, 13]
63
+ }
64
+ df = pd.DataFrame(data)
65
+
66
+ # 2. Apply Osmotic Data Filtering with Local Dynamic Scale (Default) & Integer Rounding
67
+ df_processed = odf.Osmotic_Data_FLTR(
68
+ df,
69
+ columns=['feature_col'],
70
+ osmotic_scale='adj_dnmc',
71
+ action='adjust',
72
+ round_to_integer=True
73
+ )
74
+
75
+ print(df_processed)
76
+ print("Data Type:", df_processed['feature_col'].dtype)
77
+
78
+ # 3. Check Skewness & Statistical Health
79
+ health_report = odf.check_skewness_health(
80
+ df, df_processed, columns=['feature_col']
81
+ )
82
+ print(health_report)
83
+
84
+ # 4. Render Comparison Dashboard
85
+ fig = odf.plot_osmotic_dashboard(
86
+ df, df_processed, column='feature_col'
87
+ )
88
+ ```
89
+
90
+ ## 📖 API Reference
91
+
92
+ ### `Osmotic_Data_FLTR(df, columns, osmotic_scale='adj_dnmc', action='adjust', round_to_integer=False)`
93
+
94
+ Main execution function for dataset normalization and adjustment.
95
+
96
+ - **`df`**: Input pandas DataFrame containing numerical features (Required).
97
+ - **`columns`**: List of target column names to inspect and filter (Required).
98
+ - **`osmotic_scale`**: Mode of scaling. Accepts `'adj_auto'`, `'adj_dnmc'`, or float numeric (`0 < scale < 1`). Default is `'adj_dnmc'`.
99
+ - **`action`**: Operation mode. Use `'adjust'` for non-destructive compression or `'flag'` for tagging. Default is `'adjust'`.
100
+ - **`round_to_integer`**: Boolean. If `True`, rounds adjusted values to nearest integers while maintaining `float` dtype. Default is `False`.
101
+
102
+ ### `check_skewness_health(df_original, df_filtered, columns)`
103
+
104
+ Computes initial vs. adjusted skewness, IQR variance, and distribution delta metrics.
105
+
106
+ ### `plot_osmotic_dashboard(df_original, df_filtered, column)`
107
+
108
+ Generates a publication-ready Matplotlib/Seaborn visualization comparing Original vs. Osmotically Adjusted KDE distributions and Boxplots.
109
+
110
+ ## 🔬 Scientific Methodology & Reproducibility
111
+
112
+ 1. **Row Count Conservation**: Unlike standard truncation methods, ODF guarantees `N_initial == N_final`, preventing sample bias in downstream analytical or machine learning workflows.
113
+ 2. **Schema & Type Consistency**: Enforces continuous mathematical representation (`float64`) regardless of integer rounding flags, ensuring full compatibility with Scikit-Learn, PyTorch, and TensorFlow pipelines.
114
+ 3. **Bounded Adjustments**: Strict boundary validation enforces `0 < scale < 1`, preventing divergence or inverse scaling artifacts.
115
+
116
+ ---
117
+
118
+ ## 📜 License
119
+
120
+ Distributed under the **MIT License**. See `LICENSE` file for more information.
121
+ """
122
+
123
+ # Create README.md
124
+ with open("README.md", "w", encoding="utf-8") as file:
125
+ file.write(readme_content)
126
+
127
+ print("README.md created successfully!")
128
+
129
+ # Download the file
130
+ from google.colab import files
131
+ files.download("README.md")
@@ -0,0 +1,115 @@
1
+
2
+ readme_content = r"""# Osmotic Data Filter (ODF)
3
+
4
+
5
+ **Osmotic Data Filter (ODF)** is a scientifically grounded, non-destructive data preprocessing Python library designed to handle outliers and anomalies without row deletion or hard clamping. Engineered to meet strict scientific reproducibility standards (e.g., ISEF requirements), ODF compresses extreme statistical deviations toward safe critical boundaries while fully preserving dataset volume, structure, and integrity.
6
+
7
+ ---
8
+
9
+ ## 🌟 Key Features (v0.1.8)
10
+
11
+ * **Non-Destructive Outlier Adjustment (`action='adjust'`)**: Compresses outliers toward lower/upper limit boundaries without dropping rows or clamping values to artificial caps.
12
+ * **Smart Scaling Modes (`osmotic_scale`)**: Adaptive scaling designed for both independent and time-series/contextual datasets.
13
+ * **Flexible Post-Adjustment Rounding (`round_to_integer`)**: Optional rounding of adjusted values to nearest integers while strictly preserving `float` data types to prevent DataFrame schema conflicts.
14
+ * **Outlier Flagging (`action='flag'`)**: Adds explicit boolean indicator columns (`<column>_is_outlier`) without altering underlying numerical values.
15
+ * **Comprehensive Visual & Health Metrics**: Built-in functions (`check_skewness_health` and `plot_osmotic_dashboard`) for quantitative skewness reporting and automated dual-panel exploratory dashboards.
16
+
17
+ ---
18
+
19
+ ## 🧭 Choosing the Right `osmotic_scale` (Guide)
20
+
21
+ ODF provides three strict modes for the `osmotic_scale` parameter, tailored to different data types:
22
+
23
+ 1. **`'adj_dnmc'` (Local Dynamic - *Default*):**
24
+ * **When to use:** Ideal for time-series data, sequential measurements, or datasets with local clusters and high local variance.
25
+ * **Why:** It adapts to the local neighborhood of each outlier, preserving structural trends and contextual continuity without destroying local variations.
26
+
27
+ 2. **`'adj_auto'` (Global Automatic):**
28
+ * **When to use:** Best for cross-sectional data, independent statistical measurements, or uniform distributions.
29
+ * **Why:** It evaluates global distribution ratios and applies a consistent overall compression logic.
30
+
31
+ 3. **Fixed Numeric Scale (`float` between 0 and 1, e.g., `0.3`):**
32
+ * **When to use:** When you need strict, deterministic, or experimentally controlled compression ratios mandated by specific research protocols.
33
+
34
+ ---
35
+
36
+
37
+ ## 🚀 Quick Start Example
38
+
39
+ ```python
40
+ import numpy as np
41
+ import pandas as pd
42
+ import osmotic_data_filter as odf
43
+
44
+ # 1. Create sample dataset with extreme outliers
45
+ data = {
46
+ 'feature_col': [10, 12, 11, 14, 15, 12, 900, -400, 13]
47
+ }
48
+ df = pd.DataFrame(data)
49
+
50
+ # 2. Apply Osmotic Data Filtering with Local Dynamic Scale (Default) & Integer Rounding
51
+ df_processed = odf.Osmotic_Data_FLTR(
52
+ df,
53
+ columns=['feature_col'],
54
+ osmotic_scale='adj_dnmc',
55
+ action='adjust',
56
+ round_to_integer=True
57
+ )
58
+
59
+ print(df_processed)
60
+ print("Data Type:", df_processed['feature_col'].dtype)
61
+
62
+ # 3. Check Skewness & Statistical Health
63
+ health_report = odf.check_skewness_health(
64
+ df, df_processed, columns=['feature_col']
65
+ )
66
+ print(health_report)
67
+
68
+ # 4. Render Comparison Dashboard
69
+ fig = odf.plot_osmotic_dashboard(
70
+ df, df_processed, column='feature_col'
71
+ )
72
+ ```
73
+
74
+ ## 📖 API Reference
75
+
76
+ ### `Osmotic_Data_FLTR(df, columns, osmotic_scale='adj_dnmc', action='adjust', round_to_integer=False)`
77
+
78
+ Main execution function for dataset normalization and adjustment.
79
+
80
+ - **`df`**: Input pandas DataFrame containing numerical features (Required).
81
+ - **`columns`**: List of target column names to inspect and filter (Required).
82
+ - **`osmotic_scale`**: Mode of scaling. Accepts `'adj_auto'`, `'adj_dnmc'`, or float numeric (`0 < scale < 1`). Default is `'adj_dnmc'`.
83
+ - **`action`**: Operation mode. Use `'adjust'` for non-destructive compression or `'flag'` for tagging. Default is `'adjust'`.
84
+ - **`round_to_integer`**: Boolean. If `True`, rounds adjusted values to nearest integers while maintaining `float` dtype. Default is `False`.
85
+
86
+ ### `check_skewness_health(df_original, df_filtered, columns)`
87
+
88
+ Computes initial vs. adjusted skewness, IQR variance, and distribution delta metrics.
89
+
90
+ ### `plot_osmotic_dashboard(df_original, df_filtered, column)`
91
+
92
+ Generates a publication-ready Matplotlib/Seaborn visualization comparing Original vs. Osmotically Adjusted KDE distributions and Boxplots.
93
+
94
+ ## 🔬 Scientific Methodology & Reproducibility
95
+
96
+ 1. **Row Count Conservation**: Unlike standard truncation methods, ODF guarantees `N_initial == N_final`, preventing sample bias in downstream analytical or machine learning workflows.
97
+ 2. **Schema & Type Consistency**: Enforces continuous mathematical representation (`float64`) regardless of integer rounding flags, ensuring full compatibility with Scikit-Learn, PyTorch, and TensorFlow pipelines.
98
+ 3. **Bounded Adjustments**: Strict boundary validation enforces `0 < scale < 1`, preventing divergence or inverse scaling artifacts.
99
+
100
+ ---
101
+
102
+ ## 📜 License
103
+
104
+ Distributed under the **MIT License**. See `LICENSE` file for more information.
105
+ """
106
+
107
+ # Create README.md
108
+ with open("README.md", "w", encoding="utf-8") as file:
109
+ file.write(readme_content)
110
+
111
+ print("README.md created successfully!")
112
+
113
+ # Download the file
114
+ from google.colab import files
115
+ files.download("README.md")
@@ -0,0 +1,228 @@
1
+ """
2
+ Osmotic Data Filter (ODF) - v0.1.8 Bulletproof Edition
3
+ ======================================================
4
+ A physical-chemistry inspired, safe, and hybrid outlier handling framework for Python.
5
+ Combines Interquartile Range (IQR) for critical boundaries with osmotic distance
6
+ adjustments for smooth value scaling without involuntary data deletion.
7
+
8
+ Author: Jana Elkasrawy
9
+ License: MIT
10
+ """
11
+
12
+ import numpy as np
13
+ import pandas as pd
14
+ import matplotlib.pyplot as plt
15
+ import seaborn as sns
16
+
17
+ def detect_outliers_preview(df, columns=None, iqr_factor=1.5):
18
+ if df is None or df.empty:
19
+ return pd.DataFrame()
20
+ if columns is None:
21
+ columns = [col for col in df.columns if pd.api.types.is_numeric_dtype(df[col])]
22
+ report = []
23
+ for col in columns:
24
+ if col not in df.columns or not pd.api.types.is_numeric_dtype(df[col]):
25
+ continue
26
+ series = df[col].replace([np.inf, -np.inf], np.nan).dropna()
27
+ if series.empty:
28
+ continue
29
+ q1 = series.quantile(0.25)
30
+ q3 = series.quantile(0.75)
31
+ iqr = q3 - q1
32
+ if pd.isna(iqr) or iqr <= 0:
33
+ iqr = 0.0
34
+ lower_limit = q1 - (iqr_factor * iqr)
35
+ upper_limit = q3 + (iqr_factor * iqr)
36
+ outliers_count = ((series < lower_limit) | (series > upper_limit)).sum()
37
+ total_len = len(series)
38
+ outlier_pct = (outliers_count / total_len * 100) if total_len > 0 else 0.0
39
+ report.append({
40
+ 'Column': col,
41
+ 'Q1': round(float(q1), 4),
42
+ 'Median': round(float(series.median()), 4),
43
+ 'Q3': round(float(q3), 4),
44
+ 'IQR': round(float(iqr), 4),
45
+ 'Lower_Critical_Limit': round(float(lower_limit), 4),
46
+ 'Upper_Critical_Limit': round(float(upper_limit), 4),
47
+ 'Outliers_Count': int(outliers_count),
48
+ 'Outlier_Percentage (%)': round(float(outlier_pct), 2)
49
+ })
50
+ return pd.DataFrame(report)
51
+
52
+ def partition_uncertain_cases(df, target_column, iqr_factor=1.5):
53
+ if df is None or df.empty or target_column not in df.columns:
54
+ return pd.DataFrame(), pd.DataFrame()
55
+ series = df[target_column].replace([np.inf, -np.inf], np.nan).dropna()
56
+ if series.empty:
57
+ return df.copy(), pd.DataFrame()
58
+ q1 = series.quantile(0.25)
59
+ q3 = series.quantile(0.75)
60
+ iqr = q3 - q1
61
+ if pd.isna(iqr) or iqr <= 0:
62
+ iqr = 0.0
63
+ lower_limit = q1 - (iqr_factor * iqr)
64
+ upper_limit = q3 + (iqr_factor * iqr)
65
+ temp_series = df[target_column].replace([np.inf, -np.inf], np.nan)
66
+ outlier_mask = (temp_series < lower_limit) | (temp_series > upper_limit)
67
+ outlier_mask = outlier_mask.fillna(False)
68
+ return df[~outlier_mask].copy(), df[outlier_mask].copy()
69
+
70
+ def Osmotic_Data_FLTR(df, columns=None, iqr_factor=1.5, osmotic_scale=0.5, action='adjust', round_to_integer=False):
71
+ if df is None:
72
+ return pd.DataFrame()
73
+ if action not in ['adjust', 'flag']:
74
+ raise ValueError("Action must be either 'adjust' or 'flag'")
75
+
76
+ if osmotic_scale not in ['adj_auto', 'adj_dnmc']:
77
+ if not isinstance(osmotic_scale, (int, float)) or pd.isna(osmotic_scale):
78
+ raise ValueError("Osmotic scale must be 'adj_auto', 'adj_dnmc', or a numeric value.")
79
+ if not (0.0 < osmotic_scale < 1.0):
80
+ raise ValueError("Numeric osmotic scale must be strictly greater than 0 and less than 1.")
81
+
82
+ df_result = df.copy()
83
+ if columns is None:
84
+ columns = [col for col in df_result.columns if pd.api.types.is_numeric_dtype(df_result[col])]
85
+
86
+ for col in columns:
87
+ if col not in df_result.columns or not pd.api.types.is_numeric_dtype(df_result[col]):
88
+ continue
89
+ series = df_result[col].replace([np.inf, -np.inf], np.nan)
90
+ series_clean = series.dropna()
91
+ if series_clean.empty:
92
+ continue
93
+
94
+ q1 = series_clean.quantile(0.25)
95
+ q3 = series_clean.quantile(0.75)
96
+ iqr = q3 - q1
97
+ if pd.isna(iqr) or iqr <= 0:
98
+ iqr = 0.0
99
+ lower_limit = q1 - (iqr_factor * iqr)
100
+ upper_limit = q3 + (iqr_factor * iqr)
101
+
102
+ if action == 'flag':
103
+ flag_col = f'{col}_is_outlier'
104
+ if flag_col in df_result.columns:
105
+ raise ValueError(f"Column collision: '{flag_col}' already exists!")
106
+ is_out = (series < lower_limit) | (series > upper_limit)
107
+ df_result[flag_col] = is_out.fillna(False)
108
+
109
+ elif action == 'adjust':
110
+ upper_mask = (series > upper_limit) & (~series.isna())
111
+ lower_mask = (series < lower_limit) & (~series.isna())
112
+ outlier_mask = upper_mask | lower_mask
113
+ natural_mask = (~outlier_mask) & (~series.isna())
114
+
115
+ if outlier_mask.any() or df_result[col].dtype != float:
116
+ df_result[col] = df_result[col].astype(float)
117
+ series = df_result[col]
118
+
119
+ natural_values = series[natural_mask].to_numpy()
120
+
121
+ if osmotic_scale == 'adj_auto':
122
+ if len(natural_values) >= 2:
123
+ n1 = natural_values[:-1]
124
+ n2 = natural_values[1:]
125
+ denoms = n1 + n2
126
+ ratios = np.where(denoms == 0, 0.5, np.abs((n1 - n2) / denoms))
127
+ global_ratio = np.mean(ratios)
128
+ else:
129
+ global_ratio = 0.5
130
+
131
+ if upper_mask.any():
132
+ excess = series[upper_mask] - upper_limit
133
+ df_result.loc[upper_mask, col] = np.abs(global_ratio * excess) + upper_limit
134
+ if lower_mask.any():
135
+ deficit = lower_limit - series[lower_mask]
136
+ df_result.loc[lower_mask, col] = lower_limit - np.abs(global_ratio * deficit)
137
+
138
+ elif osmotic_scale == 'adj_dnmc':
139
+ if len(natural_values) < 2:
140
+ active_ratio = 0.5
141
+ if upper_mask.any():
142
+ excess = series[upper_mask] - upper_limit
143
+ df_result.loc[upper_mask, col] = np.abs(active_ratio * excess) + upper_limit
144
+ if lower_mask.any():
145
+ deficit = lower_limit - series[lower_mask]
146
+ df_result.loc[lower_mask, col] = lower_limit - np.abs(active_ratio * deficit)
147
+ else:
148
+ natural_indices = np.where(natural_mask.to_numpy())[0]
149
+ full_series = series.to_numpy()
150
+
151
+ for idx in np.where(outlier_mask.to_numpy())[0]:
152
+ val = full_series[idx]
153
+ prev_nat_indices = natural_indices[natural_indices < idx]
154
+ if len(prev_nat_indices) >= 2:
155
+ n1 = full_series[prev_nat_indices[-1]]
156
+ n2 = full_series[prev_nat_indices[-2]]
157
+ denom = n1 + n2
158
+ local_ratio = np.abs((n1 - n2) / denom) if denom != 0 else 0.5
159
+ else:
160
+ local_ratio = 0.5
161
+
162
+ if val > upper_limit:
163
+ excess = val - upper_limit
164
+ df_result.iloc[idx, df_result.columns.get_loc(col)] = np.abs(local_ratio * excess) + upper_limit
165
+ elif val < lower_limit:
166
+ deficit = lower_limit - val
167
+ df_result.iloc[idx, df_result.columns.get_loc(col)] = lower_limit - np.abs(local_ratio * deficit)
168
+
169
+ else:
170
+ scale_val = float(osmotic_scale)
171
+ if upper_mask.any():
172
+ excess = series[upper_mask] - upper_limit
173
+ df_result.loc[upper_mask, col] = upper_limit + (excess * scale_val)
174
+ if lower_mask.any():
175
+ deficit = lower_limit - series[lower_mask]
176
+ df_result.loc[lower_mask, col] = lower_limit - (deficit * scale_val)
177
+
178
+ df_result[col] = df_result[col].astype(float)
179
+ if round_to_integer:
180
+ df_result[col] = np.round(df_result[col]).astype(float)
181
+
182
+ return df_result
183
+
184
+ def check_skewness_health(df_before, df_after, columns=None):
185
+ if df_before is None or df_after is None:
186
+ return pd.DataFrame()
187
+ if columns is None:
188
+ columns = [col for col in df_before.columns if pd.api.types.is_numeric_dtype(df_before[col])]
189
+ health_report = []
190
+ for col in columns:
191
+ if col in df_before.columns and col in df_after.columns:
192
+ if not pd.api.types.is_numeric_dtype(df_before[col]) or not pd.api.types.is_numeric_dtype(df_after[col]):
193
+ continue
194
+ s_before = df_before[col].replace([np.inf, -np.inf], np.nan).dropna()
195
+ s_after = df_after[col].replace([np.inf, -np.inf], np.nan).dropna()
196
+ if s_before.empty or s_after.empty:
197
+ continue
198
+ health_report.append({
199
+ 'Column': col,
200
+ 'Skew_Before': round(float(s_before.skew()), 4) if len(s_before) > 2 else np.nan,
201
+ 'Skew_After': round(float(s_after.skew()), 4) if len(s_after) > 2 else np.nan,
202
+ 'Kurtosis_Before': round(float(s_before.kurtosis()), 4) if len(s_before) > 3 else np.nan,
203
+ 'Kurtosis_After': round(float(s_after.kurtosis()), 4) if len(s_after) > 3 else np.nan,
204
+ 'Mean_Shift': round(float(s_after.mean() - s_before.mean()), 4),
205
+ 'Std_Change': round(float(s_after.std() - s_before.std()), 4)
206
+ })
207
+ return pd.DataFrame(health_report)
208
+
209
+ def plot_osmotic_dashboard(df_before, df_after, column):
210
+ fig, axes = plt.subplots(2, 2, figsize=(14, 8))
211
+ fig.suptitle(f"Osmotic Data Filter Analysis: '{column}'", fontsize=14, fontweight='bold')
212
+ axes[0, 0].set_title("Original Distribution (With Outliers)")
213
+ axes[1, 0].set_title("Original Boxplot")
214
+ axes[0, 1].set_title("Osmotic Adjusted Distribution")
215
+ axes[1, 1].set_title("Osmotic Adjusted Boxplot")
216
+
217
+ if df_before is not None and df_after is not None and column in df_before.columns and column in df_after.columns:
218
+ s_before = df_before[column].replace([np.inf, -np.inf], np.nan).dropna()
219
+ s_after = df_after[column].replace([np.inf, -np.inf], np.nan).dropna()
220
+ if not s_before.empty:
221
+ sns.histplot(s_before, kde=True, ax=axes[0, 0], color='crimson')
222
+ sns.boxplot(x=s_before, ax=axes[1, 0], color='crimson')
223
+ if not s_after.empty:
224
+ sns.histplot(s_after, kde=True, ax=axes[0, 1], color='teal')
225
+ sns.boxplot(x=s_after, ax=axes[1, 1], color='teal')
226
+
227
+ plt.tight_layout()
228
+ return fig
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: osmotic-data-filter
3
+ Version: 0.1.8
4
+ Summary: A physical chemistry-inspired, safe, and hybrid data bias handling framework
5
+ Author-email: Jana Elkasrawy <kasrawymahmoud@gmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: numpy
13
+ Requires-Dist: pandas
14
+ Requires-Dist: matplotlib
15
+ Requires-Dist: seaborn
16
+
17
+
18
+ readme_content = r"""# Osmotic Data Filter (ODF)
19
+
20
+
21
+ **Osmotic Data Filter (ODF)** is a scientifically grounded, non-destructive data preprocessing Python library designed to handle outliers and anomalies without row deletion or hard clamping. Engineered to meet strict scientific reproducibility standards (e.g., ISEF requirements), ODF compresses extreme statistical deviations toward safe critical boundaries while fully preserving dataset volume, structure, and integrity.
22
+
23
+ ---
24
+
25
+ ## 🌟 Key Features (v0.1.8)
26
+
27
+ * **Non-Destructive Outlier Adjustment (`action='adjust'`)**: Compresses outliers toward lower/upper limit boundaries without dropping rows or clamping values to artificial caps.
28
+ * **Smart Scaling Modes (`osmotic_scale`)**: Adaptive scaling designed for both independent and time-series/contextual datasets.
29
+ * **Flexible Post-Adjustment Rounding (`round_to_integer`)**: Optional rounding of adjusted values to nearest integers while strictly preserving `float` data types to prevent DataFrame schema conflicts.
30
+ * **Outlier Flagging (`action='flag'`)**: Adds explicit boolean indicator columns (`<column>_is_outlier`) without altering underlying numerical values.
31
+ * **Comprehensive Visual & Health Metrics**: Built-in functions (`check_skewness_health` and `plot_osmotic_dashboard`) for quantitative skewness reporting and automated dual-panel exploratory dashboards.
32
+
33
+ ---
34
+
35
+ ## 🧭 Choosing the Right `osmotic_scale` (Guide)
36
+
37
+ ODF provides three strict modes for the `osmotic_scale` parameter, tailored to different data types:
38
+
39
+ 1. **`'adj_dnmc'` (Local Dynamic - *Default*):**
40
+ * **When to use:** Ideal for time-series data, sequential measurements, or datasets with local clusters and high local variance.
41
+ * **Why:** It adapts to the local neighborhood of each outlier, preserving structural trends and contextual continuity without destroying local variations.
42
+
43
+ 2. **`'adj_auto'` (Global Automatic):**
44
+ * **When to use:** Best for cross-sectional data, independent statistical measurements, or uniform distributions.
45
+ * **Why:** It evaluates global distribution ratios and applies a consistent overall compression logic.
46
+
47
+ 3. **Fixed Numeric Scale (`float` between 0 and 1, e.g., `0.3`):**
48
+ * **When to use:** When you need strict, deterministic, or experimentally controlled compression ratios mandated by specific research protocols.
49
+
50
+ ---
51
+
52
+
53
+ ## 🚀 Quick Start Example
54
+
55
+ ```python
56
+ import numpy as np
57
+ import pandas as pd
58
+ import osmotic_data_filter as odf
59
+
60
+ # 1. Create sample dataset with extreme outliers
61
+ data = {
62
+ 'feature_col': [10, 12, 11, 14, 15, 12, 900, -400, 13]
63
+ }
64
+ df = pd.DataFrame(data)
65
+
66
+ # 2. Apply Osmotic Data Filtering with Local Dynamic Scale (Default) & Integer Rounding
67
+ df_processed = odf.Osmotic_Data_FLTR(
68
+ df,
69
+ columns=['feature_col'],
70
+ osmotic_scale='adj_dnmc',
71
+ action='adjust',
72
+ round_to_integer=True
73
+ )
74
+
75
+ print(df_processed)
76
+ print("Data Type:", df_processed['feature_col'].dtype)
77
+
78
+ # 3. Check Skewness & Statistical Health
79
+ health_report = odf.check_skewness_health(
80
+ df, df_processed, columns=['feature_col']
81
+ )
82
+ print(health_report)
83
+
84
+ # 4. Render Comparison Dashboard
85
+ fig = odf.plot_osmotic_dashboard(
86
+ df, df_processed, column='feature_col'
87
+ )
88
+ ```
89
+
90
+ ## 📖 API Reference
91
+
92
+ ### `Osmotic_Data_FLTR(df, columns, osmotic_scale='adj_dnmc', action='adjust', round_to_integer=False)`
93
+
94
+ Main execution function for dataset normalization and adjustment.
95
+
96
+ - **`df`**: Input pandas DataFrame containing numerical features (Required).
97
+ - **`columns`**: List of target column names to inspect and filter (Required).
98
+ - **`osmotic_scale`**: Mode of scaling. Accepts `'adj_auto'`, `'adj_dnmc'`, or float numeric (`0 < scale < 1`). Default is `'adj_dnmc'`.
99
+ - **`action`**: Operation mode. Use `'adjust'` for non-destructive compression or `'flag'` for tagging. Default is `'adjust'`.
100
+ - **`round_to_integer`**: Boolean. If `True`, rounds adjusted values to nearest integers while maintaining `float` dtype. Default is `False`.
101
+
102
+ ### `check_skewness_health(df_original, df_filtered, columns)`
103
+
104
+ Computes initial vs. adjusted skewness, IQR variance, and distribution delta metrics.
105
+
106
+ ### `plot_osmotic_dashboard(df_original, df_filtered, column)`
107
+
108
+ Generates a publication-ready Matplotlib/Seaborn visualization comparing Original vs. Osmotically Adjusted KDE distributions and Boxplots.
109
+
110
+ ## 🔬 Scientific Methodology & Reproducibility
111
+
112
+ 1. **Row Count Conservation**: Unlike standard truncation methods, ODF guarantees `N_initial == N_final`, preventing sample bias in downstream analytical or machine learning workflows.
113
+ 2. **Schema & Type Consistency**: Enforces continuous mathematical representation (`float64`) regardless of integer rounding flags, ensuring full compatibility with Scikit-Learn, PyTorch, and TensorFlow pipelines.
114
+ 3. **Bounded Adjustments**: Strict boundary validation enforces `0 < scale < 1`, preventing divergence or inverse scaling artifacts.
115
+
116
+ ---
117
+
118
+ ## 📜 License
119
+
120
+ Distributed under the **MIT License**. See `LICENSE` file for more information.
121
+ """
122
+
123
+ # Create README.md
124
+ with open("README.md", "w", encoding="utf-8") as file:
125
+ file.write(readme_content)
126
+
127
+ print("README.md created successfully!")
128
+
129
+ # Download the file
130
+ from google.colab import files
131
+ files.download("README.md")
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ osmotic_data_filter/__init__.py
4
+ osmotic_data_filter.egg-info/PKG-INFO
5
+ osmotic_data_filter.egg-info/SOURCES.txt
6
+ osmotic_data_filter.egg-info/dependency_links.txt
7
+ osmotic_data_filter.egg-info/requires.txt
8
+ osmotic_data_filter.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ numpy
2
+ pandas
3
+ matplotlib
4
+ seaborn
@@ -0,0 +1,3 @@
1
+ dist
2
+ osmotic_data_filter
3
+ sample_data
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "osmotic-data-filter"
7
+ version = "0.1.8"
8
+ authors = [
9
+ { name="Jana Elkasrawy", email="kasrawymahmoud@gmail.com" },
10
+ ]
11
+ description = "A physical chemistry-inspired, safe, and hybrid data bias handling framework"
12
+ readme = "README.md"
13
+ license = "MIT"
14
+ requires-python = ">=3.7"
15
+ dependencies = [
16
+ "numpy",
17
+ "pandas",
18
+ "matplotlib",
19
+ "seaborn"
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Programming Language :: Python :: 3",
24
+ "Operating System :: OS Independent",
25
+ ]
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+