60

The answer will be very obvious I think, but I don't see it at the moment.

How can I convert a record array back to a regular ndarray?

Suppose I have following simple structured array:

x = np.array([(1.0, 4.0,), (2.0, -1.0)], dtype=[('f0', '<f8'), ('f1', '<f8')])

then I want to convert it to:

array([[ 1.,  4.],
       [ 2., -1.]])

I tried asarray and astype, but that didn't work.

UPDATE (solved: float32 (f4) instead of float64 (f8))

OK, I tried the solution of Robert (x.view(np.float64).reshape(x.shape + (-1,)) ), and with a simple array it works perfectly. But with the array I wanted to convert it gives a strange outcome:

data = np.array([ (0.014793682843446732, 0.006681123282760382, 0.0, 0.0, 0.0, 0.0008984912419691682, 0.0, 0.013475529849529266, 0.0, 0.0),
       (0.014793682843446732, 0.006681123282760382, 0.0, 0.0, 0.0, 0.0008984912419691682, 0.0, 0.013475529849529266, 0.0, 0.0),
       (0.014776384457945824, 0.006656022742390633, 0.0, 0.0, 0.0, 0.0008901208057068288, 0.0, 0.013350814580917358, 0.0, 0.0),
       (0.011928378604352474, 0.002819152781739831, 0.0, 0.0, 0.0, 0.0012627150863409042, 0.0, 0.018906937912106514, 0.0, 0.0),
       (0.011928378604352474, 0.002819152781739831, 0.0, 0.0, 0.0, 0.001259754877537489, 0.0, 0.01886274479329586, 0.0, 0.0),
       (0.011969991959631443, 0.0028706740122288465, 0.0, 0.0, 0.0, 0.0007433745195157826, 0.0, 0.011164642870426178, 0.0, 0.0)], 
      dtype=[('a_soil', '<f4'), ('b_soil', '<f4'), ('Ea_V', '<f4'), ('Kcc', '<f4'), ('Koc', '<f4'), ('Lmax', '<f4'), ('malfarquhar', '<f4'), ('MRN', '<f4'), ('TCc', '<f4'), ('Vcmax_3', '<f4')])

and then:

data_array = data.view(np.float).reshape(data.shape + (-1,))

gives:

In [8]: data_array
Out[8]: 
array([[  2.28080997e-20,   0.00000000e+00,   2.78023241e-27,
          6.24133580e-18,   0.00000000e+00],
       [  2.28080997e-20,   0.00000000e+00,   2.78023241e-27,
          6.24133580e-18,   0.00000000e+00],
       [  2.21114197e-20,   0.00000000e+00,   2.55866881e-27,
          5.79825816e-18,   0.00000000e+00],
       [  2.04776835e-23,   0.00000000e+00,   3.47457730e-26,
          9.32782857e-17,   0.00000000e+00],
       [  2.04776835e-23,   0.00000000e+00,   3.41189244e-26,
          9.20222417e-17,   0.00000000e+00],
       [  2.32706550e-23,   0.00000000e+00,   4.76375305e-28,
          1.24257748e-18,   0.00000000e+00]])

which is an array with other numbers and another shape. What did I do wrong?

2
  • np.asanyarray(x) will maintain the complex dtype for each column else np.array(x.tolist()) Commented May 10, 2011 at 23:05
  • You need to replace np.float with data.dtype[0]. Please, update your question posting the solution at the end, so it is more clear for the reader. Commented Apr 1, 2016 at 19:38

5 Answers 5

49

The simplest method is probably

x.view((float, len(x.dtype.names)))

(float must generally be replaced by the type of the elements in x: x.dtype[0]). This assumes that all the elements have the same type.

This method gives you the regular numpy.ndarray version in a single step (as opposed to the two steps required by the view(…).reshape(…) method.

Sign up to request clarification or add additional context in comments.

10 Comments

I would add an improvement: x.view((x.dtype[0], len(x.dtype.names))). So, one can even define a function that does this, since everything is parametrised.
You mentioned that the Cookbook has a few efficient methods. Would you mind pointing out where. I am not sure how to find them.
The link that was in the answer became invalid. I cannot find the page it was originally referring to.
I've been using this method for a while now; unfortunately now I'm getting "FutureWarning: Numpy has detected that you may be viewing or writing to an array returned by selecting multiple fields in a structured array. This code may break in numpy 1.13 because this will return a view instead of a copy -- see release notes for details." Any thoughts how to deal with that?
This does not work for me in Numpy 1.14. Assume I have the following structured array: arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b', 'f4'), ('c', 'f4'), ('d', 'f4')]). Then trying to convert the 4-tuple inside into a regular array via out = arr[0].view((np.float32, len(arr.dtype.names))) results in ValueError: Changing the dtype of a 0d array is only supported if the itemsize is unchanged.
|
35
[~]
|5> x = np.array([(1.0, 4.0,), (2.0, -1.0)], dtype=[('f0', '<f8'), ('f1', '<f8')])

[~]
|6> x.view(np.float64).reshape(x.shape + (-1,))
array([[ 1.,  4.],
       [ 2., -1.]])

9 Comments

Thanks! I suppose this doesn't make a copy of the array?
@joris: Your array contains single-precision (32 bit) floating point numbers. To reinterpret the same memory as an unstructured array, use .view(np.float32) in the above code.
@joris, correct, it does not make a copy. It is just a view on top of the memory in the original array.
There is no need for a tuple construction: reshape(x.shape + (-1,)) can be simplified as reshape(x.shape, -1). I updated the answer.
@RobertKern: It is indeed not documented in the reshape() documentation string. However, it is used in many places in the official documentation, so I take it it is quite official (one can for instance find many instances of reshape(i, j, k) at scipy.org/Numpy_Example_List). I asked the NumPy community to clarify this (projects.scipy.org/numpy/ticket/2110).
|
19

In conjunction with changes on how it handle multi-field indexing numpy has provided two new functions that can help in converting to/from structured arrays:

In numpy.lib.recfunctions, these are structured_to_unstructured and unstructured_to_structured. repack_fields is another new function.

From the 1.16 release notes

multi-field views return a view instead of a copy

Indexing a structured array with multiple fields, e.g., arr[['f1', 'f3']], returns a view into the original array instead of a copy. The returned view will often have extra padding bytes corresponding to intervening fields in the original array, unlike before, which will affect code such as arr[['f1', 'f3']].view('float64'). This change has been planned since numpy 1.7. Operations hitting this path have emitted FutureWarnings since then. Additional FutureWarnings about this change were added in 1.12.

To help users update their code to account for these changes, a number of functions have been added to the numpy.lib.recfunctions module which safely allow such operations. For instance, the code above can be replaced with structured_to_unstructured(arr[['f1', 'f3']], dtype='float64'). See the “accessing multiple fields” section of the user guide.

4 Comments

Here's a link to the accessing multiple fields section of the user guide.
This is a better answer than the selected one. Using .view(float) doe not allow to extract and merge a subset of fields, whereas structured_to_unstructured does
Best answer! It works fine from numpy.lib.recfunctions import structured_to_unstructured structured_to_unstructured(X)
Great answer. This feels like the most "official" way of doing this. Great for reading point cloud data from a PLY file using the plyfile library, for example.
15
np.array(x.tolist())
array([[ 1.,  4.],
      [ 2., -1.]])

but maybe there is a better method...

6 Comments

This is slow, as you first convert an efficiently packed NumPy array to a regular Python list. The official method is much faster (see my answer).
This is the easiest to remember... It's surprising there is no x.toArray() method...
Don't make python lists when you don't have to, they're much more expensive.
This is indeed slow, but the only answer that works reliably. The other ones do not work for me (numpy 1.14.x).
This method works when the structured array has multiple data types, the other methods fail under this situation. Very useful for unit tests where speed isn't important, just getting a comparison to work is.
|
0

A very simple solution using the function rec2array of root_numpy:

np_array = rec2array(x)

root_numpy is actually deprecated but the rec2array code is useful anyway (source here):

def rec2array(rec, fields=None):

  simplify = False

  if fields is None:
      fields = rec.dtype.names
  elif isinstance(fields, string_types):
      fields = [fields]
      simplify = True

  # Creates a copy and casts all data to the same type
  arr = np.dstack([rec[field] for field in fields])

  # Check for array-type fields. If none, then remove outer dimension.
  # Only need to check first field since np.dstack will anyway raise an
  # exception if the shapes don't match
  # np.dstack will also fail if fields is an empty list
  if not rec.dtype[fields[0]].shape:
      arr = arr[0]

  if simplify:
      # remove last dimension (will be of size 1)
      arr = arr.reshape(arr.shape[:-1])

  return arr

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.