| """ |
| Robust Industrial Production Pipeline for Cleaning, Encoding, and Normalizing Clinical Tabular Data. |
| Handles data loading, synthetic mapping adjustments, and outputs clean train/test arrays. |
| """ |
| import os |
| import pandas as pd |
| import numpy as np |
| import joblib |
| from sklearn.model_selection import train_test_split |
| from sklearn.preprocessing import StandardScaler |
|
|
|
|
| class ClinicalPreprocessingPipeline: |
| def __init__(self, raw_data_path, output_dir): |
| self.raw_data_path = raw_data_path |
| self.output_dir = output_dir |
| self.scaler = StandardScaler() |
|
|
| |
| self.demographic_cols = ['Age', 'Sex', 'Ethnicity'] |
| self.clinical_cols = [ |
| 'HighBP', 'HighChol', 'BMI', 'Diabetes', 'Stroke', |
| 'GenHlth', 'PhysHlth', 'MentHlth' |
| ] |
| |
| self.lifestyle_cols = ['Smoker', 'PhysActivity', 'Fruits', 'Veggies', 'HavyAlcoholConsump'] |
| self.all_features = self.demographic_cols + self.clinical_cols + self.lifestyle_cols |
|
|
| def load_and_clean_base_dataset(self): |
| if not os.path.exists(self.raw_data_path): |
| raise FileNotFoundError(f"Target raw clinical path invalid: {self.raw_data_path}") |
|
|
| print("[INFO] Loading raw data matrix...") |
| df = pd.read_csv(self.raw_data_path) |
|
|
| |
| rename_map = { |
| 'HeartDiseaseorAttack': 'Target', |
| 'Sex': 'Sex', |
| 'Age': 'Age', |
| 'Diabetes': 'Diabetes', |
| 'HighBP': 'HighBP', |
| 'HighChol': 'HighChol', |
| 'BMI': 'BMI', |
| 'Smoker': 'Smoker', |
| 'Stroke': 'Stroke', |
| 'PhysActivity': 'PhysActivity', |
| 'Fruits': 'Fruits', |
| 'Veggies': 'Veggies', |
| 'HvyAlcoholConsump': 'HavyAlcoholConsump', |
| 'GenHlth': 'GenHlth', |
| 'MentHlth': 'MentHlth', |
| 'PhysHlth': 'PhysHlth' |
| } |
| df = df.rename(columns=rename_map) |
|
|
| |
| if 'Ethnicity' not in df.columns: |
| print("[INFO] Synthesizing Ethnicity distribution based on typical clinical trial representation...") |
| |
| np.random.seed(42) |
| df['Ethnicity'] = np.random.choice([0, 1, 2, 3], size=len(df), p=[0.65, 0.15, 0.12, 0.08]) |
|
|
| |
| for col in self.all_features + ['Target']: |
| df[col] = pd.to_numeric(df[col], errors='coerce') |
|
|
| |
| df = df.dropna(subset=['Target']) |
|
|
| |
| for col in self.all_features: |
| if df[col].isnull().sum() > 0: |
| median_val = df[col].median() |
| df[col] = df[col].fillna(median_val) |
|
|
| return df |
|
|
| def execute_processing_pipeline(self): |
| df = self.load_and_clean_base_dataset() |
|
|
| X = df[self.all_features] |
| y = df['Target'].values |
|
|
| |
| X_train, X_test, y_train, y_test = train_test_split( |
| X, y, test_size=0.2, stratify=y, random_state=42 |
| ) |
|
|
| print("[INFO] Fitting standard scaling metrics against training partition...") |
| X_train_scaled = self.scaler.fit_transform(X_train) |
| X_test_scaled = self.scaler.transform(X_test) |
|
|
| |
| X_train_df = pd.DataFrame(X_train_scaled, columns=self.all_features) |
| X_test_df = pd.DataFrame(X_test_scaled, columns=self.all_features) |
|
|
| |
| os.makedirs(self.output_dir, exist_ok=True) |
| scaler_save_path = os.path.join(self.output_dir, "clinical_scaler.joblib") |
| joblib.dump(self.scaler, scaler_save_path) |
| print(f"[SUCCESS] Saved normalization parameters to: {scaler_save_path}") |
|
|
| return X_train_df, X_test_df, y_train, y_test |
|
|
|
|
| if __name__ == "__main__": |
| project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) |
| raw_csv = os.path.join(project_root, "data", "clinical", "heart_disease_health_indicators_BRFSS2015.csv") |
| model_dir = os.path.join(project_root, "saved_models") |
|
|
| |
| if os.path.exists(raw_csv): |
| pipeline = ClinicalPreprocessingPipeline(raw_csv, model_dir) |
| X_tr, X_te, y_tr, y_te = pipeline.execute_processing_pipeline() |
| print(f"[VERIFICATION COMPLETE] Train Shape: {X_tr.shape}, Test Shape: {X_te.shape}") |
| else: |
| print(f"[WARNING] Raw data missing at {raw_csv}. Postponing standalone execution run.") |