From acfd0163a89f91069c205130590bc9f881696989 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Wed, 16 Aug 2017 21:40:58 -0600 Subject: [PATCH 1/4] BUG: Fix weird behavior with mask and units (Fixes #8908) Refactor many places that check for masked arrays, and fill the masked values with nans, with a helper to accomplish that. In the helper, replace the isinstance check with a attribute check for 'mask'. This allows libraries, like pint, that wrap mask arrays to pass the check and be converted appropriately. Also fix one spot using atleast_1d with _check_1d. --- lib/matplotlib/cbook/__init__.py | 13 ++++++++++++- lib/matplotlib/lines.py | 15 ++++----------- lib/matplotlib/path.py | 15 ++++----------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 838081e33f66..36986e1b4210 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -1966,6 +1966,17 @@ def is_math_text(s): return even_dollars +def _to_unmasked_float_array(x): + """ + Convert a sequence to a float array; if input was a masked array, masked + values are converted to nans. + """ + if hasattr(x, 'mask'): + return np.ma.asarray(x, float).filled(np.nan) + else: + return np.asarray(x, float) + + def _check_1d(x): ''' Converts a sequence of less than 1 dimension, to an array of 1 @@ -2252,7 +2263,7 @@ def index_of(y): try: return y.index.values, y.values except AttributeError: - y = np.atleast_1d(y) + y = _check_1d(y) return np.arange(y.shape[0], dtype=float), y diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 256e59c2eb2e..9367eb8fc853 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -16,7 +16,8 @@ from . import artist, colors as mcolors, docstring, rcParams from .artist import Artist, allow_rasterization from .cbook import ( - iterable, is_numlike, ls_mapper, ls_mapper_r, STEP_LOOKUP_MAP) + _to_unmasked_float_array, iterable, is_numlike, ls_mapper, ls_mapper_r, + STEP_LOOKUP_MAP) from .markers import MarkerStyle from .path import Path from .transforms import Bbox, TransformedPath, IdentityTransform @@ -648,20 +649,12 @@ def recache_always(self): def recache(self, always=False): if always or self._invalidx: xconv = self.convert_xunits(self._xorig) - if isinstance(self._xorig, np.ma.MaskedArray): - x = np.ma.asarray(xconv, float).filled(np.nan) - else: - x = np.asarray(xconv, float) - x = x.ravel() + x = _to_unmasked_float_array(xconv).ravel() else: x = self._x if always or self._invalidy: yconv = self.convert_yunits(self._yorig) - if isinstance(self._yorig, np.ma.MaskedArray): - y = np.ma.asarray(yconv, float).filled(np.nan) - else: - y = np.asarray(yconv, float) - y = y.ravel() + y = _to_unmasked_float_array(yconv).ravel() else: y = self._y diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index fcaf191dc04b..54ec0b2fa1fc 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -23,7 +23,8 @@ import numpy as np from . import _path, rcParams -from .cbook import simple_linear_interpolation, maxdict +from .cbook import (_to_unmasked_float_array, simple_linear_interpolation, + maxdict) class Path(object): @@ -129,11 +130,7 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, Makes the path behave in an immutable way and sets the vertices and codes as read-only arrays. """ - if isinstance(vertices, np.ma.MaskedArray): - vertices = vertices.astype(float).filled(np.nan) - else: - vertices = np.asarray(vertices, float) - + vertices = _to_unmasked_float_array(vertices) if (vertices.ndim != 2) or (vertices.shape[1] != 2): msg = "'vertices' must be a 2D list or array with shape Nx2" raise ValueError(msg) @@ -185,11 +182,7 @@ def _fast_from_codes_and_verts(cls, verts, codes, internals=None): """ internals = internals or {} pth = cls.__new__(cls) - if isinstance(verts, np.ma.MaskedArray): - verts = verts.astype(float).filled(np.nan) - else: - verts = np.asarray(verts, float) - pth._vertices = verts + pth._vertices = _to_unmasked_float_array(verts) pth._codes = codes pth._readonly = internals.pop('readonly', False) pth.should_simplify = internals.pop('should_simplify', True) From a90064ebac99a8a0f135a2b264de55e7d74dc3e4 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Thu, 17 Aug 2017 11:26:27 -0600 Subject: [PATCH 2/4] ENH: Add test for masked units behavior --- .../test_units/plot_masked_units.png | Bin 0 -> 8955 bytes lib/matplotlib/tests/test_units.py | 42 ++++++++++++------ 2 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_units/plot_masked_units.png diff --git a/lib/matplotlib/tests/baseline_images/test_units/plot_masked_units.png b/lib/matplotlib/tests/baseline_images/test_units/plot_masked_units.png new file mode 100644 index 0000000000000000000000000000000000000000..98a07b2654fe03eecdb03caaa8118480d2404517 GIT binary patch literal 8955 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 wQ#fqjEf!i-1lk|BO${=32j -2) & (data < 2)) + data_masked_units = Quantity(data_masked, 'meters') + + fig, ax = plt.subplots() + ax.plot(data_masked_units) From 6eb96c80d107579c45f43f6912b9ad2530869a9c Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 18 Aug 2017 17:18:06 -0600 Subject: [PATCH 3/4] TST: Add test for pint (and alike) behavior (#8910) This tests that unit libraries that wrap arrays, like pint, work properly. This adds an image test that checks current behavior, which seems to be fully correct. --- .../baseline_images/test_units/plot_pint.png | Bin 0 -> 28590 bytes lib/matplotlib/tests/test_units.py | 42 ++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_units/plot_pint.png diff --git a/lib/matplotlib/tests/baseline_images/test_units/plot_pint.png b/lib/matplotlib/tests/baseline_images/test_units/plot_pint.png new file mode 100644 index 0000000000000000000000000000000000000000..f15f81fda6f601c37435623833a2cde724c06d92 GIT binary patch literal 28590 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 Date: Wed, 23 Aug 2017 15:28:05 -0600 Subject: [PATCH 4/4] ENH: Update view limits on units change This adds a independent callback for units changes that both updates the data limits (relim) as well as the view limits (autoscale_view). --- lib/matplotlib/axes/_base.py | 17 +++++++++++++---- lib/matplotlib/tests/test_units.py | 1 - lib/mpl_toolkits/mplot3d/axes3d.py | 19 ++++++++++++++----- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 9cf460407511..eeadfc87a1e0 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -556,12 +556,12 @@ def __init__(self, fig, rect, self.update(kwargs) if self.xaxis is not None: - self._xcid = self.xaxis.callbacks.connect('units finalize', - self.relim) + self._xcid = self.xaxis.callbacks.connect( + 'units finalize', lambda: self._on_units_changed(scalex=True)) if self.yaxis is not None: - self._ycid = self.yaxis.callbacks.connect('units finalize', - self.relim) + self._ycid = self.yaxis.callbacks.connect( + 'units finalize', lambda: self._on_units_changed(scaley=True)) self.tick_params( top=rcParams['xtick.top'] and rcParams['xtick.minor.top'], @@ -1891,6 +1891,15 @@ def add_container(self, container): container.set_remove_method(lambda h: self.containers.remove(h)) return container + def _on_units_changed(self, scalex=False, scaley=False): + """ + Callback for processing changes to axis units. + + Currently forces updates of data limits and view limits. + """ + self.relim() + self.autoscale_view(scalex=scalex, scaley=scaley) + def relim(self, visible_only=False): """ Recompute the data limits based on current artists. If you want to diff --git a/lib/matplotlib/tests/test_units.py b/lib/matplotlib/tests/test_units.py index 2f497e2b923e..f72ac2c60476 100644 --- a/lib/matplotlib/tests/test_units.py +++ b/lib/matplotlib/tests/test_units.py @@ -76,7 +76,6 @@ def convert(value, unit, axis): ax.axvline(Quantity(120, 'minutes'), color='tab:green') ax.yaxis.set_units('inches') ax.xaxis.set_units('seconds') - ax.autoscale_view() assert qc.convert.called assert qc.axisinfo.called diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index f387e5a5582e..4738be075f2f 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -110,13 +110,13 @@ def __init__(self, fig, rect=None, *args, **kwargs): # func used to format z -- fall back on major formatters self.fmt_zdata = None - if zscale is not None : + if zscale is not None: self.set_zscale(zscale) - if self.zaxis is not None : - self._zcid = self.zaxis.callbacks.connect('units finalize', - self.relim) - else : + if self.zaxis is not None: + self._zcid = self.zaxis.callbacks.connect( + 'units finalize', lambda: self._on_units_changed(scalez=True)) + else: self._zcid = None self._ready = 1 @@ -307,6 +307,15 @@ def get_axis_position(self): zhigh = tc[0][2] > tc[2][2] return xhigh, yhigh, zhigh + def _on_units_changed(self, scalex=False, scaley=False, scalez=False): + """ + Callback for processing changes to axis units. + + Currently forces updates of data limits and view limits. + """ + self.relim() + self.autoscale_view(scalex=scalex, scaley=scaley, scalez=scalez) + def update_datalim(self, xys, **kwargs): pass