Question

I can't figure out how to use the CArray trait. Why does this class

from traits.api import HasTraits, CArray, Float,Int
import numpy as np

class Coordinate3D(HasTraits):
   coordinate = CArray(Float(), shape=(1,3) )

   def _coordinate_default(self):
     return np.array([1.,2.,3.])

apparently not use my _name_default() method?

In [152]: c=Coordinate3D()
In [153]: c.coordinate
Out[153]: np.array([[ 0.,  0.,  0.]])

I would have expected np.array([1,2,3])! The _name_default() seems to work with Int

class A(HasTraits):
    a=Int
    def _a_default(self):
       return 2

In [163]: a=A()    
In [164]: a.a
Out[164]: 2

So what I am doing wrong here? Also, I can't assign values:

In [181]: c.coordinate=[1,2,3]
TraitError: The 'coordinate' trait of a Coordinate3D instance must be an array of      
float64 values with shape (1, 3), but a value of array([ 1.,  2.,  3.]) <type
'numpy.ndarray'> was specified.

Same error message with

In [182]: c.coordinate=np.array([1,2,3])
Was it helpful?

Solution

There is a difference between one-dimensional arrays and two-dimensional arrays in which one of the dimensions has size 1. You are trying to set a 1-D array into a CArray trait expecting two dimensions. For example, your default method should be:

def _coordinate_default(self):
    return np.array([[1., 2., 3.]])

(note the extra square brackets). The array you were setting is of shape (3,), not the desired (1, 3).

Similarly, it will not coerce a flat list into a 2-D array. Try assigning a nested list like

c.coordinate=[[1, 2, 3]]

instead.

(Alternatively, if you actually want 1-D arrays, you should use shape=(3,) in your traits assignment and the other parts should work correctly.)

OTHER TIPS

Dummy me. While copy-pasting from Eclipse to iPython, I didn't use the magic %paste function and messed up the class definition there. The other actual error was the shape of the CArray which must be (3,).

This code

class Coordinate3D(HasTraits):  

   coordinate = CArray(Float(),shape=(3,))

   def __init__(self,iv=None):
     super(Coordinate3D,self).__init__()
     if iv:
       self.coordinate=iv

   def _coordinate_default(self):
     return array([1,2,3])

   def __getitem__(self,index):
     return self.coordinate[index]

works like intended:

In [3]: c=Coordinate3D()
In [6]: c.coordinate
Out[6]: array([ 1.,  2.,  3.])

In [7]: c=Coordinate3D([1,2,5])
In [8]: c.coordinate
Out[8]: array([ 1.,  2.,  5.])

In [11]: c[0]
Out[11]: 1.0

In extension to the previous answers, I experimented further:

import types
RealNumberType    = (types.IntType, types.LongType, types.FloatType)

class ScaleFactor3D(Coordinate3D):
  '''Demonstrate subclassing a HasTraits class
     and overriding __init__ and a _default method''' 
  def _coordinate_default(self):
    return array([1,1,1])

  def __init__(self,iv=None):
    if isinstance(iv,RealNumberType):
      iv=[iv,iv,iv]
    super(ScaleFactor3D,self).__init__(iv)  

This works well too:

In [35]: s=ScaleFactor3D()

In [36]: s.coordinate
Out[36]: array([ 1.,  1.,  1.])

In [37]: s=ScaleFactor3D(3)

In [38]: s.coordinate
Out[38]: array([ 3.,  3.,  3.])

I thought I'd put this here since I couldn't find much useful information on CArray on the web.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top