base = np.array([20, 22, 21, 19, 20, 23, 22, 21], dtype=float)
outliers = np.linspace(25, 140, 10)
if go is not None:
frames = []
for i, o in enumerate(outliers):
x_all = np.r_[base, o]
mean_val = x_all.mean()
median_val = np.median(x_all)
frames.append(go.Frame(
name=f"step{i}",
data=[
go.Scatter(x=base, y=np.zeros_like(base), mode="markers", name="base waits"),
go.Scatter(x=[o], y=[0], mode="markers", name="moving outlier"),
],
layout=go.Layout(
title_text=f"outlier={o:.0f}; mean={mean_val:.1f}; median={median_val:.1f}",
shapes=[
dict(type="line", x0=mean_val, x1=mean_val, y0=-0.35, y1=0.35, line=dict(dash="dash")),
dict(type="line", x0=median_val, x1=median_val, y0=-0.35, y1=0.35, line=dict(dash="dot")),
],
),
))
o0 = outliers[0]
x0 = np.r_[base, o0]
fig = go.Figure(data=frames[0].data, frames=frames)
fig.update_layout(
title_text=f"outlier={o0:.0f}; mean={x0.mean():.1f}; median={np.median(x0):.1f}",
xaxis_title="wait time", yaxis=dict(showticklabels=False, range=[-1, 1]),
xaxis=dict(range=[15, 150]), showlegend=False,
shapes=frames[0].layout.shapes,
updatemenus=[dict(type="buttons", buttons=[
dict(label="Play", method="animate", args=[None, dict(frame=dict(duration=350, redraw=True), fromcurrent=True)]),
dict(label="Pause", method="animate", args=[[None], dict(frame=dict(duration=0, redraw=False), mode="immediate")]),
])],
sliders=[dict(steps=[dict(label=f"{o:.0f}", method="animate", args=[[f"step{i}"], dict(frame=dict(duration=0, redraw=True), mode="immediate")]) for i, o in enumerate(outliers)])],
)
fig.show()
else:
fig, ax = plt.subplots(figsize=(7, 2.2))
for o in [25, 140]:
x_all = np.r_[base, o]
ax.scatter(x_all, np.full_like(x_all, o), s=18)
ax.axvline(x_all.mean(), linestyle="--", linewidth=1)
ax.axvline(np.median(x_all), linestyle=":", linewidth=1)
ax.set_xlabel("wait time")
ax.set_yticks([25, 140], labels=["small outlier", "large outlier"])
ax.set_title("Dashed = mean; dotted = median")
plt.show()