0

I am migrating some of my code from MATLAB. I was wondering if a functionality exists where I define a certain class (3d vector) and I could define arrays (or lists?) of this class. I would like to be able to use slicing operations on this array.

For example, MATLAB has this functionality:

obj = class(s,'class_name') 

creates an array of class class_name objects using the struct s as a pattern to determine the size of obj.

I understand that numpy offers everything I need for array operations. I am trying to learn right now and this is just and example. So, I would like to do this without numpy arrays.

I might be completely wrong in approaching it this way, so please feel free to suggest if there are any better methods out there to do this. I was looking into subclassing ndarray, but that seems like I would just be creating an array again. Any suggestions are greatly appreciated.

My code so far:

class vector3d(object):

    def __init__(self,*args):
        nargs = len(args);
        if(nargs == 0): # Null Vector
            self.x = None; self.y = None; self.z = None;
        elif(nargs==1):
            if (type(args[0]) is vector3d):
                self = args[0];
            elif(type(args[0]) is np.ndarray):
                Vec = args[0];
                if (np.shape(Vec)[0]==1 or np.shape(Vec)[1]==1):
                    if (np.shape(Vec) == (3,1)):
                        self.x = Vec[0,0]; self.y = Vec[1,0]; 
                        self.z = Vec[2,0];

                    elif (np.shape(Vec) == (1,3)):
                        self.x = Vec[0,0]; self.y = Vec[0,1]; 
                        self.z = Vec[0,2];
                    else:
                        raise Exception('Wrong Type of Inputs');
                else:
                    raise Exception('Wrong Type of Inputs');

VecArray = np.ndarray((10,), dtype=np.object);
print np.shape(VecArray);
for i in range(10):
    print i;
    VecArray[i] = vector3d(np.random.rand(3,1));

After running the code, when I try the following:

>>> VecArray[1].x

>>> 0.36923808713820772

>>> VecArray[1:5].x

AttributeError                            Traceback (most recent call last)
<ipython-input-92-899463ad0461> in <module>()
----> 1 VecArray[1:5].x

AttributeError: 'numpy.ndarray' object has no attribute 'x'

I understand that I could make lists of the object. I should have been more specific. I would like to get an indexable variable as output. For example, something that does not give the above as error.

user2304916
  • 7,882
  • 5
  • 39
  • 53
Srikanth
  • 769
  • 1
  • 8
  • 14
  • 1
    You can put whatever you want into regular Python lists, including instances of your custom class. – timgeb Aug 06 '14 at 13:51
  • 3
    Have you tried anything at all? – Aleksander Lidtke Aug 06 '14 at 13:52
  • @timgeb: So I just define a class and make lists of that class? What if I want to pass numpy arrays as input to the class? I would ideally want it to output a list (or array) of the class. For example, if I input numpy 3X10 array of floats.. I would like to have a list of 10 vector3d objects. Could this be done in the definition of the class? – Srikanth Aug 06 '14 at 13:56
  • @Lidtke: Yes, I have tried defining the class by itself. I am stuck in trying to figure out how to handle array inputs. As I had already mentioned, I am trying subclassing ndarray as well. But this just ended up creating another array of type vector3d. – Srikanth Aug 06 '14 at 13:58
  • You can put anything in a list, including `numpy` arrays. Perhaps you should show a [minimal example](http://stackoverflow.com/help/mcve) of what you've tried, and describe the problem in more detail. – jonrsharpe Aug 06 '14 at 13:59
  • What would you expect `VecArray[1:5].x` to actually do? – jonrsharpe Aug 06 '14 at 14:15
  • To print a numpy array of the x values of vector3d with indices 1,2,3,4. – Srikanth Aug 06 '14 at 14:17
  • @jonrsharpe: Does this make sense? Is it possible? It's a shame that this point was missed in the original question and probably no one will take another look at this because of the down-votes. – Srikanth Aug 06 '14 at 14:33
  • 1
    It does (finally) make sense, but I don't think it's (easily) possible - a slice returns a container, which doesn't have the same attributes as the objects inside. You could maybe create some custom container that implements `__getattr__` and checks its contents for any attributes it doesn't have itself, but that's a lot of work. – jonrsharpe Aug 06 '14 at 14:42
  • Thank you for your help, @jonrsharpe. I will try something along the line of iluengo's answer. Will keep the post updated. – Srikanth Aug 06 '14 at 14:46

2 Answers2

4

You can use numpy datatypes: http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html

>>> dt = np.dtype([('x', np.int32), ('y', np.int32), ('z', np.int32)])
>>> x = np.array([(1, 2, 3), (3, 2, 1)], dtype = dt)
>>> print x 
[(1, 2, 3) (3, 2, 1)]
>>> print x['x'], x['y'], x['z']
[1 3] [2 2] [3 1]
>>> print x[0]['x']
1

Extending example to add some numpy/matlab indexing:

>>> x = np.array([(1, 2, 3), (4, 5, 6), (7, 8, 9)], dtype = dt)
>>> print x[1:]['x']
[4 7]

You can notice that it omits the first element in the X axis (the 1)


EDIT to add some information about how to subclass using custom data type. Using the example in answer from a similar question https://stackoverflow.com/a/5154869/764322 and sightly modifying it:

>>> class Data(np.ndarray):
        def __new__(cls, inputarr):
            dt = np.dtype([('x', np.int32), ('y', np.int32), ('z', np.int32)])
            obj = np.asarray(inputarr, dtype = dt).view(cls)
            return obj

        def remove_some(self, col, val):
            return self[self[col] != val]

>>> a = Data([(1,2,3), (4,5,6), (7,8,9)])
>>> print a
[(1, 2, 3) (4, 5, 6) (7, 8, 9)]
>>> print a.remove_some('x', 4)
[(1, 2, 3) (7, 8, 9)]
Community
  • 1
  • 1
Imanol Luengo
  • 15,366
  • 2
  • 49
  • 67
  • Perfect.. Thank you so much! This is exactly what I was looking for! I think, if I am not mistaken, you defined a data type here. Would it be possible to extend this to a class? I would like to define a few methods for this class. Thank you! (PS: I would upvote your answer but all the down-votes to my question ended up reducing my points below 15! Sorry.) – Srikanth Aug 06 '14 at 14:42
  • If I don't get it wrong, this might be what you want: http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#simple-example-adding-an-extra-attribute-to-ndarray . You can force the numpy array to be the type of `dt` and then add new methods. – Imanol Luengo Aug 06 '14 at 14:48
  • Oh, okay! Looks like subclassing is the way to go. Thank you! – Srikanth Aug 06 '14 at 14:59
  • 1
    I've just edited to give you an example of how to subclass. Note the custom datatype in the __new__ method. – Imanol Luengo Aug 06 '14 at 15:15
0

I think what you want is numpy.empty:

>>> import numpy as np
>>> a = np.empty((2, 2), dtype=np.object_)
>>> a
array([[None, None],
       [None, None]], dtype=object)

This creates an empty array with the specified shape (in this case 2x2) and dtype (in this case, generic objects).

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437