From a67c084891500d049a36a72af121342bad378a59 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 22 Sep 2021 18:40:25 -0400 Subject: [PATCH] Backport PR #21153: Improve curve_error_band example. --- .../curve_error_band.py | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/examples/lines_bars_and_markers/curve_error_band.py b/examples/lines_bars_and_markers/curve_error_band.py index 56d95977a54c..e55b467849a8 100644 --- a/examples/lines_bars_and_markers/curve_error_band.py +++ b/examples/lines_bars_and_markers/curve_error_band.py @@ -10,7 +10,6 @@ # sphinx_gallery_thumbnail_number = 2 import numpy as np -from scipy.interpolate import splprep, splev import matplotlib.pyplot as plt from matplotlib.path import Path @@ -22,8 +21,8 @@ x, y = r * np.cos(t), r * np.sin(t) fig, ax = plt.subplots() -ax.plot(x, y) -plt.show() +ax.plot(x, y, "k") +ax.set(aspect=1) ############################################################################# # An error band can be used to indicate the uncertainty of the curve. @@ -39,34 +38,43 @@ # `~.Axes.fill_between` method (see also # :doc:`/gallery/lines_bars_and_markers/fill_between_demo`). -# Error amplitudes depending on the curve parameter *t* -# (actual values are arbitrary and only for illustrative purposes): -err = 0.05 * np.sin(2 * t) ** 2 + 0.04 + 0.02 * np.cos(9 * t + 2) -# calculate normals via derivatives of splines -tck, u = splprep([x, y], s=0) -dx, dy = splev(u, tck, der=1) -l = np.hypot(dx, dy) -nx = dy / l -ny = -dx / l +def draw_error_band(ax, x, y, err, **kwargs): + # Calculate normals via centered finite differences (except the first point + # which uses a forward difference and the last point which uses a backward + # difference). + dx = np.concatenate([[x[1] - x[0]], x[2:] - x[:-2], [x[-1] - x[-2]]]) + dy = np.concatenate([[y[1] - y[0]], y[2:] - y[:-2], [y[-1] - y[-2]]]) + l = np.hypot(dx, dy) + nx = dy / l + ny = -dx / l -# end points of errors -xp = x + nx * err -yp = y + ny * err -xn = x - nx * err -yn = y - ny * err + # end points of errors + xp = x + nx * err + yp = y + ny * err + xn = x - nx * err + yn = y - ny * err -vertices = np.block([[xp, xn[::-1]], - [yp, yn[::-1]]]).T -codes = Path.LINETO * np.ones(len(vertices), dtype=Path.code_type) -codes[0] = codes[len(xp)] = Path.MOVETO -path = Path(vertices, codes) + vertices = np.block([[xp, xn[::-1]], + [yp, yn[::-1]]]).T + codes = np.full(len(vertices), Path.LINETO) + codes[0] = codes[len(xp)] = Path.MOVETO + path = Path(vertices, codes) + ax.add_patch(PathPatch(path, **kwargs)) -patch = PathPatch(path, facecolor='C0', edgecolor='none', alpha=0.3) -fig, ax = plt.subplots() -ax.plot(x, y) -ax.add_patch(patch) +axs = (plt.figure(constrained_layout=True) + .subplots(1, 2, sharex=True, sharey=True)) +errs = [ + (axs[0], "constant error", 0.05), + (axs[1], "variable error", 0.05 * np.sin(2 * t) ** 2 + 0.04), +] +for i, (ax, title, err) in enumerate(errs): + ax.set(title=title, aspect=1, xticks=[], yticks=[]) + ax.plot(x, y, "k") + draw_error_band(ax, x, y, err=err, + facecolor=f"C{i}", edgecolor="none", alpha=.3) + plt.show() #############################################################################