9

I just discovered Numpy structured arrays and I find them to be quite powerful. The natural question arises in my mind: How in the world do I create a Numpy structure scalar. Let me show you what I mean. Let's say I want a structure containing some data:

import numpy as np
dtype = np.dtype([('a', np.float_), ('b', np.int_)])
ar = np.array((0.5, 1), dtype=dtype)
ar['a']

This gives me array(0.5) instead of 0.5. On the other hand, if I do this:

import numpy as np
dtype = np.dtype([('a', np.float_), ('b', np.int_)])
ar = np.array([(0.5, 1)], dtype=dtype)
ar[0]['a']

I get 0.5, just like I want. Which means that ar[0] isn't an array, but a scalar. Is it possible to create a structured scalar in a way more elegant than the one I've described?

4
  • 3
    Do you mean scalar when you write singleton? Commented Dec 11, 2017 at 15:28
  • 1
    ar[()]` on a 0d array produces the same thing as ar[0] for a 1d. Commented Dec 11, 2017 at 18:20
  • @kazemakase Yes. I'll edit my question accordingly. Commented Dec 11, 2017 at 20:26
  • @hpaulj I was more interested in a way that didn't require indexing and produced a structure directly, but seems that isn't possible. Commented Dec 11, 2017 at 20:27

2 Answers 2

7

Singleton isn't quite the right term, but I get what you want.

arr = np.array((0.5, 1), dtype=dtype)

Creates a 0d, single element array of this dtype. Check its dtype and shape.

arr.item() returns a tuple (0.5, 1). Aso test arr[()] and arr.tolist().

np.float64(0.5) creates a float with a numpy wrapper. It is similar to, but not exactly the same as np.array(0.5). Their methods diff some.

I don't know anything similar with a compound dtype.


In [123]: dt = np.dtype('i,f,U10')
In [124]: dt
Out[124]: dtype([('f0', '<i4'), ('f1', '<f4'), ('f2', '<U10')])
In [125]: arr = np.array((1,2,3),dtype=dt)
In [126]: arr
Out[126]: 
array((1,  2., '3'),
      dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', '<U10')])
In [127]: arr.shape
Out[127]: ()

arr is a 0d 1 element array. It can be indexed with:

In [128]: arr[()]
Out[128]: (1,  2., '3')
In [129]: type(_)
Out[129]: numpy.void

This indexing produces a np.void object. Doing the same thing on a 0d float array would produce a np.float object.

But you can't use np.void((1,2,3), dtype=dt) to directly create such an object (in contrast to np.float(12.34)).

item is the normal way of extracting a 'scalar' from an array. Here it returns a tuple, the same sort of object that we used as input to create arr:

In [131]: arr.item()
Out[131]: (1, 2.0, '3')
In [132]: type(_)
Out[132]: tuple

np.asscalar(arr) returns the same tuple.

One difference between the np.void object and the tuple, is that it can still be indexed with the field name, arr[()]['f0'], whereas the tuple has to be indexed by number arr.item()[0]. The void still has a dtype, while the tuple doesn't.

fromrecords makes a recarray. This is similar to a structured array, but allows us to access fields as attributes. It may actually be an older class, that has been merged to into numpy, hence the np.rec prefix. Mostly we use structured arrays, though np.rec still has some convenience functions. (actually in numpy.lib.recfunctions):

In [133]: res = np.rec.fromrecords((1,2,3), dt)
In [134]: res
Out[134]: 
rec.array((1,  2., '3'), 
          dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', '<U10')])
In [135]: res.f0
Out[135]: array(1, dtype=int32)
In [136]: res.item()
Out[136]: (1, 2.0, '3')
In [137]: type(_)
Out[137]: tuple
In [138]: res[()]
Out[138]: (1, 2.0, '3')
In [139]: type(_)
Out[139]: numpy.record

So this produced a np.record instead of a np.void. But that's just a subclass:

In [143]: numpy.record.__mro__
Out[143]: (numpy.record, numpy.void, numpy.flexible, numpy.generic, object)

Accessing a structured array by field name gives an array of the corresponding dtype (and same shape)

In [145]: arr['f1']
Out[145]: array(2.0, dtype=float32)
In [146]: arr[()]['f1']
Out[146]: 2.0
In [147]: type(_)
Out[147]: numpy.float32

Out[146] could also be created with np.float32(2.0).


Checking my comment on the ar[0] for the 1d array:

In [158]: arr1d = np.array([(1,2,3)], dt)
In [159]: arr1d
Out[159]: 
array([(1,  2., '3')],
      dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', '<U10')])
In [160]: arr1d[0]
Out[160]: (1,  2., '3')
In [161]: type(_)
Out[161]: numpy.void

So arr[()] and arr1d[0] do the same thing for their respective sized arrays. Likewise arr2d[0,0], which can also be written as arr2d[(0,0)].

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

1 Comment

Making what work? I cover a lot in this answer. If you have problems doing a specific task with numba you should ask a new question, rather than add onto a question that nothing to do with numba.
4

Use np.asscalar.
In both of your cases it will be just np.asscalar(ar['a']).

Also, you might find useful np.item.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.