1

I have

x = np.arange(0, 4, 1)
y = np.arange(0, 4, 1)

and I want

array([[(0,0), (0,1), (0,2), (0,3)],
       [(1,0), (1,1), (1,2), (1,3)],
       [(2,0), (2,1), (2,2), (2,3)],
       [(3,0), (3,1), (3,2), (3,3)]
      ])

Please note, that I want to have tuples on the lowest level! This is not an array of grade 3! This is an array of grade 2 with tuples as elements!

The numbers 0 to 3 are just an example. In the real situation it would be floats. After that I want to use those float-tuples as parameter to a Factory function that produces objects that replace those tuples with the objects.

For now the question is: How do I get this array of tuples?

I looked into in Numpy, how to zip two 2-D arrays? but this did not work, as my comment reveals.

Make42
  • 12,236
  • 24
  • 79
  • 155
  • @inspectorG4dget: Please read my question carefully! I added the point, where the difference is to the supposed duplicate question. Please unmark the question. – Make42 Jul 17 '17 at 15:53
  • You could call `tuple(...)` on each element. The computational aspects (you're looking for a cartesian product) of both questions are sufficiently similar, that I still feel that the two posts are duplicates. If you still feel otherwise, then please flag for moderator attention, so that someone wiser than myself can weigh in here – inspectorG4dget Jul 17 '17 at 16:13
  • You cannot call `tuple(...)` on each element after transformation into an array, because the information, what the element was (the tuple) is lost. – Make42 Jul 17 '17 at 16:46
  • I've reopened this because there's a dispute regarding a 3d array vs 2d array of tuples. But why is the array of tuples important? And what `dtype` do you expect. I see two options - object dtype or structured dtype as given in `VIX's` answer. – hpaulj Jul 17 '17 at 17:21

3 Answers3

6

array of tuples can have several meanings.

The double iteration produces a nested list of tuples:

In [292]: alist = [[(i,j) for j in y] for i in x]
In [293]: alist
Out[293]: 
[[(0, 0), (0, 1), (0, 2), (0, 3)],
 [(1, 0), (1, 1), (1, 2), (1, 3)],
 [(2, 0), (2, 1), (2, 2), (2, 3)],
 [(3, 0), (3, 1), (3, 2), (3, 3)]]

The default way of making an array from this produces a 3d array containing integer elements

In [294]: arr=np.array(alist)
In [295]: arr.dtype
Out[295]: dtype('int32')
In [296]: arr.shape
Out[296]: (4, 4, 2)
In [297]: arr[0,0]
Out[297]: array([0, 0])

Defining a compound dtype creates a structured array:

In [298]: arr=np.array(alist,'i,i')
In [299]: arr.dtype
Out[299]: dtype([('f0', '<i4'), ('f1', '<i4')])
In [300]: arr.shape
Out[300]: (4, 4)
In [301]: arr
Out[301]: 
array([[(0, 0), (0, 1), (0, 2), (0, 3)],
       [(1, 0), (1, 1), (1, 2), (1, 3)],
       [(2, 0), (2, 1), (2, 2), (2, 3)],
       [(3, 0), (3, 1), (3, 2), (3, 3)]],
      dtype=[('f0', '<i4'), ('f1', '<i4')])

arr.tolist() looks just like alist.

An element of this array is not a tuple, though it does display as such. I prefer to call it a record.

In [303]: type(arr[0,0])
Out[303]: numpy.void
In [304]: arr[0,0]
Out[304]: (0, 0)

The 2 elements of these records are fields, and can be accessed by name, e.g. arr['f0'], arr['f1'], which will both be 2d integer arrays.

It is possible to create a object array that can contain literal tuples (actually pointers to tuples elsewhere in memory:

In [305]: arr = np.empty((4,4),object)
In [306]: arr
Out[306]: 
array([[None, None, None, None],
       [None, None, None, None],
       [None, None, None, None],
       [None, None, None, None]], dtype=object)
In [307]: arr[...]=alist
In [308]: arr
Out[308]: 
array([[(0, 0), (0, 1), (0, 2), (0, 3)],
       [(1, 0), (1, 1), (1, 2), (1, 3)],
       [(2, 0), (2, 1), (2, 2), (2, 3)],
       [(3, 0), (3, 1), (3, 2), (3, 3)]], dtype=object)
In [309]: type(arr[0,0])
Out[309]: tuple

A non-iterative way of constructing an array from these x,y variables is meshgrid, which returns a list of (4,4) arrays. They can be 'stacked' in a way that matches the (4,4,2) array:

In [322]: I,J = np.meshgrid(x,y,indexing='ij')
In [323]: np.allclose(np.stack((I,J),axis=2), np.array(alist))
Out[323]: True

We can construct the record array from these as well:

In [327]: arr = np.zeros((4,4),'i,i')
In [328]: arr['f0']=I
In [329]: arr['f1']=J
hpaulj
  • 221,503
  • 14
  • 230
  • 353
1

An option would be.

M = np.asarray([[(i,j) for j in y] for i in x], dtype='int,int')
VIX
  • 605
  • 4
  • 15
0

Inspired by https://stackoverflow.com/a/17960617/4533188, it appears that

xx, yy = np.meshgrid(x,y)
np.array(list(zip(yy.ravel(), xx.ravel())), dtype=('i4,i4')).reshape(xx.shape)

resulting in

array([[(0, 0), (0, 1), (0, 2), (0, 3)],
       [(1, 0), (1, 1), (1, 2), (1, 3)],
       [(2, 0), (2, 1), (2, 2), (2, 3)],
       [(3, 0), (3, 1), (3, 2), (3, 3)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4')])

is also an answer.

Make42
  • 12,236
  • 24
  • 79
  • 155