diff --git a/src/stamp/modeling/data.py b/src/stamp/modeling/data.py index 3604ce82..19c9bb4b 100755 --- a/src/stamp/modeling/data.py +++ b/src/stamp/modeling/data.py @@ -1104,11 +1104,9 @@ def filter_complete_patient_data_( ) } - _logger.info( - f"Total patients in clinical table: {len(patient_to_ground_truth)}\n" - f"Patients appearing in slide table: {len(patient_to_slides)}\n" - f"Final usable patients (complete data): {len(patients)}\n" - ) + _logger.info("Total patients in clinical table: %d", len(patient_to_ground_truth)) + _logger.info("Patients appearing in slide table: %d", len(patient_to_slides)) + _logger.info("Final usable patients (complete data): %d", len(patients)) return patients diff --git a/src/stamp/modeling/models/cox.py b/src/stamp/modeling/models/cox.py index 48b88a6b..94da2cb6 100644 --- a/src/stamp/modeling/models/cox.py +++ b/src/stamp/modeling/models/cox.py @@ -4,7 +4,6 @@ # pylint: disable=C0103 # pylint: disable=C0301 -import sys import warnings import torch @@ -268,15 +267,3 @@ def neg_partial_log_likelihood( ) ) return loss - - -if __name__ == "__main__": - import doctest - - # Run doctest - results = doctest.testmod() - if results.failed == 0: - print("All tests passed.") - else: - print("Some doctests failed.") - sys.exit(1) diff --git a/src/stamp/statistics/__init__.py b/src/stamp/statistics/__init__.py index 0a7eedef..6b15eb87 100644 --- a/src/stamp/statistics/__init__.py +++ b/src/stamp/statistics/__init__.py @@ -30,8 +30,9 @@ ) from stamp.statistics.survival import _plot_km, _survival_stats_for_csv from stamp.types import PandasLabel, Task +from stamp.utils.path import path_safe -__all__ = ["StatsConfig", "compute_stats_"] +__all__ = ["StatsConfig", "compute_stats_", "path_safe"] __author__ = "Marko van Treeck, Minh Duc Nguyen" @@ -146,7 +147,9 @@ def _compute_multitarget_classification_stats( ) fig.tight_layout() - fig.savefig(output_dir / f"roc-curve_{target_label}={true_class}.svg") + fig.savefig( + output_dir / f"roc-curve_{path_safe(target_label)}={true_class}.svg" + ) plt.close(fig) # Plot PRC curve @@ -172,7 +175,9 @@ def _compute_multitarget_classification_stats( ) fig.tight_layout() - fig.savefig(output_dir / f"pr-curve_{target_label}={true_class}.svg") + fig.savefig( + output_dir / f"pr-curve_{path_safe(target_label)}={true_class}.svg" + ) plt.close(fig) # Compute aggregated statistics for all targets @@ -292,7 +297,8 @@ def compute_stats_( fig.tight_layout() output_dir.mkdir(parents=True, exist_ok=True) fig.savefig( - output_dir / f"roc-curve_{ground_truth_label}={true_class}.svg" + output_dir + / f"roc-curve_{path_safe(ground_truth_label)}={true_class}.svg" ) plt.close(fig) @@ -321,7 +327,8 @@ def compute_stats_( fig.tight_layout() fig.savefig( - output_dir / f"pr-curve_{ground_truth_label}={true_class}.svg" + output_dir + / f"pr-curve_{path_safe(ground_truth_label)}={true_class}.svg" ) plt.close(fig) diff --git a/src/stamp/statistics/categorical.py b/src/stamp/statistics/categorical.py index a267f5ca..ac48994d 100755 --- a/src/stamp/statistics/categorical.py +++ b/src/stamp/statistics/categorical.py @@ -7,6 +7,8 @@ import scipy.stats as st from sklearn import metrics +from stamp.utils.path import path_safe + __author__ = "Marko van Treeck" __copyright__ = "Copyright (C) 2022-2025 Marko van Treeck" __license__ = "MIT" @@ -137,9 +139,13 @@ def categorical_aggregated_( ) preds_df = pd.concat(preds_dfs).sort_index() - preds_df.to_csv(outpath / f"{ground_truth_label}_categorical-stats_individual.csv") + preds_df.to_csv( + outpath / f"{path_safe(ground_truth_label)}_categorical-stats_individual.csv" + ) stats_df = _aggregate_categorical_stats(preds_df.reset_index()) - stats_df.to_csv(outpath / f"{ground_truth_label}_categorical-stats_aggregated.csv") + stats_df.to_csv( + outpath / f"{path_safe(ground_truth_label)}_categorical-stats_aggregated.csv" + ) def categorical_aggregated_multitarget_( @@ -181,11 +187,15 @@ def categorical_aggregated_multitarget_( # Concatenate and save individual stats for this target preds_df = pd.concat(preds_dfs).sort_index() - preds_df.to_csv(outpath / f"{target_label}_categorical-stats_individual.csv") + preds_df.to_csv( + outpath / f"{path_safe(target_label)}_categorical-stats_individual.csv" + ) # Aggregate stats for this target stats_df = _aggregate_categorical_stats(preds_df.reset_index()) - stats_df.to_csv(outpath / f"{target_label}_categorical-stats_aggregated.csv") + stats_df.to_csv( + outpath / f"{path_safe(target_label)}_categorical-stats_aggregated.csv" + ) # Store for summary all_target_stats[target_label] = stats_df diff --git a/src/stamp/statistics/regression.py b/src/stamp/statistics/regression.py index c92b5bd9..d616c15f 100644 --- a/src/stamp/statistics/regression.py +++ b/src/stamp/statistics/regression.py @@ -10,6 +10,8 @@ import scipy.stats as st from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score +from stamp.utils.path import path_safe + def _regression(preds_df: pd.DataFrame, target_label: str) -> pd.Series: """Compute regression metrics for one prediction table.""" @@ -107,10 +109,14 @@ def regression_aggregated_( # Save individual stats and aggregate stats_df = pd.DataFrame(stats).transpose() - stats_df.to_csv(outpath / f"{ground_truth_label}_regression-stats_individual.csv") + stats_df.to_csv( + outpath / f"{path_safe(ground_truth_label)}_regression-stats_individual.csv" + ) mean = stats_df.mean(numeric_only=True) sem = stats_df.sem(numeric_only=True) lower, upper = st.t.interval(0.95, len(stats_df) - 1, loc=mean, scale=sem) agg = pd.DataFrame({"mean": mean, "95%_low": lower, "95%_high": upper}) - agg.to_csv(outpath / f"{ground_truth_label}_regression-stats_aggregated.csv") + agg.to_csv( + outpath / f"{path_safe(ground_truth_label)}_regression-stats_aggregated.csv" + ) diff --git a/src/stamp/utils/path.py b/src/stamp/utils/path.py new file mode 100644 index 00000000..ab577841 --- /dev/null +++ b/src/stamp/utils/path.py @@ -0,0 +1,6 @@ +"""Small path-related helpers shared across the project.""" + + +def path_safe(label: str) -> str: + """Replace path separators so labels are safe as filename parts.""" + return label.replace("/", "_").replace("\\", "_")