Skip to content

Commit

Permalink
Merge pull request #6500 from ahaldane/recarray_view_fix
Browse files Browse the repository at this point in the history
BUG: recarrays viewed as subarrays don't convert to np.record type
  • Loading branch information
charris committed Oct 18, 2015
2 parents c9dcd91 + 3a816a4 commit 8cf83ee
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 11 deletions.
30 changes: 19 additions & 11 deletions numpy/core/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,12 +448,14 @@ def __getattribute__(self, attr):

# 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. If obj is structured leave
# it as a recarray, but make sure to convert to the same dtype.type (eg
# to preserve numpy.record type if present), since nested structured
# fields do not inherit type.
# 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.fields:
return obj.view(dtype=(self.dtype.type, obj.dtype.fields))
if issubclass(obj.dtype.type, nt.void):
return obj.view(dtype=(self.dtype.type, obj.dtype))
return obj
else:
return obj.view(ndarray)

Expand All @@ -463,8 +465,9 @@ def __getattribute__(self, attr):
# Thus, you can't create attributes on-the-fly that are field names.
def __setattr__(self, attr, val):

# Automatically convert (void) dtypes to records.
if attr == 'dtype' and issubclass(val.type, nt.void):
# 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.fields:
val = sb.dtype((record, val))

newattr = attr not in self.__dict__
Expand Down Expand Up @@ -499,7 +502,9 @@ def __getitem__(self, indx):
# we might also be returning a single element
if isinstance(obj, ndarray):
if obj.dtype.fields:
return obj.view(dtype=(self.dtype.type, obj.dtype.fields))
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:
Expand All @@ -519,11 +524,14 @@ def __repr__(self):
# 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, use only dtype.descr,
# not repr(dtype).
# converts dtype to a numpy.record for us, convert back
# to non-record before printing
plain_dtype = self.dtype
if plain_dtype.type is record:
plain_dtype = sb.dtype((nt.void, plain_dtype))
lf = '\n'+' '*len("rec.array(")
return ('rec.array(%s, %sdtype=%s)' %
(lst, lf, repr(self.dtype.descr)))
(lst, lf, plain_dtype))
else:
# otherwise represent it using np.array plus a view
# This should only happen if the user is playing
Expand Down
23 changes: 23 additions & 0 deletions numpy/core/tests/test_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ def test_recarray_views(self):
assert_equal(type(rv), np.recarray)
assert_equal(rv.dtype.type, np.record)

# check that accessing nested structures keep record type, but
# not for subarrays, non-void structures, non-structured voids
test_dtype = [('a', 'f4,f4'), ('b', 'V8'), ('c', ('f4',2)),
('d', ('i8', 'i4,i4'))]
r = np.rec.array([((1,1), b'11111111', [1,1], 1),
((1,1), b'11111111', [1,1], 1)], dtype=test_dtype)
assert_equal(r.a.dtype.type, np.record)
assert_equal(r.b.dtype.type, np.void)
assert_equal(r.c.dtype.type, np.float32)
assert_equal(r.d.dtype.type, np.int64)
# check the same, but for views
r = np.rec.array(np.ones(4, dtype='i4,i4'))
assert_equal(r.view('f4,f4').dtype.type, np.record)
assert_equal(r.view(('i4',2)).dtype.type, np.int32)
assert_equal(r.view('V8').dtype.type, np.void)
assert_equal(r.view(('i8', 'i4,i4')).dtype.type, np.int64)

#check that we can undo the view
arrs = [np.ones(4, dtype='f4,i4'), np.ones(4, dtype='f8')]
for arr in arrs:
Expand All @@ -135,6 +152,12 @@ def test_recarray_repr(self):
a = np.array(np.ones(4, dtype='f8'))
assert_(repr(np.rec.array(a)).startswith('rec.array'))

# check that the 'np.record' part of the dtype isn't shown
a = np.rec.array(np.ones(3, dtype='i4,i4'))
assert_equal(repr(a).find('numpy.record'), -1)
a = np.rec.array(np.ones(3, dtype='i4'))
assert_(repr(a).find('dtype=int32') != -1)

def test_recarray_from_names(self):
ra = np.rec.array([
(1, 'abc', 3.7000002861022949, 0),
Expand Down

0 comments on commit 8cf83ee

Please sign in to comment.