Classification and survival¶
Most of the classification metrics come from scikit-learn and the survival estimate from lifelines, and depictr redraws both under the shared theme. The survival figure adds a number-at-risk table beneath the curves in one call.
import numpy as np
import depictr as dp
ct = dp.clinical_trial()
score = 1 / (1 + np.exp(-ct["biomarker"])) # a probability-like score
ROC curve¶
An ROC curve with the area under the curve.
p = dp.roc_curve_plot(ct["adverse_event"], score)
print(show(p))
Precision-recall on a rare outcome¶
When the positive class is rare the precision-recall curve is more informative than the ROC curve, because it ignores the many true negatives.
p = dp.pr_curve_plot(ct["adverse_event"], score)
print(show(p))
Gains, lift and choosing a threshold¶
The cumulative gains and lift charts read off how many positives a ranked campaign captures at each depth.
p = dp.gain_plot(ct["adverse_event"], score)
print(show(p))
p = dp.lift_plot(ct["adverse_event"], score)
print(show(p))
The threshold plot sweeps the decision cut-off, so the trade-off between the metrics as the operating point moves can be read straight off the curves.
p = dp.threshold_plot(ct["adverse_event"], score)
print(show(p))
Calibration¶
A calibration curve asks whether a predicted probability of, say, 0.3 is borne out by an event in about 30% of such cases, so it needs probabilities from a fitted model rather than the ranking score the charts above are happy with. We fit a logistic regression and pass its predicted probabilities.
from sklearn.linear_model import LogisticRegression
X = ct[["biomarker", "age"]]
fit = LogisticRegression().fit(X, ct["adverse_event"])
p = dp.calibration_plot(ct["adverse_event"], fit.predict_proba(X)[:, 1],
n_bins=5)
print(show(p))
Each of the five bins holds 60 patients, and each point is sized by its count.
The mean predicted risks run from 0.03 to 0.24 and the observed rates from 0.02
to 0.28. Every 95% Wilson interval includes its bin's predicted risk, so the
curve is consistent with a well-calibrated model. Equal-width bins
(strategy="uniform") would put 263 patients in the lowest bin and leave the
highest with four, none of whom had an event.
Confusion matrix¶
A confusion-matrix heatmap, normalised by the true class.
p = dp.confusion_matrix_plot(ct["adverse_event"], (score > 0.6).astype(int),
normalise="true")
print(show(p, width=6))
Survival¶
Kaplan-Meier curves with a log-rank test and a number-at-risk table. The dotted steps are 95% confidence limits, built on the log-log scale as in the R package, and each + marks a censored patient. The legend sits in the empty top-right the descending curves leave behind.
p = dp.survival_plot(ct["time"], ct["event"], group=ct["arm"],
risk_table=True, legend_inside=True, logrank=True,
title="Survival by arm")
print(show(p, width=8, height=6))