diff --git a/doc/api/api_changes/2017-10-24-DS.rst b/doc/api/api_changes/2017-10-24-DS.rst new file mode 100644 index 000000000000..c1c3c6ec9c55 --- /dev/null +++ b/doc/api/api_changes/2017-10-24-DS.rst @@ -0,0 +1,10 @@ +`StemContainer` now stores `LineCollection` +------------------------------------------- + +`StemContainer` objects now store a `LineCollection` object instead of a list +of `Line2D` objects for stem lines plotted using `ax.stem`. This gives a very +large performance boost to displaying and moving `ax.stem` plots. + +Line segments can be extracted from the `LineCollection` using +`LineCollection.get_segements()`. See the `LineCollection` documentation for +other methods to retrieve the collection properties. diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 9fa6e6650b00..67d5d5007aa1 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2370,6 +2370,9 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, x = y y = np.asarray(args[0], dtype=float) args = args[1:] + self._process_unit_info(xdata=x, ydata=y) + x = self.convert_xunits(x) + y = self.convert_yunits(y) # defaults for formats if linefmt is None: @@ -2421,12 +2424,12 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, markerline, = self.plot(x, y, color=markercolor, linestyle=markerstyle, marker=markermarker, label="_nolegend_") - stemlines = [] + lines = [] for thisx, thisy in zip(x, y): - l, = self.plot([thisx, thisx], [bottom, thisy], - color=linecolor, linestyle=linestyle, - marker=linemarker, label="_nolegend_") - stemlines.append(l) + lines.append(((thisx, bottom), (thisx, thisy))) + stemlines = mcoll.LineCollection(lines, linestyles=linestyle, + colors=linecolor, label='_nolegend_') + self.add_collection(stemlines) baseline, = self.plot([np.min(x), np.max(x)], [bottom, bottom], color=basecolor, linestyle=basestyle, diff --git a/lib/matplotlib/container.py b/lib/matplotlib/container.py index 552d6da5a761..0f71c606fe5c 100644 --- a/lib/matplotlib/container.py +++ b/lib/matplotlib/container.py @@ -174,10 +174,17 @@ class StemContainer(Container): baseline : :class:`~matplotlib.lines.Line2D` The artist of the horizontal baseline. - """ - def __init__(self, markerline_stemlines_baseline, **kwargs): + ''' + Parameters + ---------- + markerline_stemlines_baseline : tuple + Tuple of ``(markerline, stemlines, baseline)``. + ``markerline`` contains the `LineCollection` of the markers, + ``stemlines`` is a `LineCollection` of the main lines, + ``baseline`` is the `Line2D` of the baseline. + ''' markerline, stemlines, baseline = markerline_stemlines_baseline self.markerline = markerline self.stemlines = stemlines diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index ad574dcf33d0..44f1afc0b236 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -589,7 +589,6 @@ def get_ydata(self, legend, xdescent, ydescent, width, height, fontsize): def create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans): - markerline, stemlines, baseline = orig_handle xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, @@ -603,31 +602,44 @@ def create_artists(self, legend, orig_handle, else: bottom = self._bottom - leg_markerline = Line2D(xdata_marker, ydata[:len(xdata_marker)]) - self.update_prop(leg_markerline, markerline, legend) - leg_stemlines = [] - for thisx, thisy in zip(xdata_marker, ydata): - l = Line2D([thisx, thisx], [bottom, thisy]) - leg_stemlines.append(l) - for lm, m in zip(leg_stemlines, stemlines): - self.update_prop(lm, m, legend) + # update_prop() usually takes two Line2D collections; + # override temporarily to copy properties from a LineCollection + orig_update_func = self._update_prop_func + self._update_prop_func = self._copy_collection_props + + for thisx, thisy in zip(xdata_marker, ydata): + thisline = Line2D([thisx, thisx], [bottom, thisy]) + leg_stemlines.append(thisline) + self.update_prop(thisline, stemlines, legend) + # Reset update_prop_func + self._update_prop_func = orig_update_func leg_baseline = Line2D([np.min(xdata), np.max(xdata)], [bottom, bottom]) self.update_prop(leg_baseline, baseline, legend) - artists = [leg_markerline] - artists.extend(leg_stemlines) + leg_markerline = Line2D(xdata_marker, ydata[:len(xdata_marker)]) + self.update_prop(leg_markerline, markerline, legend) + + artists = leg_stemlines artists.append(leg_baseline) + artists.append(leg_markerline) for artist in artists: artist.set_transform(trans) return artists + def _copy_collection_props(self, legend_handle, orig_handle): + ''' + Method to copy properties from a LineCollection (orig_handle) to a + Line2D (legend_handle). + ''' + legend_handle._color = orig_handle.get_color()[0] + class HandlerTuple(HandlerBase): """ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/stem.png b/lib/matplotlib/tests/baseline_images/test_axes/stem.png new file mode 100644 index 000000000000..407c977f5b72 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/stem.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 12197bd8e55f..8a6515dbe045 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -2876,6 +2876,17 @@ def test_hist_stacked_weighted(): ax.hist((d1, d2), weights=(w1, w2), histtype="stepfilled", stacked=True) +@image_comparison(baseline_images=['stem'], extensions=['png'], style='mpl20', + remove_text=True) +def test_stem(): + x = np.linspace(0.1, 2 * np.pi, 100) + + fig, ax = plt.subplots() + ax.stem(x, np.cos(x), linefmt='C2-', markerfmt='k+', basefmt='C1-.', + label='Stem') + ax.legend() + + def test_stem_args(): fig = plt.figure() ax = fig.add_subplot(1, 1, 1)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: