diff --git a/examples/lines_bars_and_markers/markevery_demo.py b/examples/lines_bars_and_markers/markevery_demo.py index caf12c9563c7..8dc02f52e28a 100644 --- a/examples/lines_bars_and_markers/markevery_demo.py +++ b/examples/lines_bars_and_markers/markevery_demo.py @@ -3,99 +3,96 @@ Markevery Demo ============== -This example demonstrates the various options for showing a marker at a -subset of data points using the ``markevery`` property of a Line2D object. - -Integer arguments are fairly intuitive. e.g. ``markevery=5`` will plot every -5th marker starting from the first data point. - -Float arguments allow markers to be spaced at approximately equal distances -along the line. The theoretical distance along the line between markers is -determined by multiplying the display-coordinate distance of the axes -bounding-box diagonal by the value of ``markevery``. The data points closest -to the theoretical distances will be shown. - -A slice or list/array can also be used with ``markevery`` to specify the -markers to show. +The ``markevery`` property of `.Line2D` allows drawing markers at a subset of +data points. + +The list of possible parameters is specified at `.Line2D.set_markevery`. +In short: + +- A single integer N draws every N-th marker. +- A tuple of integers (start, N) draws every N-th marker, starting at data + index *start*. +- A list of integers draws the markers at the specified indices. +- A slice draws the markers at the sliced indices. +- A float specifies the distance between markers as a fraction of the Axes + diagonal in screen space. This will lead to a visually uniform distribution + of the points along the line, irrespective of scales and zooming. """ import numpy as np import matplotlib.pyplot as plt # define a list of markevery cases to plot -cases = [None, - 8, - (30, 8), - [16, 24, 30], [0, -1], - slice(100, 200, 3), - 0.1, 0.3, 1.5, - (0.0, 0.1), (0.45, 0.1)] - -# define the figure size and grid layout properties -figsize = (10, 8) -cols = 3 -rows = len(cases) // cols + 1 -# define the data for cartesian plots +cases = [ + None, + 8, + (30, 8), + [16, 24, 32], + [0, -1], + slice(100, 200, 3), + 0.1, + 0.4, + (0.2, 0.4) +] + +# data points delta = 0.11 x = np.linspace(0, 10 - 2 * delta, 200) + delta y = np.sin(x) + 1.0 + delta - -def trim_axs(axs, N): - """ - Reduce *axs* to *N* Axes. All further Axes are removed from the figure. - """ - axs = axs.flat - for ax in axs[N:]: - ax.remove() - return axs[:N] - ############################################################################### -# Plot each markevery case for linear x and y scales +# markevery with linear scales +# ---------------------------- -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots(rows, cols) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) - ax.plot(x, y, 'o', ls='-', ms=4, markevery=case) +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') + ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery) ############################################################################### -# Plot each markevery case for log x and y scales - -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots(rows, cols) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) +# markevery with log scales +# ------------------------- +# +# Note that the log scale causes a visual asymmetry in the marker distance for +# when subsampling the data using an integer. In contrast, subsampling on +# fraction of figure size creates even distributions, because it's based on +# fractions of the Axes diagonal, not on data coordinates or data indices. + +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') ax.set_xscale('log') ax.set_yscale('log') - ax.plot(x, y, 'o', ls='-', ms=4, markevery=case) + ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery) ############################################################################### -# Plot each markevery case for linear x and y scales but zoomed in -# note the behaviour when zoomed in. When a start marker offset is specified -# it is always interpreted with respect to the first data point which might be -# different to the first visible data point. - -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots(rows, cols) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) - ax.plot(x, y, 'o', ls='-', ms=4, markevery=case) +# markevery on zoomed plots +# ------------------------- +# +# Integer-based *markevery* specifications select points from the underlying +# data and are independent on the view. In contrast, float-based specifications +# are related to the Axes diagonal. While zooming does not change the Axes +# diagonal, it changes the displayed data range, and more points will be +# displayed when zooming. + +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') + ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery) ax.set_xlim((6, 6.7)) ax.set_ylim((1.1, 1.7)) -# define data for polar plots +############################################################################### +# markevery on polar plots +# ------------------------ + r = np.linspace(0, 3.0, 200) theta = 2 * np.pi * r -############################################################################### -# Plot each markevery case for polar plots - -axs = plt.figure(figsize=figsize, constrained_layout=True).subplots( - rows, cols, subplot_kw={'projection': 'polar'}) -axs = trim_axs(axs, len(cases)) -for ax, case in zip(axs, cases): - ax.set_title('markevery=%s' % str(case)) - ax.plot(theta, r, 'o', ls='-', ms=4, markevery=case) +fig, axs = plt.subplots(3, 3, figsize=(10, 6), constrained_layout=True, + subplot_kw={'projection': 'polar'}) +for ax, markevery in zip(axs.flat, cases): + ax.set_title(f'markevery={markevery}') + ax.plot(theta, r, 'o', ls='-', ms=4, markevery=markevery) plt.show()