deepecgkit.evaluation¶
Model evaluation, metrics computation, and visualization tools.
Evaluator¶
ECGEvaluator
¶
Comprehensive evaluator for ECG models.
This class provides a unified interface for evaluating ECG models with various metrics and analysis tools.
It takes either a model together with its data, or predictions computed elsewhere together with their targets. A torch model is run on the evaluation device in eval mode and handed back on the device and in the mode it came in. Its outputs are taken to be logits, as every model in the package emits and as ECGTrainer assumes, and are turned into probabilities for a classification task: a single output becomes the two columns [1 - p, p], several outputs go through a softmax, or through a sigmoid when the targets are multi-hot. Rows that already sum to one are left alone. Predictions passed in directly are used as they are.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metrics
|
list[str] | None
|
List of metrics to compute: accuracy, precision, recall, f1, mcc and auc for classification; mse, mae, r2 and rmse for regression. Multi-hot targets are scored as calculate_classification_metrics describes, where exact_match_accuracy is also available. |
None
|
task_type
|
str
|
Type of task ("classification", "regression", "auto") |
'auto'
|
device
|
str
|
Device for model evaluation |
'auto'
|
calibration_bins
|
int
|
Number of bins for the calibration metrics reported by generate_report. ECE depends on this, so the value is emitted alongside the metrics rather than left implicit. |
DEFAULT_CALIBRATION_BINS
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If metrics names one the evaluator does not compute. |
Examples:
>>> evaluator = ECGEvaluator(metrics=["accuracy", "auc", "f1"])
>>> results = evaluator.evaluate(model, test_data)
Source code in deepecgkit/evaluation/evaluator.py
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | |
evaluate
¶
evaluate(
model: Module | ndarray | None = None,
test_data: DataLoader | ndarray | tuple | None = None,
return_predictions: bool = False,
y_scores: ndarray | None = None,
) -> dict[str, float]
Evaluate model performance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module | ndarray | None
|
PyTorch model or numpy array of predictions |
None
|
test_data
|
DataLoader | ndarray | tuple | None
|
Test data loader or (predictions, targets) tuple |
None
|
return_predictions
|
bool
|
Whether to return predictions along with metrics |
False
|
y_scores
|
ndarray | None
|
Scores/probabilities for AUC calculation (optional) |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Dictionary of metric values |
Source code in deepecgkit/evaluation/evaluator.py
cross_validate
¶
cross_validate(
model_class: type,
data: Any,
k_folds: int = 5,
groups: ndarray | None = None,
epochs: int = 10,
batch_size: int = 32,
train_config: dict[str, Any] | None = None,
seed: int = 42,
**model_kwargs: Any,
) -> dict[str, list[float]]
Perform k-fold cross-validation.
A fresh model is built and fitted for every fold and scored on the fold held out. A torch module is trained with ECGTrainer; a tenth of its training fold is set aside for the learning-rate schedule and early stopping, so the held-out fold plays no part in training. Anything else with a fit method is fitted with fit(X, y) and asked for predict_proba, or failing that predict, unless it can be called directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_class
|
type
|
Class of model to evaluate |
required |
data
|
Any
|
(X, y) tuple of arrays or tensors |
required |
k_folds
|
int
|
Number of folds |
5
|
groups
|
ndarray | None
|
Group of each sample, such as the recording or patient it comes from. Folds then never split a group, which segments cut from longer recordings need for the estimate to be honest. |
None
|
epochs
|
int
|
Maximum training epochs per fold, for a torch module |
10
|
batch_size
|
int
|
Batch size for training and scoring a torch module |
32
|
train_config
|
dict[str, Any] | None
|
ECGTrainer settings for a torch module. Multi-hot targets and a regression task_type are recognised without it; a single-output binary model needs binary_classification. |
None
|
seed
|
int
|
Seed for shuffling the folds and the training batches |
42
|
**model_kwargs
|
Any
|
Keyword arguments for model initialization |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, list[float]]
|
Dictionary of metric scores across folds |
Source code in deepecgkit/evaluation/evaluator.py
bootstrap_evaluate
¶
bootstrap_evaluate(
model: Any,
test_data: Any,
n_bootstrap: int = 1000,
confidence_level: float = 0.95,
y_scores: ndarray | None = None,
) -> dict[str, dict[str, float]]
Bootstrap evaluation for confidence intervals.
Takes the same inputs as evaluate, and computes each metric the same way, so an interval describes the value evaluate reports. Resampling draws from numpy's global generator; seed it for repeatable intervals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Any
|
PyTorch model or numpy array of predictions |
required |
test_data
|
Any
|
Test data loader or (predictions, targets) tuple |
required |
n_bootstrap
|
int
|
Number of bootstrap samples |
1000
|
confidence_level
|
float
|
Confidence level for intervals |
0.95
|
y_scores
|
ndarray | None
|
Scores/probabilities for AUC calculation (optional) |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, dict[str, float]]
|
Dictionary with mean, std, and confidence intervals for each metric |
Source code in deepecgkit/evaluation/evaluator.py
generate_report
¶
generate_report(
model: Any,
test_data: Any,
save_path: str | None = None,
y_scores: ndarray | None = None,
calibration_fit: tuple | None = None,
) -> pd.DataFrame
Generate comprehensive evaluation report.
Calibration is always reported: expected calibration error, its equal-mass variant, maximum calibration error and the Brier score. Pass calibration_fit to additionally fit a temperature and report what rescaling recovers, which adds a "temperature" row and the "_temp" counterparts of each calibration metric.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Any
|
Trained model |
required |
test_data
|
Any
|
Test dataset |
required |
save_path
|
str | None
|
Path to save report (optional) |
None
|
y_scores
|
ndarray | None
|
Scores/probabilities for AUC calculation (optional) |
None
|
calibration_fit
|
tuple | None
|
Optional (targets, logits) pair from a held-out validation split, used to fit the temperature. Never pass the split being reported on. Callers holding probabilities rather than logits can convert with deepecgkit.evaluation.probabilities_to_logits. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with evaluation results |
Source code in deepecgkit/evaluation/evaluator.py
Metrics¶
calculate_classification_metrics
¶
calculate_classification_metrics(
y_true: ndarray,
y_pred: ndarray,
metrics: list[str] | None = None,
average: str = "auto",
) -> dict[str, float]
Calculate classification metrics.
Multi-hot targets of shape (n_samples, n_labels) make the problem
multi-label, and the metrics are defined as deepecg evaluate reports
them: predictions are probabilities thresholded at 0.5 (or 0/1 labels),
"accuracy" is the share of individual labels predicted correctly, as
ECGTrainer reports it, "exact_match_accuracy" the share of samples with
every label right, and precision, recall, F1 and AUC are macro averages
over the labels. MCC is not defined for them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, or multi-hot targets |
required |
y_pred
|
ndarray
|
Predicted labels or probabilities |
required |
metrics
|
list[str] | None
|
List of metrics to compute, from accuracy, label_accuracy, exact_match_accuracy, precision, recall, f1, auc and mcc. The two longer accuracy names equal accuracy for single-label targets. |
None
|
average
|
str
|
Averaging for precision/recall/f1. "auto" selects positive-class values for two-class problems and macro averaging otherwise. Pass an explicit mode to make the choice independent of the label set. Use resolve_average to obtain the mode actually applied. |
'auto'
|
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Dictionary of computed metrics |
Source code in deepecgkit/evaluation/metrics.py
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
calculate_regression_metrics
¶
calculate_regression_metrics(
y_true: ndarray,
y_pred: ndarray,
metrics: list[str] | None = None,
) -> dict[str, float]
Calculate regression metrics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True values |
required |
y_pred
|
ndarray
|
Predicted values |
required |
metrics
|
list[str] | None
|
List of metrics to compute |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Dictionary of computed metrics |
Source code in deepecgkit/evaluation/metrics.py
confusion_matrix_analysis
¶
Detailed confusion matrix analysis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels |
required |
y_pred
|
ndarray
|
Predicted labels |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary with confusion matrix and derived metrics |
Source code in deepecgkit/evaluation/metrics.py
resolve_average
¶
Resolve the averaging mode applied to precision, recall and F1.
Precision, recall and F1 are not comparable across averaging modes, so the mode in force must be recorded alongside the values rather than inferred. Under "auto" a two-class problem yields positive-class ("binary") values, not macro-averaged ones. Multi-hot targets are macro-averaged over their labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, used only to count classes under "auto" |
required |
average
|
str
|
One of "auto", "binary", "macro", "micro", "weighted" |
'auto'
|
Returns:
| Type | Description |
|---|---|
str
|
The concrete averaging mode to pass to scikit-learn. |
Source code in deepecgkit/evaluation/metrics.py
Calibration¶
Calibration measures whether predicted probabilities match observed frequencies, and temperature scaling corrects them without changing which class is predicted.
calculate_calibration_metrics
¶
calculate_calibration_metrics(
y_true: ndarray,
y_prob: ndarray,
n_bins: int = DEFAULT_CALIBRATION_BINS,
task: str | None = None,
) -> dict[str, Any]
Calculate every calibration metric in one pass.
The bin count and resolved task shape are returned alongside the values because neither ECE nor MCE means anything without them, and a table that records only the number invites comparison between runs binned differently.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, (n_samples,) or multi-hot (n_samples, n_classes) |
required |
y_prob
|
ndarray
|
Predicted probabilities, (n_samples,) or (n_samples, n_classes) |
required |
n_bins
|
int
|
Number of bins for the binned metrics |
DEFAULT_CALIBRATION_BINS
|
task
|
str | None
|
Explicit task shape, otherwise inferred from the array shapes |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary with "ece", "adaptive_ece", "mce" and "brier", the context |
dict[str, Any]
|
keys "n_bins" and "task", and for multi-label inputs the per-class |
dict[str, Any]
|
arrays "per_class_ece", "per_class_mce" and "per_class_brier". |
Source code in deepecgkit/evaluation/metrics.py
expected_calibration_error
¶
expected_calibration_error(
y_true: ndarray,
y_prob: ndarray,
n_bins: int = DEFAULT_CALIBRATION_BINS,
task: str | None = None,
) -> float
Expected calibration error over equal-width probability bins.
The gap between confidence and accuracy is averaged over bins weighted by how many samples fall in each. ECE is sensitive to n_bins and the two numbers are not comparable across different bin counts, so n_bins must travel with any reported value. The default of 15 follows Guo et al. (2017), which keeps values comparable with the temperature-scaling literature; use adaptive_calibration_error for the companion number that does not depend on where the equal-width edges happen to fall.
Multi-label inputs are scored per class and averaged, never flattened.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, (n_samples,) or multi-hot (n_samples, n_classes) |
required |
y_prob
|
ndarray
|
Predicted probabilities, (n_samples,) or (n_samples, n_classes) |
required |
n_bins
|
int
|
Number of equal-width bins spanning [0, 1] |
DEFAULT_CALIBRATION_BINS
|
task
|
str | None
|
Explicit task shape, otherwise inferred from the array shapes |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The error in [0, 1], or NaN when no bin is populated. |
Source code in deepecgkit/evaluation/metrics.py
adaptive_calibration_error
¶
adaptive_calibration_error(
y_true: ndarray,
y_prob: ndarray,
n_bins: int = DEFAULT_CALIBRATION_BINS,
task: str | None = None,
) -> float
Expected calibration error over equal-mass probability bins.
Equal-width bins are usually near-empty at the low-confidence end and crowded at the top, so an equal-width bin holding a handful of samples estimates its gap from almost nothing. Equal-mass bins put the same number of samples in every bin, so no bin's gap rests on a tiny sample.
Report this beside the equal-width number rather than instead of it. The two are not reliably ordered and neither dominates: where they disagree, the disagreement is itself evidence that the binning is doing the work rather than the model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, (n_samples,) or multi-hot (n_samples, n_classes) |
required |
y_prob
|
ndarray
|
Predicted probabilities, (n_samples,) or (n_samples, n_classes) |
required |
n_bins
|
int
|
Number of equal-mass bins |
DEFAULT_CALIBRATION_BINS
|
task
|
str | None
|
Explicit task shape, otherwise inferred from the array shapes |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The error in [0, 1], or NaN when there are no samples. |
Source code in deepecgkit/evaluation/metrics.py
maximum_calibration_error
¶
maximum_calibration_error(
y_true: ndarray,
y_prob: ndarray,
n_bins: int = DEFAULT_CALIBRATION_BINS,
task: str | None = None,
) -> float
Largest confidence-accuracy gap across equal-width bins.
MCE reports the worst bin rather than the average one, so it answers how far the model can be trusted at its least reliable confidence level. It is noisier than ECE because a sparsely populated bin can set it, and it is never smaller than the ECE computed over the same bins. Multi-label inputs take the worst class, since averaging worst cases would understate them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, (n_samples,) or multi-hot (n_samples, n_classes) |
required |
y_prob
|
ndarray
|
Predicted probabilities, (n_samples,) or (n_samples, n_classes) |
required |
n_bins
|
int
|
Number of equal-width bins spanning [0, 1] |
DEFAULT_CALIBRATION_BINS
|
task
|
str | None
|
Explicit task shape, otherwise inferred from the array shapes |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The error in [0, 1], or NaN when no bin is populated. |
Source code in deepecgkit/evaluation/metrics.py
brier_score
¶
Mean squared error of the predicted probability vector.
Two conventions are in use and this function applies the one that is standard for each shape, so the values are not comparable between shapes. Multi-class and binary problems use the original sum-over-classes form, which ranges over [0, 2]. Multi-label problems average the per-class binary Brier score, which ranges over [0, 1]. Tables are emitted per dataset, so a single table never mixes the two, but a figure spanning datasets would.
Unlike ECE this is a proper scoring rule: it rewards sharpness as well as calibration, so a model can improve its ECE while its Brier score worsens.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, (n_samples,) or multi-hot (n_samples, n_classes) |
required |
y_prob
|
ndarray
|
Predicted probabilities, (n_samples,) or (n_samples, n_classes) |
required |
task
|
str | None
|
Explicit task shape, otherwise inferred from the array shapes |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The score, lower being better, or NaN when there are no samples. |
Source code in deepecgkit/evaluation/metrics.py
resolve_task_shape
¶
Resolve which of the three prediction shapes produced these arrays.
ECGTrainer emits exactly three shapes and the correct calibration definition differs between them, so the shape is resolved once and carried down rather than re-sniffed at each step. A two-column probability matrix is reported as "binary" whether it came from a single sigmoid logit stacked into [1 - p, p] or from a two-class softmax: the two are indistinguishable from the arrays alone and yield identical top-label statistics anyway.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, (n_samples,) for single-label problems or (n_samples, n_classes) multi-hot for multi-label ones |
required |
y_prob
|
ndarray
|
Predicted probabilities, (n_samples,) or (n_samples, n_classes) |
required |
task
|
str | None
|
Explicit override, one of "binary", "multiclass", "multilabel" |
None
|
Returns:
| Type | Description |
|---|---|
str
|
One of TASK_BINARY, TASK_MULTICLASS or TASK_MULTILABEL. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If task is supplied but is not a recognised shape. |
Source code in deepecgkit/evaluation/metrics.py
Recalibration¶
TemperatureScaler
¶
Single-parameter recalibrator fitted on one split and applied to another.
The scaler holds one scalar. Fitting it on the validation split and calling transform on the test split is the supported use; fitting and reporting on the same split measures nothing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task
|
str | None
|
Task shape, one of the TASK_ constants in deepecgkit.evaluation.metrics. Inferred from the labels at fit time when omitted, which is reliable because multi-label targets are the only two-dimensional ones. |
None
|
bounds
|
tuple[float, float]
|
Search interval for the temperature |
DEFAULT_TEMPERATURE_BOUNDS
|
Examples:
>>> scaler = TemperatureScaler().fit(val_logits, val_targets)
>>> test_probabilities = scaler.transform(test_logits)
>>> round(scaler.temperature, 3)
1.42
Source code in deepecgkit/evaluation/calibration.py
fit
¶
Fit the temperature on a held-out split.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
ndarray
|
Raw model outputs on the fitting split, which should be validation data |
required |
y_true
|
ndarray
|
True labels for that split |
required |
Returns:
| Type | Description |
|---|---|
TemperatureScaler
|
This scaler, so the call can be chained. |
Source code in deepecgkit/evaluation/calibration.py
transform
¶
Apply the fitted temperature to a different split.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
ndarray
|
Raw model outputs to recalibrate |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Probabilities in the same layout ECGTrainer stores. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If called before fit. |
Source code in deepecgkit/evaluation/calibration.py
nll
¶
Negative log-likelihood on a split under the fitted temperature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
ndarray
|
Raw model outputs |
required |
y_true
|
ndarray
|
True labels |
required |
Returns:
| Type | Description |
|---|---|
float
|
The mean negative log-likelihood in nats. |
Source code in deepecgkit/evaluation/calibration.py
fit_temperature
¶
fit_temperature(
logits: ndarray,
y_true: ndarray,
task: str | None = None,
bounds: tuple[
float, float
] = DEFAULT_TEMPERATURE_BOUNDS,
) -> float
Fit the single temperature that minimises NLL on the given split.
Pass the validation split. The objective is one-dimensional and smooth, so a bounded scalar search is used rather than a gradient optimiser: it needs no autograd graph, no learning rate and no iteration count, and it returns the same answer every run.
A fitted temperature above one means the model was overconfident and its probabilities have been softened; below one means it was underconfident.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
ndarray
|
Raw model outputs on the fitting split |
required |
y_true
|
ndarray
|
True labels for that split |
required |
task
|
str | None
|
Pass TASK_MULTILABEL for per-class sigmoid outputs |
None
|
bounds
|
tuple[float, float]
|
Search interval for the temperature |
DEFAULT_TEMPERATURE_BOUNDS
|
Returns:
| Type | Description |
|---|---|
float
|
The fitted temperature, or 1.0 if the search failed, in which case a |
float
|
warning is issued and the probabilities are left untouched. |
Source code in deepecgkit/evaluation/calibration.py
apply_temperature
¶
Divide logits by a temperature and map them back to probabilities.
The returned layout matches what ECGTrainer stores, so scaled probabilities are a drop-in replacement for unscaled ones: a single logit per sample comes back as the two columns [1 - p, p].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
ndarray
|
Raw model outputs, (n_samples,), (n_samples, 1) or (n_samples, n_classes) |
required |
temperature
|
float
|
Positive scalar; values above one soften predictions |
required |
task
|
str | None
|
Pass TASK_MULTILABEL for per-class sigmoid outputs, which cannot be told apart from multi-class logits by shape alone |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Probabilities of shape (n_samples, n_classes). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If temperature is not strictly positive. |
Source code in deepecgkit/evaluation/calibration.py
temperature_nll
¶
temperature_nll(
logits: ndarray,
y_true: ndarray,
temperature: float = NEUTRAL_TEMPERATURE,
task: str | None = None,
) -> float
Mean negative log-likelihood of the labels under a scaled model.
This is the quantity temperature scaling minimises. It is computed through logsumexp and logaddexp rather than by taking the log of a probability, so a saturated logit costs precision instead of producing an infinity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
ndarray
|
Raw model outputs |
required |
y_true
|
ndarray
|
True labels, (n_samples,) or multi-hot (n_samples, n_classes) |
required |
temperature
|
float
|
Positive scalar to divide the logits by |
NEUTRAL_TEMPERATURE
|
task
|
str | None
|
Pass TASK_MULTILABEL for per-class sigmoid outputs |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The mean negative log-likelihood in nats, lower being better. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If temperature is not strictly positive. |
Source code in deepecgkit/evaluation/calibration.py
probabilities_to_logits
¶
Recover logits from stored probabilities, for callers who kept only those.
Prefer real logits where they are available. This reconstruction is exact in principle, because softmax is shift-invariant and so log(p) is a valid representative of the logit vector, and because log(p / (1 - p)) inverts the sigmoid outright. It is the float32 storage that costs accuracy: a probability rounds to exactly 1.0 once the logit reaches about 16.6, and clipping at 1e-7 caps recoverable magnitudes at about 16.1. Measured over 4000 samples, temperature-scaled probabilities rebuilt this way match those from true logits to about 4e-8 for softmax and 5e-5 for sigmoid at ordinary confidence, but drift to roughly 5e-2 once a fifth of the rows saturate. Confidently wrong models are exactly the ones a calibration study cares about, so reconstruction is a fallback and not the default path.
The probabilities written to predictions.txt carry six decimals, which caps recoverable magnitudes at about 13.8.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_prob
|
ndarray
|
Probabilities of shape (n_samples,) or (n_samples, n_classes) |
required |
task
|
str | None
|
Pass TASK_MULTILABEL for per-class sigmoid outputs. Otherwise a one- or two-column input is read as a single logistic decision and anything wider as a softmax over classes. |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Logits, shaped (n_samples,) for logistic inputs and |
ndarray
|
(n_samples, n_classes) for softmax and multi-label inputs. |
Source code in deepecgkit/evaluation/calibration.py
calibration_report
¶
calibration_report(
y_true: ndarray,
y_prob: ndarray,
n_bins: int = DEFAULT_CALIBRATION_BINS,
fit_targets: ndarray | None = None,
fit_logits: ndarray | None = None,
eval_logits: ndarray | None = None,
) -> dict[str, Any]
Calibration before and after temperature scaling, as one flat mapping.
This is the shape the paper table wants: one row per run holding both the uncalibrated numbers and what temperature scaling recovers. The scaled entries are NaN unless a fitting split is supplied, so a run that had no validation data still produces a well-formed row.
Pass the test split as y_true and y_prob and the validation split as fit_targets and fit_logits. The temperature is never fitted on the split it is reported on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels for the split being reported on |
required |
y_prob
|
ndarray
|
Predicted probabilities for that split |
required |
n_bins
|
int
|
Number of bins for the binned metrics |
DEFAULT_CALIBRATION_BINS
|
fit_targets
|
ndarray | None
|
True labels for the validation split |
None
|
fit_logits
|
ndarray | None
|
Raw model outputs for the validation split |
None
|
eval_logits
|
ndarray | None
|
Raw model outputs for the reported split. Reconstructed from y_prob when omitted, which costs accuracy on saturated predictions; see probabilities_to_logits. |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Mapping with "ece", "adaptive_ece", "mce", "brier", their "_temp" |
dict[str, Any]
|
counterparts measured after scaling, the fitted "temperature", and |
dict[str, Any]
|
"calibration_bins" recording what produced the binned values. |
Source code in deepecgkit/evaluation/calibration.py
Representation quality¶
linear_probe
¶
linear_probe(
train: FeatureSet | tuple[Any, Any],
test: FeatureSet | tuple[Any, Any],
metric: str = DEFAULT_PROBE_METRIC,
model_name: str | None = None,
source: str | None = None,
target: str | None = None,
**probe_kwargs: Any,
) -> ProbeResult
Fit a standardised logistic probe on training embeddings and score it on held-out ones.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
train
|
FeatureSet | tuple[Any, Any]
|
Embeddings and labels the probe is fitted on |
required |
test
|
FeatureSet | tuple[Any, Any]
|
Embeddings and labels the probe is scored on, never seen by the fit |
required |
metric
|
str
|
Headline metric, one of PROBE_METRICS |
DEFAULT_PROBE_METRIC
|
model_name
|
str | None
|
Registry name of the encoder, recorded on the result |
None
|
source
|
str | None
|
Dataset the encoder was trained on, recorded on the result |
None
|
target
|
str | None
|
Dataset being probed, recorded on the result |
None
|
**probe_kwargs
|
Any
|
Forwarded to build_probe |
{}
|
Returns:
| Type | Description |
|---|---|
ProbeResult
|
The probe's scores. A target whose training split holds a single class |
ProbeResult
|
cannot be fitted at all and comes back with NaN metrics and a note |
ProbeResult
|
rather than an exception, so one degenerate cell does not abort a |
ProbeResult
|
transfer matrix. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the metric is unknown, the two splits disagree on embedding width, or either split carries no labels. |
Source code in deepecgkit/evaluation/representation.py
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | |
probe_encoder
¶
probe_encoder(
extractor: FeatureExtractor,
data_module: ECGDataModule,
train_split: str = "train",
test_split: str = "test",
metric: str = DEFAULT_PROBE_METRIC,
source: str | None = None,
target: str | None = None,
**probe_kwargs: Any,
) -> ProbeResult
Embed two splits of one data module with a frozen encoder and probe them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
extractor
|
FeatureExtractor
|
A frozen encoder; nothing here updates its weights |
required |
data_module
|
ECGDataModule
|
The data module supplying both splits |
required |
train_split
|
str
|
Split the probe is fitted on |
'train'
|
test_split
|
str
|
Split the probe is scored on |
'test'
|
metric
|
str
|
Headline metric, one of PROBE_METRICS |
DEFAULT_PROBE_METRIC
|
source
|
str | None
|
Dataset the encoder was trained on, recorded on the result |
None
|
target
|
str | None
|
Dataset being probed, recorded on the result |
None
|
**probe_kwargs
|
Any
|
Forwarded to build_probe |
{}
|
Returns:
| Type | Description |
|---|---|
ProbeResult
|
The probe's scores on the held-out split. |
Source code in deepecgkit/evaluation/representation.py
build_probe
¶
build_probe(
c: float = 1.0,
max_iter: int = 1000,
random_state: int = 42,
multilabel: bool = False,
) -> Pipeline
Build the protocol's probe: standardisation followed by logistic regression.
The scaler is a pipeline step rather than a preprocessing call so that it is fitted on the training split alone and merely applied to the held-out split. Standardising the two together, or standardising the pooled features once before splitting, leaks the test distribution into the fit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
c
|
float
|
Inverse regularisation strength passed to LogisticRegression |
1.0
|
max_iter
|
int
|
Solver iteration ceiling |
1000
|
random_state
|
int
|
Seed for the solver |
42
|
multilabel
|
bool
|
Wrap the estimator in OneVsRestClassifier, for targets whose labels are a multi-hot matrix rather than a class index |
False
|
Returns:
| Type | Description |
|---|---|
Pipeline
|
An unfitted pipeline of a StandardScaler and a logistic regression. |
Source code in deepecgkit/evaluation/representation.py
cross_dataset_transfer
¶
cross_dataset_transfer(
checkpoints: dict[str, str | Path],
model_name: str,
targets: Sequence[str] | None = None,
input_channels: int = PROBE_INPUT_CHANNELS,
metric: str = DEFAULT_PROBE_METRIC,
include_random_init: bool = True,
data_modules: dict[str, ECGDataModule] | None = None,
sampling_rate: int = PROBE_SAMPLING_RATE,
data_dir: str | None = None,
download: bool = True,
force_download: bool = False,
batch_size: int = 32,
num_workers: int = 0,
device: str = "auto",
val_split: float = 0.2,
test_split: float = 0.1,
seed: int = 42,
progress: bool = False,
verbose: bool = False,
keep_embeddings: str = KEEP_NONE,
**probe_kwargs: Any,
) -> TransferMatrix
Probe every source encoder on every target dataset.
Each cell freezes the encoder trained on its source, embeds the target's training and held-out splits, fits a standardised logistic head on the training embeddings and reports on the held-out ones. The head is refitted per target because the label spaces differ and no head can be carried across them; what transfers, or fails to, is the representation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
checkpoints
|
dict[str, str | Path]
|
Source dataset name mapped to the checkpoint of the encoder trained on it |
required |
model_name
|
str
|
Registered model name matching every checkpoint |
required |
targets
|
Sequence[str] | None
|
Datasets to probe, defaulting to the supplied data modules or else to PROBE_DATASETS |
None
|
input_channels
|
int
|
Lead count the protocol runs at |
PROBE_INPUT_CHANNELS
|
metric
|
str
|
Headline metric filling the matrix, one of PROBE_METRICS |
DEFAULT_PROBE_METRIC
|
include_random_init
|
bool
|
Prepend the untrained-encoder floor as the first row |
True
|
data_modules
|
dict[str, ECGDataModule] | None
|
Pre-built modules keyed by target name, bypassing dataset construction entirely |
None
|
sampling_rate
|
int
|
Target rate for every dataset, in Hz |
PROBE_SAMPLING_RATE
|
data_dir
|
str | None
|
Directory holding the datasets, or None for their defaults |
None
|
download
|
bool
|
Fetch a dataset when it is absent |
True
|
force_download
|
bool
|
Re-download existing datasets |
False
|
batch_size
|
int
|
Batch size for embedding extraction |
32
|
num_workers
|
int
|
Worker processes for the loaders |
0
|
device
|
str
|
Device name, or auto to prefer CUDA then MPS then CPU |
'auto'
|
val_split
|
float
|
Fraction held out for validation |
0.2
|
test_split
|
float
|
Fraction the probe reports on |
0.1
|
seed
|
int
|
Seed for the splits and for the random-init encoder |
42
|
progress
|
bool
|
Show a progress bar during extraction |
False
|
verbose
|
bool
|
Print dataset construction progress |
False
|
keep_embeddings
|
str
|
Which cells keep their held-out embeddings for later plotting: none, all, or diagonal for the in-domain cells together with the random-init row, which is the pairing worth looking at |
KEEP_NONE
|
**probe_kwargs
|
Any
|
Forwarded to build_probe |
{}
|
Returns:
| Type | Description |
|---|---|
TransferMatrix
|
The filled transfer matrix. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no sources are given, or a name is not a registered dataset, or the metric is unknown. |
Source code in deepecgkit/evaluation/representation.py
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 | |
TransferMatrix
dataclass
¶
Probe performance for every (encoder source, probe target) pair.
Rows are the datasets the encoders were trained on, columns are the datasets the probes were fitted and scored on. The diagonal is the in-domain probe. When the random-init floor is included it is the first row, and it is the floor for every target column; transpose to_frame() for a figure that wants sources along the top instead.
Attributes:
| Name | Type | Description |
|---|---|---|
sources |
list[str]
|
Row labels, RANDOM_INIT first when the baseline is present |
targets |
list[str]
|
Column labels |
values |
ndarray
|
Headline metric per cell, of shape (len(sources), len(targets)) |
metric |
str
|
Name of the headline metric |
results |
dict[str, ProbeResult]
|
Full ProbeResult per cell, keyed "source->target" |
model_name |
str | None
|
Registry name of the probed architecture |
protocol |
dict[str, Any]
|
The resolved protocol settings, recorded for provenance |
embeddings |
dict[str, FeatureSet]
|
Held-out embeddings retained for the cells requested by keep_embeddings, keyed "source->target", for plotting. Empty unless they were asked for, since keeping all of them costs memory proportional to the corpus |
class_names |
dict[str, list[str]]
|
Class names per target, for labelling an embedding plot |
Source code in deepecgkit/evaluation/representation.py
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 | |
to_frame
¶
Render the headline metric as a labelled table.
Returns:
| Type | Description |
|---|---|
DataFrame
|
A frame indexed by source with one column per target. |
Source code in deepecgkit/evaluation/representation.py
embedding_for
¶
Look up the retained held-out embeddings behind one cell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
Row label |
required |
target
|
str
|
Column label |
required |
Returns:
| Type | Description |
|---|---|
FeatureSet | None
|
The cell's embeddings, or None when they were not retained. |
Source code in deepecgkit/evaluation/representation.py
result_for
¶
Look up the full result behind one cell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
Row label |
required |
target
|
str
|
Column label |
required |
Returns:
| Type | Description |
|---|---|
ProbeResult | None
|
The cell's ProbeResult, or None when the pair was not run. |
Source code in deepecgkit/evaluation/representation.py
to_dict
¶
Render the whole matrix as JSON-serialisable primitives.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dictionary holding the headline table, every cell's full metrics, |
dict[str, Any]
|
and the protocol settings the run used. |
Source code in deepecgkit/evaluation/representation.py
save
¶
Write the matrix to disk, choosing the format from the suffix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Destination ending in .json or .csv. The .json form keeps every metric of every cell and the protocol settings; .csv holds the headline table alone. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the suffix is neither of the two supported forms. |
Source code in deepecgkit/evaluation/representation.py
ProbeResult
dataclass
¶
The outcome of fitting one linear probe on one held-out split.
Attributes:
| Name | Type | Description |
|---|---|---|
metrics |
dict[str, float]
|
Every metric in PROBE_METRICS, with NaN where a metric was not defined for this label set |
metric |
str
|
Name of the headline metric |
score |
float
|
Value of the headline metric, a convenience view on metrics |
task |
str | None
|
Resolved label shape, one of binary, multiclass or multilabel |
n_train |
int
|
Samples the probe was fitted on |
n_test |
int
|
Samples the probe was scored on |
feature_dim |
int
|
Width of the embedding the probe consumed |
model_name |
str | None
|
Registry name of the encoder that produced the embeddings |
source |
str | None
|
Dataset the encoder was trained on, or RANDOM_INIT |
target |
str | None
|
Dataset the probe was fitted and scored on |
note |
str | None
|
Why a metric is missing, when one is |
Source code in deepecgkit/evaluation/representation.py
to_dict
¶
Render the result as JSON-serialisable primitives.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dictionary whose float values are plain floats, so NaN survives |
dict[str, Any]
|
json.dumps as the non-standard but universally parsed NaN literal. |
Source code in deepecgkit/evaluation/representation.py
protocol_data_module
¶
protocol_data_module(
dataset_name: str,
data_dir: str | None = None,
sampling_rate: int = PROBE_SAMPLING_RATE,
batch_size: int = 32,
num_workers: int = 0,
val_split: float = 0.2,
test_split: float = 0.1,
seed: int = 42,
download: bool = True,
force_download: bool = False,
verbose: bool = False,
) -> ECGDataModule
Build one dataset in the single-lead configuration the protocol requires.
Transfer only means anything if every encoder sees the same input shape, so the protocol runs every dataset at one lead and one sampling rate. The three AF corpora already yield a single lead at their constructor defaults; PTB-XL is pinned to lead II and resampled to match, which its standardiser handles.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset_name
|
str
|
A registered dataset name |
required |
data_dir
|
str | None
|
Directory holding the dataset, or None for its default |
None
|
sampling_rate
|
int
|
Target rate for every dataset, in Hz |
PROBE_SAMPLING_RATE
|
batch_size
|
int
|
Batch size for the loaders |
32
|
num_workers
|
int
|
Worker processes for the loaders |
0
|
val_split
|
float
|
Fraction held out for validation |
0.2
|
test_split
|
float
|
Fraction held out for the probe's report |
0.1
|
seed
|
int
|
Split seed |
42
|
download
|
bool
|
Fetch the dataset when it is absent |
True
|
force_download
|
bool
|
Re-download an existing dataset |
False
|
verbose
|
bool
|
Print dataset construction progress |
False
|
Returns:
| Type | Description |
|---|---|
ECGDataModule
|
A data module that has not yet been set up. |
Source code in deepecgkit/evaluation/representation.py
embed_2d
¶
embed_2d(
features: Any,
method: str = "tsne",
perplexity: float = DEFAULT_PERPLEXITY,
pca_components: int | None = DEFAULT_PCA_COMPONENTS,
random_state: int = 42,
) -> np.ndarray
Project embeddings to two dimensions for plotting.
Features are standardised first, for the same reason the probe standardises them: a handful of wide-range dimensions would otherwise dominate the distances t-SNE is built from. Wide embeddings are then reduced by PCA before t-SNE, which is both the conventional recipe and much faster.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
features
|
Any
|
Embeddings of shape (n_samples, feature_dim), or a FeatureSet |
required |
method
|
str
|
One of tsne or pca |
'tsne'
|
perplexity
|
float
|
t-SNE perplexity, clamped to a value the sample count supports rather than letting scikit-learn reject it |
DEFAULT_PERPLEXITY
|
pca_components
|
int | None
|
Width to reduce to before t-SNE, or None to skip |
DEFAULT_PCA_COMPONENTS
|
random_state
|
int
|
Seed, making the projection reproducible |
42
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Coordinates of shape (n_samples, 2). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the method is unknown, the input is not two-dimensional, or there are too few samples to project. |
Source code in deepecgkit/evaluation/representation.py
Visualization¶
plot_confusion_matrix
¶
plot_confusion_matrix(
y_true: ndarray,
y_pred: ndarray,
class_names: list[str] | None = None,
title: str = "Confusion Matrix",
save_path: str | None = None,
) -> None
Plot confusion matrix.
Source code in deepecgkit/evaluation/visualization.py
plot_roc_curve
¶
plot_roc_curve(
y_true: ndarray,
y_scores: ndarray,
title: str = "ROC Curve",
save_path: str | None = None,
) -> None
Plot the ROC curve of a two-class problem.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels, two classes |
required |
y_scores
|
ndarray
|
Scores of the positive class, or the two-column probability matrix that ECGTrainer.get_test_results returns for a binary model, whose second column is used |
required |
title
|
str
|
Figure title |
'ROC Curve'
|
save_path
|
str | None
|
Where to save the figure; None shows it instead |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If y_scores holds a column per class for more than two classes. |
Source code in deepecgkit/evaluation/visualization.py
plot_training_curves
¶
plot_training_curves(
metrics_path: str,
save_dir: str | None = None,
save_path: str | None = None,
) -> None
Plot training and validation loss and accuracy as separate figures.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metrics_path
|
str
|
Path to CSVLogger metrics.csv file. |
required |
save_dir
|
str | None
|
Directory to save separate loss.png and accuracy.png files. |
None
|
save_path
|
str | None
|
Deprecated combined path; if provided and save_dir is not, saves loss plot to this path for backward compatibility. |
None
|
Source code in deepecgkit/evaluation/visualization.py
plot_ecg_signals
¶
plot_ecg_signals(
data: ndarray,
sampling_rate: float = 500.0,
leads: list[str] | None = None,
title: str = "ECG Signals",
save_path: str | None = None,
unit: str | None = None,
) -> None
Plot one ECG recording, one row per lead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
ndarray
|
A single lead as a one-dimensional array, or several leads as a two-dimensional one. Both layouts are accepted for the latter, (leads, time) as the datasets and models use and (time, leads); the longer axis is taken to be time. |
required |
sampling_rate
|
float
|
Sampling rate of the recording in Hz, for the time axis |
500.0
|
leads
|
list[str] | None
|
Name of each lead, in order |
None
|
title
|
str
|
Figure title |
'ECG Signals'
|
save_path
|
str | None
|
Where to save the figure; None shows it instead |
None
|
unit
|
str | None
|
Unit of the amplitude axis, such as "mV" for a raw recording. The datasets return normalised signals, which have none, so none is shown by default. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If data has more than two dimensions, or leads does not name every lead. |
Source code in deepecgkit/evaluation/visualization.py
plot_predictions
¶
plot_predictions(
y_true: ndarray,
y_pred: ndarray,
title: str = "Predictions vs True Values",
save_path: str | None = None,
) -> None
Plot predictions vs true values.
Source code in deepecgkit/evaluation/visualization.py
plot_calibration_curve
¶
plot_calibration_curve(
y_true: ndarray,
y_prob: ndarray,
class_names: list[str] | None = None,
n_bins: int = 10,
title: str = "Calibration Plot",
save_dir: str | None = None,
save_path: str | None = None,
) -> None
Plot reliability diagram and prediction distribution as separate figures.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels. |
required |
y_prob
|
ndarray
|
Predicted probabilities (n_samples, n_classes). |
required |
class_names
|
list[str] | None
|
Optional list of class names. |
None
|
n_bins
|
int
|
Number of bins for calibration curve. |
10
|
title
|
str
|
Title for the calibration curve plot. |
'Calibration Plot'
|
save_dir
|
str | None
|
Directory to save calibration_curve.png and prediction_distribution.png. |
None
|
save_path
|
str | None
|
Deprecated combined path; if provided and save_dir is not, uses its parent directory for backward compatibility. |
None
|
Source code in deepecgkit/evaluation/visualization.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | |
plot_transfer_matrix
¶
plot_transfer_matrix(
values: ndarray,
sources: list[str],
targets: list[str],
metric: str = "auroc_macro",
baseline_rows: int = 0,
title: str = "Cross-Dataset Transfer",
save_path: str | None = None,
) -> None
Plot probe performance for every encoder source against every probe target.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
ndarray
|
Probe scores of shape (len(sources), len(targets)). Cells that could not be scored are NaN and render blank. |
required |
sources
|
list[str]
|
Row labels, the datasets the frozen encoders were trained on. |
required |
targets
|
list[str]
|
Column labels, the datasets the probes were fitted and scored on. |
required |
metric
|
str
|
Name of the plotted metric, shown on the colour bar. |
'auroc_macro'
|
baseline_rows
|
int
|
Number of leading rows holding a baseline rather than a trained encoder, separated from the rest by a rule. |
0
|
title
|
str
|
Title for the plot. |
'Cross-Dataset Transfer'
|
save_path
|
str | None
|
Path to save the figure to. |
None
|
Source code in deepecgkit/evaluation/visualization.py
plot_embedding
¶
plot_embedding(
embedding: ndarray,
labels: ndarray | None = None,
class_names: list[str] | None = None,
title: str = "Embedding",
save_path: str | None = None,
) -> None
Scatter a two-dimensional embedding, coloured by class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
embedding
|
ndarray
|
Coordinates of shape (n_samples, 2), as returned by deepecgkit.evaluation.representation.embed_2d. |
required |
labels
|
ndarray | None
|
Class indices of shape (n_samples,), or a multi-hot matrix of shape (n_samples, n_classes) in which case each point is coloured by its first positive label. |
None
|
class_names
|
list[str] | None
|
Names for the classes, defaulting to their indices. |
None
|
title
|
str
|
Title for the plot. |
'Embedding'
|
save_path
|
str | None
|
Path to save the figure to. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the coordinates are not two-dimensional, or the labels do not align with them. |