Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 630019a

Browse files
committed
added some helper functions for poly collections and masked regions
svn path=/trunk/matplotlib/; revision=6423
1 parent 2213ed3 commit 630019a

5 files changed

Lines changed: 176 additions & 19 deletions

File tree

CHANGELOG

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
2008-11-20 Added some static helper methods
2+
BrokenHBarCollection.span_masked and
3+
PolyCollection.fill_between_masked for visualizing
4+
non-masked regions. In the longer term, the better
5+
solution will be to fix the relevant classes and functions
6+
to handle masked data, so this may be a temporary solution
7+
- JDH
8+
19
2008-11-12 Add x_isdata and y_isdata attributes to Artist instances,
210
and use them to determine whether either or both
311
coordinates are used when updating dataLim. This is
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
Illustrate some helper functions for shading regions where a logical
3+
mask is True
4+
"""
5+
import numpy as np
6+
import matplotlib.pyplot as plt
7+
import matplotlib.collections as collections
8+
9+
10+
t = np.arange(0.0, 2, 0.01)
11+
s = np.sin(2*np.pi*t)
12+
13+
fig = plt.figure()
14+
ax = fig.add_subplot(111)
15+
ax.set_title('using fill_between_masked')
16+
ax.plot(t, s, '-')
17+
ax.axhline(0, color='black', lw=2)
18+
19+
collection = collections.PolyCollection.fill_between_masked(t, s, s>=0, yboundary=0, color='green', alpha=0.5)
20+
ax.add_collection(collection)
21+
22+
collection = collections.PolyCollection.fill_between_masked(t, s, s<=0, yboundary=0, color='red', alpha=0.5)
23+
ax.add_collection(collection)
24+
25+
26+
fig = plt.figure()
27+
ax = fig.add_subplot(111)
28+
ax.set_title('using span_masked')
29+
ax.plot(t, s, '-')
30+
ax.axhline(0, color='black', lw=2)
31+
32+
collection = collections.BrokenBarHCollection.span_masked(t, s>0, ymin=0, ymax=1, facecolor='green', alpha=0.5)
33+
ax.add_collection(collection)
34+
35+
collection = collections.BrokenBarHCollection.span_masked(t, s<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5)
36+
ax.add_collection(collection)
37+
38+
39+
40+
plt.show()
41+
42+
43+
44+
45+

lib/matplotlib/collections.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import matplotlib.artist as artist
2020
import matplotlib.backend_bases as backend_bases
2121
import matplotlib.path as mpath
22+
import matplotlib.mlab as mlab
2223

2324
class Collection(artist.Artist, cm.ScalarMappable):
2425
"""
@@ -234,7 +235,7 @@ def set_urls(self, urls):
234235
self._urls = [None,]
235236
else:
236237
self._urls = urls
237-
238+
238239
def get_urls(self): return self._urls
239240

240241
def set_offsets(self, offsets):
@@ -671,6 +672,49 @@ def draw(self, renderer):
671672
for x in self._sizes]
672673
return Collection.draw(self, renderer)
673674

675+
676+
@staticmethod
677+
def fill_between_masked(x, y, mask, yboundary=0, **kwargs):
678+
"""
679+
Create a :class:`PolyCollection` filling the regions between *y*
680+
and *yboundary7* where ``mask==True``
681+
682+
683+
*x*
684+
an N length np array of the x data
685+
686+
*y*
687+
an N length np array of the y data
688+
689+
*mask*
690+
an N length numpy boolean array
691+
692+
*yboundary*
693+
a scalar to fill between *y* and the boundary
694+
695+
*kwargs*
696+
keyword args passed on to the :class:`PolyCollection`
697+
698+
"""
699+
polys = []
700+
for ind0, ind1 in mlab.contiguous_regions(mask):
701+
theseverts = []
702+
xslice = x[ind0:ind1]
703+
yslice = y[ind0:ind1]
704+
N = len(xslice)
705+
X = np.zeros((2*N+2, 2), np.float)
706+
X[0] = xslice[0], yboundary
707+
X[N+1] = xslice[-1], yboundary
708+
X[1:N+1,0] = xslice
709+
X[1:N+1,1] = yslice
710+
X[N+2:,0] = xslice[::-1]
711+
X[N+2:,1] = yboundary
712+
713+
polys.append(X)
714+
715+
collection = PolyCollection(polys, **kwargs)
716+
return collection
717+
674718
class BrokenBarHCollection(PolyCollection):
675719
"""
676720
A collection of horizontal bars spanning *yrange* with a sequence of
@@ -692,6 +736,25 @@ def __init__(self, xranges, yrange, **kwargs):
692736
PolyCollection.__init__(self, verts, **kwargs)
693737
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
694738

739+
740+
@staticmethod
741+
def span_masked(x, mask, ymin, ymax, **kwargs):
742+
"""
743+
Create a BrokenBarHCollection to plot horizontal bars from
744+
over the regions in *x* where *mask* is True. The bars range
745+
on the y-axis from *ymin* to *ymax*
746+
747+
A :class:`BrokenBarHCollection` is returned.
748+
**kwargs are passed on to the collection
749+
"""
750+
xranges = []
751+
for ind0, ind1 in mlab.contiguous_regions(mask):
752+
xslice = x[ind0:ind1]
753+
xranges.append((xslice[0], xslice[-1]-xslice[0]))
754+
755+
collection = BrokenBarHCollection(xranges, [ymin, ymax-ymin], **kwargs)
756+
return collection
757+
695758
class RegularPolyCollection(Collection):
696759
"""Draw a collection of regular polygons with *numsides*."""
697760
_path_generator = mpath.Path.unit_regular_polygon

lib/matplotlib/mlab.py

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
import csv, warnings, copy, os
160160

161161
import numpy as np
162-
162+
ma = np.ma
163163
from matplotlib import verbose
164164

165165
import matplotlib.nxutils as nxutils
@@ -247,7 +247,7 @@ def _spectral_helper(x, y, NFFT=256, Fs=2, detrend=detrend_none,
247247
#The checks for if y is x are so that we can use the same function to
248248
#implement the core of psd(), csd(), and spectrogram() without doing
249249
#extra calculations. We return the unaveraged Pxy, freqs, and t.
250-
250+
251251
#Make sure we're dealing with a numpy array. If y and x were the same
252252
#object to start with, keep them that way
253253
same_data = y is x
@@ -309,7 +309,7 @@ def _spectral_helper(x, y, NFFT=256, Fs=2, detrend=detrend_none,
309309
Pxy /= (np.abs(windowVals)**2).sum()
310310
t = 1./Fs * (ind + NFFT / 2.)
311311
freqs = float(Fs) / pad_to * np.arange(numFreqs)
312-
312+
313313
return Pxy, freqs, t
314314

315315
#Split out these keyword docs so that they can be used elsewhere
@@ -2104,7 +2104,8 @@ def mapped_r2field(name):
21042104

21052105

21062106
def csv2rec(fname, comments='#', skiprows=0, checkrows=0, delimiter=',',
2107-
converterd=None, names=None, missing='', missingd=None):
2107+
converterd=None, names=None, missing='', missingd=None,
2108+
use_mrecords=True):
21082109
"""
21092110
Load data from comma/space/tab delimited file in *fname* into a
21102111
numpy record array and return the record array.
@@ -2139,9 +2140,11 @@ def csv2rec(fname, comments='#', skiprows=0, checkrows=0, delimiter=',',
21392140
be masked, e.g. '0000-00-00' or 'unused'
21402141
21412142
- *missing*: a string whose value signals a missing field regardless of
2142-
the column it appears in, e.g. 'unused'
2143+
the column it appears in
21432144
2144-
If no rows are found, *None* is returned -- see :file:`examples/loadrec.py`
2145+
- *use_mrecords*: if True, return an mrecords.fromrecords record array if any of the data are missing
2146+
2147+
If no rows are found, *None* is returned -- see :file:`examples/loadrec.py`
21452148
"""
21462149

21472150
if converterd is None:
@@ -2338,7 +2341,8 @@ def get_converters(reader):
23382341

23392342
if not len(rows):
23402343
return None
2341-
if np.any(rowmasks):
2344+
2345+
if use_mrecords and np.any(rowmasks):
23422346
try: from numpy.ma import mrecords
23432347
except ImportError:
23442348
raise RuntimeError('numpy 1.05 or later is required for masked array support')
@@ -2938,19 +2942,25 @@ def poly_below(xmin, xs, ys):
29382942
xv, yv = poly_below(0, x, y)
29392943
ax.fill(xv, yv)
29402944
"""
2941-
xs = np.asarray(xs)
2942-
ys = np.asarray(ys)
2945+
if ma.isMaskedArray(xs) or ma.isMaskedArray(ys):
2946+
nx = ma
2947+
else:
2948+
nx = np
2949+
2950+
xs = nx.asarray(xs)
2951+
ys = nx.asarray(ys)
29432952
Nx = len(xs)
29442953
Ny = len(ys)
29452954
assert(Nx==Ny)
2946-
x = xmin*np.ones(2*Nx)
2947-
y = np.ones(2*Nx)
2955+
x = xmin*nx.ones(2*Nx)
2956+
y = nx.ones(2*Nx)
29482957
x[:Nx] = xs
29492958
y[:Nx] = ys
29502959
y[Nx:] = ys[::-1]
29512960
return x, y
29522961

29532962

2963+
29542964
def poly_between(x, ylower, yupper):
29552965
"""
29562966
Given a sequence of *x*, *ylower* and *yupper*, return the polygon
@@ -2961,17 +2971,23 @@ def poly_between(x, ylower, yupper):
29612971
Return value is *x*, *y* arrays for use with
29622972
:meth:`matplotlib.axes.Axes.fill`.
29632973
"""
2974+
if ma.isMaskedArray(ylower) or ma.isMaskedArray(yupper) or ma.isMaskedArray(x):
2975+
nx = ma
2976+
else:
2977+
nx = np
2978+
29642979
Nx = len(x)
29652980
if not cbook.iterable(ylower):
2966-
ylower = ylower*np.ones(Nx)
2981+
ylower = ylower*nx.ones(Nx)
29672982

29682983
if not cbook.iterable(yupper):
2969-
yupper = yupper*np.ones(Nx)
2984+
yupper = yupper*nx.ones(Nx)
29702985

2971-
x = np.concatenate( (x, x[::-1]) )
2972-
y = np.concatenate( (yupper, ylower[::-1]) )
2986+
x = nx.concatenate( (x, x[::-1]) )
2987+
y = nx.concatenate( (yupper, ylower[::-1]) )
29732988
return x,y
29742989

2990+
29752991
def is_closed_polygon(X):
29762992
"""
29772993
Tests whether first and last object in a sequence are the same. These are
@@ -2980,6 +2996,28 @@ def is_closed_polygon(X):
29802996
"""
29812997
return np.all(X[0] == X[-1])
29822998

2999+
3000+
def contiguous_regions(mask):
3001+
"""
3002+
return a list of (ind0, ind1) such that mask[ind0:ind1].all() is
3003+
True and we cover all such regions
3004+
3005+
TODO: this is a pure python implementation which probably has a much faster numpy impl
3006+
"""
3007+
3008+
in_region = None
3009+
boundaries = []
3010+
for i, val in enumerate(mask):
3011+
if in_region is None and val:
3012+
in_region = i
3013+
elif in_region is not None and not val:
3014+
boundaries.append((in_region, i))
3015+
in_region = None
3016+
3017+
if in_region is not None:
3018+
boundaries.append((in_region, i+1))
3019+
return boundaries
3020+
29833021
##################################################
29843022
# Vector and path length geometry calculations
29853023
##################################################

src/_backend_agg.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ RendererAgg::copy_from_bbox(const Py::Tuple& args) {
385385
if (!py_convert_bbox(box_obj.ptr(), l, b, r, t))
386386
throw Py::TypeError("Invalid bbox provided to copy_from_bbox");
387387

388-
agg::rect_i rect((int)l, height - (int)t, (int)r, height - (int)b);
388+
// std::cout << l << " " << b << " " << r << " " << t << " " << (height - (int)b) << " " << height - (int)t << std::endl;
389+
agg::rect_i rect((int)l, height - (int)b, (int)r, height - (int)t);
389390

390391
BufferRegion* reg = NULL;
391392
try {
@@ -419,8 +420,10 @@ RendererAgg::restore_region(const Py::Tuple& args) {
419420
BufferRegion* region = static_cast<BufferRegion*>(args[0].ptr());
420421

421422
if (region->data==NULL)
422-
return Py::Object();
423-
//throw Py::ValueError("Cannot restore_region from NULL data");
423+
throw Py::ValueError("Cannot restore_region from NULL data");
424+
//return Py::Object();
425+
426+
//std::cout << "restoring " << region->width << " " << region->height << " " << region->stride << " " << region->rect.x1 << " " << region->rect.y1 << std::endl;
424427

425428
agg::rendering_buffer rbuf;
426429
rbuf.attach(region->data,

0 commit comments

Comments
 (0)