Skip to content

Commit

Permalink
MAINT: Fixups to new functions in np.lib.recfunctions
Browse files Browse the repository at this point in the history
  • Loading branch information
ahaldane committed Nov 26, 2018
1 parent 11a316e commit 191d5c7
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 13 deletions.
41 changes: 28 additions & 13 deletions numpy/lib/recfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from numpy.core.overrides import array_function_dispatch
from numpy.lib._iotools import _is_string_like
from numpy.compat import basestring
from numpy.testing import suppress_warnings

if sys.version_info[0] < 3:
from future_builtins import zip
Expand Down Expand Up @@ -872,7 +873,7 @@ def repack_fields(a, align=False, recurse=False):

def _get_fields_and_offsets(dt, offset=0):
"""
Returns a flat list of (name, dtype, count, offset) tuples of all the
Returns a flat list of (dtype, count, offset) tuples of all the
scalar fields in the dtype "dt", including nested fields, in left
to right order.
"""
Expand All @@ -883,7 +884,7 @@ def _get_fields_and_offsets(dt, offset=0):
count = 1
for size in field[0].shape:
count *= size
fields.append((name, field[0], count, field[1] + offset))
fields.append((field[0], count, field[1] + offset))
else:
fields.extend(_get_fields_and_offsets(field[0], field[1] + offset))
return fields
Expand Down Expand Up @@ -911,7 +912,7 @@ def structured_to_unstructured(arr, dtype=None, copy=False, casting='unsafe'):
arr : ndarray
Structured array or dtype to convert. Cannot contain object datatype.
dtype : dtype, optional
The dtype of the output unstructured array
The dtype of the output unstructured array.
copy : bool, optional
See copy argument to `ndarray.astype`. If true, always return a copy.
If false, and `dtype` requirements are satisfied, a view is returned.
Expand Down Expand Up @@ -948,8 +949,9 @@ def structured_to_unstructured(arr, dtype=None, copy=False, casting='unsafe'):
raise ValueError('arr must be a structured array')

fields = _get_fields_and_offsets(arr.dtype)
names, dts, counts, offsets = zip(*fields)
n_fields = len(names)
n_fields = len(fields)
dts, counts, offsets = zip(*fields)
names = ['f{}'.format(n) for n in range(n_fields)]

if dtype is None:
out_dtype = np.result_type(*[dt.base for dt in dts])
Expand All @@ -964,7 +966,9 @@ def structured_to_unstructured(arr, dtype=None, copy=False, casting='unsafe'):
'formats': dts,
'offsets': offsets,
'itemsize': arr.dtype.itemsize})
arr = arr.view(flattened_fields)
with suppress_warnings() as sup: # until 1.16 (gh-12447)
sup.filter(FutureWarning, "Numpy has detected")
arr = arr.view(flattened_fields)

# next cast to a packed format with all fields converted to new dtype
packed_fields = np.dtype({'names': names,
Expand Down Expand Up @@ -1041,20 +1045,22 @@ def unstructured_to_structured(arr, dtype=None, names=None, align=False,
names = ['f{}'.format(n) for n in range(n_elem)]
out_dtype = np.dtype([(n, arr.dtype) for n in names], align=align)
fields = _get_fields_and_offsets(out_dtype)
names, dts, counts, offsets = zip(*fields)
dts, counts, offsets = zip(*fields)
else:
if names is not None:
raise ValueError("don't supply both dtype and names")
# sanity check of the input dtype
fields = _get_fields_and_offsets(dtype)
names, dts, counts, offsets = zip(*fields)
dts, counts, offsets = zip(*fields)
if n_elem != sum(counts):
raise ValueError('The length of the last dimension of arr must '
'be equal to the number of fields in dtype')
out_dtype = dtype
if align and not out_dtype.isalignedstruct:
raise ValueError("align was True but dtype is not aligned")

names = ['f{}'.format(n) for n in range(len(fields))]

# Use a series of views and casts to convert to a structured array:

# first view as a packed structured array of one dtype
Expand All @@ -1081,7 +1087,9 @@ def apply_along_fields(func, arr):
Apply function 'func' as a reduction across fields of a structured array.
This is similar to `apply_along_axis`, but treats the fields of a
structured array as an extra axis.
structured array as an extra axis. The fields are all first cast to a
common type following the type-promotion rules from `numpy.result_type`
applied to the field's dtypes.
Parameters
----------
Expand Down Expand Up @@ -1145,7 +1153,7 @@ def assign_fields_by_name(dst, src, zero_unassigned=True):
"""

if dst.dtype.names is None:
dst[:] = src
dst[...] = src
return

for name in dst.dtype.names:
Expand All @@ -1164,12 +1172,13 @@ def require_fields(array, required_dtype):
"""
Casts a structured array to a new dtype using assignment by field-name.
This function assigns to from the old to the new array by name, so the
This function assigns from the old to the new array by name, so the
value of a field in the output array is the value of the field with the
same name in the source array.
same name in the source array. This has the effect of creating a new
ndarray containing only the fields "required" by the required_dtype.
If a field name in the required_dtype does not exist in the
input array, that field is set to 0 in the output array.
input array, that field is created and set to 0 in the output array.
Parameters
----------
Expand All @@ -1189,6 +1198,12 @@ def require_fields(array, required_dtype):
>>> a = np.ones(4, dtype=[('a', 'i4'), ('b', 'f8'), ('c', 'u1')])
>>> require_fields(a, [('b', 'f4'), ('c', 'u1')])
array([(1., 1), (1., 1), (1., 1), (1., 1)],
dtype=[('b', '<f4'), ('c', 'u1')])
>>> require_fields(a, [('b', 'f4'), ('newf', 'u1')])
array([(1., 0), (1., 0), (1., 0), (1., 0)],
dtype=[('b', '<f4'), ('newf', 'u1')])
"""
out = np.empty(array.shape, dtype=required_dtype)
assign_fields_by_name(out, array)
Expand Down
13 changes: 13 additions & 0 deletions numpy/lib/tests/test_recfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,14 @@ def test_structured_to_unstructured(self):
assert_(dd.base is d)
assert_(ddd.base is d)

# test that nested fields with identical names don't break anything
point = np.dtype([('x', int), ('y', int)])
triangle = np.dtype([('a', point), ('b', point), ('c', point)])
arr = np.zeros(10, triangle)
res = structured_to_unstructured(arr, dtype=int)
assert_equal(res, np.zeros((10, 6), dtype=int))


def test_field_assignment_by_name(self):
a = np.ones(2, dtype=[('a', 'i4'), ('b', 'f8'), ('c', 'u1')])
newdt = [('b', 'f4'), ('c', 'u1')]
Expand All @@ -263,6 +271,11 @@ def test_field_assignment_by_name(self):
assign_fields_by_name(a, b)
assert_equal(a, np.array([((0,2),), ((0,3),)], dtype=a.dtype))

# test unstructured code path for 0d arrays
a, b = np.array(3), np.array(0)
assign_fields_by_name(b, a)
assert_equal(b[()], 3)


class TestRecursiveFillFields(object):
# Test recursive_fill_fields.
Expand Down

0 comments on commit 191d5c7

Please sign in to comment.