From: <jd...@us...> - 2008-11-20 18:58:57
|
Revision: 6423 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6423&view=rev Author: jdh2358 Date: 2008-11-20 18:58:54 +0000 (Thu, 20 Nov 2008) Log Message: ----------- added some helper functions for poly collections and masked regions Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/collections.py trunk/matplotlib/lib/matplotlib/mlab.py trunk/matplotlib/src/_backend_agg.cpp Added Paths: ----------- trunk/matplotlib/examples/api/filled_masked_regions.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/CHANGELOG 2008-11-20 18:58:54 UTC (rev 6423) @@ -1,3 +1,11 @@ +2008-11-20 Added some static helper methods + BrokenHBarCollection.span_masked and + PolyCollection.fill_between_masked for visualizing + non-masked regions. In the longer term, the better + solution will be to fix the relevant classes and functions + to handle masked data, so this may be a temporary solution + - JDH + 2008-11-12 Add x_isdata and y_isdata attributes to Artist instances, and use them to determine whether either or both coordinates are used when updating dataLim. This is Added: trunk/matplotlib/examples/api/filled_masked_regions.py =================================================================== --- trunk/matplotlib/examples/api/filled_masked_regions.py (rev 0) +++ trunk/matplotlib/examples/api/filled_masked_regions.py 2008-11-20 18:58:54 UTC (rev 6423) @@ -0,0 +1,45 @@ +""" +Illustrate some helper functions for shading regions where a logical +mask is True +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.collections as collections + + +t = np.arange(0.0, 2, 0.01) +s = np.sin(2*np.pi*t) + +fig = plt.figure() +ax = fig.add_subplot(111) +ax.set_title('using fill_between_masked') +ax.plot(t, s, '-') +ax.axhline(0, color='black', lw=2) + +collection = collections.PolyCollection.fill_between_masked(t, s, s>=0, yboundary=0, color='green', alpha=0.5) +ax.add_collection(collection) + +collection = collections.PolyCollection.fill_between_masked(t, s, s<=0, yboundary=0, color='red', alpha=0.5) +ax.add_collection(collection) + + +fig = plt.figure() +ax = fig.add_subplot(111) +ax.set_title('using span_masked') +ax.plot(t, s, '-') +ax.axhline(0, color='black', lw=2) + +collection = collections.BrokenBarHCollection.span_masked(t, s>0, ymin=0, ymax=1, facecolor='green', alpha=0.5) +ax.add_collection(collection) + +collection = collections.BrokenBarHCollection.span_masked(t, s<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5) +ax.add_collection(collection) + + + +plt.show() + + + + + Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-11-20 18:58:54 UTC (rev 6423) @@ -19,6 +19,7 @@ import matplotlib.artist as artist import matplotlib.backend_bases as backend_bases import matplotlib.path as mpath +import matplotlib.mlab as mlab class Collection(artist.Artist, cm.ScalarMappable): """ @@ -234,7 +235,7 @@ self._urls = [None,] else: self._urls = urls - + def get_urls(self): return self._urls def set_offsets(self, offsets): @@ -671,6 +672,49 @@ for x in self._sizes] return Collection.draw(self, renderer) + + @staticmethod + def fill_between_masked(x, y, mask, yboundary=0, **kwargs): + """ + Create a :class:`PolyCollection` filling the regions between *y* + and *yboundary7* where ``mask==True`` + + + *x* + an N length np array of the x data + + *y* + an N length np array of the y data + + *mask* + an N length numpy boolean array + + *yboundary* + a scalar to fill between *y* and the boundary + + *kwargs* + keyword args passed on to the :class:`PolyCollection` + + """ + polys = [] + for ind0, ind1 in mlab.contiguous_regions(mask): + theseverts = [] + xslice = x[ind0:ind1] + yslice = y[ind0:ind1] + N = len(xslice) + X = np.zeros((2*N+2, 2), np.float) + X[0] = xslice[0], yboundary + X[N+1] = xslice[-1], yboundary + X[1:N+1,0] = xslice + X[1:N+1,1] = yslice + X[N+2:,0] = xslice[::-1] + X[N+2:,1] = yboundary + + polys.append(X) + + collection = PolyCollection(polys, **kwargs) + return collection + class BrokenBarHCollection(PolyCollection): """ A collection of horizontal bars spanning *yrange* with a sequence of @@ -692,6 +736,25 @@ PolyCollection.__init__(self, verts, **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + + @staticmethod + def span_masked(x, mask, ymin, ymax, **kwargs): + """ + Create a BrokenBarHCollection to plot horizontal bars from + over the regions in *x* where *mask* is True. The bars range + on the y-axis from *ymin* to *ymax* + + A :class:`BrokenBarHCollection` is returned. + **kwargs are passed on to the collection + """ + xranges = [] + for ind0, ind1 in mlab.contiguous_regions(mask): + xslice = x[ind0:ind1] + xranges.append((xslice[0], xslice[-1]-xslice[0])) + + collection = BrokenBarHCollection(xranges, [ymin, ymax-ymin], **kwargs) + return collection + class RegularPolyCollection(Collection): """Draw a collection of regular polygons with *numsides*.""" _path_generator = mpath.Path.unit_regular_polygon Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-20 18:58:54 UTC (rev 6423) @@ -159,7 +159,7 @@ import csv, warnings, copy, os import numpy as np - +ma = np.ma from matplotlib import verbose import matplotlib.nxutils as nxutils @@ -247,7 +247,7 @@ #The checks for if y is x are so that we can use the same function to #implement the core of psd(), csd(), and spectrogram() without doing #extra calculations. We return the unaveraged Pxy, freqs, and t. - + #Make sure we're dealing with a numpy array. If y and x were the same #object to start with, keep them that way same_data = y is x @@ -309,7 +309,7 @@ Pxy /= (np.abs(windowVals)**2).sum() t = 1./Fs * (ind + NFFT / 2.) freqs = float(Fs) / pad_to * np.arange(numFreqs) - + return Pxy, freqs, t #Split out these keyword docs so that they can be used elsewhere @@ -2104,7 +2104,8 @@ def csv2rec(fname, comments='#', skiprows=0, checkrows=0, delimiter=',', - converterd=None, names=None, missing='', missingd=None): + converterd=None, names=None, missing='', missingd=None, + use_mrecords=True): """ Load data from comma/space/tab delimited file in *fname* into a numpy record array and return the record array. @@ -2139,9 +2140,11 @@ be masked, e.g. '0000-00-00' or 'unused' - *missing*: a string whose value signals a missing field regardless of - the column it appears in, e.g. 'unused' + the column it appears in - If no rows are found, *None* is returned -- see :file:`examples/loadrec.py` + - *use_mrecords*: if True, return an mrecords.fromrecords record array if any of the data are missing + + If no rows are found, *None* is returned -- see :file:`examples/loadrec.py` """ if converterd is None: @@ -2338,7 +2341,8 @@ if not len(rows): return None - if np.any(rowmasks): + + if use_mrecords and np.any(rowmasks): try: from numpy.ma import mrecords except ImportError: raise RuntimeError('numpy 1.05 or later is required for masked array support') @@ -2938,19 +2942,25 @@ xv, yv = poly_below(0, x, y) ax.fill(xv, yv) """ - xs = np.asarray(xs) - ys = np.asarray(ys) + if ma.isMaskedArray(xs) or ma.isMaskedArray(ys): + nx = ma + else: + nx = np + + xs = nx.asarray(xs) + ys = nx.asarray(ys) Nx = len(xs) Ny = len(ys) assert(Nx==Ny) - x = xmin*np.ones(2*Nx) - y = np.ones(2*Nx) + x = xmin*nx.ones(2*Nx) + y = nx.ones(2*Nx) x[:Nx] = xs y[:Nx] = ys y[Nx:] = ys[::-1] return x, y + def poly_between(x, ylower, yupper): """ Given a sequence of *x*, *ylower* and *yupper*, return the polygon @@ -2961,17 +2971,23 @@ Return value is *x*, *y* arrays for use with :meth:`matplotlib.axes.Axes.fill`. """ + if ma.isMaskedArray(ylower) or ma.isMaskedArray(yupper) or ma.isMaskedArray(x): + nx = ma + else: + nx = np + Nx = len(x) if not cbook.iterable(ylower): - ylower = ylower*np.ones(Nx) + ylower = ylower*nx.ones(Nx) if not cbook.iterable(yupper): - yupper = yupper*np.ones(Nx) + yupper = yupper*nx.ones(Nx) - x = np.concatenate( (x, x[::-1]) ) - y = np.concatenate( (yupper, ylower[::-1]) ) + x = nx.concatenate( (x, x[::-1]) ) + y = nx.concatenate( (yupper, ylower[::-1]) ) return x,y + def is_closed_polygon(X): """ Tests whether first and last object in a sequence are the same. These are @@ -2980,6 +2996,28 @@ """ return np.all(X[0] == X[-1]) + +def contiguous_regions(mask): + """ + return a list of (ind0, ind1) such that mask[ind0:ind1].all() is + True and we cover all such regions + + TODO: this is a pure python implementation which probably has a much faster numpy impl + """ + + in_region = None + boundaries = [] + for i, val in enumerate(mask): + if in_region is None and val: + in_region = i + elif in_region is not None and not val: + boundaries.append((in_region, i)) + in_region = None + + if in_region is not None: + boundaries.append((in_region, i+1)) + return boundaries + ################################################## # Vector and path length geometry calculations ################################################## Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-11-20 18:58:54 UTC (rev 6423) @@ -385,7 +385,8 @@ if (!py_convert_bbox(box_obj.ptr(), l, b, r, t)) throw Py::TypeError("Invalid bbox provided to copy_from_bbox"); - agg::rect_i rect((int)l, height - (int)t, (int)r, height - (int)b); + // std::cout << l << " " << b << " " << r << " " << t << " " << (height - (int)b) << " " << height - (int)t << std::endl; + agg::rect_i rect((int)l, height - (int)b, (int)r, height - (int)t); BufferRegion* reg = NULL; try { @@ -419,9 +420,11 @@ BufferRegion* region = static_cast<BufferRegion*>(args[0].ptr()); if (region->data==NULL) - return Py::Object(); - //throw Py::ValueError("Cannot restore_region from NULL data"); + throw Py::ValueError("Cannot restore_region from NULL data"); + //return Py::Object(); + //std::cout << "restoring " << region->width << " " << region->height << " " << region->stride << " " << region->rect.x1 << " " << region->rect.y1 << std::endl; + agg::rendering_buffer rbuf; rbuf.attach(region->data, region->width, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |