| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779 |
- """Array printing function
- $Id: arrayprint.py,v 1.9 2005/09/13 13:58:44 teoliphant Exp $
- """
- __all__ = ["array2string", "array_str", "array_repr",
- "set_printoptions", "get_printoptions", "printoptions",
- "format_float_positional", "format_float_scientific"]
- __docformat__ = 'restructuredtext'
- #
- # Written by Konrad Hinsen <hinsenk@ere.umontreal.ca>
- # last revision: 1996-3-13
- # modified by Jim Hugunin 1997-3-3 for repr's and str's (and other details)
- # and by Perry Greenfield 2000-4-1 for numarray
- # and by Travis Oliphant 2005-8-22 for numpy
- # Note: Both scalartypes.c.src and arrayprint.py implement strs for numpy
- # scalars but for different purposes. scalartypes.c.src has str/reprs for when
- # the scalar is printed on its own, while arrayprint.py has strs for when
- # scalars are printed inside an ndarray. Only the latter strs are currently
- # user-customizable.
- import functools
- import numbers
- import sys
- try:
- from _thread import get_ident
- except ImportError:
- from _dummy_thread import get_ident
- import contextlib
- import operator
- import warnings
- import numpy as np
- from . import numerictypes as _nt
- from .fromnumeric import any
- from .multiarray import (
- array,
- datetime_as_string,
- datetime_data,
- dragon4_positional,
- dragon4_scientific,
- ndarray,
- )
- from .numeric import asarray, concatenate, errstate
- from .numerictypes import complex128, flexible, float64, int_
- from .overrides import array_function_dispatch, set_module
- from .printoptions import format_options
- from .umath import absolute, isfinite, isinf, isnat
- def _make_options_dict(precision=None, threshold=None, edgeitems=None,
- linewidth=None, suppress=None, nanstr=None, infstr=None,
- sign=None, formatter=None, floatmode=None, legacy=None,
- override_repr=None):
- """
- Make a dictionary out of the non-None arguments, plus conversion of
- *legacy* and sanity checks.
- """
- options = {k: v for k, v in list(locals().items()) if v is not None}
- if suppress is not None:
- options['suppress'] = bool(suppress)
- modes = ['fixed', 'unique', 'maxprec', 'maxprec_equal']
- if floatmode not in modes + [None]:
- raise ValueError("floatmode option must be one of " +
- ", ".join(f'"{m}"' for m in modes))
- if sign not in [None, '-', '+', ' ']:
- raise ValueError("sign option must be one of ' ', '+', or '-'")
- if legacy is False:
- options['legacy'] = sys.maxsize
- elif legacy == False: # noqa: E712
- warnings.warn(
- f"Passing `legacy={legacy!r}` is deprecated.",
- FutureWarning, stacklevel=3
- )
- options['legacy'] = sys.maxsize
- elif legacy == '1.13':
- options['legacy'] = 113
- elif legacy == '1.21':
- options['legacy'] = 121
- elif legacy == '1.25':
- options['legacy'] = 125
- elif legacy == '2.1':
- options['legacy'] = 201
- elif legacy == '2.2':
- options['legacy'] = 202
- elif legacy is None:
- pass # OK, do nothing.
- else:
- warnings.warn(
- "legacy printing option can currently only be '1.13', '1.21', "
- "'1.25', '2.1', '2.2' or `False`", stacklevel=3)
- if threshold is not None:
- # forbid the bad threshold arg suggested by stack overflow, gh-12351
- if not isinstance(threshold, numbers.Number):
- raise TypeError("threshold must be numeric")
- if np.isnan(threshold):
- raise ValueError("threshold must be non-NAN, try "
- "sys.maxsize for untruncated representation")
- if precision is not None:
- # forbid the bad precision arg as suggested by issue #18254
- try:
- options['precision'] = operator.index(precision)
- except TypeError as e:
- raise TypeError('precision must be an integer') from e
- return options
- @set_module('numpy')
- def set_printoptions(precision=None, threshold=None, edgeitems=None,
- linewidth=None, suppress=None, nanstr=None,
- infstr=None, formatter=None, sign=None, floatmode=None,
- *, legacy=None, override_repr=None):
- """
- Set printing options.
- These options determine the way floating point numbers, arrays and
- other NumPy objects are displayed.
- Parameters
- ----------
- precision : int or None, optional
- Number of digits of precision for floating point output (default 8).
- May be None if `floatmode` is not `fixed`, to print as many digits as
- necessary to uniquely specify the value.
- threshold : int, optional
- Total number of array elements which trigger summarization
- rather than full repr (default 1000).
- To always use the full repr without summarization, pass `sys.maxsize`.
- edgeitems : int, optional
- Number of array items in summary at beginning and end of
- each dimension (default 3).
- linewidth : int, optional
- The number of characters per line for the purpose of inserting
- line breaks (default 75).
- suppress : bool, optional
- If True, always print floating point numbers using fixed point
- notation, in which case numbers equal to zero in the current precision
- will print as zero. If False, then scientific notation is used when
- absolute value of the smallest number is < 1e-4 or the ratio of the
- maximum absolute value to the minimum is > 1e3. The default is False.
- nanstr : str, optional
- String representation of floating point not-a-number (default nan).
- infstr : str, optional
- String representation of floating point infinity (default inf).
- sign : string, either '-', '+', or ' ', optional
- Controls printing of the sign of floating-point types. If '+', always
- print the sign of positive values. If ' ', always prints a space
- (whitespace character) in the sign position of positive values. If
- '-', omit the sign character of positive values. (default '-')
- .. versionchanged:: 2.0
- The sign parameter can now be an integer type, previously
- types were floating-point types.
- formatter : dict of callables, optional
- If not None, the keys should indicate the type(s) that the respective
- formatting function applies to. Callables should return a string.
- Types that are not specified (by their corresponding keys) are handled
- by the default formatters. Individual types for which a formatter
- can be set are:
- - 'bool'
- - 'int'
- - 'timedelta' : a `numpy.timedelta64`
- - 'datetime' : a `numpy.datetime64`
- - 'float'
- - 'longfloat' : 128-bit floats
- - 'complexfloat'
- - 'longcomplexfloat' : composed of two 128-bit floats
- - 'numpystr' : types `numpy.bytes_` and `numpy.str_`
- - 'object' : `np.object_` arrays
- Other keys that can be used to set a group of types at once are:
- - 'all' : sets all types
- - 'int_kind' : sets 'int'
- - 'float_kind' : sets 'float' and 'longfloat'
- - 'complex_kind' : sets 'complexfloat' and 'longcomplexfloat'
- - 'str_kind' : sets 'numpystr'
- floatmode : str, optional
- Controls the interpretation of the `precision` option for
- floating-point types. Can take the following values
- (default maxprec_equal):
- * 'fixed': Always print exactly `precision` fractional digits,
- even if this would print more or fewer digits than
- necessary to specify the value uniquely.
- * 'unique': Print the minimum number of fractional digits necessary
- to represent each value uniquely. Different elements may
- have a different number of digits. The value of the
- `precision` option is ignored.
- * 'maxprec': Print at most `precision` fractional digits, but if
- an element can be uniquely represented with fewer digits
- only print it with that many.
- * 'maxprec_equal': Print at most `precision` fractional digits,
- but if every element in the array can be uniquely
- represented with an equal number of fewer digits, use that
- many digits for all elements.
- legacy : string or `False`, optional
- If set to the string ``'1.13'`` enables 1.13 legacy printing mode. This
- approximates numpy 1.13 print output by including a space in the sign
- position of floats and different behavior for 0d arrays. This also
- enables 1.21 legacy printing mode (described below).
- If set to the string ``'1.21'`` enables 1.21 legacy printing mode. This
- approximates numpy 1.21 print output of complex structured dtypes
- by not inserting spaces after commas that separate fields and after
- colons.
- If set to ``'1.25'`` approximates printing of 1.25 which mainly means
- that numeric scalars are printed without their type information, e.g.
- as ``3.0`` rather than ``np.float64(3.0)``.
- If set to ``'2.1'``, shape information is not given when arrays are
- summarized (i.e., multiple elements replaced with ``...``).
- If set to ``'2.2'``, the transition to use scientific notation for
- printing ``np.float16`` and ``np.float32`` types may happen later or
- not at all for larger values.
- If set to `False`, disables legacy mode.
- Unrecognized strings will be ignored with a warning for forward
- compatibility.
- .. versionchanged:: 1.22.0
- .. versionchanged:: 2.2
- override_repr: callable, optional
- If set a passed function will be used for generating arrays' repr.
- Other options will be ignored.
- See Also
- --------
- get_printoptions, printoptions, array2string
- Notes
- -----
- * ``formatter`` is always reset with a call to `set_printoptions`.
- * Use `printoptions` as a context manager to set the values temporarily.
- * These print options apply only to NumPy ndarrays, not to scalars.
- **Concurrency note:** see :ref:`text_formatting_options`
- Examples
- --------
- Floating point precision can be set:
- >>> import numpy as np
- >>> np.set_printoptions(precision=4)
- >>> np.array([1.123456789])
- [1.1235]
- Long arrays can be summarised:
- >>> np.set_printoptions(threshold=5)
- >>> np.arange(10)
- array([0, 1, 2, ..., 7, 8, 9], shape=(10,))
- Small results can be suppressed:
- >>> eps = np.finfo(float).eps
- >>> x = np.arange(4.)
- >>> x**2 - (x + eps)**2
- array([-4.9304e-32, -4.4409e-16, 0.0000e+00, 0.0000e+00])
- >>> np.set_printoptions(suppress=True)
- >>> x**2 - (x + eps)**2
- array([-0., -0., 0., 0.])
- A custom formatter can be used to display array elements as desired:
- >>> np.set_printoptions(formatter={'all':lambda x: 'int: '+str(-x)})
- >>> x = np.arange(3)
- >>> x
- array([int: 0, int: -1, int: -2])
- >>> np.set_printoptions() # formatter gets reset
- >>> x
- array([0, 1, 2])
- To put back the default options, you can use:
- >>> np.set_printoptions(edgeitems=3, infstr='inf',
- ... linewidth=75, nanstr='nan', precision=8,
- ... suppress=False, threshold=1000, formatter=None)
- Also to temporarily override options, use `printoptions`
- as a context manager:
- >>> with np.printoptions(precision=2, suppress=True, threshold=5):
- ... np.linspace(0, 10, 10)
- array([ 0. , 1.11, 2.22, ..., 7.78, 8.89, 10. ], shape=(10,))
- """
- _set_printoptions(precision, threshold, edgeitems, linewidth, suppress,
- nanstr, infstr, formatter, sign, floatmode,
- legacy=legacy, override_repr=override_repr)
- def _set_printoptions(precision=None, threshold=None, edgeitems=None,
- linewidth=None, suppress=None, nanstr=None,
- infstr=None, formatter=None, sign=None, floatmode=None,
- *, legacy=None, override_repr=None):
- new_opt = _make_options_dict(precision, threshold, edgeitems, linewidth,
- suppress, nanstr, infstr, sign, formatter,
- floatmode, legacy)
- # formatter and override_repr are always reset
- new_opt['formatter'] = formatter
- new_opt['override_repr'] = override_repr
- updated_opt = format_options.get() | new_opt
- updated_opt.update(new_opt)
- if updated_opt['legacy'] == 113:
- updated_opt['sign'] = '-'
- return format_options.set(updated_opt)
- @set_module('numpy')
- def get_printoptions():
- """
- Return the current print options.
- Returns
- -------
- print_opts : dict
- Dictionary of current print options with keys
- - precision : int
- - threshold : int
- - edgeitems : int
- - linewidth : int
- - suppress : bool
- - nanstr : str
- - infstr : str
- - sign : str
- - formatter : dict of callables
- - floatmode : str
- - legacy : str or False
- For a full description of these options, see `set_printoptions`.
- Notes
- -----
- These print options apply only to NumPy ndarrays, not to scalars.
- **Concurrency note:** see :ref:`text_formatting_options`
- See Also
- --------
- set_printoptions, printoptions
- Examples
- --------
- >>> import numpy as np
- >>> np.get_printoptions()
- {'edgeitems': 3, 'threshold': 1000, ..., 'override_repr': None}
- >>> np.get_printoptions()['linewidth']
- 75
- >>> np.set_printoptions(linewidth=100)
- >>> np.get_printoptions()['linewidth']
- 100
- """
- opts = format_options.get().copy()
- opts['legacy'] = {
- 113: '1.13', 121: '1.21', 125: '1.25', 201: '2.1',
- 202: '2.2', sys.maxsize: False,
- }[opts['legacy']]
- return opts
- def _get_legacy_print_mode():
- """Return the legacy print mode as an int."""
- return format_options.get()['legacy']
- @set_module('numpy')
- @contextlib.contextmanager
- def printoptions(*args, **kwargs):
- """Context manager for setting print options.
- Set print options for the scope of the `with` block, and restore the old
- options at the end. See `set_printoptions` for the full description of
- available options.
- Examples
- --------
- >>> import numpy as np
- >>> from numpy.testing import assert_equal
- >>> with np.printoptions(precision=2):
- ... np.array([2.0]) / 3
- array([0.67])
- The `as`-clause of the `with`-statement gives the current print options:
- >>> with np.printoptions(precision=2) as opts:
- ... assert_equal(opts, np.get_printoptions())
- See Also
- --------
- set_printoptions, get_printoptions
- Notes
- -----
- These print options apply only to NumPy ndarrays, not to scalars.
- **Concurrency note:** see :ref:`text_formatting_options`
- """
- token = _set_printoptions(*args, **kwargs)
- try:
- yield get_printoptions()
- finally:
- format_options.reset(token)
- def _leading_trailing(a, edgeitems, index=()):
- """
- Keep only the N-D corners (leading and trailing edges) of an array.
- Should be passed a base-class ndarray, since it makes no guarantees about
- preserving subclasses.
- """
- axis = len(index)
- if axis == a.ndim:
- return a[index]
- if a.shape[axis] > 2 * edgeitems:
- return concatenate((
- _leading_trailing(a, edgeitems, index + np.index_exp[:edgeitems]),
- _leading_trailing(a, edgeitems, index + np.index_exp[-edgeitems:])
- ), axis=axis)
- else:
- return _leading_trailing(a, edgeitems, index + np.index_exp[:])
- def _object_format(o):
- """ Object arrays containing lists should be printed unambiguously """
- if type(o) is list:
- fmt = 'list({!r})'
- else:
- fmt = '{!r}'
- return fmt.format(o)
- def repr_format(x):
- if isinstance(x, (np.str_, np.bytes_)):
- return repr(x.item())
- return repr(x)
- def str_format(x):
- if isinstance(x, (np.str_, np.bytes_)):
- return str(x.item())
- return str(x)
- def _get_formatdict(data, *, precision, floatmode, suppress, sign, legacy,
- formatter, **kwargs):
- # note: extra arguments in kwargs are ignored
- # wrapped in lambdas to avoid taking a code path
- # with the wrong type of data
- formatdict = {
- 'bool': lambda: BoolFormat(data),
- 'int': lambda: IntegerFormat(data, sign),
- 'float': lambda: FloatingFormat(
- data, precision, floatmode, suppress, sign, legacy=legacy),
- 'longfloat': lambda: FloatingFormat(
- data, precision, floatmode, suppress, sign, legacy=legacy),
- 'complexfloat': lambda: ComplexFloatingFormat(
- data, precision, floatmode, suppress, sign, legacy=legacy),
- 'longcomplexfloat': lambda: ComplexFloatingFormat(
- data, precision, floatmode, suppress, sign, legacy=legacy),
- 'datetime': lambda: DatetimeFormat(data, legacy=legacy),
- 'timedelta': lambda: TimedeltaFormat(data),
- 'object': lambda: _object_format,
- 'void': lambda: str_format,
- 'numpystr': lambda: repr_format}
- # we need to wrap values in `formatter` in a lambda, so that the interface
- # is the same as the above values.
- def indirect(x):
- return lambda: x
- if formatter is not None:
- fkeys = [k for k in formatter.keys() if formatter[k] is not None]
- if 'all' in fkeys:
- for key in formatdict.keys():
- formatdict[key] = indirect(formatter['all'])
- if 'int_kind' in fkeys:
- for key in ['int']:
- formatdict[key] = indirect(formatter['int_kind'])
- if 'float_kind' in fkeys:
- for key in ['float', 'longfloat']:
- formatdict[key] = indirect(formatter['float_kind'])
- if 'complex_kind' in fkeys:
- for key in ['complexfloat', 'longcomplexfloat']:
- formatdict[key] = indirect(formatter['complex_kind'])
- if 'str_kind' in fkeys:
- formatdict['numpystr'] = indirect(formatter['str_kind'])
- for key in formatdict.keys():
- if key in fkeys:
- formatdict[key] = indirect(formatter[key])
- return formatdict
- def _get_format_function(data, **options):
- """
- find the right formatting function for the dtype_
- """
- dtype_ = data.dtype
- dtypeobj = dtype_.type
- formatdict = _get_formatdict(data, **options)
- if dtypeobj is None:
- return formatdict["numpystr"]()
- elif issubclass(dtypeobj, _nt.bool):
- return formatdict['bool']()
- elif issubclass(dtypeobj, _nt.integer):
- if issubclass(dtypeobj, _nt.timedelta64):
- return formatdict['timedelta']()
- else:
- return formatdict['int']()
- elif issubclass(dtypeobj, _nt.floating):
- if issubclass(dtypeobj, _nt.longdouble):
- return formatdict['longfloat']()
- else:
- return formatdict['float']()
- elif issubclass(dtypeobj, _nt.complexfloating):
- if issubclass(dtypeobj, _nt.clongdouble):
- return formatdict['longcomplexfloat']()
- else:
- return formatdict['complexfloat']()
- elif issubclass(dtypeobj, (_nt.str_, _nt.bytes_)):
- return formatdict['numpystr']()
- elif issubclass(dtypeobj, _nt.datetime64):
- return formatdict['datetime']()
- elif issubclass(dtypeobj, _nt.object_):
- return formatdict['object']()
- elif issubclass(dtypeobj, _nt.void):
- if dtype_.names is not None:
- return StructuredVoidFormat.from_data(data, **options)
- else:
- return formatdict['void']()
- else:
- return formatdict['numpystr']()
- def _recursive_guard(fillvalue='...'):
- """
- Like the python 3.2 reprlib.recursive_repr, but forwards *args and **kwargs
- Decorates a function such that if it calls itself with the same first
- argument, it returns `fillvalue` instead of recursing.
- Largely copied from reprlib.recursive_repr
- """
- def decorating_function(f):
- repr_running = set()
- @functools.wraps(f)
- def wrapper(self, *args, **kwargs):
- key = id(self), get_ident()
- if key in repr_running:
- return fillvalue
- repr_running.add(key)
- try:
- return f(self, *args, **kwargs)
- finally:
- repr_running.discard(key)
- return wrapper
- return decorating_function
- # gracefully handle recursive calls, when object arrays contain themselves
- @_recursive_guard()
- def _array2string(a, options, separator=' ', prefix=""):
- # The formatter __init__s in _get_format_function cannot deal with
- # subclasses yet, and we also need to avoid recursion issues in
- # _formatArray with subclasses which return 0d arrays in place of scalars
- data = asarray(a)
- if a.shape == ():
- a = data
- if a.size > options['threshold']:
- summary_insert = "..."
- data = _leading_trailing(data, options['edgeitems'])
- else:
- summary_insert = ""
- # find the right formatting function for the array
- format_function = _get_format_function(data, **options)
- # skip over "["
- next_line_prefix = " "
- # skip over array(
- next_line_prefix += " " * len(prefix)
- lst = _formatArray(a, format_function, options['linewidth'],
- next_line_prefix, separator, options['edgeitems'],
- summary_insert, options['legacy'])
- return lst
- def _array2string_dispatcher(
- a, max_line_width=None, precision=None,
- suppress_small=None, separator=None, prefix=None,
- *, formatter=None, threshold=None,
- edgeitems=None, sign=None, floatmode=None, suffix=None,
- legacy=None):
- return (a,)
- @array_function_dispatch(_array2string_dispatcher, module='numpy')
- def array2string(a, max_line_width=None, precision=None,
- suppress_small=None, separator=' ', prefix="",
- *, formatter=None, threshold=None,
- edgeitems=None, sign=None, floatmode=None, suffix="",
- legacy=None):
- """
- Return a string representation of an array.
- Parameters
- ----------
- a : ndarray
- Input array.
- max_line_width : int, optional
- Inserts newlines if text is longer than `max_line_width`.
- Defaults to ``numpy.get_printoptions()['linewidth']``.
- precision : int or None, optional
- Floating point precision.
- Defaults to ``numpy.get_printoptions()['precision']``.
- suppress_small : bool, optional
- Represent numbers "very close" to zero as zero; default is False.
- Very close is defined by precision: if the precision is 8, e.g.,
- numbers smaller (in absolute value) than 5e-9 are represented as
- zero.
- Defaults to ``numpy.get_printoptions()['suppress']``.
- separator : str, optional
- Inserted between elements.
- prefix : str, optional
- suffix : str, optional
- The length of the prefix and suffix strings are used to respectively
- align and wrap the output. An array is typically printed as::
- prefix + array2string(a) + suffix
- The output is left-padded by the length of the prefix string, and
- wrapping is forced at the column ``max_line_width - len(suffix)``.
- It should be noted that the content of prefix and suffix strings are
- not included in the output.
- formatter : dict of callables, optional
- If not None, the keys should indicate the type(s) that the respective
- formatting function applies to. Callables should return a string.
- Types that are not specified (by their corresponding keys) are handled
- by the default formatters. Individual types for which a formatter
- can be set are:
- - 'bool'
- - 'int'
- - 'timedelta' : a `numpy.timedelta64`
- - 'datetime' : a `numpy.datetime64`
- - 'float'
- - 'longfloat' : 128-bit floats
- - 'complexfloat'
- - 'longcomplexfloat' : composed of two 128-bit floats
- - 'void' : type `numpy.void`
- - 'numpystr' : types `numpy.bytes_` and `numpy.str_`
- Other keys that can be used to set a group of types at once are:
- - 'all' : sets all types
- - 'int_kind' : sets 'int'
- - 'float_kind' : sets 'float' and 'longfloat'
- - 'complex_kind' : sets 'complexfloat' and 'longcomplexfloat'
- - 'str_kind' : sets 'numpystr'
- threshold : int, optional
- Total number of array elements which trigger summarization
- rather than full repr.
- Defaults to ``numpy.get_printoptions()['threshold']``.
- edgeitems : int, optional
- Number of array items in summary at beginning and end of
- each dimension.
- Defaults to ``numpy.get_printoptions()['edgeitems']``.
- sign : string, either '-', '+', or ' ', optional
- Controls printing of the sign of floating-point types. If '+', always
- print the sign of positive values. If ' ', always prints a space
- (whitespace character) in the sign position of positive values. If
- '-', omit the sign character of positive values.
- Defaults to ``numpy.get_printoptions()['sign']``.
- .. versionchanged:: 2.0
- The sign parameter can now be an integer type, previously
- types were floating-point types.
- floatmode : str, optional
- Controls the interpretation of the `precision` option for
- floating-point types.
- Defaults to ``numpy.get_printoptions()['floatmode']``.
- Can take the following values:
- - 'fixed': Always print exactly `precision` fractional digits,
- even if this would print more or fewer digits than
- necessary to specify the value uniquely.
- - 'unique': Print the minimum number of fractional digits necessary
- to represent each value uniquely. Different elements may
- have a different number of digits. The value of the
- `precision` option is ignored.
- - 'maxprec': Print at most `precision` fractional digits, but if
- an element can be uniquely represented with fewer digits
- only print it with that many.
- - 'maxprec_equal': Print at most `precision` fractional digits,
- but if every element in the array can be uniquely
- represented with an equal number of fewer digits, use that
- many digits for all elements.
- legacy : string or `False`, optional
- If set to the string ``'1.13'`` enables 1.13 legacy printing mode. This
- approximates numpy 1.13 print output by including a space in the sign
- position of floats and different behavior for 0d arrays. If set to
- `False`, disables legacy mode. Unrecognized strings will be ignored
- with a warning for forward compatibility.
- Returns
- -------
- array_str : str
- String representation of the array.
- Raises
- ------
- TypeError
- if a callable in `formatter` does not return a string.
- See Also
- --------
- array_str, array_repr, set_printoptions, get_printoptions
- Notes
- -----
- If a formatter is specified for a certain type, the `precision` keyword is
- ignored for that type.
- This is a very flexible function; `array_repr` and `array_str` are using
- `array2string` internally so keywords with the same name should work
- identically in all three functions.
- Examples
- --------
- >>> import numpy as np
- >>> x = np.array([1e-16,1,2,3])
- >>> np.array2string(x, precision=2, separator=',',
- ... suppress_small=True)
- '[0.,1.,2.,3.]'
- >>> x = np.arange(3.)
- >>> np.array2string(x, formatter={'float_kind':lambda x: "%.2f" % x})
- '[0.00 1.00 2.00]'
- >>> x = np.arange(3)
- >>> np.array2string(x, formatter={'int':lambda x: hex(x)})
- '[0x0 0x1 0x2]'
- """
- overrides = _make_options_dict(precision, threshold, edgeitems,
- max_line_width, suppress_small, None, None,
- sign, formatter, floatmode, legacy)
- options = format_options.get().copy()
- options.update(overrides)
- if options['legacy'] <= 113:
- if a.shape == () and a.dtype.names is None:
- return repr(a.item())
- if options['legacy'] > 113:
- options['linewidth'] -= len(suffix)
- # treat as a null array if any of shape elements == 0
- if a.size == 0:
- return "[]"
- return _array2string(a, options, separator, prefix)
- def _extendLine(s, line, word, line_width, next_line_prefix, legacy):
- needs_wrap = len(line) + len(word) > line_width
- if legacy > 113:
- # don't wrap lines if it won't help
- if len(line) <= len(next_line_prefix):
- needs_wrap = False
- if needs_wrap:
- s += line.rstrip() + "\n"
- line = next_line_prefix
- line += word
- return s, line
- def _extendLine_pretty(s, line, word, line_width, next_line_prefix, legacy):
- """
- Extends line with nicely formatted (possibly multi-line) string ``word``.
- """
- words = word.splitlines()
- if len(words) == 1 or legacy <= 113:
- return _extendLine(s, line, word, line_width, next_line_prefix, legacy)
- max_word_length = max(len(word) for word in words)
- if (len(line) + max_word_length > line_width and
- len(line) > len(next_line_prefix)):
- s += line.rstrip() + '\n'
- line = next_line_prefix + words[0]
- indent = next_line_prefix
- else:
- indent = len(line) * ' '
- line += words[0]
- for word in words[1::]:
- s += line.rstrip() + '\n'
- line = indent + word
- suffix_length = max_word_length - len(words[-1])
- line += suffix_length * ' '
- return s, line
- def _formatArray(a, format_function, line_width, next_line_prefix,
- separator, edge_items, summary_insert, legacy):
- """formatArray is designed for two modes of operation:
- 1. Full output
- 2. Summarized output
- """
- def recurser(index, hanging_indent, curr_width):
- """
- By using this local function, we don't need to recurse with all the
- arguments. Since this function is not created recursively, the cost is
- not significant
- """
- axis = len(index)
- axes_left = a.ndim - axis
- if axes_left == 0:
- return format_function(a[index])
- # when recursing, add a space to align with the [ added, and reduce the
- # length of the line by 1
- next_hanging_indent = hanging_indent + ' '
- if legacy <= 113:
- next_width = curr_width
- else:
- next_width = curr_width - len(']')
- a_len = a.shape[axis]
- show_summary = summary_insert and 2 * edge_items < a_len
- if show_summary:
- leading_items = edge_items
- trailing_items = edge_items
- else:
- leading_items = 0
- trailing_items = a_len
- # stringify the array with the hanging indent on the first line too
- s = ''
- # last axis (rows) - wrap elements if they would not fit on one line
- if axes_left == 1:
- # the length up until the beginning of the separator / bracket
- if legacy <= 113:
- elem_width = curr_width - len(separator.rstrip())
- else:
- elem_width = curr_width - max(
- len(separator.rstrip()), len(']')
- )
- line = hanging_indent
- for i in range(leading_items):
- word = recurser(index + (i,), next_hanging_indent, next_width)
- s, line = _extendLine_pretty(
- s, line, word, elem_width, hanging_indent, legacy)
- line += separator
- if show_summary:
- s, line = _extendLine(
- s, line, summary_insert, elem_width, hanging_indent, legacy
- )
- if legacy <= 113:
- line += ", "
- else:
- line += separator
- for i in range(trailing_items, 1, -1):
- word = recurser(index + (-i,), next_hanging_indent, next_width)
- s, line = _extendLine_pretty(
- s, line, word, elem_width, hanging_indent, legacy)
- line += separator
- if legacy <= 113:
- # width of the separator is not considered on 1.13
- elem_width = curr_width
- word = recurser(index + (-1,), next_hanging_indent, next_width)
- s, line = _extendLine_pretty(
- s, line, word, elem_width, hanging_indent, legacy)
- s += line
- # other axes - insert newlines between rows
- else:
- s = ''
- line_sep = separator.rstrip() + '\n' * (axes_left - 1)
- for i in range(leading_items):
- nested = recurser(
- index + (i,), next_hanging_indent, next_width
- )
- s += hanging_indent + nested + line_sep
- if show_summary:
- if legacy <= 113:
- # trailing space, fixed nbr of newlines,
- # and fixed separator
- s += hanging_indent + summary_insert + ", \n"
- else:
- s += hanging_indent + summary_insert + line_sep
- for i in range(trailing_items, 1, -1):
- nested = recurser(index + (-i,), next_hanging_indent,
- next_width)
- s += hanging_indent + nested + line_sep
- nested = recurser(index + (-1,), next_hanging_indent, next_width)
- s += hanging_indent + nested
- # remove the hanging indent, and wrap in []
- s = '[' + s[len(hanging_indent):] + ']'
- return s
- try:
- # invoke the recursive part with an initial index and prefix
- return recurser(index=(),
- hanging_indent=next_line_prefix,
- curr_width=line_width)
- finally:
- # recursive closures have a cyclic reference to themselves, which
- # requires gc to collect (gh-10620). To avoid this problem, for
- # performance and PyPy friendliness, we break the cycle:
- recurser = None
- def _none_or_positive_arg(x, name):
- if x is None:
- return -1
- if x < 0:
- raise ValueError(f"{name} must be >= 0")
- return x
- class FloatingFormat:
- """ Formatter for subtypes of np.floating """
- def __init__(self, data, precision, floatmode, suppress_small, sign=False,
- *, legacy=None):
- # for backcompatibility, accept bools
- if isinstance(sign, bool):
- sign = '+' if sign else '-'
- self._legacy = legacy
- if self._legacy <= 113:
- # when not 0d, legacy does not support '-'
- if data.shape != () and sign == '-':
- sign = ' '
- self.floatmode = floatmode
- if floatmode == 'unique':
- self.precision = None
- else:
- self.precision = precision
- self.precision = _none_or_positive_arg(self.precision, 'precision')
- self.suppress_small = suppress_small
- self.sign = sign
- self.exp_format = False
- self.large_exponent = False
- self.fillFormat(data)
- def fillFormat(self, data):
- # only the finite values are used to compute the number of digits
- finite_vals = data[isfinite(data)]
- # choose exponential mode based on the non-zero finite values:
- abs_non_zero = absolute(finite_vals[finite_vals != 0])
- if len(abs_non_zero) != 0:
- max_val = np.max(abs_non_zero)
- min_val = np.min(abs_non_zero)
- if self._legacy <= 202:
- exp_cutoff_max = 1.e8
- else:
- # consider data type while deciding the max cutoff for exp format
- exp_cutoff_max = 10.**min(8, np.finfo(data.dtype).precision)
- with errstate(over='ignore'): # division can overflow
- if max_val >= exp_cutoff_max or (not self.suppress_small and
- (min_val < 0.0001 or max_val / min_val > 1000.)):
- self.exp_format = True
- # do a first pass of printing all the numbers, to determine sizes
- if len(finite_vals) == 0:
- self.pad_left = 0
- self.pad_right = 0
- self.trim = '.'
- self.exp_size = -1
- self.unique = True
- self.min_digits = None
- elif self.exp_format:
- trim, unique = '.', True
- if self.floatmode == 'fixed' or self._legacy <= 113:
- trim, unique = 'k', False
- strs = (dragon4_scientific(x, precision=self.precision,
- unique=unique, trim=trim, sign=self.sign == '+')
- for x in finite_vals)
- frac_strs, _, exp_strs = zip(*(s.partition('e') for s in strs))
- int_part, frac_part = zip(*(s.split('.') for s in frac_strs))
- self.exp_size = max(len(s) for s in exp_strs) - 1
- self.trim = 'k'
- self.precision = max(len(s) for s in frac_part)
- self.min_digits = self.precision
- self.unique = unique
- # for back-compat with np 1.13, use 2 spaces & sign and full prec
- if self._legacy <= 113:
- self.pad_left = 3
- else:
- # this should be only 1 or 2. Can be calculated from sign.
- self.pad_left = max(len(s) for s in int_part)
- # pad_right is only needed for nan length calculation
- self.pad_right = self.exp_size + 2 + self.precision
- else:
- trim, unique = '.', True
- if self.floatmode == 'fixed':
- trim, unique = 'k', False
- strs = (dragon4_positional(x, precision=self.precision,
- fractional=True,
- unique=unique, trim=trim,
- sign=self.sign == '+')
- for x in finite_vals)
- int_part, frac_part = zip(*(s.split('.') for s in strs))
- if self._legacy <= 113:
- self.pad_left = 1 + max(len(s.lstrip('-+')) for s in int_part)
- else:
- self.pad_left = max(len(s) for s in int_part)
- self.pad_right = max(len(s) for s in frac_part)
- self.exp_size = -1
- self.unique = unique
- if self.floatmode in ['fixed', 'maxprec_equal']:
- self.precision = self.min_digits = self.pad_right
- self.trim = 'k'
- else:
- self.trim = '.'
- self.min_digits = 0
- if self._legacy > 113:
- # account for sign = ' ' by adding one to pad_left
- if self.sign == ' ' and not any(np.signbit(finite_vals)):
- self.pad_left += 1
- # if there are non-finite values, may need to increase pad_left
- if data.size != finite_vals.size:
- neginf = self.sign != '-' or any(data[isinf(data)] < 0)
- offset = self.pad_right + 1 # +1 for decimal pt
- current_options = format_options.get()
- self.pad_left = max(
- self.pad_left, len(current_options['nanstr']) - offset,
- len(current_options['infstr']) + neginf - offset
- )
- def __call__(self, x):
- if not np.isfinite(x):
- with errstate(invalid='ignore'):
- current_options = format_options.get()
- if np.isnan(x):
- sign = '+' if self.sign == '+' else ''
- ret = sign + current_options['nanstr']
- else: # isinf
- sign = '-' if x < 0 else '+' if self.sign == '+' else ''
- ret = sign + current_options['infstr']
- return ' ' * (
- self.pad_left + self.pad_right + 1 - len(ret)
- ) + ret
- if self.exp_format:
- return dragon4_scientific(x,
- precision=self.precision,
- min_digits=self.min_digits,
- unique=self.unique,
- trim=self.trim,
- sign=self.sign == '+',
- pad_left=self.pad_left,
- exp_digits=self.exp_size)
- else:
- return dragon4_positional(x,
- precision=self.precision,
- min_digits=self.min_digits,
- unique=self.unique,
- fractional=True,
- trim=self.trim,
- sign=self.sign == '+',
- pad_left=self.pad_left,
- pad_right=self.pad_right)
- @set_module('numpy')
- def format_float_scientific(x, precision=None, unique=True, trim='k',
- sign=False, pad_left=None, exp_digits=None,
- min_digits=None):
- """
- Format a floating-point scalar as a decimal string in scientific notation.
- Provides control over rounding, trimming and padding. Uses and assumes
- IEEE unbiased rounding. Uses the "Dragon4" algorithm.
- Parameters
- ----------
- x : python float or numpy floating scalar
- Value to format.
- precision : non-negative integer or None, optional
- Maximum number of digits to print. May be None if `unique` is
- `True`, but must be an integer if unique is `False`.
- unique : boolean, optional
- If `True`, use a digit-generation strategy which gives the shortest
- representation which uniquely identifies the floating-point number from
- other values of the same type, by judicious rounding. If `precision`
- is given fewer digits than necessary can be printed. If `min_digits`
- is given more can be printed, in which cases the last digit is rounded
- with unbiased rounding.
- If `False`, digits are generated as if printing an infinite-precision
- value and stopping after `precision` digits, rounding the remaining
- value with unbiased rounding
- trim : one of 'k', '.', '0', '-', optional
- Controls post-processing trimming of trailing digits, as follows:
- * 'k' : keep trailing zeros, keep decimal point (no trimming)
- * '.' : trim all trailing zeros, leave decimal point
- * '0' : trim all but the zero before the decimal point. Insert the
- zero if it is missing.
- * '-' : trim trailing zeros and any trailing decimal point
- sign : boolean, optional
- Whether to show the sign for positive values.
- pad_left : non-negative integer, optional
- Pad the left side of the string with whitespace until at least that
- many characters are to the left of the decimal point.
- exp_digits : non-negative integer, optional
- Pad the exponent with zeros until it contains at least this
- many digits. If omitted, the exponent will be at least 2 digits.
- min_digits : non-negative integer or None, optional
- Minimum number of digits to print. This only has an effect for
- `unique=True`. In that case more digits than necessary to uniquely
- identify the value may be printed and rounded unbiased.
- .. versionadded:: 1.21.0
- Returns
- -------
- rep : string
- The string representation of the floating point value
- See Also
- --------
- format_float_positional
- Examples
- --------
- >>> import numpy as np
- >>> np.format_float_scientific(np.float32(np.pi))
- '3.1415927e+00'
- >>> s = np.float32(1.23e24)
- >>> np.format_float_scientific(s, unique=False, precision=15)
- '1.230000071797338e+24'
- >>> np.format_float_scientific(s, exp_digits=4)
- '1.23e+0024'
- """
- precision = _none_or_positive_arg(precision, 'precision')
- pad_left = _none_or_positive_arg(pad_left, 'pad_left')
- exp_digits = _none_or_positive_arg(exp_digits, 'exp_digits')
- min_digits = _none_or_positive_arg(min_digits, 'min_digits')
- if min_digits > 0 and precision > 0 and min_digits > precision:
- raise ValueError("min_digits must be less than or equal to precision")
- return dragon4_scientific(x, precision=precision, unique=unique,
- trim=trim, sign=sign, pad_left=pad_left,
- exp_digits=exp_digits, min_digits=min_digits)
- @set_module('numpy')
- def format_float_positional(x, precision=None, unique=True,
- fractional=True, trim='k', sign=False,
- pad_left=None, pad_right=None, min_digits=None):
- """
- Format a floating-point scalar as a decimal string in positional notation.
- Provides control over rounding, trimming and padding. Uses and assumes
- IEEE unbiased rounding. Uses the "Dragon4" algorithm.
- Parameters
- ----------
- x : python float or numpy floating scalar
- Value to format.
- precision : non-negative integer or None, optional
- Maximum number of digits to print. May be None if `unique` is
- `True`, but must be an integer if unique is `False`.
- unique : boolean, optional
- If `True`, use a digit-generation strategy which gives the shortest
- representation which uniquely identifies the floating-point number from
- other values of the same type, by judicious rounding. If `precision`
- is given fewer digits than necessary can be printed, or if `min_digits`
- is given more can be printed, in which cases the last digit is rounded
- with unbiased rounding.
- If `False`, digits are generated as if printing an infinite-precision
- value and stopping after `precision` digits, rounding the remaining
- value with unbiased rounding
- fractional : boolean, optional
- If `True`, the cutoffs of `precision` and `min_digits` refer to the
- total number of digits after the decimal point, including leading
- zeros.
- If `False`, `precision` and `min_digits` refer to the total number of
- significant digits, before or after the decimal point, ignoring leading
- zeros.
- trim : one of 'k', '.', '0', '-', optional
- Controls post-processing trimming of trailing digits, as follows:
- * 'k' : keep trailing zeros, keep decimal point (no trimming)
- * '.' : trim all trailing zeros, leave decimal point
- * '0' : trim all but the zero before the decimal point. Insert the
- zero if it is missing.
- * '-' : trim trailing zeros and any trailing decimal point
- sign : boolean, optional
- Whether to show the sign for positive values.
- pad_left : non-negative integer, optional
- Pad the left side of the string with whitespace until at least that
- many characters are to the left of the decimal point.
- pad_right : non-negative integer, optional
- Pad the right side of the string with whitespace until at least that
- many characters are to the right of the decimal point.
- min_digits : non-negative integer or None, optional
- Minimum number of digits to print. Only has an effect if `unique=True`
- in which case additional digits past those necessary to uniquely
- identify the value may be printed, rounding the last additional digit.
- .. versionadded:: 1.21.0
- Returns
- -------
- rep : string
- The string representation of the floating point value
- See Also
- --------
- format_float_scientific
- Examples
- --------
- >>> import numpy as np
- >>> np.format_float_positional(np.float32(np.pi))
- '3.1415927'
- >>> np.format_float_positional(np.float16(np.pi))
- '3.14'
- >>> np.format_float_positional(np.float16(0.3))
- '0.3'
- >>> np.format_float_positional(np.float16(0.3), unique=False, precision=10)
- '0.3000488281'
- """
- precision = _none_or_positive_arg(precision, 'precision')
- pad_left = _none_or_positive_arg(pad_left, 'pad_left')
- pad_right = _none_or_positive_arg(pad_right, 'pad_right')
- min_digits = _none_or_positive_arg(min_digits, 'min_digits')
- if not fractional and precision == 0:
- raise ValueError("precision must be greater than 0 if "
- "fractional=False")
- if min_digits > 0 and precision > 0 and min_digits > precision:
- raise ValueError("min_digits must be less than or equal to precision")
- return dragon4_positional(x, precision=precision, unique=unique,
- fractional=fractional, trim=trim,
- sign=sign, pad_left=pad_left,
- pad_right=pad_right, min_digits=min_digits)
- class IntegerFormat:
- def __init__(self, data, sign='-'):
- if data.size > 0:
- data_max = np.max(data)
- data_min = np.min(data)
- data_max_str_len = len(str(data_max))
- if sign == ' ' and data_min < 0:
- sign = '-'
- if data_max >= 0 and sign in "+ ":
- data_max_str_len += 1
- max_str_len = max(data_max_str_len,
- len(str(data_min)))
- else:
- max_str_len = 0
- self.format = f'{{:{sign}{max_str_len}d}}'
- def __call__(self, x):
- return self.format.format(x)
- class BoolFormat:
- def __init__(self, data, **kwargs):
- # add an extra space so " True" and "False" have the same length and
- # array elements align nicely when printed, except in 0d arrays
- self.truestr = ' True' if data.shape != () else 'True'
- def __call__(self, x):
- return self.truestr if x else "False"
- class ComplexFloatingFormat:
- """ Formatter for subtypes of np.complexfloating """
- def __init__(self, x, precision, floatmode, suppress_small,
- sign=False, *, legacy=None):
- # for backcompatibility, accept bools
- if isinstance(sign, bool):
- sign = '+' if sign else '-'
- floatmode_real = floatmode_imag = floatmode
- if legacy <= 113:
- floatmode_real = 'maxprec_equal'
- floatmode_imag = 'maxprec'
- self.real_format = FloatingFormat(
- x.real, precision, floatmode_real, suppress_small,
- sign=sign, legacy=legacy
- )
- self.imag_format = FloatingFormat(
- x.imag, precision, floatmode_imag, suppress_small,
- sign='+', legacy=legacy
- )
- def __call__(self, x):
- r = self.real_format(x.real)
- i = self.imag_format(x.imag)
- # add the 'j' before the terminal whitespace in i
- sp = len(i.rstrip())
- i = i[:sp] + 'j' + i[sp:]
- return r + i
- class _TimelikeFormat:
- def __init__(self, data):
- non_nat = data[~isnat(data)]
- if len(non_nat) > 0:
- # Max str length of non-NaT elements
- max_str_len = max(len(self._format_non_nat(np.max(non_nat))),
- len(self._format_non_nat(np.min(non_nat))))
- else:
- max_str_len = 0
- if len(non_nat) < data.size:
- # data contains a NaT
- max_str_len = max(max_str_len, 5)
- self._format = f'%{max_str_len}s'
- self._nat = "'NaT'".rjust(max_str_len)
- def _format_non_nat(self, x):
- # override in subclass
- raise NotImplementedError
- def __call__(self, x):
- if isnat(x):
- return self._nat
- else:
- return self._format % self._format_non_nat(x)
- class DatetimeFormat(_TimelikeFormat):
- def __init__(self, x, unit=None, timezone=None, casting='same_kind',
- legacy=False):
- # Get the unit from the dtype
- if unit is None:
- if x.dtype.kind == 'M':
- unit = datetime_data(x.dtype)[0]
- else:
- unit = 's'
- if timezone is None:
- timezone = 'naive'
- self.timezone = timezone
- self.unit = unit
- self.casting = casting
- self.legacy = legacy
- # must be called after the above are configured
- super().__init__(x)
- def __call__(self, x):
- if self.legacy <= 113:
- return self._format_non_nat(x)
- return super().__call__(x)
- def _format_non_nat(self, x):
- return "'%s'" % datetime_as_string(x,
- unit=self.unit,
- timezone=self.timezone,
- casting=self.casting)
- class TimedeltaFormat(_TimelikeFormat):
- def _format_non_nat(self, x):
- return str(x.astype('i8'))
- class SubArrayFormat:
- def __init__(self, format_function, **options):
- self.format_function = format_function
- self.threshold = options['threshold']
- self.edge_items = options['edgeitems']
- def __call__(self, a):
- self.summary_insert = "..." if a.size > self.threshold else ""
- return self.format_array(a)
- def format_array(self, a):
- if np.ndim(a) == 0:
- return self.format_function(a)
- if self.summary_insert and a.shape[0] > 2 * self.edge_items:
- formatted = (
- [self.format_array(a_) for a_ in a[:self.edge_items]]
- + [self.summary_insert]
- + [self.format_array(a_) for a_ in a[-self.edge_items:]]
- )
- else:
- formatted = [self.format_array(a_) for a_ in a]
- return "[" + ", ".join(formatted) + "]"
- class StructuredVoidFormat:
- """
- Formatter for structured np.void objects.
- This does not work on structured alias types like
- np.dtype(('i4', 'i2,i2')), as alias scalars lose their field information,
- and the implementation relies upon np.void.__getitem__.
- """
- def __init__(self, format_functions):
- self.format_functions = format_functions
- @classmethod
- def from_data(cls, data, **options):
- """
- This is a second way to initialize StructuredVoidFormat,
- using the raw data as input. Added to avoid changing
- the signature of __init__.
- """
- format_functions = []
- for field_name in data.dtype.names:
- format_function = _get_format_function(data[field_name], **options)
- if data.dtype[field_name].shape != ():
- format_function = SubArrayFormat(format_function, **options)
- format_functions.append(format_function)
- return cls(format_functions)
- def __call__(self, x):
- str_fields = [
- format_function(field)
- for field, format_function in zip(x, self.format_functions)
- ]
- if len(str_fields) == 1:
- return f"({str_fields[0]},)"
- else:
- return f"({', '.join(str_fields)})"
- def _void_scalar_to_string(x, is_repr=True):
- """
- Implements the repr for structured-void scalars. It is called from the
- scalartypes.c.src code, and is placed here because it uses the elementwise
- formatters defined above.
- """
- options = format_options.get().copy()
- if options["legacy"] <= 125:
- return StructuredVoidFormat.from_data(array(x), **options)(x)
- if options.get('formatter') is None:
- options['formatter'] = {}
- options['formatter'].setdefault('float_kind', str)
- val_repr = StructuredVoidFormat.from_data(array(x), **options)(x)
- if not is_repr:
- return val_repr
- cls = type(x)
- cls_fqn = cls.__module__.replace("numpy", "np") + "." + cls.__name__
- void_dtype = np.dtype((np.void, x.dtype))
- return f"{cls_fqn}({val_repr}, dtype={void_dtype!s})"
- _typelessdata = [int_, float64, complex128, _nt.bool]
- def dtype_is_implied(dtype):
- """
- Determine if the given dtype is implied by the representation
- of its values.
- Parameters
- ----------
- dtype : dtype
- Data type
- Returns
- -------
- implied : bool
- True if the dtype is implied by the representation of its values.
- Examples
- --------
- >>> import numpy as np
- >>> np._core.arrayprint.dtype_is_implied(int)
- True
- >>> np.array([1, 2, 3], int)
- array([1, 2, 3])
- >>> np._core.arrayprint.dtype_is_implied(np.int8)
- False
- >>> np.array([1, 2, 3], np.int8)
- array([1, 2, 3], dtype=int8)
- """
- dtype = np.dtype(dtype)
- if format_options.get()['legacy'] <= 113 and dtype.type == np.bool:
- return False
- # not just void types can be structured, and names are not part of the repr
- if dtype.names is not None:
- return False
- # should care about endianness *unless size is 1* (e.g., int8, bool)
- if not dtype.isnative:
- return False
- return dtype.type in _typelessdata
- def dtype_short_repr(dtype):
- """
- Convert a dtype to a short form which evaluates to the same dtype.
- The intent is roughly that the following holds
- >>> from numpy import *
- >>> dt = np.int64([1, 2]).dtype
- >>> assert eval(dtype_short_repr(dt)) == dt
- """
- if type(dtype).__repr__ != np.dtype.__repr__:
- # TODO: Custom repr for user DTypes, logic should likely move.
- return repr(dtype)
- if dtype.names is not None:
- # structured dtypes give a list or tuple repr
- return str(dtype)
- elif issubclass(dtype.type, flexible):
- # handle these separately so they don't give garbage like str256
- return f"'{str(dtype)}'"
- typename = dtype.name
- if not dtype.isnative:
- # deal with cases like dtype('<u2') that are identical to an
- # established dtype (in this case uint16)
- # except that they have a different endianness.
- return f"'{str(dtype)}'"
- # quote typenames which can't be represented as python variable names
- if typename and not (typename[0].isalpha() and typename.isalnum()):
- typename = repr(typename)
- return typename
- def _array_repr_implementation(
- arr, max_line_width=None, precision=None, suppress_small=None,
- array2string=array2string):
- """Internal version of array_repr() that allows overriding array2string."""
- current_options = format_options.get()
- override_repr = current_options["override_repr"]
- if override_repr is not None:
- return override_repr(arr)
- if max_line_width is None:
- max_line_width = current_options['linewidth']
- if type(arr) is not ndarray:
- class_name = type(arr).__name__
- else:
- class_name = "array"
- prefix = class_name + "("
- if (current_options['legacy'] <= 113 and
- arr.shape == () and not arr.dtype.names):
- lst = repr(arr.item())
- else:
- lst = array2string(arr, max_line_width, precision, suppress_small,
- ', ', prefix, suffix=")")
- # Add dtype and shape information if these cannot be inferred from
- # the array string.
- extras = []
- if ((arr.size == 0 and arr.shape != (0,))
- or (current_options['legacy'] > 210
- and arr.size > current_options['threshold'])):
- extras.append(f"shape={arr.shape}")
- if not dtype_is_implied(arr.dtype) or arr.size == 0:
- extras.append(f"dtype={dtype_short_repr(arr.dtype)}")
- if not extras:
- return prefix + lst + ")"
- arr_str = prefix + lst + ","
- extra_str = ", ".join(extras) + ")"
- # compute whether we should put extras on a new line: Do so if adding the
- # extras would extend the last line past max_line_width.
- # Note: This line gives the correct result even when rfind returns -1.
- last_line_len = len(arr_str) - (arr_str.rfind('\n') + 1)
- spacer = " "
- if current_options['legacy'] <= 113:
- if issubclass(arr.dtype.type, flexible):
- spacer = '\n' + ' ' * len(prefix)
- elif last_line_len + len(extra_str) + 1 > max_line_width:
- spacer = '\n' + ' ' * len(prefix)
- return arr_str + spacer + extra_str
- def _array_repr_dispatcher(
- arr, max_line_width=None, precision=None, suppress_small=None):
- return (arr,)
- @array_function_dispatch(_array_repr_dispatcher, module='numpy')
- def array_repr(arr, max_line_width=None, precision=None, suppress_small=None):
- """
- Return the string representation of an array.
- Parameters
- ----------
- arr : ndarray
- Input array.
- max_line_width : int, optional
- Inserts newlines if text is longer than `max_line_width`.
- Defaults to ``numpy.get_printoptions()['linewidth']``.
- precision : int, optional
- Floating point precision.
- Defaults to ``numpy.get_printoptions()['precision']``.
- suppress_small : bool, optional
- Represent numbers "very close" to zero as zero; default is False.
- Very close is defined by precision: if the precision is 8, e.g.,
- numbers smaller (in absolute value) than 5e-9 are represented as
- zero.
- Defaults to ``numpy.get_printoptions()['suppress']``.
- Returns
- -------
- string : str
- The string representation of an array.
- See Also
- --------
- array_str, array2string, set_printoptions
- Examples
- --------
- >>> import numpy as np
- >>> np.array_repr(np.array([1,2]))
- 'array([1, 2])'
- >>> np.array_repr(np.ma.array([0.]))
- 'MaskedArray([0.])'
- >>> np.array_repr(np.array([], np.int32))
- 'array([], dtype=int32)'
- >>> x = np.array([1e-6, 4e-7, 2, 3])
- >>> np.array_repr(x, precision=6, suppress_small=True)
- 'array([0.000001, 0. , 2. , 3. ])'
- """
- return _array_repr_implementation(
- arr, max_line_width, precision, suppress_small)
- @_recursive_guard()
- def _guarded_repr_or_str(v):
- if isinstance(v, bytes):
- return repr(v)
- return str(v)
- def _array_str_implementation(
- a, max_line_width=None, precision=None, suppress_small=None,
- array2string=array2string):
- """Internal version of array_str() that allows overriding array2string."""
- if (format_options.get()['legacy'] <= 113 and
- a.shape == () and not a.dtype.names):
- return str(a.item())
- # the str of 0d arrays is a special case: It should appear like a scalar,
- # so floats are not truncated by `precision`, and strings are not wrapped
- # in quotes. So we return the str of the scalar value.
- if a.shape == ():
- # obtain a scalar and call str on it, avoiding problems for subclasses
- # for which indexing with () returns a 0d instead of a scalar by using
- # ndarray's getindex. Also guard against recursive 0d object arrays.
- return _guarded_repr_or_str(np.ndarray.__getitem__(a, ()))
- return array2string(a, max_line_width, precision, suppress_small, ' ', "")
- def _array_str_dispatcher(
- a, max_line_width=None, precision=None, suppress_small=None):
- return (a,)
- @array_function_dispatch(_array_str_dispatcher, module='numpy')
- def array_str(a, max_line_width=None, precision=None, suppress_small=None):
- """
- Return a string representation of the data in an array.
- The data in the array is returned as a single string. This function is
- similar to `array_repr`, the difference being that `array_repr` also
- returns information on the kind of array and its data type.
- Parameters
- ----------
- a : ndarray
- Input array.
- max_line_width : int, optional
- Inserts newlines if text is longer than `max_line_width`.
- Defaults to ``numpy.get_printoptions()['linewidth']``.
- precision : int, optional
- Floating point precision.
- Defaults to ``numpy.get_printoptions()['precision']``.
- suppress_small : bool, optional
- Represent numbers "very close" to zero as zero; default is False.
- Very close is defined by precision: if the precision is 8, e.g.,
- numbers smaller (in absolute value) than 5e-9 are represented as
- zero.
- Defaults to ``numpy.get_printoptions()['suppress']``.
- See Also
- --------
- array2string, array_repr, set_printoptions
- Examples
- --------
- >>> import numpy as np
- >>> np.array_str(np.arange(3))
- '[0 1 2]'
- """
- return _array_str_implementation(
- a, max_line_width, precision, suppress_small)
- # needed if __array_function__ is disabled
- _array2string_impl = getattr(array2string, '__wrapped__', array2string)
- _default_array_str = functools.partial(_array_str_implementation,
- array2string=_array2string_impl)
- _default_array_repr = functools.partial(_array_repr_implementation,
- array2string=_array2string_impl)
|