Skip to content

Commit f6e68eb

Browse files
committed
Expanded delete_masked_points to handle more types of argument
svn path=/trunk/matplotlib/; revision=5797
1 parent a9be4e9 commit f6e68eb

File tree

4 files changed

+119
-85
lines changed

4 files changed

+119
-85
lines changed

CHANGELOG

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2008-07-20 Rewrote cbook.delete_masked_points and corresponding
2+
unit test to support rgb color array inputs, datetime
3+
inputs, etc. - EF
14

25
2008-07-20 Renamed unit/axes_unit.py to cbook_unit.py and modified
36
in accord with Ryan's move of delete_masked_points from

lib/matplotlib/cbook.py

+70-29
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re, os, errno, sys, StringIO, traceback, locale, threading, types
77
import time, datetime
88
import numpy as np
9-
from numpy import ma
9+
import numpy.ma as ma
1010
from weakref import ref
1111

1212
major, minor1, minor2, s, tmp = sys.version_info
@@ -1168,41 +1168,82 @@ def recursive_remove(path):
11681168

11691169
def delete_masked_points(*args):
11701170
"""
1171-
Find all masked points in a set of arguments, and return
1172-
the arguments with only the unmasked points remaining.
1171+
Find all masked and/or non-finite points in a set of arguments,
1172+
and return the arguments with only the unmasked points remaining.
11731173
1174-
This will also delete any points that are not finite (nan or inf).
1174+
Arguments can be in any of 5 categories:
11751175
1176-
The overall mask is calculated from any masks that are present.
1177-
If a mask is found, any argument that does not have the same
1178-
dimensions is left unchanged; therefore the argument list may
1179-
include arguments that can take string or array values, for
1180-
example.
1176+
1) 1-D masked arrays
1177+
2) 1-D ndarrays
1178+
3) ndarrays with more than one dimension
1179+
4) other non-string iterables
1180+
5) anything else
11811181
1182-
Array arguments must have the same length; masked arguments must
1183-
be one-dimensional.
1182+
The first argument must be in one of the first four categories;
1183+
any argument with a length differing from that of the first
1184+
argument (and hence anything in category 5) then will be
1185+
passed through unchanged.
1186+
1187+
Masks are obtained from all arguments of the correct length
1188+
in categories 1, 2, and 4; a point is bad if masked in a masked
1189+
array or if it is a nan or inf. No attempt is made to
1190+
extract a mask from categories 2, 3, and 4 if *np.isfinite()*
1191+
does not yield a Boolean array.
1192+
1193+
All input arguments that are not passed unchanged are returned
1194+
as ndarrays after removing the points or rows corresponding to
1195+
masks in any of the arguments.
1196+
1197+
A vastly simpler version of this function was originally
1198+
written as a helper for Axes.scatter().
11841199
1185-
Written as a helper for scatter, but may be more generally
1186-
useful.
11871200
"""
1188-
masks = [ma.getmaskarray(x) for x in args if hasattr(x, 'mask')]
1189-
isfinite = [np.isfinite(x) for x in args]
1190-
masks.extend( [~x for x in isfinite if not isinstance(x,types.NotImplementedType)] )
1191-
if len(masks) == 0:
1192-
return args
1193-
mask = reduce(np.logical_or, masks)
1201+
if not len(args):
1202+
return ()
1203+
if (is_string_like(args[0]) or not iterable(args[0])):
1204+
raise ValueError("First argument must be a sequence")
1205+
nrecs = len(args[0])
11941206
margs = []
1195-
for x in args:
1196-
if (not is_string_like(x)
1197-
and iterable(x)
1198-
and len(x) == len(mask)):
1199-
if (hasattr(x, 'get_compressed_copy')):
1200-
compressed_x = x.get_compressed_copy(mask)
1207+
seqlist = [False] * len(args)
1208+
for i, x in enumerate(args):
1209+
if (not is_string_like(x)) and iterable(x) and len(x) == nrecs:
1210+
seqlist[i] = True
1211+
if ma.isMA(x):
1212+
if x.ndim > 1:
1213+
raise ValueError("Masked arrays must be 1-D")
12011214
else:
1202-
compressed_x = ma.masked_array(x, mask=mask).compressed()
1203-
margs.append(compressed_x)
1204-
else:
1205-
margs.append(x)
1215+
x = np.asarray(x)
1216+
margs.append(x)
1217+
masks = [] # list of masks that are True where good
1218+
for i, x in enumerate(margs):
1219+
if seqlist[i]:
1220+
if x.ndim > 1:
1221+
continue # Don't try to get nan locations unless 1-D.
1222+
if ma.isMA(x):
1223+
masks.append(~ma.getmaskarray(x)) # invert the mask
1224+
xd = x.data
1225+
else:
1226+
xd = x
1227+
try:
1228+
mask = np.isfinite(xd)
1229+
if isinstance(mask, np.ndarray):
1230+
masks.append(mask)
1231+
except: #Fixme: put in tuple of possible exceptions?
1232+
pass
1233+
if len(masks):
1234+
mask = reduce(np.logical_and, masks)
1235+
igood = mask.nonzero()[0]
1236+
if len(igood) < nrecs:
1237+
for i, x in enumerate(margs):
1238+
if seqlist[i]:
1239+
if (hasattr(x, 'get_compressed_copy')):
1240+
compressed_x = x.get_compressed_copy(~mask)
1241+
else:
1242+
compressed_x = x.take(igood, axis=0)
1243+
margs[i] = compressed_x
1244+
for i, x in enumerate(margs):
1245+
if seqlist[i] and ma.isMA(x):
1246+
margs[i] = x.filled()
12061247
return margs
12071248

12081249

lib/matplotlib/collections.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def __init__(self,
9090
self._uniform_offsets = None
9191
self._offsets = np.array([], np.float_)
9292
if offsets is not None:
93-
offsets = np.asarray(offsets, np.float_)
93+
offsets = np.asarray(offsets)
9494
if len(offsets.shape) == 1:
9595
offsets = offsets[np.newaxis,:] # Make it Nx2.
9696
if transOffset is not None:

unit/cbook_unit.py

+45-55
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,51 @@
11
import unittest
2+
from datetime import datetime
3+
24
import numpy as np
35
import matplotlib.cbook as cbook
4-
5-
class TestAxes(unittest.TestCase):
6-
def test_delete_masked_points_arrays(self):
7-
input = ( [1,2,3,np.nan,5],
8-
np.array((1,2,3,4,5)),
9-
)
10-
expected = [np.array((1,2,3,5))]*2
11-
actual = cbook.delete_masked_points(*input)
12-
assert np.allclose(actual, expected)
13-
14-
input = ( np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ),
15-
np.array((1,2,3,4,5)),
16-
)
17-
expected = [np.array((1,2,3,5))]*2
18-
actual = cbook.delete_masked_points(*input)
19-
assert np.allclose(actual, expected)
20-
21-
input = ( [1,2,3,np.nan,5],
22-
np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ),
23-
np.array((1,2,3,4,5)),
24-
)
25-
expected = [np.array((1,2,3,5))]*3
26-
actual = cbook.delete_masked_points(*input)
27-
assert np.allclose(actual, expected)
28-
29-
input = ()
30-
expected = ()
31-
actual = cbook.delete_masked_points(*input)
32-
assert np.allclose(actual, expected)
33-
34-
35-
input = ( [1,2,3,np.nan,5],
36-
)
37-
expected = [np.array((1,2,3,5))]*1
38-
actual = cbook.delete_masked_points(*input)
39-
assert np.allclose(actual, expected)
40-
41-
input = ( np.array((1,2,3,4,5)),
42-
)
43-
expected = [np.array((1,2,3,4,5))]*1
44-
actual = cbook.delete_masked_points(*input)
45-
assert np.allclose(actual, expected)
46-
47-
def test_delete_masked_points_strings(self):
48-
input = ( 'hello',
49-
)
50-
expected = ('hello',)
51-
actual = cbook.delete_masked_points(*input)
52-
assert actual == expected
53-
54-
input = ( u'hello',
55-
)
56-
expected = (u'hello',)
57-
actual = cbook.delete_masked_points(*input)
58-
assert actual == expected
6+
import matplotlib.colors as mcolors
7+
8+
from matplotlib.cbook import delete_masked_points as dmp
9+
10+
class Test_delete_masked_points(unittest.TestCase):
11+
def setUp(self):
12+
self.mask1 = [False, False, True, True, False, False]
13+
self.arr0 = np.arange(1.0,7.0)
14+
self.arr1 = [1,2,3,np.nan,np.nan,6]
15+
self.arr2 = np.array(self.arr1)
16+
self.arr3 = np.ma.array(self.arr2, mask=self.mask1)
17+
self.arr_s = ['a', 'b', 'c', 'd', 'e', 'f']
18+
self.arr_s2 = np.array(self.arr_s)
19+
self.arr_dt = [datetime(2008, 1, 1), datetime(2008, 1, 2),
20+
datetime(2008, 1, 3), datetime(2008, 1, 4),
21+
datetime(2008, 1, 5), datetime(2008, 1, 6)]
22+
self.arr_dt2 = np.array(self.arr_dt)
23+
self.arr_colors = ['r', 'g', 'b', 'c', 'm', 'y']
24+
self.arr_rgba = mcolors.colorConverter.to_rgba_array(self.arr_colors)
25+
26+
def test_bad_first_arg(self):
27+
self.assertRaises(ValueError, dmp, 'a string', self.arr0)
28+
29+
def test_string_seq(self):
30+
actual = dmp(self.arr_s, self.arr1)
31+
ind = [0, 1, 2, 5]
32+
expected = (self.arr_s2.take(ind), self.arr2.take(ind))
33+
34+
def test_datetime(self):
35+
actual = dmp(self.arr_dt, self.arr3)
36+
ind = [0, 1, 5]
37+
expected = (self.arr_dt2.take(ind),
38+
self.arr3.take(ind).compressed())
39+
self.assert_(np.all(actual[0] == expected[0]) and
40+
np.all(actual[1] == expected[1]))
41+
42+
def test_rgba(self):
43+
actual = dmp(self.arr3, self.arr_rgba)
44+
ind = [0, 1, 5]
45+
expected = (self.arr3.take(ind).compressed(),
46+
self.arr_rgba.take(ind, axis=0))
47+
self.assert_(np.all(actual[0] == expected[0]) and
48+
np.all(actual[1] == expected[1]))
5949

6050

6151
if __name__=='__main__':

0 commit comments

Comments
 (0)