summaryrefslogtreecommitdiff
path: root/numpy/ma
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2017-11-19 01:25:10 -0800
committerEric Wieser <wieser.eric@gmail.com>2017-11-20 01:02:38 -0800
commit8bac6eed123c68560ffdb5d7f7093ad4015a9d85 (patch)
tree44411e186f6fe0f3000dafcf58a39ff3441fdbfd /numpy/ma
parent18f5436641ef0a288639d81d53144706ebcd7cd3 (diff)
downloadnumpy-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.py82
-rw-r--r--numpy/ma/tests/test_core.py90
-rw-r--r--numpy/ma/tests/test_subclassing.py2
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"""