From 10edb65a065b59b5d9f9e5423d2f53479b4f41bf Mon Sep 17 00:00:00 2001 From: BMK Date: Mon, 16 Mar 2026 02:19:24 -0400 Subject: [PATCH 1/6] feat: add Zarr file format support with zarrReader --- aidrin/file_handling/file_parser.py | 5 +- aidrin/file_handling/readers/zarr_reader.py | 133 ++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 aidrin/file_handling/readers/zarr_reader.py diff --git a/aidrin/file_handling/file_parser.py b/aidrin/file_handling/file_parser.py index 88009ddf..0a0b8468 100644 --- a/aidrin/file_handling/file_parser.py +++ b/aidrin/file_handling/file_parser.py @@ -6,7 +6,7 @@ from aidrin.file_handling.readers.hdf5_reader import hdf5Reader from aidrin.file_handling.readers.json_reader import jsonReader from aidrin.file_handling.readers.npz_reader import npzReader - +from aidrin.file_handling.readers.zarr_reader import zarrReader # Notes: # To add support for new file types: # - Add a new subclass of BaseFileReader with a .read() method @@ -21,7 +21,9 @@ ".xls, .xlsb, .xlsx, .xlsm": excelReader, ".json": jsonReader, ".h5": hdf5Reader, + ".zarr": zarrReader, # Add additional file types here + } # Supported file types. Read on front end to create select features. @@ -31,6 +33,7 @@ (".json", "JSON"), (".npz", "NumPy"), (".h5", "HDF5"), + (".zarr", "Zarr"), # Add additional file types here using the format: # (file_type,file_type_name) ] diff --git a/aidrin/file_handling/readers/zarr_reader.py b/aidrin/file_handling/readers/zarr_reader.py new file mode 100644 index 00000000..fd41447d --- /dev/null +++ b/aidrin/file_handling/readers/zarr_reader.py @@ -0,0 +1,133 @@ +import zarr +import numpy as np +import pandas as pd +from aidrin.file_handling.readers.base_reader import BaseFileReader + + +class zarrReader(BaseFileReader): + # zarr is similar to hdf5 - both store data in groups and arrays + # i learned this when i was reading about scientific data formats + # zarr is newer and works better for cloud storage compared to hdf5 + + def read(self): + try: + rows = [] + + # open the zarr store - can be a folder or zip file + store = zarr.open(self.file_path, mode='r') + + def recurse(group, prefix=''): + # going through all arrays inside zarr store + # zarr stores data in groups like folders in a computer + for key in group.keys(): + full_key = f"{prefix}/{key}".strip('/') + item = group[key] + + if isinstance(item, zarr.Array): + try: + # convert zarr array to numpy then to dataframe + data = item[:] + + # handle fill values similar to hdf5 reader + if hasattr(data, 'dtype') and data.dtype.kind in ('f', 'i', 'u'): + fill_val = item.fill_value + if fill_val is not None and fill_val != 0: + data = data.astype(np.float64) + data[data == fill_val] = np.nan + + # flatten to 2d if needed + if data.ndim > 2: + data = data.reshape(-1, data.shape[-1]) + elif data.ndim == 1: + data = data.reshape(-1, 1) + + df = pd.DataFrame(data) + df.columns = [f"{full_key}_{c}" + for c in df.columns] + + for _, row in df.iterrows(): + rows.append(row.to_dict()) + + except Exception as e: + self.logger.warning( + f"could not read array {full_key}: {e}" + ) + + elif isinstance(item, zarr.Group): + # if its a group go deeper into it + recurse(item, full_key) + + recurse(store) + + if not rows: + self.logger.warning("no data found in zarr file") + return None + + df = pd.DataFrame(rows) + self.logger.info(f"zarr file read successfully, shape: {df.shape}") + return df + + except Exception as e: + self.logger.error(f"error reading zarr file: {e}") + return None + + def parse(self): + # returns all group names inside the zarr store + # similar to parse() in hdf5_reader + try: + store = zarr.open(self.file_path, mode='r') + group_names = [] + + def collect_groups(group, prefix=''): + for key in group.keys(): + full_key = f"{prefix}/{key}".strip('/') + item = group[key] + if isinstance(item, zarr.Group): + group_names.append(full_key) + collect_groups(item, full_key) + + collect_groups(store) + self.logger.info(f"zarr groups found: {group_names}") + return group_names + + except Exception as e: + self.logger.error(f"error parsing zarr file: {e}") + return None + + def filter(self, kept_keys): + # filters zarr store to keep only selected groups + # useful when zarr file has many groups and user wants specific ones + try: + if isinstance(kept_keys, str): + kept_keys = [k.strip() for k in kept_keys.split(',')] + + kept_keys = set(kept_keys) + src = zarr.open(self.file_path, mode='r') + + # create a new in memory store with only kept groups + filtered = zarr.group() + + def copy_filtered(src_group, tgt_group, prefix=''): + for key in src_group.keys(): + full_key = f"{prefix}/{key}".strip('/') + item = src_group[key] + + if isinstance(item, zarr.Array): + if prefix in kept_keys or full_key in kept_keys: + tgt_group.create_dataset( + key, data=item[:], + chunks=item.chunks + ) + elif isinstance(item, zarr.Group): + if full_key in kept_keys: + sub = tgt_group.require_group(key) + copy_filtered(item, sub, full_key) + else: + copy_filtered(item, tgt_group, full_key) + + copy_filtered(src, filtered) + return filtered + + except Exception as e: + self.logger.error(f"error filtering zarr file: {e}") + return None \ No newline at end of file From 46626e1aa1ec8616edfa8b47f574bdf25428b6f1 Mon Sep 17 00:00:00 2001 From: BMK Date: Tue, 17 Mar 2026 17:50:45 -0400 Subject: [PATCH 2/6] fix: replace deprecated inplace=True in privacy_measure.py for pandas 3.x compatibility --- aidrin/structured_data_metrics/privacy_measure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aidrin/structured_data_metrics/privacy_measure.py b/aidrin/structured_data_metrics/privacy_measure.py index 4ac5b826..d7e1158b 100644 --- a/aidrin/structured_data_metrics/privacy_measure.py +++ b/aidrin/structured_data_metrics/privacy_measure.py @@ -453,7 +453,7 @@ def compute_k_anonymity(quasi_identifiers: List[str], file_info): if qi not in data.columns: raise ValueError(f"Quasi-identifier '{qi}' not found in the dataset.") - data.replace("?", pd.NA, inplace=True) + data = data.replace("?", pd.NA) clean_data = data.dropna(subset=quasi_identifiers) if clean_data.empty: raise ValueError( From a6594bb00a7cd4b7b3c8c18914c261c93261f685 Mon Sep 17 00:00:00 2001 From: BMK Date: Wed, 18 Mar 2026 15:00:36 -0400 Subject: [PATCH 3/6] fix: replace debug print() statements with logger.debug() in metric modules --- .../class_imbalance.py | 4 +- .../conditional_demo_disp.py | 6 +- .../correlation_score.py | 4 +- .../feature_relevance.py | 112 +++++++++--------- .../privacy_measure.py | 4 +- 5 files changed, 69 insertions(+), 61 deletions(-) diff --git a/aidrin/structured_data_metrics/class_imbalance.py b/aidrin/structured_data_metrics/class_imbalance.py index 216fa6f5..8d593938 100644 --- a/aidrin/structured_data_metrics/class_imbalance.py +++ b/aidrin/structured_data_metrics/class_imbalance.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import base64 import io import warnings @@ -266,7 +268,7 @@ def class_distribution_plot(df, column): raise ValueError(f"Column '{column}' has too many classes ({len(unique_classes)}). Visualization works best with fewer than 50 classes.") # Debug: Print some info about the data - print(f"Class distribution plot - Column: {column}, Unique values: {len(class_counts)}, Total: {class_counts.sum()}") + logger.debug(f"Class distribution plot - Column: {column}, Unique values: {len(class_counts)}, Total: {class_counts.sum()}") # Convert labels to strings and handle truncation safely class_labels_modified = [] diff --git a/aidrin/structured_data_metrics/conditional_demo_disp.py b/aidrin/structured_data_metrics/conditional_demo_disp.py index 1b86be2c..f710d985 100644 --- a/aidrin/structured_data_metrics/conditional_demo_disp.py +++ b/aidrin/structured_data_metrics/conditional_demo_disp.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import pandas as pd from celery import Task, shared_task from celery.exceptions import SoftTimeLimitExceeded @@ -34,12 +36,12 @@ def conditional_demographic_disparity(self: Task, target, sensitive, accepted_va # Create a DataFrame from the input lists df = pd.DataFrame({"target": target, "sensitive": sensitive}) - print(df) + logger.debug(df) # Convert target to binary (1 for accepted, 0 for rejected) df["target_binary"] = df["target"].apply( lambda x: 1 if x == accepted_value else 0 ) - print(df["target_binary"]) + logger.debug(df["target_binary"]) # Calculate counts for each group and target combination group_counts = ( df.groupby(["sensitive", "target_binary"]).size().unstack(fill_value=0) diff --git a/aidrin/structured_data_metrics/correlation_score.py b/aidrin/structured_data_metrics/correlation_score.py index 7e0afca6..14088990 100644 --- a/aidrin/structured_data_metrics/correlation_score.py +++ b/aidrin/structured_data_metrics/correlation_score.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import base64 from io import BytesIO from typing import List @@ -36,7 +38,7 @@ def calc_correlations(self: Task, columns: List[str], file_info): categorical_correlation = associations( df[categorical_columns], nom_nom_assoc=NOMINAL_NOMINAL_ASSOC, plot=False ) - print(categorical_correlation["corr"]) + logger.debug(categorical_correlation["corr"]) # Create a subplot with 1 row and 1 column _, axes = plt.subplots(1, 1, figsize=(8, 8)) diff --git a/aidrin/structured_data_metrics/feature_relevance.py b/aidrin/structured_data_metrics/feature_relevance.py index e708caa2..cb75a330 100644 --- a/aidrin/structured_data_metrics/feature_relevance.py +++ b/aidrin/structured_data_metrics/feature_relevance.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import base64 import io @@ -151,22 +153,22 @@ @shared_task(bind=True, ignore_result=False) def data_cleaning(self: Task, cat_cols, num_cols, target_col, file_info): try: - print(f"Starting data_cleaning with cat_cols: {cat_cols}, num_cols: {num_cols}, target_col: {target_col}") + logger.debug(f"Starting data_cleaning with cat_cols: {cat_cols}, num_cols: {num_cols}, target_col: {target_col}") try: df = read_file(file_info) - print(f"File read successfully. DataFrame shape: {df.shape}") - print(f"DataFrame columns: {list(df.columns)}") - print(f"DataFrame dtypes: {df.dtypes.to_dict()}") + logger.debug(f"File read successfully. DataFrame shape: {df.shape}") + logger.debug(f"DataFrame columns: {list(df.columns)}") + logger.debug(f"DataFrame dtypes: {df.dtypes.to_dict()}") except Exception as e: - print(f"Error reading file: {e}") + logger.debug(f"Error reading file: {e}") return { "Error": "Failed to read the file. Please check the file path and type." } # Filter DataFrame to include only the specified columns selected_columns = [target_col] + cat_cols + num_cols - print(f"Selected columns: {selected_columns}") + logger.debug(f"Selected columns: {selected_columns}") # Check if all columns exist missing_columns = [col for col in selected_columns if col not in df.columns] @@ -180,133 +182,133 @@ def data_cleaning(self: Task, cat_cols, num_cols, target_col, file_info): } df_filtered = df[selected_columns].copy() - print(f"Filtered DataFrame shape: {df_filtered.shape}") + logger.debug(f"Filtered DataFrame shape: {df_filtered.shape}") # Fill missing values more robustly if cat_cols: # Only process if there are categorical columns for col in cat_cols: try: - print(f"Processing categorical column: {col}") - print(f"Column {col} unique values before fillna: {df_filtered[col].nunique()}") + logger.debug(f"Processing categorical column: {col}") + logger.debug(f"Column {col} unique values before fillna: {df_filtered[col].nunique()}") df_filtered[col] = df_filtered[col].fillna("Missing") - print(f"Column {col} unique values after fillna: {df_filtered[col].nunique()}") + logger.debug(f"Column {col} unique values after fillna: {df_filtered[col].nunique()}") except Exception as e: - print(f"Warning: Error filling missing values in categorical column {col}: {e}") + logger.debug(f"Warning: Error filling missing values in categorical column {col}: {e}") # Fallback: replace NaN with a default value df_filtered[col] = df_filtered[col].astype(str).replace('nan', 'Missing') else: - print("No categorical columns to process") + logger.debug("No categorical columns to process") if num_cols: # Only process if there are numerical columns for col in num_cols: try: - print(f"Processing numerical column: {col}") - print(f"Column {col} data type: {df_filtered[col].dtype}") + logger.debug(f"Processing numerical column: {col}") + logger.debug(f"Column {col} data type: {df_filtered[col].dtype}") # Calculate mean safely col_mean = df_filtered[col].mean() if pd.isna(col_mean): col_mean = 0.0 - print(f"Column {col} mean: {col_mean}") + logger.debug(f"Column {col} mean: {col_mean}") df_filtered[col] = df_filtered[col].fillna(col_mean) except Exception as e: - print(f"Warning: Error filling missing values in numerical column {col}: {e}") + logger.debug(f"Warning: Error filling missing values in numerical column {col}: {e}") # Fallback: replace NaN with 0 df_filtered[col] = df_filtered[col].fillna(0.0) else: - print("No numerical columns to process") + logger.debug("No numerical columns to process") # One-hot encode categorical columns only if they exist if cat_cols: try: - print(f"Starting one-hot encoding for {len(cat_cols)} categorical columns...") + logger.debug(f"Starting one-hot encoding for {len(cat_cols)} categorical columns...") df_filtered = pd.get_dummies(df_filtered, columns=cat_cols) - print(f"One-hot encoding completed. DataFrame now has {df_filtered.shape[1]} columns.") + logger.debug(f"One-hot encoding completed. DataFrame now has {df_filtered.shape[1]} columns.") except Exception as e: - print(f"Error during one-hot encoding: {e}") + logger.debug(f"Error during one-hot encoding: {e}") return {"Error": f"One-hot encoding failed: {str(e)}"} else: - print("No categorical columns to encode") + logger.debug("No categorical columns to encode") # Encode target variable if categorical if pd.api.types.is_object_dtype(df_filtered[target_col]) or isinstance(df_filtered[target_col].dtype, pd.StringDtype): try: - print(f"Encoding target column {target_col}...") + logger.debug(f"Encoding target column {target_col}...") le_target = LabelEncoder() df_filtered[target_col] = le_target.fit_transform(df_filtered[target_col]) - print(f"Target column {target_col} encoded successfully.") + logger.debug(f"Target column {target_col} encoded successfully.") except Exception as e: - print(f"Error encoding target column: {e}") + logger.debug(f"Error encoding target column: {e}") return {"Error": f"Target column encoding failed: {str(e)}"} # Convert to JSON more safely try: - print("Converting DataFrame to JSON format...") + logger.debug("Converting DataFrame to JSON format...") result = df_filtered.to_dict(orient="list") - print(f"Data cleaning completed successfully. Final data shape: {df_filtered.shape}") + logger.debug(f"Data cleaning completed successfully. Final data shape: {df_filtered.shape}") return result except Exception as e: - print(f"Error converting to JSON: {e}") + logger.debug(f"Error converting to JSON: {e}") return {"Error": f"JSON conversion failed: {str(e)}"} except SoftTimeLimitExceeded: - print("Data Cleaning task timed out.") + logger.debug("Data Cleaning task timed out.") raise Exception("Data Cleaning task timed out.") except Exception as e: - print(f"Error occurred during data cleaning: {e}") + logger.debug(f"Error occurred during data cleaning: {e}") return {"Error": f"Data cleaning failed: {str(e)}"} @shared_task(bind=True, ignore_result=False) def pearson_correlation(self: Task, df_json, target_col) -> dict: try: - print(f"Starting pearson_correlation with target_col: {target_col}") - print(f"Input df_json type: {type(df_json)}") + logger.debug(f"Starting pearson_correlation with target_col: {target_col}") + logger.debug(f"Input df_json type: {type(df_json)}") if isinstance(df_json, dict): - print(f"Input df_json keys: {list(df_json.keys())}") + logger.debug(f"Input df_json keys: {list(df_json.keys())}") # Convert JSON back to DataFrame with proper error handling try: df = pd.DataFrame.from_dict(df_json) - print(f"DataFrame created successfully. Shape: {df.shape}") - print(f"DataFrame columns: {list(df.columns)}") - print(f"DataFrame dtypes: {df.dtypes.to_dict()}") + logger.debug(f"DataFrame created successfully. Shape: {df.shape}") + logger.debug(f"DataFrame columns: {list(df.columns)}") + logger.debug(f"DataFrame dtypes: {df.dtypes.to_dict()}") except Exception as e: - print(f"Error converting JSON to DataFrame: {e}") + logger.debug(f"Error converting JSON to DataFrame: {e}") return {"Error": f"Failed to convert data: {str(e)}"} # Ensure target column exists if target_col not in df.columns: - print(f"Target column '{target_col}' not found. Available columns: {list(df.columns)}") + logger.debug(f"Target column '{target_col}' not found. Available columns: {list(df.columns)}") return {"Error": f"Target column '{target_col}' not found in the data"} # Get columns excluding target column cols = df.columns.difference([target_col]) if len(cols) == 0: - print("No feature columns found for correlation analysis") + logger.debug("No feature columns found for correlation analysis") return {"Error": "No feature columns found for correlation analysis"} - print(f"Processing {len(cols)} feature columns: {list(cols)}") + logger.debug(f"Processing {len(cols)} feature columns: {list(cols)}") correlations = {} for col in cols: if col != target_col: try: - print(f"Processing column: {col}") - print(f"Column {col} dtype: {df[col].dtype}") - print(f"Target column {target_col} dtype: {df[target_col].dtype}") + logger.debug(f"Processing column: {col}") + logger.debug(f"Column {col} dtype: {df[col].dtype}") + logger.debug(f"Target column {target_col} dtype: {df[target_col].dtype}") # Ensure both columns are numeric if not pd.api.types.is_numeric_dtype(df[col]) or not pd.api.types.is_numeric_dtype(df[target_col]): - print(f"Warning: Skipping column '{col}' - non-numeric data types") + logger.debug(f"Warning: Skipping column '{col}' - non-numeric data types") continue # Remove any NaN values for this specific column pair valid_data = df[[col, target_col]].dropna() if len(valid_data) < 2: - print(f"Warning: Skipping column '{col}' - insufficient valid data after removing NaN values") + logger.debug(f"Warning: Skipping column '{col}' - insufficient valid data after removing NaN values") continue - print(f"Column {col} valid data points: {len(valid_data)}") + logger.debug(f"Column {col} valid data points: {len(valid_data)}") # Calculate covariance cov = np.cov(valid_data[col], valid_data[target_col], ddof=0)[0, 1] @@ -316,7 +318,7 @@ def pearson_correlation(self: Task, df_json, target_col) -> dict: # Check for division by zero if std_dev_col == 0 or std_dev_target == 0: - print(f"Warning: Skipping column '{col}' - zero standard deviation") + logger.debug(f"Warning: Skipping column '{col}' - zero standard deviation") continue # Calculate Pearson correlation coefficient @@ -325,25 +327,25 @@ def pearson_correlation(self: Task, df_json, target_col) -> dict: # Ensure correlation is a valid number if np.isfinite(corr): correlations[col] = float(corr) # Convert to Python float for JSON serialization - print(f"Column {col} correlation: {corr}") + logger.debug(f"Column {col} correlation: {corr}") else: - print(f"Warning: Skipping column '{col}' - invalid correlation value: {corr}") + logger.debug(f"Warning: Skipping column '{col}' - invalid correlation value: {corr}") except Exception as e: - print(f"Warning: Error calculating correlation for column '{col}': {e}") + logger.debug(f"Warning: Error calculating correlation for column '{col}': {e}") continue if not correlations: - print("No valid correlations could be calculated") + logger.debug("No valid correlations could be calculated") return {"Error": "No valid correlations could be calculated"} - print(f"Successfully calculated correlations for {len(correlations)} features") + logger.debug(f"Successfully calculated correlations for {len(correlations)} features") return correlations except SoftTimeLimitExceeded: raise Exception("Pearson Correlation task timed out.") except Exception as e: - print(f"Unexpected error in pearson_correlation: {e}") + logger.debug(f"Unexpected error in pearson_correlation: {e}") return {"Error": f"Correlation calculation failed: {str(e)}"} @@ -423,14 +425,14 @@ def plot_features(self: Task, correlations, target_col): buf.seek(0) image_base64 = base64.b64encode(buf.getvalue()).decode("utf-8") - print(f"Successfully created visualization with {len(clean_features)} features") + logger.debug(f"Successfully created visualization with {len(clean_features)} features") return image_base64 except SoftTimeLimitExceeded: - print("Plot Features task timed out.") + logger.debug("Plot Features task timed out.") raise Exception("Plot Features task timed out.") except Exception as e: - print(f"Error occurred during plotting: {e}") + logger.debug(f"Error occurred during plotting: {e}") return None diff --git a/aidrin/structured_data_metrics/privacy_measure.py b/aidrin/structured_data_metrics/privacy_measure.py index d7e1158b..e7c60a14 100644 --- a/aidrin/structured_data_metrics/privacy_measure.py +++ b/aidrin/structured_data_metrics/privacy_measure.py @@ -74,7 +74,7 @@ def generate_single_attribute_MM_risk_scores(df, id_col, eval_cols, task=None): # Drop rows with missing values selected_df = selected_df.dropna() rows_after_dropna = len(selected_df) - print(rows_after_dropna) + logger.debug(rows_after_dropna) if rows_after_dropna == 0: raise ValueError("After removing missing values, no data remains. Please check your data quality or select different columns.") @@ -250,7 +250,7 @@ def generate_multiple_attribute_MM_risk_scores(df, id_col, eval_cols, task=None) rows_after_dropna = len(selected_df) if rows_after_dropna == 0: - print("DEBUG: About to raise ValueError - no data remains after dropna") + logger.debug("DEBUG: About to raise ValueError - no data remains after dropna") raise ValueError("After removing missing values, no data remains. Please check your data quality or select different columns.") # Check data quality for quasi-identifiers From 82b990602f5250e89b423875722ff355bd2f6996 Mon Sep 17 00:00:00 2001 From: BMK Date: Wed, 18 Mar 2026 16:58:33 -0400 Subject: [PATCH 4/6] fix: move logging import to correct position after other imports --- aidrin/structured_data_metrics/class_imbalance.py | 5 +++-- aidrin/structured_data_metrics/conditional_demo_disp.py | 5 +++-- aidrin/structured_data_metrics/correlation_score.py | 5 +++-- aidrin/structured_data_metrics/feature_relevance.py | 5 +++-- aidrin/structured_data_metrics/privacy_measure.py | 2 ++ 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/aidrin/structured_data_metrics/class_imbalance.py b/aidrin/structured_data_metrics/class_imbalance.py index 8d593938..5d846366 100644 --- a/aidrin/structured_data_metrics/class_imbalance.py +++ b/aidrin/structured_data_metrics/class_imbalance.py @@ -1,5 +1,3 @@ -import logging -logger = logging.getLogger(__name__) import base64 import io import warnings @@ -12,6 +10,9 @@ matplotlib.use('Agg') # Use non-interactive backend import matplotlib.pyplot as plt # noqa: E402 +import logging + +logger = logging.getLogger(__name__) plt.ioff() # Turn off interactive mode diff --git a/aidrin/structured_data_metrics/conditional_demo_disp.py b/aidrin/structured_data_metrics/conditional_demo_disp.py index f710d985..a7a5fdbf 100644 --- a/aidrin/structured_data_metrics/conditional_demo_disp.py +++ b/aidrin/structured_data_metrics/conditional_demo_disp.py @@ -1,8 +1,9 @@ -import logging -logger = logging.getLogger(__name__) import pandas as pd from celery import Task, shared_task from celery.exceptions import SoftTimeLimitExceeded +import logging + +logger = logging.getLogger(__name__) @shared_task(bind=True, ignore_result=False) diff --git a/aidrin/structured_data_metrics/correlation_score.py b/aidrin/structured_data_metrics/correlation_score.py index 14088990..ee381afa 100644 --- a/aidrin/structured_data_metrics/correlation_score.py +++ b/aidrin/structured_data_metrics/correlation_score.py @@ -1,5 +1,3 @@ -import logging -logger = logging.getLogger(__name__) import base64 from io import BytesIO from typing import List @@ -12,6 +10,9 @@ from dython.nominal import associations from aidrin.file_handling.file_parser import read_file +import logging + +logger = logging.getLogger(__name__) matplotlib.use("Agg") diff --git a/aidrin/structured_data_metrics/feature_relevance.py b/aidrin/structured_data_metrics/feature_relevance.py index cb75a330..13df6ebd 100644 --- a/aidrin/structured_data_metrics/feature_relevance.py +++ b/aidrin/structured_data_metrics/feature_relevance.py @@ -1,5 +1,3 @@ -import logging -logger = logging.getLogger(__name__) import base64 import io @@ -11,6 +9,9 @@ from sklearn.preprocessing import LabelEncoder from aidrin.file_handling.file_parser import read_file +import logging + +logger = logging.getLogger(__name__) # def calc_shapley(df, cat_cols, num_cols, target_col): # """ diff --git a/aidrin/structured_data_metrics/privacy_measure.py b/aidrin/structured_data_metrics/privacy_measure.py index e7c60a14..53d840e8 100644 --- a/aidrin/structured_data_metrics/privacy_measure.py +++ b/aidrin/structured_data_metrics/privacy_measure.py @@ -12,6 +12,8 @@ logger = logging.getLogger(__name__) +logger = logging.getLogger(__name__) + def generate_single_attribute_MM_risk_scores(df, id_col, eval_cols, task=None): result_dict = {} From c5269a5b3d1d7ef8e1df32924a502c762338075f Mon Sep 17 00:00:00 2001 From: BMK Date: Wed, 18 Mar 2026 15:00:36 -0400 Subject: [PATCH 5/6] fix: replace debug print() statements with logger.debug() in metric modules --- aidrin/structured_data_metrics/class_imbalance.py | 2 ++ aidrin/structured_data_metrics/conditional_demo_disp.py | 2 ++ aidrin/structured_data_metrics/correlation_score.py | 2 ++ aidrin/structured_data_metrics/feature_relevance.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/aidrin/structured_data_metrics/class_imbalance.py b/aidrin/structured_data_metrics/class_imbalance.py index 5d846366..02d6cc7c 100644 --- a/aidrin/structured_data_metrics/class_imbalance.py +++ b/aidrin/structured_data_metrics/class_imbalance.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import base64 import io import warnings diff --git a/aidrin/structured_data_metrics/conditional_demo_disp.py b/aidrin/structured_data_metrics/conditional_demo_disp.py index a7a5fdbf..7f991722 100644 --- a/aidrin/structured_data_metrics/conditional_demo_disp.py +++ b/aidrin/structured_data_metrics/conditional_demo_disp.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import pandas as pd from celery import Task, shared_task from celery.exceptions import SoftTimeLimitExceeded diff --git a/aidrin/structured_data_metrics/correlation_score.py b/aidrin/structured_data_metrics/correlation_score.py index ee381afa..0d6c2d2f 100644 --- a/aidrin/structured_data_metrics/correlation_score.py +++ b/aidrin/structured_data_metrics/correlation_score.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import base64 from io import BytesIO from typing import List diff --git a/aidrin/structured_data_metrics/feature_relevance.py b/aidrin/structured_data_metrics/feature_relevance.py index 13df6ebd..5bb242f9 100644 --- a/aidrin/structured_data_metrics/feature_relevance.py +++ b/aidrin/structured_data_metrics/feature_relevance.py @@ -1,3 +1,5 @@ +import logging +logger = logging.getLogger(__name__) import base64 import io From 59394aac66e83c2aad782144d02b882d8cf39a48 Mon Sep 17 00:00:00 2001 From: BMK Date: Wed, 18 Mar 2026 16:58:33 -0400 Subject: [PATCH 6/6] fix: move logging import to correct position after other imports --- aidrin/structured_data_metrics/class_imbalance.py | 2 -- aidrin/structured_data_metrics/conditional_demo_disp.py | 2 -- aidrin/structured_data_metrics/correlation_score.py | 2 -- aidrin/structured_data_metrics/feature_relevance.py | 2 -- 4 files changed, 8 deletions(-) diff --git a/aidrin/structured_data_metrics/class_imbalance.py b/aidrin/structured_data_metrics/class_imbalance.py index 02d6cc7c..5d846366 100644 --- a/aidrin/structured_data_metrics/class_imbalance.py +++ b/aidrin/structured_data_metrics/class_imbalance.py @@ -1,5 +1,3 @@ -import logging -logger = logging.getLogger(__name__) import base64 import io import warnings diff --git a/aidrin/structured_data_metrics/conditional_demo_disp.py b/aidrin/structured_data_metrics/conditional_demo_disp.py index 7f991722..a7a5fdbf 100644 --- a/aidrin/structured_data_metrics/conditional_demo_disp.py +++ b/aidrin/structured_data_metrics/conditional_demo_disp.py @@ -1,5 +1,3 @@ -import logging -logger = logging.getLogger(__name__) import pandas as pd from celery import Task, shared_task from celery.exceptions import SoftTimeLimitExceeded diff --git a/aidrin/structured_data_metrics/correlation_score.py b/aidrin/structured_data_metrics/correlation_score.py index 0d6c2d2f..ee381afa 100644 --- a/aidrin/structured_data_metrics/correlation_score.py +++ b/aidrin/structured_data_metrics/correlation_score.py @@ -1,5 +1,3 @@ -import logging -logger = logging.getLogger(__name__) import base64 from io import BytesIO from typing import List diff --git a/aidrin/structured_data_metrics/feature_relevance.py b/aidrin/structured_data_metrics/feature_relevance.py index 5bb242f9..13df6ebd 100644 --- a/aidrin/structured_data_metrics/feature_relevance.py +++ b/aidrin/structured_data_metrics/feature_relevance.py @@ -1,5 +1,3 @@ -import logging -logger = logging.getLogger(__name__) import base64 import io