1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099 |
- """
- Record Arrays
- =============
- Record arrays expose the fields of structured arrays as properties.
- Most commonly, ndarrays contain elements of a single type, e.g. floats,
- integers, bools etc. However, it is possible for elements to be combinations
- of these using structured types, such as::
- >>> a = np.array([(1, 2.0), (1, 2.0)], dtype=[('x', np.int64), ('y', np.float64)])
- >>> a
- array([(1, 2.), (1, 2.)], dtype=[('x', '<i8'), ('y', '<f8')])
- Here, each element consists of two fields: x (and int), and y (a float).
- This is known as a structured array. The different fields are analogous
- to columns in a spread-sheet. The different fields can be accessed as
- one would a dictionary::
- >>> a['x']
- array([1, 1])
- >>> a['y']
- array([2., 2.])
- Record arrays allow us to access fields as properties::
- >>> ar = np.rec.array(a)
- >>> ar.x
- array([1, 1])
- >>> ar.y
- array([2., 2.])
- """
- import warnings
- from collections import Counter
- from contextlib import nullcontext
- from . import numeric as sb
- from . import numerictypes as nt
- from numpy.compat import os_fspath
- from numpy.core.overrides import set_module
- from .arrayprint import _get_legacy_print_mode
- # All of the functions allow formats to be a dtype
- __all__ = [
- 'record', 'recarray', 'format_parser',
- 'fromarrays', 'fromrecords', 'fromstring', 'fromfile', 'array',
- ]
- ndarray = sb.ndarray
- _byteorderconv = {'b':'>',
- 'l':'<',
- 'n':'=',
- 'B':'>',
- 'L':'<',
- 'N':'=',
- 'S':'s',
- 's':'s',
- '>':'>',
- '<':'<',
- '=':'=',
- '|':'|',
- 'I':'|',
- 'i':'|'}
- # formats regular expression
- # allows multidimensional spec with a tuple syntax in front
- # of the letter code '(2,3)f4' and ' ( 2 , 3 ) f4 '
- # are equally allowed
- numfmt = nt.sctypeDict
- def find_duplicate(list):
- """Find duplication in a list, return a list of duplicated elements"""
- return [
- item
- for item, counts in Counter(list).items()
- if counts > 1
- ]
- @set_module('numpy')
- class format_parser:
- """
- Class to convert formats, names, titles description to a dtype.
- After constructing the format_parser object, the dtype attribute is
- the converted data-type:
- ``dtype = format_parser(formats, names, titles).dtype``
- Attributes
- ----------
- dtype : dtype
- The converted data-type.
- Parameters
- ----------
- formats : str or list of str
- The format description, either specified as a string with
- comma-separated format descriptions in the form ``'f8, i4, a5'``, or
- a list of format description strings in the form
- ``['f8', 'i4', 'a5']``.
- names : str or list/tuple of str
- The field names, either specified as a comma-separated string in the
- form ``'col1, col2, col3'``, or as a list or tuple of strings in the
- form ``['col1', 'col2', 'col3']``.
- An empty list can be used, in that case default field names
- ('f0', 'f1', ...) are used.
- titles : sequence
- Sequence of title strings. An empty list can be used to leave titles
- out.
- aligned : bool, optional
- If True, align the fields by padding as the C-compiler would.
- Default is False.
- byteorder : str, optional
- If specified, all the fields will be changed to the
- provided byte-order. Otherwise, the default byte-order is
- used. For all available string specifiers, see `dtype.newbyteorder`.
- See Also
- --------
- dtype, typename, sctype2char
- Examples
- --------
- >>> np.format_parser(['<f8', '<i4', '<a5'], ['col1', 'col2', 'col3'],
- ... ['T1', 'T2', 'T3']).dtype
- dtype([(('T1', 'col1'), '<f8'), (('T2', 'col2'), '<i4'), (('T3', 'col3'), 'S5')])
- `names` and/or `titles` can be empty lists. If `titles` is an empty list,
- titles will simply not appear. If `names` is empty, default field names
- will be used.
- >>> np.format_parser(['f8', 'i4', 'a5'], ['col1', 'col2', 'col3'],
- ... []).dtype
- dtype([('col1', '<f8'), ('col2', '<i4'), ('col3', '<S5')])
- >>> np.format_parser(['<f8', '<i4', '<a5'], [], []).dtype
- dtype([('f0', '<f8'), ('f1', '<i4'), ('f2', 'S5')])
- """
- def __init__(self, formats, names, titles, aligned=False, byteorder=None):
- self._parseFormats(formats, aligned)
- self._setfieldnames(names, titles)
- self._createdtype(byteorder)
- def _parseFormats(self, formats, aligned=False):
- """ Parse the field formats """
- if formats is None:
- raise ValueError("Need formats argument")
- if isinstance(formats, list):
- dtype = sb.dtype(
- [('f{}'.format(i), format_) for i, format_ in enumerate(formats)],
- aligned,
- )
- else:
- dtype = sb.dtype(formats, aligned)
- fields = dtype.fields
- if fields is None:
- dtype = sb.dtype([('f1', dtype)], aligned)
- fields = dtype.fields
- keys = dtype.names
- self._f_formats = [fields[key][0] for key in keys]
- self._offsets = [fields[key][1] for key in keys]
- self._nfields = len(keys)
- def _setfieldnames(self, names, titles):
- """convert input field names into a list and assign to the _names
- attribute """
- if names:
- if type(names) in [list, tuple]:
- pass
- elif isinstance(names, str):
- names = names.split(',')
- else:
- raise NameError("illegal input names %s" % repr(names))
- self._names = [n.strip() for n in names[:self._nfields]]
- else:
- self._names = []
- # if the names are not specified, they will be assigned as
- # "f0, f1, f2,..."
- # if not enough names are specified, they will be assigned as "f[n],
- # f[n+1],..." etc. where n is the number of specified names..."
- self._names += ['f%d' % i for i in range(len(self._names),
- self._nfields)]
- # check for redundant names
- _dup = find_duplicate(self._names)
- if _dup:
- raise ValueError("Duplicate field names: %s" % _dup)
- if titles:
- self._titles = [n.strip() for n in titles[:self._nfields]]
- else:
- self._titles = []
- titles = []
- if self._nfields > len(titles):
- self._titles += [None] * (self._nfields - len(titles))
- def _createdtype(self, byteorder):
- dtype = sb.dtype({
- 'names': self._names,
- 'formats': self._f_formats,
- 'offsets': self._offsets,
- 'titles': self._titles,
- })
- if byteorder is not None:
- byteorder = _byteorderconv[byteorder[0]]
- dtype = dtype.newbyteorder(byteorder)
- self.dtype = dtype
- class record(nt.void):
- """A data-type scalar that allows field access as attribute lookup.
- """
- # manually set name and module so that this class's type shows up
- # as numpy.record when printed
- __name__ = 'record'
- __module__ = 'numpy'
- def __repr__(self):
- if _get_legacy_print_mode() <= 113:
- return self.__str__()
- return super().__repr__()
- def __str__(self):
- if _get_legacy_print_mode() <= 113:
- return str(self.item())
- return super().__str__()
- def __getattribute__(self, attr):
- if attr in ('setfield', 'getfield', 'dtype'):
- return nt.void.__getattribute__(self, attr)
- try:
- return nt.void.__getattribute__(self, attr)
- except AttributeError:
- pass
- fielddict = nt.void.__getattribute__(self, 'dtype').fields
- res = fielddict.get(attr, None)
- if res:
- obj = self.getfield(*res[:2])
- # if it has fields return a record,
- # otherwise return the object
- try:
- dt = obj.dtype
- except AttributeError:
- #happens if field is Object type
- return obj
- if dt.names is not None:
- return obj.view((self.__class__, obj.dtype))
- return obj
- else:
- raise AttributeError("'record' object has no "
- "attribute '%s'" % attr)
- def __setattr__(self, attr, val):
- if attr in ('setfield', 'getfield', 'dtype'):
- raise AttributeError("Cannot set '%s' attribute" % attr)
- fielddict = nt.void.__getattribute__(self, 'dtype').fields
- res = fielddict.get(attr, None)
- if res:
- return self.setfield(val, *res[:2])
- else:
- if getattr(self, attr, None):
- return nt.void.__setattr__(self, attr, val)
- else:
- raise AttributeError("'record' object has no "
- "attribute '%s'" % attr)
- def __getitem__(self, indx):
- obj = nt.void.__getitem__(self, indx)
- # copy behavior of record.__getattribute__,
- if isinstance(obj, nt.void) and obj.dtype.names is not None:
- return obj.view((self.__class__, obj.dtype))
- else:
- # return a single element
- return obj
- def pprint(self):
- """Pretty-print all fields."""
- # pretty-print all fields
- names = self.dtype.names
- maxlen = max(len(name) for name in names)
- fmt = '%% %ds: %%s' % maxlen
- rows = [fmt % (name, getattr(self, name)) for name in names]
- return "\n".join(rows)
- # The recarray is almost identical to a standard array (which supports
- # named fields already) The biggest difference is that it can use
- # attribute-lookup to find the fields and it is constructed using
- # a record.
- # If byteorder is given it forces a particular byteorder on all
- # the fields (and any subfields)
- class recarray(ndarray):
- """Construct an ndarray that allows field access using attributes.
- Arrays may have a data-types containing fields, analogous
- to columns in a spread sheet. An example is ``[(x, int), (y, float)]``,
- where each entry in the array is a pair of ``(int, float)``. Normally,
- these attributes are accessed using dictionary lookups such as ``arr['x']``
- and ``arr['y']``. Record arrays allow the fields to be accessed as members
- of the array, using ``arr.x`` and ``arr.y``.
- Parameters
- ----------
- shape : tuple
- Shape of output array.
- dtype : data-type, optional
- The desired data-type. By default, the data-type is determined
- from `formats`, `names`, `titles`, `aligned` and `byteorder`.
- formats : list of data-types, optional
- A list containing the data-types for the different columns, e.g.
- ``['i4', 'f8', 'i4']``. `formats` does *not* support the new
- convention of using types directly, i.e. ``(int, float, int)``.
- Note that `formats` must be a list, not a tuple.
- Given that `formats` is somewhat limited, we recommend specifying
- `dtype` instead.
- names : tuple of str, optional
- The name of each column, e.g. ``('x', 'y', 'z')``.
- buf : buffer, optional
- By default, a new array is created of the given shape and data-type.
- If `buf` is specified and is an object exposing the buffer interface,
- the array will use the memory from the existing buffer. In this case,
- the `offset` and `strides` keywords are available.
- Other Parameters
- ----------------
- titles : tuple of str, optional
- Aliases for column names. For example, if `names` were
- ``('x', 'y', 'z')`` and `titles` is
- ``('x_coordinate', 'y_coordinate', 'z_coordinate')``, then
- ``arr['x']`` is equivalent to both ``arr.x`` and ``arr.x_coordinate``.
- byteorder : {'<', '>', '='}, optional
- Byte-order for all fields.
- aligned : bool, optional
- Align the fields in memory as the C-compiler would.
- strides : tuple of ints, optional
- Buffer (`buf`) is interpreted according to these strides (strides
- define how many bytes each array element, row, column, etc.
- occupy in memory).
- offset : int, optional
- Start reading buffer (`buf`) from this offset onwards.
- order : {'C', 'F'}, optional
- Row-major (C-style) or column-major (Fortran-style) order.
- Returns
- -------
- rec : recarray
- Empty array of the given shape and type.
- See Also
- --------
- core.records.fromrecords : Construct a record array from data.
- record : fundamental data-type for `recarray`.
- format_parser : determine a data-type from formats, names, titles.
- Notes
- -----
- This constructor can be compared to ``empty``: it creates a new record
- array but does not fill it with data. To create a record array from data,
- use one of the following methods:
- 1. Create a standard ndarray and convert it to a record array,
- using ``arr.view(np.recarray)``
- 2. Use the `buf` keyword.
- 3. Use `np.rec.fromrecords`.
- Examples
- --------
- Create an array with two fields, ``x`` and ``y``:
- >>> x = np.array([(1.0, 2), (3.0, 4)], dtype=[('x', '<f8'), ('y', '<i8')])
- >>> x
- array([(1., 2), (3., 4)], dtype=[('x', '<f8'), ('y', '<i8')])
- >>> x['x']
- array([1., 3.])
- View the array as a record array:
- >>> x = x.view(np.recarray)
- >>> x.x
- array([1., 3.])
- >>> x.y
- array([2, 4])
- Create a new, empty record array:
- >>> np.recarray((2,),
- ... dtype=[('x', int), ('y', float), ('z', int)]) #doctest: +SKIP
- rec.array([(-1073741821, 1.2249118382103472e-301, 24547520),
- (3471280, 1.2134086255804012e-316, 0)],
- dtype=[('x', '<i4'), ('y', '<f8'), ('z', '<i4')])
- """
- # manually set name and module so that this class's type shows
- # up as "numpy.recarray" when printed
- __name__ = 'recarray'
- __module__ = 'numpy'
- def __new__(subtype, shape, dtype=None, buf=None, offset=0, strides=None,
- formats=None, names=None, titles=None,
- byteorder=None, aligned=False, order='C'):
- if dtype is not None:
- descr = sb.dtype(dtype)
- else:
- descr = format_parser(formats, names, titles, aligned, byteorder).dtype
- if buf is None:
- self = ndarray.__new__(subtype, shape, (record, descr), order=order)
- else:
- self = ndarray.__new__(subtype, shape, (record, descr),
- buffer=buf, offset=offset,
- strides=strides, order=order)
- return self
- def __array_finalize__(self, obj):
- if self.dtype.type is not record and self.dtype.names is not None:
- # if self.dtype is not np.record, invoke __setattr__ which will
- # convert it to a record if it is a void dtype.
- self.dtype = self.dtype
- def __getattribute__(self, attr):
- # See if ndarray has this attr, and return it if so. (note that this
- # means a field with the same name as an ndarray attr cannot be
- # accessed by attribute).
- try:
- return object.__getattribute__(self, attr)
- except AttributeError: # attr must be a fieldname
- pass
- # look for a field with this name
- fielddict = ndarray.__getattribute__(self, 'dtype').fields
- try:
- res = fielddict[attr][:2]
- except (TypeError, KeyError) as e:
- raise AttributeError("recarray has no attribute %s" % attr) from e
- obj = self.getfield(*res)
- # At this point obj will always be a recarray, since (see
- # PyArray_GetField) the type of obj is inherited. Next, if obj.dtype is
- # non-structured, convert it to an ndarray. Then if obj is structured
- # with void type convert it to the same dtype.type (eg to preserve
- # numpy.record type if present), since nested structured fields do not
- # inherit type. Don't do this for non-void structures though.
- if obj.dtype.names is not None:
- if issubclass(obj.dtype.type, nt.void):
- return obj.view(dtype=(self.dtype.type, obj.dtype))
- return obj
- else:
- return obj.view(ndarray)
- # Save the dictionary.
- # If the attr is a field name and not in the saved dictionary
- # Undo any "setting" of the attribute and do a setfield
- # Thus, you can't create attributes on-the-fly that are field names.
- def __setattr__(self, attr, val):
- # Automatically convert (void) structured types to records
- # (but not non-void structures, subarrays, or non-structured voids)
- if attr == 'dtype' and issubclass(val.type, nt.void) and val.names is not None:
- val = sb.dtype((record, val))
- newattr = attr not in self.__dict__
- try:
- ret = object.__setattr__(self, attr, val)
- except Exception:
- fielddict = ndarray.__getattribute__(self, 'dtype').fields or {}
- if attr not in fielddict:
- raise
- else:
- fielddict = ndarray.__getattribute__(self, 'dtype').fields or {}
- if attr not in fielddict:
- return ret
- if newattr:
- # We just added this one or this setattr worked on an
- # internal attribute.
- try:
- object.__delattr__(self, attr)
- except Exception:
- return ret
- try:
- res = fielddict[attr][:2]
- except (TypeError, KeyError) as e:
- raise AttributeError(
- "record array has no attribute %s" % attr
- ) from e
- return self.setfield(val, *res)
- def __getitem__(self, indx):
- obj = super().__getitem__(indx)
- # copy behavior of getattr, except that here
- # we might also be returning a single element
- if isinstance(obj, ndarray):
- if obj.dtype.names is not None:
- obj = obj.view(type(self))
- if issubclass(obj.dtype.type, nt.void):
- return obj.view(dtype=(self.dtype.type, obj.dtype))
- return obj
- else:
- return obj.view(type=ndarray)
- else:
- # return a single element
- return obj
- def __repr__(self):
- repr_dtype = self.dtype
- if self.dtype.type is record or not issubclass(self.dtype.type, nt.void):
- # If this is a full record array (has numpy.record dtype),
- # or if it has a scalar (non-void) dtype with no records,
- # represent it using the rec.array function. Since rec.array
- # converts dtype to a numpy.record for us, convert back
- # to non-record before printing
- if repr_dtype.type is record:
- repr_dtype = sb.dtype((nt.void, repr_dtype))
- prefix = "rec.array("
- fmt = 'rec.array(%s,%sdtype=%s)'
- else:
- # otherwise represent it using np.array plus a view
- # This should only happen if the user is playing
- # strange games with dtypes.
- prefix = "array("
- fmt = 'array(%s,%sdtype=%s).view(numpy.recarray)'
- # get data/shape string. logic taken from numeric.array_repr
- if self.size > 0 or self.shape == (0,):
- lst = sb.array2string(
- self, separator=', ', prefix=prefix, suffix=',')
- else:
- # show zero-length shape unless it is (0,)
- lst = "[], shape=%s" % (repr(self.shape),)
- lf = '\n'+' '*len(prefix)
- if _get_legacy_print_mode() <= 113:
- lf = ' ' + lf # trailing space
- return fmt % (lst, lf, repr_dtype)
- def field(self, attr, val=None):
- if isinstance(attr, int):
- names = ndarray.__getattribute__(self, 'dtype').names
- attr = names[attr]
- fielddict = ndarray.__getattribute__(self, 'dtype').fields
- res = fielddict[attr][:2]
- if val is None:
- obj = self.getfield(*res)
- if obj.dtype.names is not None:
- return obj
- return obj.view(ndarray)
- else:
- return self.setfield(val, *res)
- def _deprecate_shape_0_as_None(shape):
- if shape == 0:
- warnings.warn(
- "Passing `shape=0` to have the shape be inferred is deprecated, "
- "and in future will be equivalent to `shape=(0,)`. To infer "
- "the shape and suppress this warning, pass `shape=None` instead.",
- FutureWarning, stacklevel=3)
- return None
- else:
- return shape
- @set_module("numpy.rec")
- def fromarrays(arrayList, dtype=None, shape=None, formats=None,
- names=None, titles=None, aligned=False, byteorder=None):
- """Create a record array from a (flat) list of arrays
- Parameters
- ----------
- arrayList : list or tuple
- List of array-like objects (such as lists, tuples,
- and ndarrays).
- dtype : data-type, optional
- valid dtype for all arrays
- shape : int or tuple of ints, optional
- Shape of the resulting array. If not provided, inferred from
- ``arrayList[0]``.
- formats, names, titles, aligned, byteorder :
- If `dtype` is ``None``, these arguments are passed to
- `numpy.format_parser` to construct a dtype. See that function for
- detailed documentation.
- Returns
- -------
- np.recarray
- Record array consisting of given arrayList columns.
- Examples
- --------
- >>> x1=np.array([1,2,3,4])
- >>> x2=np.array(['a','dd','xyz','12'])
- >>> x3=np.array([1.1,2,3,4])
- >>> r = np.core.records.fromarrays([x1,x2,x3],names='a,b,c')
- >>> print(r[1])
- (2, 'dd', 2.0) # may vary
- >>> x1[1]=34
- >>> r.a
- array([1, 2, 3, 4])
- >>> x1 = np.array([1, 2, 3, 4])
- >>> x2 = np.array(['a', 'dd', 'xyz', '12'])
- >>> x3 = np.array([1.1, 2, 3,4])
- >>> r = np.core.records.fromarrays(
- ... [x1, x2, x3],
- ... dtype=np.dtype([('a', np.int32), ('b', 'S3'), ('c', np.float32)]))
- >>> r
- rec.array([(1, b'a', 1.1), (2, b'dd', 2. ), (3, b'xyz', 3. ),
- (4, b'12', 4. )],
- dtype=[('a', '<i4'), ('b', 'S3'), ('c', '<f4')])
- """
- arrayList = [sb.asarray(x) for x in arrayList]
- # NumPy 1.19.0, 2020-01-01
- shape = _deprecate_shape_0_as_None(shape)
- if shape is None:
- shape = arrayList[0].shape
- elif isinstance(shape, int):
- shape = (shape,)
- if formats is None and dtype is None:
- # go through each object in the list to see if it is an ndarray
- # and determine the formats.
- formats = [obj.dtype for obj in arrayList]
- if dtype is not None:
- descr = sb.dtype(dtype)
- else:
- descr = format_parser(formats, names, titles, aligned, byteorder).dtype
- _names = descr.names
- # Determine shape from data-type.
- if len(descr) != len(arrayList):
- raise ValueError("mismatch between the number of fields "
- "and the number of arrays")
- d0 = descr[0].shape
- nn = len(d0)
- if nn > 0:
- shape = shape[:-nn]
- _array = recarray(shape, descr)
- # populate the record array (makes a copy)
- for k, obj in enumerate(arrayList):
- nn = descr[k].ndim
- testshape = obj.shape[:obj.ndim - nn]
- name = _names[k]
- if testshape != shape:
- raise ValueError(f'array-shape mismatch in array {k} ("{name}")')
- _array[name] = obj
- return _array
- @set_module("numpy.rec")
- def fromrecords(recList, dtype=None, shape=None, formats=None, names=None,
- titles=None, aligned=False, byteorder=None):
- """Create a recarray from a list of records in text form.
- Parameters
- ----------
- recList : sequence
- data in the same field may be heterogeneous - they will be promoted
- to the highest data type.
- dtype : data-type, optional
- valid dtype for all arrays
- shape : int or tuple of ints, optional
- shape of each array.
- formats, names, titles, aligned, byteorder :
- If `dtype` is ``None``, these arguments are passed to
- `numpy.format_parser` to construct a dtype. See that function for
- detailed documentation.
- If both `formats` and `dtype` are None, then this will auto-detect
- formats. Use list of tuples rather than list of lists for faster
- processing.
- Returns
- -------
- np.recarray
- record array consisting of given recList rows.
- Examples
- --------
- >>> r=np.core.records.fromrecords([(456,'dbe',1.2),(2,'de',1.3)],
- ... names='col1,col2,col3')
- >>> print(r[0])
- (456, 'dbe', 1.2)
- >>> r.col1
- array([456, 2])
- >>> r.col2
- array(['dbe', 'de'], dtype='<U3')
- >>> import pickle
- >>> pickle.loads(pickle.dumps(r))
- rec.array([(456, 'dbe', 1.2), ( 2, 'de', 1.3)],
- dtype=[('col1', '<i8'), ('col2', '<U3'), ('col3', '<f8')])
- """
- if formats is None and dtype is None: # slower
- obj = sb.array(recList, dtype=object)
- arrlist = [sb.array(obj[..., i].tolist()) for i in range(obj.shape[-1])]
- return fromarrays(arrlist, formats=formats, shape=shape, names=names,
- titles=titles, aligned=aligned, byteorder=byteorder)
- if dtype is not None:
- descr = sb.dtype((record, dtype))
- else:
- descr = format_parser(formats, names, titles, aligned, byteorder).dtype
- try:
- retval = sb.array(recList, dtype=descr)
- except (TypeError, ValueError):
- # NumPy 1.19.0, 2020-01-01
- shape = _deprecate_shape_0_as_None(shape)
- if shape is None:
- shape = len(recList)
- if isinstance(shape, int):
- shape = (shape,)
- if len(shape) > 1:
- raise ValueError("Can only deal with 1-d array.")
- _array = recarray(shape, descr)
- for k in range(_array.size):
- _array[k] = tuple(recList[k])
- # list of lists instead of list of tuples ?
- # 2018-02-07, 1.14.1
- warnings.warn(
- "fromrecords expected a list of tuples, may have received a list "
- "of lists instead. In the future that will raise an error",
- FutureWarning, stacklevel=2)
- return _array
- else:
- if shape is not None and retval.shape != shape:
- retval.shape = shape
- res = retval.view(recarray)
- return res
- @set_module("numpy.rec")
- def fromstring(datastring, dtype=None, shape=None, offset=0, formats=None,
- names=None, titles=None, aligned=False, byteorder=None):
- r"""Create a record array from binary data
- Note that despite the name of this function it does not accept `str`
- instances.
- Parameters
- ----------
- datastring : bytes-like
- Buffer of binary data
- dtype : data-type, optional
- Valid dtype for all arrays
- shape : int or tuple of ints, optional
- Shape of each array.
- offset : int, optional
- Position in the buffer to start reading from.
- formats, names, titles, aligned, byteorder :
- If `dtype` is ``None``, these arguments are passed to
- `numpy.format_parser` to construct a dtype. See that function for
- detailed documentation.
- Returns
- -------
- np.recarray
- Record array view into the data in datastring. This will be readonly
- if `datastring` is readonly.
- See Also
- --------
- numpy.frombuffer
- Examples
- --------
- >>> a = b'\x01\x02\x03abc'
- >>> np.core.records.fromstring(a, dtype='u1,u1,u1,S3')
- rec.array([(1, 2, 3, b'abc')],
- dtype=[('f0', 'u1'), ('f1', 'u1'), ('f2', 'u1'), ('f3', 'S3')])
- >>> grades_dtype = [('Name', (np.str_, 10)), ('Marks', np.float64),
- ... ('GradeLevel', np.int32)]
- >>> grades_array = np.array([('Sam', 33.3, 3), ('Mike', 44.4, 5),
- ... ('Aadi', 66.6, 6)], dtype=grades_dtype)
- >>> np.core.records.fromstring(grades_array.tobytes(), dtype=grades_dtype)
- rec.array([('Sam', 33.3, 3), ('Mike', 44.4, 5), ('Aadi', 66.6, 6)],
- dtype=[('Name', '<U10'), ('Marks', '<f8'), ('GradeLevel', '<i4')])
- >>> s = '\x01\x02\x03abc'
- >>> np.core.records.fromstring(s, dtype='u1,u1,u1,S3')
- Traceback (most recent call last)
- ...
- TypeError: a bytes-like object is required, not 'str'
- """
- if dtype is None and formats is None:
- raise TypeError("fromstring() needs a 'dtype' or 'formats' argument")
- if dtype is not None:
- descr = sb.dtype(dtype)
- else:
- descr = format_parser(formats, names, titles, aligned, byteorder).dtype
- itemsize = descr.itemsize
- # NumPy 1.19.0, 2020-01-01
- shape = _deprecate_shape_0_as_None(shape)
- if shape in (None, -1):
- shape = (len(datastring) - offset) // itemsize
- _array = recarray(shape, descr, buf=datastring, offset=offset)
- return _array
- def get_remaining_size(fd):
- pos = fd.tell()
- try:
- fd.seek(0, 2)
- return fd.tell() - pos
- finally:
- fd.seek(pos, 0)
- @set_module("numpy.rec")
- def fromfile(fd, dtype=None, shape=None, offset=0, formats=None,
- names=None, titles=None, aligned=False, byteorder=None):
- """Create an array from binary file data
- Parameters
- ----------
- fd : str or file type
- If file is a string or a path-like object then that file is opened,
- else it is assumed to be a file object. The file object must
- support random access (i.e. it must have tell and seek methods).
- dtype : data-type, optional
- valid dtype for all arrays
- shape : int or tuple of ints, optional
- shape of each array.
- offset : int, optional
- Position in the file to start reading from.
- formats, names, titles, aligned, byteorder :
- If `dtype` is ``None``, these arguments are passed to
- `numpy.format_parser` to construct a dtype. See that function for
- detailed documentation
- Returns
- -------
- np.recarray
- record array consisting of data enclosed in file.
- Examples
- --------
- >>> from tempfile import TemporaryFile
- >>> a = np.empty(10,dtype='f8,i4,a5')
- >>> a[5] = (0.5,10,'abcde')
- >>>
- >>> fd=TemporaryFile()
- >>> a = a.newbyteorder('<')
- >>> a.tofile(fd)
- >>>
- >>> _ = fd.seek(0)
- >>> r=np.core.records.fromfile(fd, formats='f8,i4,a5', shape=10,
- ... byteorder='<')
- >>> print(r[5])
- (0.5, 10, 'abcde')
- >>> r.shape
- (10,)
- """
- if dtype is None and formats is None:
- raise TypeError("fromfile() needs a 'dtype' or 'formats' argument")
- # NumPy 1.19.0, 2020-01-01
- shape = _deprecate_shape_0_as_None(shape)
- if shape is None:
- shape = (-1,)
- elif isinstance(shape, int):
- shape = (shape,)
- if hasattr(fd, 'readinto'):
- # GH issue 2504. fd supports io.RawIOBase or io.BufferedIOBase interface.
- # Example of fd: gzip, BytesIO, BufferedReader
- # file already opened
- ctx = nullcontext(fd)
- else:
- # open file
- ctx = open(os_fspath(fd), 'rb')
- with ctx as fd:
- if offset > 0:
- fd.seek(offset, 1)
- size = get_remaining_size(fd)
- if dtype is not None:
- descr = sb.dtype(dtype)
- else:
- descr = format_parser(formats, names, titles, aligned, byteorder).dtype
- itemsize = descr.itemsize
- shapeprod = sb.array(shape).prod(dtype=nt.intp)
- shapesize = shapeprod * itemsize
- if shapesize < 0:
- shape = list(shape)
- shape[shape.index(-1)] = size // -shapesize
- shape = tuple(shape)
- shapeprod = sb.array(shape).prod(dtype=nt.intp)
- nbytes = shapeprod * itemsize
- if nbytes > size:
- raise ValueError(
- "Not enough bytes left in file for specified shape and type")
- # create the array
- _array = recarray(shape, descr)
- nbytesread = fd.readinto(_array.data)
- if nbytesread != nbytes:
- raise OSError("Didn't read as many bytes as expected")
- return _array
- @set_module("numpy.rec")
- def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None,
- names=None, titles=None, aligned=False, byteorder=None, copy=True):
- """
- Construct a record array from a wide-variety of objects.
- A general-purpose record array constructor that dispatches to the
- appropriate `recarray` creation function based on the inputs (see Notes).
- Parameters
- ----------
- obj : any
- Input object. See Notes for details on how various input types are
- treated.
- dtype : data-type, optional
- Valid dtype for array.
- shape : int or tuple of ints, optional
- Shape of each array.
- offset : int, optional
- Position in the file or buffer to start reading from.
- strides : tuple of ints, optional
- Buffer (`buf`) is interpreted according to these strides (strides
- define how many bytes each array element, row, column, etc.
- occupy in memory).
- formats, names, titles, aligned, byteorder :
- If `dtype` is ``None``, these arguments are passed to
- `numpy.format_parser` to construct a dtype. See that function for
- detailed documentation.
- copy : bool, optional
- Whether to copy the input object (True), or to use a reference instead.
- This option only applies when the input is an ndarray or recarray.
- Defaults to True.
- Returns
- -------
- np.recarray
- Record array created from the specified object.
- Notes
- -----
- If `obj` is ``None``, then call the `~numpy.recarray` constructor. If
- `obj` is a string, then call the `fromstring` constructor. If `obj` is a
- list or a tuple, then if the first object is an `~numpy.ndarray`, call
- `fromarrays`, otherwise call `fromrecords`. If `obj` is a
- `~numpy.recarray`, then make a copy of the data in the recarray
- (if ``copy=True``) and use the new formats, names, and titles. If `obj`
- is a file, then call `fromfile`. Finally, if obj is an `ndarray`, then
- return ``obj.view(recarray)``, making a copy of the data if ``copy=True``.
- Examples
- --------
- >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
- array([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]])
- >>> np.core.records.array(a)
- rec.array([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]],
- dtype=int32)
- >>> b = [(1, 1), (2, 4), (3, 9)]
- >>> c = np.core.records.array(b, formats = ['i2', 'f2'], names = ('x', 'y'))
- >>> c
- rec.array([(1, 1.0), (2, 4.0), (3, 9.0)],
- dtype=[('x', '<i2'), ('y', '<f2')])
- >>> c.x
- rec.array([1, 2, 3], dtype=int16)
- >>> c.y
- rec.array([ 1.0, 4.0, 9.0], dtype=float16)
- >>> r = np.rec.array(['abc','def'], names=['col1','col2'])
- >>> print(r.col1)
- abc
- >>> r.col1
- array('abc', dtype='<U3')
- >>> r.col2
- array('def', dtype='<U3')
- """
- if ((isinstance(obj, (type(None), str)) or hasattr(obj, 'readinto')) and
- formats is None and dtype is None):
- raise ValueError("Must define formats (or dtype) if object is "
- "None, string, or an open file")
- kwds = {}
- if dtype is not None:
- dtype = sb.dtype(dtype)
- elif formats is not None:
- dtype = format_parser(formats, names, titles,
- aligned, byteorder).dtype
- else:
- kwds = {'formats': formats,
- 'names': names,
- 'titles': titles,
- 'aligned': aligned,
- 'byteorder': byteorder
- }
- if obj is None:
- if shape is None:
- raise ValueError("Must define a shape if obj is None")
- return recarray(shape, dtype, buf=obj, offset=offset, strides=strides)
- elif isinstance(obj, bytes):
- return fromstring(obj, dtype, shape=shape, offset=offset, **kwds)
- elif isinstance(obj, (list, tuple)):
- if isinstance(obj[0], (tuple, list)):
- return fromrecords(obj, dtype=dtype, shape=shape, **kwds)
- else:
- return fromarrays(obj, dtype=dtype, shape=shape, **kwds)
- elif isinstance(obj, recarray):
- if dtype is not None and (obj.dtype != dtype):
- new = obj.view(dtype)
- else:
- new = obj
- if copy:
- new = new.copy()
- return new
- elif hasattr(obj, 'readinto'):
- return fromfile(obj, dtype=dtype, shape=shape, offset=offset)
- elif isinstance(obj, ndarray):
- if dtype is not None and (obj.dtype != dtype):
- new = obj.view(dtype)
- else:
- new = obj
- if copy:
- new = new.copy()
- return new.view(recarray)
- else:
- interface = getattr(obj, "__array_interface__", None)
- if interface is None or not isinstance(interface, dict):
- raise ValueError("Unknown input type")
- obj = sb.array(obj)
- if dtype is not None and (obj.dtype != dtype):
- obj = obj.view(dtype)
- return obj.view(recarray)
|