Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am attempting to use a class that strings together several instances of another class as a numpy array of objects. I want to be able to concatenate attributes of the instances that are contained in the numpy array. I figured out a sloppy way to do it with a bunch of for loops, but I think there must be a more elegant, pythonic way of doing this. The following code does what I want, but I want to know if there is a cleaner way to do it:

import numpy as np


class MyClass(object):

    def __init__(self):
        self.a = 37.
        self.arr = np.arange(5)


class MyClasses(object):

    def __init__(self):
        self.N = 5 
        # number of MyClass instances to become attributes of this
    # class 

    def make_subclas_arrays(self):
        self.my_class_inst = np.empty(shape=self.N, dtype="object") 
        for i in range(self.N):
            self.my_class_inst[i] = MyClass()

    def concatenate_attributes(self):
        self.a = np.zeros(self.N)
        self.arr = np.zeros(self.N * self.my_class_inst[0].arr.size)
        for i in range(self.N):
            self.a[i] = self.my_class_inst[i].a
            slice_start = i * self.my_class_inst[i].arr.size
            slice_end = (i + 1.) * self.my_class_inst[i].arr.size
            self.arr[slice_start:slice_end] = ( 
        self.my_class_inst[i].arr )

my_inst = MyClasses()
my_inst.make_subclas_arrays()
my_inst.concatenate_attributes()

Edit: Based on the response from HYRY, here is what the methods look like now:

def make_subclass_arrays(self):
    self.my_class_inst = np.array([MyClass() for i in range(self.N)])

def concatenate_attributes(self):
    self.a = np.hstack([i.a for i in self.my_class_inst])
    self.arr = np.hstack([i.arr for i in self.my_class_inst])
share|improve this question
    
This change cut the necessary number of lines of code in half. –  Chad Feb 11 '12 at 16:13
add comment

2 Answers

up vote 1 down vote accepted

you can use numpy.hstack() to concatenate arrays:

def concatenate_attributes(self):
    self.a = np.hstack([o.a for o in self.my_class_inst])
    self.arr = np.hstack([o.arr for o in self.my_class_inst])

See Also

vstack : Stack arrays in sequence vertically (row wise). dstack : Stack arrays in sequence depth wise (along third axis). concatenate : Join a sequence of arrays together.

share|improve this answer
    
This is beautiful! This greatly improves the pythonicity of my code! Thanks. –  Chad Feb 11 '12 at 15:58
add comment

For the latter function I would recommend this:

init = []
ContainerClass.arr = np.array([init + Array(myclass.arr) for myclass in self.my_class_inst])

typecast numpy array to normal array, catenate and typecast it back. Assuming now that you have simple 1D arrays. I don't remember by heart if numpy array has catenation function. You can use that instead of '+' sign and save the trouble of typecasting.

For the first you have the simplest form I can think of, although I usually use normal arrays instead of numpy ones for objects.

If you want to be really clever you can create an __add__ function for both of the classes. Then you can use '+' sign to add classes. a + b calls a.__add__(b). Now you would have to create functions that have following properties

  1. MyClass + MyClass returns new MyClasses instance with a and b inside
  2. MyClasses + MyClass adds MyClass to MyClasses in a way you want

Now if a,b,c,d are myClass instances, a+b+c+d should return MyClasses -class which contains MyClass instances a,b,c and d and their combined arrays. This would be the pythonic way, although its a bit too complicated in my taste.

edit:

Ok, sorry my bad. I did not have python when I wrote the code. This is the correct version:

init = []
my_inst.arr = np.array([init + list(myclass.arr.flat) for myclass in my_inst.my_class_inst]).flatten()

This is what I meant with the __add__ (and the pythonic way... regadless of its complicatedness):

import numpy as np

class MyClass(object):

    def __init__(self):
        self.a = 37.
        self.arr = np.arange(5)

    def __add__(self, classToAdd):
        a = MyClasses() + self + classToAdd
        return a


class MyClasses(object):

    def __init__(self):
        self.N = 0
        self.my_class_inst = np.array([])
        self.a = np.array([])
        self.arr = np.array([])

    def __add__(self, singleClass):
        self.my_class_inst = np.hstack([self.my_class_inst, singleClass])
        self.a = np.hstack([self.a, singleClass.a])
        self.arr = np.hstack([self.arr, singleClass.arr])
        self.N = self.my_class_inst.shape[0]
        return self

#add_test = MyClass() + MyClass()
add_test = np.sum([MyClass() for i in range(5)])

print add_test.a, add_test.arr, add_test.N
print add_test.__class__, add_test.my_class_inst[0].__class__
share|improve this answer
    
Can you point to a good reference for what ContainerClass is? I am having trouble understanding what it can do for my purpose. –  Chad Feb 11 '12 at 15:56
    
hups, sorry. ContainerClass is supposed to be instance of MyClasses (my_inst in your example). Also there was also other dimension problems... now fixed. –  Juha Feb 12 '12 at 17:17
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.