From 1e219030a65a094dfa4d9b2e9ebb9f4326cf1b71 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 25 Jul 2015 02:48:01 -0400 Subject: [PATCH 1/8] ENH: plotting methods can unpack labeled data After discussions with Brian Granger, Fernando Perez Peter Wang, Matthew Rocklin and Jake VanderPlas this is a proposal for how to deal with labeled data in matplotlib. The approach taken is that if the optional kwarg 'data' is passed in any other string args/kwargs to the function are replaced be the result `data[k]` if the key exists in `data`, else leave the value as-is. --- lib/matplotlib/__init__.py | 39 +++++++++++++++++++++++++++++- lib/matplotlib/axes/_axes.py | 46 +++++++++++++++++++++++++++++++++++- lib/matplotlib/rcsetup.py | 5 ++-- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index c85984b87981..17c79bf5bbee 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -115,7 +115,7 @@ import warnings import contextlib import distutils.sysconfig - +import functools # cbook must import matplotlib only within function # definitions, so it is safe to import from it here. from matplotlib.cbook import is_string_like, mplDeprecation @@ -1515,6 +1515,43 @@ def test(verbosity=1): test.__test__ = False # nose: this function is not a test + +def _replacer(data, key): + # if key isn't a string don't bother + if not isinstance(key, six.string_types): + return key + # try to use __getitem__ + try: + return data[key] + # key does not exist, silently fall back to key + except KeyError: + return key + + +def unpack_labeled_data(func): + """ + A decorator to add a 'data' kwarg to any a function. The signature + of the input function must be :: + + def foo(ax, *args, **kwargs) + + so this is suitable for use with Axes methods. + """ + @functools.wraps(func) + def inner(ax, *args, **kwargs): + data = kwargs.pop('data', None) + if data is not None: + if rcParams['unpack_labeled']: + args = tuple(_replacer(data, a) for a in args) + kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) + else: + raise ValueError("Trying to unpack labeled data, but " + "rcParams['unpack_labeled'] is False") + + return func(ax, *args, **kwargs) + return inner + verbose.report('matplotlib version %s' % __version__) verbose.report('verbose.level %s' % verbose.level) verbose.report('interactive is %s' % is_interactive()) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 01c94be87190..3f8e54376d21 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -11,6 +11,7 @@ from numpy import ma import matplotlib +from matplotlib import unpack_labeled_data import matplotlib.cbook as cbook from matplotlib.cbook import mplDeprecation, STEP_LOOKUP_MAP @@ -901,6 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p + @unpack_labeled_data @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -979,6 +981,7 @@ def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', return coll + @unpack_labeled_data @docstring.dedent_interpd def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', label='', **kwargs): @@ -1059,6 +1062,7 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', return coll + @unpack_labeled_data @docstring.dedent_interpd def eventplot(self, positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, @@ -1241,6 +1245,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls #### Basic plotting + @unpack_labeled_data @docstring.dedent_interpd def plot(self, *args, **kwargs): """ @@ -1385,6 +1390,7 @@ def plot(self, *args, **kwargs): self.autoscale_view(scalex=scalex, scaley=scaley) return lines + @unpack_labeled_data @docstring.dedent_interpd def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, **kwargs): @@ -1458,6 +1464,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, return ret + @unpack_labeled_data @docstring.dedent_interpd def loglog(self, *args, **kwargs): """ @@ -1519,6 +1526,7 @@ def loglog(self, *args, **kwargs): return l + @unpack_labeled_data @docstring.dedent_interpd def semilogx(self, *args, **kwargs): """ @@ -1571,6 +1579,7 @@ def semilogx(self, *args, **kwargs): self._hold = b # restore the hold return l + @unpack_labeled_data @docstring.dedent_interpd def semilogy(self, *args, **kwargs): """ @@ -1623,6 +1632,7 @@ def semilogy(self, *args, **kwargs): return l + @unpack_labeled_data @docstring.dedent_interpd def acorr(self, x, **kwargs): """ @@ -1684,6 +1694,7 @@ def acorr(self, x, **kwargs): """ return self.xcorr(x, x, **kwargs) + @unpack_labeled_data @docstring.dedent_interpd def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, maxlags=10, **kwargs): @@ -1773,6 +1784,7 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, #### Specialized plotting + @unpack_labeled_data def step(self, x, y, *args, **kwargs): """ Make a step plot. @@ -1810,6 +1822,7 @@ def step(self, x, y, *args, **kwargs): return self.plot(x, y, *args, **kwargs) + @unpack_labeled_data @docstring.dedent_interpd def bar(self, left, height, width=0.8, bottom=None, **kwargs): """ @@ -2236,6 +2249,7 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): bottom=bottom, orientation='horizontal', **kwargs) return patches + @unpack_labeled_data @docstring.dedent_interpd def broken_barh(self, xranges, yrange, **kwargs): """ @@ -2281,6 +2295,7 @@ def broken_barh(self, xranges, yrange, **kwargs): return col + @unpack_labeled_data def stem(self, *args, **kwargs): """ Create a stem plot. @@ -2368,6 +2383,7 @@ def stem(self, *args, **kwargs): return stem_container + @unpack_labeled_data def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, @@ -2587,6 +2603,7 @@ def pie(self, x, explode=None, labels=None, colors=None, else: return slices, texts, autotexts + @unpack_labeled_data @docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, @@ -2950,6 +2967,7 @@ def xywhere(xs, ys, mask): return errorbar_container # (l0, caplines, barcols) + @unpack_labeled_data def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, @@ -3235,6 +3253,7 @@ def _update_dict(dictionary, rc_name, properties): manage_xticks=manage_xticks) return artists + @unpack_labeled_data def bxp(self, bxpstats, positions=None, widths=None, vert=True, patch_artist=False, shownotches=False, showmeans=False, showcaps=True, showbox=True, showfliers=True, @@ -3619,6 +3638,7 @@ def dopatch(xs, ys, **kwargs): return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) + @unpack_labeled_data @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, @@ -3826,6 +3846,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, return collection + @unpack_labeled_data @docstring.dedent_interpd def hexbin(self, x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, @@ -4326,6 +4347,7 @@ def quiverkey(self, *args, **kw): return qk quiverkey.__doc__ = mquiver.QuiverKey.quiverkey_doc + @unpack_labeled_data def quiver(self, *args, **kw): if not self._hold: self.cla() @@ -4336,10 +4358,12 @@ def quiver(self, *args, **kw): return q quiver.__doc__ = mquiver.Quiver.quiver_doc + @unpack_labeled_data def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) stackplot.__doc__ = mstack.stackplot.__doc__ + @unpack_labeled_data def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=1, start_points=None): @@ -4360,6 +4384,7 @@ def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, return stream_container streamplot.__doc__ = mstream.streamplot.__doc__ + @unpack_labeled_data @docstring.dedent_interpd def barbs(self, *args, **kw): """ @@ -4376,6 +4401,7 @@ def barbs(self, *args, **kw): self.autoscale_view() return b + @unpack_labeled_data @docstring.dedent_interpd def fill(self, *args, **kwargs): """ @@ -4427,6 +4453,7 @@ def fill(self, *args, **kwargs): self.autoscale_view() return patches + @unpack_labeled_data @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, step=None, @@ -4580,6 +4607,7 @@ def get_interp_point(ind): self.autoscale_view() return collection + @unpack_labeled_data @docstring.dedent_interpd def fill_betweenx(self, y, x1, x2=0, where=None, step=None, **kwargs): @@ -4704,7 +4732,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, return collection #### plotting z(x,y): imshow, pcolor and relatives, contour - + @unpack_labeled_data @docstring.dedent_interpd def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, @@ -4909,6 +4937,7 @@ def _pcolorargs(funcname, *args, **kw): C = C[:Ny - 1, :Nx - 1] return X, Y, C + @unpack_labeled_data @docstring.dedent_interpd def pcolor(self, *args, **kwargs): """ @@ -5185,6 +5214,7 @@ def pcolor(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection + @unpack_labeled_data @docstring.dedent_interpd def pcolormesh(self, *args, **kwargs): """ @@ -5333,6 +5363,7 @@ def pcolormesh(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection + @unpack_labeled_data @docstring.dedent_interpd def pcolorfast(self, *args, **kwargs): """ @@ -5520,6 +5551,7 @@ def pcolorfast(self, *args, **kwargs): self.autoscale_view(tight=True) return ret + @unpack_labeled_data def contour(self, *args, **kwargs): if not self._hold: self.cla() @@ -5527,6 +5559,7 @@ def contour(self, *args, **kwargs): return mcontour.QuadContourSet(self, *args, **kwargs) contour.__doc__ = mcontour.QuadContourSet.contour_doc + @unpack_labeled_data def contourf(self, *args, **kwargs): if not self._hold: self.cla() @@ -5567,6 +5600,7 @@ def table(self, **kwargs): #### Data analysis + @unpack_labeled_data @docstring.dedent_interpd def hist(self, x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', @@ -6107,6 +6141,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: return n, bins, cbook.silent_list('Lists of Patches', patches) + @unpack_labeled_data @docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, cmin=None, cmax=None, **kwargs): @@ -6200,6 +6235,7 @@ def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, return h, xedges, yedges, pc + @unpack_labeled_data @docstring.dedent_interpd def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6324,6 +6360,7 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxx, freqs, line + @unpack_labeled_data @docstring.dedent_interpd def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6435,6 +6472,7 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxy, freqs, line + @unpack_labeled_data @docstring.dedent_interpd def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, scale=None, @@ -6534,6 +6572,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] + @unpack_labeled_data @docstring.dedent_interpd def angle_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6611,6 +6650,7 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] + @unpack_labeled_data @docstring.dedent_interpd def phase_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6688,6 +6728,7 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] + @unpack_labeled_data @docstring.dedent_interpd def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, @@ -6755,6 +6796,7 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, return cxy, freqs + @unpack_labeled_data @docstring.dedent_interpd def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, @@ -7072,6 +7114,7 @@ def matshow(self, Z, **kwargs): integer=True)) return im + @unpack_labeled_data def violinplot(self, dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None): @@ -7173,6 +7216,7 @@ def _kde_method(X, coords): widths=widths, showmeans=showmeans, showextrema=showextrema, showmedians=showmedians) + @unpack_labeled_data def violin(self, vpstats, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False): """Drawing function for violin plots. diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index d3b3c88607d4..9da3b77799a7 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -809,7 +809,7 @@ def __call__(self, s): 'xtick.minor.pad': [4, validate_float], # distance to label in points 'xtick.color': ['k', validate_color], # color of the xtick labels 'xtick.minor.visible': [False, validate_bool], # visiablility of the x axis minor ticks - + # fontsize of the xtick labels 'xtick.labelsize': ['medium', validate_fontsize], 'xtick.direction': ['in', six.text_type], # direction of xticks @@ -966,7 +966,8 @@ def __call__(self, s): 'animation.convert_path': ['convert', six.text_type], # Additional arguments for mencoder movie writer (using pipes) - 'animation.convert_args': [[], validate_stringlist]} + 'animation.convert_args': [[], validate_stringlist], + 'unpack_labeled': [True, validate_bool], } if __name__ == '__main__': From 1cf0f3878ffada5076f916fb3bbd921eb9977822 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 26 Jul 2015 02:25:58 -0400 Subject: [PATCH 2/8] ENH: Make implicit x in plot pandas aware Try to grab `y.index` before returning `np.arange(len(y))` --- lib/matplotlib/axes/_base.py | 7 +++---- lib/matplotlib/cbook.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 0b3f8b20a738..7448ac01a204 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -31,7 +31,7 @@ import matplotlib.image as mimage from matplotlib.offsetbox import OffsetBox from matplotlib.artist import allow_rasterization -from matplotlib.cbook import iterable +from matplotlib.cbook import iterable, get_index_y rcParams = matplotlib.rcParams @@ -277,12 +277,11 @@ def _plot_args(self, tup, kwargs): if v is not None: kw[k] = v - y = np.atleast_1d(tup[-1]) - if len(tup) == 2: x = np.atleast_1d(tup[0]) + y = np.atleast_1d(tup[-1]) else: - x = np.arange(y.shape[0], dtype=float) + x, y = get_index_y(tup[-1]) x, y = self._xy_from_xy(x, y) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 00e2a489c407..702a101837ec 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2490,6 +2490,34 @@ def pts_to_midstep(x, *args): 'step-post': pts_to_poststep, 'step-mid': pts_to_midstep} + +def get_index_y(y): + """ + A helper function to get the index of an input to plot + against if x values are not explicitly given. + + Tries to get `y.index` (works if this is a pd.Series), if that + fails, return np.arange(y.shape[0]). + + This will be extended in the future to deal with more types of + labeled data. + + Parameters + ---------- + y : scalar or array-like + The proposed y-value + + Returns + ------- + x, y : ndarray + The x and y values to plot. + """ + try: + return y.index.values, y.values + except AttributeError: + y = np.atleast_1d(y) + return np.arange(y.shape[0], dtype=float), y + # Numpy > 1.6.x deprecates putmask in favor of the new copyto. # So long as we support versions 1.6.x and less, we need the # following local version of putmask. We choose to make a From 676eeb43c46c5909a3adc7c389dd0077e14f44bb Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 26 Jul 2015 04:45:51 -0400 Subject: [PATCH 3/8] ENH: add white-list of args/kwargs to relpace If white list is provided, only try to replace those. --- lib/matplotlib/__init__.py | 43 +++++++++++------ lib/matplotlib/axes/_axes.py | 89 ++++++++++++++++++------------------ 2 files changed, 73 insertions(+), 59 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 17c79bf5bbee..cd9e35e0382f 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1528,7 +1528,7 @@ def _replacer(data, key): return key -def unpack_labeled_data(func): +def unpack_labeled_data(wl_args=None, wl_kwargs=None): """ A decorator to add a 'data' kwarg to any a function. The signature of the input function must be :: @@ -1537,20 +1537,33 @@ def foo(ax, *args, **kwargs) so this is suitable for use with Axes methods. """ - @functools.wraps(func) - def inner(ax, *args, **kwargs): - data = kwargs.pop('data', None) - if data is not None: - if rcParams['unpack_labeled']: - args = tuple(_replacer(data, a) for a in args) - kwargs = dict((k, _replacer(data, v)) - for k, v in six.iteritems(kwargs)) - else: - raise ValueError("Trying to unpack labeled data, but " - "rcParams['unpack_labeled'] is False") - - return func(ax, *args, **kwargs) - return inner + if wl_kwargs is not None: + wl_kwargs = set(wl_kwargs) + if wl_args is not None: + wl_args = set(wl_args) + + def param(func): + @functools.wraps(func) + def inner(ax, *args, **kwargs): + data = kwargs.pop('data', None) + if data is not None: + if wl_args is None: + args = tuple(_replacer(data, a) for a in args) + else: + args = tuple(_replacer(data, a) if j in wl_args else a + for j, a in enumerate(args)) + + if wl_kwargs is None: + kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) + else: + kwargs = dict( + (k, _replacer(data, v) if k in wl_kwargs else v) + for k, v in six.iteritems(kwargs)) + + return func(ax, *args, **kwargs) + return inner + return param verbose.report('matplotlib version %s' % __version__) verbose.report('verbose.level %s' % verbose.level) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 3f8e54376d21..8d842445dd4e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -902,7 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p - @unpack_labeled_data + @unpack_labeled_data(wl_args={1, 2, 3}, wl_kwargs={'y', 'xmin', 'xmax'}) @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -981,7 +981,7 @@ def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', return coll - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', label='', **kwargs): @@ -1062,7 +1062,7 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', return coll - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def eventplot(self, positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, @@ -1245,7 +1245,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls #### Basic plotting - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def plot(self, *args, **kwargs): """ @@ -1390,7 +1390,7 @@ def plot(self, *args, **kwargs): self.autoscale_view(scalex=scalex, scaley=scaley) return lines - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, **kwargs): @@ -1464,7 +1464,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, return ret - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def loglog(self, *args, **kwargs): """ @@ -1526,7 +1526,7 @@ def loglog(self, *args, **kwargs): return l - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def semilogx(self, *args, **kwargs): """ @@ -1579,7 +1579,7 @@ def semilogx(self, *args, **kwargs): self._hold = b # restore the hold return l - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def semilogy(self, *args, **kwargs): """ @@ -1632,7 +1632,7 @@ def semilogy(self, *args, **kwargs): return l - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def acorr(self, x, **kwargs): """ @@ -1694,7 +1694,7 @@ def acorr(self, x, **kwargs): """ return self.xcorr(x, x, **kwargs) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, maxlags=10, **kwargs): @@ -1784,7 +1784,7 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, #### Specialized plotting - @unpack_labeled_data + @unpack_labeled_data() def step(self, x, y, *args, **kwargs): """ Make a step plot. @@ -1822,7 +1822,7 @@ def step(self, x, y, *args, **kwargs): return self.plot(x, y, *args, **kwargs) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def bar(self, left, height, width=0.8, bottom=None, **kwargs): """ @@ -2249,7 +2249,7 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): bottom=bottom, orientation='horizontal', **kwargs) return patches - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def broken_barh(self, xranges, yrange, **kwargs): """ @@ -2295,7 +2295,7 @@ def broken_barh(self, xranges, yrange, **kwargs): return col - @unpack_labeled_data + @unpack_labeled_data() def stem(self, *args, **kwargs): """ Create a stem plot. @@ -2383,7 +2383,8 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data + @unpack_labeled_data(wl_args={1, 3, 4}, + wl_kwargs={'x', 'labels', 'colors'}) def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, @@ -2603,7 +2604,7 @@ def pie(self, x, explode=None, labels=None, colors=None, else: return slices, texts, autotexts - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, @@ -2967,7 +2968,7 @@ def xywhere(xs, ys, mask): return errorbar_container # (l0, caplines, barcols) - @unpack_labeled_data + @unpack_labeled_data() def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, @@ -3253,7 +3254,7 @@ def _update_dict(dictionary, rc_name, properties): manage_xticks=manage_xticks) return artists - @unpack_labeled_data + @unpack_labeled_data() def bxp(self, bxpstats, positions=None, widths=None, vert=True, patch_artist=False, shownotches=False, showmeans=False, showcaps=True, showbox=True, showfliers=True, @@ -3638,7 +3639,7 @@ def dopatch(xs, ys, **kwargs): return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, @@ -3846,7 +3847,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def hexbin(self, x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, @@ -4347,7 +4348,7 @@ def quiverkey(self, *args, **kw): return qk quiverkey.__doc__ = mquiver.QuiverKey.quiverkey_doc - @unpack_labeled_data + @unpack_labeled_data() def quiver(self, *args, **kw): if not self._hold: self.cla() @@ -4358,12 +4359,12 @@ def quiver(self, *args, **kw): return q quiver.__doc__ = mquiver.Quiver.quiver_doc - @unpack_labeled_data + @unpack_labeled_data() def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) stackplot.__doc__ = mstack.stackplot.__doc__ - @unpack_labeled_data + @unpack_labeled_data() def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=1, start_points=None): @@ -4384,7 +4385,7 @@ def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, return stream_container streamplot.__doc__ = mstream.streamplot.__doc__ - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def barbs(self, *args, **kw): """ @@ -4401,7 +4402,7 @@ def barbs(self, *args, **kw): self.autoscale_view() return b - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def fill(self, *args, **kwargs): """ @@ -4453,7 +4454,7 @@ def fill(self, *args, **kwargs): self.autoscale_view() return patches - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, step=None, @@ -4607,7 +4608,7 @@ def get_interp_point(ind): self.autoscale_view() return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def fill_betweenx(self, y, x1, x2=0, where=None, step=None, **kwargs): @@ -4732,7 +4733,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, return collection #### plotting z(x,y): imshow, pcolor and relatives, contour - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, @@ -4937,7 +4938,7 @@ def _pcolorargs(funcname, *args, **kw): C = C[:Ny - 1, :Nx - 1] return X, Y, C - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def pcolor(self, *args, **kwargs): """ @@ -5214,7 +5215,7 @@ def pcolor(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def pcolormesh(self, *args, **kwargs): """ @@ -5363,7 +5364,7 @@ def pcolormesh(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def pcolorfast(self, *args, **kwargs): """ @@ -5551,7 +5552,7 @@ def pcolorfast(self, *args, **kwargs): self.autoscale_view(tight=True) return ret - @unpack_labeled_data + @unpack_labeled_data() def contour(self, *args, **kwargs): if not self._hold: self.cla() @@ -5559,7 +5560,7 @@ def contour(self, *args, **kwargs): return mcontour.QuadContourSet(self, *args, **kwargs) contour.__doc__ = mcontour.QuadContourSet.contour_doc - @unpack_labeled_data + @unpack_labeled_data() def contourf(self, *args, **kwargs): if not self._hold: self.cla() @@ -5600,7 +5601,7 @@ def table(self, **kwargs): #### Data analysis - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def hist(self, x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', @@ -6141,7 +6142,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: return n, bins, cbook.silent_list('Lists of Patches', patches) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, cmin=None, cmax=None, **kwargs): @@ -6235,7 +6236,7 @@ def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, return h, xedges, yedges, pc - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6360,7 +6361,7 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxx, freqs, line - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6472,7 +6473,7 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxy, freqs, line - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, scale=None, @@ -6572,7 +6573,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def angle_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6650,7 +6651,7 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def phase_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6728,7 +6729,7 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, @@ -6796,7 +6797,7 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, return cxy, freqs - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, @@ -7114,7 +7115,7 @@ def matshow(self, Z, **kwargs): integer=True)) return im - @unpack_labeled_data + @unpack_labeled_data() def violinplot(self, dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None): @@ -7216,7 +7217,7 @@ def _kde_method(X, coords): widths=widths, showmeans=showmeans, showextrema=showextrema, showmedians=showmedians) - @unpack_labeled_data + @unpack_labeled_data() def violin(self, vpstats, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False): """Drawing function for violin plots. From 9e6e7b43a1b39f425cdc05cb1601c23072408920 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 26 Jul 2015 19:17:55 -0400 Subject: [PATCH 4/8] MNT: remove unused rcparam --- lib/matplotlib/rcsetup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 9da3b77799a7..089d0e15fcc7 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -966,8 +966,7 @@ def __call__(self, s): 'animation.convert_path': ['convert', six.text_type], # Additional arguments for mencoder movie writer (using pipes) - 'animation.convert_args': [[], validate_stringlist], - 'unpack_labeled': [True, validate_bool], } + 'animation.convert_args': [[], validate_stringlist]} if __name__ == '__main__': From 5ace465275af7ece62f5f1f38df7f6935c359ca8 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 27 Jul 2015 12:36:25 -0400 Subject: [PATCH 5/8] MNT: python 2.6 does not support set literals --- lib/matplotlib/axes/_axes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 8d842445dd4e..91564fa5de1b 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -902,7 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p - @unpack_labeled_data(wl_args={1, 2, 3}, wl_kwargs={'y', 'xmin', 'xmax'}) + @unpack_labeled_data(wl_args=[1, 2, 3], wl_kwargs=['y', 'xmin', 'xmax']) @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -2383,8 +2383,8 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data(wl_args={1, 3, 4}, - wl_kwargs={'x', 'labels', 'colors'}) + @unpack_labeled_data(wl_args=[1, 3, 4], + wl_kwargs=['x', 'labels', 'colors']) def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, From e90d8594dbb03890a654b464e03ad13bdacf3920 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 28 Jul 2015 02:13:23 -0400 Subject: [PATCH 6/8] ENH: pass at decorator which extracts a label Not tested or used. --- lib/matplotlib/__init__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index cd9e35e0382f..0009227dbe50 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1528,7 +1528,7 @@ def _replacer(data, key): return key -def unpack_labeled_data(wl_args=None, wl_kwargs=None): +def unpack_labeled_data(wl_args=None, wl_kwargs=None, label_pos=None): """ A decorator to add a 'data' kwarg to any a function. The signature of the input function must be :: @@ -1537,6 +1537,9 @@ def foo(ax, *args, **kwargs) so this is suitable for use with Axes methods. """ + if label_pos is not None: + label_arg, label_kwarg = label_pos + if wl_kwargs is not None: wl_kwargs = set(wl_kwargs) if wl_args is not None: @@ -1560,6 +1563,18 @@ def inner(ax, *args, **kwargs): kwargs = dict( (k, _replacer(data, v) if k in wl_kwargs else v) for k, v in six.iteritems(kwargs)) + if (label_pos is not None and ('label' not in kwargs or + kwargs['label'] is None)): + if len(args) > label_arg: + try: + kwargs['label'] = args[label_arg].name + except AttributeError: + pass + elif label_kwarg in kwargs: + try: + kwargs['label'] = args[label_kwarg].name + except AttributeError: + pass return func(ax, *args, **kwargs) return inner From 664654505911fe0b28ec2ee221f0a1c1f70f5477 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 29 Jul 2015 08:55:58 -0400 Subject: [PATCH 7/8] FIX: fix typo, slightly rename variables --- lib/matplotlib/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 0009227dbe50..7df54561b12e 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1551,20 +1551,20 @@ def inner(ax, *args, **kwargs): data = kwargs.pop('data', None) if data is not None: if wl_args is None: - args = tuple(_replacer(data, a) for a in args) + new_args = tuple(_replacer(data, a) for a in args) else: - args = tuple(_replacer(data, a) if j in wl_args else a - for j, a in enumerate(args)) + new_args = tuple(_replacer(data, a) if j in wl_args else a + for j, a in enumerate(args)) if wl_kwargs is None: - kwargs = dict((k, _replacer(data, v)) - for k, v in six.iteritems(kwargs)) + new_kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) else: - kwargs = dict( + new_kwargs = dict( (k, _replacer(data, v) if k in wl_kwargs else v) for k, v in six.iteritems(kwargs)) if (label_pos is not None and ('label' not in kwargs or - kwargs['label'] is None)): + kwargs['label'] is None)): if len(args) > label_arg: try: kwargs['label'] = args[label_arg].name @@ -1572,11 +1572,11 @@ def inner(ax, *args, **kwargs): pass elif label_kwarg in kwargs: try: - kwargs['label'] = args[label_kwarg].name + kwargs['label'] = kwargs[label_kwarg].name except AttributeError: pass - return func(ax, *args, **kwargs) + return func(ax, *new_args, **new_kwargs) return inner return param From 4059b448666c63b6b532f93ccde3043f60fd8e3c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 29 Jul 2015 11:54:53 -0400 Subject: [PATCH 8/8] FIX: fix yet more typos --- lib/matplotlib/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 7df54561b12e..37381060bc78 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1563,6 +1563,9 @@ def inner(ax, *args, **kwargs): new_kwargs = dict( (k, _replacer(data, v) if k in wl_kwargs else v) for k, v in six.iteritems(kwargs)) + else: + new_args, new_kwargs = args, kwargs + if (label_pos is not None and ('label' not in kwargs or kwargs['label'] is None)): if len(args) > label_arg: @@ -1572,7 +1575,7 @@ def inner(ax, *args, **kwargs): pass elif label_kwarg in kwargs: try: - kwargs['label'] = kwargs[label_kwarg].name + new_kwargs['label'] = kwargs[label_kwarg].name except AttributeError: pass