Working with attributes¶
Fields and groups can have attributes attached which provide additional
metadata. In fact Nexus makes heavy use of attributes to store additional
information about a field or group. It is thus not recommended to use
attributes too excessively as one may runs into name conflicts with future Nexus
development.
Attributes can be accessed from fields and groups via their attributes
member. Attributes behave basically like fields with some restrictions
they cannot grow
one cannot apply compression to attributes.
Due to this restrictions one should not use attributes to store large amounts of data.
To create attributes use the create()
method of the
attributes
member
import pninexus.nexus as nx
field = ....
a = field.attributes.create("temperature",type="float32")
which would create a scalar attribute of name temperature
and with a 32-bit
floating point type. Multidimensional attributes can be created with the
optional shape
keyword argument
a = field.attributes.create("temperature",type="float32",shape=(4,4))
If an attribute already exists it can be overwritten using the overwrite
keyword argument (False
by default).
a = field.attributes.create("temperature",type="float32",shape=(4,4))
a = field.attributes.create("temperature",type="int32",shape=(3,5),
overwrite=True)
Attribute inquiry¶
Like fields and groups attributes have a couple of properties which can be queried to obtain metadata for a particular attribute instance
property |
description |
---|---|
|
a tuple with the number of elements along each dimension of the attribute |
|
the string representation of the attributes data type |
|
true if the attribute is a valid object |
|
the name of the attribute (the key which can be used to retrieve the attribute from its parent) |
|
provides access to the attributes data |
|
returns the path of the attribute |
The following code
#!/usr/bin/env python
#File: attributes_properties.py
from __future__ import print_function
import pni.io.nx.h5 as nexus
f = nexus.create_file("attributes_properties.nxs",overwrite=True)
r = f.root()
fmt = "{name:20} : {path:20} type={dtype:<10} size={size:<20}"
for a in r.attributes:
print(fmt.format(name=a.name,
path=a.path,
dtype=a.dtype,
size=a.size))
f.close()
will produce
HDF5_Version : /@HDF5_Version type=string size=1
NX_class : /@NX_class type=string size=1
file_name : /@file_name type=string size=1
file_time : /@file_time type=string size=1
file_update_time : /@file_update_time type=string size=1
Attribute retrieval and iteration¶
There are basically three ways to to access the attributes attached to a group or a field
by name via
__getitem__()
(using [])by the attributes index
by an iterator.
The attributes
attribute of groups and fields exposes an iterable
interface. The following example shows all three ways how to access the
attributes of a files root group
#!/usr/bin/env python
#File: attribute_access.py
from __future__ import print_function
import pni.io.nx.h5 as nexus
f = nexus.create_file("attributes_access.nxs",overwrite=True)
r = f.root()
#access attributes via their index
print("Access attributes via index")
for index in range(len(r.attributes)):
print("{index}: {name}".format(index=index,
name=r.attributes[index].name))
#access attributes via iterator
print()
print("Access attributes via iterator")
for attr in r.attributes:
print(attr.name)
#access directly via name
print(r.attributes["HDF5_Version"].name)
The output is
Reading and writing data from and to an attribute¶
Concerning IO operations attributes heave pretty much like fields as shown in the next example
#!/usr/bin/env python
#File: attribute_io.py
from __future__ import print_function
import numpy
import pni.io.nx.h5 as nexus
f = nexus.create_file("attribute_io.nxs",overwrite=True)
samptr = f.root().create_group("scan_1","NXentry"). \
create_group("instrument","NXinstrument"). \
create_group("sample","NXsample"). \
create_group("transformations","NXtransformations")
samptr.parent.create_field("depends_on","string")[...]="transformation/tt"
tt = samptr.create_field("tt","float64",shape=(10,))
tt[...] = numpy.array([1,2,3,4,5,6,7,8,9,10])
tt.attributes.create("transformation_type","str")[...] = "rotation"
a = tt.attributes.create("vector","float64",shape=(3,))
a[...] = numpy.array([-1,0,0])
print("r=",a[...])
print("x=",a[0])
print("y=",a[1])
print("z=",a[2])
r= [-1. 0. 0.]
x= -1.0
y= 0.0
z= 0.0
There is not too much more to day about that. When reading and writing multidimensional data numpy arrays must be used in any case (also for strings).