Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7faa07e

Browse files
BUG: Respect print options in masked array formatting
Previously, the string representation of masked arrays did not honorglobal print options such as floatmode, precision, and suppress.This led to inconsistencies between the display of masked andunmasked arrays.This update addresses the issue by:– Utilizing np.array2string to format the underlying data, ensuring consistency with global print settings.– Replacing masked elements with the masked_print_option string ('--' by default) while preserving formatting for visible elements.– Maintaining support for legacy formatting options, including truncation with ellipsis (...) when legacy='1.13' is set.(Fixes#28685)
1 parent8f11c7b commit7faa07e

File tree

2 files changed

+262
-11
lines changed

2 files changed

+262
-11
lines changed

‎numpy/ma/core.py

Lines changed: 201 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
importnumpyasnp
3333
importnumpy._core.umathasumath
3434
importnumpy._core.numerictypesasntypes
35-
fromnumpy._coreimportmultiarrayasmu
35+
fromnumpy._coreimportmultiarrayasmu,arrayprint
3636
fromnumpyimportndarray,amax,amin,iscomplexobj,bool_,_NoValue,angle
3737
fromnumpyimportarrayasnarray,expand_dims,iinfo,finfo
3838
fromnumpy._core.numericimportnormalize_axis_tuple
@@ -4082,8 +4082,207 @@ def _insert_masked_print(self):
40824082
res=self.filled(self.fill_value)
40834083
returnres
40844084

4085+
def_flatten(self,x):
4086+
# prevent infinite recursion
4087+
ifisinstance(x,np.matrix):
4088+
x=np.asarray(x)
4089+
4090+
# handle structured scalar (np.void)
4091+
ifisinstance(x,np.void):
4092+
return [itemfornameinx.dtype.namesforiteminself._flatten(x[name])]
4093+
4094+
# handle 0d structured arrays (e.g., array(..., dtype=[(...)]) )
4095+
ifisinstance(x,np.ndarray)andx.ndim==0andx.dtype.names:
4096+
returnself._flatten(x.item())
4097+
4098+
# handle masked scalars and arrays
4099+
ifisinstance(x, (MaskedArray,MaskedConstant)):
4100+
ifhasattr(x,'ndim')andx.ndim==0:
4101+
return [x.item()]
4102+
else:
4103+
return [itemforsubinxforiteminself._flatten(sub)]
4104+
4105+
# handle lists and tuples
4106+
ifisinstance(x, (list,tuple)):
4107+
return [itemforsubinxforiteminself._flatten(sub)]
4108+
4109+
# handle non-scalar ndarrays
4110+
ifisinstance(x,np.ndarray)andx.ndim>0:
4111+
return [itemforsubinxforiteminself._flatten(sub)]
4112+
4113+
# base case: scalar value
4114+
return [x]
4115+
4116+
def_replacer(self,match,data_iter,exp_format):
4117+
"""
4118+
Replace matched tokens in a string with corresponding data from an iterator.
4119+
4120+
Parameters
4121+
----------
4122+
match : re.Match
4123+
The regular expression match object containing the token to be replaced.
4124+
data_iter : iterator
4125+
An iterator providing the data to replace the matched tokens.
4126+
Returns
4127+
-------
4128+
str
4129+
The string with the matched token replaced by the appropriate data.
4130+
"""
4131+
token=match.group(0)
4132+
# handle ellipsis in legacy printing
4133+
iftoken=='...':
4134+
value=next(data_iter)# consume from iterator to keep alignment
4135+
return'...'
4136+
# compute decimal precision from token
4137+
if'.'intoken:
4138+
decimal_places=len(token.split(".")[1])
4139+
else:
4140+
decimal_places=0
4141+
4142+
width=len(token)
4143+
float_format=f"{{:>{width}.{decimal_places}f}}"
4144+
value=next(data_iter)
4145+
# handle masked values
4146+
if (
4147+
valueismaskedor
4148+
isinstance(value,_MaskedPrintOption)or
4149+
is_masked(value)
4150+
):
4151+
return'--'.center(width)
4152+
4153+
opts=np.get_printoptions()
4154+
suppress=opts.get('suppress')
4155+
precision=opts.get('precision')
4156+
4157+
ifnotsuppressandisinstance(value,float)andexp_format:
4158+
returnarrayprint.format_float_scientific(
4159+
value,
4160+
precision=precision,
4161+
min_digits=builtins.min(decimal_places,precision),
4162+
sign=False,
4163+
)
4164+
4165+
# trim trailing .0 in floats if template does so
4166+
iftoken.endswith('.')andstr(value).endswith('.0'):
4167+
returnstr(value)[:-1].rjust(width)
4168+
4169+
returnfloat_format.format(float(value))
4170+
4171+
def_list_to_string(self,template,data):
4172+
"""
4173+
Convert a data structure into a formatted string based on a template.
4174+
4175+
Parameters
4176+
----------
4177+
template : str
4178+
The string template that dictates the formatting.
4179+
data : array-like
4180+
The data to be formatted into the string.
4181+
4182+
Returns
4183+
-------
4184+
str
4185+
The formatted string representation of the data.
4186+
"""
4187+
# handle scalar object arrays
4188+
if (isinstance(data,np.ndarray)
4189+
anddata.dtype==object
4190+
anddata.shape== ()):
4191+
returnstr(data.item())
4192+
4193+
# apply truncation before flattening, if legacy and 1D MaskedArray
4194+
legacy=np.get_printoptions().get('legacy')=='1.13'
4195+
iflegacyanddata.ndim==1anddata.size>10:
4196+
head=data[:3].tolist()
4197+
tail=data[-3:].tolist()
4198+
data=head+ ['...']+tail# insert ellipsis marker in the data
4199+
4200+
flat_data=self._flatten(data)
4201+
data_iter=iter(flat_data)
4202+
# match numbers, masked token, or ellipsis
4203+
pattern= (
4204+
r"-?\d+\.\d*(?:[eE][+-]?\d+)?|"
4205+
r"-?\d+(?:[eE][+-]?\d+)?|--|\.\.\."
4206+
)
4207+
4208+
opts=np.get_printoptions()
4209+
suppress=opts.get('suppress')
4210+
precision=opts.get('precision')
4211+
legacy=opts.get('legacy')
4212+
floatmode=opts.get('floatmode')
4213+
4214+
# flatten the original masked array to extract all scalar elements
4215+
flat_data=self._flatten(self.data)
4216+
4217+
# collect all float elements to analyze formatting needs
4218+
float_values= [xforxinflat_dataifisinstance(x,float)]
4219+
4220+
# convert to a NumPy array (empty if no float values)
4221+
iffloat_values:
4222+
float_array=np.array(float_values,dtype=float)
4223+
else:
4224+
float_array=np.array([],dtype=float)
4225+
4226+
exp_format=False
4227+
ifnotsuppress:
4228+
# normalize legacy printoption to an integer (e.g., "1.13" → 113)
4229+
ifisinstance(legacy,str):
4230+
legacy_val=int(legacy.replace('.',''))
4231+
else:
4232+
legacy_val=legacy
4233+
4234+
# determine whether exponential format should be used
4235+
exp_format=arrayprint.FloatingFormat(
4236+
float_array,precision,floatmode,suppress,legacy=legacy_val
4237+
).exp_format
4238+
4239+
# prepare replacement function for regex substitution
4240+
replacer_fn=lambdamatch:self._replacer(
4241+
match,data_iter,exp_format
4242+
)
4243+
4244+
# substitute matched tokens with formatted values
4245+
returnre.sub(pattern,replacer_fn,template)
4246+
4247+
def_masked_array_to_string(self,masked_data):
4248+
"""
4249+
Process a masked array and return its string representation.
4250+
4251+
Parameters
4252+
----------
4253+
masked_data : numpy.ma.MaskedArray
4254+
The masked array to process.
4255+
4256+
Returns
4257+
-------
4258+
str
4259+
The string representation of the masked array.
4260+
"""
4261+
# get formatted string using array2string
4262+
formatted_str=np.array2string(self.data)
4263+
4264+
# if legacy mode is enabled, normalize whitespace
4265+
legacy=np.get_printoptions().get('legacy')=='1.13'
4266+
iflegacy:
4267+
formatted_str=re.sub(r"\s{2,}"," ",formatted_str)
4268+
formatted_str=re.sub(r"(?<=\[)\s+","",formatted_str)
4269+
4270+
returnself._list_to_string(formatted_str,masked_data)
4271+
40854272
def__str__(self):
4086-
returnstr(self._insert_masked_print())
4273+
# handle 0-dimensional unmasked arrays by returning the scalar value
4274+
ifself.shape== ()andnotself.mask:
4275+
returnstr(self.item())
4276+
4277+
masked_data=self._insert_masked_print()
4278+
# if the masked data has a custom __str__, use it
4279+
if (type(masked_data)isnotnp.ndarray
4280+
andhasattr(masked_data,'__str__')
4281+
andtype(masked_data).__str__isnotnp.ndarray.__str__):
4282+
returnmasked_data.__str__()
4283+
4284+
# process the array using our method
4285+
returnself._masked_array_to_string(masked_data)
40874286

40884287
def__repr__(self):
40894288
"""

‎numpy/ma/tests/test_core.py

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -852,20 +852,72 @@ def test_fancy_printoptions(self):
852852
test=array([(1, (2,3.0)), (4, (5,6.0))],
853853
mask=[(1, (0,1)), (0, (1,0))],
854854
dtype=fancydtype)
855-
control="[(--, (2, --)) (4, (--, 6.0))]"
855+
control="[(--, (2, --)) (4, (--, 6.))]"
856856
assert_equal(str(test),control)
857857

858858
# Test 0-d array with multi-dimensional dtype
859-
t_2d0=masked_array(data=(0, [[0.0,0.0,0.0],
860-
[0.0,0.0,0.0]],
861-
0.0),
862-
mask=(False, [[True,False,True],
863-
[False,False,True]],
864-
False),
865-
dtype="int, (2,3)float, float")
866-
control="(0, [[--, 0.0, --], [0.0, 0.0, --]], 0.0)"
859+
t_2d0=masked_array(data=(0, [[0.0,0.0,0.0],
860+
[0.0,0.0,0.0]],
861+
0.0),
862+
mask=(False, [[True,False,True],
863+
[False,False,True]],
864+
False),
865+
dtype="int, (2,3)float, float")
866+
control="(0, [[--, 0., --], [0., 0., --]], 0.)"
867867
assert_equal(str(t_2d0),control)
868868

869+
deftest_floatmode_printoption(self):
870+
# Test printing a masked array w/ different float modes and precise print options. Issue #28685
871+
withnp.printoptions(precision=2,suppress=True,floatmode='fixed'):
872+
# Create a masked array with a lower triangular mask
873+
mask=np.tri(5,dtype=bool)
874+
masked_array=np.ma.MaskedArray(np.ones((5,5))*0.0001,mask=mask)
875+
control= (
876+
"[[ -- 0.00 0.00 0.00 0.00]\n"
877+
" [ -- -- 0.00 0.00 0.00]\n"
878+
" [ -- -- -- 0.00 0.00]\n"
879+
" [ -- -- -- -- 0.00]\n"
880+
" [ -- -- -- -- -- ]]"
881+
)
882+
assert_equal(str(masked_array),control)
883+
884+
withnp.printoptions(precision=2,suppress=True,floatmode='unique'):
885+
control= (
886+
"[[ -- 0.0001 0.0001 0.0001 0.0001]\n"
887+
" [ -- -- 0.0001 0.0001 0.0001]\n"
888+
" [ -- -- -- 0.0001 0.0001]\n"
889+
" [ -- -- -- -- 0.0001]\n"
890+
" [ -- -- -- -- -- ]]"
891+
)
892+
assert_equal(str(masked_array),control)
893+
894+
withnp.printoptions(precision=2,suppress=False,floatmode='maxprec'):
895+
mask=np.array([
896+
[True,False,False,False],
897+
[False,True,False,False],
898+
[False,False,True,False]
899+
])
900+
data=np.array([
901+
[0.12345678,0.00001234,1.0,100.0],
902+
[0.5,0.025,0.333333,0.999999],
903+
[0.0,1.5,2.25,3.125]
904+
])
905+
masked_array=np.ma.MaskedArray(data,mask=mask)
906+
control= (
907+
"[[ -- 1.23e-05 1.00e+00 1.00e+02]\n"
908+
" [5.00e-01 -- 3.33e-01 1.00e+00]\n"
909+
" [0.00e+00 1.50e+00 -- 3.12e+00]]"
910+
)
911+
assert_equal(str(masked_array),control)
912+
913+
withnp.printoptions(precision=3,suppress=True,floatmode='maxprec_equal'):
914+
control= (
915+
"[[ -- 0.000 1.000 100.000]\n"
916+
" [ 0.500 -- 0.333 1.000]\n"
917+
" [ 0.000 1.500 -- 3.125]]"
918+
)
919+
assert_equal(str(masked_array),control)
920+
869921
deftest_flatten_structured_array(self):
870922
# Test flatten_structured_array on arrays
871923
# On ndarray

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp