Computed Fields in Blaze/DyND

This demo shows a mechanism for creating computed fields, creating 'full name' and 'age' fields.

To start, we import the DyND library.


In [13]:
from dynd import nd, ndt

Creating a Table

Let's create a table with name and birthday fields.


In [14]:
a = nd.array([('Smith', 'John', '1979-01-22'),
                ('Katz', 'Barbara', '1990-12-03'),
                ('Barker', 'Henry', '1979-06-12')],
            dtype='{lastname: string[32], firstname: string[32], birthday: date}', access='rw')

In [15]:
a


Out[15]:
nd.array([["Smith", "John", 1979-01-22],
          ["Katz", "Barbara", 1990-12-03],
          ["Barker", "Henry", 1979-06-12]],
         type="strided * {lastname : string[32], firstname : string[32], birthday : date}")

Because we used fixed-buffer strings, we can reassign their values and convert them into NumPy.


In [16]:
a[2].firstname = 'George'

In [17]:
nd.as_numpy(a, allow_copy=True)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-bd6d5c88e235> in <module>()
----> 1 nd.as_numpy(a, allow_copy=True)

C:\Users\mwiebe\Anaconda\lib\site-packages\dynd\_pydynd.pyd in dynd._pydynd.as_numpy (dynd._pydynd.cxx:11987)()

TypeError: Cannot instantiate arrfunc with signature (date) -> int64 with types (date) -> unaligned[int64]

Adding Computed Fields

Now let's add our two computed fields.

The first one, 'fullname', is simple, we simply concatenate the first and last name strings together. The second one, 'age', is a little trickier. There doesn't seem to be a simple one-liner for this, and the code here will crash on February 29th, but hopefully it demonstrates the idea nicely.


In [18]:
b = nd.add_computed_fields(a,
    [('fullname', ndt.string,
        'firstname + " " + lastname'),
     ('age', ndt.int32,
        'date.today().year - birthday.year - 1 + (date.today().replace(year=birthday.year) >= birthday)')])

Now 'b' is a deferred evaluation object, with a fairly complicated dynd type. You don't need to know what's going on in the type printout, but here it is for show.


In [19]:
nd.type_of(b)


Out[19]:
ndt.type("strided * expr<c{lastname : string[32], firstname : string[32], birthday : date, fullname : string, age : int32}, op0={lastname : string[32], firstname : string[32], birthday : date}, expr=computed_field_expr(op0)>")

We can evaluate back to an object with no deferred expression using the 'eval' method.


In [20]:
b.eval()


Out[20]:
nd.array([["Smith", "John", 1979-01-22, "John Smith", 35],
          ["Katz", "Barbara", 1990-12-03, "Barbara Katz", 23],
          ["Barker", "George", 1979-06-12, "George Barker", 35]],
         type="strided * c{lastname : string[32], firstname : string[32], birthday : date, fullname : string, age : int32}")

Testing the Deferred Evaluation

Finally, let's modify values in 'a', and see how that affects 'b'.


In [21]:
print(nd.as_py(b[1]))
print(nd.as_py(b.age))


{'age': 23, 'fullname': 'Barbara Katz', 'birthday': datetime.date(1990, 12, 3), 'firstname': 'Barbara', 'lastname': 'Katz'}
[35, 23, 35]

In [22]:
a[1] = ['Ford', 'Carol', '1967-05-12']

In [23]:
print(nd.as_py(b[1]))
print(nd.as_py(b.age))


{'age': 23, 'fullname': 'Barbara Katz', 'birthday': datetime.date(1990, 12, 3), 'firstname': 'Barbara', 'lastname': 'Katz'}
[35, 23, 35]

In [ ]: