|
32 | 32 | importnumpyasnp
|
33 | 33 | importnumpy._core.umathasumath
|
34 | 34 | importnumpy._core.numerictypesasntypes
|
35 |
| -fromnumpy._coreimportmultiarrayasmu |
| 35 | +fromnumpy._coreimportmultiarrayasmu,arrayprint |
36 | 36 | fromnumpyimportndarray,amax,amin,iscomplexobj,bool_,_NoValue,angle
|
37 | 37 | fromnumpyimportarrayasnarray,expand_dims,iinfo,finfo
|
38 | 38 | fromnumpy._core.numericimportnormalize_axis_tuple
|
@@ -4082,8 +4082,207 @@ def _insert_masked_print(self):
|
4082 | 4082 | res=self.filled(self.fill_value)
|
4083 | 4083 | returnres
|
4084 | 4084 |
|
| 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 | + |
4085 | 4272 | 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) |
4087 | 4286 |
|
4088 | 4287 | def__repr__(self):
|
4089 | 4288 | """
|
|