diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index c85984b87981..37381060bc78 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,74 @@ 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(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 :: + + 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: + 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: + new_args = tuple(_replacer(data, a) for a in args) + else: + new_args = tuple(_replacer(data, a) if j in wl_args else a + for j, a in enumerate(args)) + + if wl_kwargs is None: + new_kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) + else: + 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: + try: + kwargs['label'] = args[label_arg].name + except AttributeError: + pass + elif label_kwarg in kwargs: + try: + new_kwargs['label'] = kwargs[label_kwarg].name + except AttributeError: + pass + + return func(ax, *new_args, **new_kwargs) + return inner + return param + 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..91564fa5de1b 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(wl_args=[1, 2, 3], wl_kwargs=['y', 'xmin', 'xmax']) @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,8 @@ def stem(self, *args, **kwargs): return stem_container + @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, @@ -2587,6 +2604,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 +2968,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 +3254,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 +3639,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 +3847,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 +4348,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 +4359,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 +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() @docstring.dedent_interpd def barbs(self, *args, **kw): """ @@ -4376,6 +4402,7 @@ def barbs(self, *args, **kw): self.autoscale_view() return b + @unpack_labeled_data() @docstring.dedent_interpd def fill(self, *args, **kwargs): """ @@ -4427,6 +4454,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 +4608,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 +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() @docstring.dedent_interpd def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, @@ -4909,6 +4938,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 +5215,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 +5364,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 +5552,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 +5560,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 +5601,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 +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() @docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, cmin=None, cmax=None, **kwargs): @@ -6200,6 +6236,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 +6361,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 +6473,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 +6573,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 +6651,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 +6729,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 +6797,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 +7115,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 +7217,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/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 diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index d3b3c88607d4..089d0e15fcc7 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