Mike,
In looking into the handling of inf and nan, I think I have found some
complexities and inefficiencies that are easily eliminated (and I have
committed some such changes; others are pending), but in the process I
have also found what I am fairly sure is a bug in the path
simplification code. It is illustrated by the attached modification of
nan_test.py. With 128 or more points in the data set, so that the
simplification is invoked, the moveto command that should jump across
the gap is getting changed to a lineto. This can be seen most easily by
applying the attached patch, which includes additional debugging
statements to pin down the incorrect command yielded by the
simplification, as well as pending changes to unify the handling of
masked arrays, nans, and infs. The bug shows up with or without this
patch, however. With the patch, it is also triggered by masked_demo.py,
which is how I first found it. (The non-debugging, or substantive,
parts of the patch are included here for your review or discussion as a
separate matter.)
The middle part of the extra debugging output with the patch applied
when running the nan_test.py looks like this:
2 214.726000 395.178372
3 return cmd: 2
2 218.012000 387.824331
4 skip: 2 218.012000 387.824331
1 359.310000 396.688044
3 return cmd: 2
2 362.596000 403.422341
3 return cmd: 2
The line starting with "1" is the moveto command and coordinates yielded
by the c++ path iterator; the following line is showing that the
corresponding command yielded by the simplification code is instead "2",
and that it is being returned at a location I have called "3". All this
will make sense only when you look at the patched code.
Eric
#!/usr/bin/env python
"""
Example: simple line plots with NaNs inserted.
"""
import matplotlib
#matplotlib.use('pdf')
from pylab import *
import numpy as np
t = arange(0.0, 1.0+0.01, 0.00795)
nt = len(t)
sl1 = slice(nt/2-nt/6, nt/2, 1)
sl2 = slice(nt/2, nt/2+nt/6, 1)
s = cos(2*2*pi*t)
t[sl1] = NaN
t[sl2] = np.inf
#t = np.ma.masked_invalid(t)
subplot(2,1,1)
plot(t, s, 'o', lw=4)
xlabel('time (s)')
ylabel('voltage (mV)')
title('A sine wave with a gap of NaNs between 0.4 and 0.6')
grid(True)
subplot(2,1,2)
t[0] = NaN
t[-1] = NaN
plot(t, s, '-', lw=4)
title('Also with NaN in first and last point')
xlabel('time (s)')
ylabel('more nans')
grid(True)
print 'nt = ', nt
#print t.count()
show()
Index: src/agg_py_path_iterator.h
===================================================================
--- src/agg_py_path_iterator.h (revision 6015)
+++ src/agg_py_path_iterator.h (working copy)
@@ -116,7 +116,7 @@
agg::path_cmd_end_poly | agg::path_flags_close
};
-#define DEBUG_SIMPLIFY 0
+#define DEBUG_SIMPLIFY 1
template<class VertexSource>
class SimplifyPath
@@ -194,6 +194,7 @@
*y = front.y;
#if DEBUG_SIMPLIFY
printf((cmd == agg::path_cmd_move_to) ? "|" : "-");
+ fprintf(stderr, "1 return cmd: %d\n", cmd);
#endif
return cmd;
}
@@ -218,6 +219,9 @@
// array on each draw.
while ((cmd = m_source->vertex(x, y)) != agg::path_cmd_stop)
{
+#if DEBUG_SIMPLIFY
+ fprintf(stderr, "%d %f %f\n", (int) cmd, *x, *y);
+#endif
// Do any quantization if requested
if (m_quantize && agg::is_vertex(cmd))
{
@@ -236,6 +240,8 @@
#if DEBUG_SIMPLIFY
m_pushed++;
printf("|");
+ fprintf(stderr, "2 return cmd: %d\n", cmd);
+
#endif
return agg::path_cmd_move_to;
}
@@ -245,6 +251,8 @@
{
#if DEBUG_SIMPLIFY
m_skipped++;
+ fprintf(stderr, "1 skip: %d %f %f\n", (int) cmd, *x, *y);
+
#endif
continue;
}
@@ -262,6 +270,8 @@
m_clipped = true;
#if DEBUG_SIMPLIFY
m_skipped++;
+ fprintf(stderr, "2 skip: %d %f %f\n", (int) cmd, *x, *y);
+
#endif
continue;
}
@@ -295,6 +305,8 @@
m_lastWrittenY = m_minY = m_lasty;
#if DEBUG_SIMPLIFY
m_skipped++;
+ fprintf(stderr, "3 skip: %d %f %f\n", (int) cmd, *x, *y);
+
#endif
continue;
}
@@ -361,6 +373,8 @@
m_lasty = *y;
#if DEBUG_SIMPLIFY
m_skipped++;
+ fprintf(stderr, "4 skip: %d %f %f\n", (int) cmd, *x, *y);
+
#endif
continue;
}
@@ -434,6 +448,8 @@
*y = front.y;
#if DEBUG_SIMPLIFY
printf((cmd == agg::path_cmd_move_to) ? "|" : "-");
+ fprintf(stderr, "3 return cmd: %d\n", cmd);
+
#endif
return cmd;
}
Index: lib/matplotlib/path.py
===================================================================
--- lib/matplotlib/path.py (revision 6015)
+++ lib/matplotlib/path.py (working copy)
@@ -84,43 +84,22 @@
dimension.
If *codes* is None, *vertices* will be treated as a series of
- line segments. If *vertices* contains masked values, the
- resulting path will be compressed, with ``MOVETO`` codes
- inserted in the correct places to jump over the masked
- regions.
+ line segments. If *vertices* contains masked values, they will
+ be converted to nans, which are then handled correctly by the
+ iterator and all other consumers of Path instances. Specifically,
+ ``MOVETO`` codes are used to jump over masked, nan, or inf
+ values.
"""
if ma.isMaskedArray(vertices):
- is_mask = True
- mask = ma.getmask(vertices)
+ vertices = vertices.astype(np.float_).filled(np.nan)
else:
- is_mask = False
vertices = np.asarray(vertices, np.float_)
- mask = ma.nomask
if codes is not None:
codes = np.asarray(codes, self.code_type)
assert codes.ndim == 1
assert len(codes) == len(vertices)
- # The path being passed in may have masked values. However,
- # the backends (and any affine transformations in matplotlib
- # itself), are not expected to deal with masked arrays, so we
- # must remove them from the array (using compressed), and add
- # MOVETO commands to the codes array accordingly.
- if is_mask:
- if mask is not ma.nomask:
- mask1d = np.logical_or.reduce(mask, axis=1)
- gmask1d = np.invert(mask1d)
- if codes is None:
- codes = np.empty((len(vertices)), self.code_type)
- codes.fill(self.LINETO)
- codes[0] = self.MOVETO
- vertices = vertices[gmask1d].filled() # ndarray
- codes[np.roll(mask1d, 1)] = self.MOVETO
- codes = codes[gmask1d] # np.compress is much slower
- else:
- vertices = np.asarray(vertices, np.float_)
-
assert vertices.ndim == 2
assert vertices.shape[1] == 2
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel