-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
ENH: Various improvements to Maskedarray repr #9792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 @@ def __repr__(self): | |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Plain ndarrays also have a special case for size-0, more than 1 dimension: >>> np.empty((0,1))
array([], shape=(0, 1), dtype=float64) But I'm a bit ambivalent about that behavior since shape isn't a valid Compare to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just checked, that behavior goes back to before numpy existed! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An idea: Maybe print There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's pretty neat. I'm on board. I'll try it out as an extra commit in the newline PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't - I want to get the newline PR in as is so that I can rebase this and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or actually, we can try in a new PR to avoid cluttering things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh ok I didn't see your reply. Agreed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can always create a new PR with common commits with the old PR - if you do that, and the old PR is merged, then you can avoid having to rebase by switching the "base branch" twice in the github UI. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect that some rebasing will be needed anyway since you'll touch the typename code... we'll see. |
||
) | ||
|
||
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
import operator | ||
import itertools | ||
import sys | ||
import textwrap | ||
from functools import reduce | ||
|
||
|
||
|
@@ -487,19 +488,96 @@ def test_deepcopy(self): | |
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)''') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Latest revision changed this case. |
||
) | ||
|
||
# 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) | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like it might belong at
dtype.__format__
in future, but for now I just wanted to avoid code duplicationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is also duplicate from arrayprint.py, I was just modifying it in #10032. Maybe I can rebase than on this PR and we can move the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused - this file is
arrayprint
- where is the other duplicate?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want me to split off a PR with just the first two commits, which we can merge first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry I thought this was
ma/core.py
. This change is fine.