diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2017-11-19 01:25:10 -0800 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2017-11-20 01:02:38 -0800 |
commit | 8bac6eed123c68560ffdb5d7f7093ad4015a9d85 (patch) | |
tree | 44411e186f6fe0f3000dafcf58a39ff3441fdbfd /numpy/ma | |
parent | 18f5436641ef0a288639d81d53144706ebcd7cd3 (diff) | |
download | numpy-8bac6eed123c68560ffdb5d7f7093ad4015a9d85.tar.gz |
ENH: Improve MaskedArray.__repr__
* Commas are now used within data and mask
* dtypes are shown for float32, int8 etc, where they previously weren't
* Wrapped fields are now correctly indented
* Spaces removed around = to match PEP8
Diffstat (limited to 'numpy/ma')
-rw-r--r-- | numpy/ma/core.py | 82 | ||||
-rw-r--r-- | numpy/ma/tests/test_core.py | 90 | ||||
-rw-r--r-- | numpy/ma/tests/test_subclassing.py | 2 |
3 files changed, 153 insertions, 21 deletions
diff --git a/numpy/ma/core.py b/numpy/ma/core.py index a10d354ee..a09ec6bdb 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -2438,7 +2438,7 @@ def _recursive_printoption(result, mask, printopt): return # For better or worse, these end in a newline -_print_templates = dict( +_legacy_print_templates = dict( long_std=textwrap.dedent("""\ masked_%(name)s(data = %(data)s, @@ -3881,23 +3881,77 @@ class MaskedArray(ndarray): else: name = self._baseclass.__name__ - is_long = self.ndim > 1 - is_structured = bool(self.dtype.names) - parameters = dict( - name=name, - nlen=" " * len(name), - data=str(self), - mask=str(self._mask), - fill=str(self.fill_value), - dtype=str(self.dtype) + # 2016-11-19: Demoted to legacy format + if np.get_printoptions()['legacy'] == '1.13': + is_long = self.ndim > 1 + parameters = dict( + name=name, + nlen=" " * len(name), + data=str(self), + mask=str(self._mask), + fill=str(self.fill_value), + dtype=str(self.dtype) + ) + is_structured = bool(self.dtype.names) + key = '{}_{}'.format( + 'long' if is_long else 'short', + 'flx' if is_structured else 'std' + ) + return _legacy_print_templates[key] % parameters + + prefix = 'masked_{}('.format(name) + + dtype_needed = ( + not np.core.arrayprint.dtype_is_implied(self.dtype) or + np.all(self.mask) or + self.size == 0 ) - key = '{}_{}'.format( - 'long' if is_long else 'short', - 'flx' if is_structured else 'std' + # determine which keyword args need to be shown + keys = ['data', 'mask', 'fill_value'] + if dtype_needed: + keys.append('dtype') + + # array has only one row (non-column) + is_one_row = builtins.all(dim == 1 for dim in self.shape[:-1]) + + # choose what to indent each keyword with + min_indent = 2 + if is_one_row: + # first key on the same line as the type, remaining keys + # aligned by equals + indents = {} + indents[keys[0]] = prefix + for k in keys[1:]: + n = builtins.max(min_indent, len(prefix + keys[0]) - len(k)) + indents[k] = ' ' * n + prefix = '' # absorbed into the first indent + else: + # each key on its own line, indented by two spaces + indents = {k: ' ' * min_indent for k in keys} + prefix = prefix + '\n' # first key on the next line + + # format the field values + reprs = {} + reprs['data'] = np.array2string( + self._insert_masked_print(), + separator=", ", + prefix=indents['data'] + 'data=') + reprs['mask'] = np.array2string( + self._mask, + separator=", ", + prefix=indents['mask'] + 'mask=') + reprs['fill_value'] = repr(self.fill_value) + if dtype_needed: + reprs['dtype'] = np.core.arrayprint.dtype_short_repr(self.dtype) + + # join keys with values and indentations + result = ',\n'.join( + '{}{}={}'.format(indents[k], k, reprs[k]) + for k in keys ) - return _print_templates[key] % parameters + return prefix + result + ')' def _delegate_binop(self, other): # This emulates the logic in diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 03de71f81..be56833fd 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -14,6 +14,7 @@ import pickle import operator import itertools import sys +import textwrap from functools import reduce @@ -487,19 +488,96 @@ class TestMaskedArray(object): def test_str_repr(self): a = array([0, 1, 2], mask=[False, True, False]) assert_equal(str(a), '[0 -- 2]') - assert_equal(repr(a), 'masked_array(data = [0 -- 2],\n' - ' mask = [False True False],\n' - ' fill_value = 999999)\n') + assert_equal( + repr(a), + textwrap.dedent('''\ + masked_array(data=[0, --, 2], + mask=[False, True, False], + fill_value=999999)''') + ) + # arrays with a continuation a = np.ma.arange(2000) a[1:50] = np.ma.masked assert_equal( repr(a), - 'masked_array(data = [0 -- -- ... 1997 1998 1999],\n' - ' mask = [False True True ... False False False],\n' - ' fill_value = 999999)\n' + textwrap.dedent('''\ + masked_array(data=[0, --, --, ..., 1997, 1998, 1999], + mask=[False, True, True, ..., False, False, False], + fill_value=999999)''') + ) + + # line-wrapped 1d arrays are correctly aligned + a = np.ma.arange(20) + assert_equal( + repr(a), + textwrap.dedent('''\ + masked_array(data=[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19], + mask=False, + fill_value=999999)''') + ) + + # 2d arrays cause wrapping + a = array([[1, 2, 3], [4, 5, 6]], dtype=np.int8) + a[1,1] = np.ma.masked + assert_equal( + repr(a), + textwrap.dedent('''\ + masked_array( + data=[[1, 2, 3], + [4, --, 6]], + mask=[[False, False, False], + [False, True, False]], + fill_value=999999, + dtype=int8)''') ) + # but not it they're a row vector + assert_equal( + repr(a[:1]), + textwrap.dedent('''\ + masked_array(data=[[1, 2, 3]], + mask=[[False, False, False]], + fill_value=999999, + dtype=int8)''') + ) + + # dtype=int is implied, so not shown + assert_equal( + repr(a.astype(int)), + textwrap.dedent('''\ + masked_array( + data=[[1, 2, 3], + [4, --, 6]], + mask=[[False, False, False], + [False, True, False]], + fill_value=999999)''') + ) + + + + def test_str_repr_legacy(self): + oldopts = np.get_printoptions() + np.set_printoptions(legacy='1.13') + try: + a = array([0, 1, 2], mask=[False, True, False]) + assert_equal(str(a), '[0 -- 2]') + assert_equal(repr(a), 'masked_array(data = [0 -- 2],\n' + ' mask = [False True False],\n' + ' fill_value = 999999)\n') + + a = np.ma.arange(2000) + a[1:50] = np.ma.masked + assert_equal( + repr(a), + 'masked_array(data = [0 -- -- ... 1997 1998 1999],\n' + ' mask = [False True True ... False False False],\n' + ' fill_value = 999999)\n' + ) + finally: + np.set_printoptions(**oldopts) + def test_0d_unicode(self): u = u'caf\xe9' utype = type(u) diff --git a/numpy/ma/tests/test_subclassing.py b/numpy/ma/tests/test_subclassing.py index 5d71427ca..a54574690 100644 --- a/numpy/ma/tests/test_subclassing.py +++ b/numpy/ma/tests/test_subclassing.py @@ -343,7 +343,7 @@ class TestSubclassing(object): xsub = SubArray(x) mxsub = masked_array(xsub, mask=[True, False, True, False, False]) assert_startswith(repr(mxsub), - 'masked_{0}(data=[-- 1 -- 3 4]'.format(SubArray.__name__)) + 'masked_{0}(data=[--, 1, --, 3, 4]'.format(SubArray.__name__)) def test_subclass_str(self): """test str with subclass that has overridden str, setitem""" |