on the dev
branch, I am working on a unified slicing and plotting implementation. This introduces the new Slices
, Units
, and Labels
classes. A sample instance of RichData can be populated with
# imports
import numpy as np
from astropy import units as u
from prysm import RichData, Labels, Units
# the wavelength unit
wvl_HeNe = u.Unit('λ', 632.8 * u.nm)
# the newness
rdu = Units(x=u.mm, y=u.mm, z=u.nm, wavelength=wvl_HeNe)
rdlab = Labels(xybase='Pupil',
z='OPD',
xy_additions=['ξ', 'η'],
xy_addition_side='left')
# standard xyz data
x = np.linspace(-1, 1, 128)
y = np.linspace(-1, 1, 128)
z = np.random.rand(128, 128)
# create the instance
rd = RichData(x=x, y=y, z=z, units=rdu, labels=rdlab)
Additional functionality w.r.t. plotting and slices is then used as
rd.plot2d()
rd.slices().plot(['x', 'y', 'z', 'azavg'])
s = rd.slices()
s.x
s.y
s.azavg
all labels, units, etc are known and displayed.
Internally, there is some difficulty in implementing this. The labels in the above case are intended to produce:
>>> rd.labels.x
Pupil ξ [mm]
>>> rd.labels.z
OPD [λ]
and thus the implementation of plot2d naturally wants to look like:
def plot2d(self, ...):
...
labs = self.labels
ax.set(xlabel=labs.x, ylabel=labs.y)
cb = colorbar(label=labs.z)
...
However, in order to produce this, labels
needs to know something about units
. One way to accomplish this is to pass a parent
breadcrumb down to labels
in the RichData
constructor:
class RichData:
def __init__(self, ..., labels):
labels.parent = self
self.labels = labels
however, this double reference is a data structure I am not very fond of, and we will have to copy the labels
and units
instances on each RichData
initialization to avoid shared state woes.
Another mechanism is to make labels.x
produce a joinable sequence that we can insert the label text into:
>>> rd.labels.x
['Pupil ξ [', ']']
and then do something like:
class RichData:
....
@property
def xlabel(self):
pieces = self.labels.x
pieces.insert(-1, str(self.units.x))
return ''.join(pieces)
This avoids bad shared state (units) and means we can actually share a labels instance on all Pupil (etc) instances pretty safely. However, we define the labels in two separate places. On the other hand, this allows a graceful adoption of the labels and units objects, since we can check if self.labels is None
and use the old behavior in that case.
see _basicdata.py and conf.py on https://github.com/brandondube/prysm/tree/f7d8b43133118199f6ed1ed9745bba9895797c8f for the current implementation. Reaching into the labels and units on RichData
is not implemented yet, since I haven't figured out an elegant API for that yet.