Tumor-response waterfall chart
Oncology — synthetic data
Example from the compendium of canonical charts
Python Code
"""Oncology — Tumor-response waterfall chart (synthetic data)."""
import numpy as np
import plotly.graph_objects as go
# Response category thresholds (RECIST-like)
PR_THRESHOLD = -30 # partial response: < -30%
PD_THRESHOLD = +20 # progressive disease: > +20%
# Colors
COLOR_RESPONDER = GREEN # partial/complete response
COLOR_STABLE = TEAL # stable disease
COLOR_PROGRESSOR = "#D94F4F" # progressive disease (red)
def generate():
rng = np.random.default_rng(2024)
n = 60
# 30% responders, 50% stable, 20% progressors
n_resp = round(0.30 * n)
n_stable = round(0.50 * n)
n_prog = n - n_resp - n_stable
changes = np.concatenate([
rng.uniform(-80, -30, n_resp), # responders
rng.uniform(-30, +20, n_stable), # stable
rng.uniform(+20, +60, n_prog), # progressors
])
# Sort best to worst (ascending: most negative first)
order = np.argsort(changes)
changes = changes[order]
patient_ids = [f"P{i+1:03d}" for i in range(n)]
# Assign colors
colors = []
for c in changes:
if c < PR_THRESHOLD:
colors.append(COLOR_RESPONDER)
elif c > PD_THRESHOLD:
colors.append(COLOR_PROGRESSOR)
else:
colors.append(COLOR_STABLE)
fig = go.Figure([
go.Bar(
x=patient_ids,
y=changes,
marker_color=colors,
hovertemplate="%{x}<br>%{y:.1f}%<extra></extra>",
)
])
# Threshold lines
x_range = [-0.5, n - 0.5]
for thresh, label, dash in [
(PR_THRESHOLD, "−30% (PR)", "dash"),
(PD_THRESHOLD, "+20% (PD)", "dot"),
]:
fig.add_shape(
type="line",
x0=x_range[0], x1=x_range[1],
y0=thresh, y1=thresh,
line=dict(color="black", width=1.2, dash=dash),
)
fig.add_annotation(
x=n - 1,
y=thresh,
text=label,
showarrow=False,
xanchor="right",
yanchor="bottom",
font=dict(size=10),
yshift=3,
)
fig.update_layout(
xaxis=dict(
title="Patient (sorted by best response)",
showticklabels=False,
tickangle=0,
),
yaxis=dict(
title="Best % Change in Tumor Size",
ticksuffix="%",
zeroline=True,
zerolinewidth=1,
zerolinecolor="black",
),
showlegend=False,
margin=dict(t=40, b=60, l=80, r=40),
height=500,
bargap=0.05,
)
return fig
if __name__ == "__main__":
generate()
Made with Plotly