|
@@ -3,6 +3,7 @@
|
|
|
from __future__ import annotations
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
from pathlib import Path
|
|
|
|
|
+import re
|
|
|
from typing import Any
|
|
from typing import Any
|
|
|
|
|
|
|
|
import numpy as np
|
|
import numpy as np
|
|
@@ -16,10 +17,11 @@ from .plotting import (
|
|
|
save_boxplot,
|
|
save_boxplot,
|
|
|
save_calibration_plot,
|
|
save_calibration_plot,
|
|
|
save_performance_threshold_plot,
|
|
save_performance_threshold_plot,
|
|
|
|
|
+ save_performance_threshold_pair_plot,
|
|
|
save_uncertainty_cutoff_plot,
|
|
save_uncertainty_cutoff_plot,
|
|
|
|
|
+ save_uncertainty_cutoff_pair_plot,
|
|
|
)
|
|
)
|
|
|
from .runtime import write_json
|
|
from .runtime import write_json
|
|
|
-from .uncertainty import confidence_uncertainty
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _save_table(rows: list[dict[str, Any]], out_path: Path) -> pd.DataFrame:
|
|
def _save_table(rows: list[dict[str, Any]], out_path: Path) -> pd.DataFrame:
|
|
@@ -39,7 +41,7 @@ def _uncertainty_percentiles(values: np.ndarray) -> np.ndarray:
|
|
|
|
|
|
|
|
order = np.argsort(vals)
|
|
order = np.argsort(vals)
|
|
|
ranks = np.empty(n, dtype=float)
|
|
ranks = np.empty(n, dtype=float)
|
|
|
- ranks[order] = np.arange(n, dtype=float)
|
|
|
|
|
|
|
+ ranks[order] = np.arange(0, n, dtype=float)
|
|
|
return (ranks / float(n - 1)) * 100.0
|
|
return (ranks / float(n - 1)) * 100.0
|
|
|
|
|
|
|
|
|
|
|
|
@@ -47,7 +49,7 @@ def _save_ensemble_prediction_debug(
|
|
|
evaluation: BackendEvaluation,
|
|
evaluation: BackendEvaluation,
|
|
|
output_dir: Path,
|
|
output_dir: Path,
|
|
|
) -> str:
|
|
) -> str:
|
|
|
- uncertainty_vals = confidence_uncertainty(evaluation.y_prob)
|
|
|
|
|
|
|
+ uncertainty_vals = np.asarray(evaluation.uncertainty_confidence, dtype=float)
|
|
|
uncertainty_pct = _uncertainty_percentiles(uncertainty_vals)
|
|
uncertainty_pct = _uncertainty_percentiles(uncertainty_vals)
|
|
|
pred_label = (evaluation.y_prob >= DEFAULT_DECISION_THRESHOLD).astype(int)
|
|
pred_label = (evaluation.y_prob >= DEFAULT_DECISION_THRESHOLD).astype(int)
|
|
|
true_label = np.asarray(evaluation.y_true, dtype=int)
|
|
true_label = np.asarray(evaluation.y_true, dtype=int)
|
|
@@ -60,10 +62,10 @@ def _save_ensemble_prediction_debug(
|
|
|
"predicted_label": pred_label,
|
|
"predicted_label": pred_label,
|
|
|
"actual_label": true_label,
|
|
"actual_label": true_label,
|
|
|
"is_correct": is_correct,
|
|
"is_correct": is_correct,
|
|
|
- "uncertainty": uncertainty_vals,
|
|
|
|
|
- "uncertainty_percentile": uncertainty_pct,
|
|
|
|
|
|
|
+ "confidence": uncertainty_vals,
|
|
|
|
|
+ "confidence_percentile": uncertainty_pct,
|
|
|
}
|
|
}
|
|
|
- ).sort_values("uncertainty", ascending=True)
|
|
|
|
|
|
|
+ ).sort_values("confidence", ascending=False)
|
|
|
|
|
|
|
|
path = output_dir / "ensemble_prediction_debug.csv"
|
|
path = output_dir / "ensemble_prediction_debug.csv"
|
|
|
debug_df.to_csv(path, index=False)
|
|
debug_df.to_csv(path, index=False)
|
|
@@ -75,13 +77,18 @@ def _uncertainty_cutoff_analysis(
|
|
|
output_dir: Path,
|
|
output_dir: Path,
|
|
|
uncertainty_types: list[tuple[str, np.ndarray]],
|
|
uncertainty_types: list[tuple[str, np.ndarray]],
|
|
|
cutoff_percentiles: np.ndarray,
|
|
cutoff_percentiles: np.ndarray,
|
|
|
- keep_higher: bool,
|
|
|
|
|
table_filename: str,
|
|
table_filename: str,
|
|
|
- plot_filename: str,
|
|
|
|
|
|
|
+ plot_filename_prefix: str,
|
|
|
title_prefix: str,
|
|
title_prefix: str,
|
|
|
x_label: str,
|
|
x_label: str,
|
|
|
- selection_label: str,
|
|
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
|
|
+ def _slug(name: str) -> str:
|
|
|
|
|
+ s = re.sub(r"[^a-z0-9]+", "_", name.strip().lower())
|
|
|
|
|
+ return s.strip("_") or "metric"
|
|
|
|
|
+
|
|
|
|
|
+ def _confidence_like(name: str) -> bool:
|
|
|
|
|
+ return "confidence" in name.strip().lower()
|
|
|
|
|
+
|
|
|
cutoff_rows: list[dict[str, Any]] = []
|
|
cutoff_rows: list[dict[str, Any]] = []
|
|
|
|
|
|
|
|
for uncertainty_name, values in uncertainty_types:
|
|
for uncertainty_name, values in uncertainty_types:
|
|
@@ -92,6 +99,12 @@ def _uncertainty_cutoff_analysis(
|
|
|
values_valid = values[finite_mask]
|
|
values_valid = values[finite_mask]
|
|
|
y_true_valid = evaluation.y_true[finite_mask]
|
|
y_true_valid = evaluation.y_true[finite_mask]
|
|
|
y_prob_valid = evaluation.y_prob[finite_mask]
|
|
y_prob_valid = evaluation.y_prob[finite_mask]
|
|
|
|
|
+ keep_higher = _confidence_like(uncertainty_name)
|
|
|
|
|
+ selection_label = (
|
|
|
|
|
+ "metric >= percentile cutoff (higher is lower uncertainty)"
|
|
|
|
|
+ if keep_higher
|
|
|
|
|
+ else "metric <= percentile cutoff (lower is lower uncertainty)"
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
for cutoff_percentile in cutoff_percentiles:
|
|
for cutoff_percentile in cutoff_percentiles:
|
|
|
cutoff_value = float(np.percentile(values_valid, cutoff_percentile))
|
|
cutoff_value = float(np.percentile(values_valid, cutoff_percentile))
|
|
@@ -117,35 +130,86 @@ def _uncertainty_cutoff_analysis(
|
|
|
"n_retained": retained,
|
|
"n_retained": retained,
|
|
|
"coverage": float(retained / len(values_valid)),
|
|
"coverage": float(retained / len(values_valid)),
|
|
|
"selection_rule": selection_label,
|
|
"selection_rule": selection_label,
|
|
|
|
|
+ "keep_higher": bool(keep_higher),
|
|
|
"accuracy": float(perf["accuracy"]),
|
|
"accuracy": float(perf["accuracy"]),
|
|
|
"f1": float(perf["f1"]),
|
|
"f1": float(perf["f1"]),
|
|
|
|
|
+ "n_correct": int(perf["tp"] + perf["tn"]),
|
|
|
|
|
+ "n_incorrect": int(perf["fp"] + perf["fn"]),
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
table_path = output_dir / table_filename
|
|
table_path = output_dir / table_filename
|
|
|
- plot_path = plots_dir(output_dir) / plot_filename
|
|
|
|
|
|
|
+ accuracy_plot_path = plots_dir(output_dir) / f"{plot_filename_prefix}_accuracy.png"
|
|
|
|
|
+ f1_plot_path = plots_dir(output_dir) / f"{plot_filename_prefix}_f1.png"
|
|
|
|
|
+ pair_plot_path = plots_dir(output_dir) / f"{plot_filename_prefix}_accuracy_f1.png"
|
|
|
if not cutoff_rows:
|
|
if not cutoff_rows:
|
|
|
- return {"table": str(table_path), "plot": str(plot_path), "rows": 0}
|
|
|
|
|
|
|
+ return {
|
|
|
|
|
+ "table": str(table_path),
|
|
|
|
|
+ "accuracy_plot": str(accuracy_plot_path),
|
|
|
|
|
+ "f1_plot": str(f1_plot_path),
|
|
|
|
|
+ "pair_plot": str(pair_plot_path),
|
|
|
|
|
+ "rows": 0,
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
cutoff_df = pd.DataFrame(cutoff_rows)
|
|
cutoff_df = pd.DataFrame(cutoff_rows)
|
|
|
cutoff_df.to_csv(table_path, index=False)
|
|
cutoff_df.to_csv(table_path, index=False)
|
|
|
|
|
|
|
|
# Restriction level increases from left to right so right-most points are most restricted.
|
|
# Restriction level increases from left to right so right-most points are most restricted.
|
|
|
- if keep_higher:
|
|
|
|
|
- cutoff_df["restriction_level"] = cutoff_df["cutoff_percentile"]
|
|
|
|
|
- else:
|
|
|
|
|
- cutoff_df["restriction_level"] = 100.0 - cutoff_df["cutoff_percentile"]
|
|
|
|
|
-
|
|
|
|
|
- save_uncertainty_cutoff_plot(
|
|
|
|
|
- cutoff_df=cutoff_df,
|
|
|
|
|
- title_prefix=title_prefix,
|
|
|
|
|
- x_label=x_label,
|
|
|
|
|
- output_path=plot_path,
|
|
|
|
|
|
|
+ cutoff_df["restriction_level"] = np.where(
|
|
|
|
|
+ cutoff_df["keep_higher"].astype(bool),
|
|
|
|
|
+ cutoff_df["cutoff_percentile"],
|
|
|
|
|
+ 100.0 - cutoff_df["cutoff_percentile"],
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+ plots_by_uncertainty: dict[str, dict[str, str]] = {}
|
|
|
|
|
+ for uncertainty_name in sorted(pd.unique(cutoff_df["uncertainty_type"])):
|
|
|
|
|
+ sub_df = cutoff_df[cutoff_df["uncertainty_type"] == uncertainty_name].copy()
|
|
|
|
|
+ slug = _slug(str(uncertainty_name))
|
|
|
|
|
+ sub_accuracy_plot_path = (
|
|
|
|
|
+ plots_dir(output_dir) / f"{plot_filename_prefix}_{slug}_accuracy.png"
|
|
|
|
|
+ )
|
|
|
|
|
+ sub_f1_plot_path = (
|
|
|
|
|
+ plots_dir(output_dir) / f"{plot_filename_prefix}_{slug}_f1.png"
|
|
|
|
|
+ )
|
|
|
|
|
+ sub_pair_plot_path = (
|
|
|
|
|
+ plots_dir(output_dir) / f"{plot_filename_prefix}_{slug}_accuracy_f1.png"
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ save_uncertainty_cutoff_plot(
|
|
|
|
|
+ cutoff_df=sub_df,
|
|
|
|
|
+ title_prefix=f"{title_prefix} ({uncertainty_name})",
|
|
|
|
|
+ x_label=x_label,
|
|
|
|
|
+ output_path=sub_accuracy_plot_path,
|
|
|
|
|
+ metric_column="accuracy",
|
|
|
|
|
+ metric_label="Accuracy",
|
|
|
|
|
+ plot_key=f"{plot_filename_prefix}_{slug}_accuracy",
|
|
|
|
|
+ )
|
|
|
|
|
+ save_uncertainty_cutoff_plot(
|
|
|
|
|
+ cutoff_df=sub_df,
|
|
|
|
|
+ title_prefix=f"{title_prefix} ({uncertainty_name})",
|
|
|
|
|
+ x_label=x_label,
|
|
|
|
|
+ output_path=sub_f1_plot_path,
|
|
|
|
|
+ metric_column="f1",
|
|
|
|
|
+ metric_label="F1",
|
|
|
|
|
+ plot_key=f"{plot_filename_prefix}_{slug}_f1",
|
|
|
|
|
+ )
|
|
|
|
|
+ save_uncertainty_cutoff_pair_plot(
|
|
|
|
|
+ cutoff_df=sub_df,
|
|
|
|
|
+ title_prefix=f"{title_prefix} ({uncertainty_name})",
|
|
|
|
|
+ x_label=x_label,
|
|
|
|
|
+ output_path=sub_pair_plot_path,
|
|
|
|
|
+ plot_key=f"{plot_filename_prefix}_{slug}_accuracy_f1",
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ plots_by_uncertainty[str(uncertainty_name)] = {
|
|
|
|
|
+ "accuracy": str(sub_accuracy_plot_path),
|
|
|
|
|
+ "f1": str(sub_f1_plot_path),
|
|
|
|
|
+ "accuracy_f1": str(sub_pair_plot_path),
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
"table": str(table_path),
|
|
"table": str(table_path),
|
|
|
- "plot": str(plot_path),
|
|
|
|
|
|
|
+ "plots_by_uncertainty": plots_by_uncertainty,
|
|
|
"rows": int(len(cutoff_df)),
|
|
"rows": int(len(cutoff_df)),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -159,22 +223,46 @@ def run_performance(
|
|
|
table_path = output_dir / "performance_threshold_sweep.csv"
|
|
table_path = output_dir / "performance_threshold_sweep.csv"
|
|
|
df = _save_table(rows, table_path)
|
|
df = _save_table(rows, table_path)
|
|
|
|
|
|
|
|
- plot_path = plots_dir(output_dir) / "performance_threshold_sweep.png"
|
|
|
|
|
|
|
+ accuracy_plot_path = plots_dir(output_dir) / "performance_threshold_accuracy.png"
|
|
|
|
|
+ f1_plot_path = plots_dir(output_dir) / "performance_threshold_f1.png"
|
|
|
|
|
+ pair_plot_path = plots_dir(output_dir) / "performance_threshold_accuracy_f1.png"
|
|
|
save_performance_threshold_plot(
|
|
save_performance_threshold_plot(
|
|
|
df=df,
|
|
df=df,
|
|
|
backend=evaluation.backend,
|
|
backend=evaluation.backend,
|
|
|
- output_path=plot_path,
|
|
|
|
|
|
|
+ output_path=accuracy_plot_path,
|
|
|
|
|
+ metric_column="accuracy",
|
|
|
|
|
+ metric_label="Accuracy",
|
|
|
|
|
+ plot_key="performance_threshold_accuracy",
|
|
|
|
|
+ )
|
|
|
|
|
+ save_performance_threshold_plot(
|
|
|
|
|
+ df=df,
|
|
|
|
|
+ backend=evaluation.backend,
|
|
|
|
|
+ output_path=f1_plot_path,
|
|
|
|
|
+ metric_column="f1",
|
|
|
|
|
+ metric_label="F1",
|
|
|
|
|
+ plot_key="performance_threshold_f1",
|
|
|
|
|
+ )
|
|
|
|
|
+ save_performance_threshold_pair_plot(
|
|
|
|
|
+ df=df,
|
|
|
|
|
+ backend=evaluation.backend,
|
|
|
|
|
+ output_path=pair_plot_path,
|
|
|
|
|
+ plot_key="performance_threshold_accuracy_f1",
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
best_idx = int(df["f1"].idxmax())
|
|
best_idx = int(df["f1"].idxmax())
|
|
|
best = df.iloc[best_idx].to_dict()
|
|
best = df.iloc[best_idx].to_dict()
|
|
|
|
|
|
|
|
cutoff_percentiles = uncertainty_cutoff_percentiles()
|
|
cutoff_percentiles = uncertainty_cutoff_percentiles()
|
|
|
- confidence_uncertainty_vals = confidence_uncertainty(evaluation.y_prob)
|
|
|
|
|
|
|
+ model_output_vals = np.asarray(evaluation.uncertainty_confidence, dtype=float)
|
|
|
secondary_uncertainty = np.asarray(evaluation.uncertainty_std, dtype=float)
|
|
secondary_uncertainty = np.asarray(evaluation.uncertainty_std, dtype=float)
|
|
|
|
|
+ secondary_uncertainty_name = (
|
|
|
|
|
+ "predictive uncertainty"
|
|
|
|
|
+ if evaluation.uncertainty_metric == "predictive_entropy"
|
|
|
|
|
+ else "standard deviation"
|
|
|
|
|
+ )
|
|
|
uncertainty_types = [
|
|
uncertainty_types = [
|
|
|
- ("confidence_uncertainty", confidence_uncertainty_vals),
|
|
|
|
|
- (evaluation.uncertainty_metric, secondary_uncertainty),
|
|
|
|
|
|
|
+ ("confidence", model_output_vals),
|
|
|
|
|
+ (secondary_uncertainty_name, secondary_uncertainty),
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
uncertainty_cutoff = _uncertainty_cutoff_analysis(
|
|
uncertainty_cutoff = _uncertainty_cutoff_analysis(
|
|
@@ -182,12 +270,10 @@ def run_performance(
|
|
|
output_dir=output_dir,
|
|
output_dir=output_dir,
|
|
|
uncertainty_types=uncertainty_types,
|
|
uncertainty_types=uncertainty_types,
|
|
|
cutoff_percentiles=cutoff_percentiles,
|
|
cutoff_percentiles=cutoff_percentiles,
|
|
|
- keep_higher=False,
|
|
|
|
|
table_filename="performance_uncertainty_cutoff.csv",
|
|
table_filename="performance_uncertainty_cutoff.csv",
|
|
|
- plot_filename="performance_uncertainty_cutoff.png",
|
|
|
|
|
- title_prefix="Uncertainty Cutoff Percentile",
|
|
|
|
|
- x_label="Restriction Level (0 = all results, 100 = lowest-uncertainty subset)",
|
|
|
|
|
- selection_label="uncertainty <= percentile cutoff",
|
|
|
|
|
|
|
+ plot_filename_prefix="performance_uncertainty_cutoff",
|
|
|
|
|
+ title_prefix="Model Output / Uncertainty Cutoff Percentile",
|
|
|
|
|
+ x_label="Restriction Level (0 = all samples, 100 = most restricted subset)",
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
percentile_cutoffs = uncertainty_cutoff_percentiles()
|
|
percentile_cutoffs = uncertainty_cutoff_percentiles()
|
|
@@ -196,32 +282,34 @@ def run_performance(
|
|
|
output_dir=output_dir,
|
|
output_dir=output_dir,
|
|
|
uncertainty_types=uncertainty_types,
|
|
uncertainty_types=uncertainty_types,
|
|
|
cutoff_percentiles=percentile_cutoffs,
|
|
cutoff_percentiles=percentile_cutoffs,
|
|
|
- keep_higher=True,
|
|
|
|
|
table_filename="performance_uncertainty_percentile_cutoff.csv",
|
|
table_filename="performance_uncertainty_percentile_cutoff.csv",
|
|
|
- plot_filename="performance_uncertainty_percentile_cutoff.png",
|
|
|
|
|
- title_prefix="Uncertainty Percentile Floor",
|
|
|
|
|
- x_label="Uncertainty Percentile Floor (0 = all results, 100 = highest-uncertainty subset)",
|
|
|
|
|
- selection_label="uncertainty >= percentile floor",
|
|
|
|
|
|
|
+ plot_filename_prefix="performance_uncertainty_percentile_cutoff",
|
|
|
|
|
+ title_prefix="Model Output / Uncertainty Percentile Floor",
|
|
|
|
|
+ x_label="Percentile Floor (0 = all samples, 100 = top percentile subset)",
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
cutoff_table_path = Path(uncertainty_cutoff["table"])
|
|
cutoff_table_path = Path(uncertainty_cutoff["table"])
|
|
|
- cutoff_plot_path = Path(uncertainty_cutoff["plot"])
|
|
|
|
|
percentile_cutoff_table_path = Path(uncertainty_percentile_cutoff["table"])
|
|
percentile_cutoff_table_path = Path(uncertainty_percentile_cutoff["table"])
|
|
|
- percentile_cutoff_plot_path = Path(uncertainty_percentile_cutoff["plot"])
|
|
|
|
|
summary = {
|
|
summary = {
|
|
|
"best_by_f1": {
|
|
"best_by_f1": {
|
|
|
k: float(v) for k, v in best.items() if isinstance(v, (int, float))
|
|
k: float(v) for k, v in best.items() if isinstance(v, (int, float))
|
|
|
},
|
|
},
|
|
|
"table": str(table_path),
|
|
"table": str(table_path),
|
|
|
- "plot": str(plot_path),
|
|
|
|
|
|
|
+ "plots": {
|
|
|
|
|
+ "accuracy": str(accuracy_plot_path),
|
|
|
|
|
+ "f1": str(f1_plot_path),
|
|
|
|
|
+ "accuracy_f1": str(pair_plot_path),
|
|
|
|
|
+ },
|
|
|
"uncertainty_cutoff": {
|
|
"uncertainty_cutoff": {
|
|
|
"table": str(cutoff_table_path),
|
|
"table": str(cutoff_table_path),
|
|
|
- "plot": str(cutoff_plot_path),
|
|
|
|
|
|
|
+ "plots_by_uncertainty": uncertainty_cutoff["plots_by_uncertainty"],
|
|
|
"decision_threshold": DEFAULT_DECISION_THRESHOLD,
|
|
"decision_threshold": DEFAULT_DECISION_THRESHOLD,
|
|
|
},
|
|
},
|
|
|
"uncertainty_percentile_cutoff": {
|
|
"uncertainty_percentile_cutoff": {
|
|
|
"table": str(percentile_cutoff_table_path),
|
|
"table": str(percentile_cutoff_table_path),
|
|
|
- "plot": str(percentile_cutoff_plot_path),
|
|
|
|
|
|
|
+ "plots_by_uncertainty": uncertainty_percentile_cutoff[
|
|
|
|
|
+ "plots_by_uncertainty"
|
|
|
|
|
+ ],
|
|
|
"decision_threshold": DEFAULT_DECISION_THRESHOLD,
|
|
"decision_threshold": DEFAULT_DECISION_THRESHOLD,
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
@@ -277,9 +365,9 @@ def run_physician(
|
|
|
else "std"
|
|
else "std"
|
|
|
)
|
|
)
|
|
|
secondary_label = (
|
|
secondary_label = (
|
|
|
- "Model Predictive Entropy"
|
|
|
|
|
|
|
+ "Predictive Uncertainty"
|
|
|
if secondary_key == "predictive_entropy"
|
|
if secondary_key == "predictive_entropy"
|
|
|
- else "Model Uncertainty Std"
|
|
|
|
|
|
|
+ else "Standard Deviation"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
col = physician_column(clinical_df)
|
|
col = physician_column(clinical_df)
|
|
@@ -292,7 +380,7 @@ def run_physician(
|
|
|
eval_df = pd.DataFrame(
|
|
eval_df = pd.DataFrame(
|
|
|
{
|
|
{
|
|
|
"Image Data ID": evaluation.image_ids.astype(int),
|
|
"Image Data ID": evaluation.image_ids.astype(int),
|
|
|
- "model_confidence": evaluation.uncertainty_confidence,
|
|
|
|
|
|
|
+ "model_probability": evaluation.uncertainty_confidence,
|
|
|
"model_std": evaluation.uncertainty_std,
|
|
"model_std": evaluation.uncertainty_std,
|
|
|
"model_prob": evaluation.y_prob,
|
|
"model_prob": evaluation.y_prob,
|
|
|
}
|
|
}
|
|
@@ -304,7 +392,7 @@ def run_physician(
|
|
|
|
|
|
|
|
grouped_rows: list[dict[str, Any]] = []
|
|
grouped_rows: list[dict[str, Any]] = []
|
|
|
uncertainty_specs = [
|
|
uncertainty_specs = [
|
|
|
- ("confidence", "model_confidence", "Model Confidence (2*|p-0.5|)"),
|
|
|
|
|
|
|
+ ("confidence", "model_probability", "Confidence"),
|
|
|
(secondary_key, "model_std", secondary_label),
|
|
(secondary_key, "model_std", secondary_label),
|
|
|
]
|
|
]
|
|
|
ratings = [int(r) for r in sorted(pd.unique(merged[col]))]
|
|
ratings = [int(r) for r in sorted(pd.unique(merged[col]))]
|
|
@@ -341,7 +429,7 @@ def run_physician(
|
|
|
tick_labels=[str(r) for r in ratings],
|
|
tick_labels=[str(r) for r in ratings],
|
|
|
x_label="Physician Confidence Rating (DXCONFID)",
|
|
x_label="Physician Confidence Rating (DXCONFID)",
|
|
|
y_label=metric_label,
|
|
y_label=metric_label,
|
|
|
- title=f"{metric_label} vs Physician Confidence ({evaluation.backend})",
|
|
|
|
|
|
|
+ title=f"{metric_label} by Physician Confidence Rating ({evaluation.backend})",
|
|
|
output_path=plot_path,
|
|
output_path=plot_path,
|
|
|
)
|
|
)
|
|
|
|
|
|
|
@@ -405,9 +493,9 @@ def run_longitudinal(
|
|
|
else "std"
|
|
else "std"
|
|
|
)
|
|
)
|
|
|
secondary_label = (
|
|
secondary_label = (
|
|
|
- "Mean Model Predictive Entropy"
|
|
|
|
|
|
|
+ "Mean Predictive Uncertainty"
|
|
|
if secondary_key == "predictive_entropy"
|
|
if secondary_key == "predictive_entropy"
|
|
|
- else "Mean Model Uncertainty Std"
|
|
|
|
|
|
|
+ else "Mean Standard Deviation"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
required = ["Image Data ID", "PTID"]
|
|
required = ["Image Data ID", "PTID"]
|
|
@@ -469,7 +557,7 @@ def run_longitudinal(
|
|
|
cohort = "stable_cn"
|
|
cohort = "stable_cn"
|
|
|
elif unique_dx.issubset({"AD"}):
|
|
elif unique_dx.issubset({"AD"}):
|
|
|
cohort = "stable_ad"
|
|
cohort = "stable_ad"
|
|
|
- elif first_dx == "CN" and "AD" in unique_dx and last_dx == "AD":
|
|
|
|
|
|
|
+ elif first_dx == "CN" and last_dx == "AD" and unique_dx.issubset({"CN", "AD"}):
|
|
|
cohort = "cn_to_ad"
|
|
cohort = "cn_to_ad"
|
|
|
|
|
|
|
|
patient_rows.append(
|
|
patient_rows.append(
|
|
@@ -486,6 +574,10 @@ def run_longitudinal(
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
patient_df = pd.DataFrame(patient_rows)
|
|
patient_df = pd.DataFrame(patient_rows)
|
|
|
|
|
+ patient_df = patient_df[
|
|
|
|
|
+ patient_df["cohort"].isin(["stable_cn", "stable_ad", "cn_to_ad"])
|
|
|
|
|
+ ].copy()
|
|
|
|
|
+
|
|
|
table_path = output_dir / "longitudinal_patient_summary.csv"
|
|
table_path = output_dir / "longitudinal_patient_summary.csv"
|
|
|
patient_df.to_csv(table_path, index=False)
|
|
patient_df.to_csv(table_path, index=False)
|
|
|
|
|
|
|
@@ -504,7 +596,7 @@ def run_longitudinal(
|
|
|
|
|
|
|
|
cohorts = ["stable_cn", "stable_ad", "cn_to_ad"]
|
|
cohorts = ["stable_cn", "stable_ad", "cn_to_ad"]
|
|
|
uncertainty_specs = [
|
|
uncertainty_specs = [
|
|
|
- ("confidence", "mean_confidence", "Mean Model Confidence"),
|
|
|
|
|
|
|
+ ("confidence", "mean_confidence", "Mean Confidence"),
|
|
|
(secondary_key, "mean_std", secondary_label),
|
|
(secondary_key, "mean_std", secondary_label),
|
|
|
]
|
|
]
|
|
|
plot_paths: dict[str, str] = {}
|
|
plot_paths: dict[str, str] = {}
|
|
@@ -519,9 +611,9 @@ def run_longitudinal(
|
|
|
save_boxplot(
|
|
save_boxplot(
|
|
|
data=values,
|
|
data=values,
|
|
|
tick_labels=cohorts,
|
|
tick_labels=cohorts,
|
|
|
- x_label="Cohort",
|
|
|
|
|
|
|
+ x_label="Longitudinal Cohort",
|
|
|
y_label=metric_label,
|
|
y_label=metric_label,
|
|
|
- title=f"Longitudinal Cohort {metric_label} ({evaluation.backend})",
|
|
|
|
|
|
|
+ title=f"Longitudinal Cohort Comparison: {metric_label} ({evaluation.backend})",
|
|
|
output_path=plot_path,
|
|
output_path=plot_path,
|
|
|
)
|
|
)
|
|
|
plot_paths[metric_name] = str(plot_path)
|
|
plot_paths[metric_name] = str(plot_path)
|